import io import json import optparse import os import sys import subprocess import time import zipfile import platform # This script changes test run classpath by unpacking tests.jar -> tests-dir. The goal # is to launch tests with the same classpath as maven does. def parse_args(): parser = optparse.OptionParser() parser.disable_interspersed_args() parser.add_option('--trace-file') parser.add_option('--jar-binary') parser.add_option('--tests-jar-path') parser.add_option('--classpath-option-type', choices=('manifest', 'command_file', 'list'), default='manifest') return parser.parse_args() # temporary, for jdk8/jdk9+ compatibility def fix_cmd(cmd): if not cmd: return cmd java = cmd[0] if not java.endswith('java') and not java.endswith('java.exe'): return cmd p = subprocess.Popen([java, '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() out, err = out.strip(), err.strip() if (out or '').strip().startswith('java version "1.8') or (err or '').strip().startswith('java version "1.8'): res = [] i = 0 while i < len(cmd): for option in ('--add-exports', '--add-modules'): if cmd[i] == option: i += 1 break elif cmd[i].startswith(option + '='): break else: res.append(cmd[i]) i += 1 return res return cmd def dump_event(etype, data, filename): event = { 'timestamp': time.time(), 'value': data, 'name': etype, } with io.open(filename, 'a', encoding='utf8') as afile: afile.write(unicode(json.dumps(event) + '\n')) def dump_chunk_event(data, filename): return dump_event('chunk-event', data, filename) def extract_jars(dest, archive): os.makedirs(dest) with zipfile.ZipFile(archive) as zf: zf.extractall(dest) def make_bfg_from_cp(class_path, out): class_path = ' '.join(map(lambda path: ('file:/' + path.lstrip('/')) if os.path.isabs(path) else path, class_path)) with zipfile.ZipFile(out, 'w') as zf: lines = [] while class_path: lines.append(class_path[:60]) class_path = class_path[60:] if lines: zf.writestr('META-INF/MANIFEST.MF', 'Manifest-Version: 1.0\nClass-Path: \n ' + '\n '.join(lines) + ' \n\n') def make_command_file_from_cp(class_path, out): with open(out, 'w') as cp_file: cp_file.write(os.pathsep.join(class_path)) def main(): s = time.time() opts, args = parse_args() # unpack tests jar try: build_root = args[args.index('--build-root') + 1] dest = os.path.join(build_root, 'test-classes') except Exception: build_root = '' dest = os.path.abspath('test-classes') extract_jars(dest, opts.tests_jar_path) metrics = { 'suite_jtest_extract_jars_(seconds)': time.time() - s, } s = time.time() # fix java classpath cp_idx = args.index('-classpath') if args[cp_idx + 1].startswith('@'): real_name = args[cp_idx + 1][1:] mf = os.path.join(os.path.dirname(real_name), 'fixed.bfg.jar') with open(real_name) as origin: class_path = [os.path.join(build_root, i.strip()) for i in origin] if opts.tests_jar_path in class_path: class_path.remove(opts.tests_jar_path) if opts.classpath_option_type == 'manifest': make_bfg_from_cp(class_path, mf) mf = os.pathsep.join([dest, mf]) elif opts.classpath_option_type == 'command_file': mf = os.path.splitext(mf)[0] + '.txt' make_command_file_from_cp([dest] + class_path, mf) mf = "@" + mf elif opts.classpath_option_type == 'list': mf = os.pathsep.join([dest] + class_path) else: raise Exception("Unexpected classpath option type: " + opts.classpath_option_type) args = fix_cmd(args[: cp_idx + 1]) + [mf] + args[cp_idx + 2 :] else: args[cp_idx + 1] = args[cp_idx + 1].replace(opts.tests_jar_path, dest) args = fix_cmd(args[:cp_idx]) + args[cp_idx:] metrics['suite_jtest_fix_classpath_(seconds)'] = time.time() - s if opts.trace_file: dump_chunk_event({'metrics': metrics}, opts.trace_file) # run java cmd if platform.system() == 'Windows': sys.exit(subprocess.Popen(args).wait()) else: os.execv(args[0], args) if __name__ == '__main__': main()