2 # Copyright 2014 The Chromium Authors. All rights reserved.
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
17 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
19 # For 'standard_gtest_merge.py'.
21 0, os.path.abspath(os.path.join(THIS_DIR, '..', 'resources')))
25 import standard_gtest_merge
28 # gtest json output for successfully finished shard #0.
31 'AlignedMemoryTest.DynamicAllocation',
32 'AlignedMemoryTest.ScopedDynamicAllocation',
33 'AlignedMemoryTest.StackAlignment',
34 'AlignedMemoryTest.StaticAlignment',
37 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
38 'FileTest.TouchGetInfo',
39 'MessageLoopTestTypeDefault.EnsureDeletion',
41 'global_tags': ['CPU_64_BITS', 'MODE_DEBUG', 'OS_LINUX', 'OS_POSIX'],
42 'per_iteration_data': [{
43 'AlignedMemoryTest.DynamicAllocation': [{
45 'losless_snippet': True,
46 'output_snippet': 'blah\\n',
47 'output_snippet_base64': 'YmxhaAo=',
50 'AlignedMemoryTest.ScopedDynamicAllocation': [{
52 'losless_snippet': True,
53 'output_snippet': 'blah\\n',
54 'output_snippet_base64': 'YmxhaAo=',
59 'AlignedMemoryTest.DynamicAllocation': {
60 'file': 'foo/bar/allocation_test.cc',
63 'AlignedMemoryTest.ScopedDynamicAllocation': {
64 'file': 'foo/bar/allocation_test.cc',
67 # This is a test from a different shard, but this happens in practice and we
68 # should not fail if information is repeated.
69 'AlignedMemoryTest.StaticAlignment': {
70 'file': 'foo/bar/allocation_test.cc',
77 # gtest json output for successfully finished shard #1.
80 'AlignedMemoryTest.DynamicAllocation',
81 'AlignedMemoryTest.ScopedDynamicAllocation',
82 'AlignedMemoryTest.StackAlignment',
83 'AlignedMemoryTest.StaticAlignment',
86 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
87 'FileTest.TouchGetInfo',
88 'MessageLoopTestTypeDefault.EnsureDeletion',
90 'global_tags': ['CPU_64_BITS', 'MODE_DEBUG', 'OS_LINUX', 'OS_POSIX'],
91 'per_iteration_data': [{
92 'AlignedMemoryTest.StackAlignment': [{
94 'losless_snippet': True,
95 'output_snippet': 'blah\\n',
96 'output_snippet_base64': 'YmxhaAo=',
99 'AlignedMemoryTest.StaticAlignment': [{
100 'elapsed_time_ms': 0,
101 'losless_snippet': True,
102 'output_snippet': 'blah\\n',
103 'output_snippet_base64': 'YmxhaAo=',
108 'AlignedMemoryTest.StackAlignment': {
109 'file': 'foo/bar/allocation_test.cc',
112 'AlignedMemoryTest.StaticAlignment': {
113 'file': 'foo/bar/allocation_test.cc',
120 TIMED_OUT_GTEST_JSON_1 = {
121 'disabled_tests': [],
124 'AlignedMemoryTest.DynamicAllocation',
125 'AlignedMemoryTest.ScopedDynamicAllocation',
126 'AlignedMemoryTest.StackAlignment',
127 'AlignedMemoryTest.StaticAlignment',
129 'per_iteration_data': [{
130 'AlignedMemoryTest.StackAlignment': [{
131 'elapsed_time_ms': 54000,
132 'losless_snippet': True,
133 'output_snippet': 'timed out',
134 'output_snippet_base64': '',
137 'AlignedMemoryTest.StaticAlignment': [{
138 'elapsed_time_ms': 0,
139 'losless_snippet': True,
140 'output_snippet': '',
141 'output_snippet_base64': '',
146 'AlignedMemoryTest.StackAlignment': {
147 'file': 'foo/bar/allocation_test.cc',
150 'AlignedMemoryTest.StaticAlignment': {
151 'file': 'foo/bar/allocation_test.cc',
157 # GOOD_GTEST_JSON_0 and GOOD_GTEST_JSON_1 merged.
158 GOOD_GTEST_JSON_MERGED = {
160 'AlignedMemoryTest.DynamicAllocation',
161 'AlignedMemoryTest.ScopedDynamicAllocation',
162 'AlignedMemoryTest.StackAlignment',
163 'AlignedMemoryTest.StaticAlignment',
166 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
167 'FileTest.TouchGetInfo',
168 'MessageLoopTestTypeDefault.EnsureDeletion',
170 'global_tags': ['CPU_64_BITS', 'MODE_DEBUG', 'OS_LINUX', 'OS_POSIX'],
171 'missing_shards': [],
172 'per_iteration_data': [{
173 'AlignedMemoryTest.DynamicAllocation': [{
174 'elapsed_time_ms': 0,
175 'losless_snippet': True,
176 'output_snippet': 'blah\\n',
177 'output_snippet_base64': 'YmxhaAo=',
180 'AlignedMemoryTest.ScopedDynamicAllocation': [{
181 'elapsed_time_ms': 0,
182 'losless_snippet': True,
183 'output_snippet': 'blah\\n',
184 'output_snippet_base64': 'YmxhaAo=',
187 'AlignedMemoryTest.StackAlignment': [{
188 'elapsed_time_ms': 0,
189 'losless_snippet': True,
190 'output_snippet': 'blah\\n',
191 'output_snippet_base64': 'YmxhaAo=',
194 'AlignedMemoryTest.StaticAlignment': [{
195 'elapsed_time_ms': 0,
196 'losless_snippet': True,
197 'output_snippet': 'blah\\n',
198 'output_snippet_base64': 'YmxhaAo=',
202 'swarming_summary': {
205 u'state': u'COMPLETED',
207 u'view_url': u'blah',
213 'AlignedMemoryTest.StackAlignment': {
214 'file': 'foo/bar/allocation_test.cc',
217 'AlignedMemoryTest.StaticAlignment': {
218 'file': 'foo/bar/allocation_test.cc',
221 'AlignedMemoryTest.DynamicAllocation': {
222 'file': 'foo/bar/allocation_test.cc',
225 'AlignedMemoryTest.ScopedDynamicAllocation': {
226 'file': 'foo/bar/allocation_test.cc',
233 # Only shard #1 finished. UNRELIABLE_RESULTS is set.
234 BAD_GTEST_JSON_ONLY_1_SHARD = {
236 'AlignedMemoryTest.DynamicAllocation',
237 'AlignedMemoryTest.ScopedDynamicAllocation',
238 'AlignedMemoryTest.StackAlignment',
239 'AlignedMemoryTest.StaticAlignment',
242 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
243 'FileTest.TouchGetInfo',
244 'MessageLoopTestTypeDefault.EnsureDeletion',
251 'UNRELIABLE_RESULTS',
253 'missing_shards': [0],
254 'per_iteration_data': [{
255 'AlignedMemoryTest.StackAlignment': [{
256 'elapsed_time_ms': 0,
257 'losless_snippet': True,
258 'output_snippet': 'blah\\n',
259 'output_snippet_base64': 'YmxhaAo=',
262 'AlignedMemoryTest.StaticAlignment': [{
263 'elapsed_time_ms': 0,
264 'losless_snippet': True,
265 'output_snippet': 'blah\\n',
266 'output_snippet_base64': 'YmxhaAo=',
271 'AlignedMemoryTest.StackAlignment': {
272 'file': 'foo/bar/allocation_test.cc',
275 'AlignedMemoryTest.StaticAlignment': {
276 'file': 'foo/bar/allocation_test.cc',
283 # GOOD_GTEST_JSON_0 and TIMED_OUT_GTEST_JSON_1 merged.
284 TIMED_OUT_GTEST_JSON_MERGED = {
286 'AlignedMemoryTest.DynamicAllocation',
287 'AlignedMemoryTest.ScopedDynamicAllocation',
288 'AlignedMemoryTest.StackAlignment',
289 'AlignedMemoryTest.StaticAlignment',
292 'ConditionVariableTest.TimeoutAcrossSetTimeOfDay',
293 'FileTest.TouchGetInfo',
294 'MessageLoopTestTypeDefault.EnsureDeletion',
296 'global_tags': ['CPU_64_BITS', 'MODE_DEBUG', 'OS_LINUX', 'OS_POSIX'],
297 'missing_shards': [],
298 'per_iteration_data': [{
299 'AlignedMemoryTest.DynamicAllocation': [{
300 'elapsed_time_ms': 0,
301 'losless_snippet': True,
302 'output_snippet': 'blah\\n',
303 'output_snippet_base64': 'YmxhaAo=',
306 'AlignedMemoryTest.ScopedDynamicAllocation': [{
307 'elapsed_time_ms': 0,
308 'losless_snippet': True,
309 'output_snippet': 'blah\\n',
310 'output_snippet_base64': 'YmxhaAo=',
313 'AlignedMemoryTest.StackAlignment': [{
314 'elapsed_time_ms': 54000,
315 'losless_snippet': True,
316 'output_snippet': 'timed out',
317 'output_snippet_base64': '',
320 'AlignedMemoryTest.StaticAlignment': [{
321 'elapsed_time_ms': 0,
322 'losless_snippet': True,
323 'output_snippet': '',
324 'output_snippet_base64': '',
328 'swarming_summary': {
331 u'state': u'COMPLETED',
334 u'state': u'TIMED_OUT',
339 'AlignedMemoryTest.StackAlignment': {
340 'file': 'foo/bar/allocation_test.cc',
343 'AlignedMemoryTest.StaticAlignment': {
344 'file': 'foo/bar/allocation_test.cc',
347 'AlignedMemoryTest.DynamicAllocation': {
348 'file': 'foo/bar/allocation_test.cc',
351 'AlignedMemoryTest.ScopedDynamicAllocation': {
352 'file': 'foo/bar/allocation_test.cc',
359 class _StandardGtestMergeTest(unittest.TestCase):
362 self.temp_dir = tempfile.mkdtemp()
365 shutil.rmtree(self.temp_dir)
367 def _write_temp_file(self, path, content):
368 abs_path = os.path.join(self.temp_dir, path.replace('/', os.sep))
369 if not os.path.exists(os.path.dirname(abs_path)):
370 os.makedirs(os.path.dirname(abs_path))
371 with open(abs_path, 'w') as f:
372 if isinstance(content, dict):
373 json.dump(content, f)
375 assert isinstance(content, str)
380 class LoadShardJsonTest(_StandardGtestMergeTest):
382 def test_double_digit_jsons(self):
385 json_dir = os.path.join(self.temp_dir, str(i))
386 json_path = os.path.join(json_dir, 'output.json')
387 if not os.path.exists(json_dir):
388 os.makedirs(json_dir)
389 with open(json_path, 'w') as f:
390 json.dump({'all_tests': ['LoadShardJsonTest.test%d' % i]}, f)
391 jsons_to_merge.append(json_path)
393 content, err = standard_gtest_merge.load_shard_json(
394 0, None, jsons_to_merge)
395 self.assertEqual({'all_tests': ['LoadShardJsonTest.test0']}, content)
396 self.assertIsNone(err)
398 content, err = standard_gtest_merge.load_shard_json(
399 12, None, jsons_to_merge)
400 self.assertEqual({'all_tests': ['LoadShardJsonTest.test12']}, content)
401 self.assertIsNone(err)
403 def test_double_task_id_jsons(self):
406 json_dir = os.path.join(self.temp_dir, 'deadbeef%d' % i)
407 json_path = os.path.join(json_dir, 'output.json')
408 if not os.path.exists(json_dir):
409 os.makedirs(json_dir)
410 with open(json_path, 'w') as f:
411 json.dump({'all_tests': ['LoadShardJsonTest.test%d' % i]}, f)
412 jsons_to_merge.append(json_path)
414 content, err = standard_gtest_merge.load_shard_json(
415 0, 'deadbeef0', jsons_to_merge)
416 self.assertEqual({'all_tests': ['LoadShardJsonTest.test0']},
418 self.assertIsNone(err)
420 content, err = standard_gtest_merge.load_shard_json(
421 12, 'deadbeef12', jsons_to_merge)
422 self.assertEqual({'all_tests': ['LoadShardJsonTest.test12']},
424 self.assertIsNone(err)
427 class MergeShardResultsTest(_StandardGtestMergeTest):
428 """Tests for merge_shard_results function."""
431 super(MergeShardResultsTest, self).setUp()
435 def stage(self, summary, files):
436 self.summary = self._write_temp_file('summary.json', summary)
437 for path, content in files.iteritems():
438 abs_path = self._write_temp_file(path, content)
439 self.test_files.append(abs_path)
442 stdout = cStringIO.StringIO()
443 with mock.patch('sys.stdout', stdout):
444 merged = standard_gtest_merge.merge_shard_results(
445 self.summary, self.test_files)
446 return merged, stdout.getvalue().strip()
448 def assertUnicodeEquals(self, expectation, result):
449 def convert_to_unicode(key_or_value):
450 if isinstance(key_or_value, str):
451 return unicode(key_or_value)
452 if isinstance(key_or_value, dict):
453 return {convert_to_unicode(k): convert_to_unicode(v)
454 for k, v in key_or_value.items()}
455 if isinstance(key_or_value, list):
456 return [convert_to_unicode(x) for x in key_or_value]
459 unicode_expectations = convert_to_unicode(expectation)
460 unicode_result = convert_to_unicode(result)
461 self.assertEquals(unicode_expectations, unicode_result)
464 # Two shards, both successfully finished.
468 u'state': u'COMPLETED',
471 u'state': u'COMPLETED',
476 '0/output.json': GOOD_GTEST_JSON_0,
477 '1/output.json': GOOD_GTEST_JSON_1,
479 merged, stdout = self.call()
480 merged['swarming_summary'] = {
483 u'state': u'COMPLETED',
485 u'view_url': u'blah',
490 self.assertUnicodeEquals(GOOD_GTEST_JSON_MERGED, merged)
491 self.assertEqual('', stdout)
493 def test_timed_out(self):
494 # Two shards, both successfully finished.
498 'state': 'COMPLETED',
501 'state': 'TIMED_OUT',
506 '0/output.json': GOOD_GTEST_JSON_0,
507 '1/output.json': TIMED_OUT_GTEST_JSON_1,
509 merged, stdout = self.call()
511 self.assertUnicodeEquals(TIMED_OUT_GTEST_JSON_MERGED, merged)
513 'Test runtime exceeded allocated time\n', stdout)
515 def test_missing_summary_json(self):
516 # summary.json is missing, should return None and emit warning.
517 self.summary = os.path.join(self.temp_dir, 'summary.json')
518 merged, output = self.call()
519 self.assertEqual(None, merged)
520 self.assertIn('@@@STEP_WARNINGS@@@', output)
521 self.assertIn('summary.json is missing or can not be read', output)
523 def test_unfinished_shards(self):
524 # Only one shard (#1) finished. Shard #0 did not.
529 u'state': u'COMPLETED',
534 u'1/output.json': GOOD_GTEST_JSON_1,
536 merged, stdout = self.call()
537 merged.pop('swarming_summary')
538 self.assertUnicodeEquals(BAD_GTEST_JSON_ONLY_1_SHARD, merged)
540 '@@@STEP_WARNINGS@@@\nsome shards did not complete: 0\n', stdout)
542 '@@@STEP_LOG_LINE@some shards did not complete: 0@'
543 'Missing results from the following shard(s): 0@@@\n', stdout)
545 def test_missing_output_json(self):
546 # Shard #0 output json is missing.
550 u'state': u'COMPLETED',
553 u'state': u'COMPLETED',
558 u'1/output.json': GOOD_GTEST_JSON_1,
560 merged, stdout = self.call()
561 merged.pop('swarming_summary')
562 self.assertUnicodeEquals(BAD_GTEST_JSON_ONLY_1_SHARD, merged)
564 'No result was found: '
565 'shard 0 test output was missing', stdout)
567 def test_large_output_json(self):
568 # a shard is too large.
572 u'state': u'COMPLETED',
575 u'state': u'COMPLETED',
580 '0/output.json': GOOD_GTEST_JSON_0,
581 '1/output.json': GOOD_GTEST_JSON_1,
583 old_json_limit = standard_gtest_merge.OUTPUT_JSON_SIZE_LIMIT
584 len0 = len(json.dumps(GOOD_GTEST_JSON_0))
585 len1 = len(json.dumps(GOOD_GTEST_JSON_1))
586 large_shard = "0" if len0 > len1 else "1"
588 # Override max output.json size just for this test.
589 standard_gtest_merge.OUTPUT_JSON_SIZE_LIMIT = min(len0,len1)
590 merged, stdout = self.call()
591 merged.pop('swarming_summary')
592 self.assertUnicodeEquals(BAD_GTEST_JSON_ONLY_1_SHARD, merged)
594 'No result was found: '
595 'shard %s test output exceeded the size limit' % large_shard, stdout)
597 standard_gtest_merge.OUTPUT_JSON_SIZE_LIMIT = old_json_limit
600 class CommandLineTest(common_merge_script_tests.CommandLineTest):
602 def __init__(self, methodName='runTest'):
603 super(CommandLineTest, self).__init__(methodName, standard_gtest_merge)
606 if __name__ == '__main__':
608 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
610 unittest.TestCase.maxDiff = None