OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2013 The Chromium Authors. All rights reserved. | 3 # Copyright 2013 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """Runs all types of tests from one unified interface.""" | 7 """Runs all types of tests from one unified interface.""" |
8 | 8 |
9 import argparse | 9 import argparse |
10 import collections | 10 import collections |
11 import contextlib | 11 import contextlib |
12 import itertools | 12 import itertools |
13 import logging | 13 import logging |
14 import os | 14 import os |
15 import shutil | 15 import shutil |
16 import signal | 16 import signal |
17 import sys | 17 import sys |
| 18 import tempfile |
18 import threading | 19 import threading |
19 import traceback | 20 import traceback |
20 import unittest | 21 import unittest |
21 | 22 |
22 # Import _strptime before threaded code. datetime.datetime.strptime is | 23 # Import _strptime before threaded code. datetime.datetime.strptime is |
23 # threadsafe except for the initial import of the _strptime module. | 24 # threadsafe except for the initial import of the _strptime module. |
24 # See http://crbug.com/724524 and https://bugs.python.org/issue7980. | 25 # See http://crbug.com/724524 and https://bugs.python.org/issue7980. |
25 import _strptime # pylint: disable=unused-import | 26 import _strptime # pylint: disable=unused-import |
26 | 27 |
27 from pylib.constants import host_paths | 28 from pylib.constants import host_paths |
28 | 29 |
29 if host_paths.DEVIL_PATH not in sys.path: | 30 if host_paths.DEVIL_PATH not in sys.path: |
30 sys.path.append(host_paths.DEVIL_PATH) | 31 sys.path.append(host_paths.DEVIL_PATH) |
31 | 32 |
32 from devil import base_error | 33 from devil import base_error |
33 from devil.utils import reraiser_thread | 34 from devil.utils import reraiser_thread |
34 from devil.utils import run_tests_helper | 35 from devil.utils import run_tests_helper |
35 | 36 |
36 from pylib import constants | 37 from pylib import constants |
37 from pylib.base import base_test_result | 38 from pylib.base import base_test_result |
38 from pylib.base import environment_factory | 39 from pylib.base import environment_factory |
| 40 from pylib.base import output_manager |
| 41 from pylib.base import output_manager_factory |
39 from pylib.base import test_instance_factory | 42 from pylib.base import test_instance_factory |
40 from pylib.base import test_run_factory | 43 from pylib.base import test_run_factory |
41 from pylib.results import json_results | 44 from pylib.results import json_results |
42 from pylib.results import report_results | 45 from pylib.results import report_results |
| 46 from pylib.results.presentation import test_results_presentation |
43 from pylib.utils import logdog_helper | 47 from pylib.utils import logdog_helper |
44 from pylib.utils import logging_utils | 48 from pylib.utils import logging_utils |
45 | 49 |
46 from py_utils import contextlib_ext | 50 from py_utils import contextlib_ext |
47 | 51 |
48 | 52 |
49 _DEVIL_STATIC_CONFIG_FILE = os.path.abspath(os.path.join( | 53 _DEVIL_STATIC_CONFIG_FILE = os.path.abspath(os.path.join( |
50 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'devil_config.json')) | 54 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'devil_config.json')) |
51 | 55 |
52 | 56 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 help='Run the test scripts in platform mode, which ' | 138 help='Run the test scripts in platform mode, which ' |
135 'conceptually separates the test runner from the ' | 139 'conceptually separates the test runner from the ' |
136 '"device" (local or remote, real or emulated) on ' | 140 '"device" (local or remote, real or emulated) on ' |
137 'which the tests are running. [experimental]') | 141 'which the tests are running. [experimental]') |
138 | 142 |
139 parser.add_argument( | 143 parser.add_argument( |
140 '-e', '--environment', | 144 '-e', '--environment', |
141 default='local', choices=constants.VALID_ENVIRONMENTS, | 145 default='local', choices=constants.VALID_ENVIRONMENTS, |
142 help='Test environment to run in (default: %(default)s).') | 146 help='Test environment to run in (default: %(default)s).') |
143 | 147 |
| 148 parser.add_argument( |
| 149 '--local-output', action='store_true', |
| 150 help='Whether to archive test output locally and generate ' |
| 151 'a local results detail page.') |
| 152 |
144 class FastLocalDevAction(argparse.Action): | 153 class FastLocalDevAction(argparse.Action): |
145 def __call__(self, parser, namespace, values, option_string=None): | 154 def __call__(self, parser, namespace, values, option_string=None): |
146 namespace.verbose_count = max(namespace.verbose_count, 1) | 155 namespace.verbose_count = max(namespace.verbose_count, 1) |
147 namespace.num_retries = 0 | 156 namespace.num_retries = 0 |
148 namespace.enable_device_cache = True | 157 namespace.enable_device_cache = True |
149 namespace.enable_concurrent_adb = True | 158 namespace.enable_concurrent_adb = True |
150 namespace.skip_clear_data = True | 159 namespace.skip_clear_data = True |
151 namespace.extract_test_list_from_filter = True | 160 namespace.extract_test_list_from_filter = True |
152 | 161 |
153 parser.add_argument( | 162 parser.add_argument( |
(...skipping 645 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
799 logging.critical( | 808 logging.critical( |
800 'Logcat: %s', logdog_helper.get_viewer_url('unified_logcats')) | 809 'Logcat: %s', logdog_helper.get_viewer_url('unified_logcats')) |
801 | 810 |
802 | 811 |
803 logcats_uploader = contextlib_ext.Optional( | 812 logcats_uploader = contextlib_ext.Optional( |
804 upload_logcats_file(), | 813 upload_logcats_file(), |
805 'upload_logcats_file' in args and args.upload_logcats_file) | 814 'upload_logcats_file' in args and args.upload_logcats_file) |
806 | 815 |
807 ### Set up test objects. | 816 ### Set up test objects. |
808 | 817 |
809 env = environment_factory.CreateEnvironment(args, infra_error) | 818 out_manager = output_manager_factory.CreateOutputManager(args) |
| 819 env = environment_factory.CreateEnvironment( |
| 820 args, out_manager, infra_error) |
810 test_instance = test_instance_factory.CreateTestInstance(args, infra_error) | 821 test_instance = test_instance_factory.CreateTestInstance(args, infra_error) |
811 test_run = test_run_factory.CreateTestRun( | 822 test_run = test_run_factory.CreateTestRun( |
812 args, env, test_instance, infra_error) | 823 args, env, test_instance, infra_error) |
813 | 824 |
814 ### Run. | 825 ### Run. |
815 | 826 |
816 with json_writer, logcats_uploader, env, test_instance, test_run: | 827 with json_writer, logcats_uploader, out_manager, env, test_instance, test_run: |
817 | 828 |
818 repetitions = (xrange(args.repeat + 1) if args.repeat >= 0 | 829 repetitions = (xrange(args.repeat + 1) if args.repeat >= 0 |
819 else itertools.count()) | 830 else itertools.count()) |
820 result_counts = collections.defaultdict( | 831 result_counts = collections.defaultdict( |
821 lambda: collections.defaultdict(int)) | 832 lambda: collections.defaultdict(int)) |
822 iteration_count = 0 | 833 iteration_count = 0 |
823 for _ in repetitions: | 834 for _ in repetitions: |
824 raw_results = test_run.RunTests() | 835 raw_results = test_run.RunTests() |
825 if not raw_results: | 836 if not raw_results: |
826 continue | 837 continue |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
863 ', '.join('%s %s' % (str(result_counts[test_name][i]), i) | 874 ', '.join('%s %s' % (str(result_counts[test_name][i]), i) |
864 for i in base_test_result.ResultType.GetTypes())) | 875 for i in base_test_result.ResultType.GetTypes())) |
865 else: | 876 else: |
866 all_pass += 1 | 877 all_pass += 1 |
867 | 878 |
868 logging.critical('%s of %s tests passed in all %s runs', | 879 logging.critical('%s of %s tests passed in all %s runs', |
869 str(all_pass), | 880 str(all_pass), |
870 str(tot_tests), | 881 str(tot_tests), |
871 str(iteration_count)) | 882 str(iteration_count)) |
872 | 883 |
| 884 if args.local_output: |
| 885 try: |
| 886 results_detail_file = tempfile.NamedTemporaryFile(delete=False) |
| 887 result_html_string = test_results_presentation.result_details( |
| 888 json_path=args.json_results_file, |
| 889 test_name=args.command, |
| 890 cs_base_url='http://cs.chromium.org', |
| 891 local_output=True) |
| 892 results_detail_file.write(result_html_string) |
| 893 results_detail_file.flush() |
| 894 finally: |
| 895 results_detail_link = out_manager.ArchiveAndDeleteFile( |
| 896 results_detail_file.name, |
| 897 'test_results_presentation.html', |
| 898 'test_results_presentation', |
| 899 output_manager.Datatype.HTML) |
| 900 logging.critical('TEST RESULTS: %s', results_detail_link) |
| 901 |
873 if args.command == 'perf' and (args.steps or args.single_step): | 902 if args.command == 'perf' and (args.steps or args.single_step): |
874 return 0 | 903 return 0 |
875 | 904 |
876 return (0 if all(r.DidRunPass() for r in all_iteration_results) | 905 return (0 if all(r.DidRunPass() for r in all_iteration_results) |
877 else constants.ERROR_EXIT_CODE) | 906 else constants.ERROR_EXIT_CODE) |
878 | 907 |
879 | 908 |
880 def DumpThreadStacks(_signal, _frame): | 909 def DumpThreadStacks(_signal, _frame): |
881 for thread in threading.enumerate(): | 910 for thread in threading.enumerate(): |
882 reraiser_thread.LogThreadStack(thread) | 911 reraiser_thread.LogThreadStack(thread) |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
963 if e.is_infra_error: | 992 if e.is_infra_error: |
964 return constants.INFRA_EXIT_CODE | 993 return constants.INFRA_EXIT_CODE |
965 return constants.ERROR_EXIT_CODE | 994 return constants.ERROR_EXIT_CODE |
966 except: # pylint: disable=W0702 | 995 except: # pylint: disable=W0702 |
967 logging.exception('Unrecognized error occurred.') | 996 logging.exception('Unrecognized error occurred.') |
968 return constants.ERROR_EXIT_CODE | 997 return constants.ERROR_EXIT_CODE |
969 | 998 |
970 | 999 |
971 if __name__ == '__main__': | 1000 if __name__ == '__main__': |
972 sys.exit(main()) | 1001 sys.exit(main()) |
OLD | NEW |