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 """Shards a given test suite and runs the shards in parallel. | 6 """Shards a given test suite and runs the shards in parallel. |
7 | 7 |
8 ShardingSupervisor is called to process the command line options and creates | 8 ShardingSupervisor is called to process the command line options and creates |
9 the specified number of worker threads. These threads then run each shard of | 9 the specified number of worker threads. These threads then run each shard of |
10 the test in a separate process and report on the results. When all the shards | 10 the test in a separate process and report on the results. When all the shards |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
116 | 116 |
117 if not final_xml: | 117 if not final_xml: |
118 # Out final xml is empty, let's prepopulate it with the first one we see. | 118 # Out final xml is empty, let's prepopulate it with the first one we see. |
119 return shard_xml | 119 return shard_xml |
120 | 120 |
121 shard_node = shard_xml.documentElement | 121 shard_node = shard_xml.documentElement |
122 final_node = final_xml.documentElement | 122 final_node = final_xml.documentElement |
123 | 123 |
124 testcases = shard_node.getElementsByTagName('testcase') | 124 testcases = shard_node.getElementsByTagName('testcase') |
125 final_testcases = final_node.getElementsByTagName('testcase') | 125 final_testcases = final_node.getElementsByTagName('testcase') |
| 126 |
| 127 final_testsuites = final_node.getElementsByTagName('testsuite') |
| 128 final_testsuites_by_name = dict( |
| 129 (suite.getAttribute('name'), suite) for suite in final_testsuites) |
| 130 |
126 for testcase in testcases: | 131 for testcase in testcases: |
127 name = testcase.getAttribute('name') | 132 name = testcase.getAttribute('name') |
128 classname = testcase.getAttribute('classname') | 133 classname = testcase.getAttribute('classname') |
129 failures = testcase.getElementsByTagName('failure') | 134 failures = testcase.getElementsByTagName('failure') |
130 status = testcase.getAttribute('status') | 135 status = testcase.getAttribute('status') |
131 elapsed = testcase.getAttribute('time') | 136 elapsed = testcase.getAttribute('time') |
132 | 137 |
133 # don't bother updating the final xml if there is no data. | 138 # don't bother updating the final xml if there is no data. |
134 if status == 'notrun': | 139 if status == 'notrun': |
135 continue | 140 continue |
136 | 141 |
137 # Look in our final xml to see if it's there. | 142 # Look in our final xml to see if it's there. |
138 # There has to be a better way... | 143 # There has to be a better way... |
| 144 merged_into_final_testcase = False |
139 for final_testcase in final_testcases: | 145 for final_testcase in final_testcases: |
140 final_name = final_testcase.getAttribute('name') | 146 final_name = final_testcase.getAttribute('name') |
141 final_classname = final_testcase.getAttribute('classname') | 147 final_classname = final_testcase.getAttribute('classname') |
142 if final_name == name and final_classname == classname: | 148 if final_name == name and final_classname == classname: |
143 # We got the same entry. | 149 # We got the same entry. |
144 final_testcase.setAttribute('status', status) | 150 final_testcase.setAttribute('status', status) |
145 final_testcase.setAttribute('time', elapsed) | 151 final_testcase.setAttribute('time', elapsed) |
146 for failure in failures: | 152 for failure in failures: |
147 final_testcase.appendChild(failure) | 153 final_testcase.appendChild(failure) |
| 154 merged_into_final_testcase = True |
| 155 |
| 156 # We couldn't find an existing testcase to merge the results into, so we |
| 157 # copy the node into the existing test suite. |
| 158 if not merged_into_final_testcase: |
| 159 testsuite = testcase.parentNode |
| 160 final_testsuite = final_testsuites_by_name[testsuite.getAttribute('name')] |
| 161 final_testsuite.appendChild(testcase) |
148 | 162 |
149 return final_xml | 163 return final_xml |
150 | 164 |
151 | 165 |
152 def RunShard(test, total_shards, index, gtest_args, stdout, stderr): | 166 def RunShard(test, total_shards, index, gtest_args, stdout, stderr): |
153 """Runs a single test shard in a subprocess. | 167 """Runs a single test shard in a subprocess. |
154 | 168 |
155 Returns: | 169 Returns: |
156 The Popen object representing the subprocess handle. | 170 The Popen object representing the subprocess handle. |
157 """ | 171 """ |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
281 shard_output: Buffer that stores the output from each shard. | 295 shard_output: Buffer that stores the output from each shard. |
282 test_counter: Stores the total number of tests run. | 296 test_counter: Stores the total number of tests run. |
283 total_slaves: Total number of slaves running this test. | 297 total_slaves: Total number of slaves running this test. |
284 slave_index: Current slave to run tests for. | 298 slave_index: Current slave to run tests for. |
285 | 299 |
286 If total_slaves is set, we run only a subset of the tests. This is meant to be | 300 If total_slaves is set, we run only a subset of the tests. This is meant to be |
287 used when we want to shard across machines as well as across cpus. In that | 301 used when we want to shard across machines as well as across cpus. In that |
288 case the number of shards to execute will be the same, but they will be | 302 case the number of shards to execute will be the same, but they will be |
289 smaller, as the total number of shards in the test suite will be multiplied | 303 smaller, as the total number of shards in the test suite will be multiplied |
290 by 'total_slaves'. | 304 by 'total_slaves'. |
291 | 305 |
292 For example, if you are on a quad core machine, the sharding supervisor by | 306 For example, if you are on a quad core machine, the sharding supervisor by |
293 default will use 20 shards for the whole suite. However, if you set | 307 default will use 20 shards for the whole suite. However, if you set |
294 total_slaves to 2, it will split the suite in 40 shards and will only run | 308 total_slaves to 2, it will split the suite in 40 shards and will only run |
295 shards [0-19] or shards [20-39] depending if you set slave_index to 0 or 1. | 309 shards [0-19] or shards [20-39] depending if you set slave_index to 0 or 1. |
296 """ | 310 """ |
297 | 311 |
298 SHARD_COMPLETED = object() | 312 SHARD_COMPLETED = object() |
299 | 313 |
300 def __init__(self, test, num_shards_to_run, num_runs, color, original_order, | 314 def __init__(self, test, num_shards_to_run, num_runs, color, original_order, |
301 prefix, retry_percent, timeout, total_slaves, slave_index, | 315 prefix, retry_percent, timeout, total_slaves, slave_index, |
(...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 # shard and run the whole test | 659 # shard and run the whole test |
646 ss = ShardingSupervisor( | 660 ss = ShardingSupervisor( |
647 test, num_shards_to_run, num_runs, options.color, | 661 test, num_shards_to_run, num_runs, options.color, |
648 options.original_order, options.prefix, options.retry_percent, | 662 options.original_order, options.prefix, options.retry_percent, |
649 options.timeout, options.total_slaves, options.slave_index, gtest_args) | 663 options.timeout, options.total_slaves, options.slave_index, gtest_args) |
650 return ss.ShardTest() | 664 return ss.ShardTest() |
651 | 665 |
652 | 666 |
653 if __name__ == "__main__": | 667 if __name__ == "__main__": |
654 sys.exit(main()) | 668 sys.exit(main()) |
OLD | NEW |