OLD | NEW |
---|---|
(Empty) | |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import unittest | |
6 | |
7 from common.diff import ChangeType | |
8 from waterfall import build_failure_analysis | |
9 from waterfall.failure_signal import FailureSignal | |
10 | |
11 | |
12 class BuildFailureAnalysisTest(unittest.TestCase): | |
13 | |
14 def testIsSameFile(self): | |
15 self.assertTrue(build_failure_analysis.IsSameFile('a/b/x.cc', 'x.cc')) | |
16 self.assertFalse(build_failure_analysis.IsSameFile('a/b/pre_x.cc.', 'x.cc')) | |
17 self.assertFalse(build_failure_analysis.IsSameFile('a/b/x.cc.', 'a/b/y.cc')) | |
18 | |
19 def testJoinAsFilePath(self): | |
20 self.assertEqual('a/x.cc', | |
21 build_failure_analysis.JoinAsFilePath('a', 'x.cc')) | |
22 self.assertEqual('x.cc', | |
23 build_failure_analysis.JoinAsFilePath(None, 'x.cc')) | |
24 | |
25 def testNormalizeObjectFile(self): | |
26 cases = { | |
27 'obj/a/T.x.o': 'a/x.o', | |
28 'obj/a/T.x.y.o': 'a/x.y.o', | |
29 'x.o': 'x.o' | |
30 } | |
31 for obj_file, expected_file in cases.iteritems(): | |
32 self.assertEqual( | |
33 expected_file, | |
34 build_failure_analysis.NormalizeObjectFile(obj_file)) | |
35 | |
36 def testStripCommonPostfix(self): | |
37 cases = { | |
38 'a_file': | |
39 'a_file_%s.cc' % '_'.join(build_failure_analysis.COMMON_POSTFIXES), | |
40 'src/b_file': 'src/b_file_impl_mac.h', | |
41 'c_file': 'c_file_browsertest.cc' | |
42 } | |
43 for expected_file, file_path in cases.iteritems(): | |
44 self.assertEqual( | |
45 expected_file, | |
46 build_failure_analysis.StripExtensionAndCommonPostfix(file_path)) | |
47 | |
48 def testIsCorrelated(self): | |
49 self.assertTrue(build_failure_analysis.IsCorrelated('a.h', 'a_test.py')) | |
50 self.assertTrue(build_failure_analysis.IsCorrelated('a.h', 'a_impl_test.o')) | |
51 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
| |
52 | |
53 def testCheckFilesAgainstSuspectedCL(self): | |
54 failure_signal_json = { | |
55 'files': { | |
56 'src/a/b/f1.cc': [], | |
57 'b/c/f2.h': [10, 20], | |
58 'd/e/f3_test.cc': [], | |
59 'x/y/f4.py': [], | |
60 'f5_impl.cc': [] | |
61 } | |
62 } | |
63 change_log_json = { | |
64 'touched_files': [ | |
65 { | |
66 'change_type': ChangeType.ADD, | |
67 'old_path': '/dev/null', | |
68 'new_path': 'a/b/f1.cc' | |
69 }, | |
70 { | |
71 'change_type': ChangeType.MODIFY, | |
72 'old_path': 'a/b/c/f2.h', | |
73 'new_path': 'a/b/c/f2.h' | |
74 }, | |
75 { | |
76 'change_type': ChangeType.MODIFY, | |
77 'old_path': 'd/e/f3.h', | |
78 'new_path': 'd/e/f3.h' | |
79 }, | |
80 { | |
81 'change_type': ChangeType.DELETE, | |
82 'old_path': 'x/y/f4.py', | |
83 'new_path': '/dev/null' | |
84 }, | |
85 { | |
86 'change_type': ChangeType.DELETE, | |
87 'old_path': 'h/f5.h', | |
88 'new_path': '/dev/null' | |
89 }, | |
90 { | |
91 'change_type': ChangeType.RENAME, | |
92 'old_path': 't/y/x.cc', | |
93 'new_path': 's/z/x.cc' | |
94 }, | |
95 ] | |
96 } | |
97 | |
98 justification = build_failure_analysis.CheckFiles( | |
99 FailureSignal.FromJson(failure_signal_json), change_log_json) | |
100 self.assertIsNotNone(justification) | |
101 self.assertEqual(2, justification['suspects']) | |
102 self.assertEqual(13, justification['scores']) | |
103 | |
104 def testCheckFilesAgainstUnrelatedCL(self): | |
105 failure_signal_json = { | |
106 'files': { | |
107 'src/a/b/f.cc': [], | |
108 } | |
109 } | |
110 change_log_json = { | |
111 'touched_files': [ | |
112 { | |
113 'change_type': ChangeType.ADD, | |
114 'old_path': '/dev/null', | |
115 'new_path': 'a/d/f1.cc' | |
116 }, | |
117 ] | |
118 } | |
119 | |
120 justification = build_failure_analysis.CheckFiles( | |
121 FailureSignal.FromJson(failure_signal_json), change_log_json) | |
122 self.assertIsNone(justification) | |
123 | |
124 def testAnalyzeSuccesfulBuild(self): | |
125 failure_info = { | |
126 'failed': False, | |
127 } | |
128 result = build_failure_analysis.AnalyzeBuildFailure( | |
129 failure_info, None, None) | |
130 self.assertEqual(0, len(result)) | |
131 | |
132 def testAnalyzeBuildFailure(self): | |
133 failure_info = { | |
134 'failed': True, | |
135 'failed_steps': { | |
136 'a': { | |
137 'current_failure': 99, | |
138 'first_failure': 98, | |
139 }, | |
140 'b': { | |
141 'current_failure': 99, | |
142 'first_failure': 98, | |
143 }, | |
144 }, | |
145 'builds': { | |
146 '99': { | |
147 'blame_list': ['r99_1', 'r99_2'], | |
148 }, | |
149 '98': { | |
150 'blame_list': ['r98_1'], | |
151 }, | |
152 } | |
153 } | |
154 change_logs = { | |
155 'r99_1': { | |
156 'touched_files': [ | |
157 { | |
158 'change_type': ChangeType.ADD, | |
159 'old_path': '/dev/null', | |
160 'new_path': 'x/y/f99_1.cc' | |
161 }, | |
162 ], | |
163 }, | |
164 'r99_2': { | |
165 'touched_files': [ | |
166 { | |
167 'change_type': ChangeType.MODIFY, | |
168 'old_path': 'a/b/f99_2.cc', | |
169 'new_path': 'a/b/f99_2.cc' | |
170 }, | |
171 ], | |
172 }, | |
173 'r98_1': { | |
174 'touched_files': [ | |
175 { | |
176 'change_type': ChangeType.MODIFY, | |
177 'old_path': 'y/z/f98.cc', | |
178 'new_path': 'y/z/f98.cc' | |
179 }, | |
180 ], | |
181 }, | |
182 } | |
183 failure_signals_json = { | |
184 'a': { | |
185 'files': { | |
186 'src/a/b/f99_2.cc': [], | |
187 }, | |
188 }, | |
189 'b': { | |
190 'files': { | |
191 'f.cc': [], | |
192 }, | |
193 }, | |
194 } | |
195 expected_analysis_result = { | |
196 'a': { | |
197 'r99_2': { | |
198 'suspects': 0, | |
199 'scores': 1, | |
200 'hints': [ | |
201 'modify a/b/f99_2.cc', | |
202 ] | |
203 }, | |
204 }, | |
205 } | |
206 | |
207 analysis_result = build_failure_analysis.AnalyzeBuildFailure( | |
208 failure_info, change_logs, failure_signals_json) | |
209 self.assertEqual(expected_analysis_result, analysis_result) | |
OLD | NEW |