package org.dynjs;
import org.dynjs.runtime.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author lanceball
*/
public class TestRunner {
private static final String SCRIPT = "" +
"var executor = require('./target/test-classes/specRunner.js');" +
"executor.run('" + testPattern() + "');";
public static String testPattern() {
String pattern = System.getProperty("test.pattern");
if (pattern == null) {
pattern = "**/*Spec.js";
}
return pattern;
}
public static void main(String... args) throws InterruptedException {
TestRunner runner = new TestRunner();
runner.run();
}
private final ScheduledExecutorService executor;
private final List<Future> futures = new ArrayList<>();
private final AtomicInteger counter = new AtomicInteger(0);
private final CountDownLatch finishLatch = new CountDownLatch(1);
private final DynJS dynjs;
public TestRunner() {
Config config = new Config(TestRunner.class.getClassLoader());
config.setCompileMode(Config.CompileMode.OFF);
this.executor = Executors.newSingleThreadScheduledExecutor( new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(false);
return t;
}
});
//config.setArgv(new String[]{ SCRIPT});
this.dynjs = new DynJS(config);
setupTimers();
setupConsole();
}
public void increment() {
counter.incrementAndGet();
}
public void decrement() {
int val = counter.decrementAndGet();
if ( val == 0 ) {
finishLatch.countDown();
}
}
public void run() throws InterruptedException {
counter.incrementAndGet();
futures.add(executor.submit(new Runnable() {
@Override
public void run() {
try {
dynjs.newRunner().withSource(SCRIPT).execute();
} finally {
decrement();
}
}
}));
finishLatch.await();
// Look for any exceptions from futures
try {
for (Future future : futures) {
future.get();
}
} catch (ExecutionException e) {
throw new RuntimeException("one or more jasmine specs failed");
} finally {
executor.shutdown();
futures.clear();
}
}
private void setupConsole() {
GlobalContext context = dynjs.getGlobalContext();
JSObject object = context.getObject();
JSObject console = new DynObject( context );
console.put( null, "log", new Log( context), false );
object.put( null, "console", console, false );
}
private void setupTimers() {
GlobalContext context = dynjs.getGlobalContext();
JSObject object = context.getObject();
object.put(null, "setTimeout", new SetTimeout(context, executor), false);
object.put(null, "clearTimeout", new ClearTimeout(context, executor), false);
object.put(null, "setInterval", new SetInterval(context, executor), false);
object.put(null, "clearInterval", new ClearInterval(context, executor), false);
}
public class SetTimeout extends AbstractNativeFunction {
public SetTimeout(GlobalContext globalContext, ScheduledExecutorService executor) {
super(globalContext);
}
@Override
public Object call(final ExecutionContext context, Object self, Object... args) {
final JSFunction fn = (JSFunction) args[0];
long timeout = Types.toInteger( context, args[1] );
increment();
futures.add(executor.schedule( new Runnable() {
@Override
public void run() {
try {
context.call(fn, Types.UNDEFINED);
} finally {
decrement();
}
}
}, timeout, TimeUnit.MILLISECONDS ));
return Types.UNDEFINED;
}
}
public class ClearTimeout extends AbstractNativeFunction {
public ClearTimeout(GlobalContext globalContext, ScheduledExecutorService executor) {
super(globalContext);
}
@Override
public Object call(ExecutionContext context, Object self, Object... args) {
System.err.println( "clearTimeout: " + self + ", " + Arrays.asList( args ) );
return null;
}
}
public class SetInterval extends AbstractNativeFunction {
public SetInterval(GlobalContext globalContext, ScheduledExecutorService executor) {
super(globalContext);
}
@Override
public Object call(ExecutionContext context, Object self, Object... args) {
System.err.println( "setInterval: " + self + ", " + Arrays.asList( args ) );
return null;
}
}
public class ClearInterval extends AbstractNativeFunction {
public ClearInterval(GlobalContext globalContext, ScheduledExecutorService executor) {
super(globalContext);
}
@Override
public Object call(ExecutionContext context, Object self, Object... args) {
System.err.println( "clearInterval: " + self + ", " + Arrays.asList( args ) );
return null;
}
}
public class Log extends AbstractNativeFunction {
public Log(GlobalContext globalContext) {
super(globalContext);
}
@Override
public Object call(ExecutionContext context, Object self, Object... args) {
System.err.println( args[0] );
return Types.UNDEFINED;
}
}
}