mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Refactor test runner
1. Deprecate WhileClassCompilation annotation, introduce EachTestCompiledSeparately instead 2. Add annotations to enabled/disable tests for particular backends
This commit is contained in:
parent
7806a699c5
commit
db4418ae1d
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import static org.teavm.junit.PropertyNames.SOURCE_DIRS;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.teavm.backend.wasm.WasmRuntimeType;
|
||||||
|
import org.teavm.backend.wasm.WasmTarget;
|
||||||
|
import org.teavm.backend.wasm.generate.DirectorySourceFileResolver;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
import org.teavm.vm.TeaVM;
|
||||||
|
|
||||||
|
abstract class BaseWebAssemblyPlatformSupport extends TestPlatformSupport<WasmTarget> {
|
||||||
|
public BaseWebAssemblyPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache) {
|
||||||
|
super(classSource, referenceCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getExtension() {
|
||||||
|
return ".wasm";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract WasmRuntimeType getRuntimeType();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
CompileResult compile(Consumer<TeaVM> additionalProcessing, String baseName,
|
||||||
|
TeaVMTestConfiguration<WasmTarget> configuration, File path) {
|
||||||
|
Supplier<WasmTarget> targetSupplier = () -> {
|
||||||
|
WasmTarget target = new WasmTarget();
|
||||||
|
target.setRuntimeType(getRuntimeType());
|
||||||
|
var sourceDirs = System.getProperty(SOURCE_DIRS);
|
||||||
|
if (sourceDirs != null) {
|
||||||
|
var dirs = new ArrayList<File>();
|
||||||
|
for (var tokenizer = new StringTokenizer(sourceDirs, Character.toString(File.pathSeparatorChar));
|
||||||
|
tokenizer.hasMoreTokens();) {
|
||||||
|
var dir = new File(tokenizer.nextToken());
|
||||||
|
if (dir.isDirectory()) {
|
||||||
|
dirs.add(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dirs.isEmpty()) {
|
||||||
|
target.setSourceFileResolver(new DirectorySourceFileResolver(dirs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
return compile(configuration, targetSupplier, TestNativeEntryPoint.class.getName(), path,
|
||||||
|
".wasm", null, false, additionalProcessing, baseName);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,11 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.junit;
|
package org.teavm.junit;
|
||||||
|
|
||||||
|
import static org.teavm.junit.PropertyNames.JS_DECODE_STACK;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -28,6 +30,8 @@ import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -38,6 +42,7 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import javax.servlet.ServletConfig;
|
import javax.servlet.ServletConfig;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
@ -55,7 +60,7 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||||
|
|
||||||
class BrowserRunStrategy implements TestRunStrategy {
|
class BrowserRunStrategy implements TestRunStrategy {
|
||||||
private boolean decodeStack = Boolean.parseBoolean(System.getProperty(TeaVMTestRunner.JS_DECODE_STACK, "true"));
|
private boolean decodeStack = Boolean.parseBoolean(System.getProperty(JS_DECODE_STACK, "true"));
|
||||||
private final File baseDir;
|
private final File baseDir;
|
||||||
private final String type;
|
private final String type;
|
||||||
private final Function<String, Process> browserRunner;
|
private final Function<String, Process> browserRunner;
|
||||||
|
@ -409,4 +414,122 @@ class BrowserRunStrategy implements TestRunStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Process customBrowser(String url) {
|
||||||
|
System.out.println("Open link to run tests: " + url + "?logging=true");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Process chromeBrowser(String url) {
|
||||||
|
return browserTemplate("chrome", url, (profile, params) -> {
|
||||||
|
addChromeCommand(params);
|
||||||
|
params.addAll(Arrays.asList(
|
||||||
|
"--headless",
|
||||||
|
"--disable-gpu",
|
||||||
|
"--remote-debugging-port=9222",
|
||||||
|
"--no-first-run",
|
||||||
|
"--user-data-dir=" + profile
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Process firefoxBrowser(String url) {
|
||||||
|
return browserTemplate("firefox", url, (profile, params) -> {
|
||||||
|
addFirefoxCommand(params);
|
||||||
|
params.addAll(Arrays.asList(
|
||||||
|
"--headless",
|
||||||
|
"--profile",
|
||||||
|
profile
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addChromeCommand(List<String> params) {
|
||||||
|
if (isMacos()) {
|
||||||
|
params.add("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
|
||||||
|
} else if (isWindows()) {
|
||||||
|
params.add("cmd.exe");
|
||||||
|
params.add("start");
|
||||||
|
params.add("/C");
|
||||||
|
params.add("chrome");
|
||||||
|
} else {
|
||||||
|
params.add("google-chrome-stable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addFirefoxCommand(List<String> params) {
|
||||||
|
if (isMacos()) {
|
||||||
|
params.add("/Applications/Firefox.app/Contents/MacOS/firefox");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isWindows()) {
|
||||||
|
params.add("cmd.exe");
|
||||||
|
params.add("/C");
|
||||||
|
params.add("start");
|
||||||
|
}
|
||||||
|
params.add("firefox");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isWindows() {
|
||||||
|
return System.getProperty("os.name").toLowerCase().startsWith("windows");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMacos() {
|
||||||
|
return System.getProperty("os.name").toLowerCase().startsWith("mac");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Process browserTemplate(String name, String url, BiConsumer<String, List<String>> paramsBuilder) {
|
||||||
|
File temp;
|
||||||
|
try {
|
||||||
|
temp = File.createTempFile("teavm", "teavm");
|
||||||
|
temp.delete();
|
||||||
|
temp.mkdirs();
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> deleteDir(temp)));
|
||||||
|
System.out.println("Running " + name + " with user data dir: " + temp.getAbsolutePath());
|
||||||
|
List<String> params = new ArrayList<>();
|
||||||
|
paramsBuilder.accept(temp.getAbsolutePath(), params);
|
||||||
|
params.add(url);
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(params.toArray(new String[0]));
|
||||||
|
Process process = pb.start();
|
||||||
|
logStream(process.getInputStream(), name + " stdout");
|
||||||
|
logStream(process.getErrorStream(), name + " stderr");
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
System.out.println(name + " process terminated with code: " + process.waitFor());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return process;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logStream(InputStream stream, String name) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
|
||||||
|
while (true) {
|
||||||
|
String line = reader.readLine();
|
||||||
|
if (line == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
System.out.println(name + ": " + line);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteDir(File dir) {
|
||||||
|
for (File file : dir.listFiles()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
deleteDir(file);
|
||||||
|
} else {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import static org.teavm.junit.PropertyNames.C_COMPILER;
|
||||||
|
import static org.teavm.junit.PropertyNames.C_ENABLED;
|
||||||
|
import static org.teavm.junit.PropertyNames.C_LINE_NUMBERS;
|
||||||
|
import static org.teavm.junit.PropertyNames.OPTIMIZED;
|
||||||
|
import static org.teavm.junit.TestUtil.resourceToFile;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.teavm.backend.c.CTarget;
|
||||||
|
import org.teavm.backend.c.generate.CNameProvider;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
import org.teavm.vm.TeaVM;
|
||||||
|
|
||||||
|
class CPlatformSupport extends TestPlatformSupport<CTarget> {
|
||||||
|
CPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache) {
|
||||||
|
super(classSource, referenceCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TestRunStrategy createRunStrategy(File outputDir) {
|
||||||
|
String cCommand = System.getProperty(C_COMPILER);
|
||||||
|
if (cCommand != null) {
|
||||||
|
return new CRunStrategy(cCommand);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TestPlatform getPlatform() {
|
||||||
|
return TestPlatform.C;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getPath() {
|
||||||
|
return "c";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getExtension() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<TeaVMTestConfiguration<CTarget>> getConfigurations() {
|
||||||
|
List<TeaVMTestConfiguration<CTarget>> configurations = new ArrayList<>();
|
||||||
|
if (Boolean.getBoolean(C_ENABLED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.C_DEFAULT);
|
||||||
|
if (Boolean.getBoolean(OPTIMIZED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.C_OPTIMIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
CompileResult compile(Consumer<TeaVM> additionalProcessing, String baseName,
|
||||||
|
TeaVMTestConfiguration<CTarget> configuration, File path) {
|
||||||
|
CompilePostProcessor postBuild = (vm, file) -> {
|
||||||
|
try {
|
||||||
|
resourceToFile("teavm-CMakeLists.txt", new File(file.getParent(), "CMakeLists.txt"),
|
||||||
|
Collections.emptyMap());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return compile(configuration, this::createCTarget, TestNativeEntryPoint.class.getName(), path, ".c",
|
||||||
|
postBuild, true, additionalProcessing, baseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CTarget createCTarget() {
|
||||||
|
CTarget cTarget = new CTarget(new CNameProvider());
|
||||||
|
cTarget.setLineNumbersGenerated(Boolean.parseBoolean(System.getProperty(C_LINE_NUMBERS, "false")));
|
||||||
|
return cTarget;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import org.teavm.vm.TeaVM;
|
||||||
|
|
||||||
|
interface CompilePostProcessor {
|
||||||
|
void process(TeaVM vm, File targetFile);
|
||||||
|
}
|
25
tools/junit/src/main/java/org/teavm/junit/CompileResult.java
Normal file
25
tools/junit/src/main/java/org/teavm/junit/CompileResult.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
class CompileResult {
|
||||||
|
boolean success = true;
|
||||||
|
String errorMessage;
|
||||||
|
File file;
|
||||||
|
Throwable throwable;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface EachTestCompiledSeparately {
|
||||||
|
}
|
150
tools/junit/src/main/java/org/teavm/junit/JSPlatformSupport.java
Normal file
150
tools/junit/src/main/java/org/teavm/junit/JSPlatformSupport.java
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.teavm.junit.PropertyNames.JS_DECODE_STACK;
|
||||||
|
import static org.teavm.junit.PropertyNames.JS_ENABLED;
|
||||||
|
import static org.teavm.junit.PropertyNames.JS_RUNNER;
|
||||||
|
import static org.teavm.junit.PropertyNames.MINIFIED;
|
||||||
|
import static org.teavm.junit.PropertyNames.OPTIMIZED;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||||
|
import org.teavm.debugging.information.DebugInformation;
|
||||||
|
import org.teavm.debugging.information.DebugInformationBuilder;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
import org.teavm.vm.TeaVM;
|
||||||
|
|
||||||
|
class JSPlatformSupport extends TestPlatformSupport<JavaScriptTarget> {
|
||||||
|
JSPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache) {
|
||||||
|
super(classSource, referenceCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TestRunStrategy createRunStrategy(File outputDir) {
|
||||||
|
String runStrategyName = System.getProperty(JS_RUNNER);
|
||||||
|
if (runStrategyName != null) {
|
||||||
|
switch (runStrategyName) {
|
||||||
|
case "browser":
|
||||||
|
return new BrowserRunStrategy(outputDir, "JAVASCRIPT", BrowserRunStrategy::customBrowser);
|
||||||
|
case "browser-chrome":
|
||||||
|
return new BrowserRunStrategy(outputDir, "JAVASCRIPT", BrowserRunStrategy::chromeBrowser);
|
||||||
|
case "browser-firefox":
|
||||||
|
return new BrowserRunStrategy(outputDir, "JAVASCRIPT", BrowserRunStrategy::firefoxBrowser);
|
||||||
|
case "none":
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unknown run strategy: " + runStrategyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TestPlatform getPlatform() {
|
||||||
|
return TestPlatform.JAVASCRIPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getPath() {
|
||||||
|
return "js";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getExtension() {
|
||||||
|
return ".js";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<TeaVMTestConfiguration<JavaScriptTarget>> getConfigurations() {
|
||||||
|
List<TeaVMTestConfiguration<JavaScriptTarget>> configurations = new ArrayList<>();
|
||||||
|
if (Boolean.parseBoolean(System.getProperty(JS_ENABLED, "true"))) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.JS_DEFAULT);
|
||||||
|
if (Boolean.getBoolean(MINIFIED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.JS_MINIFIED);
|
||||||
|
}
|
||||||
|
if (Boolean.getBoolean(OPTIMIZED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.JS_OPTIMIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
CompileResult compile(Consumer<TeaVM> additionalProcessing, String baseName,
|
||||||
|
TeaVMTestConfiguration<JavaScriptTarget> configuration, File path) {
|
||||||
|
boolean decodeStack = Boolean.parseBoolean(System.getProperty(JS_DECODE_STACK, "true"));
|
||||||
|
var debugEmitter = new DebugInformationBuilder(new ReferenceCache());
|
||||||
|
Supplier<JavaScriptTarget> targetSupplier = () -> {
|
||||||
|
JavaScriptTarget target = new JavaScriptTarget();
|
||||||
|
target.setStrict(true);
|
||||||
|
if (decodeStack) {
|
||||||
|
target.setDebugEmitter(debugEmitter);
|
||||||
|
target.setStackTraceIncluded(true);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
CompilePostProcessor postBuild = null;
|
||||||
|
if (decodeStack) {
|
||||||
|
postBuild = (vm, file) -> {
|
||||||
|
DebugInformation debugInfo = debugEmitter.getDebugInformation();
|
||||||
|
File sourceMapsFile = new File(file.getPath() + ".map");
|
||||||
|
File debugFile = new File(file.getPath() + ".teavmdbg");
|
||||||
|
try {
|
||||||
|
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file, true), UTF_8)) {
|
||||||
|
writer.write("\n//# sourceMappingURL=");
|
||||||
|
writer.write(sourceMapsFile.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Writer sourceMapsOut = new OutputStreamWriter(new FileOutputStream(sourceMapsFile), UTF_8)) {
|
||||||
|
debugInfo.writeAsSourceMaps(sourceMapsOut, "", file.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
try (OutputStream out = new FileOutputStream(debugFile)) {
|
||||||
|
debugInfo.write(out);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return compile(configuration, targetSupplier, TestJsEntryPoint.class.getName(), path, ".js",
|
||||||
|
postBuild, false, additionalProcessing, baseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void additionalOutput(File outputPath, File outputPathForMethod, TeaVMTestConfiguration<?> configuration,
|
||||||
|
MethodReference reference) {
|
||||||
|
htmlOutput(outputPath, outputPathForMethod, configuration, reference, "teavm-run-test.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void additionalSingleTestOutput(File outputPathForMethod, TeaVMTestConfiguration<?> configuration,
|
||||||
|
MethodReference reference) {
|
||||||
|
htmlSingleTestOutput(outputPathForMethod, configuration, "teavm-run-test.html");
|
||||||
|
}
|
||||||
|
}
|
27
tools/junit/src/main/java/org/teavm/junit/OnlyPlatform.java
Normal file
27
tools/junit/src/main/java/org/teavm/junit/OnlyPlatform.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||||
|
public @interface OnlyPlatform {
|
||||||
|
TestPlatform[] value();
|
||||||
|
}
|
37
tools/junit/src/main/java/org/teavm/junit/PropertyNames.java
Normal file
37
tools/junit/src/main/java/org/teavm/junit/PropertyNames.java
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
final class PropertyNames {
|
||||||
|
static final String PATH_PARAM = "teavm.junit.target";
|
||||||
|
static final String JS_RUNNER = "teavm.junit.js.runner";
|
||||||
|
static final String WASM_RUNNER = "teavm.junit.wasm.runner";
|
||||||
|
static final String THREAD_COUNT = "teavm.junit.threads";
|
||||||
|
static final String JS_ENABLED = "teavm.junit.js";
|
||||||
|
static final String JS_DECODE_STACK = "teavm.junit.js.decodeStack";
|
||||||
|
static final String C_ENABLED = "teavm.junit.c";
|
||||||
|
static final String WASM_ENABLED = "teavm.junit.wasm";
|
||||||
|
static final String WASI_ENABLED = "teavm.junit.wasi";
|
||||||
|
static final String WASI_RUNNER = "teavm.junit.wasi.runner";
|
||||||
|
static final String C_COMPILER = "teavm.junit.c.compiler";
|
||||||
|
static final String C_LINE_NUMBERS = "teavm.junit.c.lineNumbers";
|
||||||
|
static final String MINIFIED = "teavm.junit.minified";
|
||||||
|
static final String OPTIMIZED = "teavm.junit.optimized";
|
||||||
|
static final String SOURCE_DIRS = "teavm.junit.sourceDirs";
|
||||||
|
|
||||||
|
private PropertyNames() {
|
||||||
|
}
|
||||||
|
}
|
27
tools/junit/src/main/java/org/teavm/junit/SkipPlatform.java
Normal file
27
tools/junit/src/main/java/org/teavm/junit/SkipPlatform.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||||
|
public @interface SkipPlatform {
|
||||||
|
TestPlatform[] value();
|
||||||
|
}
|
|
@ -15,17 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.junit;
|
package org.teavm.junit;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static org.teavm.junit.PropertyNames.PATH_PARAM;
|
||||||
|
import static org.teavm.junit.TestUtil.getOutputFile;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
@ -39,13 +38,10 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.junit.runner.Description;
|
import org.junit.runner.Description;
|
||||||
import org.junit.runner.Runner;
|
import org.junit.runner.Runner;
|
||||||
import org.junit.runner.manipulation.Filter;
|
import org.junit.runner.manipulation.Filter;
|
||||||
|
@ -54,20 +50,6 @@ import org.junit.runner.manipulation.NoTestsRemainException;
|
||||||
import org.junit.runner.notification.Failure;
|
import org.junit.runner.notification.Failure;
|
||||||
import org.junit.runner.notification.RunNotifier;
|
import org.junit.runner.notification.RunNotifier;
|
||||||
import org.junit.runners.model.InitializationError;
|
import org.junit.runners.model.InitializationError;
|
||||||
import org.teavm.backend.c.CTarget;
|
|
||||||
import org.teavm.backend.c.generate.CNameProvider;
|
|
||||||
import org.teavm.backend.javascript.JavaScriptTarget;
|
|
||||||
import org.teavm.backend.wasm.WasmRuntimeType;
|
|
||||||
import org.teavm.backend.wasm.WasmTarget;
|
|
||||||
import org.teavm.backend.wasm.generate.DirectorySourceFileResolver;
|
|
||||||
import org.teavm.callgraph.CallGraph;
|
|
||||||
import org.teavm.debugging.information.DebugInformation;
|
|
||||||
import org.teavm.debugging.information.DebugInformationBuilder;
|
|
||||||
import org.teavm.dependency.DependencyAnalyzerFactory;
|
|
||||||
import org.teavm.dependency.FastDependencyAnalyzer;
|
|
||||||
import org.teavm.dependency.PreciseDependencyAnalyzer;
|
|
||||||
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
|
||||||
import org.teavm.diagnostics.Problem;
|
|
||||||
import org.teavm.model.AnnotationHolder;
|
import org.teavm.model.AnnotationHolder;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.AnnotationValue;
|
import org.teavm.model.AnnotationValue;
|
||||||
|
@ -81,11 +63,7 @@ import org.teavm.model.PreOptimizingClassHolderSource;
|
||||||
import org.teavm.model.ReferenceCache;
|
import org.teavm.model.ReferenceCache;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.parsing.ClasspathClassHolderSource;
|
import org.teavm.parsing.ClasspathClassHolderSource;
|
||||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
|
||||||
import org.teavm.vm.DirectoryBuildTarget;
|
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
import org.teavm.vm.TeaVMBuilder;
|
|
||||||
import org.teavm.vm.TeaVMOptimizationLevel;
|
|
||||||
import org.teavm.vm.TeaVMTarget;
|
import org.teavm.vm.TeaVMTarget;
|
||||||
|
|
||||||
public class TeaVMTestRunner extends Runner implements Filterable {
|
public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
|
@ -101,22 +79,6 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
static final String JUNIT4_AFTER = "org.junit.After";
|
static final String JUNIT4_AFTER = "org.junit.After";
|
||||||
static final String TESTNG_AFTER = "org.testng.annotations.AfterMethod";
|
static final String TESTNG_AFTER = "org.testng.annotations.AfterMethod";
|
||||||
static final String TESTNG_PROVIDER = "org.testng.annotations.DataProvider";
|
static final String TESTNG_PROVIDER = "org.testng.annotations.DataProvider";
|
||||||
private static final String PATH_PARAM = "teavm.junit.target";
|
|
||||||
private static final String JS_RUNNER = "teavm.junit.js.runner";
|
|
||||||
private static final String WASM_RUNNER = "teavm.junit.wasm.runner";
|
|
||||||
private static final String THREAD_COUNT = "teavm.junit.threads";
|
|
||||||
private static final String JS_ENABLED = "teavm.junit.js";
|
|
||||||
static final String JS_DECODE_STACK = "teavm.junit.js.decodeStack";
|
|
||||||
private static final String C_ENABLED = "teavm.junit.c";
|
|
||||||
private static final String WASM_ENABLED = "teavm.junit.wasm";
|
|
||||||
private static final String WASI_ENABLED = "teavm.junit.wasi";
|
|
||||||
private static final String WASI_RUNNER = "teavm.junit.wasi.runner";
|
|
||||||
private static final String C_COMPILER = "teavm.junit.c.compiler";
|
|
||||||
private static final String C_LINE_NUMBERS = "teavm.junit.c.lineNumbers";
|
|
||||||
private static final String MINIFIED = "teavm.junit.minified";
|
|
||||||
private static final String OPTIMIZED = "teavm.junit.optimized";
|
|
||||||
private static final String FAST_ANALYSIS = "teavm.junit.fastAnalysis";
|
|
||||||
private static final String SOURCE_DIRS = "teavm.junit.sourceDirs";
|
|
||||||
|
|
||||||
private Class<?> testClass;
|
private Class<?> testClass;
|
||||||
private boolean isWholeClassCompilation;
|
private boolean isWholeClassCompilation;
|
||||||
|
@ -125,11 +87,13 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
private Description suiteDescription;
|
private Description suiteDescription;
|
||||||
private static File outputDir;
|
private static File outputDir;
|
||||||
private Map<Method, Description> descriptions = new HashMap<>();
|
private Map<Method, Description> descriptions = new HashMap<>();
|
||||||
private static Map<RunKind, TestRunStrategy> runners = new HashMap<>();
|
private static Map<TestPlatform, TestRunStrategy> runners = new HashMap<>();
|
||||||
private List<Method> filteredChildren;
|
private List<Method> filteredChildren;
|
||||||
private static ReferenceCache referenceCache = new ReferenceCache();
|
private static ReferenceCache referenceCache = new ReferenceCache();
|
||||||
private boolean classCompilationOk;
|
private boolean classCompilationOk;
|
||||||
private List<TestRun> runsInCurrentClass = new ArrayList<>();
|
private List<TestRun> runsInCurrentClass = new ArrayList<>();
|
||||||
|
private static List<TestPlatformSupport<?>> platforms = new ArrayList<>();
|
||||||
|
private List<TestPlatformSupport<?>> participatingPlatforms = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
classLoader = TeaVMTestRunner.class.getClassLoader();
|
classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||||
|
@ -140,55 +104,14 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
outputDir = new File(outputPath);
|
outputDir = new File(outputPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
String runStrategyName = System.getProperty(JS_RUNNER);
|
platforms.add(new JSPlatformSupport(classSource, referenceCache));
|
||||||
if (runStrategyName != null) {
|
platforms.add(new WebAssemblyPlatformSupport(classSource, referenceCache));
|
||||||
TestRunStrategy jsRunStrategy;
|
platforms.add(new WasiPlatformSupport(classSource, referenceCache));
|
||||||
switch (runStrategyName) {
|
platforms.add(new CPlatformSupport(classSource, referenceCache));
|
||||||
case "browser":
|
|
||||||
jsRunStrategy = new BrowserRunStrategy(outputDir, "JAVASCRIPT", TeaVMTestRunner::customBrowser);
|
|
||||||
break;
|
|
||||||
case "browser-chrome":
|
|
||||||
jsRunStrategy = new BrowserRunStrategy(outputDir, "JAVASCRIPT", TeaVMTestRunner::chromeBrowser);
|
|
||||||
break;
|
|
||||||
case "browser-firefox":
|
|
||||||
jsRunStrategy = new BrowserRunStrategy(outputDir, "JAVASCRIPT", TeaVMTestRunner::firefoxBrowser);
|
|
||||||
break;
|
|
||||||
case "none":
|
|
||||||
jsRunStrategy = null;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unknown run strategy: " + runStrategyName);
|
|
||||||
}
|
|
||||||
runners.put(RunKind.JAVASCRIPT, jsRunStrategy);
|
|
||||||
}
|
|
||||||
|
|
||||||
String cCommand = System.getProperty(C_COMPILER);
|
for (var platform : platforms) {
|
||||||
if (cCommand != null) {
|
var runStrategy = platform.createRunStrategy(outputDir);
|
||||||
runners.put(RunKind.C, new CRunStrategy(cCommand));
|
runners.put(platform.getPlatform(), runStrategy);
|
||||||
}
|
|
||||||
String wasiCommand = System.getProperty(WASI_RUNNER);
|
|
||||||
if (wasiCommand != null) {
|
|
||||||
runners.put(RunKind.WASI, new WasiRunStrategy(wasiCommand));
|
|
||||||
}
|
|
||||||
|
|
||||||
runStrategyName = System.getProperty(WASM_RUNNER);
|
|
||||||
if (runStrategyName != null) {
|
|
||||||
TestRunStrategy wasmRunStrategy;
|
|
||||||
switch (runStrategyName) {
|
|
||||||
case "browser":
|
|
||||||
wasmRunStrategy = new BrowserRunStrategy(outputDir, "WASM", TeaVMTestRunner::customBrowser);
|
|
||||||
break;
|
|
||||||
case "chrome":
|
|
||||||
case "browser-chrome":
|
|
||||||
wasmRunStrategy = new BrowserRunStrategy(outputDir, "WASM", TeaVMTestRunner::chromeBrowser);
|
|
||||||
break;
|
|
||||||
case "browser-firefox":
|
|
||||||
wasmRunStrategy = new BrowserRunStrategy(outputDir, "WASM", TeaVMTestRunner::firefoxBrowser);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unknown run strategy: " + runStrategyName);
|
|
||||||
}
|
|
||||||
runners.put(RunKind.WASM, wasmRunStrategy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var strategy : runners.values()) {
|
for (var strategy : runners.values()) {
|
||||||
|
@ -206,126 +129,6 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
this.testClass = testClass;
|
this.testClass = testClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Process customBrowser(String url) {
|
|
||||||
System.out.println("Open link to run tests: " + url + "?logging=true");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Process chromeBrowser(String url) {
|
|
||||||
return browserTemplate("chrome", url, (profile, params) -> {
|
|
||||||
addChromeCommand(params);
|
|
||||||
params.addAll(Arrays.asList(
|
|
||||||
"--headless",
|
|
||||||
"--disable-gpu",
|
|
||||||
"--remote-debugging-port=9222",
|
|
||||||
"--no-first-run",
|
|
||||||
"--user-data-dir=" + profile
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Process firefoxBrowser(String url) {
|
|
||||||
return browserTemplate("firefox", url, (profile, params) -> {
|
|
||||||
addFirefoxCommand(params);
|
|
||||||
params.addAll(Arrays.asList(
|
|
||||||
"--headless",
|
|
||||||
"--profile",
|
|
||||||
profile
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addChromeCommand(List<String> params) {
|
|
||||||
if (isMacos()) {
|
|
||||||
params.add("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
|
|
||||||
} else if (isWindows()) {
|
|
||||||
params.add("cmd.exe");
|
|
||||||
params.add("start");
|
|
||||||
params.add("/C");
|
|
||||||
params.add("chrome");
|
|
||||||
} else {
|
|
||||||
params.add("google-chrome-stable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addFirefoxCommand(List<String> params) {
|
|
||||||
if (isMacos()) {
|
|
||||||
params.add("/Applications/Firefox.app/Contents/MacOS/firefox");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isWindows()) {
|
|
||||||
params.add("cmd.exe");
|
|
||||||
params.add("/C");
|
|
||||||
params.add("start");
|
|
||||||
}
|
|
||||||
params.add("firefox");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isWindows() {
|
|
||||||
return System.getProperty("os.name").toLowerCase().startsWith("windows");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isMacos() {
|
|
||||||
return System.getProperty("os.name").toLowerCase().startsWith("mac");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Process browserTemplate(String name, String url, BiConsumer<String, List<String>> paramsBuilder) {
|
|
||||||
File temp;
|
|
||||||
try {
|
|
||||||
temp = File.createTempFile("teavm", "teavm");
|
|
||||||
temp.delete();
|
|
||||||
temp.mkdirs();
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> deleteDir(temp)));
|
|
||||||
System.out.println("Running " + name + " with user data dir: " + temp.getAbsolutePath());
|
|
||||||
List<String> params = new ArrayList<>();
|
|
||||||
paramsBuilder.accept(temp.getAbsolutePath(), params);
|
|
||||||
int tabs = Integer.parseInt(System.getProperty(THREAD_COUNT, "1"));
|
|
||||||
for (int i = 0; i < tabs; ++i) {
|
|
||||||
params.add(url);
|
|
||||||
}
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(params.toArray(new String[0]));
|
|
||||||
Process process = pb.start();
|
|
||||||
logStream(process.getInputStream(), name + " stdout");
|
|
||||||
logStream(process.getErrorStream(), name + " stderr");
|
|
||||||
new Thread(() -> {
|
|
||||||
try {
|
|
||||||
System.out.println(name + " process terminated with code: " + process.waitFor());
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return process;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void logStream(InputStream stream, String name) {
|
|
||||||
new Thread(() -> {
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
|
|
||||||
while (true) {
|
|
||||||
String line = reader.readLine();
|
|
||||||
if (line == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
System.out.println(name + ": " + line);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void deleteDir(File dir) {
|
|
||||||
for (File file : dir.listFiles()) {
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
deleteDir(file);
|
|
||||||
} else {
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dir.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Description getDescription() {
|
public Description getDescription() {
|
||||||
|
@ -340,11 +143,17 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(RunNotifier notifier) {
|
public void run(RunNotifier notifier) {
|
||||||
|
for (var platform : platforms) {
|
||||||
|
if (!platform.getConfigurations().isEmpty()) {
|
||||||
|
participatingPlatforms.add(platform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Method> children = getFilteredChildren();
|
List<Method> children = getFilteredChildren();
|
||||||
var description = getDescription();
|
var description = getDescription();
|
||||||
|
|
||||||
notifier.fireTestStarted(description);
|
notifier.fireTestStarted(description);
|
||||||
isWholeClassCompilation = testClass.isAnnotationPresent(WholeClassCompilation.class);
|
isWholeClassCompilation = !testClass.isAnnotationPresent(EachTestCompiledSeparately.class);
|
||||||
if (isWholeClassCompilation) {
|
if (isWholeClassCompilation) {
|
||||||
classCompilationOk = compileWholeClass(children, notifier);
|
classCompilationOk = compileWholeClass(children, notifier);
|
||||||
}
|
}
|
||||||
|
@ -402,45 +211,69 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean compileWholeClass(List<Method> children, RunNotifier notifier) {
|
private boolean compileWholeClass(List<Method> children, RunNotifier notifier) {
|
||||||
File outputPath = getOutputPathForClass();
|
|
||||||
boolean hasErrors = false;
|
|
||||||
Description description = getDescription();
|
Description description = getDescription();
|
||||||
|
|
||||||
for (TeaVMTestConfiguration<JavaScriptTarget> configuration : getJavaScriptConfigurations()) {
|
for (var platformSupport : participatingPlatforms) {
|
||||||
CompileResult result = compileToJs(wholeClass(children), "classTest", configuration, outputPath);
|
if (!compileClassForPlatform(platformSupport, children, description, notifier)) {
|
||||||
if (!result.success) {
|
return false;
|
||||||
hasErrors = true;
|
}
|
||||||
notifier.fireTestFailure(createFailure(description, result));
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private boolean compileClassForPlatform(TestPlatformSupport<?> platform, List<Method> children,
|
||||||
|
Description description, RunNotifier notifier) {
|
||||||
|
if (hasChildrenToRun(children, platform.getPlatform())) {
|
||||||
|
for (var configuration : platform.getConfigurations()) {
|
||||||
|
var path = getOutputPathForClass(platform);
|
||||||
|
var castPlatform = (TestPlatformSupport<TeaVMTarget>) platform;
|
||||||
|
var castConfiguration = (TeaVMTestConfiguration<TeaVMTarget>) configuration;
|
||||||
|
var result = castPlatform.compile(wholeClass(children, platform.getPlatform()), "classTest",
|
||||||
|
castConfiguration, path);
|
||||||
|
if (!result.success) {
|
||||||
|
notifier.fireTestFailure(createFailure(description, result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPlatformPresent(AnnotatedElement declaration, TestPlatform platform) {
|
||||||
|
var skipPlatform = declaration.getAnnotation(SkipPlatform.class);
|
||||||
|
if (skipPlatform != null) {
|
||||||
|
for (var toSkip : skipPlatform.value()) {
|
||||||
|
if (toSkip == platform) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
|
var onlyPlatform = declaration.getAnnotation(OnlyPlatform.class);
|
||||||
CompileResult result = compileToC(wholeClass(children), "classTest", configuration, outputPath);
|
if (onlyPlatform != null) {
|
||||||
if (!result.success) {
|
for (var allowedPlatform : onlyPlatform.value()) {
|
||||||
hasErrors = true;
|
if (allowedPlatform == platform) {
|
||||||
notifier.fireTestFailure(createFailure(description, result));
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
|
return true;
|
||||||
CompileResult result = compileToWasm(WasmRuntimeType.TEAVM, wholeClass(children), "classTest",
|
}
|
||||||
configuration, outputPath);
|
|
||||||
if (!result.success) {
|
|
||||||
hasErrors = true;
|
|
||||||
notifier.fireTestFailure(createFailure(description, result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasiConfigurations()) {
|
private boolean hasChildrenToRun(List<Method> children, TestPlatform platform) {
|
||||||
CompileResult result = compileToWasm(WasmRuntimeType.WASI, wholeClass(children), "classTest",
|
return isPlatformPresent(testClass, platform)
|
||||||
configuration, outputPath);
|
&& children.stream().anyMatch(child -> isPlatformPresent(child, platform));
|
||||||
if (!result.success) {
|
}
|
||||||
hasErrors = true;
|
|
||||||
notifier.fireTestFailure(createFailure(description, result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !hasErrors;
|
private List<Method> filterChildren(List<Method> children, TestPlatform platform) {
|
||||||
|
return children.stream().filter(child -> isPlatformPresent(child, platform)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRunChild(Method child, TestPlatform platform) {
|
||||||
|
return isPlatformPresent(testClass, platform) && isPlatformPresent(child, platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runChild(Method child, RunNotifier notifier) {
|
private void runChild(Method child, RunNotifier notifier) {
|
||||||
|
@ -496,123 +329,49 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareTestsFromWholeClass(Method child, List<TestRun> runs) {
|
private void prepareTestsFromWholeClass(Method child, List<TestRun> runs) {
|
||||||
File outputPath = getOutputPathForClass();
|
|
||||||
File outputPathForMethod = getOutputPath(child);
|
|
||||||
MethodDescriptor descriptor = getDescriptor(child);
|
MethodDescriptor descriptor = getDescriptor(child);
|
||||||
MethodReference reference = new MethodReference(child.getDeclaringClass().getName(), descriptor);
|
MethodReference reference = new MethodReference(child.getDeclaringClass().getName(), descriptor);
|
||||||
|
|
||||||
File testFilePath = getOutputPath(child);
|
for (var platform : participatingPlatforms) {
|
||||||
testFilePath.mkdirs();
|
if (shouldRunChild(child, platform.getPlatform())) {
|
||||||
|
var outputPath = getOutputPathForClass(platform);
|
||||||
Map<String, String> properties = new HashMap<>();
|
var outputPathForMethod = getOutputPath(child, platform);
|
||||||
for (var configuration : getJavaScriptConfigurations()) {
|
for (var configuration : platform.getConfigurations()) {
|
||||||
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".js");
|
var testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false,
|
||||||
runs.add(createTestRun(configuration, testPath, child, RunKind.JAVASCRIPT, reference.toString()));
|
platform.getExtension());
|
||||||
File htmlPath = getOutputFile(outputPathForMethod, "test", configuration.getSuffix(), false, ".html");
|
runs.add(createTestRun(configuration, testPath, child, platform.getPlatform(),
|
||||||
properties.put("SCRIPT", "../" + testPath.getName());
|
reference.toString()));
|
||||||
properties.put("IDENTIFIER", reference.toString());
|
platform.additionalOutput(outputPath, outputPathForMethod, configuration, reference);
|
||||||
try {
|
}
|
||||||
resourceToFile("teavm-run-test.html", htmlPath, properties);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var configuration : getWasmConfigurations()) {
|
|
||||||
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".wasm");
|
|
||||||
runs.add(createTestRun(configuration, testPath, child, RunKind.WASM, reference.toString()));
|
|
||||||
File htmlPath = getOutputFile(outputPathForMethod, "test-wasm", configuration.getSuffix(), false, ".html");
|
|
||||||
properties.put("SCRIPT", "../" + testPath.getName());
|
|
||||||
properties.put("IDENTIFIER", reference.toString());
|
|
||||||
try {
|
|
||||||
resourceToFile("teavm-run-test-wasm.html", htmlPath, properties);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var configuration : getWasiConfigurations()) {
|
|
||||||
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".wasm");
|
|
||||||
runs.add(createTestRun(configuration, testPath, child, RunKind.WASI, reference.toString()));
|
|
||||||
File htmlPath = getOutputFile(outputPathForMethod, "test-wasm", configuration.getSuffix(), false, ".html");
|
|
||||||
properties.put("SCRIPT", "../" + testPath.getName());
|
|
||||||
properties.put("IDENTIFIER", reference.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var configuration : getCConfigurations()) {
|
|
||||||
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), true, ".c");
|
|
||||||
runs.add(createTestRun(configuration, testPath, child, RunKind.C, reference.toString()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareCompiledTest(Method child, RunNotifier notifier, List<TestRun> runs) {
|
private void prepareCompiledTest(Method child, RunNotifier notifier, List<TestRun> runs) {
|
||||||
|
MethodDescriptor descriptor = getDescriptor(child);
|
||||||
|
MethodReference reference = new MethodReference(child.getDeclaringClass().getName(), descriptor);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
File outputPath = getOutputPath(child);
|
for (var platform : participatingPlatforms) {
|
||||||
|
if (shouldRunChild(child, platform.getPlatform())) {
|
||||||
Map<String, String> properties = new HashMap<>();
|
File outputPath = getOutputPath(child, platform);
|
||||||
for (var configuration : getJavaScriptConfigurations()) {
|
for (var configuration : platform.getConfigurations()) {
|
||||||
CompileResult compileResult = compileToJs(singleTest(child), "test", configuration, outputPath);
|
@SuppressWarnings("unchecked")
|
||||||
TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.JAVASCRIPT);
|
var castPlatform = (TestPlatformSupport<TeaVMTarget>) platform;
|
||||||
if (run != null) {
|
@SuppressWarnings("unchecked")
|
||||||
runs.add(run);
|
var castConfig = (TeaVMTestConfiguration<TeaVMTarget>) configuration;
|
||||||
|
var compileResult = castPlatform.compile(singleTest(child), "test", castConfig, outputPath);
|
||||||
File testPath = getOutputFile(outputPath, "test", configuration.getSuffix(), false, ".js");
|
var run = prepareRun(configuration, child, compileResult, notifier, platform.getPlatform());
|
||||||
File htmlPath = getOutputFile(outputPath, "test", configuration.getSuffix(), false, ".html");
|
if (run != null) {
|
||||||
properties.put("SCRIPT", testPath.getName());
|
runs.add(run);
|
||||||
properties.put("IDENTIFIER", "");
|
platform.additionalSingleTestOutput(outputPath, configuration, reference);
|
||||||
|
}
|
||||||
try {
|
|
||||||
resourceToFile("teavm-run-test.html", htmlPath, properties);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var configuration : getCConfigurations()) {
|
|
||||||
CompileResult compileResult = compileToC(singleTest(child), "test", configuration, outputPath);
|
|
||||||
TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.C);
|
|
||||||
if (run != null) {
|
|
||||||
runs.add(run);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var configuration : getWasmConfigurations()) {
|
|
||||||
CompileResult compileResult = compileToWasm(WasmRuntimeType.TEAVM, singleTest(child),
|
|
||||||
"test", configuration, outputPath);
|
|
||||||
TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.WASM);
|
|
||||||
if (run != null) {
|
|
||||||
runs.add(run);
|
|
||||||
|
|
||||||
File testPath = getOutputFile(outputPath, "test", configuration.getSuffix(), false, ".wasm");
|
|
||||||
File htmlPath = getOutputFile(outputPath, "test", configuration.getSuffix(), false, ".html");
|
|
||||||
properties.put("SCRIPT", testPath.getName());
|
|
||||||
properties.put("IDENTIFIER", "");
|
|
||||||
|
|
||||||
try {
|
|
||||||
resourceToFile("teavm-run-test-wasm.html", htmlPath, properties);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var configuration : getWasiConfigurations()) {
|
|
||||||
CompileResult compileResult = compileToWasm(WasmRuntimeType.WASI, singleTest(child), "test",
|
|
||||||
configuration, outputPath);
|
|
||||||
TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.WASI);
|
|
||||||
if (run != null) {
|
|
||||||
runs.add(run);
|
|
||||||
|
|
||||||
File testPath = getOutputFile(outputPath, "test", configuration.getSuffix(), false, ".wasm");
|
|
||||||
properties.put("SCRIPT", testPath.getName());
|
|
||||||
properties.put("IDENTIFIER", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
notifier.fireTestFailure(new Failure(describeChild(child), e));
|
||||||
notifier.fireTestFinished(describeChild(child));
|
notifier.fireTestFinished(describeChild(child));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -939,7 +698,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestRun prepareRun(TeaVMTestConfiguration<?> configuration, Method child, CompileResult result,
|
private TestRun prepareRun(TeaVMTestConfiguration<?> configuration, Method child, CompileResult result,
|
||||||
RunNotifier notifier, RunKind kind) {
|
RunNotifier notifier, TestPlatform kind) {
|
||||||
Description description = describeChild(child);
|
Description description = describeChild(child);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
|
@ -951,7 +710,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
return createTestRun(configuration, result.file, child, kind, null);
|
return createTestRun(configuration, result.file, child, kind, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestRun createTestRun(TeaVMTestConfiguration<?> configuration, File file, Method child, RunKind kind,
|
private TestRun createTestRun(TeaVMTestConfiguration<?> configuration, File file, Method child, TestPlatform kind,
|
||||||
String argument) {
|
String argument) {
|
||||||
return new TestRun(generateName(child.getName(), configuration), file.getParentFile(), child,
|
return new TestRun(generateName(child.getName(), configuration), file.getParentFile(), child,
|
||||||
file.getName(), kind, argument);
|
file.getName(), kind, argument);
|
||||||
|
@ -984,107 +743,21 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getOutputPath(Method method) {
|
private File getOutputPath(Method method, TestPlatformSupport<?> platform) {
|
||||||
File path = outputDir;
|
File path = outputDir;
|
||||||
path = new File(path, testClass.getName().replace('.', '/'));
|
path = new File(new File(path, platform.getPath()), testClass.getName().replace('.', '/'));
|
||||||
path = new File(path, method.getName());
|
path = new File(path, method.getName());
|
||||||
path.mkdirs();
|
path.mkdirs();
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getOutputPathForClass() {
|
private File getOutputPathForClass(TestPlatformSupport<?> platform) {
|
||||||
File path = outputDir;
|
File path = outputDir;
|
||||||
path = new File(path, testClass.getName().replace('.', '/'));
|
path = new File(new File(path, platform.getPath()), testClass.getName().replace('.', '/'));
|
||||||
path.mkdirs();
|
path.mkdirs();
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompileResult compileToJs(Consumer<TeaVM> additionalProcessing, String baseName,
|
|
||||||
TeaVMTestConfiguration<JavaScriptTarget> configuration, File path) {
|
|
||||||
boolean decodeStack = Boolean.parseBoolean(System.getProperty(JS_DECODE_STACK, "true"));
|
|
||||||
DebugInformationBuilder debugEmitter = new DebugInformationBuilder(new ReferenceCache());
|
|
||||||
Supplier<JavaScriptTarget> targetSupplier = () -> {
|
|
||||||
JavaScriptTarget target = new JavaScriptTarget();
|
|
||||||
target.setStrict(true);
|
|
||||||
if (decodeStack) {
|
|
||||||
target.setDebugEmitter(debugEmitter);
|
|
||||||
target.setStackTraceIncluded(true);
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
CompilePostProcessor postBuild = null;
|
|
||||||
if (decodeStack) {
|
|
||||||
postBuild = (vm, file) -> {
|
|
||||||
DebugInformation debugInfo = debugEmitter.getDebugInformation();
|
|
||||||
File sourceMapsFile = new File(file.getPath() + ".map");
|
|
||||||
File debugFile = new File(file.getPath() + ".teavmdbg");
|
|
||||||
try {
|
|
||||||
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file, true), UTF_8)) {
|
|
||||||
writer.write("\n//# sourceMappingURL=");
|
|
||||||
writer.write(sourceMapsFile.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
try (Writer sourceMapsOut = new OutputStreamWriter(new FileOutputStream(sourceMapsFile), UTF_8)) {
|
|
||||||
debugInfo.writeAsSourceMaps(sourceMapsOut, "", file.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
try (OutputStream out = new FileOutputStream(debugFile)) {
|
|
||||||
debugInfo.write(out);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return compile(configuration, targetSupplier, TestJsEntryPoint.class.getName(), path, ".js",
|
|
||||||
postBuild, false, additionalProcessing, baseName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompileResult compileToC(Consumer<TeaVM> additionalProcessing, String baseName,
|
|
||||||
TeaVMTestConfiguration<CTarget> configuration, File path) {
|
|
||||||
CompilePostProcessor postBuild = (vm, file) -> {
|
|
||||||
try {
|
|
||||||
resourceToFile("teavm-CMakeLists.txt", new File(file.getParent(), "CMakeLists.txt"),
|
|
||||||
Collections.emptyMap());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return compile(configuration, this::createCTarget, TestNativeEntryPoint.class.getName(), path, ".c",
|
|
||||||
postBuild, true, additionalProcessing, baseName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CTarget createCTarget() {
|
|
||||||
CTarget cTarget = new CTarget(new CNameProvider());
|
|
||||||
cTarget.setLineNumbersGenerated(Boolean.parseBoolean(System.getProperty(C_LINE_NUMBERS, "false")));
|
|
||||||
return cTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompileResult compileToWasm(WasmRuntimeType runtimeType, Consumer<TeaVM> additionalProcessing,
|
|
||||||
String baseName, TeaVMTestConfiguration<WasmTarget> configuration, File path) {
|
|
||||||
Supplier<WasmTarget> targetSupplier = () -> {
|
|
||||||
WasmTarget target = new WasmTarget();
|
|
||||||
target.setRuntimeType(runtimeType);
|
|
||||||
var sourceDirs = System.getProperty(SOURCE_DIRS);
|
|
||||||
if (sourceDirs != null) {
|
|
||||||
var dirs = new ArrayList<File>();
|
|
||||||
for (var tokenizer = new StringTokenizer(sourceDirs, Character.toString(File.pathSeparatorChar));
|
|
||||||
tokenizer.hasMoreTokens();) {
|
|
||||||
var dir = new File(tokenizer.nextToken());
|
|
||||||
if (dir.isDirectory()) {
|
|
||||||
dirs.add(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!dirs.isEmpty()) {
|
|
||||||
target.setSourceFileResolver(new DirectorySourceFileResolver(dirs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
return compile(configuration, targetSupplier, TestNativeEntryPoint.class.getName(), path,
|
|
||||||
".wasm", null, false, additionalProcessing, baseName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Consumer<TeaVM> singleTest(Method method) {
|
private Consumer<TeaVM> singleTest(Method method) {
|
||||||
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
|
ClassHolder classHolder = classSource.get(method.getDeclaringClass().getName());
|
||||||
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
MethodHolder methodHolder = classHolder.getMethod(getDescriptor(method));
|
||||||
|
@ -1097,13 +770,13 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Consumer<TeaVM> wholeClass(List<Method> methods) {
|
private Consumer<TeaVM> wholeClass(List<Method> methods, TestPlatform platform) {
|
||||||
return vm -> {
|
return vm -> {
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
applyProperties(testClass, properties);
|
applyProperties(testClass, properties);
|
||||||
vm.setProperties(properties);
|
vm.setProperties(properties);
|
||||||
List<MethodReference> methodReferences = new ArrayList<>();
|
List<MethodReference> methodReferences = new ArrayList<>();
|
||||||
for (Method method : methods) {
|
for (Method method : filterChildren(methods, platform)) {
|
||||||
if (isIgnored(method)) {
|
if (isIgnored(method)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1143,139 +816,6 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
return cls.getAnnotations().get(name);
|
return cls.getAnnotations().get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private <T extends TeaVMTarget> CompileResult compile(TeaVMTestConfiguration<T> configuration,
|
|
||||||
Supplier<T> targetSupplier, String entryPoint, File path, String extension,
|
|
||||||
CompilePostProcessor postBuild, boolean separateDir,
|
|
||||||
Consumer<TeaVM> additionalProcessing, String baseName) {
|
|
||||||
CompileResult result = new CompileResult();
|
|
||||||
|
|
||||||
File outputFile = getOutputFile(path, baseName, configuration.getSuffix(), separateDir, extension);
|
|
||||||
result.file = outputFile;
|
|
||||||
|
|
||||||
ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader();
|
|
||||||
|
|
||||||
T target = targetSupplier.get();
|
|
||||||
configuration.apply(target);
|
|
||||||
|
|
||||||
DependencyAnalyzerFactory dependencyAnalyzerFactory = PreciseDependencyAnalyzer::new;
|
|
||||||
boolean fastAnalysis = Boolean.parseBoolean(System.getProperty(FAST_ANALYSIS));
|
|
||||||
if (fastAnalysis) {
|
|
||||||
dependencyAnalyzerFactory = FastDependencyAnalyzer::new;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
TeaVM vm = new TeaVMBuilder(target)
|
|
||||||
.setClassLoader(classLoader)
|
|
||||||
.setClassSource(classSource)
|
|
||||||
.setReferenceCache(referenceCache)
|
|
||||||
.setDependencyAnalyzerFactory(dependencyAnalyzerFactory)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
configuration.apply(vm);
|
|
||||||
additionalProcessing.accept(vm);
|
|
||||||
vm.installPlugins();
|
|
||||||
|
|
||||||
new TestExceptionPlugin().install(vm);
|
|
||||||
|
|
||||||
vm.entryPoint(entryPoint);
|
|
||||||
|
|
||||||
if (fastAnalysis) {
|
|
||||||
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
|
|
||||||
vm.addVirtualMethods(m -> true);
|
|
||||||
}
|
|
||||||
if (!outputFile.getParentFile().exists()) {
|
|
||||||
outputFile.getParentFile().mkdirs();
|
|
||||||
}
|
|
||||||
vm.build(new DirectoryBuildTarget(outputFile.getParentFile()), outputFile.getName());
|
|
||||||
if (!vm.getProblemProvider().getProblems().isEmpty()) {
|
|
||||||
result.success = false;
|
|
||||||
result.errorMessage = buildErrorMessage(vm);
|
|
||||||
} else {
|
|
||||||
if (postBuild != null) {
|
|
||||||
postBuild.process(vm, outputFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (Exception e) {
|
|
||||||
result = new CompileResult();
|
|
||||||
result.success = false;
|
|
||||||
result.throwable = e;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getOutputFile(File path, String baseName, String suffix, boolean separateDir, String extension) {
|
|
||||||
StringBuilder simpleName = new StringBuilder();
|
|
||||||
simpleName.append(baseName);
|
|
||||||
if (!suffix.isEmpty()) {
|
|
||||||
if (!separateDir) {
|
|
||||||
simpleName.append('-').append(suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File outputFile;
|
|
||||||
if (separateDir) {
|
|
||||||
outputFile = new File(new File(path, simpleName.toString()), "test" + extension);
|
|
||||||
} else {
|
|
||||||
simpleName.append(extension);
|
|
||||||
outputFile = new File(path, simpleName.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CompilePostProcessor {
|
|
||||||
void process(TeaVM vm, File targetFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TeaVMTestConfiguration<JavaScriptTarget>> getJavaScriptConfigurations() {
|
|
||||||
List<TeaVMTestConfiguration<JavaScriptTarget>> configurations = new ArrayList<>();
|
|
||||||
if (Boolean.parseBoolean(System.getProperty(JS_ENABLED, "true"))) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.JS_DEFAULT);
|
|
||||||
if (Boolean.getBoolean(MINIFIED)) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.JS_MINIFIED);
|
|
||||||
}
|
|
||||||
if (Boolean.getBoolean(OPTIMIZED)) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.JS_OPTIMIZED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TeaVMTestConfiguration<WasmTarget>> getWasmConfigurations() {
|
|
||||||
List<TeaVMTestConfiguration<WasmTarget>> configurations = new ArrayList<>();
|
|
||||||
if (Boolean.getBoolean(WASM_ENABLED)) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.WASM_DEFAULT);
|
|
||||||
if (Boolean.getBoolean(OPTIMIZED)) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.WASM_OPTIMIZED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TeaVMTestConfiguration<WasmTarget>> getWasiConfigurations() {
|
|
||||||
List<TeaVMTestConfiguration<WasmTarget>> configurations = new ArrayList<>();
|
|
||||||
if (Boolean.getBoolean(WASI_ENABLED)) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.WASM_DEFAULT);
|
|
||||||
if (Boolean.getBoolean(OPTIMIZED)) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.WASM_OPTIMIZED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TeaVMTestConfiguration<CTarget>> getCConfigurations() {
|
|
||||||
List<TeaVMTestConfiguration<CTarget>> configurations = new ArrayList<>();
|
|
||||||
if (Boolean.getBoolean(C_ENABLED)) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.C_DEFAULT);
|
|
||||||
if (Boolean.getBoolean(OPTIMIZED)) {
|
|
||||||
configurations.add(TeaVMTestConfiguration.C_OPTIMIZED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyProperties(Class<?> cls, Properties result) {
|
private void applyProperties(Class<?> cls, Properties result) {
|
||||||
if (cls.getSuperclass() != null) {
|
if (cls.getSuperclass() != null) {
|
||||||
applyProperties(cls.getSuperclass(), result);
|
applyProperties(cls.getSuperclass(), result);
|
||||||
|
@ -1295,70 +835,6 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
return new MethodDescriptor(method.getName(), signature);
|
return new MethodDescriptor(method.getName(), signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildErrorMessage(TeaVM vm) {
|
|
||||||
CallGraph cg = vm.getDependencyInfo().getCallGraph();
|
|
||||||
DefaultProblemTextConsumer consumer = new DefaultProblemTextConsumer();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (Problem problem : vm.getProblemProvider().getProblems()) {
|
|
||||||
consumer.clear();
|
|
||||||
problem.render(consumer);
|
|
||||||
sb.append(consumer.getText());
|
|
||||||
TeaVMProblemRenderer.renderCallStack(cg, problem.getLocation(), sb);
|
|
||||||
sb.append("\n");
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resourceToFile(String resource, File file, Map<String, String> properties) throws IOException {
|
|
||||||
if (properties.isEmpty()) {
|
|
||||||
try (InputStream input = TeaVMTestRunner.class.getClassLoader().getResourceAsStream(resource);
|
|
||||||
OutputStream output = new BufferedOutputStream(new FileOutputStream(file))) {
|
|
||||||
IOUtils.copy(input, output);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String content;
|
|
||||||
try (InputStream input = TeaVMTestRunner.class.getClassLoader().getResourceAsStream(resource)) {
|
|
||||||
content = IOUtils.toString(input, UTF_8);
|
|
||||||
}
|
|
||||||
content = replaceProperties(content, properties);
|
|
||||||
try (OutputStream output = new BufferedOutputStream(new FileOutputStream(file));
|
|
||||||
Writer writer = new OutputStreamWriter(output)) {
|
|
||||||
writer.write(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String replaceProperties(String s, Map<String, String> properties) {
|
|
||||||
int i = 0;
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
while (i < s.length()) {
|
|
||||||
int next = s.indexOf("${", i);
|
|
||||||
if (next < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int end = s.indexOf('}', next + 2);
|
|
||||||
if (end < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append(s, i, next);
|
|
||||||
String property = s.substring(next + 2, end);
|
|
||||||
String value = properties.get(property);
|
|
||||||
if (value == null) {
|
|
||||||
sb.append(s, next, end + 1);
|
|
||||||
} else {
|
|
||||||
sb.append(value);
|
|
||||||
}
|
|
||||||
i = end + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.append(s.substring(i)).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ClassHolderSource getClassSource(ClassLoader classLoader) {
|
private static ClassHolderSource getClassSource(ClassLoader classLoader) {
|
||||||
return new PreOptimizingClassHolderSource(new ClasspathClassHolderSource(classLoader, referenceCache));
|
return new PreOptimizingClassHolderSource(new ClasspathClassHolderSource(classLoader, referenceCache));
|
||||||
}
|
}
|
||||||
|
@ -1380,7 +856,19 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File outputDir = getOutputPathForClass();
|
for (var platform : participatingPlatforms) {
|
||||||
|
writeRunsDescriptor(platform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeRunsDescriptor(TestPlatformSupport<?> platform) {
|
||||||
|
var runs = runsInCurrentClass.stream().filter(run -> run.getKind() == platform.getPlatform())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (runs.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File outputDir = getOutputPathForClass(platform);
|
||||||
outputDir.mkdirs();
|
outputDir.mkdirs();
|
||||||
File descriptorFile = new File(outputDir, "tests.json");
|
File descriptorFile = new File(outputDir, "tests.json");
|
||||||
try (OutputStream output = new FileOutputStream(descriptorFile);
|
try (OutputStream output = new FileOutputStream(descriptorFile);
|
||||||
|
@ -1388,7 +876,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
Writer writer = new OutputStreamWriter(bufferedOutput)) {
|
Writer writer = new OutputStreamWriter(bufferedOutput)) {
|
||||||
writer.write("[\n");
|
writer.write("[\n");
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (TestRun run : runsInCurrentClass.toArray(new TestRun[0])) {
|
for (TestRun run : runs) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
writer.write(",\n");
|
writer.write(",\n");
|
||||||
}
|
}
|
||||||
|
@ -1460,11 +948,4 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
private static char hex(int digit) {
|
private static char hex(int digit) {
|
||||||
return (char) (digit < 10 ? '0' + digit : 'A' + digit - 10);
|
return (char) (digit < 10 ? '0' + digit : 'A' + digit - 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class CompileResult {
|
|
||||||
boolean success = true;
|
|
||||||
String errorMessage;
|
|
||||||
File file;
|
|
||||||
Throwable throwable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2018 Alexey Andreev.
|
* Copyright 2023 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,9 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.junit;
|
package org.teavm.junit;
|
||||||
|
|
||||||
enum RunKind {
|
public enum TestPlatform {
|
||||||
JAVASCRIPT,
|
JAVASCRIPT,
|
||||||
C,
|
WEBASSEMBLY,
|
||||||
WASM,
|
WASI,
|
||||||
WASI
|
C
|
||||||
}
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import static org.teavm.junit.TestUtil.resourceToFile;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.teavm.callgraph.CallGraph;
|
||||||
|
import org.teavm.dependency.DependencyAnalyzerFactory;
|
||||||
|
import org.teavm.dependency.PreciseDependencyAnalyzer;
|
||||||
|
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
||||||
|
import org.teavm.diagnostics.Problem;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||||
|
import org.teavm.vm.DirectoryBuildTarget;
|
||||||
|
import org.teavm.vm.TeaVM;
|
||||||
|
import org.teavm.vm.TeaVMBuilder;
|
||||||
|
import org.teavm.vm.TeaVMTarget;
|
||||||
|
|
||||||
|
abstract class TestPlatformSupport<T extends TeaVMTarget> {
|
||||||
|
private ClassHolderSource classSource;
|
||||||
|
private ReferenceCache referenceCache;
|
||||||
|
|
||||||
|
TestPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
this.referenceCache = referenceCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract TestRunStrategy createRunStrategy(File outputDir);
|
||||||
|
|
||||||
|
abstract TestPlatform getPlatform();
|
||||||
|
|
||||||
|
abstract String getPath();
|
||||||
|
|
||||||
|
abstract String getExtension();
|
||||||
|
|
||||||
|
abstract List<TeaVMTestConfiguration<T>> getConfigurations();
|
||||||
|
|
||||||
|
abstract CompileResult compile(Consumer<TeaVM> additionalProcessing, String baseName,
|
||||||
|
TeaVMTestConfiguration<T> configuration, File path);
|
||||||
|
|
||||||
|
CompileResult compile(TeaVMTestConfiguration<T> configuration,
|
||||||
|
Supplier<T> targetSupplier, String entryPoint, File path, String extension,
|
||||||
|
CompilePostProcessor postBuild, boolean separateDir,
|
||||||
|
Consumer<TeaVM> additionalProcessing, String baseName) {
|
||||||
|
CompileResult result = new CompileResult();
|
||||||
|
|
||||||
|
File outputFile = getOutputFile(path, baseName, configuration.getSuffix(), separateDir, extension);
|
||||||
|
result.file = outputFile;
|
||||||
|
|
||||||
|
ClassLoader classLoader = TeaVMTestRunner.class.getClassLoader();
|
||||||
|
|
||||||
|
var target = targetSupplier.get();
|
||||||
|
configuration.apply(target);
|
||||||
|
|
||||||
|
DependencyAnalyzerFactory dependencyAnalyzerFactory = PreciseDependencyAnalyzer::new;
|
||||||
|
|
||||||
|
try {
|
||||||
|
TeaVM vm = new TeaVMBuilder(target)
|
||||||
|
.setClassLoader(classLoader)
|
||||||
|
.setClassSource(classSource)
|
||||||
|
.setReferenceCache(referenceCache)
|
||||||
|
.setDependencyAnalyzerFactory(dependencyAnalyzerFactory)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
configuration.apply(vm);
|
||||||
|
additionalProcessing.accept(vm);
|
||||||
|
vm.installPlugins();
|
||||||
|
|
||||||
|
new TestExceptionPlugin().install(vm);
|
||||||
|
|
||||||
|
vm.entryPoint(entryPoint);
|
||||||
|
|
||||||
|
if (!outputFile.getParentFile().exists()) {
|
||||||
|
outputFile.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
vm.build(new DirectoryBuildTarget(outputFile.getParentFile()), outputFile.getName());
|
||||||
|
if (!vm.getProblemProvider().getProblems().isEmpty()) {
|
||||||
|
result.success = false;
|
||||||
|
result.errorMessage = buildErrorMessage(vm);
|
||||||
|
} else {
|
||||||
|
if (postBuild != null) {
|
||||||
|
postBuild.process(vm, outputFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
result = new CompileResult();
|
||||||
|
result.success = false;
|
||||||
|
result.throwable = e;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getOutputFile(File path, String baseName, String suffix, boolean separateDir, String extension) {
|
||||||
|
StringBuilder simpleName = new StringBuilder();
|
||||||
|
simpleName.append(baseName);
|
||||||
|
if (!suffix.isEmpty()) {
|
||||||
|
if (!separateDir) {
|
||||||
|
simpleName.append('-').append(suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File outputFile;
|
||||||
|
if (separateDir) {
|
||||||
|
outputFile = new File(new File(path, simpleName.toString()), "test" + extension);
|
||||||
|
} else {
|
||||||
|
simpleName.append(extension);
|
||||||
|
outputFile = new File(path, simpleName.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildErrorMessage(TeaVM vm) {
|
||||||
|
CallGraph cg = vm.getDependencyInfo().getCallGraph();
|
||||||
|
DefaultProblemTextConsumer consumer = new DefaultProblemTextConsumer();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Problem problem : vm.getProblemProvider().getProblems()) {
|
||||||
|
consumer.clear();
|
||||||
|
problem.render(consumer);
|
||||||
|
sb.append(consumer.getText());
|
||||||
|
TeaVMProblemRenderer.renderCallStack(cg, problem.getLocation(), sb);
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void additionalOutput(File outputPath, File outputPathForMethod, TeaVMTestConfiguration<?> configuration,
|
||||||
|
MethodReference reference) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void additionalSingleTestOutput(File outputPathForMethod, TeaVMTestConfiguration<?> configuration,
|
||||||
|
MethodReference reference) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void htmlOutput(File outputPath, File outputPathForMethod, TeaVMTestConfiguration<?> configuration,
|
||||||
|
MethodReference reference, String template) {
|
||||||
|
var testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, getExtension());
|
||||||
|
var htmlPath = getOutputFile(outputPathForMethod, "test", configuration.getSuffix(), false, ".html");
|
||||||
|
var properties = Map.of(
|
||||||
|
"SCRIPT", "../" + testPath.getName(),
|
||||||
|
"IDENTIFIER", reference.toString()
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
resourceToFile(template, htmlPath, properties);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void htmlSingleTestOutput(File outputPathForMethod, TeaVMTestConfiguration<?> configuration,
|
||||||
|
String template) {
|
||||||
|
File testPath = getOutputFile(outputPathForMethod, "test", configuration.getSuffix(), false, ".wasm");
|
||||||
|
File htmlPath = getOutputFile(outputPathForMethod, "test", configuration.getSuffix(), false, ".html");
|
||||||
|
var properties = Map.of(
|
||||||
|
"SCRIPT", testPath.getName(),
|
||||||
|
"IDENTIFIER", ""
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
resourceToFile(template, htmlPath, properties);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,10 +23,10 @@ class TestRun {
|
||||||
private File baseDirectory;
|
private File baseDirectory;
|
||||||
private Method method;
|
private Method method;
|
||||||
private String fileName;
|
private String fileName;
|
||||||
private RunKind kind;
|
private TestPlatform kind;
|
||||||
private String argument;
|
private String argument;
|
||||||
|
|
||||||
TestRun(String name, File baseDirectory, Method method, String fileName, RunKind kind,
|
TestRun(String name, File baseDirectory, Method method, String fileName, TestPlatform kind,
|
||||||
String argument) {
|
String argument) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.baseDirectory = baseDirectory;
|
this.baseDirectory = baseDirectory;
|
||||||
|
@ -52,7 +52,7 @@ class TestRun {
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RunKind getKind() {
|
public TestPlatform getKind() {
|
||||||
return kind;
|
return kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
104
tools/junit/src/main/java/org/teavm/junit/TestUtil.java
Normal file
104
tools/junit/src/main/java/org/teavm/junit/TestUtil.java
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
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.Map;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
final class TestUtil {
|
||||||
|
private TestUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static File getOutputFile(File path, String baseName, String suffix, boolean separateDir, String extension) {
|
||||||
|
StringBuilder simpleName = new StringBuilder();
|
||||||
|
simpleName.append(baseName);
|
||||||
|
if (!suffix.isEmpty()) {
|
||||||
|
if (!separateDir) {
|
||||||
|
simpleName.append('-').append(suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File outputFile;
|
||||||
|
if (separateDir) {
|
||||||
|
outputFile = new File(new File(path, simpleName.toString()), "test" + extension);
|
||||||
|
} else {
|
||||||
|
simpleName.append(extension);
|
||||||
|
outputFile = new File(path, simpleName.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void resourceToFile(String resource, File file, Map<String, String> properties) throws IOException {
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
|
if (properties.isEmpty()) {
|
||||||
|
try (InputStream input = TeaVMTestRunner.class.getClassLoader().getResourceAsStream(resource);
|
||||||
|
OutputStream output = new BufferedOutputStream(new FileOutputStream(file))) {
|
||||||
|
IOUtils.copy(input, output);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String content;
|
||||||
|
try (InputStream input = TeaVMTestRunner.class.getClassLoader().getResourceAsStream(resource)) {
|
||||||
|
content = IOUtils.toString(input, UTF_8);
|
||||||
|
}
|
||||||
|
content = replaceProperties(content, properties);
|
||||||
|
try (OutputStream output = new BufferedOutputStream(new FileOutputStream(file));
|
||||||
|
Writer writer = new OutputStreamWriter(output)) {
|
||||||
|
writer.write(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String replaceProperties(String s, Map<String, String> properties) {
|
||||||
|
int i = 0;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
while (i < s.length()) {
|
||||||
|
int next = s.indexOf("${", i);
|
||||||
|
if (next < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int end = s.indexOf('}', next + 2);
|
||||||
|
if (end < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(s, i, next);
|
||||||
|
String property = s.substring(next + 2, end);
|
||||||
|
String value = properties.get(property);
|
||||||
|
if (value == null) {
|
||||||
|
sb.append(s, next, end + 1);
|
||||||
|
} else {
|
||||||
|
sb.append(value);
|
||||||
|
}
|
||||||
|
i = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.append(s.substring(i)).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import static org.teavm.junit.PropertyNames.OPTIMIZED;
|
||||||
|
import static org.teavm.junit.PropertyNames.WASI_ENABLED;
|
||||||
|
import static org.teavm.junit.PropertyNames.WASI_RUNNER;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.backend.wasm.WasmRuntimeType;
|
||||||
|
import org.teavm.backend.wasm.WasmTarget;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
|
||||||
|
class WasiPlatformSupport extends BaseWebAssemblyPlatformSupport {
|
||||||
|
WasiPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache) {
|
||||||
|
super(classSource, referenceCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TestRunStrategy createRunStrategy(File outputDir) {
|
||||||
|
String wasiCommand = System.getProperty(WASI_RUNNER);
|
||||||
|
if (wasiCommand != null) {
|
||||||
|
return new WasiRunStrategy(wasiCommand);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected WasmRuntimeType getRuntimeType() {
|
||||||
|
return WasmRuntimeType.WASI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TestPlatform getPlatform() {
|
||||||
|
return TestPlatform.WASI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getPath() {
|
||||||
|
return "wasi";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<TeaVMTestConfiguration<WasmTarget>> getConfigurations() {
|
||||||
|
List<TeaVMTestConfiguration<WasmTarget>> configurations = new ArrayList<>();
|
||||||
|
if (Boolean.getBoolean(WASI_ENABLED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.WASM_DEFAULT);
|
||||||
|
if (Boolean.getBoolean(OPTIMIZED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.WASM_OPTIMIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configurations;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.junit;
|
||||||
|
|
||||||
|
import static org.teavm.junit.PropertyNames.OPTIMIZED;
|
||||||
|
import static org.teavm.junit.PropertyNames.WASM_ENABLED;
|
||||||
|
import static org.teavm.junit.PropertyNames.WASM_RUNNER;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.backend.wasm.WasmRuntimeType;
|
||||||
|
import org.teavm.backend.wasm.WasmTarget;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
|
||||||
|
class WebAssemblyPlatformSupport extends BaseWebAssemblyPlatformSupport {
|
||||||
|
WebAssemblyPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache) {
|
||||||
|
super(classSource, referenceCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TestRunStrategy createRunStrategy(File outputDir) {
|
||||||
|
var runStrategyName = System.getProperty(WASM_RUNNER);
|
||||||
|
if (runStrategyName != null) {
|
||||||
|
switch (runStrategyName) {
|
||||||
|
case "browser":
|
||||||
|
return new BrowserRunStrategy(outputDir, "WASM", BrowserRunStrategy::customBrowser);
|
||||||
|
case "chrome":
|
||||||
|
case "browser-chrome":
|
||||||
|
return new BrowserRunStrategy(outputDir, "WASM", BrowserRunStrategy::chromeBrowser);
|
||||||
|
case "browser-firefox":
|
||||||
|
return new BrowserRunStrategy(outputDir, "WASM", BrowserRunStrategy::firefoxBrowser);
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unknown run strategy: " + runStrategyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TestPlatform getPlatform() {
|
||||||
|
return TestPlatform.WEBASSEMBLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getPath() {
|
||||||
|
return "wasm";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<TeaVMTestConfiguration<WasmTarget>> getConfigurations() {
|
||||||
|
List<TeaVMTestConfiguration<WasmTarget>> configurations = new ArrayList<>();
|
||||||
|
if (Boolean.getBoolean(WASM_ENABLED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.WASM_DEFAULT);
|
||||||
|
if (Boolean.getBoolean(OPTIMIZED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.WASM_OPTIMIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected WasmRuntimeType getRuntimeType() {
|
||||||
|
return WasmRuntimeType.TEAVM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void additionalOutput(File outputPath, File outputPathForMethod, TeaVMTestConfiguration<?> configuration,
|
||||||
|
MethodReference reference) {
|
||||||
|
htmlOutput(outputPath, outputPathForMethod, configuration, reference, "teavm-run-test-wasm.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void additionalSingleTestOutput(File outputPathForMethod, TeaVMTestConfiguration<?> configuration,
|
||||||
|
MethodReference reference) {
|
||||||
|
htmlSingleTestOutput(outputPathForMethod, configuration, "teavm-run-test-wasm.html");
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,5 +22,6 @@ import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
|
@Deprecated
|
||||||
public @interface WholeClassCompilation {
|
public @interface WholeClassCompilation {
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user