From 90fec4e2190aa4bc8f486d6e9dd8da50cf4253e5 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 8 Oct 2015 18:27:48 +0300 Subject: [PATCH] Refactoring test compiler --- .../java/org/teavm/tooling/TeaVMTestCase.java | 12 +- .../org/teavm/tooling/TeaVMTestClass.java | 39 ++++ .../org/teavm/tooling/TeaVMTestMethod.java | 49 +++++ .../java/org/teavm/tooling/TeaVMTestTool.java | 193 ++++++++++-------- 4 files changed, 211 insertions(+), 82 deletions(-) create mode 100644 core/src/main/java/org/teavm/tooling/TeaVMTestClass.java create mode 100644 core/src/main/java/org/teavm/tooling/TeaVMTestMethod.java diff --git a/core/src/main/java/org/teavm/tooling/TeaVMTestCase.java b/core/src/main/java/org/teavm/tooling/TeaVMTestCase.java index d33833c98..98ec0ffea 100644 --- a/core/src/main/java/org/teavm/tooling/TeaVMTestCase.java +++ b/core/src/main/java/org/teavm/tooling/TeaVMTestCase.java @@ -16,6 +16,9 @@ package org.teavm.tooling; import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.teavm.model.MethodReference; /** @@ -27,12 +30,15 @@ public class TeaVMTestCase { private File runtimeScript; private File testScript; private File debugTable; + private List expectedExceptions = new ArrayList<>(); - public TeaVMTestCase(MethodReference testMethod, File runtimeScript, File testScript, File debugTable) { + public TeaVMTestCase(MethodReference testMethod, File runtimeScript, File testScript, File debugTable, + List expectedExceptions) { this.testMethod = testMethod; this.runtimeScript = runtimeScript; this.testScript = testScript; this.debugTable = debugTable; + this.expectedExceptions = Collections.unmodifiableList(new ArrayList<>(expectedExceptions)); } public MethodReference getTestMethod() { @@ -50,4 +56,8 @@ public class TeaVMTestCase { public File getDebugTable() { return debugTable; } + + public List getExpectedExceptions() { + return expectedExceptions; + } } diff --git a/core/src/main/java/org/teavm/tooling/TeaVMTestClass.java b/core/src/main/java/org/teavm/tooling/TeaVMTestClass.java new file mode 100644 index 000000000..b69d59126 --- /dev/null +++ b/core/src/main/java/org/teavm/tooling/TeaVMTestClass.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.tooling; + +import java.util.List; + +/** + * + * @author Alexey Andreev + */ +class TeaVMTestClass { + private String className; + private List methods; + + public TeaVMTestClass(String className) { + this.className = className; + } + + public String getClassName() { + return className; + } + + public List getMethods() { + return methods; + } +} diff --git a/core/src/main/java/org/teavm/tooling/TeaVMTestMethod.java b/core/src/main/java/org/teavm/tooling/TeaVMTestMethod.java new file mode 100644 index 000000000..a4adb7f39 --- /dev/null +++ b/core/src/main/java/org/teavm/tooling/TeaVMTestMethod.java @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.tooling; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +class TeaVMTestMethod { + private MethodReference method; + private String fileName; + private List expectedExceptions = new ArrayList<>(); + + public TeaVMTestMethod(MethodReference method, String fileName, List expectedExceptions) { + this.method = method; + this.fileName = fileName; + this.expectedExceptions = Collections.unmodifiableList(new ArrayList<>(expectedExceptions)); + } + + public MethodReference getMethod() { + return method; + } + + public String getFileName() { + return fileName; + } + + public List getExpectedExceptions() { + return expectedExceptions; + } +} diff --git a/core/src/main/java/org/teavm/tooling/TeaVMTestTool.java b/core/src/main/java/org/teavm/tooling/TeaVMTestTool.java index 01768014b..2a73c839d 100644 --- a/core/src/main/java/org/teavm/tooling/TeaVMTestTool.java +++ b/core/src/main/java/org/teavm/tooling/TeaVMTestTool.java @@ -15,8 +15,16 @@ */ package org.teavm.tooling; -import java.io.*; -import java.util.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; import org.apache.commons.io.IOUtils; import org.teavm.common.FiniteExecutor; import org.teavm.common.SimpleFiniteExecutor; @@ -26,7 +34,16 @@ import org.teavm.debugging.information.DebugInformationBuilder; import org.teavm.javascript.EmptyRegularMethodNodeCache; import org.teavm.javascript.InMemoryRegularMethodNodeCache; import org.teavm.javascript.MethodNodeCache; -import org.teavm.model.*; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.CopyClassHolderSource; +import org.teavm.model.InMemoryProgramCache; +import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReference; +import org.teavm.model.PreOptimizingClassHolderSource; +import org.teavm.model.ProgramCache; +import org.teavm.model.ValueType; import org.teavm.parsing.ClasspathClassHolderSource; import org.teavm.testing.JUnitTestAdapter; import org.teavm.testing.TestAdapter; @@ -39,9 +56,6 @@ import org.teavm.vm.TeaVMBuilder; * @author Alexey Andreev */ public class TeaVMTestTool { - private Map> groupedMethods = new HashMap<>(); - private Map fileNames = new HashMap<>(); - private List testMethods = new ArrayList<>(); private File outputDir = new File("."); private boolean minifying = true; private int numThreads = 1; @@ -62,6 +76,8 @@ public class TeaVMTestTool { private ProgramCache programCache; private SourceFilesCopier sourceFilesCopier; private List listeners = new ArrayList<>(); + private List testPlan = new ArrayList<>(); + private int fileIndexGenerator; public File getOutputDir() { return outputDir; @@ -199,58 +215,8 @@ public class TeaVMTestTool { astCache = new InMemoryRegularMethodNodeCache(); programCache = new InMemoryProgramCache(); } - File allTestsFile = new File(outputDir, "tests/all.js"); - try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) { - allTestsWriter.write("prepare = function() {\n"); - allTestsWriter.write(" return new JUnitServer(document.body).readTests(["); - boolean first = true; - for (String testClass : testClasses) { - Collection methods = groupedMethods.get(testClass); - if (methods == null) { - continue; - } - if (!first) { - allTestsWriter.append(","); - } - first = false; - allTestsWriter.append("\n { name : \"").append(testClass).append("\", methods : ["); - boolean firstMethod = true; - for (MethodReference methodRef : methods) { - String scriptName = "tests/" + fileNames.size() + ".js"; - fileNames.put(methodRef, scriptName); - if (!firstMethod) { - allTestsWriter.append(","); - } - firstMethod = false; - allTestsWriter.append("\n { name : \"" + methodRef.getName() + "\", script : \"" - + scriptName + "\", expected : ["); - MethodHolder methodHolder = classSource.get(testClass).getMethod( - methodRef.getDescriptor()); - boolean firstException = true; - for (String exception : adapter.getExpectedExceptions(methodHolder)) { - if (!firstException) { - allTestsWriter.append(", "); - } - firstException = false; - allTestsWriter.append("\"" + exception + "\""); - } - allTestsWriter.append("], additionalScripts : ["); - for (int i = 0; i < additionalScriptLocalPaths.size(); ++i) { - if (i > 0) { - allTestsWriter.append(", "); - } - escapeString(additionalScriptLocalPaths.get(i), allTestsWriter); - } - allTestsWriter.append("] }"); - } - allTestsWriter.append("] }"); - } - allTestsWriter.write("], function() {}); }"); - } - int methodsGenerated = 0; - log.info("Generating test files"); - sourceFilesCopier = new SourceFilesCopier(sourceFileProviders); - sourceFilesCopier.setLog(log); + writeMetadata(); + FiniteExecutor executor = new SimpleFiniteExecutor(); if (numThreads != 1) { int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors(); @@ -258,20 +224,8 @@ public class TeaVMTestTool { finalizer = () -> threadedExecutor.stop(); executor = threadedExecutor; } - for (final MethodReference method : testMethods) { - final ClassHolderSource builderClassSource = classSource; - executor.execute(() -> { - log.debug("Building test for " + method); - try { - decompileClassesForTest(classLoader, new CopyClassHolderSource(builderClassSource), method, - fileNames.get(method)); - } catch (IOException e) { - log.error("Error generating JavaScript", e); - } - }); - ++methodsGenerated; - } - executor.complete(); + int methodsGenerated = writeMethods(executor, classSource); + if (sourceFilesCopied) { sourceFilesCopier.copy(new File(new File(outputDir, "tests"), "src")); } @@ -285,6 +239,75 @@ public class TeaVMTestTool { } } + private void writeMetadata() throws IOException { + File allTestsFile = new File(outputDir, "tests/all.js"); + try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) { + allTestsWriter.write("prepare = function() {\n"); + allTestsWriter.write(" return new JUnitServer(document.body).readTests(["); + boolean first = true; + for (TeaVMTestClass testClass : testPlan) { + if (!first) { + allTestsWriter.append(","); + } + first = false; + allTestsWriter.append("\n { name : \"").append(testClass.getClassName()) + .append("\", methods : ["); + boolean firstMethod = true; + for (TeaVMTestMethod testMethod : testClass.getMethods()) { + String scriptName = testMethod.getFileName(); + if (!firstMethod) { + allTestsWriter.append(","); + } + firstMethod = false; + allTestsWriter.append("\n { name : \"" + testMethod.getMethod().getName() + + "\", script : \"" + scriptName + "\", expected : ["); + boolean firstException = true; + for (String exception : testMethod.getExpectedExceptions()) { + if (!firstException) { + allTestsWriter.append(", "); + } + firstException = false; + allTestsWriter.append("\"" + exception + "\""); + } + allTestsWriter.append("], additionalScripts : ["); + for (int i = 0; i < additionalScriptLocalPaths.size(); ++i) { + if (i > 0) { + allTestsWriter.append(", "); + } + escapeString(additionalScriptLocalPaths.get(i), allTestsWriter); + } + allTestsWriter.append("] }"); + } + allTestsWriter.append("] }"); + } + allTestsWriter.write("], function() {}); }"); + } + } + + private int writeMethods(FiniteExecutor executor, ClassHolderSource classSource) { + int methodsGenerated = 0; + log.info("Generating test files"); + sourceFilesCopier = new SourceFilesCopier(sourceFileProviders); + sourceFilesCopier.setLog(log); + for (TeaVMTestClass testClass : testPlan) { + for (TeaVMTestMethod testMethod : testClass.getMethods()) { + final ClassHolderSource builderClassSource = classSource; + executor.execute(() -> { + log.debug("Building test for " + testMethod.getMethod()); + try { + decompileClassesForTest(classLoader, new CopyClassHolderSource(builderClassSource), + testMethod); + } catch (IOException e) { + log.error("Error generating JavaScript", e); + } + }); + ++methodsGenerated; + } + } + executor.complete(); + return methodsGenerated; + } + private void resourceToFile(String resource, String fileName) throws IOException { try (InputStream input = TeaVMTestTool.class.getClassLoader().getResourceAsStream(resource)) { try (OutputStream output = new FileOutputStream(new File(outputDir, fileName))) { @@ -294,18 +317,24 @@ public class TeaVMTestTool { } private void findTests(ClassHolder cls) { + TeaVMTestClass testClass = new TeaVMTestClass(cls.getName()); for (MethodHolder method : cls.getMethods()) { if (adapter.acceptMethod(method)) { MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor()); - testMethods.add(ref); - List group = groupedMethods.get(cls.getName()); - if (group == null) { - group = new ArrayList<>(); - groupedMethods.put(cls.getName(), group); + String fileName = "tests/" + fileIndexGenerator++ + ".js"; + + List exceptions = new ArrayList<>(); + for (String exception : adapter.getExpectedExceptions(method)) { + exceptions.add(exception); } - group.add(ref); + + TeaVMTestMethod testMethod = new TeaVMTestMethod(ref, fileName, exceptions); + testClass.getMethods().add(testMethod); } } + if (!testClass.getMethods().isEmpty()) { + testPlan.add(testClass); + } } private void includeAdditionalScripts(ClassLoader classLoader) throws TeaVMToolException { @@ -333,7 +362,8 @@ public class TeaVMTestTool { } private void decompileClassesForTest(ClassLoader classLoader, ClassHolderSource classSource, - MethodReference methodRef, String targetName) throws IOException { + TeaVMTestMethod testMethod) throws IOException { + String targetName = testMethod.getFileName(); TeaVM vm = new TeaVMBuilder() .setClassLoader(classLoader) .setClassSource(classSource) @@ -349,9 +379,10 @@ public class TeaVMTestTool { vm.add(transformer); } - File file = new File(outputDir, targetName); + File file = new File(outputDir, testMethod.getFileName()); DebugInformationBuilder debugInfoBuilder = sourceMapsGenerated || debugInformationGenerated ? new DebugInformationBuilder() : null; + MethodReference methodRef = testMethod.getMethod(); try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { MethodReference cons = new MethodReference(methodRef.getClassName(), "", ValueType.VOID); MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException", @@ -401,7 +432,7 @@ public class TeaVMTestTool { } TeaVMTestCase testCase = new TeaVMTestCase(methodRef, new File(outputDir, "res/runtime.js"), - file, debugTableFile); + file, debugTableFile, testMethod.getExpectedExceptions()); for (TeaVMTestToolListener listener : listeners) { listener.testGenerated(testCase); }