def testClassifyCallStack(self): """Tests ``ClassifyCallStack`` method.""" callstack = CallStack(0, [ StackFrame(0, 'src/', 'func', 'comp1/a.cc', 'src/comp1/a.cc', [2]) ]) self.assertEqual(self.classifier.ClassifyCallStack(callstack), ['Comp1>Dummy']) callstack = CallStack(0, [ StackFrame(0, 'dummy/', 'no_func', 'comp2/a.cc', 'dummy/comp2.cc', [32]) ]) self.assertEqual(self.classifier.ClassifyCallStack(callstack), []) crash_stack = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'func', 'comp1/a.cc', 'src/comp1/a.cc', [2]), StackFrame(1, 'src/', 'ff', 'comp1/a.cc', 'src/comp1/a.cc', [21]), StackFrame(2, 'src/', 'func2', 'comp2/b.cc', 'src/comp2/b.cc', [8]) ]) self.assertEqual(self.classifier.ClassifyCallStack(crash_stack), ['Comp1>Dummy', 'Comp2>Dummy'])
def testClassifyCrashStack(self): dummy_classifier = DummyClassifier() crash_stack = CallStack(0) self.assertEqual(dummy_classifier.Classify([], crash_stack), '') crash_stack = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'a::c(p* &d)', 'f0.cc', 'src/f0.cc', [177]), StackFrame(1, 'src/', 'a::d(a* c)', 'f1.cc', 'src/f1.cc', [227]), StackFrame(2, 'src/dummy', 'a::e(int)', 'f2.cc', 'src/f2.cc', [87]), StackFrame(3, 'dummy/', 'a::g(int)', 'f3.cc', 'src/f3.cc', [87]) ]) self.assertEqual(dummy_classifier.Classify([], crash_stack), 'class_1') crash_stack = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'a::c(p* &d)', 'f0.cc', 'src/f0.cc', [177]), StackFrame(1, 'src/dummy', 'a::d(a* c)', 'f1.cc', 'src/f1.cc', [227]), StackFrame(2, 'src/dummy', 'a::e(int)', 'f2.cc', 'src/f2.cc', [87]) ]) self.assertEqual(dummy_classifier.Classify([], crash_stack), 'class_2')
def testChromeCrashParserParseLineMultipleCallstacks(self): parser = ChromeCrashParser() deps = {'src/': Dependency('src/', 'https://repo', '1')} stacktrace_string = textwrap.dedent( """ CRASHED [EXC @ 0x66] #0 0x7fee in a::b::c(p* &d) src/f0.cc:177 #1 0x4b6e in a::b::d(a* c) src/f1.cc:227 CRASHED [EXC @ 0x508] #0 0x8fee in e::f::g(p* &d) src/f.cc:20:2 #1 0x1fae in h::i::j(p* &d) src/ff.cc:9:1 """ ) stacktrace = parser.Parse(stacktrace_string, deps) expected_callstack0 = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'a::b::c(p* &d)', 'f0.cc', 'src/f0.cc', [177]), StackFrame(1, 'src/', 'a::b::d(a* c)', 'f1.cc', 'src/f1.cc', [227])]) expected_callstack1 = CallStack(0, frame_list=[ StackFrame( 0, 'src/', 'e::f::g(p* &d)', 'f.cc', 'src/f.cc', [20, 21, 22]), StackFrame( 1, 'src/', 'h::i::j(p* &d)', 'ff.cc', 'src/ff.cc', [9, 10])]) expected_stacktrace = Stacktrace([expected_callstack0, expected_callstack1], expected_callstack0) self._VerifyTwoStacktracesEqual(stacktrace, expected_stacktrace)
def testClassifyCallStack(self): """Tests ``ClassifyCallStack`` method.""" callstack = CallStack( 0, [StackFrame(0, 'src', 'func', 'f.cc', 'src/f.cc', [2])]) self.assertEqual(self.classifier.ClassifyCallStack(callstack), 'chromium') callstack = CallStack( 0, [StackFrame(0, '', 'android.a', 'comp1.cc', 'src/comp1.cc', [2])]) self.assertEqual(self.classifier.ClassifyCallStack(callstack), 'android_os') callstack = CallStack(0, [ StackFrame(0, '', 'func', 'comp2.cc', 'googleplex-android/src/comp2.cc', [32]) ]) self.assertEqual(self.classifier.ClassifyCallStack(callstack), 'android_os') callstack = CallStack( 0, [StackFrame(0, '', 'func', 'comp2.cc', 'unknown/comp2.cc', [32])]) self.assertIsNone(self.classifier.ClassifyCallStack(callstack)) callstack = CallStack(0, [ StackFrame(0, '', 'android.a.b', 'f.java', 'unknown/f.java', [32]) ], language_type=LanguageType.JAVA) self.assertEqual(self.classifier.ClassifyCallStack(callstack), 'android_os')
def testFilteringIncludesExtraFrameInShiftCase(self): """Tests that one extra frame is included in a 'shift' case. In a 'shift' case (i.e. where execution time at the root has shifted entirely from one function to another), the subtree and one extra frame above it should be included. """ parser = UMASamplingProfilerParser() frame1 = { 'difference': 0, 'log_change_factor': 0, 'responsible': False } frame2 = { 'difference': 0.1, 'log_change_factor': float('inf'), 'responsible': True } frame3 = { 'difference': 0.1, 'log_change_factor': float('-inf'), 'responsible': True } subtree_root_depth = 2 subtree_stacks = [ # In this case the root is the first ``frame2`` or ``frame3`` instance. { 'frames': [frame1, frame1, frame2, frame2] }, { 'frames': [frame1, frame1, frame3, frame3, frame3] }, ] deps = {'chrome/': Dependency('chrome/', 'https://repo', '1')} stacktrace = parser.Parse(subtree_stacks, subtree_root_depth, deps) filtered_stacks = ( CallStack( 0, [ ProfilerStackFrame(1, 0.0, 0.0, False), # extra node ProfilerStackFrame(2, 0.1, float('inf'), True), ProfilerStackFrame(3, 0.1, float('inf'), True), ]), CallStack( 0, [ ProfilerStackFrame(1, 0.0, 0.0, False), # extra node ProfilerStackFrame(2, 0.1, float('-inf'), True), ProfilerStackFrame(3, 0.1, float('-inf'), True), ProfilerStackFrame(4, 0.1, float('-inf'), True), ]), ) self.assertEqual(stacktrace.stacks, filtered_stacks)
def testStacktraceLen(self): """Tests ``len`` for ``Stacktrace`` object.""" frame_list1 = [ StackFrame(0, 'src/', 'func', 'file0.cc', 'src/file0.cc', [32]) ] frame_list2 = [ StackFrame(0, 'src/', 'func2', 'file0.cc', 'src/file0.cc', [32]) ] stack1 = CallStack(0, frame_list1, None, None) stack2 = CallStack(1, frame_list2, None, None) stacktrace = Stacktrace((stack1, stack2), stack1) self.assertEqual(len(stacktrace), 2)
def testStacktraceBool(self): """Tests ``bool`` for ``Stacktrace`` object.""" self.assertFalse(bool(Stacktrace([], None))) frame_list1 = [ StackFrame(0, 'src/', 'func', 'file0.cc', 'src/file0.cc', [32]) ] frame_list2 = [ StackFrame(0, 'src/', 'func2', 'file0.cc', 'src/file0.cc', [32]) ] stack1 = CallStack(0, frame_list1, None, None) stack2 = CallStack(1, frame_list2, None, None) self.assertTrue(bool(Stacktrace((stack1, stack2), stack1)))
def testUpdateInvetedIndexHandlerWithStacktraces(self): """Tests ``UpdateInvertedIndex`` handler when there are stacktraces.""" crash_analysis = FracasCrashAnalysis.Create('sig1') crash_analysis.identifiers = {'signature': 'sig1'} crash_analysis.requested_time = datetime.utcnow() - timedelta(hours=1) frames = [ StackFrame(0, 'src/', 'fun1', 'f1.cc', 'src/f1.cc', [2, 3], 'http://repo'), StackFrame(0, 'src/', 'fun2', 'f2.cc', 'xx/src/f2.cc', [8, 10], 'http://repo'), StackFrame(0, 'src/', 'fun3', 'f3.cc', 'y/src/f3.cc', [20, 30], 'http://repo') ] callstack = CallStack(0, frames) crash_analysis.stacktrace = Stacktrace([callstack], callstack) crash_analysis.put() response = self.test_app.get('/process/update-inverted-index', headers={'X-AppEngine-Cron': 'true'}) self.assertEqual(response.status_int, 200) self.assertEqual(ChromeCrashInvertedIndex.GetRoot().n_of_doc, 1) self.assertEqual(ChromeCrashInvertedIndex.Get('src/f1.cc').n_of_doc, 1) self.assertEqual(ChromeCrashInvertedIndex.Get('src/f2.cc').n_of_doc, 1) self.assertEqual(ChromeCrashInvertedIndex.Get('src/f3.cc').n_of_doc, 1)
def testParseStacktraceReturnsCache(self): """Tests that ``stacktrace`` returns cached ``_stacktrace`` value.""" crash_data = ClusterfuzzData(self.GetDummyClusterfuzzData()) stack = CallStack(1) stacktrace = Stacktrace([stack], stack) crash_data._stacktrace = stacktrace self._VerifyTwoStacktracesEqual(crash_data.stacktrace, stacktrace)
def _GetDummyReport(self, deps=None, dep_rolls=None): crash_stack = CallStack(0, [ StackFrame(0, 'src/', 'func', 'a.cc', 'a.cc', [2], 'https://repo') ]) return CrashReport('rev', 'sig', 'win', Stacktrace([crash_stack], crash_stack), ('rev0', 'rev9'), deps, dep_rolls)
def testChromeCrashParserParseLineJavaCallstack(self): parser = ChromeCrashParser() deps = {'src/': Dependency('src/', 'https://repo', '1')} stacktrace_string = textwrap.dedent( """ (JAVA) CRASHED [EXC @ 0x508] #0 0x7fee in a.f0.c f0.java:177 #1 0x4b6e in org.chromium.chrome.browser.a.f1.d f1.java:227 #2 0x7ff9 in a.f2.e f2.java:87:1 """ ) stacktrace = parser.Parse(stacktrace_string, deps) stack = CallStack(0, language_type=LanguageType.JAVA, frame_list=[ StackFrame(0, '', 'a.f0.c', 'a/f0.java', 'a/f0.java', [177]), StackFrame( 1, 'src/', 'org.chromium.chrome.browser.a.f1.d', 'chrome/android/java/src/org/chromium/chrome/browser/a/f1.java', 'src/chrome/android/java/src/org/chromium/chrome/' 'browser/a/f1.java', [227]), StackFrame(2, '', 'a.f2.e', 'a/f2.java', 'a/f2.java', [87, 88])]) expected_stacktrace = Stacktrace([stack], stack) self._VerifyTwoStacktracesEqual(stacktrace, expected_stacktrace)
def testDependencyRoll(self): """Tests parsing ``regression_rolls`` from regression_range.""" dep_roll = DependencyRoll('src/', 'https://repo', 'rev1', 'rev6') regression_rolls = { dep_roll.path: dep_roll, 'src/dummy': DependencyRoll('src/dummy', 'https://r', 'rev2', 'rev4'), 'src/add': DependencyRoll('src/add', 'https://rr', None, 'rev5') } with mock.patch( 'libs.deps.chrome_dependency_fetcher.ChromeDependencyFetcher' '.GetDependencyRollsDict') as mock_get_dependency_rolls: mock_get_dependency_rolls.return_value = regression_rolls crash_data = ChromeCrashData( self.GetDummyChromeCrashData(), ChromeDependencyFetcher(self.GetMockRepoFactory())) crash_data._regression_range = ('rev1', 'rev6') chromium_dep = Dependency('src/', 'https://repo', 'rev1') crash_data._crashed_version_deps = { chromium_dep.path: chromium_dep, 'src/dummy': Dependency('src/dummy', 'https://r', 'rev2')} stack = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [5])]) stacktrace = Stacktrace([stack], stack) crash_data._stacktrace = stacktrace self.assertEqual(crash_data.dependency_rolls, {dep_roll.path: dep_roll})
def testClusterfuzzParserParseStacktrace(self): parser = ClusterfuzzParser() deps = {'src/': Dependency('src/', 'https://repo', '1')} stacktrace_string = textwrap.dedent(""" Blabla... ==1==ERROR: AddressSanitizer: stack-overflow on address 0x7ffec59ebec0 #0 0x7f5b944a37bb in a::aa(p* d) src/a.h:225 #1 0x7f5b9449a880 in b::bb(p* d) src/b.h:266:1 #2 0x7f5b9449a880 in c::cc(p* d) src/c.h:281 """) stacktrace = parser.Parse(stacktrace_string, deps, 'asan_job', SanitizerType.ADDRESS_SANITIZER) stack = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'a::aa(p* d)', 'a.h', 'src/a.h', [225]), StackFrame(1, 'src/', 'b::bb(p* d)', 'b.h', 'src/b.h', [266, 267]), StackFrame(2, 'src/', 'c::cc(p* d)', 'c.h', 'src/c.h', [281]) ]) expected_stacktrace = Stacktrace([stack], stack) self._VerifyTwoStacktracesEqual(stacktrace, expected_stacktrace)
def testToJson(self): """Tests ``ToJson`` method.""" analysis = MockCrashAnalysis() analysis.crashed_version = '50.0.1234.0' analysis.signature = 'sig' analysis.platform = 'win' analysis.stack_trace = 'stack trace' self.assertDictEqual( analysis.ToJson(), { 'signature': analysis.signature, 'platform': analysis.platform, 'stack_trace': analysis.stack_trace, 'client_id': 'mock_client', 'crash_identifiers': 'crash_identifiers' }) frame = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [3]) callstack = CallStack(0, [frame]) analysis.stack_trace = None analysis.stacktrace = Stacktrace([callstack], callstack) self.assertDictEqual( analysis.ToJson(), { 'signature': analysis.signature, 'platform': analysis.platform, 'stack_trace': analysis.stacktrace.ToString(), 'client_id': 'mock_client', 'crash_identifiers': 'crash_identifiers' })
def testClassifyJavaCallstack(self): """Tests ``ClassifyCallStack`` classify java callstack.""" callstack = CallStack( 0, [StackFrame(0, 'src/', 'org.chromium.ab', 'f.java', 'unknown/f.java', [32])], language_type=LanguageType.JAVA) self.assertEqual( self.classifier.ClassifyCallStack(callstack), 'chromium')
def testParseStacktraceSucceeded(self): """Tests parsing ``stacktrace``.""" crash_data = ClusterfuzzData(self.GetDummyClusterfuzzData()) stack = CallStack(0) stacktrace = Stacktrace([stack], stack) with mock.patch('analysis.clusterfuzz_parser.ClusterfuzzParser.Parse' ) as mock_parse: mock_parse.return_value = stacktrace self._VerifyTwoStacktracesEqual(crash_data.stacktrace, stacktrace)
def testDoNotIndexFramesWithNoneCrashedGroup(self): """Tests ``IndexFramesWithCrashedGroup`` function.""" frame = StackFrame(0, 'src/', 'func', '', '', [2], 'h://repo') stack = CallStack(0, frame_list=[frame]) stack_trace = Stacktrace([stack], stack) deps = {'src/': Dependency('src/', 'h://repo', 'rev3')} indexed_frame_infos = crash_util.IndexFramesWithCrashedGroup( stack_trace, Factory, deps) self.assertEqual(indexed_frame_infos, {})
def testClassifySuspects(self): dummy_classifier = DummyClassifier() suspect = Suspect(self.GetDummyChangeLog(), 'src/') suspect.file_to_stack_infos = { 'f0.cc': [(StackFrame(0, 'src/', 'a::c(p* &d)', 'f0.cc', 'src/f0.cc', [177]), 0)] } self.assertEqual(dummy_classifier.Classify([suspect], CallStack(0)), 'class_3')
def testParseStacktraceSucceeded(self, mock_get_dependency): """Tests parsing ``stacktrace``.""" mock_get_dependency.return_value = {} crash_data = CracasCrashData( self.GetDummyChromeCrashData(), ChromeDependencyFetcher(self.GetMockRepoFactory())) stack = CallStack(0) stacktrace = Stacktrace([stack], stack) with mock.patch('analysis.chromecrash_parser.CracasCrashParser.Parse' ) as mock_parse: mock_parse.return_value = stacktrace self._VerifyTwoStacktracesEqual(crash_data.stacktrace, stacktrace)
def setUp(self): super(TouchCrashedDirectoryBaseFeatureTest, self).setUp() frame1 = StackFrame(0, 'src/', 'func', 'p/f.cc', 'src/p/f.cc', [2, 3], 'h://repo') stack = CallStack(0, frame_list=[frame1]) stack_trace = Stacktrace([stack], stack) deps = {'src/': Dependency('src/', 'h://repo', '8')} dep_rolls = {'src/': DependencyRoll('src/', 'h://repo', '2', '6')} self._report = CrashReport('8', 'sig', 'linux', stack_trace, ('2', '6'), deps, dep_rolls) self._feature = TouchCrashedDirectoryFeature()
def testToCallStackForNonEmptyCallStackBuffer(self): """Tests ``ToCallStack`` for non empty ``CallStackBuffer`` object.""" frame_list = [ StackFrame(0, 'repo/path', 'func', 'a/c.cc', 'a/c.cc', [3, 4], 'https://repo') ] stack_buffer = CallStackBuffer(0, frame_list=frame_list) expected_callstack = CallStack(stack_buffer.priority, tuple(frame_list), CallStackFormatType.DEFAULT, LanguageType.CPP) self.assertTupleEqual(stack_buffer.ToCallStack(), expected_callstack)
def testFeatureValueIsZeroWhenNoMatchedComponent(self): """Test that the feature returns 0 when there no matched component.""" frame = StackFrame(0, 'src/', 'func', 'dir/f.cc', 'src/dir/f.cc', [2, 3], 'h://repo') stack = CallStack(0, frame_list=[frame]) stack_trace = Stacktrace([stack], stack) deps = {'src/': Dependency('src/', 'h://repo', '8')} dep_rolls = {'src/': DependencyRoll('src/', 'h://repo', '2', '6')} report = CrashReport('8', 'sig', 'linux', stack_trace, ('2', '6'), deps, dep_rolls) suspect = Suspect(_DUMMY_CHANGELOG, 'src/') feature_value = self.feature(report)(suspect) self.assertEqual(0.0, feature_value.value)
def testDependencies(self): """Tests that ``dependencies`` returns filtered ``_CrashedVersionDeps``.""" crash_data = ChromeCrashData(self.GetDummyChromeCrashData(), None) chromium_dep = Dependency('src/', 'https://repo', 'rev1') crash_data._crashed_version_deps = { chromium_dep.path: chromium_dep, 'src/dummy': Dependency('src/dummy', 'https://r', 'rev2')} stack = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [5])]) stacktrace = Stacktrace([stack], stack) crash_data._stacktrace = stacktrace self.assertEqual(crash_data.dependencies, {chromium_dep.path: chromium_dep})
def testFilterFramesBeforeSubtree(self): """Tests that the frames before the subtree root are filtered out.""" parser = UMASamplingProfilerParser() frame1 = { 'difference': 0, 'log_change_factor': 0, 'responsible': False } frame2 = { 'difference': 0.1, 'log_change_factor': 0.1, 'responsible': True } subtree_root_depth = 2 subtree_stacks = [ # In this case the root is the first ``frame2`` instance. { 'frames': [frame1, frame1, frame2, frame2] }, { 'frames': [frame1, frame1, frame2, frame2, frame2] }, ] deps = {'chrome': Dependency('chrome', 'https://repo', '1')} stacktrace = parser.Parse(subtree_stacks, subtree_root_depth, deps) filtered_stacks = ( CallStack(0, [ ProfilerStackFrame(2, 0.1, 0.1, True), ProfilerStackFrame(3, 0.1, 0.1, True), ]), CallStack(0, [ ProfilerStackFrame(2, 0.1, 0.1, True), ProfilerStackFrame(3, 0.1, 0.1, True), ProfilerStackFrame(4, 0.1, 0.1, True), ]), ) self.assertEqual(stacktrace.stacks, filtered_stacks)
def testIndexFramesWithCrashedGroupWhenFrameHasNoDepPath(self): """Tests a bug with ``IndexFramesWithCrashedGroup``. This function would crash when passed a frame with a ``None`` dep path. Instead it should ignore this frame. """ frame = ProfilerStackFrame(0, 0.1, 0.5, True, dep_path=None) stack = CallStack(0, frame_list=[frame]) stack_trace = Stacktrace([stack], stack) deps = {'src': Dependency('src', 'h://repo', 'rev3')} indexed_frame_infos = crash_util.IndexFramesWithCrashedGroup( stack_trace, Factory, deps) self.assertDictEqual(indexed_frame_infos, {})
def testFeatureValueIsOneWhenThereIsMatchedComponent(self): """Test that feature value is 1 when there no matched component.""" # One dummy component in config is ['src/comp1.*', '', 'Comp1>Dummy']. frame1 = StackFrame(0, 'src/', 'func', 'comp1/f.cc', 'src/comp1/f.cc', [2, 3], 'h://repo') stack = CallStack(0, frame_list=[frame1]) stack_trace = Stacktrace([stack], stack) deps = {'src/': Dependency('src/', 'h://repo', '8')} dep_rolls = {'src/': DependencyRoll('src/', 'h://repo', '2', '6')} report = CrashReport('8', 'sig', 'linux', stack_trace, ('2', '6'), deps, dep_rolls) suspect = Suspect(_DUMMY_CHANGELOG, 'src/') feature_value = self.feature(report)(suspect) self.assertEqual(1.0, feature_value.value)
def testDisplayAnanlysisResultWithParsedStactrace(self): analysis = self.analysis frame = StackFrame(0, 'src/', 'func', 'file0.cc', 'src/file0.cc', [32]) stack = CallStack(0, [frame]) stacktrace = Stacktrace([stack], stack) analysis.stacktrace = stacktrace analysis.stack_trace = None expected_result = self._GenerateDisplayData(analysis) response_json = self.test_app.get('/result-feedback?format=json&' 'key=%s' % self.analysis.key.urlsafe()) self.assertEqual(200, response_json.status_int) self.assertEqual(expected_result, response_json.json_body)
def testDependencies(self, mock_get_dependencies, mock_stacktrace): """Tests that ``dependencies`` calls GetDependencies.""" crash_data = ChromeCrashData(self.GetDummyChromeCrashData(), None) crashed_version_deps = { 'src/': Dependency('src/', 'https://repo', 'rev') } mock_get_dependencies.return_value = crashed_version_deps stack = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [5]) ]) stacktrace = Stacktrace([stack], stack) mock_stacktrace.return_value = stacktrace self.assertEqual(crash_data.dependencies, crashed_version_deps) mock_get_dependencies.assert_called_with( [crash_data.stacktrace.crash_stack])
def testDependencyRolls(self, mock_get_dependency_rolls, mock_stacktrace): """Tests that ``dependency_rolls`` calls GetDependencyRolls.""" crash_data = ChromeCrashData(self.GetDummyChromeCrashData(), None) dep_roll = { 'src/': DependencyRoll('src/', 'https://repo', 'rev0', 'rev3') } mock_get_dependency_rolls.return_value = dep_roll stack = CallStack(0, frame_list=[ StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [5]) ]) stacktrace = Stacktrace([stack], stack) mock_stacktrace.return_value = stacktrace self.assertEqual(crash_data.dependency_rolls, dep_roll) mock_get_dependency_rolls.assert_called_with( [crash_data.stacktrace.crash_stack])
def testFeatureValueIsOneWhenThereIsMatchedDirectory(self): """Test that feature value is 1 when there is matched directory.""" frame1 = StackFrame(0, 'src/', 'func', 'p/f.cc', 'src/p/f.cc', [2, 3], 'h://repo') stack = CallStack(0, frame_list=[frame1]) stack_trace = Stacktrace([stack], stack) deps = {'src/': Dependency('src/', 'h://repo', '8')} dep_rolls = {'src/': DependencyRoll('src/', 'h://repo', '2', '6')} report = CrashReport('8', 'sig', 'linux', stack_trace, ('2', '6'), deps, dep_rolls) changelog = self.GetDummyChangeLog()._replace(touched_files=[ FileChangeInfo.FromDict({ 'change_type': 'add', 'new_path': 'p/a.cc', 'old_path': None, }) ]) suspect = Suspect(changelog, 'src/') feature_value = self._feature(report)(suspect) self.assertEqual(1.0, feature_value.value)