Coverage for CIResults/tests/test_run_import.py: 100%
415 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, call, mock_open, MagicMock
2from django.core.management import call_command
3from django.test import TestCase
4from django.utils import timezone
5from io import StringIO
7from CIResults.models import Component, Build, Test, Machine, RunConfig, TestSuite, TestsuiteRun
8from CIResults.models import TextStatus, TestResult, KnownFailure, RunFilterStatistic, UnknownFailure
9from CIResults.models import IssueFilter, Issue, IssueFilterAssociated
10from CIResults.run_import import TestsuiteRunResults, TestsuiteTestResult, TestsuiteResults
11from CIResults.run_import import RunConfigResults, PiglitResult, TestSuiteRunDef, issue_simple_stats_recomputing
12from add_build import BuildResult
14import configparser
15import datetime
18class BuildResultTests(TestCase):
20 def __mock_configparser__(self):
21 # Replace the configparser instanciated by RunConfigResult with our own
22 self.config = configparser.ConfigParser()
23 patcher_api_call = patch('configparser.ConfigParser')
24 mock_matches = patcher_api_call.start()
25 mock_matches.return_value = self.config
26 self.addCleanup(patcher_api_call.stop)
28 # Make the read() function a noop
29 self.config.read = lambda x: 0
31 def test_parse_run_info__empty(self):
32 self.__mock_configparser__()
34 # Check empty
35 self.config.read_string("")
36 self.assertRaisesMessage(ValueError,
37 "The build info file build.ini is invalid: missing the section CIRESULTS_BUILD",
38 BuildResult.from_build_ini, "")
40 def test_parse_run_info__more_sections(self):
41 self.__mock_configparser__()
43 cfg = """[CIRESULTS_BUILD]
44 field = value
46 [SECTION]
47 field = value"""
48 self.config.read_string(cfg)
49 msg = "The build info file build.ini is invalid: only the section CIRESULTS_BUILD is allowed"
50 self.assertRaisesMessage(ValueError, msg, BuildResult.from_build_ini, "")
52 def test_parse_run_info__minimal(self):
53 self.__mock_configparser__()
55 component = Component.objects.create(name="COMPONENT1", description="desc",
56 url="url", public=True)
58 cfg = """[CIRESULTS_BUILD]
59 name: BUILD2
60 component: COMPONENT1
61 version: VERSION1"""
62 self.config.read_string(cfg)
63 build = BuildResult.from_build_ini("")
64 build.commit_to_db()
66 obj = Build.objects.get(name="BUILD2")
67 self.assertEqual(obj.name, "BUILD2")
68 self.assertEqual(obj.component, component)
69 self.assertEqual(obj.version, "VERSION1")
70 self.assertEqual(set(obj.parents.all()), set())
71 self.assertEqual(obj.branch, None)
72 self.assertEqual(obj.repo_type, None)
73 self.assertEqual(obj.repo, None)
74 self.assertEqual(obj.upstream_url, None) # Check that the invalid URL was not used
75 self.assertEqual(obj.parameters, None)
76 self.assertEqual(obj.build_log, None)
78 @patch('builtins.open', mock_open(read_data="Parameters\nfile"))
79 def test_parse_run_info__parameters_file(self):
80 self.__mock_configparser__()
82 Component.objects.create(name="COMPONENT1", description="desc", url="url", public=True)
84 cfg = """[CIRESULTS_BUILD]
85 name: BUILD1
86 component: COMPONENT1
87 version: VERSION1
88 parameters_file: file"""
89 self.config.read_string(cfg)
90 build = BuildResult.from_build_ini("")
91 build.commit_to_db()
93 obj = Build.objects.get(name="BUILD1")
94 self.assertEqual(obj.parameters, "Parameters\nfile")
96 @patch('builtins.open', mock_open(read_data="Parameters\nfile"))
97 def test_parse_run_info__parameters_priority(self):
98 self.__mock_configparser__()
100 Component.objects.create(name="COMPONENT1", description="desc", url="url", public=True)
102 cfg = """[CIRESULTS_BUILD]
103 name: BUILD1
104 component: COMPONENT1
105 version: VERSION1
106 parameters: parameters inline
107 parameters_file: file"""
108 self.config.read_string(cfg)
109 build = BuildResult.from_build_ini("")
110 build.commit_to_db()
112 obj = Build.objects.get(name="BUILD1")
113 self.assertEqual(obj.parameters, "parameters inline")
115 @patch('builtins.open', mock_open(read_data="My\nBuild\nLog"))
116 def test_parse_run_info__complete(self):
117 self.__mock_configparser__()
119 # Create some objects in the DB that are dependencies for the new build
120 component = Component.objects.create(name="COMPONENT1", description="desc",
121 url="url", public=True)
122 builds = []
123 for i in range(0, 2):
124 b = Build.objects.create(name="BUILD{}".format(i), component=component,
125 version="version")
126 builds.append(b)
128 # Check a complete configuration
129 cfg = """[CIRESULTS_BUILD]
130 name: BUILD2
131 component: COMPONENT1
132 version: VERSION1
133 parents: BUILD0 BUILD1
134 branch: BRANCH1
135 repo_type: REPO_TYPE1
136 repo: http://repo.example
137 upstream_url: http://URL1.de
138 parameters: PARAMETERS WITH SPACES
139 AND
140 NEW
141 LINES
142 build_log_file: logfile
143 """
144 self.config.read_string(cfg)
145 build = BuildResult.from_build_ini("")
146 build.commit_to_db()
148 obj = Build.objects.get(name="BUILD2")
149 self.assertEqual(obj.name, "BUILD2")
150 self.assertEqual(obj.component, component)
151 self.assertEqual(obj.version, "VERSION1")
152 self.assertEqual(set(obj.parents.all()), set(builds))
153 self.assertEqual(obj.branch, "BRANCH1")
154 self.assertEqual(obj.repo_type, "REPO_TYPE1")
155 self.assertEqual(obj.repo, "http://repo.example")
156 self.assertEqual(obj.upstream_url, "http://URL1.de")
157 self.assertEqual(obj.parameters, "PARAMETERS WITH SPACES\nAND\nNEW\nLINES")
158 self.assertEqual(obj.build_log, "My\nBuild\nLog")
160 @patch('builtins.open', mock_open(read_data="My\nBuild\nLog"))
161 def test_parse_run_info__no_ini__complete(self):
162 # Create some objects in the DB that are dependencies for the new build
163 component = Component.objects.create(name="COMPONENT1", description="desc",
164 url="url", public=True)
165 builds = []
166 for i in range(0, 2):
167 b = Build.objects.create(name="BUILD{}".format(i), component=component,
168 version="version")
169 builds.append(b)
171 # Check a complete configuration
172 build = BuildResult.from_dict(name="BUILD2", component="COMPONENT1", version="VERSION1",
173 parents=["BUILD0", "BUILD1"], branch="BRANCH1",
174 repo_type="REPO_TYPE1", repo="http://repo.example",
175 upstream_url="http://URL1.de",
176 parameters="PARAMETERS WITH SPACES\nAND\nNEW\nLINES",
177 config_file="", build_log="My\nBuild\nLog")
178 build.commit_to_db()
180 obj = Build.objects.get(name="BUILD2")
181 self.assertEqual(obj.name, "BUILD2")
182 self.assertEqual(obj.component, component)
183 self.assertEqual(obj.version, "VERSION1")
184 self.assertEqual(set(obj.parents.all()), set(builds))
185 self.assertEqual(obj.branch, "BRANCH1")
186 self.assertEqual(obj.repo_type, "REPO_TYPE1")
187 self.assertEqual(obj.repo, "http://repo.example")
188 self.assertEqual(obj.upstream_url, "http://URL1.de")
189 self.assertEqual(obj.parameters, "PARAMETERS WITH SPACES\nAND\nNEW\nLINES")
190 self.assertEqual(obj.build_log, "My\nBuild\nLog")
192 def test_parse_run_info__no_ini__parents_is_None(self):
193 # Create some objects in the DB that are dependencies for the new build
194 component = Component.objects.create(name="COMPONENT1", description="desc",
195 url="url", public=True)
196 builds = []
197 for i in range(0, 2):
198 b = Build.objects.create(name="BUILD{}".format(i), component=component,
199 version="version")
200 builds.append(b)
202 # Check a complete configuration
203 build = BuildResult.from_dict(name="BUILD2", component="COMPONENT1", version="VERSION1", parents=[])
204 build.commit_to_db()
206 obj = Build.objects.get(name="BUILD2")
207 self.assertEqual(obj.name, "BUILD2")
208 self.assertEqual(obj.component, component)
209 self.assertEqual(obj.version, "VERSION1")
210 self.assertEqual(set(obj.parents.all()), set())
213class TestsuiteRunResultsTests(TestCase):
214 def test___result_url__(self):
215 pattern = "http://hello.world/{runconfig}/{machine}/{testsuite_build}/{run_id}/{test}"
216 testsuite = Mock(build="build1", result_url_pattern=pattern, runconfig=Mock())
217 testsuite.runconfig.name = "runcfg"
219 self.assertEqual(TestsuiteRunResults.__result_url__(testsuite, 42, "machine1", "test1"),
220 "http://hello.world/runcfg/machine1/build1/42/test1")
223class TestsuiteResultsTests(TestCase):
224 def setUp(self):
225 self.runconfig = Mock(name="runcfg")
227 @patch('CIResults.models.Build.objects.get', return_value=Build(component=Component()))
228 @patch('CIResults.models.TestSuite.objects.get', return_value=TestSuite())
229 def test_sanity(self, ts_mock, build_mock):
230 ts_run = TestsuiteResults(self.runconfig, "name1", "build1", "piglit", 1, '')
231 self.assertEqual(self.runconfig, self.runconfig)
232 self.assertEqual(ts_run.name, "name1")
233 self.assertEqual(ts_run.build, "build1")
234 build_mock.assert_called_once()
235 ts_mock.assert_called_once()
236 self.assertEqual(type(ts_run.db_object), TestSuite)
237 self.assertEqual(ts_run.format, "piglit")
238 self.assertEqual(ts_run.version, 1)
239 self.assertEqual(ts_run._result_type, PiglitResult)
241 @patch('CIResults.models.Build.objects.get', return_value=Build(component=Component()))
242 @patch('CIResults.models.TestSuite.objects.get', return_value=TestSuite())
243 def test_invalid_format(self, ts_mock, build_mock):
244 self.assertRaisesMessage(ValueError, "The testsuite result format 'nonexisting' is unsupported",
245 TestsuiteResults, self.runconfig, "name1", "build1", "nonexisting", 1, '')
247 self.assertRaisesMessage(ValueError, "The version 0 of the testsuite result format 'piglit' is unsupported",
248 TestsuiteResults, self.runconfig, "name1", "build1", "piglit", 0, '')
250 @patch('CIResults.models.Build.objects.get', return_value=Build(component=Component()))
251 @patch('CIResults.models.TestSuite.objects.get', return_value=TestSuite())
252 @patch('CIResults.run_import.PiglitResult', return_value=None)
253 def test_read_results(self, piglitresult_mock, ts_mock, build_mock):
254 ts_run = TestsuiteResults(self.runconfig, "name1", "build1", "piglit", 1, '')
255 ts_run.read_results("machine1", "1", "path/to/results")
256 piglitresult_mock.assert_called_once()
259class TestSuiteRunDefTests(TestCase):
260 def test_init(self):
261 for field in ["testsuite_build", "results_format", "results_format_version",
262 "machine", "ts_run_id", "ts_run_path"]:
263 kargs = {"testsuite_build": "BUILD", "results_format": "piglit",
264 "results_format_version": 1, "machine": "MACHINE",
265 "ts_run_id": 0, "ts_run_path": ""}
266 kargs[field] = None
268 msg = "The parameter {} cannot be None".format(field)
269 if field == "results_format_version" or field == "ts_run_id":
270 msg = "The parameter {} 'None' should be an integer".format(field)
272 self.assertRaisesMessage(ValueError, msg, TestSuiteRunDef, **kargs)
275class RunConfigResultsTests(TestCase):
277 def __mock_configparser__(self):
278 # Replace the configparser instanciated by RunConfigResult with our own
279 self.config = configparser.ConfigParser()
280 patcher_api_call = patch('configparser.ConfigParser')
281 mock_matches = patcher_api_call.start()
282 mock_matches.return_value = self.config
283 self.addCleanup(patcher_api_call.stop)
284 patcher_api_call = patch('configparser.ConfigParser.read')
285 mock_matches = patcher_api_call.start()
286 mock_matches.return_value = None
287 self.addCleanup(patcher_api_call.stop)
289 def test_parse_run_info__empty(self):
290 self.__mock_configparser__()
292 # Check empty
293 self.config.read_string("")
294 self.assertRaisesMessage(ValueError, "The RunConfig file runconfig.ini is invalid",
295 RunConfigResults, "")
297 def test_parse_run_info__name_missing(self):
298 self.__mock_configparser__()
300 # Check name missing
301 cfg = """[CIRESULTS_RUNCONFIG]
302 """
303 self.config.read_string(cfg)
304 self.assertRaisesMessage(ValueError, "The RunConfig file runconfig.ini is invalid: runconfig name unspecified",
305 RunConfigResults, "")
307 @patch('os.listdir', return_value=[])
308 def test_parse_run_info__minimal(self, mocked_listdir):
309 self.__mock_configparser__()
311 cfg = """[CIRESULTS_RUNCONFIG]
312 name: RUNCFG1
313 """
314 self.config.read_string(cfg)
315 runcfg = RunConfigResults("")
316 self.assertEqual(runcfg.name, "RUNCFG1")
317 self.assertEqual(runcfg.url, None)
318 self.assertEqual(runcfg.environment, None)
319 self.assertEqual(runcfg.builds, [])
320 self.assertEqual(runcfg.tags, [])
321 self.assertEqual(runcfg.testsuites, {})
322 self.assertFalse(runcfg.temporary)
324 def test_parse_run_info__invalid_build(self):
325 self.__mock_configparser__()
327 cfg = """[CIRESULTS_RUNCONFIG]
328 name: RUNCFG1
330 [Testsuite1]
331 build: build1
332 """
333 self.config.read_string(cfg)
334 msg = "The build 'build1' of the testsuite 'Testsuite1' is not found in the list of builds "
335 msg += "of the runconfig RUNCFG1"
336 self.assertRaisesMessage(ValueError, msg, RunConfigResults, "")
338 def test_parse_run_info__invalid_single_testsuite(self):
339 self.__mock_configparser__()
341 cfg = """[CIRESULTS_RUNCONFIG]
342 name: RUNCFG1
343 builds: build1 build2
345 [CIRESULTS_TESTSUITE]
346 build: build1
348 [Testsuite2]
349 build: build2
350 """
351 self.config.read_string(cfg)
352 msg = "If the section CIRESULTS_TESTSUITE exists, then no additional section/testsuite can be added"
353 self.assertRaisesMessage(ValueError, msg, RunConfigResults, "")
355 @patch('os.listdir', return_value=[])
356 @patch('CIResults.run_import.TestsuiteResults')
357 def test_parse_run_info__multi_complete(self, mocked_ts_run, mocked_listdir):
358 self.__mock_configparser__()
360 # Check a complete configuration
361 cfg = """[CIRESULTS_RUNCONFIG]
362 name: RUNCFG2
363 url: http://test.url.com
364 environment: blablabla
365 blibliblib
366 builds: build1 build2 build3
367 tags: tag1 tag2 tag3
368 temporary: true
370 [Testsuite1]
371 build: build2
372 format: format1
373 result_url_pattern = pattern1
375 [Testsuite2]
376 build: build3
377 format: format2
378 version: 2
379 result_url_pattern = pattern2
380 """
381 self.config.read_string(cfg)
382 runcfg = RunConfigResults("")
383 self.assertEqual(runcfg.name, "RUNCFG2")
384 self.assertEqual(runcfg.url, "http://test.url.com")
385 self.assertEqual(runcfg.environment, "blablabla\nblibliblib")
386 self.assertEqual(runcfg.builds, ['build1', 'build2', 'build3'])
387 self.assertEqual(runcfg.tags, ['tag1', 'tag2', 'tag3'])
388 self.assertEqual(set(runcfg.testsuites.keys()),
389 set(["Testsuite1", "Testsuite2"]))
390 self.assertTrue(runcfg.temporary)
392 self.assertEqual(len(mocked_ts_run.call_args_list), 2)
393 mocked_ts_run.assert_any_call(runcfg, 'Testsuite1', 'build2', 'format1', 1, 'pattern1')
394 mocked_ts_run.assert_any_call(runcfg, 'Testsuite2', 'build3', 'format2', 2, 'pattern2')
396 @patch('os.listdir', return_value=[])
397 @patch('CIResults.run_import.TestsuiteResults')
398 @patch('CIResults.run_import.RunConfigResults.__load_testsuite_results__')
399 def test_parse_run_info__single_complete(self, mocked_load_results, mocked_ts_run, mocked_listdir):
400 self.__mock_configparser__()
402 cfg = """[CIRESULTS_RUNCONFIG]
403 name: RUNCFG2
404 builds: build1 build2 build3
405 tags: tag1 tag2 tag3
407 [CIRESULTS_TESTSUITE]
408 build: build2
409 format: format1
410 """
411 self.config.read_string(cfg)
412 runcfg = RunConfigResults("")
413 self.assertEqual(runcfg.name, "RUNCFG2")
414 self.assertEqual(runcfg.builds, ['build1', 'build2', 'build3'])
415 self.assertEqual(runcfg.tags, ['tag1', 'tag2', 'tag3'])
416 self.assertEqual(set(runcfg.testsuites.keys()),
417 set(["CIRESULTS_TESTSUITE"]))
419 self.assertEqual(len(mocked_ts_run.call_args_list), 1)
420 mocked_ts_run.assert_any_call(runcfg, 'CIRESULTS_TESTSUITE', 'build2', 'format1', 1, "")
422 self.assertEqual(len(mocked_load_results.call_args_list), 1)
423 mocked_load_results.assert_any_call(runcfg._testsuites.get("CIRESULTS_TESTSUITE"), "")
425 @patch('sys.stdout', new_callable=StringIO)
426 @patch('CIResults.run_import.TestsuiteResults')
427 def test_load_results(self, mocked_ts_run, mocked_stdout):
428 runcfg = RunConfigResults("CIResults/tests/results")
430 self.assertEqual(set(mocked_stdout.getvalue().splitlines()),
431 set(["RunConfigResults: testsuite run ID 'invalid_id' should be an integer",
432 "Ignore the testsuite 'UNREFERENCED' because it is not listed in the runconfig file"]))
433 self.assertNotIn("UNREFERENCED", runcfg.testsuites)
435 calls = [call('machine1', 0, 'CIResults/tests/results/RENDERCHECK/machine1/0'),
436 call('machine2', 0, 'CIResults/tests/results/RENDERCHECK/machine2/0'),
437 call('machine1', 0, 'CIResults/tests/results/IGT/machine1/0'),
438 call('machine1', 1, 'CIResults/tests/results/IGT/machine1/1'),
439 call('machine2', 0, 'CIResults/tests/results/IGT/machine2/0')]
440 mocked_ts_run.return_value.read_results.assert_has_calls(calls, any_order=True)
442 def test_init__invalid_testsuite_build(self):
443 results = [TestSuiteRunDef(testsuite_build="INVALID", results_format="piglit",
444 results_format_version=1, machine="machine",
445 ts_run_id=1, ts_run_path="")]
446 msg = "The build named 'INVALID' does not exist"
447 self.assertRaisesMessage(ValueError, msg, RunConfigResults, name="RUNCFG",
448 builds=['INVALID'], results=results)
450 def test_init__testsuite_build_not_in_the_list_of_builds(self):
451 results = [TestSuiteRunDef(testsuite_build="MISSING", results_format="piglit",
452 results_format_version=1, machine="machine",
453 ts_run_id=1, ts_run_path="")]
454 msg = "The build named 'MISSING' is not part of the list of builds of the runconfig"
455 self.assertRaisesMessage(ValueError, msg, RunConfigResults, name="RUNCFG",
456 builds=["build1", "build2"], results=results)
458 @patch('sys.stderr', new_callable=StringIO)
459 def test_init__dual_import_of_a_testsuite_run(self, mocked_stderr):
460 call_command('loaddata', 'CIResults/fixtures/RunConfigResults_commit_to_db', verbosity=0)
462 results = [TestSuiteRunDef(testsuite_build="build1", results_format="piglit",
463 results_format_version=1, machine="machine",
464 ts_run_id=1, ts_run_path="CIResults/tests/results/IGT/machine1/0/results.json.bz2"),
465 TestSuiteRunDef(testsuite_build="build1", results_format="piglit",
466 results_format_version=1, machine="machine",
467 ts_run_id=1, ts_run_path="CIResults/tests/results/IGT/machine1/0/results.json.bz2")]
469 msg = "Try to import twice testsuite1's run ID 1 on the runconfig 'RUNCFG' for the machine 'machine'"
470 self.assertRaisesMessage(ValueError, msg, RunConfigResults, name="RUNCFG",
471 builds=["build1"], results=results)
473 @patch('os.listdir', return_value=[])
474 def __create_commit_to_db_env__(self, mocked_listdir, temporary=False):
475 self.__mock_configparser__()
477 # Mock a configuration
478 cfg = """[CIRESULTS_RUNCONFIG]
479 name: RUNCFG
480 url: http://test.url.com
481 result_url_pattern = http://hello.world/{runconfig}/{machine}/{testsuite_build}/{run_id}/{test}
482 environment: my environment
483 builds: build1 build2
484 tags: tag1 tag2
485 temporary: {temporary}
487 [testsuite1]
488 build: build1
489 format: piglit
491 [testsuite2]
492 build: build2
493 format: piglit
494 """.format(temporary=temporary, runconfig="{runconfig}", machine="{machine}",
495 testsuite_build="{testsuite_build}", run_id="{run_id}", test="{test}")
496 self.config.read_string(cfg)
497 runcfg = RunConfigResults("")
499 # Add results
500 self.start_time_run = timezone.make_aware(datetime.datetime.fromtimestamp(0),
501 timezone.get_default_timezone())
502 self.start_time_test = self.start_time_run + datetime.timedelta(seconds=1)
503 for testsuite in ["testsuite1", "testsuite2"]:
504 for i in range(1, 3):
505 results = []
506 for t in range(1, 3):
507 results.append(TestsuiteTestResult(name="test{}".format(t),
508 status="pass" if t != 2 else "broken",
509 command="command",
510 stdout="stdout",
511 stderr="stderr",
512 dmesg="dmesg",
513 start_time=self.start_time_test,
514 duration=datetime.timedelta(seconds=1)))
516 tsr = TestsuiteRunResults(testsuite=runcfg.testsuites[testsuite],
517 machine_name="machine{}".format(i),
518 run_id=i, test_results=results,
519 start_time=self.start_time_run,
520 duration=datetime.timedelta(seconds=4242))
521 runcfg._run_results.append(tsr)
523 return runcfg
525 @patch('sys.stdout', new_callable=StringIO)
526 def test_commit_to_db_machine_public(self, mocked_stdout):
527 call_command('loaddata', 'CIResults/fixtures/RunConfigResults_commit_to_db', verbosity=0)
528 runcfg = self.__create_commit_to_db_env__()
530 # Check the amount of objects before adding anything
531 self.assertEqual(Machine.objects.all().count(), 1)
532 self.assertEqual(Test.objects.all().count(), 1)
533 self.assertEqual(TextStatus.objects.all().count(), 4)
534 self.assertEqual(TestsuiteRun.objects.all().count(), 0)
535 self.assertEqual(TestResult.objects.all().count(), 0)
536 self.assertEqual(KnownFailure.objects.all().count(), 0)
537 self.assertEqual(UnknownFailure.objects.all().count(), 0)
538 self.assertEqual(RunFilterStatistic.objects.all().count(), 0)
540 testsuite1 = TestSuite.objects.get(name="testsuite1")
541 testsuite2 = TestSuite.objects.get(name="testsuite2")
543 # Add the runconfig to the db
544 runcfg.commit_to_db(new_machines_public=True, new_tests_public=False)
546 # Check stdout
547 stdout_lines = mocked_stdout.getvalue().splitlines()
548 self.assertEqual(stdout_lines[0:-6],
549 ['adding 1 missing machine(s)',
550 'adding 1 missing test(s) (testsuite1)',
551 'adding 2 missing test(s) (testsuite2)',
552 'adding 1 missing statuse(s) (testsuite1)',
553 'adding 1 missing statuse(s) (testsuite2)',
554 'adding 4 testsuite runs',
555 'adding 8 test results'])
556 err_msg = 'Found 4 test failures (0 filters matched, 4 failures left unmatched) in '
557 self.assertTrue(stdout_lines[-6].startswith(err_msg), stdout_lines[-6])
559 err_msg = 'Found 0/0 recently-archived filters matching some unknown failures in '
560 self.assertTrue(stdout_lines[-1].startswith(err_msg), stdout_lines[-1])
562 # Check that the machine and test got created with the correct public attribute
563 self.assertTrue(Machine.objects.get(name="machine2").public)
564 self.assertFalse(Test.objects.get(name="test2", testsuite=testsuite1).public)
565 self.assertFalse(Test.objects.get(name="test1", testsuite=testsuite2).public)
566 self.assertFalse(Test.objects.get(name="test2", testsuite=testsuite2).public)
568 # Check that the new status is in the database for both testsuites
569 self.assertEqual(TextStatus.objects.filter(name="broken").count(), 2)
571 # Check that the right amount of objects is found in the db
572 self.assertEqual(Machine.objects.all().count(), 2)
573 self.assertEqual(Test.objects.all().count(), 4)
574 self.assertEqual(TextStatus.objects.all().count(), 6)
575 self.assertEqual(TestsuiteRun.objects.all().count(), 4)
576 self.assertEqual(TestResult.objects.all().count(), 8)
577 self.assertEqual(KnownFailure.objects.all().count(), 0)
578 self.assertEqual(UnknownFailure.objects.all().count(), 4)
579 self.assertEqual(RunFilterStatistic.objects.all().count(), 0)
581 # Check that the testsuite of the result matches the one of the run
582 for result in TestResult.objects.all().select_related('status', 'ts_run'):
583 self.assertEqual(result.status.testsuite_id, result.ts_run.testsuite_id)
585 @patch('sys.stdout', new_callable=StringIO)
586 def test_commit_to_db_test_public(self, mocked_stdout):
587 call_command('loaddata', 'CIResults/fixtures/RunConfigResults_commit_to_db', verbosity=0)
588 runcfg = self.__create_commit_to_db_env__()
590 testsuite1 = TestSuite.objects.get(name="testsuite1")
591 testsuite2 = TestSuite.objects.get(name="testsuite2")
593 runcfg.commit_to_db(new_machines_public=False, new_tests_public=True)
595 # Check that the machine and test got created with the correct public attribute
596 self.assertFalse(Machine.objects.get(name="machine2").public)
597 self.assertTrue(Test.objects.get(name="test2", testsuite=testsuite1).public)
598 self.assertTrue(Test.objects.get(name="test1", testsuite=testsuite2).public)
599 self.assertTrue(Test.objects.get(name="test2", testsuite=testsuite2).public)
601 # Check that they all have the first_runconfig set correctly (new tests
602 # got the current runconfig, and the old one still has the same value)
603 old_runcfg = RunConfig.objects.get(name="OLD_RUNCFG")
604 new_runcfg = RunConfig.objects.get(name="RUNCFG")
605 for test in Test.objects.all():
606 runcfg = (old_runcfg if test.id == 1 else new_runcfg)
607 self.assertEqual(test.first_runconfig, runcfg, test)
609 @patch('sys.stdout', new_callable=StringIO)
610 def test_commit_to_db_test_temporary(self, mocked_stdout):
611 call_command('loaddata', 'CIResults/fixtures/RunConfigResults_commit_to_db', verbosity=0)
612 runcfg = self.__create_commit_to_db_env__(temporary=True)
614 runcfg.commit_to_db(new_machines_public=False, new_tests_public=True)
616 # Check that they all have the first_runconfig set correctly, and the new
617 # test get None instead
618 old_runcfg = RunConfig.objects.get(name="OLD_RUNCFG")
619 for test in Test.objects.all():
620 runcfg = (old_runcfg if test.id == 1 else None)
621 self.assertEqual(test.first_runconfig, runcfg, test)
623 def test_commit_to_db__two_builds_of_the_same_component(self):
624 call_command('loaddata', 'CIResults/fixtures/RunConfigResults_commit_to_db', verbosity=0)
626 component1 = Component.objects.get(name="testsuite1")
627 Build.objects.create(name="build3", component=component1)
629 runcfg = RunConfigResults(name="RUNCFG", url="http://test.url.com",
630 result_url_pattern="", builds=["build1", "build3"],
631 tags=["tag1", "tag2"])
633 msg = "ERROR: Two builds (build3 and build1) cannot be from the same component (testsuite1)"
634 self.assertRaisesMessage(ValueError, msg, runcfg.commit_to_db)
636 @patch('sys.stdout', new_callable=StringIO)
637 def test_commit_to_db__add_one_component(self, mocked_stdout):
638 call_command('loaddata', 'CIResults/fixtures/RunConfigResults_commit_to_db', verbosity=0)
640 # First, create a runconfig with two builds from two different testsuites
641 runcfg = RunConfigResults(name="RUNCFG", url="http://test.url.com",
642 result_url_pattern="", builds=["build1"],
643 tags=["tag1", "tag2"])
644 runcfg.commit_to_db()
646 # Add the build2
647 runcfg = RunConfigResults(name="RUNCFG", builds=["build2"])
648 runcfg.commit_to_db()
650 # Verify that the runconfig has been modified to also contain the new build
651 runcfg_obj = RunConfig.objects.get(name="RUNCFG")
652 self.assertEqual(set([b.id for b in runcfg_obj.builds.all()]), set([1, 2]))
654 @patch('sys.stdout', new_callable=StringIO)
655 def test_commit_to_db__try_changing_build_of_one_component(self, mocked_stdout):
656 call_command('loaddata', 'CIResults/fixtures/RunConfigResults_commit_to_db', verbosity=0)
658 testsuite1 = Component.objects.get(name="testsuite1")
659 Build.objects.create(name="build3", component=testsuite1)
661 # First, create a runconfig with two builds from two different testsuites
662 runcfg = RunConfigResults(name="RUNCFG", url="http://test.url.com",
663 result_url_pattern="", builds=["build1", "build2"],
664 tags=["tag1", "tag2"])
665 runcfg.commit_to_db()
667 runcfg = RunConfigResults(name="RUNCFG", builds=["build3"])
668 msg = "ERROR: Two builds (build3 and build1) cannot be from the same component (testsuite1)"
669 self.assertRaisesMessage(ValueError, msg, runcfg.commit_to_db)
672class issue_simple_stats_recomputingTests(TestCase):
673 def setUp(self):
674 self.filter1 = IssueFilter.objects.create(description="filter1")
675 self.filter2 = IssueFilter.objects.create(description="filter2")
676 self.unrelated_filter = IssueFilter.objects.create(description="unrelated filter")
678 self.issue = Issue.objects.create()
679 self.issue.save = MagicMock()
681 IssueFilterAssociated.objects.create(filter=self.filter1, issue=self.issue)
682 IssueFilterAssociated.objects.create(filter=self.filter2, issue=self.issue)
684 self.runconfig = RunConfig.objects.create(name='runconfig', temporary=False)
686 self.stats = dict()
688 def __check_result(self, runconfigs_covered_count=0, runconfigs_affected_count=0,
689 last_seen_runconfig=None, save_called=False):
691 issue_simple_stats_recomputing(self.issue, self.runconfig, self.stats)
693 self.assertEqual(self.issue.runconfigs_covered_count, runconfigs_covered_count)
694 self.assertEqual(self.issue.runconfigs_affected_count, runconfigs_affected_count)
695 self.assertEqual(self.issue.last_seen_runconfig, last_seen_runconfig)
696 if last_seen_runconfig is not None:
697 self.assertEqual(self.issue.last_seen, last_seen_runconfig.added_on)
698 else:
699 self.assertIsNone(self.issue.last_seen)
700 self.assertEqual(self.issue.save.called, save_called)
702 def test_update_issue_stats__unrelated_stats_have_no_impact(self):
703 self.stats[self.unrelated_filter] = (None, RunFilterStatistic(matched_count=42, covered_count=42))
704 self.__check_result()
706 def test_update_issue_stats__new_covered_filters(self):
707 self.stats[self.filter1] = (None, RunFilterStatistic(runconfig=None, matched_count=0, covered_count=42))
708 self.stats[self.filter2] = (None, RunFilterStatistic(runconfig=None, matched_count=0, covered_count=10))
710 self.__check_result(runconfigs_covered_count=1, save_called=True)
712 def test_update_issue_stats__new_covered_filters_but_was_already_covered(self):
713 self.issue.runconfigs_covered_count = 1
715 self.stats[self.filter1] = (None, RunFilterStatistic(runconfig=None, matched_count=0, covered_count=42))
716 self.stats[self.filter2] = (RunFilterStatistic(runconfig=None, matched_count=0, covered_count=10),
717 RunFilterStatistic(runconfig=None, matched_count=0, covered_count=23))
719 self.__check_result(runconfigs_covered_count=1, save_called=False)
721 def test_update_issue_stats__new_matched_filters(self):
722 self.issue.runconfigs_covered_count = 1
724 self.stats[self.filter1] = (None, RunFilterStatistic(runconfig=None, matched_count=0, covered_count=42))
725 self.stats[self.filter2] = (RunFilterStatistic(runconfig=None, matched_count=0, covered_count=10),
726 RunFilterStatistic(runconfig=None, matched_count=2, covered_count=10))
728 self.__check_result(runconfigs_covered_count=1, runconfigs_affected_count=1,
729 last_seen_runconfig=self.runconfig, save_called=True)
731 def test_update_issue_stats__new_matched_filters_but_was_already_matched(self):
732 self.issue.runconfigs_covered_count = 1
733 self.issue.runconfigs_affected_count = 1
735 self.stats[self.filter1] = (None, RunFilterStatistic(runconfig=None, matched_count=20, covered_count=42))
736 self.stats[self.filter2] = (RunFilterStatistic(runconfig=None, matched_count=1, covered_count=10),
737 RunFilterStatistic(runconfig=None, matched_count=2, covered_count=10))
739 # NOTE: last_seen_runconfig is None because it is not written to when no changes happened
740 self.__check_result(runconfigs_covered_count=1, runconfigs_affected_count=1,
741 last_seen_runconfig=None, save_called=False)