2 # Copyright 2012 The Swarming Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 that
4 # can be found in the LICENSE file.
6 """Runs the whole set of swarming client unit tests on swarming itself.
8 This is done in a few steps:
9 - Archive the whole directory as a single .isolated file.
10 - Create one test-specific .isolated for each test to run. The file is created
11 directly and archived manually with isolateserver.py.
12 - Trigger each of these test-specific .isolated file per OS.
13 - Get all results out of order.
27 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
29 # Must be first import.
30 import parallel_execution
32 from third_party import colorama
33 from third_party.depot_tools import fix_encoding
34 from utils import tools
37 def check_output(cmd):
38 return subprocess.check_output([sys.executable] + cmd, cwd=ROOT_DIR)
41 def archive_tree(isolate_server):
42 """Archives a whole tree and return the sha1 of the .isolated file.
44 Manually creates a temporary isolated file and archives it.
47 'isolateserver.py', 'archive', '--isolate-server', isolate_server,
48 ROOT_DIR, '--blacklist="\\.git"',
50 if logging.getLogger().isEnabledFor(logging.INFO):
51 cmd.append('--verbose')
52 out = check_output(cmd)
56 def archive_isolated_triggers(isolate_server, tree_isolated, tests):
57 """Creates and archives all the .isolated files for the tests at once.
59 Archiving them in one batch is faster than archiving each file individually.
60 Also the .isolated files can be reused across OSes, reducing the amount of
64 list of (test, sha1) tuples.
66 logging.info('archive_isolated_triggers(%s, %s)', tree_isolated, tests)
67 tempdir = tempfile.mkdtemp(prefix='run_swarming_tests_on_swarming_')
71 test_name = os.path.basename(test)
72 # Creates a manual .isolated file. See
73 # https://code.google.com/p/swarming/wiki/IsolatedDesign for more details.
76 'command': ['python', test],
77 'includes': [tree_isolated],
80 v = os.path.join(tempdir, test_name + '.isolated')
81 tools.write_json(v, isolated, True)
84 'isolateserver.py', 'archive', '--isolate-server', isolate_server,
86 if logging.getLogger().isEnabledFor(logging.INFO):
87 cmd.append('--verbose')
88 items = [i.split() for i in check_output(cmd).splitlines()]
89 assert len(items) == len(tests)
91 items[i][1].endswith(os.path.basename(tests[i]) + '.isolated')
92 for i in xrange(len(tests)))
93 return zip(tests, [i[0] for i in items])
95 shutil.rmtree(tempdir)
99 def run_swarming_tests_on_swarming(
100 swarming_server, isolate_server, priority, oses, tests, logs):
101 """Archives, triggers swarming jobs and gets results."""
102 print('Archiving the whole tree.')
104 tree_isolated = archive_tree(isolate_server)
106 # Create and archive all the .isolated files.
107 isolateds = archive_isolated_triggers(isolate_server, tree_isolated, tests)
108 logging.debug('%s', isolateds)
109 print('Archival took %3.2fs' % (time.time() - start))
112 for test_path, isolated_hash in isolateds:
113 test_name = os.path.basename(test_path).split('.')[0]
114 for platform in oses:
115 exploded.append((test_name, platform, isolated_hash))
119 parallel_execution.task_to_name(name, {'os': platform}, isolated_hash),
122 ) for name, platform, isolated_hash in exploded
127 extra_args.extend(['--priority', str(priority)])
128 print('Using priority %s' % priority)
131 for failed_task in parallel_execution.run_swarming_tasks_parallel(
132 swarming_server, isolate_server, extra_args, tasks):
133 test_name, dimensions, stdout = failed_task
135 # Write the logs are they are retrieved.
136 if not os.path.isdir(logs):
138 name = '%s_%s.log' % (dimensions['os'], test_name.split('/', 1)[0])
139 with open(os.path.join(logs, name), 'wb') as f:
146 parser = parallel_execution.OptionParser(
147 usage='%prog [options]', version=__version__)
150 help='Destination where to store the failure logs (recommended!)')
151 parser.add_option('-o', '--os', help='Run tests only on this OS')
153 '-t', '--test', action='append',
154 help='Run only these test, can be specified multiple times')
155 options, args = parser.parse_args()
157 parser.error('Unsupported argument %s' % args)
159 oses = ['Linux', 'Mac', 'Windows']
161 os.path.relpath(i, ROOT_DIR)
163 glob.glob(os.path.join(ROOT_DIR, 'tests', '*_test.py')) +
164 glob.glob(os.path.join(ROOT_DIR, 'googletest', 'tests', '*_test.py')))
166 valid_tests = sorted(map(os.path.basename, tests))
167 assert len(valid_tests) == len(set(valid_tests)), (
168 'Can\'t have 2 tests with the same base name')
171 for t in options.test:
172 if not t in valid_tests:
174 '--test %s is unknown. Valid values are:\n%s' % (
175 t, '\n'.join(' ' + i for i in valid_tests)))
176 filters = tuple(os.path.sep + t for t in options.test)
177 tests = [t for t in tests if t.endswith(filters)]
180 if options.os not in oses:
182 '--os %s is unknown. Valid values are %s' % (
183 options.os, ', '.join(sorted(oses))))
186 if sys.platform in ('win32', 'cygwin'):
187 # If we are on Windows, don't generate the tests for Linux and Mac since
188 # they use symlinks and we can't create symlinks on windows.
190 if options.os != 'win32':
191 print('Linux and Mac tests skipped since running on Windows.')
193 return run_swarming_tests_on_swarming(
195 options.isolate_server,
202 if __name__ == '__main__':
203 fix_encoding.fix_encoding()
204 tools.disable_buffering()