Chromium Code Reviews| Index: appengine/findit/waterfall/test/build_failure_analysis_test.py |
| diff --git a/appengine/findit/waterfall/test/build_failure_analysis_test.py b/appengine/findit/waterfall/test/build_failure_analysis_test.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..392b1a910b23c52f6c609134e4acb9f26bb0a151 |
| --- /dev/null |
| +++ b/appengine/findit/waterfall/test/build_failure_analysis_test.py |
| @@ -0,0 +1,209 @@ |
| +# Copyright 2015 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +import unittest |
| + |
| +from common.diff import ChangeType |
| +from waterfall import build_failure_analysis |
| +from waterfall.failure_signal import FailureSignal |
| + |
| + |
| +class BuildFailureAnalysisTest(unittest.TestCase): |
| + |
| + def testIsSameFile(self): |
| + self.assertTrue(build_failure_analysis.IsSameFile('a/b/x.cc', 'x.cc')) |
| + self.assertFalse(build_failure_analysis.IsSameFile('a/b/pre_x.cc.', 'x.cc')) |
| + self.assertFalse(build_failure_analysis.IsSameFile('a/b/x.cc.', 'a/b/y.cc')) |
| + |
| + def testJoinAsFilePath(self): |
| + self.assertEqual('a/x.cc', |
| + build_failure_analysis.JoinAsFilePath('a', 'x.cc')) |
| + self.assertEqual('x.cc', |
| + build_failure_analysis.JoinAsFilePath(None, 'x.cc')) |
| + |
| + def testNormalizeObjectFile(self): |
| + cases = { |
| + 'obj/a/T.x.o': 'a/x.o', |
| + 'obj/a/T.x.y.o': 'a/x.y.o', |
| + 'x.o': 'x.o' |
| + } |
| + for obj_file, expected_file in cases.iteritems(): |
| + self.assertEqual( |
| + expected_file, |
| + build_failure_analysis.NormalizeObjectFile(obj_file)) |
| + |
| + def testStripCommonPostfix(self): |
| + cases = { |
| + 'a_file': |
| + 'a_file_%s.cc' % '_'.join(build_failure_analysis.COMMON_POSTFIXES), |
| + 'src/b_file': 'src/b_file_impl_mac.h', |
| + 'c_file': 'c_file_browsertest.cc' |
| + } |
| + for expected_file, file_path in cases.iteritems(): |
| + self.assertEqual( |
| + expected_file, |
| + build_failure_analysis.StripExtensionAndCommonPostfix(file_path)) |
| + |
| + def testIsCorrelated(self): |
| + self.assertTrue(build_failure_analysis.IsCorrelated('a.h', 'a_test.py')) |
| + self.assertTrue(build_failure_analysis.IsCorrelated('a.h', 'a_impl_test.o')) |
| + self.assertTrue(build_failure_analysis.IsCorrelated('a/b/x.cc', 'a/b/y.cc')) |
|
qyearsley
2015/01/15 21:15:03
Maybe add a negative case -- when is IsCorrelated
stgao
2015/01/16 20:21:39
Negative case added.
If a CL changed file a.h, an
|
| + |
| + def testCheckFilesAgainstSuspectedCL(self): |
| + failure_signal_json = { |
| + 'files': { |
| + 'src/a/b/f1.cc': [], |
| + 'b/c/f2.h': [10, 20], |
| + 'd/e/f3_test.cc': [], |
| + 'x/y/f4.py': [], |
| + 'f5_impl.cc': [] |
| + } |
| + } |
| + change_log_json = { |
| + 'touched_files': [ |
| + { |
| + 'change_type': ChangeType.ADD, |
| + 'old_path': '/dev/null', |
| + 'new_path': 'a/b/f1.cc' |
| + }, |
| + { |
| + 'change_type': ChangeType.MODIFY, |
| + 'old_path': 'a/b/c/f2.h', |
| + 'new_path': 'a/b/c/f2.h' |
| + }, |
| + { |
| + 'change_type': ChangeType.MODIFY, |
| + 'old_path': 'd/e/f3.h', |
| + 'new_path': 'd/e/f3.h' |
| + }, |
| + { |
| + 'change_type': ChangeType.DELETE, |
| + 'old_path': 'x/y/f4.py', |
| + 'new_path': '/dev/null' |
| + }, |
| + { |
| + 'change_type': ChangeType.DELETE, |
| + 'old_path': 'h/f5.h', |
| + 'new_path': '/dev/null' |
| + }, |
| + { |
| + 'change_type': ChangeType.RENAME, |
| + 'old_path': 't/y/x.cc', |
| + 'new_path': 's/z/x.cc' |
| + }, |
| + ] |
| + } |
| + |
| + justification = build_failure_analysis.CheckFiles( |
| + FailureSignal.FromJson(failure_signal_json), change_log_json) |
| + self.assertIsNotNone(justification) |
| + self.assertEqual(2, justification['suspects']) |
| + self.assertEqual(13, justification['scores']) |
| + |
| + def testCheckFilesAgainstUnrelatedCL(self): |
| + failure_signal_json = { |
| + 'files': { |
| + 'src/a/b/f.cc': [], |
| + } |
| + } |
| + change_log_json = { |
| + 'touched_files': [ |
| + { |
| + 'change_type': ChangeType.ADD, |
| + 'old_path': '/dev/null', |
| + 'new_path': 'a/d/f1.cc' |
| + }, |
| + ] |
| + } |
| + |
| + justification = build_failure_analysis.CheckFiles( |
| + FailureSignal.FromJson(failure_signal_json), change_log_json) |
| + self.assertIsNone(justification) |
| + |
| + def testAnalyzeSuccesfulBuild(self): |
| + failure_info = { |
| + 'failed': False, |
| + } |
| + result = build_failure_analysis.AnalyzeBuildFailure( |
| + failure_info, None, None) |
| + self.assertEqual(0, len(result)) |
| + |
| + def testAnalyzeBuildFailure(self): |
| + failure_info = { |
| + 'failed': True, |
| + 'failed_steps': { |
| + 'a': { |
| + 'current_failure': 99, |
| + 'first_failure': 98, |
| + }, |
| + 'b': { |
| + 'current_failure': 99, |
| + 'first_failure': 98, |
| + }, |
| + }, |
| + 'builds': { |
| + '99': { |
| + 'blame_list': ['r99_1', 'r99_2'], |
| + }, |
| + '98': { |
| + 'blame_list': ['r98_1'], |
| + }, |
| + } |
| + } |
| + change_logs = { |
| + 'r99_1': { |
| + 'touched_files': [ |
| + { |
| + 'change_type': ChangeType.ADD, |
| + 'old_path': '/dev/null', |
| + 'new_path': 'x/y/f99_1.cc' |
| + }, |
| + ], |
| + }, |
| + 'r99_2': { |
| + 'touched_files': [ |
| + { |
| + 'change_type': ChangeType.MODIFY, |
| + 'old_path': 'a/b/f99_2.cc', |
| + 'new_path': 'a/b/f99_2.cc' |
| + }, |
| + ], |
| + }, |
| + 'r98_1': { |
| + 'touched_files': [ |
| + { |
| + 'change_type': ChangeType.MODIFY, |
| + 'old_path': 'y/z/f98.cc', |
| + 'new_path': 'y/z/f98.cc' |
| + }, |
| + ], |
| + }, |
| + } |
| + failure_signals_json = { |
| + 'a': { |
| + 'files': { |
| + 'src/a/b/f99_2.cc': [], |
| + }, |
| + }, |
| + 'b': { |
| + 'files': { |
| + 'f.cc': [], |
| + }, |
| + }, |
| + } |
| + expected_analysis_result = { |
| + 'a': { |
| + 'r99_2': { |
| + 'suspects': 0, |
| + 'scores': 1, |
| + 'hints': [ |
| + 'modify a/b/f99_2.cc', |
| + ] |
| + }, |
| + }, |
| + } |
| + |
| + analysis_result = build_failure_analysis.AnalyzeBuildFailure( |
| + failure_info, change_logs, failure_signals_json) |
| + self.assertEqual(expected_analysis_result, analysis_result) |