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())); } [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 (10 20 30))"); Assert.Equal(((Integer) e).value, 10); e = new Executor().eval("(cdr (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 (2 3))"); Assert.Equal(string.Format("{0}", e), "(1 2 3)"); e = new Executor().eval("(length (cons 1 (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"); } [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 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 pi 3.1415) 1) //"""); //Assert.Equal(string.Format("{0}", r), "1"); } } }