Path: blob/master/test/jdk/tools/launcher/TestHelper.java
41145 views
/*1* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.OutputStream;24import java.io.InputStream;25import java.lang.annotation.ElementType;26import java.lang.annotation.Retention;27import java.lang.annotation.RetentionPolicy;28import java.lang.annotation.Target;29import java.lang.reflect.Method;30import java.util.regex.Pattern;31import java.io.StringWriter;32import java.io.PrintWriter;33import java.util.Set;34import java.io.BufferedReader;35import java.io.File;36import java.io.FileFilter;37import java.io.FileNotFoundException;38import java.io.FileOutputStream;39import java.io.IOException;40import java.io.InputStreamReader;41import java.io.PrintStream;42import java.nio.charset.Charset;43import java.nio.file.attribute.BasicFileAttributes;44import java.nio.file.Files;45import java.nio.file.FileVisitResult;46import java.nio.file.SimpleFileVisitor;47import java.nio.file.Path;48import java.util.ArrayList;49import java.util.List;50import java.util.Locale;51import java.util.Map;52import java.util.Arrays;53import java.util.spi.ToolProvider;5455import static java.nio.file.StandardCopyOption.*;56import static java.nio.file.StandardOpenOption.*;5758/**59* This class provides some common utilities for the launcher tests.60*/61public class TestHelper {62// commonly used jtreg constants63static final File TEST_CLASSES_DIR;64static final File TEST_SOURCES_DIR;6566static final String JAVAHOME = System.getProperty("java.home");67static final String JAVA_BIN;68static final String JAVA_LIB;69static final String javaCmd;70static final String javawCmd;71static final String javacCmd;72static final String jarCmd;73static final boolean haveServerVM;74static final boolean haveClientVM;7576static final ToolProvider compiler = ToolProvider.findFirst("javac").orElse(null);7778static final boolean debug = Boolean.getBoolean("TestHelper.Debug");79static final boolean isWindows =80System.getProperty("os.name", "unknown").startsWith("Windows");81static final boolean isMacOSX =82System.getProperty("os.name", "unknown").contains("OS X");83static final boolean is64Bit =84System.getProperty("sun.arch.data.model").equals("64");85static final boolean is32Bit =86System.getProperty("sun.arch.data.model").equals("32");87static final boolean isLinux =88System.getProperty("os.name", "unknown").startsWith("Linux");89static final boolean isAIX =90System.getProperty("os.name", "unknown").startsWith("AIX");91static final String LIBJVM = isWindows92? "jvm.dll"93: "libjvm" + (isMacOSX ? ".dylib" : ".so");9495// make a note of the golden default locale96static final Locale DefaultLocale = Locale.getDefault();9798static final String JAVA_FILE_EXT = ".java";99static final String CLASS_FILE_EXT = ".class";100static final String JAR_FILE_EXT = ".jar";101static final String EXE_FILE_EXT = ".exe";102static final String MAC_DSYM_EXT = ".dsym";103static final String NIX_DBGINFO_EXT = ".debuginfo";104static final String JLDEBUG_KEY = "_JAVA_LAUNCHER_DEBUG";105static final String EXPECTED_MARKER = "TRACER_MARKER:About to EXEC";106static final String TEST_PREFIX = "###TestError###: ";107108static int testExitValue = 0;109110static {111String tmp = System.getProperty("test.classes", null);112if (tmp == null) {113throw new Error("property test.classes not defined ??");114}115TEST_CLASSES_DIR = new File(tmp).getAbsoluteFile();116117tmp = System.getProperty("test.src", null);118if (tmp == null) {119throw new Error("property test.src not defined ??");120}121TEST_SOURCES_DIR = new File(tmp).getAbsoluteFile();122123if (is64Bit && is32Bit) {124throw new RuntimeException("arch model cannot be both 32 and 64 bit");125}126if (!is64Bit && !is32Bit) {127throw new RuntimeException("arch model is not 32 or 64 bit ?");128}129130File binDir = new File(JAVAHOME, "bin");131JAVA_BIN = binDir.getAbsolutePath();132File libDir = new File(JAVAHOME, "lib");133JAVA_LIB = libDir.getAbsolutePath();134135File javaCmdFile = (isWindows)136? new File(binDir, "java.exe")137: new File(binDir, "java");138javaCmd = javaCmdFile.getAbsolutePath();139if (!javaCmdFile.canExecute()) {140throw new RuntimeException("java <" + TestHelper.javaCmd +141"> must exist and should be executable");142}143144File javacCmdFile = (isWindows)145? new File(binDir, "javac.exe")146: new File(binDir, "javac");147javacCmd = javacCmdFile.getAbsolutePath();148149File jarCmdFile = (isWindows)150? new File(binDir, "jar.exe")151: new File(binDir, "jar");152jarCmd = jarCmdFile.getAbsolutePath();153if (!jarCmdFile.canExecute()) {154throw new RuntimeException("java <" + TestHelper.jarCmd +155"> must exist and should be executable");156}157158if (isWindows) {159File javawCmdFile = new File(binDir, "javaw.exe");160javawCmd = javawCmdFile.getAbsolutePath();161if (!javawCmdFile.canExecute()) {162throw new RuntimeException("java <" + javawCmd +163"> must exist and should be executable");164}165} else {166javawCmd = null;167}168169if (!javacCmdFile.canExecute()) {170throw new RuntimeException("java <" + javacCmd +171"> must exist and should be executable");172}173174haveClientVM = haveVmVariant("client");175haveServerVM = haveVmVariant("server");176}177private static boolean haveVmVariant(String type) {178if (isWindows) {179File vmDir = new File(JAVA_BIN, type);180File jvmFile = new File(vmDir, LIBJVM);181return jvmFile.exists();182} else {183File vmDir = new File(JAVA_LIB, type);184File jvmFile = new File(vmDir, LIBJVM);185return jvmFile.exists();186}187}188void run(String[] args) throws Exception {189int passed = 0, failed = 0;190final Pattern p = (args != null && args.length > 0)191? Pattern.compile(args[0])192: null;193for (Method m : this.getClass().getDeclaredMethods()) {194boolean selected = (p == null)195? m.isAnnotationPresent(Test.class)196: p.matcher(m.getName()).matches();197if (selected) {198try {199m.invoke(this, (Object[]) null);200System.out.println(m.getName() + ": OK");201passed++;202System.out.printf("Passed: %d, Failed: %d, ExitValue: %d%n",203passed, failed, testExitValue);204} catch (Throwable ex) {205System.out.printf("Test %s failed: %s %n", m, ex);206System.out.println("----begin detailed exceptions----");207ex.printStackTrace(System.out);208for (Throwable t : ex.getSuppressed()) {209t.printStackTrace(System.out);210}211System.out.println("----end detailed exceptions----");212failed++;213}214}215}216System.out.printf("Total: Passed: %d, Failed %d%n", passed, failed);217if (failed > 0) {218throw new RuntimeException("Tests failed: " + failed);219}220if (passed == 0 && failed == 0) {221throw new AssertionError("No test(s) selected: passed = " +222passed + ", failed = " + failed + " ??????????");223}224}225226/*227* usually the jre/lib/arch-name is the same as os.arch, except for x86.228*/229static String getJreArch() {230String arch = System.getProperty("os.arch");231return arch.equals("x86") ? "i386" : arch;232}233static String getArch() {234return System.getProperty("os.arch");235}236static File getClassFile(File javaFile) {237String s = javaFile.getAbsolutePath().replace(JAVA_FILE_EXT, CLASS_FILE_EXT);238return new File(s);239}240241static File getJavaFile(File classFile) {242String s = classFile.getAbsolutePath().replace(CLASS_FILE_EXT, JAVA_FILE_EXT);243return new File(s);244}245246static String baseName(File f) {247String s = f.getName();248return s.substring(0, s.indexOf("."));249}250251/*252* A convenience method to create a jar with jar file name and defs253*/254static void createJar(File jarName, String... mainDefs)255throws FileNotFoundException{256createJar(null, jarName, new File("Foo"), mainDefs);257}258259/*260* A convenience method to create a java file, compile and jar it up, using261* the sole class file name in the jar, as the Main-Class attribute value.262*/263static void createJar(File jarName, File mainClass, String... mainDefs)264throws FileNotFoundException {265createJar(null, jarName, mainClass, mainDefs);266}267268/*269* A convenience method to compile java files.270*/271static void compile(String... compilerArgs) {272if (compiler.run(System.out, System.err, compilerArgs) != 0) {273String sarg = "";274for (String x : compilerArgs) {275sarg.concat(x + " ");276}277throw new Error("compilation failed: " + sarg);278}279}280281/*282* A generic jar file creator to create a java file, compile it283* and jar it up, a specific Main-Class entry name in the284* manifest can be specified or a null to use the sole class file name285* as the Main-Class attribute value.286*/287static void createJar(String mEntry, File jarName, File mainClass,288String... mainDefs) throws FileNotFoundException {289if (jarName.exists()) {290jarName.delete();291}292try (PrintStream ps = new PrintStream(new FileOutputStream(mainClass + ".java"))) {293ps.println("public class Foo {");294if (mainDefs != null) {295for (String x : mainDefs) {296ps.println(x);297}298}299ps.println("}");300}301302String compileArgs[] = {303mainClass + ".java"304};305if (compiler.run(System.out, System.err, compileArgs) != 0) {306throw new RuntimeException("compilation failed " + mainClass + ".java");307}308if (mEntry == null) {309mEntry = mainClass.getName();310}311String jarArgs[] = {312(debug) ? "cvfe" : "cfe",313jarName.getAbsolutePath(),314mEntry,315mainClass.getName() + ".class"316};317createJar(jarArgs);318}319320static void createJar(String... args) {321List<String> cmdList = new ArrayList<>();322cmdList.add(jarCmd);323cmdList.addAll(Arrays.asList(args));324doExec(cmdList.toArray(new String[cmdList.size()]));325}326327static void copyStream(InputStream in, OutputStream out) throws IOException {328byte[] buf = new byte[8192];329int n = in.read(buf);330while (n > 0) {331out.write(buf, 0, n);332n = in.read(buf);333}334}335336static void copyFile(File src, File dst) throws IOException {337Path parent = dst.toPath().getParent();338if (parent != null) {339Files.createDirectories(parent);340}341Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING);342}343344/**345* Attempt to create a file at the given location. If an IOException346* occurs then back off for a moment and try again. When a number of347* attempts fail, give up and throw an exception.348*/349void createAFile(File aFile, List<String> lines) throws IOException {350createAFile(aFile, lines, true);351}352353void createAFile(File aFile, List<String> lines, boolean endWithNewline) throws IOException {354IOException cause = null;355for (int attempts = 0; attempts < 10; attempts++) {356try {357if (endWithNewline) {358Files.write(aFile.getAbsoluteFile().toPath(),359lines, Charset.defaultCharset(),360CREATE, TRUNCATE_EXISTING, WRITE);361} else {362Files.write(aFile.getAbsoluteFile().toPath(),363String.join(System.lineSeparator(), lines).getBytes(Charset.defaultCharset()),364CREATE, TRUNCATE_EXISTING, WRITE);365}366if (cause != null) {367/*368* report attempts and errors that were encountered369* for diagnostic purposes370*/371System.err.println("Created batch file " +372aFile + " in " + (attempts + 1) +373" attempts");374System.err.println("Errors encountered: " + cause);375cause.printStackTrace();376}377return;378} catch (IOException ioe) {379if (cause != null) {380// chain the exceptions so they all get reported for diagnostics381cause.addSuppressed(ioe);382} else {383cause = ioe;384}385}386387try {388Thread.sleep(500);389} catch (InterruptedException ie) {390if (cause != null) {391// cause should alway be non-null here392ie.addSuppressed(cause);393}394throw new RuntimeException("Interrupted while creating batch file", ie);395}396}397throw new RuntimeException("Unable to create batch file", cause);398}399400static void createFile(File outFile, List<String> content) throws IOException {401Files.write(outFile.getAbsoluteFile().toPath(), content,402Charset.defaultCharset(), CREATE_NEW);403}404405static void recursiveDelete(File target) throws IOException {406if (!target.exists()) {407return;408}409Files.walkFileTree(target.toPath(), new SimpleFileVisitor<Path>() {410@Override411public FileVisitResult postVisitDirectory(Path dir, IOException exc) {412try {413Files.deleteIfExists(dir);414} catch (IOException ex) {415System.out.println("Error: could not delete: " + dir.toString());416System.out.println(ex.getMessage());417return FileVisitResult.TERMINATE;418}419return FileVisitResult.CONTINUE;420}421@Override422public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {423try {424Files.deleteIfExists(file);425} catch (IOException ex) {426System.out.println("Error: could not delete: " + file.toString());427System.out.println(ex.getMessage());428return FileVisitResult.TERMINATE;429}430return FileVisitResult.CONTINUE;431}432});433}434435static TestResult doExec(String...cmds) {436return doExec(null, null, cmds);437}438439static TestResult doExec(Map<String, String> envToSet, String...cmds) {440return doExec(envToSet, null, cmds);441}442/*443* A method which executes a java cmd and returns the results in a container444*/445static TestResult doExec(Map<String, String> envToSet,446Set<String> envToRemove, String...cmds) {447String cmdStr = "";448for (String x : cmds) {449cmdStr = cmdStr.concat(x + " ");450}451ProcessBuilder pb = new ProcessBuilder(cmds);452Map<String, String> env = pb.environment();453if (envToRemove != null) {454for (String key : envToRemove) {455env.remove(key);456}457}458if (envToSet != null) {459env.putAll(envToSet);460}461BufferedReader rdr = null;462try {463List<String> outputList = new ArrayList<>();464pb.redirectErrorStream(true);465Process p = pb.start();466rdr = new BufferedReader(new InputStreamReader(p.getInputStream()));467String in = rdr.readLine();468while (in != null) {469outputList.add(in);470in = rdr.readLine();471}472p.waitFor();473p.destroy();474475return new TestHelper.TestResult(cmdStr, p.exitValue(), outputList,476env, new Throwable("current stack of the test"));477} catch (Exception ex) {478ex.printStackTrace();479throw new RuntimeException(ex.getMessage());480}481}482483static FileFilter createFilter(final String extension) {484return new FileFilter() {485@Override486public boolean accept(File pathname) {487String name = pathname.getName();488if (name.endsWith(extension)) {489return true;490}491return false;492}493};494}495496static boolean isEnglishLocale() {497return Locale.getDefault().getLanguage().equals("en");498}499500/**501* Helper method to initialize a simple module that just prints the passed in arguments502*/503static void createEchoArgumentsModule(File modulesDir) throws IOException {504if (modulesDir.exists()) {505recursiveDelete(modulesDir);506}507508modulesDir.mkdirs();509510File srcDir = new File(modulesDir, "src");511File modsDir = new File(modulesDir, "mods");512File testDir = new File(srcDir, "test");513File launcherTestDir = new File(testDir, "launcher");514srcDir.mkdir();515modsDir.mkdir();516testDir.mkdir();517launcherTestDir.mkdir();518519String[] moduleInfoCode = { "module test {}" };520createFile(new File(testDir, "module-info.java"), Arrays.asList(moduleInfoCode));521522String[] moduleCode = {523"package launcher;",524"import java.util.Arrays;",525"public class Main {",526"public static void main(String[] args) {",527"System.out.println(Arrays.toString(args));",528"}",529"}"530};531createFile(new File(launcherTestDir, "Main.java"), Arrays.asList(moduleCode));532}533534static class ToolFilter implements FileFilter {535final List<String> exclude = new ArrayList<>();536protected ToolFilter(String... exclude) {537for (String x : exclude) {538String str = x + ((isWindows) ? EXE_FILE_EXT : "");539this.exclude.add(str.toLowerCase());540}541}542543@Override544public boolean accept(File pathname) {545if (!pathname.isFile() || !pathname.canExecute()) {546return false;547}548String name = pathname.getName().toLowerCase();549if (isWindows) {550if (!name.endsWith(EXE_FILE_EXT)) {551return false;552}553} else if (isMacOSX) {554if (name.endsWith(MAC_DSYM_EXT)) {555return false;556}557} else {558if (name.endsWith(NIX_DBGINFO_EXT)) {559return false;560}561}562for (String x : exclude) {563if (name.endsWith(x)) {564return false;565}566}567return true;568}569}570571/*572* A class to encapsulate the test results and stuff, with some ease573* of use methods to check the test results.574*/575static class TestResult {576PrintWriter status;577StringWriter sw;578int exitValue;579List<String> testOutput;580Map<String, String> env;581Throwable t;582boolean testStatus;583584public TestResult(String str, int rv, List<String> oList,585Map<String, String> env, Throwable t) {586sw = new StringWriter();587status = new PrintWriter(sw);588status.println("Executed command: " + str + "\n");589exitValue = rv;590testOutput = oList;591this.env = env;592this.t = t;593testStatus = true;594}595596void appendError(String x) {597testStatus = false;598testExitValue++;599status.println(TEST_PREFIX + x);600}601602void indentStatus(String x) {603status.println(" " + x);604}605606void checkNegative() {607if (exitValue == 0) {608appendError("test must not return 0 exit value");609}610}611612void checkPositive() {613if (exitValue != 0) {614appendError("test did not return 0 exit value");615}616}617618boolean isOK() {619return exitValue == 0;620}621622boolean isZeroOutput() {623if (!testOutput.isEmpty()) {624appendError("No message from cmd please");625return false;626}627return true;628}629630boolean isNotZeroOutput() {631if (testOutput.isEmpty()) {632appendError("Missing message");633return false;634}635return true;636}637638@Override639public String toString() {640status.println("++++Begin Test Info++++");641status.println("Test Status: " + (testStatus ? "PASS" : "FAIL"));642status.println("++++Test Environment++++");643for (String x : env.keySet()) {644indentStatus(x + "=" + env.get(x));645}646status.println("++++Test Output++++");647for (String x : testOutput) {648indentStatus(x);649}650status.println("++++Test Stack Trace++++");651status.println(t.toString());652for (StackTraceElement e : t.getStackTrace()) {653indentStatus(e.toString());654}655status.println("++++End of Test Info++++");656status.flush();657String out = sw.toString();658status.close();659return out;660}661662boolean contains(String str) {663for (String x : testOutput) {664if (x.contains(str)) {665return true;666}667}668appendError("string <" + str + "> not found");669return false;670}671672boolean notContains(String str) {673for (String x : testOutput) {674if (x.contains(str)) {675appendError("string <" + str + "> found");676return false;677}678}679return true;680}681682boolean matches(String regexToMatch) {683for (String x : testOutput) {684if (x.matches(regexToMatch)) {685return true;686}687}688appendError("regex <" + regexToMatch + "> not matched");689return false;690}691692boolean notMatches(String regexToMatch) {693for (String x : testOutput) {694if (!x.matches(regexToMatch)) {695return true;696}697}698appendError("regex <" + regexToMatch + "> matched");699return false;700}701}702/**703* Indicates that the annotated method is a test method.704*/705@Retention(RetentionPolicy.RUNTIME)706@Target(ElementType.METHOD)707public @interface Test {}708}709710711