blob: 2d45d1c69f1086a25fc14b3a6c9e297f3f931bfd [file] [log] [blame]
Mads Ager418d1ca2017-05-22 09:35:49 +02001#!/usr/bin/env python
2# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
3# for details. All rights reserved. Use of this source code is governed by a
4# BSD-style license that can be found in the LICENSE file.
5
6# Convenience script for running tests. If no argument is given run all tests,
7# if an argument is given, run only tests with that pattern. This script will
8# force the tests to run, even if no input changed.
9
Mads Ager418d1ca2017-05-22 09:35:49 +020010import optparse
Ian Zerny5fffb0a2019-02-11 13:54:22 +010011import os
Ian Zerny24398bc2019-02-22 11:59:18 +010012import shutil
Rico Windf65a1d62017-06-30 09:41:56 +020013import subprocess
Mads Ager418d1ca2017-05-22 09:35:49 +020014import sys
Rico Windda6836e2018-12-07 12:32:03 +010015import thread
16import time
Rico Wind06487ac2018-12-10 09:09:19 +010017import uuid
Ian Zerny5fffb0a2019-02-11 13:54:22 +010018
19import gradle
Stephan Herhutd24b1b72017-08-24 15:09:36 +020020import notify
Ian Zerny5fffb0a2019-02-11 13:54:22 +010021import utils
Jean-Marie Henaffce162f32017-10-04 10:39:27 +020022
Søren Gjessefe7c0112018-12-03 12:33:12 +010023ALL_ART_VMS = [
24 "default",
25 "9.0.0",
26 "8.1.0",
27 "7.0.0",
28 "6.0.1",
29 "5.1.1",
30 "4.4.4",
31 "4.0.4"]
Mads Ager418d1ca2017-05-22 09:35:49 +020032
Rico Windda6836e2018-12-07 12:32:03 +010033# How often do we check for progress on the bots:
34# Should be long enough that a normal run would always have med progress
35# Should be short enough that we ensure that two calls are close enough
36# to happen before bot times out.
37# A false positiv, i.e., printing the stacks of non hanging processes
38# is not a problem, no harm done except some logging in the stdout.
39TIMEOUT_HANDLER_PERIOD = 60 * 18
40
Rico Wind06487ac2018-12-10 09:09:19 +010041BUCKET = 'r8-test-results'
42
Ian Zerny24398bc2019-02-22 11:59:18 +010043NUMBER_OF_TEST_REPORTS = 5
44REPORTS_PATH = os.path.join(utils.BUILD, 'reports')
45REPORT_INDEX = ['tests', 'test', 'index.html']
46
Mads Ager418d1ca2017-05-22 09:35:49 +020047def ParseOptions():
48 result = optparse.OptionParser()
Søren Gjesse77527982018-10-05 12:58:49 +020049 result.add_option('--no-internal', '--no_internal',
Mads Ager418d1ca2017-05-22 09:35:49 +020050 help='Do not run Google internal tests.',
51 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020052 result.add_option('--archive-failures', '--archive_failures',
Rico Winda94f01c2017-06-27 10:32:34 +020053 help='Upload test results to cloud storage on failure.',
54 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020055 result.add_option('--only-internal', '--only_internal',
Mads Ager418d1ca2017-05-22 09:35:49 +020056 help='Only run Google internal tests.',
57 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020058 result.add_option('--all-tests', '--all_tests',
Mads Ager418d1ca2017-05-22 09:35:49 +020059 help='Run tests in all configurations.',
60 default=False, action='store_true')
Christoffer Quist Adamsen748e4662019-08-23 14:53:49 +020061 result.add_option('--slow-tests', '--slow_tests',
62 help='Also run slow tests.',
63 default=False, action='store_true')
Mads Ager418d1ca2017-05-22 09:35:49 +020064 result.add_option('-v', '--verbose',
65 help='Print test stdout to, well, stdout.',
66 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020067 result.add_option('--dex-vm', '--dex_vm',
Mads Ager418d1ca2017-05-22 09:35:49 +020068 help='The android version of the vm to use. "all" will run the tests on '
69 'all art vm versions (stopping after first failed execution)',
70 default="default",
71 choices=ALL_ART_VMS + ["all"])
Søren Gjesse77527982018-10-05 12:58:49 +020072 result.add_option('--dex-vm-kind', '--dex_vm_kind',
Jean-Marie Henaffce162f32017-10-04 10:39:27 +020073 help='Whether to use host or target version of runtime',
74 default="host",
75 nargs=1,
76 choices=["host", "target"])
Søren Gjesse77527982018-10-05 12:58:49 +020077 result.add_option('--one-line-per-test', '--one_line_per_test',
Mads Ager418d1ca2017-05-22 09:35:49 +020078 help='Print a line before a tests starts and after it ends to stdout.',
79 default=False, action='store_true')
80 result.add_option('--tool',
Tamas Kenezcfb2c052018-10-12 11:03:57 +020081 help='Tool to run ART tests with: "r8" (default) or "d8" or "r8cf"'
82 ' (r8 w/CF-backend). Ignored if "--all_tests" enabled.',
83 default=None, choices=["r8", "d8", "r8cf"])
Mads Ager418d1ca2017-05-22 09:35:49 +020084 result.add_option('--jctf',
Tamas Kenezcfb2c052018-10-12 11:03:57 +020085 help='Run JCTF tests with: "r8" (default) or "d8" or "r8cf".',
Mads Ager418d1ca2017-05-22 09:35:49 +020086 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020087 result.add_option('--only-jctf', '--only_jctf',
Tamas Kenezcfb2c052018-10-12 11:03:57 +020088 help='Run only JCTF tests with: "r8" (default) or "d8" or "r8cf".',
Mads Ager418d1ca2017-05-22 09:35:49 +020089 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020090 result.add_option('--jctf-compile-only', '--jctf_compile_only',
Mads Ager418d1ca2017-05-22 09:35:49 +020091 help="Don't run, only compile JCTF tests.",
92 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020093 result.add_option('--disable-assertions', '--disable_assertions',
Tamas Kenezb77b7d82017-08-17 14:05:16 +020094 help='Disable assertions when running tests.',
Søren Gjesseaf1c5e22017-06-15 12:24:03 +020095 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020096 result.add_option('--with-code-coverage', '--with_code_coverage',
Tamas Kenezb77b7d82017-08-17 14:05:16 +020097 help='Enable code coverage with Jacoco.',
Sebastien Hertze2687b62017-07-25 11:16:04 +020098 default=False, action='store_true')
Søren Gjesse77527982018-10-05 12:58:49 +020099 result.add_option('--test-dir', '--test_dir',
Tamas Kenezb77b7d82017-08-17 14:05:16 +0200100 help='Use a custom directory for the test artifacts instead of a'
101 ' temporary (which is automatically removed after the test).'
102 ' Note that the directory will not be cleared before the test.')
Søren Gjesse77527982018-10-05 12:58:49 +0200103 result.add_option('--java-home', '--java_home',
Mikaël Peltier5c0a3232017-10-18 09:14:40 +0200104 help='Use a custom java version to run tests.')
Ian Zerny5fffb0a2019-02-11 13:54:22 +0100105 result.add_option('--java-max-memory-size', '--java_max_memory_size',
Rico Wind97b0a992019-08-30 11:09:15 +0200106 help='Set memory for running tests, default 4G',
107 default='4G')
Rico Wind8e2f7e42019-02-21 10:13:21 +0100108 result.add_option('--shard-count', '--shard_count',
109 help='We are running this many shards.')
110 result.add_option('--shard-number', '--shard_number',
111 help='We are running this shard.')
Søren Gjesse77527982018-10-05 12:58:49 +0200112 result.add_option('--generate-golden-files-to', '--generate_golden_files_to',
Jean-Marie Henaff7a64eec2018-05-31 15:30:35 +0200113 help='Store dex files produced by tests in the specified directory.'
114 ' It is aimed to be read on platforms with no host runtime available'
115 ' for comparison.')
Søren Gjesse77527982018-10-05 12:58:49 +0200116 result.add_option('--use-golden-files-in', '--use_golden_files_in',
Jean-Marie Henaff7a64eec2018-05-31 15:30:35 +0200117 help='Download golden files hierarchy for this commit in the specified'
118 ' location and use them instead of executing on host runtime.')
Søren Gjesse1956bdb2019-02-19 08:37:52 +0100119 result.add_option('--no-r8lib', '--no_r8lib',
120 default=False, action='store_true',
Morten Krogh-Jespersen2243b162019-01-14 08:40:53 +0100121 help='Run the tests on R8 full with relocated dependencies.')
Søren Gjesse1956bdb2019-02-19 08:37:52 +0100122 result.add_option('--r8lib-no-deps', '--r8lib_no_deps',
123 default=False, action='store_true',
Morten Krogh-Jespersen807b15f2018-12-17 14:24:22 +0100124 help='Run the tests on r8lib without relocated dependencies.')
Ian Zerny24398bc2019-02-22 11:59:18 +0100125 result.add_option('--failed',
126 default=False, action='store_true',
127 help='Run the tests that failed last execution.')
128 result.add_option('--fail-fast', '--fail_fast',
129 default=False, action='store_true',
130 help='Stop on first failure. Passes --fail-fast to gradle test runner.')
Mads Ager418d1ca2017-05-22 09:35:49 +0200131 return result.parse_args()
132
Rico Wind06487ac2018-12-10 09:09:19 +0100133def archive_failures():
134 upload_dir = os.path.join(utils.REPO_ROOT, 'build', 'reports', 'tests')
135 u_dir = uuid.uuid4()
136 destination = 'gs://%s/%s' % (BUCKET, u_dir)
137 utils.upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
138 url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, u_dir)
139 print 'Test results available at: %s' % url
140 print '@@@STEP_LINK@Test failures@%s@@@' % url
141
Mads Ager418d1ca2017-05-22 09:35:49 +0200142def Main():
143 (options, args) = ParseOptions()
Rico Windda6836e2018-12-07 12:32:03 +0100144 if utils.is_bot():
Rico Wind22707fc2019-03-15 13:19:57 +0100145 gradle.RunGradle(['--no-daemon', 'clean'])
Sebastien Hertze2687b62017-07-25 11:16:04 +0200146
Jean-Marie Henaff7a64eec2018-05-31 15:30:35 +0200147 gradle_args = ['--stacktrace']
Rico Wind22707fc2019-03-15 13:19:57 +0100148 if utils.is_bot():
149 # Bots don't like dangling processes
150 gradle_args.append('--no-daemon')
151
Sebastien Hertze2687b62017-07-25 11:16:04 +0200152 # Set all necessary Gradle properties and options first.
Rico Wind8e2f7e42019-02-21 10:13:21 +0100153 if options.shard_count:
154 assert options.shard_number
155 gradle_args.append('-Pshard_count=%s' % options.shard_count)
156 gradle_args.append('-Pshard_number=%s' % options.shard_number)
Mads Ager418d1ca2017-05-22 09:35:49 +0200157 if options.verbose:
158 gradle_args.append('-Pprint_test_stdout')
159 if options.no_internal:
160 gradle_args.append('-Pno_internal')
161 if options.only_internal:
162 gradle_args.append('-Ponly_internal')
163 if options.all_tests:
164 gradle_args.append('-Pall_tests')
Christoffer Quist Adamsen748e4662019-08-23 14:53:49 +0200165 if options.slow_tests:
166 gradle_args.append('-Pslow_tests=1')
Mads Ager418d1ca2017-05-22 09:35:49 +0200167 if options.tool:
168 gradle_args.append('-Ptool=%s' % options.tool)
169 if options.one_line_per_test:
170 gradle_args.append('-Pone_line_per_test')
171 if options.jctf:
172 gradle_args.append('-Pjctf')
173 if options.only_jctf:
174 gradle_args.append('-Ponly_jctf')
175 if options.jctf_compile_only:
176 gradle_args.append('-Pjctf_compile_only')
Søren Gjesseaf1c5e22017-06-15 12:24:03 +0200177 if options.disable_assertions:
178 gradle_args.append('-Pdisable_assertions')
Sebastien Hertze2687b62017-07-25 11:16:04 +0200179 if options.with_code_coverage:
180 gradle_args.append('-Pwith_code_coverage')
Jean-Marie Henaff7b424e92017-06-15 11:02:56 +0200181 if os.name == 'nt':
182 # temporary hack
183 gradle_args.append('-Pno_internal')
184 gradle_args.append('-x')
185 gradle_args.append('createJctfTests')
186 gradle_args.append('-x')
187 gradle_args.append('jctfCommonJar')
188 gradle_args.append('-x')
189 gradle_args.append('jctfTestsClasses')
Tamas Kenezb77b7d82017-08-17 14:05:16 +0200190 if options.test_dir:
191 gradle_args.append('-Ptest_dir=' + options.test_dir)
192 if not os.path.exists(options.test_dir):
193 os.makedirs(options.test_dir)
Mikaël Peltier5c0a3232017-10-18 09:14:40 +0200194 if options.java_home:
195 gradle_args.append('-Dorg.gradle.java.home=' + options.java_home)
Ian Zerny5fffb0a2019-02-11 13:54:22 +0100196 if options.java_max_memory_size:
Rico Wind97b0a992019-08-30 11:09:15 +0200197 gradle_args.append('-Ptest_xmx=' + options.java_max_memory_size)
Jean-Marie Henaff7a64eec2018-05-31 15:30:35 +0200198 if options.generate_golden_files_to:
199 gradle_args.append('-Pgenerate_golden_files_to=' + options.generate_golden_files_to)
200 if not os.path.exists(options.generate_golden_files_to):
201 os.makedirs(options.generate_golden_files_to)
202 gradle_args.append('-PHEAD_sha1=' + utils.get_HEAD_sha1())
203 if options.use_golden_files_in:
204 gradle_args.append('-Puse_golden_files_in=' + options.use_golden_files_in)
205 if not os.path.exists(options.use_golden_files_in):
206 os.makedirs(options.use_golden_files_in)
207 gradle_args.append('-PHEAD_sha1=' + utils.get_HEAD_sha1())
Morten Krogh-Jespersen432dd912019-01-14 13:26:35 +0100208 if (not options.no_r8lib) and options.r8lib_no_deps:
Morten Krogh-Jespersen2243b162019-01-14 08:40:53 +0100209 print('Cannot run tests on r8lib with and without deps. R8lib is now default target.')
Morten Krogh-Jespersen807b15f2018-12-17 14:24:22 +0100210 exit(1)
Morten Krogh-Jespersen2243b162019-01-14 08:40:53 +0100211 if not options.no_r8lib:
Morten Krogh-Jespersen807b15f2018-12-17 14:24:22 +0100212 gradle_args.append('-Pr8lib')
Morten Krogh-Jespersen54f196e2019-01-14 16:10:08 +0100213 # Force gradle to build a version of r8lib without dependencies for
214 # BootstrapCurrentEqualityTest.
215 gradle_args.append('R8LibNoDeps')
Morten Krogh-Jespersen807b15f2018-12-17 14:24:22 +0100216 if options.r8lib_no_deps:
217 gradle_args.append('-Pr8lib_no_deps')
218
Morten Krogh-Jespersen54f196e2019-01-14 16:10:08 +0100219 # Build an R8 with dependencies for bootstrapping tests before adding test sources.
220 gradle_args.append('r8WithRelocatedDeps')
clementbera55e84822019-06-06 16:08:11 +0200221 gradle_args.append('r8WithRelocatedDeps11')
Morten Krogh-Jespersen54f196e2019-01-14 16:10:08 +0100222
Sebastien Hertze2687b62017-07-25 11:16:04 +0200223 # Add Gradle tasks
224 gradle_args.append('cleanTest')
225 gradle_args.append('test')
Ian Zerny24398bc2019-02-22 11:59:18 +0100226 if options.fail_fast:
227 gradle_args.append('--fail-fast')
228 if options.failed:
229 args = compute_failed_tests(args)
230 if args is None:
231 return 1
232 if len(args) == 0:
233 print "No failing tests"
234 return 0
Sebastien Hertz0f4e7fb2017-10-02 11:33:45 +0200235 # Test filtering. Must always follow the 'test' task.
236 for testFilter in args:
Sebastien Hertze2687b62017-07-25 11:16:04 +0200237 gradle_args.append('--tests')
Sebastien Hertz0f4e7fb2017-10-02 11:33:45 +0200238 gradle_args.append(testFilter)
Sebastien Hertze2687b62017-07-25 11:16:04 +0200239 if options.with_code_coverage:
240 # Create Jacoco report after tests.
241 gradle_args.append('jacocoTestReport')
242
Jean-Marie Henaff7a64eec2018-05-31 15:30:35 +0200243 if options.use_golden_files_in:
244 sha1 = '%s' % utils.get_HEAD_sha1()
245 with utils.ChangedWorkingDirectory(options.use_golden_files_in):
246 utils.download_file_from_cloud_storage(
247 'gs://r8-test-results/golden-files/%s.tar.gz' % sha1,
248 '%s.tar.gz' % sha1)
249 utils.unpack_archive('%s.tar.gz' % sha1)
250
Rico Windda6836e2018-12-07 12:32:03 +0100251 if utils.is_bot() and not utils.IsWindows():
252 timestamp_file = os.path.join(utils.BUILD, 'last_test_time')
253 if os.path.exists(timestamp_file):
254 os.remove(timestamp_file)
255 gradle_args.append('-Pupdate_test_timestamp=' + timestamp_file)
256 thread.start_new_thread(timeout_handler, (timestamp_file,))
Jean-Marie Henaff7a64eec2018-05-31 15:30:35 +0200257
Ian Zerny24398bc2019-02-22 11:59:18 +0100258 rotate_test_reports()
259
Ian Zerny324d7612019-03-20 10:52:28 +0100260 if options.only_jctf:
261 # Note: not setting -Pruntimes will run with all available runtimes.
262 return_code = gradle.RunGradle(gradle_args, throw_on_failure=False)
Ian Zerny17561362019-05-27 15:16:26 +0200263 return archive_and_return(return_code, options)
Ian Zerny324d7612019-03-20 10:52:28 +0100264
Sebastien Hertze2687b62017-07-25 11:16:04 +0200265 # Now run tests on selected runtime(s).
Mads Ager418d1ca2017-05-22 09:35:49 +0200266 vms_to_test = [options.dex_vm] if options.dex_vm != "all" else ALL_ART_VMS
Rico Wind124b2712019-02-28 13:49:25 +0100267
Mads Ager418d1ca2017-05-22 09:35:49 +0200268 for art_vm in vms_to_test:
Ian Zerny4dfd5a52019-03-12 07:56:11 +0100269 vm_suffix = "_" + options.dex_vm_kind if art_vm != "default" else ""
Ian Zerny324d7612019-03-20 10:52:28 +0100270 runtimes = ['dex-' + art_vm]
271 # Only append the "none" runtime and JVMs if running on the "default" DEX VM.
272 if art_vm == "default":
clementbera1757ec02019-04-27 12:48:37 +0200273 runtimes.extend(['jdk8', 'jdk9', 'jdk11', 'none'])
Ian Zerny4dfd5a52019-03-12 07:56:11 +0100274 return_code = gradle.RunGradle(
275 gradle_args + [
276 '-Pdex_vm=%s' % art_vm + vm_suffix,
Ian Zerny324d7612019-03-20 10:52:28 +0100277 '-Pruntimes=%s' % ':'.join(runtimes),
278 ],
Ian Zerny4dfd5a52019-03-12 07:56:11 +0100279 throw_on_failure=False)
Jean-Marie Henaff7a64eec2018-05-31 15:30:35 +0200280 if options.generate_golden_files_to:
281 sha1 = '%s' % utils.get_HEAD_sha1()
282 with utils.ChangedWorkingDirectory(options.generate_golden_files_to):
283 archive = utils.create_archive(sha1)
284 utils.upload_file_to_cloud_storage(archive,
285 'gs://r8-test-results/golden-files/' + archive)
286
Rico Winda94f01c2017-06-27 10:32:34 +0200287 if return_code != 0:
Ian Zerny17561362019-05-27 15:16:26 +0200288 return archive_and_return(return_code, options)
Mads Ager418d1ca2017-05-22 09:35:49 +0200289
Jinseong Jeon9749d172017-09-19 00:25:01 -0700290 return 0
291
Rico Windda6836e2018-12-07 12:32:03 +0100292
Ian Zerny17561362019-05-27 15:16:26 +0200293def archive_and_return(return_code, options):
Rico Windc58a20e2019-05-23 09:43:19 +0200294 if return_code != 0:
295 if options.archive_failures and os.name != 'nt':
296 archive_failures()
297 return return_code
298
Rico Windda6836e2018-12-07 12:32:03 +0100299def print_jstacks():
300 processes = subprocess.check_output(['ps', 'aux'])
301 for l in processes.splitlines():
302 if 'java' in l and 'openjdk' in l:
303 # Example line:
304 # ricow 184313 2.6 0.0 36839068 31808 ? Sl 09:53 0:00 /us..
305 columns = l.split()
306 pid = columns[1]
307 return_value = subprocess.call(['jstack', pid])
308 if return_value:
309 print('Could not jstack %s' % l)
Rico Windda6836e2018-12-07 12:32:03 +0100310
311def get_time_from_file(timestamp_file):
312 if os.path.exists(timestamp_file):
313 timestamp = os.stat(timestamp_file).st_mtime
314 print('TIMEOUT HANDLER timestamp: %s' % (timestamp))
Rico Windda6836e2018-12-07 12:32:03 +0100315 sys.stdout.flush()
316 return timestamp
317 else:
318 print('TIMEOUT HANDLER no timestamp file yet')
Rico Windda6836e2018-12-07 12:32:03 +0100319 sys.stdout.flush()
320 return None
321
322def timeout_handler(timestamp_file):
323 last_timestamp = None
324 while True:
325 time.sleep(TIMEOUT_HANDLER_PERIOD)
326 new_timestamp = get_time_from_file(timestamp_file)
327 if last_timestamp and new_timestamp == last_timestamp:
328 print_jstacks()
329 last_timestamp = new_timestamp
330
Ian Zerny24398bc2019-02-22 11:59:18 +0100331def report_dir_path(index):
332 if index is 0:
333 return REPORTS_PATH
334 return '%s%d' % (REPORTS_PATH, index)
335
336def report_index_path(index):
337 return os.path.join(report_dir_path(index), *REPORT_INDEX)
338
339# Rotate test results so previous results are still accessible.
340def rotate_test_reports():
341 if not os.path.exists(report_dir_path(0)):
342 return
343 i = 1
344 while i < NUMBER_OF_TEST_REPORTS and os.path.exists(report_dir_path(i)):
345 i += 1
346 if i == NUMBER_OF_TEST_REPORTS and os.path.exists(report_dir_path(i)):
347 shutil.rmtree(report_dir_path(i))
348 while i > 0:
349 shutil.move(report_dir_path(i - 1), report_dir_path(i))
350 i -= 1
351
352def compute_failed_tests(args):
353 if len(args) > 1:
354 print "Running with --failed can take an optional path to a report index (or report number)."
355 return None
356 report = report_index_path(0)
357 # If the default report does not exist, fall back to the previous report as it may be a failed
358 # gradle run which has already moved the report to report1, but did not produce a new report.
359 if not os.path.exists(report):
360 report1 = report_index_path(1)
361 if os.path.exists(report1):
362 report = report1
363 if len(args) == 1:
364 try:
365 # try to parse the arg as a report index.
366 index = int(args[0])
367 report = report_index_path(index)
368 except ValueError:
369 # if integer parsing failed assume it is a report file path.
370 report = args[0]
371 if not os.path.exists(report):
372 print "Can't re-run failing, no report at:", report
373 return None
374 print "Reading failed tests in", report
375 failing = set()
376 inFailedSection = False
377 for line in file(report):
378 l = line.strip()
379 if l == "<h2>Failed tests</h2>":
380 inFailedSection = True
381 elif l.startswith("<h2>"):
382 inFailedSection = False
383 prefix = '<a href="classes/'
384 if inFailedSection and l.startswith(prefix):
385 href = l[len(prefix):l.index('">')]
386 # Ignore enties ending with .html which are test classes, not test methods.
387 if not href.endswith('.html'):
388 # Remove the .html and anchor separateor, also, a classMethod test is the static
389 # setup failing so rerun the full class of tests.
390 test = href.replace('.html','').replace('#', '.').replace('.classMethod', '')
391 failing.add(test)
392 return list(failing)
393
Mads Ager418d1ca2017-05-22 09:35:49 +0200394if __name__ == '__main__':
Stephan Herhutd24b1b72017-08-24 15:09:36 +0200395 return_code = Main()
396 if return_code != 0:
397 notify.notify("Tests failed.")
398 else:
399 notify.notify("Tests passed.")
400 sys.exit(return_code)