forked from graalvm/mx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mx_unittest.py
executable file
·284 lines (242 loc) · 11.5 KB
/
mx_unittest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#!/usr/bin/env python2.7
#
# ----------------------------------------------------------------------------------------------------
#
# Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
# ----------------------------------------------------------------------------------------------------
#
import mx
import os
import re
import tempfile
import fnmatch
from argparse import ArgumentParser, RawDescriptionHelpFormatter
from os.path import exists, join
def _find_classes_with_annotations(p, pkgRoot, annotations, includeInnerClasses=False):
"""
Scan the sources of project 'p' for Java source files containing a line starting with 'annotation'
(ignoring preceding whitespace) and return the fully qualified class name for each Java
source file matched in a list.
"""
matches = lambda line: len([a for a in annotations if line == a or line.startswith(a + '(')]) != 0
return p.find_classes_with_matching_source_line(pkgRoot, matches, includeInnerClasses)
def _run_tests(args, harness, vmLauncher, annotations, testfile, blacklist, whitelist, regex, suite):
vmArgs, tests = mx.extract_VM_args(args)
for t in tests:
if t.startswith('-'):
mx.abort('VM option ' + t + ' must precede ' + tests[0])
candidates = {}
for p in mx.projects(opt_limit_to_suite=True):
if not p.isJavaProject():
continue
if suite and not p.suite == suite:
continue
if mx.get_jdk().javaCompliance < p.javaCompliance:
continue
for c in _find_classes_with_annotations(p, None, annotations):
candidates[c] = p
classes = []
if len(tests) == 0:
classes = candidates.keys()
jdk = mx.get_jdk()
projectsCp = mx.classpath([pcp.name for pcp in mx.projects(opt_limit_to_suite=True) if pcp.isJavaProject() and pcp.javaCompliance <= jdk.javaCompliance])
else:
projs = set()
found = False
if len(tests) == 1 and '#' in tests[0]:
words = tests[0].split('#')
if len(words) != 2:
mx.abort("Method specification is class#method: " + tests[0])
t, method = words
for c, p in candidates.iteritems():
# prefer exact matches first
if t == c:
found = True
classes.append(c)
projs.add(p.name)
if not found:
for c, p in candidates.iteritems():
if t in c:
found = True
classes.append(c)
projs.add(p.name)
if not found:
mx.log('warning: no tests matched by substring "' + t)
elif len(classes) != 1:
mx.abort('More than one test matches substring {0} {1}'.format(t, classes))
classes = [c + "#" + method for c in classes]
else:
for t in tests:
if '#' in t:
mx.abort('Method specifications can only be used in a single test: ' + t)
for c, p in candidates.iteritems():
if t in c:
found = True
classes.append(c)
projs.add(p.name)
if not found:
mx.log('warning: no tests matched by substring "' + t)
projectsCp = mx.classpath(projs)
if blacklist:
classes = [c for c in classes if not any((glob.match(c) for glob in blacklist))]
if whitelist:
classes = [c for c in classes if any((glob.match(c) for glob in whitelist))]
if regex:
classes = [c for c in classes if re.search(regex, c)]
if len(classes) != 0:
f_testfile = open(testfile, 'w')
for c in classes:
f_testfile.write(c + '\n')
f_testfile.close()
harness(projectsCp, vmLauncher, vmArgs)
_vm_launcher = None
_config_participants = []
def set_vm_launcher(name, launcher):
global _vm_launcher
assert _vm_launcher is None, 'cannot override unit test VM launcher ' + _vm_launcher[0]
_vm_launcher = (name, launcher)
def add_config_participant(p):
_config_participants.append(p)
def _unittest(args, annotations, prefixCp="", blacklist=None, whitelist=None, verbose=False, fail_fast=False, enable_timing=False, regex=None, color=False, eager_stacktrace=False, gc_after_test=False, suite=None):
testfile = os.environ.get('MX_TESTFILE', None)
if testfile is None:
(_, testfile) = tempfile.mkstemp(".testclasses", "mxtool")
os.close(_)
mainClass = 'com.oracle.mxtool.junit.MxJUnitWrapper'
if not exists(join(mx.project('com.oracle.mxtool.junit').output_dir(), mainClass.replace('.', os.sep) + '.class')):
mx.build(['--only', 'com.oracle.mxtool.junit'])
coreCp = mx.classpath(['com.oracle.mxtool.junit'])
coreArgs = []
if verbose:
coreArgs.append('-JUnitVerbose')
if fail_fast:
coreArgs.append('-JUnitFailFast')
if enable_timing:
coreArgs.append('-JUnitEnableTiming')
if color:
coreArgs.append('-JUnitColor')
if eager_stacktrace:
coreArgs.append('-JUnitEagerStackTrace')
if gc_after_test:
coreArgs.append('-JUnitGCAfterTest')
def harness(projectsCp, vmLauncher, vmArgs):
prefixArgs = ['-esa', '-ea']
if gc_after_test:
prefixArgs.append('-XX:-DisableExplicitGC')
with open(testfile) as fp:
testclasses = [l.rstrip() for l in fp.readlines()]
cp = prefixCp + coreCp + os.pathsep + projectsCp
# suppress menubar and dock when running on Mac
vmArgs = prefixArgs + ['-Djava.awt.headless=true'] + vmArgs + ['-cp', mx._separatedCygpathU2W(cp)]
# Execute Junit directly when one test is being run. This simplifies
# replaying the VM execution in a native debugger (e.g., gdb).
mainClassArgs = coreArgs + (testclasses if len(testclasses) == 1 else ['@' + mx._cygpathU2W(testfile)])
config = (vmArgs, mainClass, mainClassArgs)
for p in _config_participants:
config = p(config)
_, launcher = vmLauncher
launcher(*config)
vmLauncher = _vm_launcher
if vmLauncher is None:
vmLauncher = ('default VM launcher', lambda vmArgs, mainClass, mainClassArgs: mx.run_java(vmArgs + [mainClass] + mainClassArgs))
try:
_run_tests(args, harness, vmLauncher, annotations, testfile, blacklist, whitelist, regex, mx.suite(suite) if suite else None)
finally:
if os.environ.get('MX_TESTFILE') is None:
os.remove(testfile)
unittestHelpSuffix = """
Unittest options:
--blacklist <file> run all testcases not specified in the blacklist
--whitelist <file> run only testcases which are included
in the given whitelist
--verbose enable verbose JUnit output
--fail-fast stop after first JUnit test class that has a failure
--enable-timing enable JUnit test timing
--regex <regex> run only testcases matching a regular expression
--color enable colors output
--eager-stacktrace print stacktrace eagerly
--gc-after-test force a GC after each test
To avoid conflicts with VM options '--' can be used as delimiter.
If filters are supplied, only tests whose fully qualified name
includes a filter as a substring are run.
For example, this command line:
mx unittest -G:Dump= -G:MethodFilter=BC_aload.* -G:+PrintCFG BC_aload
will run all JUnit test classes that contain 'BC_aload' in their
fully qualified name and will pass these options to the VM:
-G:Dump= -G:MethodFilter=BC_aload.* -G:+PrintCFG
To get around command line length limitations on some OSes, the
JUnit class names to be executed are written to a file that a
custom JUnit wrapper reads and passes onto JUnit proper. The
MX_TESTFILE environment variable can be set to specify a
file which will not be deleted once the unittests are done
(unlike the temporary file otherwise used).
As with all other commands, using the global '-v' before 'unittest'
command will cause mx to show the complete command line
it uses to run the VM.
"""
def unittest(args):
"""run the JUnit tests"""
parser = ArgumentParser(prog='mx unittest',
description='run the JUnit tests',
add_help=False,
formatter_class=RawDescriptionHelpFormatter,
epilog=unittestHelpSuffix,
)
parser.add_argument('--blacklist', help='run all testcases not specified in <file>', metavar='<file>')
parser.add_argument('--whitelist', help='run testcases specified in <file> only', metavar='<file>')
parser.add_argument('--verbose', help='enable verbose JUnit output', action='store_true')
parser.add_argument('--fail-fast', help='stop after first JUnit test class that has a failure', action='store_true')
parser.add_argument('--enable-timing', help='enable JUnit test timing', action='store_true')
parser.add_argument('--regex', help='run only testcases matching a regular expression', metavar='<regex>')
parser.add_argument('--color', help='enable color output', action='store_true')
parser.add_argument('--eager-stacktrace', help='print stacktrace eagerly', action='store_true')
parser.add_argument('--gc-after-test', help='force a GC after each test', action='store_true')
parser.add_argument('--suite', help='run only the unit tests in <suite>', metavar='<suite>')
ut_args = []
delimiter = False
# check for delimiter
while len(args) > 0:
arg = args.pop(0)
if arg == '--':
delimiter = True
break
ut_args.append(arg)
if delimiter:
# all arguments before '--' must be recognized
parsed_args = parser.parse_args(ut_args)
else:
# parse all know arguments
parsed_args, args = parser.parse_known_args(ut_args)
if parsed_args.whitelist:
try:
with open(parsed_args.whitelist) as fp:
parsed_args.whitelist = [re.compile(fnmatch.translate(l.rstrip())) for l in fp.readlines() if not l.startswith('#')]
except IOError:
mx.log('warning: could not read whitelist: ' + parsed_args.whitelist)
if parsed_args.blacklist:
try:
with open(parsed_args.blacklist) as fp:
parsed_args.blacklist = [re.compile(fnmatch.translate(l.rstrip())) for l in fp.readlines() if not l.startswith('#')]
except IOError:
mx.log('warning: could not read blacklist: ' + parsed_args.blacklist)
_unittest(args, ['@Test', '@Parameters'], **parsed_args.__dict__)