diff options
Diffstat (limited to 'lab4/src/test/scala')
-rw-r--r-- | lab4/src/test/scala/jsy/lab4/Lab4Spec.scala | 217 | ||||
-rw-r--r-- | lab4/src/test/scala/jsy/tester/JavascriptyTester.scala | 23 |
2 files changed, 240 insertions, 0 deletions
diff --git a/lab4/src/test/scala/jsy/lab4/Lab4Spec.scala b/lab4/src/test/scala/jsy/lab4/Lab4Spec.scala new file mode 100644 index 0000000..0c05aab --- /dev/null +++ b/lab4/src/test/scala/jsy/lab4/Lab4Spec.scala @@ -0,0 +1,217 @@ +package jsy.lab4 + +import org.scalatest.flatspec.AnyFlatSpec +import Parser.parse +import ast._ +import Lab4._ + +class Lab4StudentSpec extends AnyFlatSpec { +} + +class Lab4Spec extends AnyFlatSpec { + + /***** hastype Tests *****/ + { + val xtype = TNumber + val tenvx = extend(Map.empty, "x", xtype) + + "TypeVar" should "perform TypeVar" in { + assertResult(xtype) { + hastype(tenvx, Var("x")) + } + } + + "hastype/call-non-fun" should "yield a static type error" in { + for (e <- List( + ("3(4)"), + ("3(true)"), + ("(10 + 4 + 7 - true * 3)(true)"), + ("((function (x: number) { return 0 })(10))(true)") + )) { + intercept[StaticTypeError] { + inferType(e) + } + } + } + + "hastype/arithmetic" should "type check JavaScripty arithmetic expressions according to the lab spec" in { + for ((e,t) <- List( + ("3 <= 4", TBool), + ("'3' < '4'", TBool), + ("3 + 4", TNumber), + ("'3' + '4'", TString), + ("3 - 4", TNumber), + ("3 * 4 + 1 - 4 / 12", TNumber), + ("undefined", TUndefined) + )) { + assertResult(t){ inferType(e) } + } + } + + "hastype/functions-objects" should "type check JavaScripty function-object expressions according to the lab spec" in { + val f1 = parse("function (x: number) { return x }") + val o1 = parse("{f: 3, g: true, h: undefined}") + for ((e,t) <- List( + (f1, TFun(List(("x", TNumber)), TNumber)), + (o1, TObj(Map("f" -> TNumber, "g" -> TBool, "h" -> TUndefined))), + (Call(f1, List(N(3))), TNumber), + (GetField(o1, "g"), TBool) + )) { + assertResult(t){ inferType(e) } + } + } + + "hastype/simple-programs" should "type check JavaScripty programs according to the lab spec" in { + assertResult(TNumber) { inferType( + """ + const w = function w(y: number): number { return y === 0 ? 0.1 : y + w(y - 1) }; + w(3) + """ + )} + assertResult(TNumber) { inferType( + """ + const x = 1; + const g = function(y: number) { return x; }; + const h = function(x: number) { return g(2); }; + h(3) + """ + )} + } + + "hastype/recursive-programs" should "type check recursive JavaScripty programs according to the lab spec" in { + assertResult(TNumber) { inferType( + """ + const factorial = function f(n: number): number { + return n === 0 ? 1 : n * f(n - 1) + } + factorial(4) + """ + )} + } + } + + + /***** substitute Tests *****/ + { + "substitute" should "substitute a value for uses of a variable" in { + for ((r,e,v,x) <- List( + ("const x = 3; x", "const x = x; x", "3", "x"), + ("function (x: number) { return x + y }", "function (x: number) { return x + y }", "3", "x"), + ("function x(y: number) { return x + y }", "function x(y: number) { return x + y }", "3", "x") + )) { + assertResult(parse(r)){ substitute(parse(v), x, parse(e)) } + } + } + } + + + /***** step Tests *****/ + { + "step" should "perform one step of evaluation according to the lab spec" in { + for ((r, s) <- List ( + ("1", "4 - 3"), + ("3 + 4", "1, 3 + 4"), + ("(3) + 3", "(1 + 2) + 3"), + ("(2) + (3 / 4)", "(1 * 2) + (3 / 4)"), + ("(2 + 4)", "true ? (2 + 4) : (1 + 1)"), + ("const x = 2; x", "const x = 1 + 1; x"), + ("2", "const x = 2; x"), + ("3 + 3 + 4 + 5", "1 + 2 + 3 + 4 + 5"), + ("3", "(function (x: number) { return x })(3)"), +// ("3 + 1", "(function (x: number) { return x })(3 + 1)") + )) { + assertResult(parse(r)){oneStep(s)} + } + } + + "step/call-non-fun" should "yield a stuck error" in { + for (e <- List( + ("3(4)"), + ("3(true)"), + ("((function (x: number) { return 0 })(10))(true)") + )) { + intercept[StuckError] { iterateStep(e) } + } + } + + "step/simple-programs" should "evaluate JavaScripty programs according to the lab spec" in { + assertResult(N(6.1)) { iterateStep( + """ + const w = function w(y: number) { return y === 0 ? 0.1 : y + w(y - 1) } + w(3) + """ + )} + assertResult(N(1)) { iterateStep( + """ + const x = 1; + const g = function(y: number) { return x; }; + const h = function(x: number) { return g(2); }; + h(3) + """ + )} + assertResult(N(1.1)) { iterateStep( + """ + const g = function(x: number) { return function(y: number) { return x - y } }; + g(1)(0) + 0.1 + """ + )} + assertResult(N(6)) { iterateStep( + """ + const plus = function(x: number, y: number) { return x + y }; + plus(1 + 2, 3) + """ + )} + assertResult(N(6)) { iterateStep( + """ + const plus = function(x: number) { return function(y: number) { return x + y } }; + plus(1 + 2)(3) + """ + )} + } + + "step/recursive-programs" should "evaluate recursive JavaScripty programs according to the lab spec" in { + assertResult(N(6)) { iterateStep( + """ + const factorial = function f(n: number) { + return n === 0 ? 1 : n * f(n - 1) + } + factorial(3) + """ + )} + assertResult(S("aaa")) { iterateStep( + """ + const repeat = function(s: string) { + return function loop(n: number) { + return n === 0 ? "" : s + loop(n - 1) + } + } + repeat("a")(3) + """ + )} + } + + "step/objects" should "evaluate JavaScripty objects according to the lab spec" in { + assertResult(N(7.0)) { iterateStep( + """ + const pair = function(x: number, y: number) { + return {x: x, y: y} + }; + const p = pair(3, 4); + p.x + p.y + """ + )} + assertResult(N(5.0)) { iterateStep( + """ + const suspension = function(f: (a: number) => number, n: number) { + return {f: f, n: n} + }; + const s = suspension((a: number) => a + 1, 4); + s.f(s.n) + """ + )} + } + } + +} + +class Lab4JsyTests extends jsy.tester.JavascriptyTester(None, "lab4", Lab4) diff --git a/lab4/src/test/scala/jsy/tester/JavascriptyTester.scala b/lab4/src/test/scala/jsy/tester/JavascriptyTester.scala new file mode 100644 index 0000000..48e789c --- /dev/null +++ b/lab4/src/test/scala/jsy/tester/JavascriptyTester.scala @@ -0,0 +1,23 @@ +package jsy.tester +import java.io.File +import jsy.util.JsyApplication +import org.scalatest._ +import flatspec._ + +/* + * A ScalaTest interface for running system tests. + */ +class JavascriptyTester(rootPrefix: Option[String], testDirectory: String, jsy: JsyApplication) extends AnyFlatSpec { + val testResources = s"src${File.separator}test${File.separator}resources" + val testPrefix = rootPrefix match { + case None => testResources + case Some(p) => s"${p}${File.separator}${testResources}" + } + val testPath = s"${testPrefix}${File.separator}${testDirectory}" + jsy.test(new File(testPath)) { case (in,ans,assertion) => + s"eval on ${in}" should s"match ${ans}" in { + val (b: Boolean, msg: String) = assertion + assert(b, msg) + } + } +} |