/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.traceur;
import static com.github.anba.es6draft.util.Resources.loadConfiguration;
import static com.github.anba.es6draft.util.Resources.loadTests;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.configuration.Configuration;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
import com.github.anba.es6draft.parser.Parser;
import com.github.anba.es6draft.runtime.extensions.timer.Timers;
import com.github.anba.es6draft.runtime.internal.Console;
import com.github.anba.es6draft.runtime.internal.Properties;
import com.github.anba.es6draft.runtime.internal.RuntimeContext;
import com.github.anba.es6draft.runtime.modules.ResolutionException;
import com.github.anba.es6draft.util.NullConsole;
import com.github.anba.es6draft.util.Parallelized;
import com.github.anba.es6draft.util.ParameterizedRunnerFactory;
import com.github.anba.es6draft.util.TestConfiguration;
import com.github.anba.es6draft.util.TestGlobals;
import com.github.anba.es6draft.util.TestInfo;
import com.github.anba.es6draft.util.rules.ExceptionHandlers.ScriptExceptionHandler;
import com.github.anba.es6draft.util.rules.ExceptionHandlers.StandardErrorHandler;
/**
*
*/
@RunWith(Parallelized.class)
@UseParametersRunnerFactory(ParameterizedRunnerFactory.class)
@TestConfiguration(name = "traceur.test", file = "resource:/test-configuration.properties")
public final class TraceurTest {
private static final Configuration configuration = loadConfiguration(TraceurTest.class);
@Parameters(name = "{0}")
public static List<TraceurTestInfo> suiteValues() throws IOException {
return loadTests(configuration, TraceurTest::createTest);
}
@BeforeClass
public static void setUpClass() throws IOException {
TraceurTestGlobalObject.testLoadInitializationScript();
}
@ClassRule
public static TestGlobals<TraceurTestGlobalObject, TraceurTestInfo> globals = new TestGlobals<TraceurTestGlobalObject, TraceurTestInfo>(
configuration, TraceurTestGlobalObject::new, TraceurFileModuleLoader::new) {
@Override
protected RuntimeContext createContext(Console console, TraceurTestInfo test) {
RuntimeContext context = super.createContext(console, test);
if (test.tailCall) {
context.getParserOptions().add(Parser.Option.Strict);
}
return context;
}
};
@Rule
public Timeout maxTime = new Timeout(120, TimeUnit.SECONDS);
@Rule
public StandardErrorHandler errorHandler = StandardErrorHandler.none();
@Rule
public ScriptExceptionHandler exceptionHandler = ScriptExceptionHandler.none();
@Rule
public ExpectedException expected = ExpectedException.none();
@Parameter(0)
public TraceurTestInfo test;
private static final class TraceurTestInfo extends TestInfo {
boolean negative = false;
boolean async = false;
boolean tailCall = false;
TraceurTestInfo(Path basedir, Path script) {
super(basedir, script);
}
@Override
public boolean isModule() {
return getScript().getFileName().toString().endsWith(".module.js");
}
@Override
public String toModuleName() {
String moduleName = super.toModuleName();
assert moduleName.endsWith(".js");
return moduleName.substring(0, moduleName.length() - 3);
}
}
private TraceurTestGlobalObject global;
private AsyncHelper async;
private Timers timers;
@Before
public void setUp() throws Throwable {
assumeTrue("Test disabled", test.isEnabled());
global = globals.newGlobal(new NullConsole(), test);
exceptionHandler.setExecutionContext(global.getRealm().defaultContext());
if (test.async) {
async = global.createGlobalProperties(new AsyncHelper(), AsyncHelper.class);
timers = global.createGlobalProperties(new Timers(), Timers.class);
}
if (test.negative) {
if (test.isModule()) {
expected.expect(Matchers.either(StandardErrorHandler.defaultMatcher())
.or(ScriptExceptionHandler.defaultMatcher()).or(Matchers.instanceOf(ResolutionException.class))
.or(Matchers.instanceOf(NoSuchFileException.class)));
} else {
expected.expect(Matchers.either(StandardErrorHandler.defaultMatcher())
.or(ScriptExceptionHandler.defaultMatcher()));
}
} else {
errorHandler.match(StandardErrorHandler.defaultMatcher());
exceptionHandler.match(ScriptExceptionHandler.defaultMatcher());
}
}
@After
public void tearDown() throws InterruptedException {
globals.release(global);
}
@Test
public void runTest() throws Throwable {
// evaluate actual test-script
if (test.isModule()) {
global.eval(test.toModuleName());
} else {
global.eval(test.getScript(), test.toFile());
}
// wait for pending tasks to finish
if (test.async) {
assertFalse(async.doneCalled);
global.getRealm().getWorld().runEventLoop(timers);
assertTrue(async.doneCalled);
} else {
global.getRealm().getWorld().runEventLoop();
}
}
public static final class AsyncHelper {
boolean doneCalled = false;
@Properties.Function(name = "done", arity = 0)
public void done() {
assertFalse(doneCalled);
doneCalled = true;
}
}
private static final Pattern FlagsPattern = Pattern.compile("\\s*//\\s*(.*)\\s*");
private static BiFunction<Path, Iterator<String>, TraceurTestInfo> createTest(Path basedir) {
return (file, lines) -> {
TraceurTestInfo test = new TraceurTestInfo(basedir, file);
Pattern p = FlagsPattern;
while (lines.hasNext()) {
String line = lines.next();
Matcher m = p.matcher(line);
if (m.matches()) {
String s = m.group(1);
if ("Only in browser.".equals(s) || s.startsWith("Skip.")) {
test.setEnabled(false);
} else if (s.equals("Async.")) {
test.async = true;
} else if (s.startsWith("Error:")) {
test.negative = true;
} else if (s.startsWith("Options:")) {
if (s.contains("--proper-tail-calls")) {
test.tailCall = true;
}
// ignore
} else {
break;
}
} else {
break;
}
}
return test;
};
}
}