1 /*
2 * Copyright (c) 2008, 2014, 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 import java.io.OutputStream;
25 import java.io.InputStream;
26 import java.lang.annotation.ElementType;
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.lang.annotation.Target;
30 import java.lang.reflect.Method;
31 import java.util.regex.Pattern;
32 import java.io.StringWriter;
33 import java.io.PrintWriter;
34 import java.util.Set;
35 import java.io.BufferedReader;
36 import java.io.File;
37 import java.io.FileFilter;
38 import java.io.FileNotFoundException;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.io.InputStreamReader;
42 import java.io.PrintStream;
43 import java.nio.charset.Charset;
44 import java.nio.file.attribute.BasicFileAttributes;
45 import java.nio.file.Files;
46 import java.nio.file.FileVisitResult;
47 import java.nio.file.SimpleFileVisitor;
48 import java.nio.file.Path;
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.Locale;
52 import java.util.Map;
53 import java.util.Arrays;
54 import java.util.spi.ToolProvider;
55
56 import static java.nio.file.StandardCopyOption.*;
57 import static java.nio.file.StandardOpenOption.*;
58
59 /**
60 * This class provides some common utilities for the launcher tests.
61 */
62 public class TestHelper {
63 // commonly used jtreg constants
64 static final File TEST_CLASSES_DIR;
65 static final File TEST_SOURCES_DIR;
66
67 static final String JAVAHOME = System.getProperty("java.home");
68 static final String JAVA_BIN;
69 static final String JAVA_LIB;
70 static final String javaCmd;
71 static final String javawCmd;
72 static final String javacCmd;
73 static final String jarCmd;
74 static final boolean haveServerVM;
75 static final boolean haveClientVM;
76
77 static final ToolProvider compiler = ToolProvider.findFirst("javac").orElse(null);
78
79 static final boolean debug = Boolean.getBoolean("TestHelper.Debug");
80 static final boolean isWindows =
81 System.getProperty("os.name", "unknown").startsWith("Windows");
82 static final boolean isMacOSX =
83 System.getProperty("os.name", "unknown").contains("OS X");
84 static final boolean is64Bit =
85 System.getProperty("sun.arch.data.model").equals("64");
86 static final boolean is32Bit =
87 System.getProperty("sun.arch.data.model").equals("32");
88 static final boolean isSolaris =
89 System.getProperty("os.name", "unknown").startsWith("SunOS");
90 static final boolean isLinux =
91 System.getProperty("os.name", "unknown").startsWith("Linux");
92 static final boolean isAIX =
93 System.getProperty("os.name", "unknown").startsWith("AIX");
94 static final String LIBJVM = isWindows
95 ? "jvm.dll"
96 : "libjvm" + (isMacOSX ? ".dylib" : ".so");
97
98 static final boolean isSparc = System.getProperty("os.arch").startsWith("sparc");
99
100 // make a note of the golden default locale
101 static final Locale DefaultLocale = Locale.getDefault();
102
103 static final String JAVA_FILE_EXT = ".java";
104 static final String CLASS_FILE_EXT = ".class";
105 static final String JAR_FILE_EXT = ".jar";
106 static final String EXE_FILE_EXT = ".exe";
107 static final String JLDEBUG_KEY = "_JAVA_LAUNCHER_DEBUG";
108 static final String EXPECTED_MARKER = "TRACER_MARKER:About to EXEC";
109 static final String TEST_PREFIX = "###TestError###: ";
110
111 static int testExitValue = 0;
112
113 static {
114 String tmp = System.getProperty("test.classes", null);
115 if (tmp == null) {
116 throw new Error("property test.classes not defined ??");
117 }
118 TEST_CLASSES_DIR = new File(tmp).getAbsoluteFile();
119
120 tmp = System.getProperty("test.src", null);
121 if (tmp == null) {
122 throw new Error("property test.src not defined ??");
123 }
124 TEST_SOURCES_DIR = new File(tmp).getAbsoluteFile();
125
126 if (is64Bit && is32Bit) {
127 throw new RuntimeException("arch model cannot be both 32 and 64 bit");
128 }
129 if (!is64Bit && !is32Bit) {
130 throw new RuntimeException("arch model is not 32 or 64 bit ?");
131 }
132
133 File binDir = new File(JAVAHOME, "bin");
134 JAVA_BIN = binDir.getAbsolutePath();
135 File libDir = new File(JAVAHOME, "lib");
136 JAVA_LIB = libDir.getAbsolutePath();
137
138 File javaCmdFile = (isWindows)
139 ? new File(binDir, "java.exe")
140 : new File(binDir, "java");
141 javaCmd = javaCmdFile.getAbsolutePath();
142 if (!javaCmdFile.canExecute()) {
143 throw new RuntimeException("java <" + TestHelper.javaCmd +
144 "> must exist and should be executable");
145 }
146
147 File javacCmdFile = (isWindows)
148 ? new File(binDir, "javac.exe")
149 : new File(binDir, "javac");
150 javacCmd = javacCmdFile.getAbsolutePath();
151
152 File jarCmdFile = (isWindows)
153 ? new File(binDir, "jar.exe")
154 : new File(binDir, "jar");
155 jarCmd = jarCmdFile.getAbsolutePath();
156 if (!jarCmdFile.canExecute()) {
157 throw new RuntimeException("java <" + TestHelper.jarCmd +
158 "> must exist and should be executable");
159 }
160
161 if (isWindows) {
162 File javawCmdFile = new File(binDir, "javaw.exe");
163 javawCmd = javawCmdFile.getAbsolutePath();
164 if (!javawCmdFile.canExecute()) {
165 throw new RuntimeException("java <" + javawCmd +
166 "> must exist and should be executable");
167 }
168 } else {
169 javawCmd = null;
170 }
171
172 if (!javacCmdFile.canExecute()) {
173 throw new RuntimeException("java <" + javacCmd +
174 "> must exist and should be executable");
175 }
176
177 haveClientVM = haveVmVariant("client");
178 haveServerVM = haveVmVariant("server");
179 }
180 private static boolean haveVmVariant(String type) {
181 if (isWindows) {
182 File vmDir = new File(JAVA_BIN, type);
183 File jvmFile = new File(vmDir, LIBJVM);
184 return jvmFile.exists();
185 } else {
186 File vmDir = new File(JAVA_LIB, type);
187 File jvmFile = new File(vmDir, LIBJVM);
188 return jvmFile.exists();
189 }
190 }
191 void run(String[] args) throws Exception {
192 int passed = 0, failed = 0;
193 final Pattern p = (args != null && args.length > 0)
194 ? Pattern.compile(args[0])
195 : null;
196 for (Method m : this.getClass().getDeclaredMethods()) {
197 boolean selected = (p == null)
198 ? m.isAnnotationPresent(Test.class)
199 : p.matcher(m.getName()).matches();
200 if (selected) {
201 try {
202 m.invoke(this, (Object[]) null);
203 System.out.println(m.getName() + ": OK");
204 passed++;
205 System.out.printf("Passed: %d, Failed: %d, ExitValue: %d%n",
206 passed, failed, testExitValue);
207 } catch (Throwable ex) {
208 System.out.printf("Test %s failed: %s %n", m, ex);
209 System.out.println("----begin detailed exceptions----");
210 ex.printStackTrace(System.out);
211 for (Throwable t : ex.getSuppressed()) {
212 t.printStackTrace(System.out);
213 }
214 System.out.println("----end detailed exceptions----");
215 failed++;
216 }
217 }
218 }
219 System.out.printf("Total: Passed: %d, Failed %d%n", passed, failed);
220 if (failed > 0) {
221 throw new RuntimeException("Tests failed: " + failed);
222 }
223 if (passed == 0 && failed == 0) {
224 throw new AssertionError("No test(s) selected: passed = " +
225 passed + ", failed = " + failed + " ??????????");
226 }
227 }
228
229 /*
230 * usually the jre/lib/arch-name is the same as os.arch, except for x86.
231 */
232 static String getJreArch() {
233 String arch = System.getProperty("os.arch");
234 return arch.equals("x86") ? "i386" : arch;
235 }
236 static String getArch() {
237 return System.getProperty("os.arch");
238 }
239 static File getClassFile(File javaFile) {
240 String s = javaFile.getAbsolutePath().replace(JAVA_FILE_EXT, CLASS_FILE_EXT);
241 return new File(s);
242 }
243
244 static File getJavaFile(File classFile) {
245 String s = classFile.getAbsolutePath().replace(CLASS_FILE_EXT, JAVA_FILE_EXT);
246 return new File(s);
247 }
248
249 static String baseName(File f) {
250 String s = f.getName();
251 return s.substring(0, s.indexOf("."));
252 }
253
254 /*
255 * A convenience method to create a jar with jar file name and defs
256 */
257 static void createJar(File jarName, String... mainDefs)
258 throws FileNotFoundException{
259 createJar(null, jarName, new File("Foo"), mainDefs);
260 }
261
262 /*
263 * A convenience method to create a java file, compile and jar it up, using
264 * the sole class file name in the jar, as the Main-Class attribute value.
265 */
266 static void createJar(File jarName, File mainClass, String... mainDefs)
267 throws FileNotFoundException {
268 createJar(null, jarName, mainClass, mainDefs);
269 }
270
271 /*
272 * A convenience method to compile java files.
273 */
274 static void compile(String... compilerArgs) {
275 if (compiler.run(System.out, System.err, compilerArgs) != 0) {
276 String sarg = "";
277 for (String x : compilerArgs) {
278 sarg.concat(x + " ");
279 }
280 throw new Error("compilation failed: " + sarg);
281 }
282 }
283
284 /*
285 * A generic jar file creator to create a java file, compile it
286 * and jar it up, a specific Main-Class entry name in the
287 * manifest can be specified or a null to use the sole class file name
288 * as the Main-Class attribute value.
289 */
290 static void createJar(String mEntry, File jarName, File mainClass,
291 String... mainDefs) throws FileNotFoundException {
292 if (jarName.exists()) {
293 jarName.delete();
294 }
295 try (PrintStream ps = new PrintStream(new FileOutputStream(mainClass + ".java"))) {
296 ps.println("public class Foo {");
297 if (mainDefs != null) {
298 for (String x : mainDefs) {
299 ps.println(x);
300 }
301 }
302 ps.println("}");
303 }
304
305 String compileArgs[] = {
306 mainClass + ".java"
307 };
308 if (compiler.run(System.out, System.err, compileArgs) != 0) {
309 throw new RuntimeException("compilation failed " + mainClass + ".java");
310 }
311 if (mEntry == null) {
312 mEntry = mainClass.getName();
313 }
314 String jarArgs[] = {
315 (debug) ? "cvfe" : "cfe",
316 jarName.getAbsolutePath(),
317 mEntry,
318 mainClass.getName() + ".class"
319 };
320 createJar(jarArgs);
321 }
322
323 static void createJar(String... args) {
324 List<String> cmdList = new ArrayList<>();
325 cmdList.add(jarCmd);
326 cmdList.addAll(Arrays.asList(args));
327 doExec(cmdList.toArray(new String[cmdList.size()]));
328 }
329
330 static void copyStream(InputStream in, OutputStream out) throws IOException {
331 byte[] buf = new byte[8192];
332 int n = in.read(buf);
333 while (n > 0) {
334 out.write(buf, 0, n);
335 n = in.read(buf);
336 }
337 }
338
339 static void copyFile(File src, File dst) throws IOException {
340 Path parent = dst.toPath().getParent();
341 if (parent != null) {
342 Files.createDirectories(parent);
343 }
344 Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING);
345 }
346
347 /**
348 * Attempt to create a file at the given location. If an IOException
349 * occurs then back off for a moment and try again. When a number of
350 * attempts fail, give up and throw an exception.
351 */
352 void createAFile(File aFile, List<String> lines) throws IOException {
353 createAFile(aFile, lines, true);
354 }
355
356 void createAFile(File aFile, List<String> lines, boolean endWithNewline) throws IOException {
357 IOException cause = null;
358 for (int attempts = 0; attempts < 10; attempts++) {
359 try {
360 if (endWithNewline) {
361 Files.write(aFile.getAbsoluteFile().toPath(),
362 lines, Charset.defaultCharset(),
363 CREATE, TRUNCATE_EXISTING, WRITE);
364 } else {
365 Files.write(aFile.getAbsoluteFile().toPath(),
366 String.join(System.lineSeparator(), lines).getBytes(Charset.defaultCharset()),
367 CREATE, TRUNCATE_EXISTING, WRITE);
368 }
369 if (cause != null) {
370 /*
371 * report attempts and errors that were encountered
372 * for diagnostic purposes
373 */
374 System.err.println("Created batch file " +
375 aFile + " in " + (attempts + 1) +
376 " attempts");
377 System.err.println("Errors encountered: " + cause);
378 cause.printStackTrace();
379 }
380 return;
381 } catch (IOException ioe) {
382 if (cause != null) {
383 // chain the exceptions so they all get reported for diagnostics
384 cause.addSuppressed(ioe);
385 } else {
386 cause = ioe;
387 }
388 }
389
390 try {
391 Thread.sleep(500);
392 } catch (InterruptedException ie) {
393 if (cause != null) {
394 // cause should alway be non-null here
395 ie.addSuppressed(cause);
396 }
397 throw new RuntimeException("Interrupted while creating batch file", ie);
398 }
399 }
400 throw new RuntimeException("Unable to create batch file", cause);
401 }
402
403 static void createFile(File outFile, List<String> content) throws IOException {
404 Files.write(outFile.getAbsoluteFile().toPath(), content,
405 Charset.defaultCharset(), CREATE_NEW);
406 }
407
408 static void recursiveDelete(File target) throws IOException {
409 if (!target.exists()) {
410 return;
411 }
412 Files.walkFileTree(target.toPath(), new SimpleFileVisitor<Path>() {
413 @Override
414 public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
415 try {
416 Files.deleteIfExists(dir);
417 } catch (IOException ex) {
418 System.out.println("Error: could not delete: " + dir.toString());
419 System.out.println(ex.getMessage());
420 return FileVisitResult.TERMINATE;
421 }
422 return FileVisitResult.CONTINUE;
423 }
424 @Override
425 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
426 try {
427 Files.deleteIfExists(file);
428 } catch (IOException ex) {
429 System.out.println("Error: could not delete: " + file.toString());
430 System.out.println(ex.getMessage());
431 return FileVisitResult.TERMINATE;
432 }
433 return FileVisitResult.CONTINUE;
434 }
435 });
436 }
437
438 static TestResult doExec(String...cmds) {
439 return doExec(null, null, cmds);
440 }
441
442 static TestResult doExec(Map<String, String> envToSet, String...cmds) {
443 return doExec(envToSet, null, cmds);
444 }
445 /*
446 * A method which executes a java cmd and returns the results in a container
447 */
448 static TestResult doExec(Map<String, String> envToSet,
449 Set<String> envToRemove, String...cmds) {
450 String cmdStr = "";
451 for (String x : cmds) {
452 cmdStr = cmdStr.concat(x + " ");
453 }
454 ProcessBuilder pb = new ProcessBuilder(cmds);
455 Map<String, String> env = pb.environment();
456 if (envToRemove != null) {
457 for (String key : envToRemove) {
458 env.remove(key);
459 }
460 }
461 if (envToSet != null) {
462 env.putAll(envToSet);
463 }
464 BufferedReader rdr = null;
465 try {
466 List<String> outputList = new ArrayList<>();
467 pb.redirectErrorStream(true);
468 Process p = pb.start();
469 rdr = new BufferedReader(new InputStreamReader(p.getInputStream()));
470 String in = rdr.readLine();
471 while (in != null) {
472 outputList.add(in);
473 in = rdr.readLine();
474 }
475 p.waitFor();
476 p.destroy();
477
478 return new TestHelper.TestResult(cmdStr, p.exitValue(), outputList,
479 env, new Throwable("current stack of the test"));
480 } catch (Exception ex) {
481 ex.printStackTrace();
482 throw new RuntimeException(ex.getMessage());
483 }
484 }
485
486 static FileFilter createFilter(final String extension) {
487 return new FileFilter() {
488 @Override
489 public boolean accept(File pathname) {
490 String name = pathname.getName();
491 if (name.endsWith(extension)) {
492 return true;
493 }
494 return false;
495 }
496 };
497 }
498
499 static boolean isEnglishLocale() {
500 return Locale.getDefault().getLanguage().equals("en");
501 }
502
503 /**
504 * Helper method to initialize a simple module that just prints the passed in arguments
505 */
506 static void createEchoArgumentsModule(File modulesDir) throws IOException {
507 if (modulesDir.exists()) {
508 recursiveDelete(modulesDir);
509 }
510
511 modulesDir.mkdirs();
512
513 File srcDir = new File(modulesDir, "src");
514 File modsDir = new File(modulesDir, "mods");
515 File testDir = new File(srcDir, "test");
516 File launcherTestDir = new File(testDir, "launcher");
517 srcDir.mkdir();
518 modsDir.mkdir();
519 testDir.mkdir();
520 launcherTestDir.mkdir();
521
522 String[] moduleInfoCode = { "module test {}" };
523 createFile(new File(testDir, "module-info.java"), Arrays.asList(moduleInfoCode));
524
525 String[] moduleCode = {
526 "package launcher;",
527 "import java.util.Arrays;",
528 "public class Main {",
529 "public static void main(String[] args) {",
530 "System.out.println(Arrays.toString(args));",
531 "}",
532 "}"
533 };
534 createFile(new File(launcherTestDir, "Main.java"), Arrays.asList(moduleCode));
535 }
536
537 /*
538 * A class to encapsulate the test results and stuff, with some ease
539 * of use methods to check the test results.
540 */
541 static class TestResult {
542 PrintWriter status;
543 StringWriter sw;
544 int exitValue;
545 List<String> testOutput;
546 Map<String, String> env;
547 Throwable t;
548 boolean testStatus;
549
550 public TestResult(String str, int rv, List<String> oList,
551 Map<String, String> env, Throwable t) {
552 sw = new StringWriter();
553 status = new PrintWriter(sw);
554 status.println("Executed command: " + str + "\n");
555 exitValue = rv;
556 testOutput = oList;
557 this.env = env;
558 this.t = t;
559 testStatus = true;
560 }
561
562 void appendError(String x) {
563 testStatus = false;
564 testExitValue++;
565 status.println(TEST_PREFIX + x);
566 }
567
568 void indentStatus(String x) {
569 status.println(" " + x);
570 }
571
572 void checkNegative() {
573 if (exitValue == 0) {
574 appendError("test must not return 0 exit value");
575 }
576 }
577
578 void checkPositive() {
579 if (exitValue != 0) {
580 appendError("test did not return 0 exit value");
581 }
582 }
583
584 boolean isOK() {
585 return exitValue == 0;
586 }
587
588 boolean isZeroOutput() {
589 if (!testOutput.isEmpty()) {
590 appendError("No message from cmd please");
591 return false;
592 }
593 return true;
594 }
595
596 boolean isNotZeroOutput() {
597 if (testOutput.isEmpty()) {
598 appendError("Missing message");
599 return false;
600 }
601 return true;
602 }
603
604 @Override
605 public String toString() {
606 status.println("++++Begin Test Info++++");
607 status.println("Test Status: " + (testStatus ? "PASS" : "FAIL"));
608 status.println("++++Test Environment++++");
609 for (String x : env.keySet()) {
610 indentStatus(x + "=" + env.get(x));
611 }
612 status.println("++++Test Output++++");
613 for (String x : testOutput) {
614 indentStatus(x);
615 }
616 status.println("++++Test Stack Trace++++");
617 status.println(t.toString());
618 for (StackTraceElement e : t.getStackTrace()) {
619 indentStatus(e.toString());
620 }
621 status.println("++++End of Test Info++++");
622 status.flush();
623 String out = sw.toString();
624 status.close();
625 return out;
626 }
627
628 boolean contains(String str) {
629 for (String x : testOutput) {
630 if (x.contains(str)) {
631 return true;
632 }
633 }
634 appendError("string <" + str + "> not found");
635 return false;
636 }
637
638 boolean notContains(String str) {
639 for (String x : testOutput) {
640 if (x.contains(str)) {
641 appendError("string <" + str + "> found");
642 return false;
643 }
644 }
645 return true;
646 }
647
648 boolean matches(String regexToMatch) {
649 for (String x : testOutput) {
650 if (x.matches(regexToMatch)) {
651 return true;
652 }
653 }
654 appendError("regex <" + regexToMatch + "> not matched");
655 return false;
656 }
657
658 boolean notMatches(String regexToMatch) {
659 for (String x : testOutput) {
660 if (!x.matches(regexToMatch)) {
661 return true;
662 }
663 }
664 appendError("regex <" + regexToMatch + "> matched");
665 return false;
666 }
667 }
668 /**
669 * Indicates that the annotated method is a test method.
670 */
671 @Retention(RetentionPolicy.RUNTIME)
672 @Target(ElementType.METHOD)
673 public @interface Test {}
674 }