125 lines
4.8 KiB
Java
125 lines
4.8 KiB
Java
package cljtest;
|
|
|
|
import clojure.java.api.Clojure;
|
|
import clojure.lang.ArraySeq;
|
|
import clojure.lang.IFn;
|
|
import common.Engine;
|
|
import common.EngineException;
|
|
|
|
import java.nio.file.Path;
|
|
import java.util.Arrays;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* Utility class for Clojure tests.
|
|
*
|
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
|
*/
|
|
public final class ClojureScript {
|
|
public static final IFn LOAD_STRING = var("clojure.core/load-string");
|
|
public static final IFn LOAD_FILE = asUser("load-file");
|
|
public static final IFn LOAD_STRING_IN = asUser("load-string");
|
|
public static Path CLOJURE_ROOT = Path.of(".");
|
|
|
|
private ClojureScript() {
|
|
}
|
|
|
|
private static IFn asUser(final String function) {
|
|
return (IFn) LOAD_STRING.invoke(
|
|
"(fn " + function + "-in [arg]" +
|
|
" (binding [*ns* *ns*]" +
|
|
" (in-ns 'user)" +
|
|
" (" + function + " arg)))"
|
|
);
|
|
}
|
|
|
|
public static void loadScript(final String script) {
|
|
final String escaped = CLOJURE_ROOT.toString().replace("\\", "\\\\");
|
|
LOAD_STRING_IN.invoke("(defn load-file [file] (clojure.core/load-file (str \"" + escaped + "/\" file)))");
|
|
LOAD_FILE.invoke(CLOJURE_ROOT.resolve(script).toString());
|
|
}
|
|
|
|
static <T> Engine.Result<T> call(final IFn f, final Class<T> type, final String context, final Object[] args) {
|
|
final Object result;
|
|
try {
|
|
result = callUnsafe(f, args);
|
|
} catch (final AssertionError e) {
|
|
throw e;
|
|
} catch (final Throwable e) {
|
|
throw new EngineException("No error expected in " + context, e);
|
|
}
|
|
if (result == null) {
|
|
throw new EngineException("Expected %s, found null\n%s".formatted(type.getSimpleName(), context), new NullPointerException());
|
|
}
|
|
if (!type.isInstance(result)) {
|
|
throw new EngineException("Expected %s, found %s (%s)\n%s".formatted(type.getSimpleName(), result, result.getClass().getSimpleName(), context), null);
|
|
}
|
|
return new Engine.Result<>(context, type.cast(result));
|
|
}
|
|
|
|
private static Object callUnsafe(final IFn f, final Object[] args) {
|
|
return switch (args.length) {
|
|
case 0 -> f.invoke();
|
|
case 1 -> f.invoke(args[0]);
|
|
case 2 -> f.invoke(args[0], args[1]);
|
|
case 3 -> f.invoke(args[0], args[1], args[2]);
|
|
case 4 -> f.invoke(args[0], args[1], args[2], args[3]);
|
|
case 5 -> f.invoke(args[0], args[1], args[2], args[3], args[4]);
|
|
case 6 -> f.invoke(args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
case 7 -> f.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
|
case 8 -> f.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
|
|
case 9 -> f.invoke(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
|
|
default -> f.applyTo(ArraySeq.create(args));
|
|
};
|
|
}
|
|
|
|
public static Engine.Result<Throwable> expectException(final IFn f, final Object[] args, final String context) {
|
|
try {
|
|
callUnsafe(f, args);
|
|
} catch (final Throwable e) {
|
|
return new Engine.Result<>(context, e);
|
|
}
|
|
assert false : "Exception expected in " + context;
|
|
return null;
|
|
}
|
|
|
|
public static <T> F<T> function(final String name, final Class<T> type) {
|
|
return new F<>(name, type);
|
|
}
|
|
|
|
/**
|
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
|
*/
|
|
public record F<T>(String name, Class<T> type, IFn f) {
|
|
public F(final String name, final Class<T> type) {
|
|
this(name.substring(name.indexOf("/") + 1), type, var(name));
|
|
}
|
|
|
|
public Engine.Result<T> call(final Engine.Result<?>... args) {
|
|
return ClojureScript.call(
|
|
f,
|
|
type,
|
|
callToString(args),
|
|
Arrays.stream(args).map(Engine.Result::value).toArray()
|
|
);
|
|
}
|
|
|
|
public String callToString(final Engine.Result<?>[] args) {
|
|
return "(" + name + Arrays.stream(args).map(arg -> " " + arg.context()).collect(Collectors.joining()) + ")";
|
|
}
|
|
|
|
public Engine.Result<Throwable> expectException(final Engine.Result<?>... args) {
|
|
return ClojureScript.expectException(
|
|
f,
|
|
Arrays.stream(args).map(Engine.Result::value).toArray(),
|
|
"(" + name + " " + Arrays.stream(args).map(Engine.Result::context).collect(Collectors.joining(" ")) + ")"
|
|
);
|
|
}
|
|
}
|
|
|
|
public static IFn var(final String name) {
|
|
final String qualifiedName = (name.contains("/") ? "" : "user/") + name;
|
|
return Clojure.var(qualifiedName);
|
|
}
|
|
}
|