OLD | NEW |
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python2 |
2 # Copyright 2013 Google Inc. All rights reserved. | 2 # Copyright 2013 Google Inc. All rights reserved. |
3 # | 3 # |
4 # Licensed under the Apache License, Version 2.0 (the "License"); | 4 # Licensed under the Apache License, Version 2.0 (the "License"); |
5 # you may not use this file except in compliance with the License. | 5 # you may not use this file except in compliance with the License. |
6 # You may obtain a copy of the License at | 6 # You may obtain a copy of the License at |
7 # | 7 # |
8 # http://www.apache.org/licenses/LICENSE-2.0 | 8 # http://www.apache.org/licenses/LICENSE-2.0 |
9 # | 9 # |
10 # Unless required by applicable law or agreed to in writing, software | 10 # Unless required by applicable law or agreed to in writing, software |
11 # distributed under the License is distributed on an "AS IS" BASIS, | 11 # distributed under the License is distributed on an "AS IS" BASIS, |
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 # See the License for the specific language governing permissions and | 13 # See the License for the specific language governing permissions and |
14 # limitations under the License. | 14 # limitations under the License. |
15 import cPickle | 15 import cPickle |
16 import errno | 16 import errno |
17 import gzip | 17 import gzip |
18 import json | 18 import json |
19 import multiprocessing | 19 import multiprocessing |
20 import optparse | 20 import optparse |
21 import os | 21 import os |
| 22 import re |
22 import signal | 23 import signal |
23 import subprocess | 24 import subprocess |
24 import sys | 25 import sys |
25 import tempfile | 26 import tempfile |
26 import thread | 27 import thread |
27 import threading | 28 import threading |
28 import time | 29 import time |
29 import zlib | 30 import zlib |
30 | 31 |
31 # An object that catches SIGINT sent to the Python process and notices | 32 # An object that catches SIGINT sent to the Python process and notices |
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
386 # run if it was successful, or None if the test is new or the most | 387 # run if it was successful, or None if the test is new or the most |
387 # recent run failed. | 388 # recent run failed. |
388 tests = [x for x in tests if x[0] is None] | 389 tests = [x for x in tests if x[0] is None] |
389 | 390 |
390 # Sort tests by falling runtime (with None, which is what we get for | 391 # Sort tests by falling runtime (with None, which is what we get for |
391 # new and failing tests, being considered larger than any real | 392 # new and failing tests, being considered larger than any real |
392 # runtime). | 393 # runtime). |
393 tests.sort(reverse=True, key=lambda x: ((1 if x[0] is None else 0), x)) | 394 tests.sort(reverse=True, key=lambda x: ((1 if x[0] is None else 0), x)) |
394 | 395 |
395 # Repeat tests (-r flag). | 396 # Repeat tests (-r flag). |
396 tests *= options.repeat | 397 tests = [(test, i + 1) for test in tests for i in range(options.repeat)] |
397 test_lock = threading.Lock() | 398 test_lock = threading.Lock() |
398 job_id = 0 | 399 job_id = 0 |
399 logger.log(str(-1) + ': TESTCNT ' + ' ' + str(len(tests))) | 400 logger.log(str(-1) + ': TESTCNT ' + ' ' + str(len(tests))) |
400 | 401 |
401 exit_code = 0 | 402 exit_code = 0 |
402 | 403 |
403 # Create directory for test log output. | 404 # Create directory for test log output. |
404 try: | 405 try: |
405 os.makedirs(options.output_dir) | 406 os.makedirs(options.output_dir) |
406 except OSError as e: | 407 except OSError as e: |
407 # Ignore errors if this directory already exists. | 408 # Ignore errors if this directory already exists. |
408 if e.errno != errno.EEXIST or not os.path.isdir(options.output_dir): | 409 if e.errno != errno.EEXIST or not os.path.isdir(options.output_dir): |
409 raise e | 410 raise e |
410 # Remove files from old test runs. | 411 # Remove files from old test runs. |
411 for logfile in os.listdir(options.output_dir): | 412 for logfile in os.listdir(options.output_dir): |
412 os.remove(os.path.join(options.output_dir, logfile)) | 413 os.remove(os.path.join(options.output_dir, logfile)) |
413 | 414 |
414 # Run the specified job. Return the elapsed time in milliseconds if | 415 # Run the specified job. Return the elapsed time in milliseconds if |
415 # the job succeeds, or None if the job fails. (This ensures that | 416 # the job succeeds, or None if the job fails. (This ensures that |
416 # failing tests will run first the next time.) | 417 # failing tests will run first the next time.) |
417 def run_job((command, job_id, test)): | 418 def run_job((command, job_id, test, test_index)): |
418 begin = time.time() | 419 begin = time.time() |
419 | 420 |
420 with tempfile.NamedTemporaryFile(dir=options.output_dir, delete=False) as log: | 421 test_name = re.sub('[^A-Za-z0-9]', '_', test) + '-' + str(test_index) + '.log' |
| 422 with open(os.path.join(options.output_dir, test_name), 'w') as log: |
421 sub = subprocess.Popen(command + ['--gtest_filter=' + test] + | 423 sub = subprocess.Popen(command + ['--gtest_filter=' + test] + |
422 ['--gtest_color=' + options.gtest_color], | 424 ['--gtest_color=' + options.gtest_color], |
423 stdout=log.file, | 425 stdout=log, stderr=log) |
424 stderr=log.file) | |
425 try: | 426 try: |
426 code = sigint_handler.wait(sub) | 427 code = sigint_handler.wait(sub) |
427 except sigint_handler.ProcessWasInterrupted: | 428 except sigint_handler.ProcessWasInterrupted: |
428 thread.exit() | 429 thread.exit() |
429 runtime_ms = int(1000 * (time.time() - begin)) | 430 runtime_ms = int(1000 * (time.time() - begin)) |
430 logger.logfile(job_id, log.name) | 431 logger.logfile(job_id, log.name) |
431 | 432 |
432 test_results.log(test, { | 433 test_results.log(test, { |
433 "expected": "PASS", | 434 "expected": "PASS", |
434 "actual": "PASS" if code == 0 else "FAIL", | 435 "actual": "PASS" if code == 0 else "FAIL", |
435 "time": runtime_ms, | 436 "time": runtime_ms, |
436 }) | 437 }) |
437 logger.log("%s: EXIT %s %d" % (job_id, code, runtime_ms)) | 438 logger.log("%s: EXIT %s %d" % (job_id, code, runtime_ms)) |
438 if code == 0: | 439 if code == 0: |
439 return runtime_ms | 440 return runtime_ms |
440 global exit_code | 441 global exit_code |
441 exit_code = code | 442 exit_code = code |
442 return None | 443 return None |
443 | 444 |
444 def worker(): | 445 def worker(): |
445 global job_id | 446 global job_id |
446 while True: | 447 while True: |
447 job = None | 448 job = None |
448 test_lock.acquire() | 449 test_lock.acquire() |
449 if job_id < len(tests): | 450 if job_id < len(tests): |
450 (_, test_binary, test, command) = tests[job_id] | 451 ((_, test_binary, test, command), test_index) = tests[job_id] |
451 logger.log(str(job_id) + ': TEST ' + test_binary + ' ' + test) | 452 logger.log(str(job_id) + ': TEST ' + test_binary + ' ' + test) |
452 job = (command, job_id, test) | 453 job = (command, job_id, test, test_index) |
453 job_id += 1 | 454 job_id += 1 |
454 test_lock.release() | 455 test_lock.release() |
455 if job is None: | 456 if job is None: |
456 return | 457 return |
457 times.record_test_time(test_binary, test, run_job(job)) | 458 times.record_test_time(test_binary, test, run_job(job)) |
458 | 459 |
459 def start_daemon(func): | 460 def start_daemon(func): |
460 t = threading.Thread(target=func) | 461 t = threading.Thread(target=func) |
461 t.daemon = True | 462 t.daemon = True |
462 t.start() | 463 t.start() |
463 return t | 464 return t |
464 | 465 |
465 workers = [start_daemon(worker) for i in range(options.workers)] | 466 workers = [start_daemon(worker) for i in range(options.workers)] |
466 | 467 |
467 [t.join() for t in workers] | 468 [t.join() for t in workers] |
468 logger.end() | 469 logger.end() |
469 times.write_to_file(save_file) | 470 times.write_to_file(save_file) |
470 if options.print_test_times: | 471 if options.print_test_times: |
471 ts = sorted((times.get_test_time(test_binary, test), test_binary, test) | 472 ts = sorted((times.get_test_time(test_binary, test), test_binary, test) |
472 for (_, test_binary, test, _) in tests | 473 for (_, test_binary, test, _) in tests |
473 if times.get_test_time(test_binary, test) is not None) | 474 if times.get_test_time(test_binary, test) is not None) |
474 for (time_ms, test_binary, test) in ts: | 475 for (time_ms, test_binary, test) in ts: |
475 print "%8s %s" % ("%dms" % time_ms, test) | 476 print "%8s %s" % ("%dms" % time_ms, test) |
476 | 477 |
477 test_results.dump_to_file_and_close() | 478 test_results.dump_to_file_and_close() |
478 sys.exit(-signal.SIGINT if sigint_handler.got_sigint() else exit_code) | 479 sys.exit(-signal.SIGINT if sigint_handler.got_sigint() else exit_code) |
OLD | NEW |