1 /*
   2  * Copyright 2007-2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @compile -XDignore.symbol.file RunMTest.java classp/MainX.java
  27  * @run main/othervm/timeout=600 RunMTest
  28  */
  29 
  30 import java.util.*;
  31 import java.io.*;
  32 import java.net.*;
  33 import java.lang.reflect.*;
  34 
  35 import javax.tools.*;
  36 
  37 import java.module.*;
  38 import java.module.annotation.MainClass;
  39 
  40 public class RunMTest {
  41 
  42     private final static char SEP = File.separatorChar;
  43 
  44     private final static String WARNING_HEADER =
  45         "// This is a machine generated file, do not edit\n" +
  46         "// Created by RunMTest v0.5\n";
  47 
  48     private final static String[] SP_HEADER = new String[] {
  49         "import java.lang.ModuleInfo.*;",
  50         "import java.module.annotation.*;",
  51         "",
  52     };
  53 
  54     private static final Map<String,String> defaultProperties;
  55 
  56     static {
  57         Properties p = System.getProperties();
  58         @SuppressWarnings("unchecked")
  59         Map<String,String> m = (Map<String,String>)(Map)p;
  60         defaultProperties = new HashMap<String,String>(m);
  61         defaultProperties.put("header", WARNING_HEADER);
  62     }
  63 
  64     private final File file;
  65     private final File outputDirectory;
  66     private final List<ModuleDescription> modules;
  67     private final List<TestDescription> tests;
  68 
  69     private RunMTest(File file, String baseDirectory) throws IOException {
  70         this.file = file;
  71         String mString = SEP + "mtest" + SEP;
  72         String cPath = file.getCanonicalPath();
  73         int k = cPath.lastIndexOf(mString);
  74         String subdir;
  75         if (k == -1) {
  76             subdir = file.getName();
  77         } else {
  78             subdir = cPath.substring(k + mString.length());
  79         }
  80         System.out.println(">>> Test " + subdir);
  81         outputDirectory = new File(baseDirectory, subdir);
  82         if (outputDirectory.exists()) {
  83             recursiveDelete(outputDirectory);
  84         }
  85         modules = new ArrayList<ModuleDescription>();
  86         tests = new ArrayList<TestDescription>();
  87         parse();
  88     }
  89 
  90     private String getName() {
  91         return file.getName();
  92     }
  93 
  94     private static String template;
  95 
  96     static String getTemplate() throws IOException {
  97         if (template != null) {
  98             return template;
  99         }
 100         File f = new File(System.getProperty("test.src", "."), "cl-template.java");
 101         InputStream in = new FileInputStream(f);
 102         ByteArrayOutputStream out = new ByteArrayOutputStream();
 103         byte[] data = new byte[8192];
 104         while (true) {
 105             int n = in.read(data);
 106             if (n < 0) {
 107                 break;
 108             }
 109             out.write(data, 0, n);
 110         }
 111         in.close();
 112         byte[] b = out.toByteArray();
 113         template = new String(b, "UTF8");
 114         return template;
 115     }
 116 
 117     private void parse() throws IOException {
 118         InputStream in = new FileInputStream(file);
 119         BufferedReader reader = new BufferedReader(new InputStreamReader(in));
 120         while (true) {
 121             String line = getLine(reader);
 122             if (line == null) {
 123                 break;
 124             }
 125             if (line.trim().length() == 0) {
 126                 continue;
 127             }
 128             if (line.startsWith(">>> begin module ")) {
 129                 ModuleDescription m = parseModule(line, reader);
 130                 modules.add(m);
 131             } else if (line.startsWith(">>> begin test ")) {
 132                 TestDescription t = parseTest(line, reader);
 133                 tests.add(t);
 134             } else {
 135                 throw new IOException("Invalid declaration: " + line);
 136             }
 137         }
 138         in.close();
 139     }
 140 
 141     private String getLine(BufferedReader reader) throws IOException {
 142         while (true) {
 143             String line = reader.readLine();
 144             if (line == null) {
 145                 return null;
 146             }
 147             if (line.startsWith("#")) {
 148                 continue;
 149             }
 150             return line;
 151         }
 152     }
 153 
 154     private static void recursiveDelete(File dir) throws IOException {
 155         if (dir.isFile()) {
 156             dir.delete();
 157         } else if (dir.isDirectory()) {
 158             File[] entries = dir.listFiles();
 159             for (int i = 0; i < entries.length; i++) {
 160                 if (entries[i].isDirectory()) {
 161                     recursiveDelete(entries[i]);
 162                 }
 163                 entries[i].delete();
 164             }
 165             dir.delete();
 166         }
 167     }
 168 
 169     private static class ModuleDescription {
 170 
 171         private final String name;
 172         private final RunMTest mTest;
 173         private List<String> annotations;
 174         private List<String> imports;
 175         private List<String> exports;
 176         private List<ClassDescription> classes;
 177         private List<FileDescription> otherFiles;
 178 
 179         private List<File> sourceFiles;
 180 
 181         private File moduleDir;
 182 
 183         private ModuleDescription(RunMTest mTest, String name) {
 184             this.mTest = mTest;
 185             this.name = name;
 186             annotations = new ArrayList<String>();
 187             imports = new ArrayList<String>();
 188             exports = new ArrayList<String>();
 189             classes = new ArrayList<ClassDescription>();
 190             otherFiles = new ArrayList<FileDescription>();
 191             sourceFiles = new ArrayList<File>();
 192         }
 193 
 194         private String getMangledName() {
 195             return name.replace('.', '$');
 196         }
 197 
 198         private File getModuleDir() {
 199             if (moduleDir == null) {
 200                 String suffix = "";
 201                 for (String s : annotations) {
 202                     if (s.startsWith("@Version(\"")) {
 203                         suffix = "-" + s.substring(10, s.length() - 2);
 204                     }
 205                 }
 206                 moduleDir = new File(mTest.outputDirectory, getMangledName() + suffix);
 207             }
 208             return moduleDir;
 209         }
 210 
 211         private File write() throws IOException {
 212             File moduledir = getModuleDir();
 213             File dir = new File(moduleDir, name.replace('.', SEP));
 214             dir.mkdirs();
 215             String mangledName = getMangledName();
 216             File spfile = new File(dir, "module_info.java");
 217             PrintWriter writer = new PrintWriter(spfile);
 218             writer.println(WARNING_HEADER);
 219             writer.println("package " + mangledName + ";");
 220             writer.println();
 221             for (String s : SP_HEADER) {
 222                 writer.println(s);
 223             }
 224             for (String s : annotations) {
 225                 writer.println(s);
 226             }
 227             writer.println("class module_info {");
 228             writer.println();
 229             for (String s : exports) {
 230                 writer.println("    exports " + s.replace(".", "$") + ";");
 231             }
 232             writer.println();
 233             writer.println("}");
 234             writer.close();
 235             return spfile;
 236         }
 237 
 238     }
 239 
 240     private static String substituteProperties(String template, Map<String,String> props) {
 241         while (true) {
 242             int k = template.indexOf("${");
 243             if (k == -1) {
 244                 break;
 245             }
 246             int e = template.indexOf("}", k);
 247             String name = template.substring(k + 2, e);
 248             String prop = props.get(name);
 249             if (prop == null) {
 250                 prop = defaultProperties.get(name);
 251             }
 252             if (prop == null) {
 253                 throw new IllegalArgumentException("Property not defined: " + name);
 254             }
 255             template = template.substring(0, k) + prop + template.substring(e + 1);
 256         }
 257         return template;
 258     }
 259 
 260     private static class ClassDescription {
 261 
 262         private final String name;
 263         private final String pkg;
 264 
 265         private final Map<String,String> properties;
 266 
 267         private ClassDescription(String line) {
 268             String[] ss = line.split(" ");
 269             String s = ss[ss.length - 1];
 270             int k = s.lastIndexOf('.');
 271             pkg = s.substring(0, k);
 272             name = s.substring(k + 1);
 273             properties = new HashMap<String,String>();
 274             properties.put("name", name);
 275             properties.put("pkg", pkg);
 276             properties.put("import", "");
 277             properties.put("super", "");
 278             properties.put("info", "");
 279             properties.put("run", "");
 280             properties.put("body", "");
 281         }
 282 
 283         private void updateProperty(String propertyName, String line) {
 284             String s = properties.get(propertyName);
 285             if (s == null) {
 286                 properties.put(propertyName, line);
 287             } else {
 288                 properties.put(propertyName, s + "\n" + line);
 289             }
 290         }
 291 
 292         private File write(ModuleDescription md) throws IOException {
 293             File moduledir = md.getModuleDir();
 294             File pkgdir = new File(moduledir, pkg.replace('.', SEP));
 295             pkgdir.mkdirs();
 296             File srcfile = new File(pkgdir, name + ".java");
 297             String template = getTemplate();
 298             String srcstring = substituteProperties(template, properties);
 299             OutputStream out = new FileOutputStream(srcfile);
 300             out.write(srcstring.getBytes("UTF8"));
 301             out.close();
 302             return srcfile;
 303         }
 304 
 305     }
 306 
 307     private void runTests() throws Exception {
 308         for (TestDescription t : tests) {
 309             t.runTest(this);
 310         }
 311     }
 312 
 313     private static class TestDescription {
 314 
 315         private final String name;
 316         private String result;
 317 
 318         private TestDescription(String name) {
 319             this.name = name;
 320         }
 321 
 322         private void runTest(RunMTest mTest) throws Exception {
 323             System.out.println("> Running test " + name + "...");
 324             Repository parent = sun.module.repository.RepositoryConfig.getSystemRepository();
 325             Repository repository = Modules.newLocalRepository(mTest.getName(), mTest.outputDirectory, null, parent);
 326             ModuleDefinition md = repository.find(name);
 327             try {
 328                 Module m = md.getModuleInstance();
 329                 String mainClassName = md.getAnnotation(MainClass.class).value();
 330                 ClassLoader cl = m.getClassLoader();
 331                 Class<?> clazz = cl.loadClass(mainClassName);
 332                 Method method = clazz.getMethod("main", String[].class);
 333                 method.invoke(null, (Object)new String[0]);
 334             } catch (Exception e) {
 335                 if ((e instanceof InvocationTargetException) && (e.getCause() instanceof Exception)) {
 336                     e = (Exception)e.getCause();
 337                 }
 338                 if (result.equals("return")) {
 339                     throw new Exception("test failed", e);
 340                 }
 341                 if (result.startsWith("exception ")) {
 342                     String excname = result.substring("exception ".length());
 343                     String[] fqe = e.getClass().getName().split("\\.");
 344                     if (fqe[fqe.length - 1].equals(excname)) {
 345                         System.out.println("> Test completed with expected exception: " + e);
 346                         return;
 347                     }
 348                     throw new Exception("Expected exception " + excname, e);
 349                 }
 350                 throw new Exception("Unknown test result: " + result);
 351             }
 352             if (!result.equals("return")) {
 353                 throw new Exception("Test unexpectedly returned normally");
 354             }
 355             System.out.println("> Test completed.");
 356         }
 357 
 358     }
 359 
 360     private TestDescription parseTest(String header, BufferedReader reader) throws IOException {
 361         String[] s = header.split(" ");
 362         String name = s[s.length - 1];
 363         TestDescription test = new TestDescription(name);
 364         while (true) {
 365             String line = getLine(reader);
 366             if (line == null) {
 367                 throw new EOFException();
 368             }
 369             if (line.equals(">>> end test")) {
 370                 break;
 371             }
 372             test.result = line;
 373         }
 374         return test;
 375     }
 376 
 377     private ClassDescription parseClass(String header, BufferedReader reader) throws IOException {
 378         ClassDescription cd = new ClassDescription(header);
 379         String prop = "run";
 380         while (true) {
 381             String line = getLine(reader);
 382             if (line == null) {
 383                 throw new EOFException();
 384             }
 385             if (line.equals(">> end class")) {
 386                 break;
 387             }
 388             if (line.startsWith("> ")) {
 389                 prop = line.substring(2).trim();
 390                 continue;
 391             }
 392             cd.updateProperty(prop, line);
 393         }
 394         return cd;
 395     }
 396 
 397     private static class FileDescription {
 398 
 399         final String name;
 400         String sourceName;
 401 
 402         private FileDescription(String line) {
 403             String[] ss = line.split(" ");
 404             name = ss[ss.length - 1];
 405         }
 406 
 407         private File write(ModuleDescription md) throws IOException {
 408             File moduledir = md.getModuleDir();
 409             File destFile = new File(moduledir, name.replace('/', SEP));
 410             destFile.getParentFile().mkdirs();
 411             File srcFile = new File(md.mTest.file.getParentFile(), sourceName.replace('/', SEP));
 412             InputStream in = new FileInputStream(srcFile);
 413             OutputStream out = new FileOutputStream(destFile);
 414             byte[] buffer = new byte[2048];
 415             while (true) {
 416                 int n = in.read(buffer);
 417                 if (n < 0) {
 418                     break;
 419                 }
 420                 out.write(buffer, 0, n);
 421             }
 422             in.close();
 423             out.close();
 424             return destFile;
 425         }
 426 
 427     }
 428 
 429     private FileDescription parseFile(String header, BufferedReader reader) throws IOException {
 430         FileDescription fd = new FileDescription(header);
 431         while (true) {
 432             String line = getLine(reader);
 433             if (line == null) {
 434                 throw new EOFException();
 435             }
 436             if (line.equals(">> end file")) {
 437                 break;
 438             }
 439             if (line.startsWith("> copy ")) {
 440                 String name = line.substring(7).trim();
 441                 fd.sourceName = name;
 442                 continue;
 443             }
 444             throw new IOException("Invalid file declaration: " + line);
 445         }
 446         return fd;
 447 
 448     }
 449 
 450     private ModuleDescription parseModule(String header, BufferedReader reader) throws IOException {
 451         String[] s = header.split(" ");
 452         String name = s[s.length - 1];
 453         ModuleDescription md = new ModuleDescription(this, name);
 454         String section = "";
 455         while (true) {
 456             String line = getLine(reader);
 457             if (line == null) {
 458                 throw new EOFException();
 459             }
 460             if (line.startsWith(">> begin class ")) {
 461                 md.classes.add(parseClass(line, reader));
 462                 continue;
 463             }
 464             if (line.startsWith(">> begin file ")) {
 465                 md.otherFiles.add(parseFile(line, reader));
 466                 continue;
 467             }
 468             if (line.startsWith("> ")) {
 469                 section = line;
 470                 continue;
 471             }
 472             if (line.equals(">>> end module")) {
 473                 break;
 474             }
 475             if (section.equals("> annotations")) {
 476                 md.annotations.add(line);
 477             } else if (section.equals("> import")) {
 478                 md.imports.add(line);
 479             } else if (section.equals("> export")) {
 480                 md.exports.add(line);
 481             } else {
 482                 throw new IOException("Invalid module declaration: " + line);
 483             }
 484         }
 485         return md;
 486     }
 487 
 488     private void createSources() throws IOException {
 489         File f;
 490         for (ModuleDescription md : modules) {
 491             md.sourceFiles.clear();
 492             f = md.write();
 493             md.sourceFiles.add(f);
 494             for (ClassDescription c : md.classes) {
 495                 f = c.write(md);
 496                 md.sourceFiles.add(f);
 497             }
 498             for (FileDescription fd : md.otherFiles) {
 499                 fd.write(md);
 500             }
 501         }
 502     }
 503 
 504     private void makeJAM() throws IOException {
 505         // since we may have multiple versions of the same module,
 506         // with types of the same name, which javac does not like, compile
 507         // each module separately
 508         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 509         StringBuilder srcpath = new StringBuilder();
 510         boolean first = true;
 511         for (ModuleDescription module : modules) {
 512             File moduleDir = module.getModuleDir();
 513             if (first) {
 514                 first = false;
 515             } else {
 516                 srcpath.append(File.pathSeparatorChar);
 517             }
 518             srcpath.append(moduleDir);
 519         }
 520         for (ModuleDescription module : modules) {
 521             StringBuilder srclist = new StringBuilder();
 522             for (File f : module.sourceFiles) {
 523                 srclist.append(f.getPath());
 524                 srclist.append(" ");
 525             }
 526             String cmd = "-source 6 -target 6 -XDignore.symbol.file -implicit:none -Xlint:all -sourcepath "
 527                 + srcpath.toString() + " " + srclist;
 528             int r = compiler.run(null, null, null, cmd.split(" "));
 529             if (r != 0) {
 530                 throw new RuntimeException("Compilation failed: " + r);
 531             }
 532 
 533             File moduleDir = module.getModuleDir();
 534             File moduleJam = new File(outputDirectory, moduleDir.getName() + ".jam");
 535 
 536             ArrayList<String> args = new ArrayList<String>();
 537             args.add("cfsS");
 538             args.add(moduleJam.getCanonicalPath());
 539             args.add(module.getMangledName());
 540             args.add(moduleDir.getCanonicalPath() + File.separator);
 541 
 542             // Presume all other entries are directories containing classes.
 543             for (File f : moduleDir.listFiles()) {
 544                 if (!f.getName().equals("MODULE-INF")) {
 545                     args.add("-C");
 546                     args.add(moduleDir.getCanonicalPath());
 547                     args.add(f.getName());
 548                 }
 549                 else {
 550                     File tf = new File(moduleDir.getCanonicalPath(), "MODULE-INF" + File.separator + "legacy-classes.list");
 551                     if (tf.exists() && !tf.isDirectory()) {
 552                         args.add("-C");
 553                         args.add(moduleDir.getCanonicalPath());
 554                         args.add("MODULE-INF" + File.separator + "legacy-classes.list");
 555                     }
 556                 }
 557             }
 558 
 559             sun.module.tools.Jam jam = new sun.module.tools.Jam(System.out, System.err, "RunMTest");
 560             jam.run(args.toArray(new String[0]));
 561         }
 562     }
 563 
 564     private static void findFiles(File directory, Collection<File> results) throws IOException {
 565         File[] files = directory.listFiles();
 566         for (File file : files) {
 567             if (file.isDirectory()) {
 568                 findFiles(file, results);
 569             }
 570             if (file.isFile() && file.getPath().endsWith(".mtest")) {
 571                 results.add(file);
 572             }
 573         }
 574     }
 575 
 576     public static void main(String[] args) throws Exception {
 577         long start = System.currentTimeMillis();
 578         if (args.length == 0) {
 579             args = new String[] { "." };
 580         }
 581         Set<File> testFiles = new TreeSet<File>();
 582         File base = new File(System.getProperty("test.src", ".") + SEP + "mtest");
 583         for (String arg : args) {
 584             File file;
 585             if (!arg.equals(".")) {
 586                 file = new File(arg);
 587                 if (file.isFile()) {
 588                     testFiles.add(file);
 589                 }
 590                 if (file.isDirectory()) {
 591                     findFiles(file, testFiles);
 592                 }
 593             }
 594             file = new File(base, arg);
 595             if (file.isFile()) {
 596                 testFiles.add(file);
 597             }
 598             if (file.isDirectory()) {
 599                 findFiles(file, testFiles);
 600             }
 601         }
 602         for (Iterator<File> t = testFiles.iterator(); t.hasNext(); ) {
 603             File f = t.next();
 604             String fname = f.getName();
 605             if (fname.startsWith("s.") || fname.startsWith("p.") || fname.startsWith(",")) {
 606                 t.remove();
 607             }
 608         }
 609         if (testFiles.isEmpty()) {
 610             throw new RuntimeException("No tests found");
 611         }
 612         String outputDir = System.getProperty("test.classes", ".") + SEP + "tmp_mtest";
 613         int modules = 0;
 614         int tests = 0;
 615         for (File file : testFiles) {
 616             RunMTest test = new RunMTest(file, outputDir);
 617             test.createSources();
 618             test.makeJAM();
 619             test.runTests();
 620             modules += test.modules.size();
 621             tests += test.tests.size();
 622         }
 623         long stop = System.currentTimeMillis();
 624         System.out.println("All tests completed ("
 625             + testFiles.size() + " mtest files, "
 626             + modules + " modules, "
 627             + tests + " tests, time "
 628             + (stop - start) + " ms)");
 629     }
 630 
 631 }