Coverage for CIResults/tests/test_runconfigdiff.py: 100%
282 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-19 09:20 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-19 09:20 +0000
1from unittest.mock import patch, Mock, MagicMock, PropertyMock
2from django.test import TestCase
3from django.utils import timezone
4from datetime import timedelta
6from CIResults.models import Component, Build, Test, Machine, RunConfig, TestSuite
7from CIResults.models import TextStatus, TestResult
8from CIResults.runconfigdiff import RunConfigResultsForTest, RunConfigResultsForTestDiff
9from CIResults.runconfigdiff import RunConfigResultsForNotRunTest, ExecutionTime
12class ExecutionTimeTests(TestCase):
13 def test_empty(self):
14 empty = ExecutionTime()
16 self.assertEqual(empty.minimum, None)
17 self.assertEqual(empty.maximum, None)
18 self.assertEqual(empty.count, 0)
20 def test_add__normal_case(self):
21 val = ExecutionTime(23) + ExecutionTime(24)
23 self.assertEqual(val.minimum, 23)
24 self.assertEqual(val.maximum, 24)
25 self.assertEqual(str(val), "[23, 24] s")
27 def test_add__with_empty(self):
28 val = ExecutionTime() + ExecutionTime(23) + ExecutionTime()
29 self.assertEqual(val, ExecutionTime(23))
30 self.assertEqual(str(val), "[23] s")
32 def test_equal(self):
33 # Check that arriving at the same result twice leads to values being equal
34 val1 = ExecutionTime(23) + ExecutionTime(24)
35 val2 = ExecutionTime(23) + ExecutionTime(24)
36 self.assertEqual(val1, val2)
38 # Check that re-adding a value indeed does not result in the results being equal
39 val2 += ExecutionTime(23)
40 self.assertNotEqual(val1, val2)
42 def test_str__with_timedelta(self):
43 self.assertEqual(str(ExecutionTime(timedelta(seconds=0.987654321))), "[0.99] s")
44 self.assertEqual(str(ExecutionTime(timedelta(seconds=12345.987654321))), "[12345.99] s")
47class RunConfigResultsForTestTests(TestCase):
48 def __create_testsuite(self, name, statuses, acceptable_statuses=['pass'], public=True):
49 testsuite = TestSuite.objects.create(name=name, description="nothing", public=public)
51 db_statuses = dict()
52 for status_name in statuses:
53 status = TextStatus.objects.create(testsuite=testsuite, name=status_name,
54 vetted_on=timezone.now())
55 db_statuses[status_name] = status
57 for status_name in acceptable_statuses:
58 testsuite.acceptable_statuses.add(db_statuses[status_name])
60 return testsuite, db_statuses
62 def test_RunConfigResultsForTests_check_no_results(self):
63 self.assertRaisesMessage(ValueError, "No results provided", RunConfigResultsForTest, [])
65 def test_RunConfigResultsForTests_check_single_result(self):
66 self.testsuite1, self.testsuite1_statuses = self.__create_testsuite("testsuite1",
67 ['pass', 'fail'],
68 ['pass'],
69 public=True)
70 self.testsuite1_statuses["fail"].suppress()
72 test = Test.objects.create(name="Test", testsuite=self.testsuite1, public=True)
73 result_pass = TestResult(id=1, test=test, status=self.testsuite1_statuses['pass'])
74 result_fail = TestResult(id=2, test=test, status=self.testsuite1_statuses['fail'])
76 r = RunConfigResultsForTest([result_pass])
77 self.assertTrue(r.was_run)
78 self.assertEqual(str(r), "pass")
79 self.assertEqual(r, RunConfigResultsForTest([result_pass]))
80 self.assertNotEqual(r, RunConfigResultsForTest([result_fail]))
82 # Now try with a suppressed status
83 result_fail = TestResult(test=test, status=self.testsuite1_statuses['fail'])
84 r = RunConfigResultsForTest([result_fail])
85 self.assertEqual(str(r), "{fail}")
87 def test_RunConfigResultsForTests_check_results_from_different_testsuites(self):
88 self.testsuite1, self.testsuite1_statuses = self.__create_testsuite("testsuite1",
89 ['pass'], [],
90 public=True)
91 self.testsuite2, self.testsuite2_statuses = self.__create_testsuite("testsuite2",
92 ['pass'], [],
93 public=True)
95 test1 = Test.objects.create(name="Test", testsuite=self.testsuite1, public=True)
96 test2 = Test.objects.create(name="Test", testsuite=self.testsuite2, public=True)
98 result_pass_ts1 = TestResult(test=test1, status=self.testsuite1_statuses['pass'])
99 result_pass_ts2 = TestResult(test=test2, status=self.testsuite2_statuses['pass'])
101 self.assertRaisesMessage(ValueError, "Results from multiple tests",
102 RunConfigResultsForTest, [result_pass_ts1, result_pass_ts2])
104 def test_RunConfigResultsForTests_check_two_results_same_status(self):
105 self.testsuite1, self.testsuite1_statuses = self.__create_testsuite("testsuite1",
106 ['pass', 'fail'], [],
107 public=True)
108 self.testsuite1_statuses["fail"].suppress()
110 test = Test.objects.create(name="Test", testsuite=self.testsuite1, public=True)
111 result_pass1 = TestResult(id=1, test=test, status=self.testsuite1_statuses['pass'])
112 result_pass2 = TestResult(id=2, test=test, status=self.testsuite1_statuses['pass'])
113 result_fail = TestResult(id=3, test=test, status=self.testsuite1_statuses['fail'])
115 r = RunConfigResultsForTest([result_pass1, result_pass2])
116 self.assertEqual(str(r), "( 2 pass )")
117 self.assertEqual(r, RunConfigResultsForTest([result_pass1, result_pass2]))
118 self.assertNotEqual(r, RunConfigResultsForTest([result_pass1, result_fail]))
120 # Now try with a suppressed status
121 result_fail1 = TestResult(test=test, status=self.testsuite1_statuses['fail'])
122 result_fail2 = TestResult(test=test, status=self.testsuite1_statuses['fail'])
123 r = RunConfigResultsForTest([result_fail1, result_fail2])
124 self.assertEqual(str(r), "( 2 {fail} )")
126 def test_RunConfigResultsForTests_check_same_statuses_but_different_bugs(self):
127 self.testsuite1, self.testsuite1_statuses = self.__create_testsuite("testsuite1",
128 ['pass', 'fail'], [],
129 public=True)
130 self.testsuite1_statuses["fail"].suppress()
132 test = Test.objects.create(name="Test", testsuite=self.testsuite1, public=True)
133 result_pass = TestResult(test=test, status=self.testsuite1_statuses['pass'])
134 result_fail = TestResult(test=test, status=self.testsuite1_statuses['fail'])
136 r1 = RunConfigResultsForTest([result_fail, result_pass])
137 r1.bugs_covering = set(['bug1', 'bug2'])
139 r2 = RunConfigResultsForTest([result_pass, result_fail])
140 r2.bugs_covering = set(['bug1', 'bug2'])
142 self.assertEqual(r1, r2)
144 r2.bugs_covering = set(['bug1', 'bug3'])
145 self.assertNotEqual(r1, r2)
147 def test_RunConfigResultsForTests_check_two_results_different_status(self):
148 self.testsuite1, self.testsuite1_statuses = self.__create_testsuite("testsuite1",
149 ['pass', 'fail'], [],
150 public=True)
151 self.testsuite1_statuses["fail"].suppress()
153 test = Test.objects.create(name="Test", testsuite=self.testsuite1, public=True)
154 result_pass1 = TestResult(id=1, test=test, status=self.testsuite1_statuses['pass'])
155 result_pass2 = TestResult(id=2, test=test, status=self.testsuite1_statuses['pass'])
156 result_fail = TestResult(id=3, test=test, status=self.testsuite1_statuses['fail'])
158 r = RunConfigResultsForTest([result_pass1, result_fail, result_pass2])
159 self.assertEqual(str(r), "( 1 {fail}, 2 pass )")
160 self.assertEqual(r, RunConfigResultsForTest([result_pass1, result_pass2, result_fail]))
161 self.assertEqual(r, RunConfigResultsForTest([result_pass1, result_fail]))
163 def test_RunConfigResultsForTests_check_not_runs_ignored(self):
164 self.testsuite1, self.testsuite1_statuses = self.__create_testsuite("testsuite1",
165 ['pass', 'fail', 'notrun'],
166 [], public=True)
167 self.testsuite1.notrun_status = self.testsuite1_statuses['notrun']
168 self.testsuite1.save()
170 test = Test.objects.create(name="Test", testsuite=self.testsuite1, public=True)
171 result_pass = TestResult(id=1, test=test, status=self.testsuite1_statuses['pass'])
172 result_notrun = TestResult(id=2, test=test, status=self.testsuite1_statuses['notrun'])
173 result_fail = TestResult(id=3, test=test, status=self.testsuite1_statuses['fail'])
175 r = RunConfigResultsForTest([result_pass, result_notrun, result_fail])
176 self.assertEqual(str(r), "( 1 fail, 1 pass )")
177 self.assertEqual(r, RunConfigResultsForTest([result_pass, result_fail]))
179 def test_RunConfigResultsForTests_check_only_not_runs(self):
180 self.testsuite1, self.testsuite1_statuses = self.__create_testsuite("testsuite1",
181 ['pass', 'fail', 'notrun'],
182 [], public=True)
183 self.testsuite1.notrun_status = self.testsuite1_statuses['notrun']
184 self.testsuite1.save()
186 test = Test.objects.create(name="Test", testsuite=self.testsuite1, public=True)
187 result_notrun1 = TestResult(test=test, status=self.testsuite1_statuses['notrun'])
188 result_notrun2 = TestResult(test=test, status=self.testsuite1_statuses['notrun'])
189 result_notrun3 = TestResult(test=test, status=self.testsuite1_statuses['notrun'])
191 self.assertRaisesMessage(ValueError, "No results provided",
192 RunConfigResultsForTest, [result_notrun1, result_notrun2, result_notrun3])
194 def test_RunConfigResultsForTests_is_suppressed(self):
195 self.testsuite1, self.testsuite1_statuses = self.__create_testsuite("testsuite1",
196 ['pass', 'fail'], [],
197 public=True)
198 self.testsuite1.acceptable_statuses.add(self.testsuite1_statuses['pass'])
200 test = Test.objects.create(name="Test", testsuite=self.testsuite1, public=True)
201 result_pass1 = TestResult(test=test, status=self.testsuite1_statuses['pass'])
202 result_pass2 = TestResult(test=test, status=self.testsuite1_statuses['pass'])
203 result_fail = TestResult(test=test, status=self.testsuite1_statuses['fail'])
205 # Try without suppressing fails
206 r = RunConfigResultsForTest([result_pass1, result_fail, result_pass2])
207 self.assertFalse(r.is_suppressed)
209 # Try again, with fail suppressed
210 self.testsuite1_statuses["fail"].suppress()
211 r = RunConfigResultsForTest([result_pass1, result_fail, result_pass2])
212 self.assertTrue(r.is_suppressed)
215class RunConfigResultsForTestDiffTests(TestCase):
216 def setUp(self):
217 first_runconfig = RunConfig.objects.create(name="Runconfig", temporary=False)
219 self.testsuite = TestSuite.objects.create(name="testsuite", public=True,
220 vetted_on=timezone.now())
221 self.test = Test.objects.create(name="test1", testsuite=self.testsuite, public=True,
222 vetted_on=timezone.now(), first_runconfig=first_runconfig)
223 self.machine = Machine.objects.create(name="machine1", public=True,
224 vetted_on=timezone.now())
226 def test_is_fix(self):
227 result_from = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="FAIL"),
228 is_failure=True, bugs_covering=[], all_failures_covered=False, is_suppressed=False)
229 result_to = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="PASS"),
230 is_failure=False, bugs_covering=[], all_failures_covered=True, is_suppressed=False)
232 r = RunConfigResultsForTestDiff(test=self.test, testsuite=self.testsuite,
233 machine=self.machine, result_from=result_from,
234 result_to=result_to)
236 self.assertTrue(r.is_fix and r.is_known_change)
237 self.assertFalse(r.is_regression or r.is_warning or r.is_unknown_change or r.is_regression)
238 self.assertEqual(str(r), "machine1: FAIL -> PASS")
240 def test_is_regression(self):
241 result_from = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="PASS"),
242 is_failure=False, bugs_covering=[], all_failures_covered=True, is_suppressed=False)
243 result_to = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="FAIL"),
244 is_failure=True, bugs_covering=[], all_failures_covered=False, is_suppressed=False)
246 r = RunConfigResultsForTestDiff(test=self.test, testsuite=self.testsuite,
247 machine=self.machine, result_from=result_from,
248 result_to=result_to)
250 self.assertTrue(r.is_regression and r.is_unknown_change)
251 self.assertFalse(r.is_fix or r.is_warning or r.is_suppressed or r.is_known_change)
252 self.assertEqual(str(r), "machine1: PASS -> FAIL")
254 def test_is_warning(self):
255 result_from = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="FAIL"),
256 is_failure=True, bugs_covering=[], all_failures_covered=False, is_suppressed=False)
257 result_to = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="FAIL"),
258 is_failure=True, bugs_covering=[], all_failures_covered=False, is_suppressed=False)
260 r = RunConfigResultsForTestDiff(test=self.test, testsuite=self.testsuite,
261 machine=self.machine, result_from=result_from,
262 result_to=result_to)
264 self.assertTrue(r.is_warning and r.is_unknown_change)
265 self.assertFalse(r.is_fix or r.is_regression or r.is_regression or r.is_known_change)
266 self.assertEqual(str(r), "machine1: FAIL -> FAIL")
268 def test_is_suppressed(self):
269 result_from = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="PASS"),
270 is_failure=True, bugs_covering=[], all_failures_covered=False, is_suppressed=False)
271 result_to = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="{FAIL}"),
272 is_failure=True, bugs_covering=[], all_failures_covered=False, is_suppressed=True)
274 r = RunConfigResultsForTestDiff(test=self.test, testsuite=self.testsuite,
275 machine=self.machine, result_from=result_from,
276 result_to=result_to)
278 self.assertTrue(r.is_suppressed and r.is_unknown_change)
279 self.assertFalse(r.is_fix or r.is_regression or r.is_warning or r.is_known_change)
280 self.assertEqual(str(r), "machine1: PASS -> {FAIL}")
282 def test_is_known_change(self):
283 result_from = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="FAIL"),
284 is_failure=True, all_failures_covered=True, is_suppressed=False,
285 bugs_covering=[MagicMock(spec="Bug", short_name="fdo#1234"),
286 MagicMock(spec="Bug", short_name="fdo#1235")])
287 result_to = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="FAIL"),
288 is_failure=True, all_failures_covered=True, is_suppressed=False,
289 bugs_covering=[MagicMock(spec="Bug", short_name="fdo#1236"),
290 MagicMock(spec="Bug", short_name="fdo#1237")])
292 r = RunConfigResultsForTestDiff(test=self.test, testsuite=self.testsuite,
293 machine=self.machine, result_from=result_from,
294 result_to=result_to)
296 self.assertTrue(r.is_warning and r.is_known_change)
297 self.assertFalse(r.is_regression or r.is_fix or r.is_unknown_change)
298 self.assertEqual(str(r), "machine1: FAIL ([fdo#1234] / [fdo#1235]) -> FAIL ([fdo#1236] / [fdo#1237])")
300 def test_is_new_test(self):
301 result_from = RunConfigResultsForNotRunTest()
302 result_to = MagicMock(spec="RunConfigResultsForTest", __str__=MagicMock(return_value="FAIL"),
303 is_failure=True, bugs_covering=[], all_failures_covered=False, is_suppressed=False)
305 new_test = Test.objects.create(name="new_test", testsuite=self.testsuite, public=True,
306 vetted_on=timezone.now(), first_runconfig=None)
307 r = RunConfigResultsForTestDiff(test=new_test, testsuite=self.testsuite,
308 machine=self.machine, result_from=result_from,
309 result_to=result_to)
311 self.assertTrue(r.is_new_test and r.is_regression and r.is_unknown_change)
312 self.assertFalse(r.is_fix or r.is_warning or r.is_known_change)
313 self.assertEqual(str(r), "machine1: NOTRUN -> FAIL")
316class RunConfigDiffTests(TestCase):
317 def setUp(self):
318 self.runcfg_from = RunConfig.objects.create(name="runcfg_from", temporary=False)
319 self.runcfg_to = RunConfig.objects.create(name="runcfg_to", temporary=False)
321 self.component1 = Component.objects.create(name="component1", description="", public=True)
322 self.component2 = Component.objects.create(name="component2", description="", public=True)
323 self.component3 = Component.objects.create(name="component3", description="", public=True)
325 self.build_c1_1 = Build.objects.create(name="build_c1_1", component=self.component1, version="1")
326 self.build_c1_2 = Build.objects.create(name="build_c1_2", component=self.component1, version="2")
327 self.build_c2_1 = Build.objects.create(name="build_c2_1", component=self.component2, version="1")
328 self.build_c2_2 = Build.objects.create(name="build_c2_2", component=self.component2, version="2")
329 self.build_c3_1 = Build.objects.create(name="build_c3_1", component=self.component3, version="1")
331 self.machines = []
332 for i in range(10):
333 self.machines.append(Mock(spec=Machine, name='machine_{}'.format(i)))
335 def test_builds__simple(self):
336 self.runcfg_from.builds.add(self.build_c1_1, self.build_c2_1, self.build_c3_1)
337 self.runcfg_to.builds.add(self.build_c1_2, self.build_c2_2, self.build_c3_1)
339 diff = self.runcfg_from.compare(self.runcfg_to)
340 diff_builds = diff.builds
342 self.assertEqual(len(diff_builds), 2)
344 self.assertEqual(diff_builds[self.component1].from_build, self.build_c1_1)
345 self.assertEqual(diff_builds[self.component1].to_build, self.build_c1_2)
347 self.assertEqual(diff_builds[self.component2].from_build, self.build_c2_1)
348 self.assertEqual(diff_builds[self.component2].to_build, self.build_c2_2)
350 self.assertIn("component1: build_c1_1 -> build_c1_2", diff.text)
351 self.assertIn("component2: build_c2_1 -> build_c2_2", diff.text)
352 self.assertNotIn("component3", diff.text)
354 def test_builds__asymmetric(self):
355 self.runcfg_from.builds.add(self.build_c1_1)
356 self.runcfg_to.builds.add(self.build_c2_2)
358 diff = self.runcfg_from.compare(self.runcfg_to)
359 diff_builds = diff.builds
361 self.assertEqual(len(diff_builds), 2)
363 self.assertEqual(diff_builds[self.component1].from_build, self.build_c1_1)
364 self.assertEqual(diff_builds[self.component1].to_build, None)
366 self.assertEqual(diff_builds[self.component2].from_build, None)
367 self.assertEqual(diff_builds[self.component2].to_build, self.build_c2_2)
369 self.assertIn("component1: build_c1_1 -> None", diff.text)
370 self.assertIn("component2: None -> build_c2_2", diff.text)
371 self.assertNotIn("component3", diff.text)
373 def test_testsuites__no_results(self):
374 diff = self.runcfg_from.compare(self.runcfg_to)
376 self.assertIn("No changes found", diff.text)
378 @patch('CIResults.runconfigdiff.RunConfigDiff.runcfg_from_results', new_callable=PropertyMock)
379 @patch('CIResults.runconfigdiff.RunConfigDiff.runcfg_to_results', new_callable=PropertyMock)
380 def test_testsuites__all_combinaisons(self, mock_runcfg_to_results, mock_runcfg_from_results):
381 mock_runcfg_from_results.return_value = {"testsuite3": {}, "testsuite1": {}}
382 mock_runcfg_to_results.return_value = {"testsuite2": {}, "testsuite1": {}}
384 diff = self.runcfg_from.compare(self.runcfg_to)
386 self.assertEqual(diff.testsuites.runcfg_from, set(mock_runcfg_from_results.return_value.keys()))
387 self.assertEqual(diff.testsuites.runcfg_to, set(mock_runcfg_to_results.return_value.keys()))
388 self.assertEqual(diff.testsuites.new, set(["testsuite2"]))
389 self.assertEqual(diff.testsuites.removed, set(["testsuite3"]))
390 self.assertEqual(diff.testsuites.all, ["testsuite1", "testsuite2", "testsuite3"])
392 def test_has_sufficient_machines__no_machines(self):
393 diff = self.runcfg_from.compare(self.runcfg_to)
394 self.assertTrue(diff.has_sufficient_machines)
395 self.assertEqual(diff.status, "SUCCESS")
397 def test_has_sufficient_machines__same_machines(self):
398 m = self.machines
399 with patch('CIResults.runconfigdiff.RunConfigDiff._get_machine_list',
400 side_effect=[set([m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9]]),
401 set([m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9]])]):
402 diff = self.runcfg_from.compare(self.runcfg_to)
403 self.assertEqual(len(diff.machines.runcfg_from), 10)
404 self.assertEqual(len(diff.machines.removed), 0)
405 self.assertTrue(diff.has_sufficient_machines)
406 self.assertEqual(diff.status, "SUCCESS")
408 def test_has_sufficient_machines__at_threshold(self):
409 m = self.machines
411 with patch('CIResults.runconfigdiff.RunConfigDiff._get_machine_list',
412 side_effect=[set([m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9]]),
413 set([m[0], m[3], m[4], m[5], m[6], m[9]])]):
414 diff = self.runcfg_from.compare(self.runcfg_to, 0.4)
415 self.assertEqual(len(diff.machines.runcfg_from), 10)
416 self.assertEqual(len(diff.machines.removed), 4)
417 self.assertTrue(diff.has_sufficient_machines)
418 self.assertEqual(diff.status, "SUCCESS")
419 self.assertNotIn("prevented too many machines from booting", diff.text)
421 def test_has_sufficient_machines__under_threshold(self):
422 m = self.machines
423 with patch('CIResults.runconfigdiff.RunConfigDiff._get_machine_list',
424 side_effect=[set([m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9]]),
425 set([m[0], m[3], m[4], m[5], m[8]])]):
426 diff = self.runcfg_from.compare(self.runcfg_to, 0.4)
427 self.assertEqual(len(diff.machines.runcfg_from), 10)
428 self.assertEqual(len(diff.machines.removed), 5)
429 self.assertFalse(diff.has_sufficient_machines)
430 self.assertEqual(diff.status, "FAILURE")
431 self.assertIn("prevented too many machines from booting", diff.text)
433 def test_has_suppressed_results(self):
434 diff = self.runcfg_from.compare(self.runcfg_to, 0.4)
435 diff.results = [MagicMock(), MagicMock(), MagicMock()]
437 # Mock the return values of the is_suppressed property
438 p_true = PropertyMock(return_value=True)
439 p_false = PropertyMock(return_value=False)
440 type(diff.results[0]).is_suppressed = p_false
441 type(diff.results[1]).is_suppressed = p_true
442 type(diff.results[2]).is_suppressed = p_false
444 self.assertTrue(diff.has_suppressed_results)
446 p_true.assert_called_once_with()
447 p_false.assert_called_once_with()
449 # TODO: Test machines, bugs, status (with suppressed results)