def testMinDistanceChangedFiles(self): """Tests ``ChangedFile`` method.""" report = self._GetDummyReport( deps={'src/': Dependency('src/', 'https://repo', '6')}, dep_rolls={'src/': DependencyRoll('src/', 'https://repo', '0', '4')}) distance = 42 crashed = CrashedFile(_MOCK_FRAME) matches = { crashed: CrashMatch(crashed, [FileChangeInfo(ChangeType.MODIFY, 'file', 'file')], [FrameInfo(_MOCK_FRAME, 0)]) } frame = StackFrame(0, 'src/', 'func', 'f.cc', 'f.cc', [7], 'https://repo') with mock.patch('analysis.linear.changelist_features.min_distance.' 'MinDistanceFeature.' 'DistanceBetweenTouchedFileAndFrameInfos') as mock_distance: mock_distance.return_value = min_distance.Distance(distance, frame) self.assertEqual( min_distance.MinDistanceFeature( self._get_repository, _MAXIMUM)(report)( self._GetMockSuspect(), matches).changed_files, [ChangedFile(name='file', blame_url=('%s/+blame/%s/f.cc#%d' % (frame.repo_url, report.crashed_version, frame.crashed_line_numbers[0])), reasons=['Distance between touched lines and crashed' ' lines is %d, in frame #%d' % ( distance, frame.index)])])
def testAggregateChangedFilesAggregates(self): """Test that ``AggregateChangedFiles`` does aggregate reasons per file. In the main/inner loop of ``AggregateChangedFiles``: if multiple features all blame the same file change, we try to aggregate those reasons so that we only report the file once (with all reasons). None of the other tests here actually check the case where the same file is blamed multiple times, so we check that here. In particular, we provide the same ``FeatureValue`` twice, and hence the same ``ChangedFile`` twice; so we should get back a single ``ChangedFile`` but with the ``reasons`` fields concatenated. """ self.assertListEqual(self.feature.changed_files, [ChangedFile(name='a.cc', blame_url=None, reasons=['file_reason0']), ChangedFile(name='b.cc', blame_url=None, reasons=['file_reason0', 'file_reason1'])]) self.assertEqual(self.feature.changed_files, self.feature._changed_files)
def ChangedFiles(suspect, touched_file_to_distance, crashed_version): """Get all the changed files causing this feature to blame this result. Arg: suspect (Suspect): the suspect being blamed. touched_file_to_distance (dict): Dict mapping file name to ``Distance``s. crashed_version (str): Crashed version. Returns: List of ``ChangedFile`` objects sorted by frame index. For example: [ChangedFile( file = 'render_frame_impl.cc', blame_url = 'https://chr.com/../render_frame_impl.cc#1586', reasons = ['Minimum distance (LOC) 1, frame #5'] )] """ frame_index_to_changed_files = {} for touched_file, distance in (touched_file_to_distance.iteritems()): file_name = touched_file.new_path.split('/')[-1] if distance.frame is None: # pragma: no cover logging.warning('Missing the min_distance_frame for file %s' % file_name) continue frame_index_to_changed_files[distance.frame.index] = ChangedFile( name=file_name, blame_url=distance.frame.BlameUrl(crashed_version), reasons=[ 'Distance between touched lines and crashed lines is %d,' ' in frame #%d' % (distance.distance, distance.frame.index) ]) if not frame_index_to_changed_files: # pragma: no cover logging.warning('Found no changed files for suspect: %s', str(suspect)) return [] # Sort changed file by frame index. _, changed_files = zip(*sorted( frame_index_to_changed_files.iteritems(), key=lambda x: x[0])) return list(changed_files)
def __call__(self, x): return lambda y: FeatureValue(self.name, y == ((x % 2) == 1), 'reason1', [ChangedFile(name='b.cc', blame_url=None, reasons=['file_reason1'])])