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 }