test/hotspot/jtreg/compiler/graalunit/com.oracle.mxtool.junit/com/oracle/mxtool/junit/MxJUnitWrapper.java
changeset 58523 fb3d408c7a7e
parent 50908 7c51db95ccb6
equal deleted inserted replaced
58522:17a34e111667 58523:fb3d408c7a7e
    27 import java.io.FileNotFoundException;
    27 import java.io.FileNotFoundException;
    28 import java.io.FileOutputStream;
    28 import java.io.FileOutputStream;
    29 import java.io.FileReader;
    29 import java.io.FileReader;
    30 import java.io.IOException;
    30 import java.io.IOException;
    31 import java.io.PrintStream;
    31 import java.io.PrintStream;
    32 import java.lang.annotation.Annotation;
       
    33 import java.lang.reflect.Method;
       
    34 import java.util.ArrayList;
    32 import java.util.ArrayList;
    35 import java.util.Collections;
    33 import java.util.Collections;
    36 import java.util.HashSet;
       
    37 import java.util.List;
    34 import java.util.List;
    38 import java.util.Map;
    35 import java.util.Map;
    39 import java.util.Optional;
       
    40 import java.util.ServiceLoader;
    36 import java.util.ServiceLoader;
    41 import java.util.Set;
    37 import java.util.Set;
    42 import java.util.regex.Matcher;
    38 import java.util.TreeSet;
    43 import java.util.regex.Pattern;
       
    44 
    39 
    45 import org.junit.internal.JUnitSystem;
    40 import org.junit.internal.JUnitSystem;
    46 import org.junit.internal.RealSystem;
    41 import org.junit.internal.RealSystem;
    47 import org.junit.runner.Description;
    42 import org.junit.runner.Description;
    48 import org.junit.runner.JUnitCore;
    43 import org.junit.runner.JUnitCore;
    56 import org.junit.runners.model.RunnerScheduler;
    51 import org.junit.runners.model.RunnerScheduler;
    57 
    52 
    58 import junit.runner.Version;
    53 import junit.runner.Version;
    59 
    54 
    60 public class MxJUnitWrapper {
    55 public class MxJUnitWrapper {
       
    56 
       
    57     // Unit tests that start a JVM subprocess can use these system properties to
       
    58     // add --add-exports and --add-opens as necessary to the JVM command line.
       
    59     //
       
    60     // Known usages:
       
    61     // org.graalvm.compiler.test.SubprocessUtil.getPackageOpeningOptions()
       
    62     public static final String OPENED_PACKAGES_PROPERTY_NAME = "com.oracle.mxtool.junit.opens";
       
    63     public static final String EXPORTED_PACKAGES_PROPERTY_NAME = "com.oracle.mxtool.junit.exports";
    61 
    64 
    62     public static class MxJUnitConfig {
    65     public static class MxJUnitConfig {
    63 
    66 
    64         public boolean verbose = false;
    67         public boolean verbose = false;
    65         public boolean veryVerbose = false;
    68         public boolean veryVerbose = false;
   134         MxJUnitRequest.Builder builder = new MxJUnitRequest.Builder();
   137         MxJUnitRequest.Builder builder = new MxJUnitRequest.Builder();
   135         MxJUnitConfig config = new MxJUnitConfig();
   138         MxJUnitConfig config = new MxJUnitConfig();
   136 
   139 
   137         String[] expandedArgs = expandArgs(args);
   140         String[] expandedArgs = expandArgs(args);
   138         int i = 0;
   141         int i = 0;
       
   142         List<String> testSpecs = new ArrayList<>();
       
   143         List<String> openPackagesSpecs = new ArrayList<>();
   139         while (i < expandedArgs.length) {
   144         while (i < expandedArgs.length) {
   140             String each = expandedArgs[i];
   145             String each = expandedArgs[i];
   141             if (each.charAt(0) == '-') {
   146             if (each.charAt(0) == '-') {
   142                 // command line arguments
   147                 // command line arguments
   143                 if (each.contentEquals("-JUnitVerbose")) {
   148                 if (each.contentEquals("-JUnitVerbose")) {
   144                     config.verbose = true;
   149                     config.verbose = true;
       
   150                     config.enableTiming = true;
       
   151                 } else if (each.contentEquals("-JUnitOpenPackages")) {
       
   152                     if (i + 1 >= expandedArgs.length) {
       
   153                         system.out().println("Must include argument for -JUnitAddExports");
       
   154                         System.exit(1);
       
   155                     }
       
   156                     openPackagesSpecs.add(expandedArgs[++i]);
   145                 } else if (each.contentEquals("-JUnitVeryVerbose")) {
   157                 } else if (each.contentEquals("-JUnitVeryVerbose")) {
       
   158                     config.verbose = true;
   146                     config.veryVerbose = true;
   159                     config.veryVerbose = true;
       
   160                     config.enableTiming = true;
   147                 } else if (each.contentEquals("-JUnitFailFast")) {
   161                 } else if (each.contentEquals("-JUnitFailFast")) {
   148                     config.failFast = true;
   162                     config.failFast = true;
   149                 } else if (each.contentEquals("-JUnitEnableTiming")) {
   163                 } else if (each.contentEquals("-JUnitEnableTiming")) {
   150                     config.enableTiming = true;
   164                     config.enableTiming = true;
   151                 } else if (each.contentEquals("-JUnitColor")) {
   165                 } else if (each.contentEquals("-JUnitColor")) {
   170                 } else {
   184                 } else {
   171                     system.out().println("Unknown command line argument: " + each);
   185                     system.out().println("Unknown command line argument: " + each);
   172                 }
   186                 }
   173 
   187 
   174             } else {
   188             } else {
   175 
   189                 testSpecs.add(each);
   176                 try {
       
   177                     builder.addTestSpec(each);
       
   178                 } catch (MxJUnitRequest.BuilderException ex) {
       
   179                     system.out().println(ex.getMessage());
       
   180                     System.exit(1);
       
   181                 }
       
   182             }
   190             }
   183             i++;
   191             i++;
   184         }
   192         }
   185 
   193 
       
   194         ModuleSupport moduleSupport = new ModuleSupport(system.out());
       
   195         Set<String> opened = new TreeSet<>();
       
   196         Set<String> exported = new TreeSet<>();
       
   197         for (String spec : openPackagesSpecs) {
       
   198             moduleSupport.openPackages(spec, "-JUnitOpenPackages", opened, exported);
       
   199         }
       
   200 
       
   201         for (String spec : testSpecs) {
       
   202             try {
       
   203                 builder.addTestSpec(spec);
       
   204             } catch (MxJUnitRequest.BuilderException ex) {
       
   205                 system.out().println(ex.getMessage());
       
   206                 System.exit(1);
       
   207             }
       
   208         }
       
   209 
   186         MxJUnitRequest request = builder.build();
   210         MxJUnitRequest request = builder.build();
   187 
   211         moduleSupport.processAddExportsAnnotations(request.classes, opened, exported);
   188         if (System.getProperty("java.specification.version").compareTo("1.9") >= 0) {
   212 
   189             addExports(request.classes, system.out());
   213         if (!opened.isEmpty()) {
       
   214             System.setProperty(OPENED_PACKAGES_PROPERTY_NAME, String.join(System.lineSeparator(), opened));
       
   215         }
       
   216         if (!exported.isEmpty()) {
       
   217             System.setProperty(EXPORTED_PACKAGES_PROPERTY_NAME, String.join(System.lineSeparator(), exported));
   190         }
   218         }
   191 
   219 
   192         for (RunListener p : ServiceLoader.load(RunListener.class)) {
   220         for (RunListener p : ServiceLoader.load(RunListener.class)) {
   193             junitCore.addListener(p);
   221             junitCore.addListener(p);
   194         }
   222         }
   283         }
   311         }
   284 
   312 
   285         return result;
   313         return result;
   286     }
   314     }
   287 
   315 
   288     private static final Pattern MODULE_PACKAGE_RE = Pattern.compile("([^/]+)/(.+)");
       
   289 
       
   290     private static class Timing<T> implements Comparable<Timing<T>> {
   316     private static class Timing<T> implements Comparable<Timing<T>> {
   291         final T subject;
   317         final T subject;
   292         final long value;
   318         final long value;
   293 
   319 
   294         Timing(T subject, long value) {
   320         Timing(T subject, long value) {
   338             Object[] current = timings.getCurrentTestDuration();
   364             Object[] current = timings.getCurrentTestDuration();
   339             if (current != null) {
   365             if (current != null) {
   340                 System.out.printf("Test %s not finished after %d ms%n", current[0], current[1]);
   366                 System.out.printf("Test %s not finished after %d ms%n", current[0], current[1]);
   341             }
   367             }
   342 
   368 
   343         }
       
   344     }
       
   345 
       
   346     /**
       
   347      * Adds the super types of {@code cls} to {@code supertypes}.
       
   348      */
       
   349     private static void gatherSupertypes(Class<?> cls, Set<Class<?>> supertypes) {
       
   350         if (!supertypes.contains(cls)) {
       
   351             supertypes.add(cls);
       
   352             Class<?> superclass = cls.getSuperclass();
       
   353             if (superclass != null) {
       
   354                 gatherSupertypes(superclass, supertypes);
       
   355             }
       
   356             for (Class<?> iface : cls.getInterfaces()) {
       
   357                 gatherSupertypes(iface, supertypes);
       
   358             }
       
   359         }
       
   360     }
       
   361 
       
   362     /**
       
   363      * Updates modules specified in {@code AddExport} annotations on {@code classes} to export
       
   364      * concealed packages to the annotation classes' declaring modules.
       
   365      */
       
   366     private static void addExports(Set<Class<?>> classes, PrintStream out) {
       
   367         Set<Class<?>> types = new HashSet<>();
       
   368         for (Class<?> cls : classes) {
       
   369             gatherSupertypes(cls, types);
       
   370         }
       
   371         for (Class<?> cls : types) {
       
   372             Annotation[] annos = cls.getAnnotations();
       
   373             for (Annotation a : annos) {
       
   374                 Class<? extends Annotation> annotationType = a.annotationType();
       
   375                 if (annotationType.getSimpleName().equals("AddExports")) {
       
   376                     Optional<String[]> value = getElement("value", String[].class, a);
       
   377                     if (value.isPresent()) {
       
   378                         for (String export : value.get()) {
       
   379                             Matcher m = MODULE_PACKAGE_RE.matcher(export);
       
   380                             if (m.matches()) {
       
   381                                 String moduleName = m.group(1);
       
   382                                 String packageName = m.group(2);
       
   383                                 JLModule module = JLModule.find(moduleName);
       
   384                                 if (module == null) {
       
   385                                     out.printf("%s: Cannot find module named %s specified in \"AddExports\" annotation: %s%n", cls.getName(), moduleName, a);
       
   386                                 } else {
       
   387                                     if (packageName.equals("*")) {
       
   388                                         module.exportAllPackagesTo(JLModule.fromClass(cls));
       
   389                                     } else {
       
   390                                         module.addExports(packageName, JLModule.fromClass(cls));
       
   391                                         module.addOpens(packageName, JLModule.fromClass(cls));
       
   392                                     }
       
   393                                 }
       
   394                             } else {
       
   395                                 out.printf("%s: Ignoring \"AddExports\" annotation with value not matching <module>/<package> pattern: %s%n", cls.getName(), a);
       
   396                             }
       
   397                         }
       
   398                     } else {
       
   399                         out.printf("%s: Ignoring \"AddExports\" annotation without `String value` element: %s%n", cls.getName(), a);
       
   400                     }
       
   401                 }
       
   402             }
       
   403         }
       
   404     }
       
   405 
       
   406     /**
       
   407      * Gets the value of the element named {@code name} of type {@code type} from {@code annotation}
       
   408      * if present.
       
   409      *
       
   410      * @return the requested element value wrapped in an {@link Optional} or
       
   411      *         {@link Optional#empty()} if {@code annotation} has no element named {@code name}
       
   412      * @throws AssertionError if {@code annotation} has an element of the given name but whose type
       
   413      *             is not {@code type} or if there's some problem reading the value via reflection
       
   414      */
       
   415     private static <T> Optional<T> getElement(String name, Class<T> type, Annotation annotation) {
       
   416         Class<? extends Annotation> annotationType = annotation.annotationType();
       
   417         Method valueAccessor;
       
   418         try {
       
   419             valueAccessor = annotationType.getMethod(name);
       
   420             if (!valueAccessor.getReturnType().equals(type)) {
       
   421                 throw new AssertionError(String.format("Element %s of %s is of type %s, not %s ", name, annotationType.getName(), valueAccessor.getReturnType().getName(), type.getName()));
       
   422             }
       
   423         } catch (NoSuchMethodException e) {
       
   424             return Optional.empty();
       
   425         }
       
   426         try {
       
   427             return Optional.of(type.cast(valueAccessor.invoke(annotation)));
       
   428         } catch (Exception e) {
       
   429             throw new AssertionError(String.format("Could not read %s element from %s", name, annotation), e);
       
   430         }
   369         }
   431     }
   370     }
   432 
   371 
   433     /**
   372     /**
   434      * Expand any arguments starting with @ and return the resulting argument array.
   373      * Expand any arguments starting with @ and return the resulting argument array.