1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package compiler.graalunit.common; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.util.*; 29 import java.nio.file.*; 30 import java.util.stream.Collectors; 31 32 import jdk.test.lib.process.OutputAnalyzer; 33 import jdk.test.lib.process.ProcessTools; 34 35 /* 36 * This is helper class used to run Graal unit tests. 37 * It accepts two arguments: 38 * -prefix TEST_PREFIX_TO_DEFINE_SET_OF_TESTS_TO_RUN (Ex: -prefix org.graalvm.compiler.api.test) 39 * -exclude EXCLUDED_TESTS_FILE_NAME 40 */ 41 public class GraalUnitTestLauncher { 42 43 static final String MXTOOL_JARFILE = "com.oracle.mxtool.junit.jar"; 44 static final String GRAAL_UNITTESTS_JARFILE = "jdk.vm.compiler.tests.jar"; 45 46 static final String[] GRAAL_EXTRA_JARS = {"junit-4.12.jar", "asm-5.0.4.jar", "asm-tree-5.0.4.jar", 47 "hamcrest-core-1.3.jar", "java-allocation-instrumenter.jar"}; 48 49 static final String GENERATED_TESTCLASSES_FILENAME = "list.testclasses"; 50 51 // Library dir used to find Graal specific jar files. 52 static String libsDir; 53 static { 54 libsDir = System.getProperty("graalunit.libs"); 55 if (libsDir == null || libsDir.isEmpty()) { 56 libsDir = System.getenv("TEST_IMAGE_GRAAL_DIR"); 57 } 58 59 if (libsDir == null || libsDir.isEmpty()) 60 throw new RuntimeException("ERROR: Graal library directory is not specified, use -Dgraalunit.libs or TEST_IMAGE_GRAAL_DIR environment variable."); 61 62 System.out.println("INFO: graal libs dir is '" + libsDir + "'"); 63 } 64 65 /* 66 * Generates --add-exports <module>/<package>=<target-module> flags and 67 * returns them as array list. 68 * 69 * @param moduleName 70 * Name of the module to update export data 71 * 72 * @param targetModule 73 * Name of the module to whom to export 74 */ 75 static ArrayList<String> getModuleExports(String moduleName, String targetModule) { 76 ArrayList<String> exports = new ArrayList<String>(); 77 78 Optional<Module> mod = ModuleLayer.boot().findModule(moduleName); 79 Set<String> packages; 80 if (mod.isPresent()) { 81 packages = mod.get().getPackages(); 82 83 for (String pName : packages) { 84 exports.add("--add-exports"); 85 exports.add(moduleName + "/" + pName + "=" + targetModule); 86 } 87 } 88 89 return exports; 90 } 91 92 /* 93 * Return list of tests which match specified prefix 94 * 95 * @param testPrefix 96 * String prefix to select tests 97 */ 98 static ArrayList<String> getListOfTestsByPrefix(String testPrefix, Set<String> excludeTests) throws Exception { 99 ArrayList<String> classes = new ArrayList<String>(); 100 101 final String testAnnotationName = "@Test"; 102 103 // return empty list in case no selection prefix specified 104 if (testPrefix == null || testPrefix.isEmpty()) 105 return classes; 106 107 // replace "." by "\." in test pattern 108 testPrefix = testPrefix.replaceAll("\\.", "\\\\.") + ".*"; 109 System.out.println("INFO: use following pattern to find tests: " + testPrefix); 110 111 String graalUnitTestFilePath = String.join(File.separator, libsDir, GRAAL_UNITTESTS_JARFILE); 112 String classPath = String.join(File.pathSeparator, System.getProperty("java.class.path"), 113 String.join(File.separator, libsDir, MXTOOL_JARFILE)); 114 115 ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(false, 116 "-cp", classPath, 117 "com.oracle.mxtool.junit.FindClassesByAnnotatedMethods", graalUnitTestFilePath, testAnnotationName); 118 119 System.out.println("INFO: run command " + String.join(" ", pb.command())); 120 121 OutputAnalyzer out = new OutputAnalyzer(pb.start()); 122 int exitCode = out.getExitValue(); 123 if (exitCode != 0) { 124 throw new Exception("Failed to find tests, VM crashed with exit code " + exitCode); 125 } 126 127 String outStr = out.getOutput().trim(); 128 System.out.println("INFO: command output: [" + outStr + "]"); 129 130 String[] lines = outStr.split(" "); 131 Arrays.sort(lines); 132 133 if (lines.length > 1) { // first line contains jar file name 134 for (int i = 1; i < lines.length; i++) { 135 String className = lines[i]; 136 137 if (testPrefix.equals(".*") || className.matches(testPrefix)) { 138 // add the test only in case it is not in exclude list 139 if (excludeTests!= null && excludeTests.contains(className)) { 140 System.out.println("INFO: excluded test: " + className); 141 } else { 142 classes.add(className); 143 } 144 } 145 } 146 } 147 148 return classes; 149 } 150 151 /* 152 * Return set of excluded tests 153 * 154 * @param excludeFileName 155 * Name of the file to read excluded test list 156 */ 157 static Set loadExcludeList(String excludeFileName) { 158 Set<String> excludeTests; 159 160 Path excludeFilePath = Paths.get(excludeFileName); 161 try { 162 excludeTests = Files.readAllLines(excludeFilePath).stream() 163 .filter(l -> !l.trim().isEmpty()) 164 .filter(l -> !l.trim().startsWith("#")) 165 .map(l -> l.split(" ")[0]) 166 .collect(Collectors.toSet()); 167 168 } catch (IOException ioe) { 169 throw new Error("TESTBUG: failed to read " + excludeFilePath); 170 } 171 172 return excludeTests; 173 } 174 175 static String getUsageString() { 176 return "Usage: " + GraalUnitTestLauncher.class.getName() + " " + 177 "-prefix (org.graalvm.compiler.api.test) " + 178 "-exclude <ExcludedTestsFileName>" + System.lineSeparator(); 179 } 180 181 public static void main(String... args) throws Exception { 182 183 String testPrefix = null; 184 String excludeFileName = null; 185 186 int i=0; 187 String arg, val; 188 while (i+1 < args.length) { 189 arg = args[i++]; 190 val = args[i++]; 191 192 switch (arg) { 193 case "-prefix": 194 testPrefix = val; 195 break; 196 197 case "-exclude": 198 excludeFileName = val; 199 break; 200 201 default: 202 System.out.println("WARN: illegal option " + arg); 203 break; 204 } 205 } 206 207 if (testPrefix == null) 208 throw new Error("TESTBUG: no tests to run specified." + System.lineSeparator() + getUsageString()); 209 210 211 Set<String> excludeTests = null; 212 if (excludeFileName != null) { 213 excludeTests = loadExcludeList(excludeFileName); 214 } 215 216 // Find list of tests which match provided predicate and write into GENERATED_TESTCLASSES_FILENAME file 217 ArrayList<String> tests = getListOfTestsByPrefix(testPrefix, excludeTests); 218 if (tests.size() > 0) { 219 Files.write(Paths.get(GENERATED_TESTCLASSES_FILENAME), String.join(System.lineSeparator(), tests).getBytes()); 220 } else { 221 throw new Error("TESTBUG: no tests found for prefix " + testPrefix); 222 } 223 224 ArrayList<String> javaFlags = new ArrayList<String>(); 225 226 // add modules and exports 227 javaFlags.add("--add-modules"); 228 javaFlags.add("jdk.internal.vm.compiler,jdk.internal.vm.ci"); 229 javaFlags.add("--add-exports"); 230 javaFlags.add("java.base/jdk.internal.module=ALL-UNNAMED"); 231 javaFlags.addAll(getModuleExports("jdk.internal.vm.compiler", "ALL-UNNAMED")); 232 javaFlags.addAll(getModuleExports("jdk.internal.vm.ci", "ALL-UNNAMED,jdk.internal.vm.compiler")); 233 234 235 // add VM flags 236 javaFlags.add("-XX:+UnlockExperimentalVMOptions"); 237 javaFlags.add("-XX:+EnableJVMCI"); 238 javaFlags.add("-Djava.awt.headless=true"); 239 javaFlags.add("-esa"); 240 javaFlags.add("-ea"); 241 242 243 // generate class path 244 ArrayList<String> graalJars = new ArrayList<String>(Arrays.asList(GRAAL_EXTRA_JARS)); 245 graalJars.add(MXTOOL_JARFILE); 246 graalJars.add(GRAAL_UNITTESTS_JARFILE); 247 248 String graalJarsCP = graalJars.stream() 249 .map(s -> String.join(File.separator, libsDir, s)) 250 .collect(Collectors.joining(File.pathSeparator)); 251 252 javaFlags.add("-cp"); 253 javaFlags.add(String.join(File.pathSeparator, System.getProperty("java.class.path"), graalJarsCP)); 254 255 // 256 javaFlags.add("com.oracle.mxtool.junit.MxJUnitWrapper"); 257 javaFlags.add("-JUnitVerbose"); 258 javaFlags.add("-JUnitEagerStackTrace"); 259 javaFlags.add("-JUnitEnableTiming"); 260 261 javaFlags.add("@"+GENERATED_TESTCLASSES_FILENAME); 262 263 ProcessBuilder javaPB = ProcessTools.createJavaProcessBuilder(true, 264 javaFlags.toArray(new String[javaFlags.size()])); 265 System.out.println("INFO: run command: " + String.join(" ", javaPB.command())); 266 267 OutputAnalyzer outputAnalyzer = new OutputAnalyzer(javaPB.start()); 268 System.out.println("INFO: execution result: " + outputAnalyzer.getOutput()); 269 outputAnalyzer.shouldHaveExitValue(0); 270 } 271 }