package org.enumerable.lambda.support.clojure;
import static clojure.lang.RT.*;
import static org.enumerable.lambda.Parameters.*;
import static org.enumerable.lambda.support.clojure.ClojureSeqs.*;
import static org.enumerable.lambda.support.clojure.ClojureSeqs.Vars.*;
import static org.enumerable.lambda.support.clojure.LambdaClojure.*;
import static org.junit.Assert.*;
import groovy.lang.Closure;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.enumerable.lambda.Lambda;
import org.enumerable.lambda.enumerable.Enumerable;
import org.enumerable.lambda.support.clojure.LambdaClojure;
import org.enumerable.lambda.support.groovy.GroovyTest;
import org.enumerable.lambda.support.groovy.LambdaGroovy;
import org.enumerable.lambda.support.javascript.JavaScriptTest;
import org.enumerable.lambda.support.javascript.LambdaJavaScript;
import org.enumerable.lambda.support.jruby.JRubyTest;
import org.enumerable.lambda.support.jruby.LambdaJRuby;
import org.enumerable.lambda.support.scala.LambdaScala;
import org.enumerable.lambda.support.scala.ScalaTest;
import org.enumerable.lambda.support.scala.ScalaTest.ScalaInterpreter;
import org.jruby.RubyProc;
import org.junit.Before;
import org.junit.Test;
import scala.Function2;
import sun.org.mozilla.javascript.internal.Function;
import clojure.lang.APersistentMap;
import clojure.lang.APersistentSet;
import clojure.lang.APersistentVector;
import clojure.lang.IFn;
import clojure.lang.IPersistentMap;
import clojure.lang.IPersistentVector;
import clojure.lang.ISeq;
import clojure.lang.Namespace;
import clojure.lang.RT;
import clojure.lang.Symbol;
import clojure.lang.Var;
public class ClojureTest {
ScriptEngine clj;
@Test
public void inUserNamespaceByDefault() throws Exception {
assertEquals("user", eval("*ns*").toString());
}
@Test
public void initDoesNotChangeNamespaceOtherThanClojureCore() throws Exception {
try {
CURRENT_NS.doReset(Namespace.findOrCreate(Symbol.create("my-ns")));
LambdaClojure.init();
assertEquals("my-ns", CURRENT_NS.get().toString());
} finally {
CURRENT_NS.doReset(Namespace.findOrCreate(Symbol.create("user")));
}
}
@Test
public void evalUsingThreadBindings() throws Exception {
try {
Var.pushThreadBindings(RT.map(RT.CURRENT_NS, RT.CURRENT_NS.deref()));
assertEquals("my-ns", eval("(in-ns 'my-ns)").toString());
} finally {
Var.popThreadBindings();
}
assertEquals("user", eval("*ns*").toString());
}
@Test
public void clojureEngineDoesNotChangeNamespaceBetweenInvocations() throws Exception {
assertEquals("my-ns", clj.eval("(in-ns 'my-ns)").toString());
assertEquals("user", clj.eval("*ns*").toString());
}
@Test
public void defnLambda() throws Exception {
Var square = defn("square", fn(n, n * n));
assertEquals(4, square.invoke(2));
Var found = var(CURRENT_NS.get().toString(), "square");
assertSame(square, found);
ISeq squares = (map(square, list(2, 4)));
assertEquals(list(4, 16), squares);
}
@Test
public void defaultValuesForIFns() throws ScriptException {
defn("f", fn(n = 2, n * 2));
assertEquals(4, eval("(f)"));
defn("f", fn(n, m = 2, n * m));
assertEquals(8, eval("(f 4)"));
defn("f", fn(n = 2, m = 2, n * m));
assertEquals(4, eval("(f)"));
defn("f", fn(s, n = 2, m = 2, s + n * m));
assertEquals("#: 4", eval("(f \"#: \")"));
}
@Test
public void basicSequenceOperations() throws Exception {
ISeq map = (map(fn(s, s.toUpperCase()), list("hello", "world")));
assertEquals(list("HELLO", "WORLD"), map);
ISeq filter = (filter(fn(n, n % 2 == 0), list(1, 2, 3, 4, 5)));
assertEquals(list(2, 4), filter);
Integer reduce = (reduce(fn(n, m, n * m), list(1, 2, 3, 4, 5)));
assertEquals(120, reduce.intValue());
}
@Test
public void creatingPersistentCollections() throws Exception {
APersistentVector vector = (APersistentVector) vec(list("hello", "world"));
assertEquals("hello", vector.invoke(0));
assertEquals("world", vector.invoke(1));
assertEquals("[\"hello\" \"world\"]", vector.toString());
APersistentSet set = (APersistentSet) set(list("hello", "world"));
assertEquals("hello", set.invoke("hello"));
assertEquals("world", set.invoke("world"));
assertEquals("#{\"hello\" \"world\"}", set.toString());
APersistentMap map = (APersistentMap) zipmap(list("hello", "world"), list(1, 2));
assertEquals(1, map.invoke("hello"));
assertEquals(2, map.invoke("world"));
assertEquals("{\"world\" 2, \"hello\" 1}", map.toString());
}
@Test
public void varargsExpansion() throws Exception {
ISeq concat = (concat(list(1), list(2), list(3, 4), list(5)));
assertEquals(list(1, 2, 3, 4, 5), concat);
ISeq interleave = (interleave(list(2, 4), list(3, 6), list(4, 8), list(5, 10), list(6, 12)));
assertEquals(vector(2, 3, 4, 5, 6, 4, 6, 8, 10, 12), interleave);
ISeq mapcat = (mapcat(fn(n, m, list(n * m)), list(2, 4), list(3, 6)));
assertEquals(list(6, 24), mapcat);
ISeq pmap = (pmap(fn(n, m, n * m), list(2, 4), list(3, 6)));
assertEquals(list(6, 24), pmap);
}
@Test
public void interactingWithClojure() throws Exception {
eval("(def v [1 2 3 4 5])");
IPersistentVector v = eval("v");
IFn times = defn("times", fn(n, m, n * m));
Integer factorial = 120;
assertEquals(factorial, (reduce(times, 1, v)));
assertEquals(factorial, eval(reduce, times, 1, v));
assertEquals(factorial, eval("(reduce times 1 v)"));
IFn isOdd = eval("odd?");
ISeq odd = list(1L, 3L, 5L);
assertEquals(odd, (filter(isOdd, v)));
assertEquals(odd, eval(filter, isOdd, v));
assertEquals(odd, eval("(filter odd? v)"));
IFn isEven = defn("is-even?", toIFn(Lambda.λ(n, n % 2 == 0)));
ISeq even = list(2L, 4L);
assertEquals(even, (filter(isEven, v)));
assertEquals(even, eval(filter, isEven, v));
assertEquals(even, eval("(filter is-even? v)"));
}
@Test
public void interactingWithClojureEngine() throws Exception {
eval("(def v [1 2 3 4 5])");
defn("times", fn(n, m, n * m));
Integer factorial = 120;
assertEquals(factorial, clj.eval("(reduce times 1 v)"));
ISeq odd = list(1L, 3L, 5L);
assertEquals(odd, clj.eval("(filter odd? v)"));
defn("is-even?", toIFn(Lambda.λ(n, n % 2 == 0)));
ISeq even = list(2L, 4L);
assertEquals(even, clj.eval("(filter is-even? v)"));
}
@Test
public void convertFnToIFn() throws Exception {
IFn fn = toIFn(Lambda.λ(s, s.toUpperCase()));
assertEquals("HELLO", fn.invoke("hello"));
}
@Test
public void convertFnToIFnKeepsDefaultValues() throws Exception {
IFn fn = toIFn(Lambda.λ(s = "world", s.toUpperCase()));
assertEquals("WORLD", fn.invoke());
}
@Test(expected = IllegalArgumentException.class)
public void convertedFnToIFnThrowsArityWhenCalledWithTooFewArguments() throws Exception {
IFn fn = toIFn(Lambda.λ(s, s.toUpperCase()));
fn.invoke();
}
@Test(expected = IllegalArgumentException.class)
public void convertedFnToIFnThrowsArityWhenCalledWithTooManyArguments() throws Exception {
IFn fn = toIFn(Lambda.λ(s, s.toUpperCase()));
fn.invoke("hello", "world");
}
@Test
public void convertFnToIFnHandlesWithMeta() throws Exception {
IFn fn = (IFn) toIFn(Lambda.λ(s = "world", s.toUpperCase()));
defn("to-upper-case", fn);
IFn fnWithMeta = (IFn) clj.eval("(with-meta to-upper-case {:hello \"world\"})");
assertNotSame(fn, fnWithMeta);
defn("to-upper-case-with-meta", fnWithMeta);
IPersistentMap meta = (IPersistentMap) clj.eval("(meta to-upper-case-with-meta)");
assertEquals(1, meta.count());
assertEquals("world", meta.valAt(clj.eval(":hello")));
assertEquals("HELLO", fnWithMeta.invoke("hello"));
assertEquals("WORLD", fnWithMeta.invoke());
}
@Test
public void convertIFnToFn() throws ScriptException {
IFn star = eval("*");
assertEquals(6L, toFn2(star).call(2, 3));
}
@SuppressWarnings("unchecked")
@Test
public void interactingWithEnumerableJava() throws Exception {
APersistentVector v = (APersistentVector) clj.eval("[1 2 3 4 5]");
IFn star = (IFn) clj.eval("*");
assertEquals(120L, Enumerable.inject(v, 1, toFn2(star)));
}
@Test
public void interactingWithJRuby() throws Exception {
ScriptEngine rb = JRubyTest.getJRubyEngine();
RubyProc proc = (RubyProc) rb.eval(":*.to_proc");
IFn times = toIFn(LambdaJRuby.toFn2(proc));
assertEquals(6L, times.invoke(2, 3));
defn("times-rb", times);
assertEquals(120L, clj.eval("(reduce times-rb 1 [1, 2, 3, 4, 5])"));
}
@Test
public void interactingWithJavaScript() throws Exception {
ScriptEngine js = JavaScriptTest.getJavaScriptEngine();
Function f = (Function) js.eval("var f = function(n, m) { return n * m; }; f;");
IFn times = toIFn(LambdaJavaScript.toFn2(f));
assertEquals(6.0, times.invoke(2, 3));
defn("times-js", times);
assertEquals(120.0, clj.eval("(reduce times-js 1 [1, 2, 3, 4, 5])"));
}
@Test
public void interactingWithGroovy() throws Exception {
ScriptEngine groovy = GroovyTest.getGroovyEngine();
Closure<?> closure = (Closure<?>) groovy.eval("{ n, m -> n * m }");
IFn times = toIFn(LambdaGroovy.toFn2(closure));
assertEquals(6, times.invoke(2, 3));
defn("times-groovy", times);
assertEquals(120L, clj.eval("(reduce times-groovy 1 [1, 2, 3, 4, 5])"));
}
@SuppressWarnings("unchecked")
@Test
public void interactingWithScala() throws Exception {
ScalaInterpreter scala = ScalaTest.getScalaInterpreter();
Function2<Long, Long, Long> f = (Function2<Long, Long, Long>) scala.eval("(n: Long, m: Long) => n * m");
IFn times = toIFn(LambdaScala.toFn2(f));
assertEquals(6L, times.invoke(2L, 3L));
defn("times-scala", times);
assertEquals(120L, clj.eval("(reduce times-scala 1 [1, 2, 3, 4, 5])"));
}
@Before
public void initEngine() {
clj = getClojureEngine();
}
public static ScriptEngine getClojureEngine() {
ScriptEngineManager manager = new ScriptEngineManager();
return manager.getEngineByName("Clojure");
}
@Before
public void ensureClojureIsInitialzed() {
LambdaClojure.init();
}
}