Utils.java revision 2757:d2f32a6fef00
1/* 2 * Copyright (c) 2013, 2017, 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 24package jdk.test.lib; 25 26import java.io.File; 27import java.io.IOException; 28import java.net.InetAddress; 29import java.net.MalformedURLException; 30import java.net.ServerSocket; 31import java.net.URL; 32import java.net.URLClassLoader; 33import java.net.UnknownHostException; 34import java.nio.file.Files; 35import java.nio.file.Path; 36import java.nio.file.Paths; 37import java.util.ArrayList; 38import java.util.Arrays; 39import java.util.Collection; 40import java.util.Collections; 41import java.util.Iterator; 42import java.util.Map; 43import java.util.HashMap; 44import java.util.List; 45import java.util.Objects; 46import java.util.Random; 47import java.util.function.BooleanSupplier; 48import java.util.concurrent.TimeUnit; 49import java.util.function.Consumer; 50import java.util.function.Function; 51import java.util.regex.Matcher; 52import java.util.regex.Pattern; 53 54import static jdk.test.lib.Asserts.assertTrue; 55import jdk.test.lib.process.ProcessTools; 56import jdk.test.lib.process.OutputAnalyzer; 57 58/** 59 * Common library for various test helper functions. 60 */ 61public final class Utils { 62 63 /** 64 * Returns the value of 'test.class.path' system property. 65 */ 66 public static final String TEST_CLASS_PATH = System.getProperty("test.class.path", "."); 67 68 /** 69 * Returns the sequence used by operating system to separate lines. 70 */ 71 public static final String NEW_LINE = System.getProperty("line.separator"); 72 73 /** 74 * Returns the value of 'test.vm.opts' system property. 75 */ 76 public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim(); 77 78 /** 79 * Returns the value of 'test.java.opts' system property. 80 */ 81 public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim(); 82 83 /** 84 * Returns the value of 'test.src' system property. 85 */ 86 public static final String TEST_SRC = System.getProperty("test.src", "").trim(); 87 88 /* 89 * Returns the value of 'test.jdk' system property 90 */ 91 public static final String TEST_JDK = System.getProperty("test.jdk"); 92 93 /* 94 * Returns the value of 'compile.jdk' system property 95 */ 96 public static final String COMPILE_JDK= System.getProperty("compile.jdk", TEST_JDK); 97 98 /** 99 * Returns the value of 'test.classes' system property 100 */ 101 public static final String TEST_CLASSES = System.getProperty("test.classes", "."); 102 /** 103 * Defines property name for seed value. 104 */ 105 public static final String SEED_PROPERTY_NAME = "jdk.test.lib.random.seed"; 106 107 /* (non-javadoc) 108 * Random generator with (or without) predefined seed. Depends on 109 * "jdk.test.lib.random.seed" property value. 110 */ 111 private static volatile Random RANDOM_GENERATOR; 112 113 /** 114 * Contains the seed value used for {@link java.util.Random} creation. 115 */ 116 public static final long SEED = Long.getLong(SEED_PROPERTY_NAME, new Random().nextLong()); 117 /** 118 * Returns the value of 'test.timeout.factor' system property 119 * converted to {@code double}. 120 */ 121 public static final double TIMEOUT_FACTOR; 122 static { 123 String toFactor = System.getProperty("test.timeout.factor", "1.0"); 124 TIMEOUT_FACTOR = Double.parseDouble(toFactor); 125 } 126 127 /** 128 * Returns the value of JTREG default test timeout in milliseconds 129 * converted to {@code long}. 130 */ 131 public static final long DEFAULT_TEST_TIMEOUT = TimeUnit.SECONDS.toMillis(120); 132 133 private Utils() { 134 // Private constructor to prevent class instantiation 135 } 136 137 /** 138 * Returns the list of VM options. 139 * 140 * @return List of VM options 141 */ 142 public static List<String> getVmOptions() { 143 return Arrays.asList(safeSplitString(VM_OPTIONS)); 144 } 145 146 /** 147 * Returns the list of VM options with -J prefix. 148 * 149 * @return The list of VM options with -J prefix 150 */ 151 public static List<String> getForwardVmOptions() { 152 String[] opts = safeSplitString(VM_OPTIONS); 153 for (int i = 0; i < opts.length; i++) { 154 opts[i] = "-J" + opts[i]; 155 } 156 return Arrays.asList(opts); 157 } 158 159 /** 160 * Returns the default JTReg arguments for a jvm running a test. 161 * This is the combination of JTReg arguments test.vm.opts and test.java.opts. 162 * @return An array of options, or an empty array if no options. 163 */ 164 public static String[] getTestJavaOpts() { 165 List<String> opts = new ArrayList<String>(); 166 Collections.addAll(opts, safeSplitString(VM_OPTIONS)); 167 Collections.addAll(opts, safeSplitString(JAVA_OPTIONS)); 168 return opts.toArray(new String[0]); 169 } 170 171 /** 172 * Combines given arguments with default JTReg arguments for a jvm running a test. 173 * This is the combination of JTReg arguments test.vm.opts and test.java.opts 174 * @return The combination of JTReg test java options and user args. 175 */ 176 public static String[] addTestJavaOpts(String... userArgs) { 177 List<String> opts = new ArrayList<String>(); 178 Collections.addAll(opts, getTestJavaOpts()); 179 Collections.addAll(opts, userArgs); 180 return opts.toArray(new String[0]); 181 } 182 183 /** 184 * Removes any options specifying which GC to use, for example "-XX:+UseG1GC". 185 * Removes any options matching: -XX:(+/-)Use*GC 186 * Used when a test need to set its own GC version. Then any 187 * GC specified by the framework must first be removed. 188 * @return A copy of given opts with all GC options removed. 189 */ 190 private static final Pattern useGcPattern = Pattern.compile( 191 "(?:\\-XX\\:[\\+\\-]Use.+GC)" 192 + "|(?:\\-Xconcgc)"); 193 public static List<String> removeGcOpts(List<String> opts) { 194 List<String> optsWithoutGC = new ArrayList<String>(); 195 for (String opt : opts) { 196 if (useGcPattern.matcher(opt).matches()) { 197 System.out.println("removeGcOpts: removed " + opt); 198 } else { 199 optsWithoutGC.add(opt); 200 } 201 } 202 return optsWithoutGC; 203 } 204 205 /** 206 * Returns the default JTReg arguments for a jvm running a test without 207 * options that matches regular expressions in {@code filters}. 208 * This is the combination of JTReg arguments test.vm.opts and test.java.opts. 209 * @param filters Regular expressions used to filter out options. 210 * @return An array of options, or an empty array if no options. 211 */ 212 public static String[] getFilteredTestJavaOpts(String... filters) { 213 String options[] = getTestJavaOpts(); 214 215 if (filters.length == 0) { 216 return options; 217 } 218 219 List<String> filteredOptions = new ArrayList<String>(options.length); 220 Pattern patterns[] = new Pattern[filters.length]; 221 for (int i = 0; i < filters.length; i++) { 222 patterns[i] = Pattern.compile(filters[i]); 223 } 224 225 for (String option : options) { 226 boolean matched = false; 227 for (int i = 0; i < patterns.length && !matched; i++) { 228 Matcher matcher = patterns[i].matcher(option); 229 matched = matcher.find(); 230 } 231 if (!matched) { 232 filteredOptions.add(option); 233 } 234 } 235 236 return filteredOptions.toArray(new String[filteredOptions.size()]); 237 } 238 239 /** 240 * Splits a string by white space. 241 * Works like String.split(), but returns an empty array 242 * if the string is null or empty. 243 */ 244 private static String[] safeSplitString(String s) { 245 if (s == null || s.trim().isEmpty()) { 246 return new String[] {}; 247 } 248 return s.trim().split("\\s+"); 249 } 250 251 /** 252 * @return The full command line for the ProcessBuilder. 253 */ 254 public static String getCommandLine(ProcessBuilder pb) { 255 StringBuilder cmd = new StringBuilder(); 256 for (String s : pb.command()) { 257 cmd.append(s).append(" "); 258 } 259 return cmd.toString(); 260 } 261 262 /** 263 * Returns the free port on the local host. 264 * The function will spin until a valid port number is found. 265 * 266 * @return The port number 267 * @throws InterruptedException if any thread has interrupted the current thread 268 * @throws IOException if an I/O error occurs when opening the socket 269 */ 270 public static int getFreePort() throws InterruptedException, IOException { 271 int port = -1; 272 273 while (port <= 0) { 274 Thread.sleep(100); 275 276 ServerSocket serverSocket = null; 277 try { 278 serverSocket = new ServerSocket(0); 279 port = serverSocket.getLocalPort(); 280 } finally { 281 serverSocket.close(); 282 } 283 } 284 285 return port; 286 } 287 288 /** 289 * Returns the name of the local host. 290 * 291 * @return The host name 292 * @throws UnknownHostException if IP address of a host could not be determined 293 */ 294 public static String getHostname() throws UnknownHostException { 295 InetAddress inetAddress = InetAddress.getLocalHost(); 296 String hostName = inetAddress.getHostName(); 297 298 assertTrue((hostName != null && !hostName.isEmpty()), 299 "Cannot get hostname"); 300 301 return hostName; 302 } 303 304 /** 305 * Uses "jcmd -l" to search for a jvm pid. This function will wait 306 * forever (until jtreg timeout) for the pid to be found. 307 * @param key Regular expression to search for 308 * @return The found pid. 309 */ 310 public static int waitForJvmPid(String key) throws Throwable { 311 final long iterationSleepMillis = 250; 312 System.out.println("waitForJvmPid: Waiting for key '" + key + "'"); 313 System.out.flush(); 314 while (true) { 315 int pid = tryFindJvmPid(key); 316 if (pid >= 0) { 317 return pid; 318 } 319 Thread.sleep(iterationSleepMillis); 320 } 321 } 322 323 /** 324 * Searches for a jvm pid in the output from "jcmd -l". 325 * 326 * Example output from jcmd is: 327 * 12498 sun.tools.jcmd.JCmd -l 328 * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar 329 * 330 * @param key A regular expression to search for. 331 * @return The found pid, or -1 if not found. 332 * @throws Exception If multiple matching jvms are found. 333 */ 334 public static int tryFindJvmPid(String key) throws Throwable { 335 OutputAnalyzer output = null; 336 try { 337 JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd"); 338 jcmdLauncher.addToolArg("-l"); 339 output = ProcessTools.executeProcess(jcmdLauncher.getCommand()); 340 output.shouldHaveExitValue(0); 341 342 // Search for a line starting with numbers (pid), follwed by the key. 343 Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n"); 344 Matcher matcher = pattern.matcher(output.getStdout()); 345 346 int pid = -1; 347 if (matcher.find()) { 348 pid = Integer.parseInt(matcher.group(1)); 349 System.out.println("findJvmPid.pid: " + pid); 350 if (matcher.find()) { 351 throw new Exception("Found multiple JVM pids for key: " + key); 352 } 353 } 354 return pid; 355 } catch (Throwable t) { 356 System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t)); 357 throw t; 358 } 359 } 360 361 /** 362 * Adjusts the provided timeout value for the TIMEOUT_FACTOR 363 * @param tOut the timeout value to be adjusted 364 * @return The timeout value adjusted for the value of "test.timeout.factor" 365 * system property 366 */ 367 public static long adjustTimeout(long tOut) { 368 return Math.round(tOut * Utils.TIMEOUT_FACTOR); 369 } 370 371 /** 372 * Return the contents of the named file as a single String, 373 * or null if not found. 374 * @param filename name of the file to read 375 * @return String contents of file, or null if file not found. 376 * @throws IOException 377 * if an I/O error occurs reading from the file or a malformed or 378 * unmappable byte sequence is read 379 */ 380 public static String fileAsString(String filename) throws IOException { 381 Path filePath = Paths.get(filename); 382 if (!Files.exists(filePath)) return null; 383 return new String(Files.readAllBytes(filePath)); 384 } 385 386 private static final char[] hexArray = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 387 388 /** 389 * Returns hex view of byte array 390 * 391 * @param bytes byte array to process 392 * @return Space separated hexadecimal string representation of bytes 393 */ 394 395 public static String toHexString(byte[] bytes) { 396 char[] hexView = new char[bytes.length * 3]; 397 int i = 0; 398 for (byte b : bytes) { 399 hexView[i++] = hexArray[(b >> 4) & 0x0F]; 400 hexView[i++] = hexArray[b & 0x0F]; 401 hexView[i++] = ' '; 402 } 403 return new String(hexView); 404 } 405 406 /** 407 * Returns {@link java.util.Random} generator initialized with particular seed. 408 * The seed could be provided via system property {@link Utils#SEED_PROPERTY_NAME} 409 * In case no seed is provided, the method uses a random number. 410 * The used seed printed to stdout. 411 * @return {@link java.util.Random} generator with particular seed. 412 */ 413 public static Random getRandomInstance() { 414 if (RANDOM_GENERATOR == null) { 415 synchronized (Utils.class) { 416 if (RANDOM_GENERATOR == null) { 417 RANDOM_GENERATOR = new Random(SEED); 418 System.out.printf("For random generator using seed: %d%n", SEED); 419 System.out.printf("To re-run test with same seed value please add \"-D%s=%d\" to command line.%n", SEED_PROPERTY_NAME, SEED); 420 } 421 } 422 } 423 return RANDOM_GENERATOR; 424 } 425 426 /** 427 * Returns random element of non empty collection 428 * 429 * @param <T> a type of collection element 430 * @param collection collection of elements 431 * @return random element of collection 432 * @throws IllegalArgumentException if collection is empty 433 */ 434 public static <T> T getRandomElement(Collection<T> collection) 435 throws IllegalArgumentException { 436 if (collection.isEmpty()) { 437 throw new IllegalArgumentException("Empty collection"); 438 } 439 Random random = getRandomInstance(); 440 int elementIndex = 1 + random.nextInt(collection.size() - 1); 441 Iterator<T> iterator = collection.iterator(); 442 while (--elementIndex != 0) { 443 iterator.next(); 444 } 445 return iterator.next(); 446 } 447 448 /** 449 * Returns random element of non empty array 450 * 451 * @param <T> a type of array element 452 * @param array array of elements 453 * @return random element of array 454 * @throws IllegalArgumentException if array is empty 455 */ 456 public static <T> T getRandomElement(T[] array) 457 throws IllegalArgumentException { 458 if (array == null || array.length == 0) { 459 throw new IllegalArgumentException("Empty or null array"); 460 } 461 Random random = getRandomInstance(); 462 return array[random.nextInt(array.length)]; 463 } 464 465 /** 466 * Wait for condition to be true 467 * 468 * @param condition, a condition to wait for 469 */ 470 public static final void waitForCondition(BooleanSupplier condition) { 471 waitForCondition(condition, -1L, 100L); 472 } 473 474 /** 475 * Wait until timeout for condition to be true 476 * 477 * @param condition, a condition to wait for 478 * @param timeout a time in milliseconds to wait for condition to be true 479 * specifying -1 will wait forever 480 * @return condition value, to determine if wait was successful 481 */ 482 public static final boolean waitForCondition(BooleanSupplier condition, 483 long timeout) { 484 return waitForCondition(condition, timeout, 100L); 485 } 486 487 /** 488 * Wait until timeout for condition to be true for specified time 489 * 490 * @param condition, a condition to wait for 491 * @param timeout a time in milliseconds to wait for condition to be true, 492 * specifying -1 will wait forever 493 * @param sleepTime a time to sleep value in milliseconds 494 * @return condition value, to determine if wait was successful 495 */ 496 public static final boolean waitForCondition(BooleanSupplier condition, 497 long timeout, long sleepTime) { 498 long startTime = System.currentTimeMillis(); 499 while (!(condition.getAsBoolean() || (timeout != -1L 500 && ((System.currentTimeMillis() - startTime) > timeout)))) { 501 try { 502 Thread.sleep(sleepTime); 503 } catch (InterruptedException e) { 504 Thread.currentThread().interrupt(); 505 throw new Error(e); 506 } 507 } 508 return condition.getAsBoolean(); 509 } 510 511 /** 512 * Interface same as java.lang.Runnable but with 513 * method {@code run()} able to throw any Throwable. 514 */ 515 public static interface ThrowingRunnable { 516 void run() throws Throwable; 517 } 518 519 /** 520 * Filters out an exception that may be thrown by the given 521 * test according to the given filter. 522 * 523 * @param test - method that is invoked and checked for exception. 524 * @param filter - function that checks if the thrown exception matches 525 * criteria given in the filter's implementation. 526 * @return - exception that matches the filter if it has been thrown or 527 * {@code null} otherwise. 528 * @throws Throwable - if test has thrown an exception that does not 529 * match the filter. 530 */ 531 public static Throwable filterException(ThrowingRunnable test, 532 Function<Throwable, Boolean> filter) throws Throwable { 533 try { 534 test.run(); 535 } catch (Throwable t) { 536 if (filter.apply(t)) { 537 return t; 538 } else { 539 throw t; 540 } 541 } 542 return null; 543 } 544 545 /** 546 * Ensures a requested class is loaded 547 * @param aClass class to load 548 */ 549 public static void ensureClassIsLoaded(Class<?> aClass) { 550 if (aClass == null) { 551 throw new Error("Requested null class"); 552 } 553 try { 554 Class.forName(aClass.getName(), /* initialize = */ true, 555 ClassLoader.getSystemClassLoader()); 556 } catch (ClassNotFoundException e) { 557 throw new Error("Class not found", e); 558 } 559 } 560 /** 561 * @param parent a class loader to be the parent for the returned one 562 * @return an UrlClassLoader with urls made of the 'test.class.path' jtreg 563 * property and with the given parent 564 */ 565 public static URLClassLoader getTestClassPathURLClassLoader(ClassLoader parent) { 566 URL[] urls = Arrays.stream(TEST_CLASS_PATH.split(File.pathSeparator)) 567 .map(Paths::get) 568 .map(Path::toUri) 569 .map(x -> { 570 try { 571 return x.toURL(); 572 } catch (MalformedURLException ex) { 573 throw new Error("Test issue. JTREG property" 574 + " 'test.class.path'" 575 + " is not defined correctly", ex); 576 } 577 }).toArray(URL[]::new); 578 return new URLClassLoader(urls, parent); 579 } 580 581 /** 582 * Runs runnable and checks that it throws expected exception. If exceptionException is null it means 583 * that we expect no exception to be thrown. 584 * @param runnable what we run 585 * @param expectedException expected exception 586 */ 587 public static void runAndCheckException(Runnable runnable, Class<? extends Throwable> expectedException) { 588 runAndCheckException(runnable, t -> { 589 if (t == null) { 590 if (expectedException != null) { 591 throw new AssertionError("Didn't get expected exception " + expectedException.getSimpleName()); 592 } 593 } else { 594 String message = "Got unexpected exception " + t.getClass().getSimpleName(); 595 if (expectedException == null) { 596 throw new AssertionError(message, t); 597 } else if (!expectedException.isAssignableFrom(t.getClass())) { 598 message += " instead of " + expectedException.getSimpleName(); 599 throw new AssertionError(message, t); 600 } 601 } 602 }); 603 } 604 605 /** 606 * Runs runnable and makes some checks to ensure that it throws expected exception. 607 * @param runnable what we run 608 * @param checkException a consumer which checks that we got expected exception and raises a new exception otherwise 609 */ 610 public static void runAndCheckException(Runnable runnable, Consumer<Throwable> checkException) { 611 try { 612 runnable.run(); 613 checkException.accept(null); 614 } catch (Throwable t) { 615 checkException.accept(t); 616 } 617 } 618 619 /** 620 * Converts to VM type signature 621 * 622 * @param type Java type to convert 623 * @return string representation of VM type 624 */ 625 public static String toJVMTypeSignature(Class<?> type) { 626 if (type.isPrimitive()) { 627 if (type == boolean.class) { 628 return "Z"; 629 } else if (type == byte.class) { 630 return "B"; 631 } else if (type == char.class) { 632 return "C"; 633 } else if (type == double.class) { 634 return "D"; 635 } else if (type == float.class) { 636 return "F"; 637 } else if (type == int.class) { 638 return "I"; 639 } else if (type == long.class) { 640 return "J"; 641 } else if (type == short.class) { 642 return "S"; 643 } else if (type == void.class) { 644 return "V"; 645 } else { 646 throw new Error("Unsupported type: " + type); 647 } 648 } 649 String result = type.getName().replaceAll("\\.", "/"); 650 if (!type.isArray()) { 651 return "L" + result + ";"; 652 } 653 return result; 654 } 655 656 public static Object[] getNullValues(Class<?>... types) { 657 Object[] result = new Object[types.length]; 658 int i = 0; 659 for (Class<?> type : types) { 660 result[i++] = NULL_VALUES.get(type); 661 } 662 return result; 663 } 664 private static Map<Class<?>, Object> NULL_VALUES = new HashMap<>(); 665 static { 666 NULL_VALUES.put(boolean.class, false); 667 NULL_VALUES.put(byte.class, (byte) 0); 668 NULL_VALUES.put(short.class, (short) 0); 669 NULL_VALUES.put(char.class, '\0'); 670 NULL_VALUES.put(int.class, 0); 671 NULL_VALUES.put(long.class, 0L); 672 NULL_VALUES.put(float.class, 0.0f); 673 NULL_VALUES.put(double.class, 0.0d); 674 } 675 676 /** 677 * Returns mandatory property value 678 * @param propName is a name of property to request 679 * @return a String with requested property value 680 */ 681 public static String getMandatoryProperty(String propName) { 682 Objects.requireNonNull(propName, "Requested null property"); 683 String prop = System.getProperty(propName); 684 Objects.requireNonNull(prop, 685 String.format("A mandatory property '%s' isn't set", propName)); 686 return prop; 687 } 688 689 /* 690 * Run uname with specified arguments. 691 */ 692 public static OutputAnalyzer uname(String... args) throws Throwable { 693 String[] cmds = new String[args.length + 1]; 694 cmds[0] = "uname"; 695 System.arraycopy(args, 0, cmds, 1, args.length); 696 return ProcessTools.executeCommand(cmds); 697 } 698 699 /* 700 * Returns the system distro. 701 */ 702 public static String distro() { 703 try { 704 return uname("-v").asLines().get(0); 705 } catch (Throwable t) { 706 throw new RuntimeException("Failed to determine distro.", t); 707 } 708 } 709 710 // This method is intended to be called from a jtreg test. 711 // It will identify the name of the test by means of stack walking. 712 // It can handle both jtreg tests and a testng tests wrapped inside jtreg tests. 713 // For jtreg tests the name of the test will be searched by stack-walking 714 // until the method main() is found; the class containing that method is the 715 // main test class and will be returned as the name of the test. 716 // Special handling is used for testng tests. 717 public static String getTestName() { 718 String result = null; 719 // If we are using testng, then we should be able to load the "Test" annotation. 720 Class testClassAnnotation; 721 722 try { 723 testClassAnnotation = Class.forName("org.testng.annotations.Test"); 724 } catch (ClassNotFoundException e) { 725 testClassAnnotation = null; 726 } 727 728 StackTraceElement[] elms = (new Throwable()).getStackTrace(); 729 for (StackTraceElement n: elms) { 730 String className = n.getClassName(); 731 732 // If this is a "main" method, then use its class name, but only 733 // if we are not using testng. 734 if (testClassAnnotation == null && "main".equals(n.getMethodName())) { 735 result = className; 736 break; 737 } 738 739 // If this is a testng test, the test will have no "main" method. We can 740 // detect a testng test class by looking for the org.testng.annotations.Test 741 // annotation. If present, then use the name of this class. 742 if (testClassAnnotation != null) { 743 try { 744 Class c = Class.forName(className); 745 if (c.isAnnotationPresent(testClassAnnotation)) { 746 result = className; 747 break; 748 } 749 } catch (ClassNotFoundException e) { 750 throw new RuntimeException("Unexpected exception: " + e, e); 751 } 752 } 753 } 754 755 if (result == null) { 756 throw new RuntimeException("Couldn't find main test class in stack trace"); 757 } 758 759 return result; 760 } 761 762} 763 764