OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Verify basic usage of sharding_supervisor.""" | 6 """Verify basic usage of sharding_supervisor.""" |
7 | 7 |
| 8 import difflib |
8 import os | 9 import os |
9 import subprocess | 10 import subprocess |
10 import sys | 11 import sys |
11 import unittest | 12 import unittest |
12 | 13 |
| 14 from xml.dom import minidom |
| 15 |
13 import sharding_supervisor | 16 import sharding_supervisor |
14 | 17 |
15 SHARDING_SUPERVISOR = os.path.join(os.path.dirname(sys.argv[0]), | 18 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) |
16 'sharding_supervisor.py') | 19 SHARDING_SUPERVISOR = os.path.join(ROOT_DIR, 'sharding_supervisor.py') |
17 DUMMY_TEST = os.path.join(os.path.dirname(sys.argv[0]), 'dummy_test.py') | 20 DUMMY_TEST = os.path.join(ROOT_DIR, 'dummy_test.py') |
18 NUM_CORES = sharding_supervisor.DetectNumCores() | 21 NUM_CORES = sharding_supervisor.DetectNumCores() |
19 SHARDS_PER_CORE = sharding_supervisor.SS_DEFAULT_SHARDS_PER_CORE | 22 SHARDS_PER_CORE = sharding_supervisor.SS_DEFAULT_SHARDS_PER_CORE |
20 | 23 |
21 | 24 |
22 def generate_expected_output(start, end, num_shards): | 25 def generate_expected_output(start, end, num_shards): |
23 """Generate the expected stdout and stderr for the dummy test.""" | 26 """Generate the expected stdout and stderr for the dummy test.""" |
24 stdout = '' | 27 stdout = '' |
25 stderr = '' | 28 stderr = '' |
26 for i in range(start, end): | 29 for i in range(start, end): |
27 stdout += 'Running shard %d of %d\n' % (i, num_shards) | 30 stdout += 'Running shard %d of %d\n' % (i, num_shards) |
28 stdout += '\nALL SHARDS PASSED!\nALL TESTS PASSED!\n' | 31 stdout += '\nALL SHARDS PASSED!\nALL TESTS PASSED!\n' |
29 | 32 |
30 return (stdout, stderr) | 33 return (stdout, stderr) |
31 | 34 |
32 | 35 |
33 class ShardingSupervisorUnittest(unittest.TestCase): | 36 class ShardingSupervisorUnittest(unittest.TestCase): |
34 def test_basic_run(self): | 37 def test_basic_run(self): |
35 # Default test. | 38 # Default test. |
36 expected_shards = NUM_CORES * SHARDS_PER_CORE | 39 expected_shards = NUM_CORES * SHARDS_PER_CORE |
37 (expected_out, expected_err) = \ | 40 (expected_out, expected_err) = generate_expected_output( |
38 generate_expected_output(0, expected_shards, expected_shards) | 41 0, expected_shards, expected_shards) |
39 p = subprocess.Popen([SHARDING_SUPERVISOR, '--no-color', DUMMY_TEST], | 42 p = subprocess.Popen([SHARDING_SUPERVISOR, '--no-color', DUMMY_TEST], |
40 stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 43 stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
41 | 44 |
42 (out, err) = p.communicate() | 45 (out, err) = p.communicate() |
43 self.assertEqual(expected_out, out) | 46 self.assertEqual(expected_out, out) |
44 self.assertEqual(expected_err, err) | 47 self.assertEqual(expected_err, err) |
45 self.assertEqual(0, p.returncode) | 48 self.assertEqual(0, p.returncode) |
46 | 49 |
47 def test_shard_per_core(self): | 50 def test_shard_per_core(self): |
48 """Test the --shards_per_core parameter.""" | 51 """Test the --shards_per_core parameter.""" |
49 expected_shards = NUM_CORES * 25 | 52 expected_shards = NUM_CORES * 25 |
50 (expected_out, expected_err) = \ | 53 (expected_out, expected_err) = generate_expected_output( |
51 generate_expected_output(0, expected_shards, expected_shards) | 54 0, expected_shards, expected_shards) |
52 p = subprocess.Popen([SHARDING_SUPERVISOR, '--no-color', | 55 p = subprocess.Popen([SHARDING_SUPERVISOR, '--no-color', |
53 '--shards_per_core', '25', DUMMY_TEST], | 56 '--shards_per_core', '25', DUMMY_TEST], |
54 stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 57 stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
55 | 58 |
56 (out, err) = p.communicate() | 59 (out, err) = p.communicate() |
57 self.assertEqual(expected_out, out) | 60 self.assertEqual(expected_out, out) |
58 self.assertEqual(expected_err, err) | 61 self.assertEqual(expected_err, err) |
59 self.assertEqual(0, p.returncode) | 62 self.assertEqual(0, p.returncode) |
60 | 63 |
61 def test_slave_sharding(self): | 64 def test_slave_sharding(self): |
62 """Test the --total-slaves and --slave-index parameters.""" | 65 """Test the --total-slaves and --slave-index parameters.""" |
63 total_shards = 6 | 66 total_shards = 6 |
64 expected_shards = NUM_CORES * SHARDS_PER_CORE * total_shards | 67 expected_shards = NUM_CORES * SHARDS_PER_CORE * total_shards |
65 | 68 |
66 # Test every single index to make sure they run correctly. | 69 # Test every single index to make sure they run correctly. |
67 for index in range(total_shards): | 70 for index in range(total_shards): |
68 begin = NUM_CORES * SHARDS_PER_CORE * index | 71 begin = NUM_CORES * SHARDS_PER_CORE * index |
69 end = begin + NUM_CORES * SHARDS_PER_CORE | 72 end = begin + NUM_CORES * SHARDS_PER_CORE |
70 (expected_out, expected_err) = \ | 73 (expected_out, expected_err) = generate_expected_output( |
71 generate_expected_output(begin, end, expected_shards) | 74 begin, end, expected_shards) |
72 p = subprocess.Popen([SHARDING_SUPERVISOR, '--no-color', | 75 p = subprocess.Popen([SHARDING_SUPERVISOR, '--no-color', |
73 '--total-slaves', str(total_shards), | 76 '--total-slaves', str(total_shards), |
74 '--slave-index', str(index), | 77 '--slave-index', str(index), |
75 DUMMY_TEST], | 78 DUMMY_TEST], |
76 stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 79 stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
77 | 80 |
78 (out, err) = p.communicate() | 81 (out, err) = p.communicate() |
79 self.assertEqual(expected_out, out) | 82 self.assertEqual(expected_out, out) |
80 self.assertEqual(expected_err, err) | 83 self.assertEqual(expected_err, err) |
81 self.assertEqual(0, p.returncode) | 84 self.assertEqual(0, p.returncode) |
82 | 85 |
| 86 def test_append_to_xml(self): |
| 87 shard_xml_path = os.path.join(ROOT_DIR, 'data', 'gtest_results.xml') |
| 88 expected_xml_path = os.path.join( |
| 89 ROOT_DIR, 'data', 'gtest_results_expected.xml') |
| 90 merged_xml = sharding_supervisor.AppendToXML(None, shard_xml_path, 0) |
| 91 merged_xml = sharding_supervisor.AppendToXML(merged_xml, shard_xml_path, 1) |
| 92 |
| 93 with open(expected_xml_path) as expected_xml_file: |
| 94 expected_xml = minidom.parse(expected_xml_file) |
| 95 |
| 96 # Serialize XML to a list of strings that is consistently formatted |
| 97 # (ignoring whitespace between elements) so that it may be compared. |
| 98 def _serialize_xml(xml): |
| 99 def _remove_whitespace_and_comments(xml): |
| 100 children_to_remove = [] |
| 101 for child in xml.childNodes: |
| 102 if (child.nodeType == minidom.Node.TEXT_NODE and |
| 103 not child.data.strip()): |
| 104 children_to_remove.append(child) |
| 105 elif child.nodeType == minidom.Node.COMMENT_NODE: |
| 106 children_to_remove.append(child) |
| 107 elif child.nodeType == minidom.Node.ELEMENT_NODE: |
| 108 _remove_whitespace_and_comments(child) |
| 109 |
| 110 for child in children_to_remove: |
| 111 xml.removeChild(child) |
| 112 |
| 113 _remove_whitespace_and_comments(xml) |
| 114 return xml.toprettyxml(indent=' ').splitlines() |
| 115 |
| 116 diff = list(difflib.unified_diff( |
| 117 _serialize_xml(expected_xml), |
| 118 _serialize_xml(merged_xml), |
| 119 fromfile='gtest_results_expected.xml', |
| 120 tofile='gtest_results_actual.xml')) |
| 121 if diff: |
| 122 self.fail('Did not merge results XML correctly:\n' + '\n'.join(diff)) |
| 123 |
83 | 124 |
84 if __name__ == '__main__': | 125 if __name__ == '__main__': |
85 unittest.main() | 126 unittest.main() |
OLD | NEW |