TestSearchPaths.java revision 3294:9adfb22ff08f
11541Srgrimes/*
21541Srgrimes * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
31541Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41541Srgrimes *
51541Srgrimes * This code is free software; you can redistribute it and/or modify it
61541Srgrimes * under the terms of the GNU General Public License version 2 only, as
71541Srgrimes * published by the Free Software Foundation.
81541Srgrimes *
91541Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
101541Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
111541Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
121541Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
131541Srgrimes * accompanied this code).
141541Srgrimes *
151541Srgrimes * You should have received a copy of the GNU General Public License version
161541Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
171541Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
181541Srgrimes *
191541Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
201541Srgrimes * or visit www.oracle.com if you need additional information or have any
211541Srgrimes * questions.
221541Srgrimes */
231541Srgrimes
241541Srgrimes/*
251541Srgrimes * @test
261541Srgrimes * @bug 7026941
271541Srgrimes * @summary path options ignored when reusing filemanager across tasks
281541Srgrimes * @modules java.compiler
291541Srgrimes *          jdk.compiler
301541Srgrimes */
311541Srgrimes
321541Srgrimesimport java.io.File;
3314505Shsuimport java.io.FileWriter;
3450477Speterimport java.io.IOException;
351541Srgrimesimport java.io.PrintWriter;
361541Srgrimesimport java.net.URI;
372165Spaulimport java.nio.file.Files;
382165Spaulimport java.util.ArrayList;
392165Spaulimport java.util.Arrays;
4015492Sbdeimport java.util.Collections;
411541Srgrimesimport java.util.EnumSet;
421541Srgrimesimport java.util.List;
431541Srgrimesimport java.util.Objects;
441541Srgrimesimport java.util.jar.JarEntry;
451541Srgrimesimport java.util.jar.JarOutputStream;
461541Srgrimesimport java.util.regex.Matcher;
471541Srgrimesimport java.util.regex.Pattern;
481541Srgrimes
4936079Swollmanimport javax.tools.JavaCompiler;
5036079Swollmanimport javax.tools.JavaCompiler.CompilationTask;
511541Srgrimesimport javax.tools.JavaFileObject;
5236079Swollmanimport javax.tools.SimpleJavaFileObject;
531541Srgrimesimport javax.tools.StandardJavaFileManager;
541541Srgrimesimport javax.tools.StandardLocation;
551541Srgrimesimport javax.tools.ToolProvider;
561541Srgrimes
571541Srgrimesimport static javax.tools.StandardLocation.*;
581541Srgrimes
591541Srgrimes/**
6013765Smpp * Test for combinations of using javac command-line options and fileManager setLocation
611541Srgrimes * calls to affect the locations available in the fileManager.
621541Srgrimes *
631541Srgrimes * Using a single Java compiler and file manager, for each of the standard locations,
641541Srgrimes * a series of operations is performed, using either compiler options or setLocation
651541Srgrimes * calls. Each operation includes a compilation, and then a check for the value of
661541Srgrimes * the standard location available in the file manager.
671541Srgrimes *
681541Srgrimes * The operations generate and use unique files to minimize the possibility of false
691541Srgrimes * positive results.
701541Srgrimes */
7114547Sdgpublic class TestSearchPaths {
7214547Sdg
7314547Sdg    public static void main(String... args) throws Exception {
7414547Sdg        TestSearchPaths t = new TestSearchPaths();
7518787Spst        t.run();
7618787Spst    }
771541Srgrimes
781541Srgrimes    void run() throws Exception {
791541Srgrimes        compiler = ToolProvider.getSystemJavaCompiler();
8041087Struckman        fileManager = compiler.getStandardFileManager(null, null, null);
8141087Struckman        try {
821541Srgrimes            // basic output path
8355943Sjasone            testClassOutput();
841541Srgrimes
851541Srgrimes            // basic search paths
861541Srgrimes            testClassPath();
871541Srgrimes            testSourcePath();
881541Srgrimes            testPlatformClassPath();
891541Srgrimes
901541Srgrimes            // annotation processing
911541Srgrimes            testAnnotationProcessorPath();
921541Srgrimes            testSourceOutput();
931541Srgrimes
941541Srgrimes            // javah equivalent
951541Srgrimes            testNativeHeaderOutput();
961541Srgrimes
971541Srgrimes            // future-proof: guard against new StandardLocations being added
981541Srgrimes            if (!tested.equals(EnumSet.allOf(StandardLocation.class))) {
991541Srgrimes                // FIXME: need to update for JDK 9 locations
1001541Srgrimes                // error("not all standard locations have been tested");
1011541Srgrimes                out.println("not yet tested: " + EnumSet.complementOf(tested));
1021541Srgrimes            }
1031541Srgrimes
10436527Speter            if (errors > 0) {
1051541Srgrimes                throw new Exception(errors + " errors occurred");
10655943Sjasone            }
1071541Srgrimes        } finally {
10838482Swollman            fileManager.close();
10938482Swollman        }
11051381Sgreen    }
11138482Swollman
11236079Swollman    void testClassOutput() throws IOException {
11343458Sbde        String test = "testClassOutput";
1141541Srgrimes        System.err.println("test: " + test);
1151541Srgrimes
1161541Srgrimes        for (int i = 1; i <= 5; i++) {
1171541Srgrimes            File classes = createDir(test + "/" + i + "/classes");
1181541Srgrimes            List<String> options;
11914547Sdg            switch (i) {
12014547Sdg                default:
12114547Sdg                    options = getOptions("-d", classes.getPath());
12214547Sdg                    break;
12314547Sdg
12414547Sdg                case 3:
12514547Sdg                    setLocation(CLASS_OUTPUT, classes);
1261541Srgrimes                    options = null;
12714547Sdg                    break;
12814547Sdg            }
12914547Sdg            List<JavaFileObject> sources = getSources("class C" + i + " { }");
1301541Srgrimes            callTask(options, sources);
13114547Sdg            checkPath(CLASS_OUTPUT, Mode.EQUALS, classes);
13214547Sdg            checkFile(CLASS_OUTPUT, "C" + i + ".class");
13343196Sfenner        }
1341541Srgrimes
13536079Swollman        tested.add(CLASS_OUTPUT);
13636079Swollman    }
13736079Swollman
13836079Swollman    void testClassPath() throws IOException {
13936079Swollman        String test = "testClassPath";
14036079Swollman        System.err.println("test: " + test);
14136079Swollman
14236079Swollman        for (int i = 1; i <= 5; i++) {
14336079Swollman            File classes = createDir(test + "/" + i + "/classes");
14436079Swollman            File classpath = new File("testClassOutput/" + i + "/classes");
14536079Swollman            List<String> options;
14636079Swollman            switch (i) {
14736079Swollman                default:
14836079Swollman                    options = getOptions("-d", classes.getPath(), "-classpath", classpath.getPath());
14936079Swollman                    break;
15036079Swollman
15136079Swollman                case 3:
15236079Swollman                    setLocation(CLASS_PATH, classpath);
15336079Swollman                    options = getOptions("-d", classes.getPath());
15436079Swollman                    break;
15536079Swollman
15636079Swollman                case 4:
15736079Swollman                    options = getOptions("-d", classes.getPath(), "-cp", classpath.getPath());
15836079Swollman                    break;
15936079Swollman            }
16036079Swollman            List<JavaFileObject> sources = getSources("class D" + i + " { C" + i + " c; }");
16136079Swollman            callTask(options, sources);
16236079Swollman            checkPath(CLASS_PATH, Mode.EQUALS, classpath);
16336079Swollman            checkFile(CLASS_OUTPUT, "D" + i + ".class");
16436079Swollman        }
16536079Swollman
16614547Sdg        tested.add(CLASS_PATH);
1671541Srgrimes        System.err.println();
1681541Srgrimes    }
1691541Srgrimes
1701541Srgrimes    void testSourcePath() throws IOException {
1711541Srgrimes        String test = "testSourcePath";
17236527Speter        System.err.println("test: " + test);
17336527Speter        setLocation(CLASS_PATH); // empty
17455943Sjasone
17555943Sjasone        for (int i = 1; i <= 5; i++) {
17636527Speter            File src = createDir(test + "/" + i + "/src");
17736527Speter            writeFile(src, "C" + i + ".java", "class C" + i + "{ }");
1781541Srgrimes            File classes = createDir(test + "/" + i + "/classes");
1791541Srgrimes            File srcpath = src;
1801541Srgrimes            List<String> options;
1811541Srgrimes            switch (i) {
1821541Srgrimes                default:
1831541Srgrimes                    options = getOptions("-d", classes.getPath(), "-sourcepath", srcpath.getPath());
1841541Srgrimes                    break;
1851541Srgrimes
1861541Srgrimes                case 3:
1871541Srgrimes                    setLocation(SOURCE_PATH, srcpath);
1881541Srgrimes                    options = getOptions("-d", classes.getPath());
1891541Srgrimes                    break;
1901541Srgrimes            }
1911541Srgrimes            List<JavaFileObject> sources = getSources("class D" + i + " { C" + i + " c; }");
1921541Srgrimes            callTask(options, sources);
1931541Srgrimes            checkPath(SOURCE_PATH, Mode.EQUALS, srcpath);
1941541Srgrimes            checkFile(CLASS_OUTPUT, "D" + i + ".class");
19514547Sdg        }
1961541Srgrimes
1971541Srgrimes        tested.add(SOURCE_PATH);
1981541Srgrimes        System.err.println();
1993304Sphk    }
2001541Srgrimes
2013304Sphk    void testPlatformClassPath() throws IOException {
2021541Srgrimes        String test = "testPlatformClassPath";
2031541Srgrimes        System.err.println("test: " + test);
2041541Srgrimes
2051541Srgrimes        List<File> defaultPath = getLocation(PLATFORM_CLASS_PATH);
2061541Srgrimes        StringBuilder sb = new StringBuilder();
2071541Srgrimes        for (File f: defaultPath) {
2081541Srgrimes            if (sb.length() > 0)
2091541Srgrimes                sb.append(File.pathSeparator);
2101541Srgrimes            sb.append(f);
2111541Srgrimes        }
2121541Srgrimes        String defaultPathString = sb.toString();
2131541Srgrimes
2141541Srgrimes        setLocation(CLASS_PATH); // empty
2151541Srgrimes        setLocation(SOURCE_PATH); // empty
2161541Srgrimes
2171541Srgrimes        // Use -source 8 -target 8 to enable use of platform class path options
2181541Srgrimes        // FIXME: temporarily exclude cases referring to default bootclasspath
2191541Srgrimes        // for (int i = 1; i <= 10; i++) {
2201541Srgrimes        int[] cases = new int[] { 1, 2, 4, 5, 6, 7 };
2211541Srgrimes        for (int i : cases) {
2221541Srgrimes            File classes = createDir(test + "/" + i + "/classes");
2231541Srgrimes            File testJars = createDir(test + "/" + i + "/testJars");
2241541Srgrimes            File testClasses = createDir(test + "/" + i + "/testClasses");
2251541Srgrimes            callTask(getOptions("-d", testClasses.getPath()), getSources("class C" + i + " { }"));
2261541Srgrimes
2271541Srgrimes            List<String> options;
2281541Srgrimes            Mode mode;
2291541Srgrimes            List<File> match;
2301541Srgrimes            String reference = "C" + i + " c;";
2311541Srgrimes
2321541Srgrimes            File jar;
2331541Srgrimes
2341541Srgrimes            switch (i) {
2351541Srgrimes                case 1:
2361541Srgrimes                    options = getOptions("-d", classes.getPath(),
2371541Srgrimes                        "-source", "8", "-target", "8",
2381541Srgrimes                        "-Xbootclasspath/p:" + testClasses);
23936527Speter                    mode = Mode.STARTS_WITH;
24036527Speter                    match = Arrays.asList(testClasses);
24136527Speter                    break;
24236527Speter
2431541Srgrimes                case 2:
24436527Speter                    // the default values for -extdirs and -endorseddirs come after the bootclasspath;
24536527Speter                    // so to check -Xbootclasspath/a: we specify empty values for those options.
24636527Speter                    options = getOptions("-d", classes.getPath(),
24736527Speter                        "-source", "8", "-target", "8",
2481541Srgrimes                        "-Xbootclasspath/a:" + testClasses,
24955205Speter                        "-extdirs", "",
25031927Sbde                        "-endorseddirs", "");
25138482Swollman                    mode = Mode.ENDS_WITH;
25238482Swollman                    match = Arrays.asList(testClasses);
25338482Swollman                    break;
25438482Swollman
25538482Swollman                case 3:
25638482Swollman                    options = getOptions("-d", classes.getPath(), "-Xbootclasspath:" + defaultPathString);
25738482Swollman                    mode = Mode.EQUALS;
25838482Swollman                    match = defaultPath;
25938482Swollman                    reference = "";
26038482Swollman                    break;
26138482Swollman
26238482Swollman                case 4:
26338482Swollman                    fileManager.setLocation(PLATFORM_CLASS_PATH, null);
26438482Swollman                    jar = new File(testJars, "j" + i + ".jar");
26540931Sdg                    writeJar(jar, testClasses, "C" + i + ".class");
26640931Sdg                    options = getOptions("-d", classes.getPath(),
26740931Sdg                        "-source", "8", "-target", "8",
26840931Sdg                        "-endorseddirs", testJars.getPath());
26940931Sdg                    mode = Mode.CONTAINS;
27040931Sdg                    match = Arrays.asList(jar);
27140931Sdg                    break;
27231927Sbde
27331927Sbde                case 5:
27431927Sbde                    fileManager.setLocation(PLATFORM_CLASS_PATH, null);
27531927Sbde                    jar = new File(testJars, "j" + i + ".jar");
27631927Sbde                    writeJar(jar, testClasses, "C" + i + ".class");
27736079Swollman                    options = getOptions("-d", classes.getPath(),
2782112Swollman                        "-source", "8", "-target", "8",
27936079Swollman                        "-Djava.endorsed.dirs=" + testJars.getPath());
28036079Swollman                    mode = Mode.CONTAINS;
28114505Shsu                    match = Arrays.asList(jar);
28232995Sbde                    break;
28315492Sbde
28415492Sbde                case 6:
28515492Sbde                    fileManager.setLocation(PLATFORM_CLASS_PATH, null);
28615492Sbde                    jar = new File(testJars, "j" + i + ".jar");
28732995Sbde                    writeJar(jar, testClasses, "C" + i + ".class");
28832995Sbde                    options = getOptions("-d", classes.getPath(),
28915492Sbde                        "-source", "8", "-target", "8",
2901541Srgrimes                        "-extdirs", testJars.getPath());
2911541Srgrimes                    mode = Mode.CONTAINS;
2921541Srgrimes                    match = Arrays.asList(jar);
29345311Sdt                    break;
29451418Sgreen
29545311Sdt                case 7:
29651418Sgreen                    fileManager.setLocation(PLATFORM_CLASS_PATH, null);
29743512Snewton                    jar = new File(testJars, "j" + i + ".jar");
29836735Sdfr                    writeJar(jar, testClasses, "C" + i + ".class");
29914505Shsu                    options = getOptions("-d", classes.getPath(),
30029350Speter                        "-source", "8", "-target", "8",
30129350Speter                        "-Djava.ext.dirs=" + testJars.getPath());
30252984Speter                    mode = Mode.CONTAINS;
3033304Sphk                    match = Arrays.asList(jar);
3043304Sphk                    break;
3053304Sphk
3063304Sphk                case 8:
30728270Swollman                    setLocation(PLATFORM_CLASS_PATH, defaultPath);
30814505Shsu                    options = getOptions("-d", classes.getPath());
30914505Shsu                    mode = Mode.EQUALS;
31028270Swollman                    match = defaultPath;
31114505Shsu                    reference = "";
31214505Shsu                    break;
31314505Shsu
31414505Shsu                default:
31514505Shsu                    options = getOptions("-d", classes.getPath(),
31614505Shsu                        "-source", "8", "-target", "8",
31714505Shsu                        "-bootclasspath", defaultPathString);
31814505Shsu                    mode = Mode.EQUALS;
31919670Sbde                    match = defaultPath;
32019670Sbde                    reference = "";
32114505Shsu                    break;
32214505Shsu            }
32314505Shsu            List<JavaFileObject> sources = getSources("class D" + i + " { " + reference + " }");
32414505Shsu
32552070Sgreen            callTask(options, sources);
32652070Sgreen            checkPath(PLATFORM_CLASS_PATH, mode, match);
32752070Sgreen            checkFile(CLASS_OUTPUT, "D" + i + ".class");
32836079Swollman        }
32914505Shsu
33014505Shsu        tested.add(PLATFORM_CLASS_PATH);
33114505Shsu        System.err.println();
33228270Swollman    }
33336079Swollman
33428270Swollman    void testAnnotationProcessorPath() throws IOException {
33514505Shsu        String test = "testAnnotationProcessorPath";
33614505Shsu        System.err.println("test: " + test);
33714505Shsu
33828270Swollman        fileManager.setLocation(PLATFORM_CLASS_PATH, null);
33914505Shsu
34014505Shsu        String template =
34114505Shsu                "import java.util.*;\n"
34236079Swollman                + "import javax.annotation.processing.*;\n"
34314505Shsu                + "import javax.lang.model.*;\n"
34414505Shsu                + "import javax.lang.model.element.*;\n"
34538482Swollman                + "@SupportedAnnotationTypes(\"*\")\n"
34614505Shsu                + "public class A%d extends AbstractProcessor {\n"
34714505Shsu                + "    public boolean process(Set<? extends TypeElement> annos, RoundEnvironment rEnv) {\n"
34814505Shsu                + "        return true;\n"
34914505Shsu                + "    }\n"
35014505Shsu                + "    public SourceVersion getSupportedSourceVersion() {\n"
35125201Swollman                + "        return SourceVersion.latest();\n"
35214505Shsu                + "    }\n"
35318787Spst                + "}";
35418787Spst
35527531Sfenner        for (int i = 1; i <= 5; i++) {
35651381Sgreen            File classes = createDir(test + "/" + i + "/classes");
35751381Sgreen            File annodir = createDir(test + "/" + i + "/processors");
35838482Swollman            callTask(getOptions("-d", annodir.getPath()), getSources(String.format(template, i)));
35938482Swollman            File annopath = annodir;
36038482Swollman            List<String> options;
36153541Sshin            switch (i) {
36253541Sshin                default:
36353541Sshin                    options = getOptions("-d", classes.getPath(),
36453541Sshin                            "-processorpath", annopath.getPath(),
36553541Sshin                            "-processor", "A" + i);
36653541Sshin                    break;
36729350Speter
36829350Speter                case 3:
36928270Swollman                    setLocation(ANNOTATION_PROCESSOR_PATH, annopath);
37028270Swollman                    options = getOptions("-d", classes.getPath(),
37128270Swollman                            "-processor", "A" + i);
37214505Shsu                    break;
37314505Shsu            }
37428270Swollman            List<JavaFileObject> sources = getSources("class D" + i + " { }");
37528270Swollman            callTask(options, sources);
37628270Swollman            checkPath(ANNOTATION_PROCESSOR_PATH, Mode.EQUALS, annopath);
37738482Swollman            checkFile(CLASS_OUTPUT, "D" + i + ".class");
37814505Shsu        }
37936079Swollman
38014505Shsu        tested.add(ANNOTATION_PROCESSOR_PATH);
38131927Sbde        System.err.println();
38255205Speter    }
3832165Spaul
38414505Shsu    void testSourceOutput() throws IOException {
385        String test = "testAnnotationProcessorPath";
386        System.err.println("test: " + test);
387
388        String source =
389                "import java.io.*;\n"
390                + "import java.util.*;\n"
391                + "import javax.annotation.processing.*;\n"
392                + "import javax.lang.model.*;\n"
393                + "import javax.lang.model.element.*;\n"
394                + "import javax.tools.*;\n"
395                + "@SupportedOptions(\"name\")\n"
396                + "@SupportedAnnotationTypes(\"*\")\n"
397                + "public class A extends AbstractProcessor {\n"
398                + "    int round = 0;\n"
399                + "    public boolean process(Set<? extends TypeElement> annos, RoundEnvironment rEnv) {\n"
400                + "        if (round++ == 0) try {\n"
401                + "            String name = processingEnv.getOptions().get(\"name\");\n"
402                + "            JavaFileObject fo = processingEnv.getFiler().createSourceFile(name);\n"
403                + "            try (Writer out = fo.openWriter()) {\n"
404                + "                out.write(\"class \" + name + \" { }\");\n"
405                + "            }\n"
406                + "        } catch (IOException e) { throw new Error(e); }\n"
407                + "        return true;\n"
408                + "    }\n"
409                + "    public SourceVersion getSupportedSourceVersion() {\n"
410                + "        return SourceVersion.latest();\n"
411                + "    }\n"
412                + "}";
413
414        File annodir = createDir(test + "/processors");
415        callTask(getOptions("-d", annodir.getPath()), getSources(source));
416        setLocation(ANNOTATION_PROCESSOR_PATH, annodir);
417
418        for (int i = 1; i <= 5; i++) {
419            File classes = createDir(test + "/" + i + "/classes");
420            File genSrc = createDir(test + "/" + "/genSrc");
421            List<String> options;
422            switch (i) {
423                default:
424                    options = getOptions("-d", classes.getPath(),
425                            "-processor", "A", "-Aname=G" + i,
426                            "-s", genSrc.getPath());
427                    break;
428
429                case 3:
430                    setLocation(SOURCE_OUTPUT, genSrc);
431                    options = getOptions("-d", classes.getPath(),
432                            "-processor", "A", "-Aname=G" + i);
433                    break;
434            }
435            List<JavaFileObject> sources = getSources("class D" + i + " { }");
436            callTask(options, sources);
437            checkPath(SOURCE_OUTPUT, Mode.EQUALS, genSrc);
438            checkFile(CLASS_OUTPUT, "D" + i + ".class");
439            checkFile(CLASS_OUTPUT, "G" + i + ".class");
440        }
441        tested.add(SOURCE_OUTPUT);
442        System.err.println();
443    }
444
445    void testNativeHeaderOutput() throws IOException {
446        String test = "testNativeHeaderOutput";
447        System.err.println("test: " + test);
448
449        for (int i = 1; i <= 5; i++) {
450            File classes = createDir(test + "/" + i + "/classes");
451            File headers = createDir(test + "/" + i + "/hdrs");
452            List<String> options;
453            switch (i) {
454                default:
455                    options = getOptions("-d", classes.getPath(), "-h", headers.getPath());
456                    break;
457
458                case 3:
459                    setLocation(NATIVE_HEADER_OUTPUT, headers);
460                    options = getOptions("-d", classes.getPath());
461                    break;
462            }
463            List<JavaFileObject> sources = getSources("class C" + i + " { native void m(); }");
464            callTask(options, sources);
465            checkPath(NATIVE_HEADER_OUTPUT, Mode.EQUALS, headers);
466            checkFile(NATIVE_HEADER_OUTPUT, "C" + i + ".h");
467        }
468
469        tested.add(StandardLocation.NATIVE_HEADER_OUTPUT);
470        System.err.println();
471    }
472
473    List<String> getOptions(String... args) {
474        return Arrays.asList(args);
475    }
476
477    List<JavaFileObject> getSources(String... sources) {
478        List<JavaFileObject> list = new ArrayList<>();
479        for (String s: sources)
480            list.add(getSource(s));
481        return list;
482    }
483
484    JavaFileObject getSource(final String source) {
485        return new SimpleJavaFileObject(getURIFromSource(source), JavaFileObject.Kind.SOURCE) {
486            @Override
487            public CharSequence getCharContent(boolean ignoreEncodingErrors) {
488                return source;
489            }
490        };
491    }
492
493    void callTask(List<String> options, List<JavaFileObject> files) {
494        out.print("compile: ");
495        if (options != null) {
496            for (String o: options) {
497                if (o.length() > 64) {
498                    o = o.substring(0, 32) + "..." + o.substring(o.length() - 32);
499                }
500                out.print(" " + o);
501            }
502        }
503        for (JavaFileObject f: files)
504            out.print(" " + f.getName());
505        out.println();
506        CompilationTask t = compiler.getTask(out, fileManager, null, options, null, files);
507        boolean ok = t.call();
508        if (!ok)
509            error("compilation failed");
510    }
511
512    enum Mode { EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH };
513
514    void checkFile(StandardLocation l, String path) {
515        if (!l.isOutputLocation()) {
516            error("Not an output location: " + l);
517            return;
518        }
519
520        List<File> files = getLocation(l);
521        if (files == null) {
522            error("location is unset: " + l);
523            return;
524        }
525
526        if (files.size() != 1)
527            error("unexpected number of entries on " + l + ": " + files.size());
528
529        File f = new File(files.get(0), path);
530        if (!f.exists())
531            error("file not found: " + f);
532    }
533
534    void checkPath(StandardLocation l, Mode m, File expect) {
535        checkPath(l, m, Arrays.asList(expect));
536    }
537
538    void checkPath(StandardLocation l, Mode m, List<File> expect) {
539        List<File> files = getLocation(l);
540        if (files == null) {
541            error("location is unset: " + l);
542            return;
543        }
544
545        switch (m) {
546            case EQUALS:
547                if (!Objects.equals(files, expect)) {
548                    error("location does not match the expected files: " + l);
549                    out.println("found:  " + files);
550                    out.println("expect: " + expect);
551                }
552                break;
553
554            case CONTAINS:
555                int containsIndex = Collections.indexOfSubList(files, expect);
556                if (containsIndex == -1) {
557                    error("location does not contain the expected files: " + l);
558                    out.println("found:  " + files);
559                    out.println("expect: " + expect);
560                }
561            break;
562
563            case STARTS_WITH:
564                int startsIndex = Collections.indexOfSubList(files, expect);
565                if (startsIndex != 0) {
566                    error("location does not start with the expected files: " + l);
567                    out.println("found:  " + files);
568                    out.println("expect: " + expect);
569                }
570            break;
571
572            case ENDS_WITH:
573                int endsIndex = Collections.lastIndexOfSubList(files, expect);
574                if (endsIndex != files.size() - expect.size()) {
575                    error("location does not end with the expected files: " + l);
576                    out.println("found:  " + files);
577                    out.println("expect: " + expect);
578                }
579            break;
580
581        }
582    }
583
584    List<File> getLocation(StandardLocation l) {
585        Iterable<? extends File> iter = fileManager.getLocation(l);
586        if (iter == null)
587            return null;
588        List<File> files = new ArrayList<>();
589        for (File f: iter)
590            files.add(f);
591        return files;
592    }
593
594    void setLocation(StandardLocation l, File... files) throws IOException {
595        fileManager.setLocation(l, Arrays.asList(files));
596    }
597
598    void setLocation(StandardLocation l, List<File> files) throws IOException {
599        fileManager.setLocation(l, files);
600    }
601
602    void writeFile(File dir, String path, String body) throws IOException {
603        try (FileWriter w = new FileWriter(new File(dir, path))) {
604            w.write(body);
605        }
606    }
607
608    void writeJar(File jar, File dir, String... entries) throws IOException {
609        try (JarOutputStream j = new JarOutputStream(Files.newOutputStream(jar.toPath()))) {
610            for (String entry: entries) {
611                j.putNextEntry(new JarEntry(entry));
612                j.write(Files.readAllBytes(dir.toPath().resolve(entry)));
613            }
614        }
615    }
616
617    private static final Pattern packagePattern
618            = Pattern.compile("package\\s+(((?:\\w+\\.)*)(?:\\w+))");
619    private static final Pattern classPattern
620            = Pattern.compile("(?:public\\s+)?(?:class|enum|interface)\\s+(\\w+)");
621
622
623    private static URI getURIFromSource(String source) {
624        String packageName = null;
625
626        Matcher matcher = packagePattern.matcher(source);
627        if (matcher.find()) {
628            packageName = matcher.group(1).replace(".", "/");
629        }
630
631        matcher = classPattern.matcher(source);
632        if (matcher.find()) {
633            String className = matcher.group(1);
634            String path = ((packageName == null) ? "" : packageName + "/") + className + ".java";
635            return URI.create("myfo:///" + path);
636        } else {
637            throw new Error("Could not extract the java class "
638                    + "name from the provided source");
639        }
640    }
641
642    File createDir(String path) {
643        File dir = new File(path);
644        dir.mkdirs();
645        return dir;
646    }
647
648    JavaCompiler compiler;
649    StandardJavaFileManager fileManager;
650
651    /**
652     * Map for recording which standard locations have been tested.
653     */
654    EnumSet<StandardLocation> tested = EnumSet.noneOf(StandardLocation.class);
655
656    /**
657     * Logging stream. Used directly with test and for getTask calls.
658     */
659    final PrintWriter out = new PrintWriter(System.err, true);
660
661    /**
662     * Count of errors so far.
663     */
664    int errors;
665
666    void error(String message) {
667        errors++;
668        out.println("Error: " + message);
669    }
670}
671