ModuleTestBase.java revision 3573:c4a18ee691c4
1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24import com.sun.tools.classfile.ClassFile;
25import com.sun.tools.classfile.ConstantPool;
26import com.sun.tools.classfile.ConstantPoolException;
27import com.sun.tools.classfile.Module_attribute;
28import com.sun.tools.javac.util.Pair;
29
30import java.io.IOException;
31import java.lang.annotation.Retention;
32import java.lang.annotation.RetentionPolicy;
33import java.lang.reflect.Method;
34import java.nio.file.Files;
35import java.nio.file.Path;
36import java.nio.file.Paths;
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.Collections;
40import java.util.HashMap;
41import java.util.List;
42import java.util.Map;
43import java.util.regex.Pattern;
44import java.util.stream.Collectors;
45
46import toolbox.JavacTask;
47import toolbox.Task;
48import toolbox.ToolBox;
49
50public class ModuleTestBase {
51    protected final ToolBox tb = new ToolBox();
52    private final TestResult tr = new TestResult();
53
54
55    protected void run() throws Exception {
56        boolean noTests = true;
57        for (Method method : this.getClass().getMethods()) {
58            if (method.isAnnotationPresent(Test.class)) {
59                noTests = false;
60                try {
61                    tr.addTestCase(method.getName());
62                    method.invoke(this, Paths.get(method.getName()));
63                } catch (Throwable th) {
64                    tr.addFailure(th);
65                }
66            }
67        }
68        if (noTests) throw new AssertionError("Tests are not found.");
69        tr.checkStatus();
70    }
71
72    protected void testModuleAttribute(Path modulePath, ModuleDescriptor moduleDescriptor) throws Exception {
73        ClassFile classFile = ClassFile.read(modulePath.resolve("module-info.class"));
74        Module_attribute moduleAttribute = (Module_attribute) classFile.getAttribute("Module");
75        ConstantPool constantPool = classFile.constant_pool;
76
77        testRequires(moduleDescriptor, moduleAttribute, constantPool);
78        testExports(moduleDescriptor, moduleAttribute, constantPool);
79        testProvides(moduleDescriptor, moduleAttribute, constantPool);
80        testUses(moduleDescriptor, moduleAttribute, constantPool);
81    }
82
83    private void testRequires(ModuleDescriptor moduleDescriptor, Module_attribute module, ConstantPool constantPool) throws ConstantPoolException {
84        tr.checkEquals(module.requires_count, moduleDescriptor.requires.size(), "Wrong amount of requires.");
85
86        List<Pair<String, Integer>> actualRequires = new ArrayList<>();
87        for (Module_attribute.RequiresEntry require : module.requires) {
88            actualRequires.add(Pair.of(
89                    require.getRequires(constantPool), require.requires_flags));
90        }
91        tr.checkContains(actualRequires, moduleDescriptor.requires, "Lists of requires don't match");
92    }
93
94    private void testExports(ModuleDescriptor moduleDescriptor, Module_attribute module, ConstantPool constantPool) throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry {
95        tr.checkEquals(module.exports_count, moduleDescriptor.exports.size(), "Wrong amount of exports.");
96        for (Module_attribute.ExportsEntry export : module.exports) {
97            String pkg = constantPool.getUTF8Value(export.exports_index);
98            if (tr.checkTrue(moduleDescriptor.exports.containsKey(pkg), "Unexpected export " + pkg)) {
99                List<String> expectedTo = moduleDescriptor.exports.get(pkg);
100                tr.checkEquals(export.exports_to_count, expectedTo.size(), "Wrong amount of exports to");
101                List<String> actualTo = new ArrayList<>();
102                for (int toIdx : export.exports_to_index) {
103                    actualTo.add(constantPool.getUTF8Value(toIdx));
104                }
105                tr.checkContains(actualTo, expectedTo, "Lists of \"exports to\" don't match.");
106            }
107        }
108    }
109
110    private void testUses(ModuleDescriptor moduleDescriptor, Module_attribute module, ConstantPool constantPool) throws ConstantPoolException {
111        tr.checkEquals(module.uses_count, moduleDescriptor.uses.size(), "Wrong amount of uses.");
112        List<String> actualUses = new ArrayList<>();
113        for (int usesIdx : module.uses_index) {
114            String uses = constantPool.getClassInfo(usesIdx).getBaseName().replace('/', '.');
115            actualUses.add(uses);
116        }
117        tr.checkContains(actualUses, moduleDescriptor.uses, "Lists of uses don't match");
118    }
119
120    private void testProvides(ModuleDescriptor moduleDescriptor, Module_attribute module, ConstantPool constantPool) throws ConstantPoolException {
121        tr.checkEquals(module.provides_count, moduleDescriptor.provides.size(), "Wrong amount of provides.");
122        List<Pair<String, String>> actualProvides = new ArrayList<>();
123        for (Module_attribute.ProvidesEntry provide : module.provides) {
124            String provides = constantPool.getClassInfo(provide.provides_index).getBaseName().replace('/', '.');
125            String with = constantPool.getClassInfo(provide.with_index).getBaseName().replace('/', '.');
126            actualProvides.add(Pair.of(provides, with));
127        }
128        tr.checkContains(actualProvides, moduleDescriptor.provides, "Lists of provides don't match");
129    }
130
131    protected void compile(Path base, String... options) throws IOException {
132        new JavacTask(tb)
133                .options(options)
134                .files(findJavaFiles(base))
135                .run(Task.Expect.SUCCESS)
136                .writeAll();
137    }
138
139    private static Path[] findJavaFiles(Path src) throws IOException {
140        return Files.find(src, Integer.MAX_VALUE, (path, attr) -> path.toString().endsWith(".java"))
141                .toArray(Path[]::new);
142    }
143
144    @Retention(RetentionPolicy.RUNTIME)
145    @interface Test {
146    }
147
148    class ModuleDescriptor {
149
150        private final String name;
151        //pair is name of module and flag(public,mandated,synthetic)
152        private final List<Pair<String, Integer>> requires = new ArrayList<>();
153
154        {
155            requires.add(new Pair<>("java.base", Module_attribute.ACC_MANDATED));
156        }
157
158        private final Map<String, List<String>> exports = new HashMap<>();
159
160        //List of service and implementation
161        private final List<Pair<String, String>> provides = new ArrayList<>();
162        private final List<String> uses = new ArrayList<>();
163
164        private static final String LINE_END = ";\n";
165
166        StringBuilder content = new StringBuilder("module ");
167
168        public ModuleDescriptor(String moduleName) {
169            this.name = moduleName;
170            content.append(name).append('{').append('\n');
171        }
172
173        public ModuleDescriptor requires(String... requires) {
174            for (String require : requires) {
175                this.requires.add(Pair.of(require, 0));
176                content.append("    requires ").append(require).append(LINE_END);
177            }
178            return this;
179        }
180
181        public ModuleDescriptor requiresPublic(String... requiresPublic) {
182            for (String require : requiresPublic) {
183                this.requires.add(new Pair<>(require, Module_attribute.ACC_PUBLIC));
184                content.append("    requires public ").append(require).append(LINE_END);
185            }
186            return this;
187        }
188
189        public ModuleDescriptor exports(String... exports) {
190            for (String export : exports) {
191                this.exports.putIfAbsent(export, new ArrayList<>());
192                content.append("    exports ").append(export).append(LINE_END);
193            }
194            return this;
195        }
196
197        public ModuleDescriptor exportsTo(String exports, String to) {
198            List<String> tos = Pattern.compile(",")
199                    .splitAsStream(to)
200                    .map(String::trim)
201                    .collect(Collectors.toList());
202            this.exports.computeIfAbsent(exports, k -> new ArrayList<>()).addAll(tos);
203            content.append("    exports ").append(exports).append(" to ").append(to).append(LINE_END);
204            return this;
205        }
206
207        public ModuleDescriptor provides(String provides, String with) {
208            this.provides.add(Pair.of(provides, with));
209            content.append("    provides ").append(provides).append(" with ").append(with).append(LINE_END);
210            return this;
211        }
212
213        public ModuleDescriptor uses(String... uses) {
214            Collections.addAll(this.uses, uses);
215            for (String use : uses) {
216                content.append("    uses ").append(use).append(LINE_END);
217            }
218            return this;
219        }
220
221        public ModuleDescriptor write(Path path) throws IOException {
222            String src = content.append('}').toString();
223
224            tb.createDirectories(path);
225            tb.writeJavaFiles(path, src);
226            return this;
227        }
228    }
229}
230