using Xunit; using Lisp_Environment = Jellyfin.Plugin.SmartPlaylist.Lisp.Environment; using Lisp_Boolean = Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler.Boolean; using Lisp_Object = Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler.Object; using Jellyfin.Plugin.SmartPlaylist.Lisp; using Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler; namespace Tests { public class O { int _i; bool _b; public O(int i, bool b) { _i = i; _b = b; } public int i { get => _i; } public bool b { get => _b; } } public class Test { [Fact] public static void TestTokenizer() { StringTokenStream sts = StringTokenStream.generate("(\"some literal string\" def ghj +100 -+300 1 >= ++ !=)"); Assert.Equal(sts.Get().value, "("); Assert.Equal(sts.Get().value, "\""); Assert.Equal(sts.Get().value, "some"); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "literal"); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "string"); Assert.Equal(sts.Get().value, "\""); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "def"); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "ghj"); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "+"); Assert.Equal(sts.Get().value, "100"); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "-"); Assert.Equal(sts.Get().value, "+"); Assert.Equal(sts.Get().value, "300"); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "1"); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, ">"); Assert.Equal(sts.Get().value, "="); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "+"); Assert.Equal(sts.Get().value, "+"); Assert.Equal(sts.Get().value, " "); Assert.Equal(sts.Get().value, "!"); Assert.Equal(sts.Get().value, "="); Assert.Equal(sts.Get().value, ")"); sts.Commit(); Assert.Equal(sts.Available(), 0); } [Fact] public static void TestParser() { string program = "(+ 1 (* 2 3))"; StringTokenStream sts = StringTokenStream.generate(program); Parser p = new Parser(sts); Assert.Equal(program, string.Format("{0}", p.parse())); program = "(haskeys o \"i\" \"b\")"; sts = StringTokenStream.generate(program); p = new Parser(sts); Assert.Equal(program, string.Format("{0}", p.parse())); //program = "(* 2.4 2)"; //sts = StringTokenStream.generate(program); //p = new Parser(sts); //Assert.Equal(program, p.parse().ToString()); } [Fact] public static void TestFunctions() { IList> cases = new List>(); Expression e = new Executor().eval("(+ 10 20)"); Assert.Equal(((Integer) e).value, 30); e = new Executor().eval("(> 1 2)"); Assert.Equal(((Lisp_Boolean) e).value, false); e = new Executor().eval("(if (> 1 2) 3 4)"); Assert.Equal(((Integer) e).value, 4); e = new Executor().eval("(begin (define x 1) x)"); Assert.Equal(((Integer) e).value, 1); e = new Executor().eval("(apply + (1 2))"); Assert.Equal(((Integer) e).value, 3); e = new Executor().eval("(car (list 10 20 30))"); Assert.Equal(((Integer) e).value, 10); e = new Executor().eval("(cdr (list 10 20 30))"); Assert.Equal(string.Format("{0}", e), "(20 30)"); e = new Executor().eval("(cons 1 3)"); Assert.Equal(string.Format("{0}", e), "(1 3)"); e = new Executor().eval("(cons 1 (list 2 3))"); Assert.Equal(string.Format("{0}", e), "(1 2 3)"); e = new Executor().eval("(length (cons 1 (list 2 3)))"); Assert.Equal(string.Format("{0}", e), "3"); e = new Executor().eval("(>= 2 2)"); Assert.Equal(string.Format("{0}", e), "t"); e = new Executor().eval("(> 2 2))"); Assert.Equal(string.Format("{0}", e), "nil"); e = new Executor().eval("(and 2 3 4)"); Assert.Equal("4", e.ToString()); e = new Executor().eval("(and 2 nil 4)"); Assert.Equal("nil", e.ToString()); e = new Executor().eval("(or 2 nil 4)"); Assert.Equal("2", e.ToString()); e = new Executor().eval("(or nil 4)"); Assert.Equal("4", e.ToString()); e = new Executor().eval("(= (list 1 2) (list 1 2))"); Assert.Equal(e.ToString(), "t"); e = new Executor().eval("(= (list 1 2 3) (list 1 2))"); Assert.Equal(e.ToString(), "nil"); } [Fact] public static void ObjectTest() { Executor e = new Executor(); Expression r; e.environment.Set("o", new Lisp_Object(new O(5, false))); r = e.eval("(haskeys o 'i' 'b')"); Assert.Equal(((Lisp_Boolean)r).value, true); r = e.eval("(getitems o 'i' 'b')"); Assert.Equal(string.Format("{0}", r), "(5 nil)"); } [Fact] public static void ScalarTest() { Executor e = new Executor(); Expression r; r = e.eval("(* 2 2)"); Assert.Equal("4", r.ToString()); } [Fact] public static void ProcedureTest() { Executor e = new Executor(); Expression r; r = e.eval("((lambda (a) (* a a)) 2)"); Assert.Equal(string.Format("{0}", r), "4"); r = e.eval("(begin (define mull (lambda (a) (* a a))) (mull 3))"); Assert.Equal(string.Format("{0}", r), "9"); r = e.eval("(begin (define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1)))))) (fact 10))"); Assert.Equal(string.Format("{0}", r), "3628800"); r = e.eval("(begin (define find (lambda (item list) (if (= list ()) nil (if (= item (car list)) (car list) (find item (cdr list)))))) (find 3 (list 1 2 3 4)))"); Assert.Equal(string.Format("{0}", r), "3"); e = new Executor(); r = e.eval("(begin (define find (lambda (item list) (if (= list ()) nil (if (= item (car list)) (car list) (find item (cdr list)))))) (find 0 (list 1 2 3 4)))"); Assert.Equal(string.Format("{0}", r), "nil"); e = new Executor(); r = e.eval(@" (begin (define map (lambda (fc l) (if (= l ()) () (cons (fc (car l)) (map fc (cdr l)))))) (define multwo (lambda (x) (* 2 x))) (map multwo (list 1 2 3))) "); Assert.Equal(string.Format("{0}", r), "(2 4 6)"); } [Fact] public static void DefaultEnvironmentTest() { Executor e = new Executor(new DefaultEnvironment()); Assert.Equal("nil", e.eval("(find 0 (list 1 2 3 4))").ToString()); Assert.Equal("3", e.eval("(find 3 (list 1 2 3 4))").ToString()); } } }