1 #!/usr/bin/env vpython3
2 # Copyright 2014 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
15 import common_merge_script_tests
18 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
20 # For 'standard_gtest_merge.py'.
22 0, os.path.abspath(os.path.join(THIS_DIR, '..', 'resources')))
26 import standard_gtest_merge
29 # gtest json output for successfully finished shard #0.
32 'AlignedMemoryTest.DynamicAllocation',
33 'AlignedMemoryTest.ScopedDynamicAllocation',
34 'AlignedMemoryTest.StackAlignment',
35 'AlignedMemoryTest.StaticAlignment',
38 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
39 'FileTest.TouchGetInfo',
40 'MessageLoopTestTypeDefault.EnsureDeletion',
42 'global_tags': ['CPU_64_BITS', 'MODE_DEBUG', 'OS_LINUX', 'OS_POSIX'],
43 'per_iteration_data': [{
44 'AlignedMemoryTest.DynamicAllocation': [{
46 'losless_snippet': True,
47 'output_snippet': 'blah\\n',
48 'output_snippet_base64': 'YmxhaAo=',
51 'AlignedMemoryTest.ScopedDynamicAllocation': [{
53 'losless_snippet': True,
54 'output_snippet': 'blah\\n',
55 'output_snippet_base64': 'YmxhaAo=',
60 'AlignedMemoryTest.DynamicAllocation': {
61 'file': 'foo/bar/allocation_test.cc',
64 'AlignedMemoryTest.ScopedDynamicAllocation': {
65 'file': 'foo/bar/allocation_test.cc',
68 # This is a test from a different shard, but this happens in practice and we
69 # should not fail if information is repeated.
70 'AlignedMemoryTest.StaticAlignment': {
71 'file': 'foo/bar/allocation_test.cc',
78 # gtest json output for successfully finished shard #1.
81 'AlignedMemoryTest.DynamicAllocation',
82 'AlignedMemoryTest.ScopedDynamicAllocation',
83 'AlignedMemoryTest.StackAlignment',
84 'AlignedMemoryTest.StaticAlignment',
87 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
88 'FileTest.TouchGetInfo',
89 'MessageLoopTestTypeDefault.EnsureDeletion',
91 'global_tags': ['CPU_64_BITS', 'MODE_DEBUG', 'OS_LINUX', 'OS_POSIX'],
92 'per_iteration_data': [{
93 'AlignedMemoryTest.StackAlignment': [{
95 'losless_snippet': True,
96 'output_snippet': 'blah\\n',
97 'output_snippet_base64': 'YmxhaAo=',
100 'AlignedMemoryTest.StaticAlignment': [{
101 'elapsed_time_ms': 0,
102 'losless_snippet': True,
103 'output_snippet': 'blah\\n',
104 'output_snippet_base64': 'YmxhaAo=',
109 'AlignedMemoryTest.StackAlignment': {
110 'file': 'foo/bar/allocation_test.cc',
113 'AlignedMemoryTest.StaticAlignment': {
114 'file': 'foo/bar/allocation_test.cc',
121 TIMED_OUT_GTEST_JSON_1 = {
122 'disabled_tests': [],
125 'AlignedMemoryTest.DynamicAllocation',
126 'AlignedMemoryTest.ScopedDynamicAllocation',
127 'AlignedMemoryTest.StackAlignment',
128 'AlignedMemoryTest.StaticAlignment',
130 'per_iteration_data': [{
131 'AlignedMemoryTest.StackAlignment': [{
132 'elapsed_time_ms': 54000,
133 'losless_snippet': True,
134 'output_snippet': 'timed out',
135 'output_snippet_base64': '',
138 'AlignedMemoryTest.StaticAlignment': [{
139 'elapsed_time_ms': 0,
140 'losless_snippet': True,
141 'output_snippet': '',
142 'output_snippet_base64': '',
147 'AlignedMemoryTest.StackAlignment': {
148 'file': 'foo/bar/allocation_test.cc',
151 'AlignedMemoryTest.StaticAlignment': {
152 'file': 'foo/bar/allocation_test.cc',
158 # GOOD_GTEST_JSON_0 and GOOD_GTEST_JSON_1 merged.
159 GOOD_GTEST_JSON_MERGED = {
161 'AlignedMemoryTest.DynamicAllocation',
162 'AlignedMemoryTest.ScopedDynamicAllocation',
163 'AlignedMemoryTest.StackAlignment',
164 'AlignedMemoryTest.StaticAlignment',
167 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
168 'FileTest.TouchGetInfo',
169 'MessageLoopTestTypeDefault.EnsureDeletion',
171 'global_tags': ['CPU_64_BITS', 'MODE_DEBUG', 'OS_LINUX', 'OS_POSIX'],
172 'missing_shards': [],
173 'per_iteration_data': [{
174 'AlignedMemoryTest.DynamicAllocation': [{
175 'elapsed_time_ms': 0,
176 'losless_snippet': True,
177 'output_snippet': 'blah\\n',
178 'output_snippet_base64': 'YmxhaAo=',
181 'AlignedMemoryTest.ScopedDynamicAllocation': [{
182 'elapsed_time_ms': 0,
183 'losless_snippet': True,
184 'output_snippet': 'blah\\n',
185 'output_snippet_base64': 'YmxhaAo=',
188 'AlignedMemoryTest.StackAlignment': [{
189 'elapsed_time_ms': 0,
190 'losless_snippet': True,
191 'output_snippet': 'blah\\n',
192 'output_snippet_base64': 'YmxhaAo=',
195 'AlignedMemoryTest.StaticAlignment': [{
196 'elapsed_time_ms': 0,
197 'losless_snippet': True,
198 'output_snippet': 'blah\\n',
199 'output_snippet_base64': 'YmxhaAo=',
203 'swarming_summary': {
206 u'state': u'COMPLETED',
208 u'view_url': u'blah',
214 'AlignedMemoryTest.StackAlignment': {
215 'file': 'foo/bar/allocation_test.cc',
218 'AlignedMemoryTest.StaticAlignment': {
219 'file': 'foo/bar/allocation_test.cc',
222 'AlignedMemoryTest.DynamicAllocation': {
223 'file': 'foo/bar/allocation_test.cc',
226 'AlignedMemoryTest.ScopedDynamicAllocation': {
227 'file': 'foo/bar/allocation_test.cc',
234 # Only shard #1 finished. UNRELIABLE_RESULTS is set.
235 BAD_GTEST_JSON_ONLY_1_SHARD = {
237 'AlignedMemoryTest.DynamicAllocation',
238 'AlignedMemoryTest.ScopedDynamicAllocation',
239 'AlignedMemoryTest.StackAlignment',
240 'AlignedMemoryTest.StaticAlignment',
243 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
244 'FileTest.TouchGetInfo',
245 'MessageLoopTestTypeDefault.EnsureDeletion',
252 'UNRELIABLE_RESULTS',
254 'missing_shards': [0],
255 'per_iteration_data': [{
256 'AlignedMemoryTest.StackAlignment': [{
257 'elapsed_time_ms': 0,
258 'losless_snippet': True,
259 'output_snippet': 'blah\\n',
260 'output_snippet_base64': 'YmxhaAo=',
263 'AlignedMemoryTest.StaticAlignment': [{
264 'elapsed_time_ms': 0,
265 'losless_snippet': True,
266 'output_snippet': 'blah\\n',
267 'output_snippet_base64': 'YmxhaAo=',
272 'AlignedMemoryTest.StackAlignment': {
273 'file': 'foo/bar/allocation_test.cc',
276 'AlignedMemoryTest.StaticAlignment': {
277 'file': 'foo/bar/allocation_test.cc',
284 # GOOD_GTEST_JSON_0 and TIMED_OUT_GTEST_JSON_1 merged.
285 TIMED_OUT_GTEST_JSON_MERGED = {
287 'AlignedMemoryTest.DynamicAllocation',
288 'AlignedMemoryTest.ScopedDynamicAllocation',
289 'AlignedMemoryTest.StackAlignment',
290 'AlignedMemoryTest.StaticAlignment',
293 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
294 'FileTest.TouchGetInfo',
295 'MessageLoopTestTypeDefault.EnsureDeletion',
297 'global_tags': ['CPU_64_BITS', 'MODE_DEBUG', 'OS_LINUX', 'OS_POSIX'],
298 'missing_shards': [],
299 'per_iteration_data': [{
300 'AlignedMemoryTest.DynamicAllocation': [{
301 'elapsed_time_ms': 0,
302 'losless_snippet': True,
303 'output_snippet': 'blah\\n',
304 'output_snippet_base64': 'YmxhaAo=',
307 'AlignedMemoryTest.ScopedDynamicAllocation': [{
308 'elapsed_time_ms': 0,
309 'losless_snippet': True,
310 'output_snippet': 'blah\\n',
311 'output_snippet_base64': 'YmxhaAo=',
314 'AlignedMemoryTest.StackAlignment': [{
315 'elapsed_time_ms': 54000,
316 'losless_snippet': True,
317 'output_snippet': 'timed out',
318 'output_snippet_base64': '',
321 'AlignedMemoryTest.StaticAlignment': [{
322 'elapsed_time_ms': 0,
323 'losless_snippet': True,
324 'output_snippet': '',
325 'output_snippet_base64': '',
329 'swarming_summary': {
332 u'state': u'COMPLETED',
335 u'state': u'TIMED_OUT',
340 'AlignedMemoryTest.StackAlignment': {
341 'file': 'foo/bar/allocation_test.cc',
344 'AlignedMemoryTest.StaticAlignment': {
345 'file': 'foo/bar/allocation_test.cc',
348 'AlignedMemoryTest.DynamicAllocation': {
349 'file': 'foo/bar/allocation_test.cc',
352 'AlignedMemoryTest.ScopedDynamicAllocation': {
353 'file': 'foo/bar/allocation_test.cc',
360 class _StandardGtestMergeTest(unittest.TestCase):
363 self.temp_dir = tempfile.mkdtemp()
366 shutil.rmtree(self.temp_dir)
368 def _write_temp_file(self, path, content):
369 abs_path = os.path.join(self.temp_dir, path.replace('/', os.sep))
370 if not os.path.exists(os.path.dirname(abs_path)):
371 os.makedirs(os.path.dirname(abs_path))
372 with open(abs_path, 'w') as f:
373 if isinstance(content, dict):
374 json.dump(content, f)
376 assert isinstance(content, str)
381 class LoadShardJsonTest(_StandardGtestMergeTest):
383 def test_double_digit_jsons(self):
386 json_dir = os.path.join(self.temp_dir, str(i))
387 json_path = os.path.join(json_dir, 'output.json')
388 if not os.path.exists(json_dir):
389 os.makedirs(json_dir)
390 with open(json_path, 'w') as f:
391 json.dump({'all_tests': ['LoadShardJsonTest.test%d' % i]}, f)
392 jsons_to_merge.append(json_path)
394 content, err = standard_gtest_merge.load_shard_json(
395 0, None, jsons_to_merge)
396 self.assertEqual({'all_tests': ['LoadShardJsonTest.test0']}, content)
397 self.assertIsNone(err)
399 content, err = standard_gtest_merge.load_shard_json(
400 12, None, jsons_to_merge)
401 self.assertEqual({'all_tests': ['LoadShardJsonTest.test12']}, content)
402 self.assertIsNone(err)
404 def test_double_task_id_jsons(self):
407 json_dir = os.path.join(self.temp_dir, 'deadbeef%d' % i)
408 json_path = os.path.join(json_dir, 'output.json')
409 if not os.path.exists(json_dir):
410 os.makedirs(json_dir)
411 with open(json_path, 'w') as f:
412 json.dump({'all_tests': ['LoadShardJsonTest.test%d' % i]}, f)
413 jsons_to_merge.append(json_path)
415 content, err = standard_gtest_merge.load_shard_json(
416 0, 'deadbeef0', jsons_to_merge)
417 self.assertEqual({'all_tests': ['LoadShardJsonTest.test0']},
419 self.assertIsNone(err)
421 content, err = standard_gtest_merge.load_shard_json(
422 12, 'deadbeef12', jsons_to_merge)
423 self.assertEqual({'all_tests': ['LoadShardJsonTest.test12']},
425 self.assertIsNone(err)
428 class MergeShardResultsTest(_StandardGtestMergeTest):
429 """Tests for merge_shard_results function."""
431 # pylint: disable=super-with-arguments
433 super(MergeShardResultsTest, self).setUp()
436 # pylint: enable=super-with-arguments
438 def stage(self, summary, files):
439 self.summary = self._write_temp_file('summary.json', summary)
440 for path, content in files.items():
441 abs_path = self._write_temp_file(path, content)
442 self.test_files.append(abs_path)
445 stdout = six.StringIO()
446 with mock.patch('sys.stdout', stdout):
447 merged = standard_gtest_merge.merge_shard_results(
448 self.summary, self.test_files)
449 return merged, stdout.getvalue().strip()
451 def assertUnicodeEquals(self, expectation, result):
452 def convert_to_unicode(key_or_value):
453 if isinstance(key_or_value, str):
454 return six.text_type(key_or_value)
455 if isinstance(key_or_value, dict):
456 return {convert_to_unicode(k): convert_to_unicode(v)
457 for k, v in key_or_value.items()}
458 if isinstance(key_or_value, list):
459 return [convert_to_unicode(x) for x in key_or_value]
462 unicode_expectations = convert_to_unicode(expectation)
463 unicode_result = convert_to_unicode(result)
464 self.assertEquals(unicode_expectations, unicode_result)
467 # Two shards, both successfully finished.
471 u'state': u'COMPLETED',
474 u'state': u'COMPLETED',
479 '0/output.json': GOOD_GTEST_JSON_0,
480 '1/output.json': GOOD_GTEST_JSON_1,
482 merged, stdout = self.call()
483 merged['swarming_summary'] = {
486 u'state': u'COMPLETED',
488 u'view_url': u'blah',
493 self.assertUnicodeEquals(GOOD_GTEST_JSON_MERGED, merged)
494 self.assertEqual('', stdout)
496 def test_timed_out(self):
497 # Two shards, both successfully finished.
501 'state': 'COMPLETED',
504 'state': 'TIMED_OUT',
509 '0/output.json': GOOD_GTEST_JSON_0,
510 '1/output.json': TIMED_OUT_GTEST_JSON_1,
512 merged, stdout = self.call()
514 self.assertUnicodeEquals(TIMED_OUT_GTEST_JSON_MERGED, merged)
516 'Test runtime exceeded allocated time\n', stdout)
518 def test_missing_summary_json(self):
519 # summary.json is missing, should return None and emit warning.
520 self.summary = os.path.join(self.temp_dir, 'summary.json')
521 merged, output = self.call()
522 self.assertEqual(None, merged)
523 self.assertIn('@@@STEP_WARNINGS@@@', output)
524 self.assertIn('summary.json is missing or can not be read', output)
526 def test_unfinished_shards(self):
527 # Only one shard (#1) finished. Shard #0 did not.
532 u'state': u'COMPLETED',
537 u'1/output.json': GOOD_GTEST_JSON_1,
539 merged, stdout = self.call()
540 merged.pop('swarming_summary')
541 self.assertUnicodeEquals(BAD_GTEST_JSON_ONLY_1_SHARD, merged)
543 '@@@STEP_WARNINGS@@@\nsome shards did not complete: 0\n', stdout)
545 '@@@STEP_LOG_LINE@some shards did not complete: 0@'
546 'Missing results from the following shard(s): 0@@@\n', stdout)
548 def test_missing_output_json(self):
549 # Shard #0 output json is missing.
553 u'state': u'COMPLETED',
556 u'state': u'COMPLETED',
561 u'1/output.json': GOOD_GTEST_JSON_1,
563 merged, stdout = self.call()
564 merged.pop('swarming_summary')
565 self.assertUnicodeEquals(BAD_GTEST_JSON_ONLY_1_SHARD, merged)
567 'No result was found: '
568 'shard 0 test output was missing', stdout)
570 def test_large_output_json(self):
571 # a shard is too large.
575 u'state': u'COMPLETED',
578 u'state': u'COMPLETED',
583 '0/output.json': GOOD_GTEST_JSON_0,
584 '1/output.json': GOOD_GTEST_JSON_1,
586 old_json_limit = standard_gtest_merge.OUTPUT_JSON_SIZE_LIMIT
587 len0 = len(json.dumps(GOOD_GTEST_JSON_0))
588 len1 = len(json.dumps(GOOD_GTEST_JSON_1))
589 large_shard = "0" if len0 > len1 else "1"
591 # Override max output.json size just for this test.
592 standard_gtest_merge.OUTPUT_JSON_SIZE_LIMIT = min(len0,len1)
593 merged, stdout = self.call()
594 merged.pop('swarming_summary')
595 self.assertUnicodeEquals(BAD_GTEST_JSON_ONLY_1_SHARD, merged)
597 'No result was found: '
598 'shard %s test output exceeded the size limit' % large_shard, stdout)
600 standard_gtest_merge.OUTPUT_JSON_SIZE_LIMIT = old_json_limit
603 class CommandLineTest(common_merge_script_tests.CommandLineTest):
605 # pylint: disable=super-with-arguments
606 def __init__(self, methodName='runTest'):
607 super(CommandLineTest, self).__init__(methodName, standard_gtest_merge)
608 # pylint: enable=super-with-arguments
611 if __name__ == '__main__':
613 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
615 unittest.TestCase.maxDiff = None