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 protected final File file;
65 protected 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 protected 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
314 // Abstracting the creation of TestDescription instances to a factory
315 // allows other tests to override TestDescription.runTest.
316 //
317 public static class TestDescriptionFactory {
318 private static TestDescriptionFactory instance;
319
320 static TestDescription create(String name) {
321 return getInstance().doCreate(name);
322 }
323
324 private static TestDescriptionFactory getInstance() {
325 if (instance == null) {
326 String factName = System.getProperty("TestDescriptionFactory.classname");
327 if (factName != null) {
328 try {
329 Class<?> clazz = Class.forName(factName);
330 instance = (TestDescriptionFactory) clazz.newInstance();
331 } catch (Throwable t) {
332 throw new RuntimeException(t);
333 }
334 } else {
335 instance = new TestDescriptionFactory();
336 }
337 }
338 return instance;
339 }
340
341 protected TestDescription doCreate(String name) {
342 return new TestDescription(name);
343 }
344 }
345
346 public static class TestDescription {
347
348 protected final String name;
349 protected String result;
350
351 TestDescription(String name) {
352 this.name = name;
353 }
354
355 protected void runTest(RunMTest mTest) throws Exception {
356 System.out.println("> Running test " + name + "...");
357 Repository parent = sun.module.repository.RepositoryConfig.getSystemRepository();
358 Repository repository = Modules.newLocalRepository(parent, mTest.getName(), mTest.outputDirectory);
359 ModuleDefinition md = repository.find(name);
360 try {
361 Module m = md.getModuleInstance();
362 String mainClassName = md.getAnnotation(MainClass.class).value();
363 ClassLoader cl = m.getClassLoader();
364 Class<?> clazz = cl.loadClass(mainClassName);
365 Method method = clazz.getMethod("main", String[].class);
366 method.invoke(null, (Object)new String[0]);
367 } catch (Exception e) {
368 if ((e instanceof InvocationTargetException) && (e.getCause() instanceof Exception)) {
369 e = (Exception)e.getCause();
370 }
371 if (result.equals("return")) {
372 throw new Exception("test failed", e);
373 }
374 if (result.startsWith("exception ")) {
375 String excname = result.substring("exception ".length());
376 String[] fqe = e.getClass().getName().split("\\.");
377 if (fqe[fqe.length - 1].equals(excname)) {
378 System.out.println("> Test completed with expected exception: " + e);
379 return;
380 }
381 throw new Exception("Expected exception " + excname, e);
382 }
383 throw new Exception("Unknown test result: " + result);
384 }
385 if (!result.equals("return")) {
386 throw new Exception("Test unexpectedly returned normally");
387 }
388 System.out.println("> Test completed.");
389 }
390
391 }
392
393 private TestDescription parseTest(String header, BufferedReader reader) throws IOException {
394 String[] s = header.split(" ");
395 String name = s[s.length - 1];
396 TestDescription test = TestDescriptionFactory.create(name);
397 while (true) {
398 String line = getLine(reader);
399 if (line == null) {
400 throw new EOFException();
401 }
402 if (line.equals(">>> end test")) {
403 break;
404 }
405 test.result = line;
406 }
407 return test;
408 }
409
410 private ClassDescription parseClass(String header, BufferedReader reader) throws IOException {
411 ClassDescription cd = new ClassDescription(header);
412 String prop = "run";
413 while (true) {
414 String line = getLine(reader);
415 if (line == null) {
416 throw new EOFException();
417 }
418 if (line.equals(">> end class")) {
419 break;
420 }
421 if (line.startsWith("> ")) {
422 prop = line.substring(2).trim();
423 continue;
424 }
425 cd.updateProperty(prop, line);
426 }
427 return cd;
428 }
429
430 private static class FileDescription {
431
432 final String name;
433 String sourceName;
434
435 private FileDescription(String line) {
436 String[] ss = line.split(" ");
437 name = ss[ss.length - 1];
438 }
439
440 private File write(ModuleDescription md) throws IOException {
441 File moduledir = md.getModuleDir();
442 File destFile = new File(moduledir, name.replace('/', SEP));
443 destFile.getParentFile().mkdirs();
444 File srcFile = new File(md.mTest.file.getParentFile(), sourceName.replace('/', SEP));
445 InputStream in = new FileInputStream(srcFile);
446 OutputStream out = new FileOutputStream(destFile);
447 byte[] buffer = new byte[2048];
448 while (true) {
449 int n = in.read(buffer);
450 if (n < 0) {
451 break;
452 }
453 out.write(buffer, 0, n);
454 }
455 in.close();
456 out.close();
457 return destFile;
458 }
459
460 }
461
462 private FileDescription parseFile(String header, BufferedReader reader) throws IOException {
463 FileDescription fd = new FileDescription(header);
464 while (true) {
465 String line = getLine(reader);
466 if (line == null) {
467 throw new EOFException();
468 }
469 if (line.equals(">> end file")) {
470 break;
471 }
472 if (line.startsWith("> copy ")) {
473 String name = line.substring(7).trim();
474 fd.sourceName = name;
475 continue;
476 }
477 throw new IOException("Invalid file declaration: " + line);
478 }
479 return fd;
480
481 }
482
483 private ModuleDescription parseModule(String header, BufferedReader reader) throws IOException {
484 String[] s = header.split(" ");
485 String name = s[s.length - 1];
486 ModuleDescription md = new ModuleDescription(this, name);
487 String section = "";
488 while (true) {
489 String line = getLine(reader);
490 if (line == null) {
491 throw new EOFException();
492 }
493 if (line.startsWith(">> begin class ")) {
494 md.classes.add(parseClass(line, reader));
495 continue;
496 }
497 if (line.startsWith(">> begin file ")) {
498 md.otherFiles.add(parseFile(line, reader));
499 continue;
500 }
501 if (line.startsWith("> ")) {
502 section = line;
503 continue;
504 }
505 if (line.equals(">>> end module")) {
506 break;
507 }
508 if (section.equals("> annotations")) {
509 md.annotations.add(line);
510 } else if (section.equals("> import")) {
511 md.imports.add(line);
512 } else if (section.equals("> export")) {
513 md.exports.add(line);
514 } else {
515 throw new IOException("Invalid module declaration: " + line);
516 }
517 }
518 return md;
519 }
520
521 private void createSources() throws IOException {
522 File f;
523 for (ModuleDescription md : modules) {
524 md.sourceFiles.clear();
525 f = md.write();
526 md.sourceFiles.add(f);
527 for (ClassDescription c : md.classes) {
528 f = c.write(md);
529 md.sourceFiles.add(f);
530 }
531 for (FileDescription fd : md.otherFiles) {
532 fd.write(md);
533 }
534 }
535 }
536
537 private void makeJAM() throws IOException {
538 // since we may have multiple versions of the same module,
539 // with types of the same name, which javac does not like, compile
540 // each module separately
541 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
542 StringBuilder srcpath = new StringBuilder();
543 boolean first = true;
544 for (ModuleDescription module : modules) {
545 File moduleDir = module.getModuleDir();
546 if (first) {
547 first = false;
548 } else {
549 srcpath.append(File.pathSeparatorChar);
550 }
551 srcpath.append(moduleDir);
552 }
553 for (ModuleDescription module : modules) {
554 StringBuilder srclist = new StringBuilder();
555 for (File f : module.sourceFiles) {
556 srclist.append(f.getPath());
557 srclist.append(" ");
558 }
559 String cmd = "-source 6 -target 6 -XDignore.symbol.file -implicit:none -Xlint:all -sourcepath "
560 + srcpath.toString() + " " + srclist;
561 int r = compiler.run(null, null, null, cmd.split(" "));
562 if (r != 0) {
563 throw new RuntimeException("Compilation failed: " + r);
564 }
565
566 File moduleDir = module.getModuleDir();
567 File moduleJam = new File(outputDirectory, moduleDir.getName() + ".jam");
568
569 ArrayList<String> args = new ArrayList<String>();
570 args.add("cfsS");
571 args.add(moduleJam.getCanonicalPath());
572 args.add(module.getMangledName());
573 args.add(moduleDir.getCanonicalPath() + File.separator);
574
575 // Presume all other entries are directories containing classes.
576 for (File f : moduleDir.listFiles()) {
577 if (!f.getName().equals("MODULE-INF")) {
578 args.add("-C");
579 args.add(moduleDir.getCanonicalPath());
580 args.add(f.getName());
581 }
582 else {
583 File tf = new File(moduleDir.getCanonicalPath(), "MODULE-INF" + File.separator + "legacy-classes.list");
584 if (tf.exists() && !tf.isDirectory()) {
585 args.add("-C");
586 args.add(moduleDir.getCanonicalPath());
587 args.add("MODULE-INF" + File.separator + "legacy-classes.list");
588 }
589 }
590 }
591
592 sun.module.tools.Jam jam = new sun.module.tools.Jam(System.out, System.err, "RunMTest");
593 jam.run(args.toArray(new String[0]));
594 }
595 }
596
597 private static void findFiles(File directory, Collection<File> results) throws IOException {
598 File[] files = directory.listFiles();
599 for (File file : files) {
600 if (file.isDirectory()) {
601 findFiles(file, results);
602 }
603 if (file.isFile() && file.getPath().endsWith(".mtest")) {
604 results.add(file);
605 }
606 }
607 }
608
609 public static void main(String[] args) throws Exception {
610 long start = System.currentTimeMillis();
611 if (args.length == 0) {
612 args = new String[] { "." };
613 }
614 Set<File> testFiles = new TreeSet<File>();
615 File base = new File(System.getProperty("test.src", ".") + SEP + "mtest");
616 for (String arg : args) {
617 File file;
618 if (!arg.equals(".")) {
619 file = new File(arg);
620 if (file.isFile()) {
621 testFiles.add(file);
622 }
623 if (file.isDirectory()) {
624 findFiles(file, testFiles);
625 }
626 }
627 file = new File(base, arg);
628 if (file.isFile()) {
629 testFiles.add(file);
630 }
631 if (file.isDirectory()) {
632 findFiles(file, testFiles);
633 }
634 }
635 for (Iterator<File> t = testFiles.iterator(); t.hasNext(); ) {
636 File f = t.next();
637 String fname = f.getName();
638 if (fname.startsWith("s.") || fname.startsWith("p.") || fname.startsWith(",")) {
639 t.remove();
640 }
641 }
642 if (testFiles.isEmpty()) {
643 throw new RuntimeException("No tests found");
644 }
645 String outputDir = System.getProperty("test.classes", ".") + SEP + "tmp_mtest";
646 int modules = 0;
647 int tests = 0;
648 for (File file : testFiles) {
649 RunMTest test = new RunMTest(file, outputDir);
650 test.createSources();
651 test.makeJAM();
652 test.runTests();
653 modules += test.modules.size();
654 tests += test.tests.size();
655 }
656 long stop = System.currentTimeMillis();
657 System.out.println("All tests completed ("
658 + testFiles.size() + " mtest files, "
659 + modules + " modules, "
660 + tests + " tests, time "
661 + (stop - start) + " ms)");
662 }
663
664 }