| OLD | NEW |
| 1 # Copyright 2017 The LUCI Authors. All rights reserved. | 1 # Copyright 2017 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 from __future__ import print_function | 5 from __future__ import print_function |
| 6 | 6 |
| 7 import argparse | 7 import argparse |
| 8 import bdb | 8 import bdb |
| 9 import cStringIO | 9 import cStringIO |
| 10 import contextlib | 10 import contextlib |
| (...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 536 | 536 |
| 537 | 537 |
| 538 def worker(f): | 538 def worker(f): |
| 539 """Wrapper for a multiprocessing worker function. | 539 """Wrapper for a multiprocessing worker function. |
| 540 | 540 |
| 541 This addresses known issues with multiprocessing workers: | 541 This addresses known issues with multiprocessing workers: |
| 542 | 542 |
| 543 - they can hang on uncaught exceptions | 543 - they can hang on uncaught exceptions |
| 544 - we need explicit kill switch to clearly terminate parent""" | 544 - we need explicit kill switch to clearly terminate parent""" |
| 545 @functools.wraps(f) | 545 @functools.wraps(f) |
| 546 def wrapper(*args, **kwargs): | 546 def wrapper(test, *args, **kwargs): |
| 547 try: | 547 try: |
| 548 if _KILL_SWITCH.is_set(): | 548 if _KILL_SWITCH.is_set(): |
| 549 return (False, 'kill switch') | 549 return (False, test, 'kill switch') |
| 550 return (True, f(*args, **kwargs)) | 550 return (True, test, f(test, *args, **kwargs)) |
| 551 except Exception: | 551 except Exception: |
| 552 return (False, traceback.format_exc()) | 552 return (False, test, traceback.format_exc()) |
| 553 return wrapper | 553 return wrapper |
| 554 | 554 |
| 555 | 555 |
| 556 @worker | 556 @worker |
| 557 def run_worker(test, debug=False, train=False): | 557 def run_worker(test, debug=False, train=False): |
| 558 """Worker for 'run' command (note decorator above).""" | 558 """Worker for 'run' command (note decorator above).""" |
| 559 return run_test(test, debug=debug, train=train) | 559 return run_test(test, debug=debug, train=train) |
| 560 | 560 |
| 561 | 561 |
| 562 def scan_for_expectations(root, inside_expectations=False): | 562 def scan_for_expectations(root, inside_expectations=False): |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 610 results.append(run_worker(t, debug=debug, train=train)) | 610 results.append(run_worker(t, debug=debug, train=train)) |
| 611 else: | 611 else: |
| 612 with kill_switch(): | 612 with kill_switch(): |
| 613 pool = multiprocessing.Pool(jobs) | 613 pool = multiprocessing.Pool(jobs) |
| 614 results = pool.map(functools.partial(run_worker, train=train), tests) | 614 results = pool.map(functools.partial(run_worker, train=train), tests) |
| 615 | 615 |
| 616 print() | 616 print() |
| 617 | 617 |
| 618 used_expectations = set() | 618 used_expectations = set() |
| 619 | 619 |
| 620 for success, details in results: | 620 for success, test_description, details in results: |
| 621 if success: | 621 if success: |
| 622 assert isinstance(details, TestResult) | 622 assert isinstance(details, TestResult) |
| 623 if details.failures: | 623 if details.failures: |
| 624 rc = 1 | 624 rc = 1 |
| 625 key = details.test_description.full_name | 625 key = details.test_description.full_name |
| 626 print('%s failed:' % key) | 626 print('%s failed:' % key) |
| 627 for failure in details.failures: | 627 for failure in details.failures: |
| 628 results_proto.test_failures[key].failures.extend([failure.as_proto()]) | 628 results_proto.test_failures[key].failures.extend([failure.as_proto()]) |
| 629 print(failure.format()) | 629 print(failure.format()) |
| 630 coverage_data.update(details.coverage_data) | 630 coverage_data.update(details.coverage_data) |
| 631 if details.generates_expectation: | 631 if details.generates_expectation: |
| 632 used_expectations.add(details.test_description.expectation_path) | 632 used_expectations.add(details.test_description.expectation_path) |
| 633 used_expectations.add( | 633 used_expectations.add( |
| 634 os.path.dirname(details.test_description.expectation_path)) | 634 os.path.dirname(details.test_description.expectation_path)) |
| 635 else: | 635 else: |
| 636 rc = 1 | 636 rc = 1 |
| 637 results_proto.valid = False | 637 results_proto.valid = False |
| 638 print('Internal failure:') | 638 failure_proto = test_result_pb2.TestResult.TestFailure() |
| 639 failure_proto.internal_failure.MergeFrom( |
| 640 test_result_pb2.TestResult.InternalFailure()) |
| 641 results_proto.test_failures[test_description.full_name].failures.extend([ |
| 642 failure_proto]) |
| 643 print('%s failed:' % test_description.full_name) |
| 639 print(details) | 644 print(details) |
| 640 | 645 |
| 641 if test_filter: | 646 if test_filter: |
| 642 print('NOTE: not checking coverage, because a filter is enabled') | 647 print('NOTE: not checking coverage, because a filter is enabled') |
| 643 else: | 648 else: |
| 644 try: | 649 try: |
| 645 # TODO(phajdan.jr): Add API to coverage to load data from memory. | 650 # TODO(phajdan.jr): Add API to coverage to load data from memory. |
| 646 with tempfile.NamedTemporaryFile(delete=False) as coverage_file: | 651 with tempfile.NamedTemporaryFile(delete=False) as coverage_file: |
| 647 coverage_data.write_file(coverage_file.name) | 652 coverage_data.write_file(coverage_file.name) |
| 648 | 653 |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 878 Returns: | 883 Returns: |
| 879 Exit code | 884 Exit code |
| 880 """ | 885 """ |
| 881 global _UNIVERSE_VIEW | 886 global _UNIVERSE_VIEW |
| 882 _UNIVERSE_VIEW = universe_view | 887 _UNIVERSE_VIEW = universe_view |
| 883 global _ENGINE_FLAGS | 888 global _ENGINE_FLAGS |
| 884 _ENGINE_FLAGS = engine_flags | 889 _ENGINE_FLAGS = engine_flags |
| 885 | 890 |
| 886 args = parse_args(raw_args) | 891 args = parse_args(raw_args) |
| 887 return args.func(args) | 892 return args.func(args) |
| OLD | NEW |