package nbtool.gui.utilitypanes;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import nbtool.data.group.AllGroups;
import nbtool.data.group.Group;
import nbtool.data.log.Log;
import nbtool.data.log.LogReference;
import nbtool.io.CommonIO.IOFirstResponder;
import nbtool.io.CommonIO.IOInstance;
import nbtool.io.CommonIO.IOState;
import nbtool.nio.CrossServer;
import nbtool.nio.CrossServer.CrossInstance;
import nbtool.util.Center;
import nbtool.util.Debug;
public class AutomaticCrossTester <K, R> {
private static final Debug.DebugSettings debug = Debug.createSettings(Debug.INFO);
public static interface TestInterface <K,R> {
public void notifyTestStart();
public String testName();
/* should this reference be used, and if so under what category. */
/* if return == null, should not be used */
public K qualifiesForTesting(LogReference logr);
public String crossFunctionName();
//true if the test should continue with this log
public boolean finishAnnotations(K key, Log log);
public R getResult(LogReference from, K key, Log ... out );
public boolean createGroupForResult(R resultType);
public String groupNameFor(R resultType);
public void notifyTestFinished();
}
private final AutomaticCrossTester<K,R> outerThis = this;
private final TestInterface<K,R> theInterface;
public AutomaticCrossTester(TestInterface<K,R> theInterface) {
this.theInterface = theInterface;
}
private IOState state = IOState.STARTING;
public boolean running() { return state == IOState.RUNNING; }
public boolean finished() { return state == IOState.FINISHED; }
private final synchronized void finish() {
state = IOState.FINISHED;
theInterface.notifyTestFinished();
}
private final Map<K, LinkedList<LogReference>> qualified = new HashMap<>();
private static <K2, V> void insert(Map<K2, LinkedList<V>> map, K2 key, V value) {
synchronized(map) {
if ( map.containsKey(key) ) {
map.get(key).add(value);
} else {
LinkedList<V> list = new LinkedList<>();
list.add(value);
map.put(key, list);
}
}
}
private CrossInstance theInstance = null;
public final synchronized void runTests() {
if (state != IOState.STARTING) {
throw new RuntimeException("cannot start from state: " + state);
}
debug.info("starting test of: %s", theInterface.testName());
state = IOState.RUNNING;
for (Group group : AllGroups.allGroups) {
for (LogReference ref : group.logs) {
K key = theInterface.qualifiesForTesting(ref);
if (key != null) {
insert(qualified, key, ref);
}
}
}
debug.info("found %d cases:", qualified.size());
for (K key : qualified.keySet()) {
debug.info("\t%s: %d", key.toString(), qualified.get(key).size());
}
theInstance = CrossServer.instanceByIndex(0);
if (theInstance == null) {
debug.error("cannot run test '%s' without NBCross instance!", theInterface.testName());
this.finish();
return;
}
Center.addEvent(new Center.EventRunnable() {
@Override
protected void run() {
add_tests();
}
});
}
private int outstanding_tests = 0;
private final Map<R, LinkedList<LogReference>> results = new HashMap<>();
private class TestResponder implements IOFirstResponder {
TestResponder(LogReference r, K k) {
this.from = r; this.key = k;
}
LogReference from;
K key;
@Override
public void ioFinished(IOInstance instance) {
if (state == IOState.RUNNING) debug.error("test-%s failed when nbcross died!",
theInterface.testName() );
}
@Override
public void ioReceived(IOInstance inst, int ret, Log... out) {
R result = theInterface.getResult(from, key, out);
if (result != null) {
insert(results, result, from);
} else {
debug.warn("null result {input had key %s}", key.toString());
}
synchronized(outerThis) {
--outstanding_tests;
if (outstanding_tests == 0) {
debug.info("TestResponder sees testing finished!");
Center.addEvent(new Center.EventRunnable() {
@Override
protected void run() {
ship_it();
}
});
}
}
}
@Override
public boolean ioMayRespondOnCenterThread(IOInstance inst) {
return true;
}
}
public void add_tests() {
// debug.warn("%s starting!", Thread.currentThread().getName());
debug.warn("adding tests for %s", theInterface.testName());
synchronized(this) {
for (K key : qualified.keySet()) {
List<LogReference> list = qualified.get(key);
for (LogReference ref : list) {
Log arg = ref.get();
if (theInterface.finishAnnotations(key, arg)) {
theInstance.tryAddCall(new TestResponder(ref, key), theInterface.crossFunctionName(), arg);
++outstanding_tests;
}
}
}
}
}
public void ship_it() {
debug.warn("wrapping up %s", theInterface.testName());
for (R resultType : results.keySet()) {
if (theInterface.createGroupForResult(resultType)) {
String name = theInterface.groupNameFor(resultType);
Group group = AllGroups.request(name);
group.logs.addAll(results.get(resultType));
} else {
debug.warn("ignoring results of type %s", resultType);
}
}
this.finish();
}
}