private/tests/eval.ss
(module eval mzscheme
  (require (planet "test.ss" ("schematics" "schemeunit.plt" 2 1))
           (planet "test.ss" ("dherman" "test.plt" 1 2))
           (planet "graphical-ui.ss" ("schematics" "schemeunit.plt" 2 1))
           (planet "text-ui.ss" ("schematics" "schemeunit.plt" 2 1))
           (planet "io.ss" ("dherman" "io.plt" 1 8))
           (lib "string.ss" "srfi" "13")
           (lib "string.ss")
           "../../eval.ss"
           "../../config.ss")

  (define test-ns (make-javascript-namespace))

  (enable-let-expressions? #t)

  (define-simple-check (check-output* expected lines)
    (reset-javascript-namespace! test-ns)
    (let ([actual (with-output-to-string (eval-javascript-string (string-join lines "\n") test-ns))])
      (andmap string=?
              expected
              (regexp-split #rx"[\r\n]+" (string-trim-both actual)))))

  (define-syntax check-output
    (syntax-rules ()
      [(_ expected lines ...)
       (check-output* expected (list lines ...))]))

  (define binding-tests
    (test-suite "binding tests"
      (test-case "top-level binding"
        (check-output '("true")
                      "var a = true;"
                      "print(a);"))
      (test-case "non-with lexical binding"
        (check-output '("true")
                      "(function(a){print(a)})(true)"))
      (test-case "non-with catch binding"
        (check-output '("true")
                      "try{throw true}catch(a){print(a)}"))
      (test-case "non-with let binding"
        (check-output '("true")
                      "let (a = true){print(a)}"))
      (test-case "non-with lexical shadowing of top-level"
        (check-output '("true")
                      "var a = false;"
                      "(function(a){print(a)})(true);"))
      (test-case "non-with lexical shadowing of lexical"
        (check-output '("true")
                      "(function(a){(function(a){print(a)})(true)})(false);"))
      (test-case "non-with lexical shadowing of catch"
        (check-output '("true")
                      "try{throw false}catch(a){(function(a){print(a)})(true)}"))
      (test-case "non-with lexical shadowing of let"
        (check-output '("true")
                      "let (a = false){(function(a){print(a)})(true)}"))
      (test-case "non-with catch shadowing of top-level"
        (check-output '("true")
                      "var a = false"
                      "try{throw true}catch(a){print(a)}"))
      (test-case "non-with catch shadowing of lexical"
        (check-output '("true")
                      "(function(a){try{throw true}catch(a){print(a)}})(false)"))
      (test-case "non-with catch shadowing of catch"
        (check-output '("true")
                      "try{throw false}catch(a){try{throw true}catch(a){print(a)}}"))
      (test-case "non-with catch shadowing of let"
        (check-output '("true")
                      "let (a = false){try{throw true}catch(a){print(a)}}"))
      #;(test-case "non-with let shadowing of top-level"
        (check-output '("true")
                      ))
      #;(test-case "non-with let shadowing of lexical"
        (check-output '("true")
                      ))
      #;(test-case "non-with let shadowing of catch"
        (check-output '("true")
                      ))
      #;(test-case "non-with let shadowing of let"
        (check-output '("true")
                      ))
      (test-case "with binding"
        (check-output '("true")
                      "var o = {a:true}"
                      "with(o){print(a)}"))
      (test-case "with shadowing of top-level"
        (check-output '("true")
                      "var o = {a:true}"
                      "var a = false"
                      "with(o){print(a)}"))
      (test-case "with shadowing of lexical"
        (check-output '("true")
                      "var o = {a:true};"
                      "(function(a){with(o){print(a)}})(false);"))
      (test-case "with shadowing of with"
        (check-output '("true")
                      "var o1 = {a:false};"
                      "var o2 = {a:true};"
                      "with(o1){with(o2){print(a)}}"))
      (test-case "with shadowing of catch"
        (check-output '("true")
                      "var o = {a:true}"
                      "try{throw false}catch(a){with(o){print(a)}}"))
      #;(test-case "with shadowing of let"
        (check-output '("true")
                      ))
      (test-case "lexical shadowing of with"
        (check-output '("true")
                      "var o = {a:false}"
                      "with(o) {(function(a){print(a)})(true)}"))
      (test-case "catch shadowing of with"
        (check-output '("true")
                      "var o = {a:false}"
                      "with(o){try{throw true}catch(a){print(a)}}"))
      #;(test-case "let shadowing of with"
        (check-output '("true")
                      ))
      (test-case "with shadowing of lexical shadowing of with"
        (check-output '("true")
                      "var o1 = {a:1}"
                      "var o2 = {a:true}"
                      "with(o1){(function(a){with(o2){print(a)}})(2)}"))
      (test-case "with shadowing of catch shadowing of with"
        (check-output '("true")
                      "var o1 = {a:1}"
                      "var o2 = {a:true}"
                      "with(o1){try{throw 2}catch(a){with(o2){print(a)}}}"))
      #;(test-case "with shadowing of let shadowing of with"
        (check-output '("true")
                      ))
      (test-case "with shadowing of catch shadowing of lexical"
        (check-output '("true")
                      "var o = {a:true};"
                      "(function(a){try{throw 1}catch(a){with(o){print(a)}}})(2)"))
      (test-case "lexical shadowing of catch shadowing of with"
        (check-output '("true")
                      "var o = {a:1};"
                      "try{throw 2}catch(a){(function(a){print(a)})(true)}"))
      #;(test-case "with shadowing of catch shadowing of let"
        (check-output '("true")
                      ))
      (test-case "lexical shadowing of with shadowing of catch"
        (check-output '("true")
                      "var o = {a:1};"
                      "try{throw 2}catch(a){with(o){(function(a){print(a)})(true)}}"))
      (test-case "catch shadowing of with shadowing of lexical"
        (check-output '("true")
                      "var o = {a:1};"
                      "(function(a){with(o){try{throw true}catch(a){print(a)}}})(2);"))
      #;(test-case "let shadowing of with shadowing of catch"
        (check-output '("true")
                      ))
      #;(test-case "let shadowing of with shadowing of lexical"
        (check-output '("true")
                      ))
      #;(test-case "lexical shadowing of with shadowing of let"
        (check-output '("true")
                      ))
      #;(test-case "catch shadowing of with shadowing of let"
        (check-output '("true")
                      ))
      (test-case "with shadowing of lexical shadowing of catch"
        (check-output '("true")
                      "var o = {a:true};"
                      "try{throw 1}catch(a){(function(a){with(o){print(a)}})(2)}"))
      #;(test-case "with shadowing of lexical shadowing of let"
        (check-output '("true")
                      ))
      (test-case "catch shadowing of lexical shadowing of with"
        (check-output '("true")
                      "var o = {a:1};"
                      "with(o){(function(a){try{throw true}catch(a){print(a)}})(2)}"))
      #;(test-case "let shadowing of lexical shadowing of with"
        (check-output '("true")
                      ))
      (test-case "mutation of with-bound variable"
        (check-output '("true")
                      "var o = {a:false}"
                      "with(o) {o.a=true;print(a)}"))
      (test-case "temporary with shadowing of top-level"
        (check-output '("true")
                      "var a = true;"
                      "var o = {a:false}"
                      "with(o) {delete o.a;print(a)}"))
      (test-case "temporary with shadowing of lexical"
        (check-output '("true")
                      "var o = {a:false};"
                      "(function(a){with(o){delete o.a;print(a)}})(true);"))
      (test-case "temporary with shadowing of with"
        (check-output '("true")
                      "var o1 = {a:true};"
                      "var o2 = {a:false};"
                      "with(o1){with(o2){delete o2.a;print(a)}}"))
      (test-case "temporary with shadowing of catch"
        (check-output '("true")
                      "var o = {a:false};"
                      "try{throw true}catch(a){with(o){delete o.a;print(a)}}"))
      #;(test-case "temporary with shadowing of let"
        (check-output '("true")
                      ))
      (test-case "lexical shadowing of temporary with"
        (check-output '("true")
                      "var o = {a:false};"
                      "with(o){(function(a){delete o.a;print(a)})(true)}"))
      (test-case "catch shadowing of temporary with"
        (check-output '("true")
                      "var o = {a:false};"
                      "with(o){try{throw true}catch(a){delete o.a;print(a)}}"))
      #;(test-case "let shadowing of temporary with"
        (check-output '("true")
                      ))
      (test-case "temporary with shadowing of lexical shadowing of with"
        (check-output '("true")
                      "var o1 = {a:1};"
                      "var o2 = {a:2};"
                      "with(o1){(function(a){with(o2){delete o2.a;print(a)}})(true)}"))
      (test-case "temporary with shadowing of catch shadowing of with"
        (check-output '("true")
                      "var o1 = {a:1};"
                      "var o2 = {a:2};"
                      "with(o1){try{throw true}catch(a){with(o2){delete o2.a;print(a)}}}"))
      (test-case "with non-shadowing of lexical shadowing of with"
        (check-output '("true")
                      "var o1 = {a:false};"
                      "var o2 = {};"
                      "with(o1){(function(a){with(o2){print(a)}})(true)}"))
      (test-case "with non-shadowing of catch shadowing of with"
        (check-output '("true")
                      "var o1 = {a:false};"
                      "var o2 = {};"
                      "with(o1){try{throw true}catch(a){with(o2){print(a)}}}"))
      (test-case "with non-shadowing of let shadowing of with"
        (check-output '("true")
                      "var o1 = {a:false};"
                      "var o2 = {};"
                      "with(o1){let(a=true){with(o2){print(a)}}}"))
      ))

  (define prototype-tests
    (test-suite "prototype tests"
      (test-case "global object toString"
        (before
         (reset-javascript-namespace! test-ns)
         (check-equal? (eval-javascript-string "this.toString()" test-ns) "[object DrScheme]")))
      (test-case "vanilla object toString"
        (before
         (reset-javascript-namespace! test-ns)
         (check-equal? (eval-javascript-string "({}).toString()" test-ns) "[object Object]")))
      (test-case "dot method call sets up current this"
        (check-output '("true")
                      "({foo:function(){print(this.bar)},bar:true}).foo()"))
      (test-case "eval maintains current this"
        (check-output '("true")
                      "({foo:function(){eval('print(this.bar)')},bar:true}).foo()"))
      (test-case "eval gets same namespace"
        (check-output '("true")
                      "var a = true;"
                      "eval('print(this.a)');"))
      ))

  (define library-tests
    (test-suite "library tests"
      (test-case "String called as a function"
        (before
         (reset-javascript-namespace! test-ns)
         (check-equal? (eval-javascript-string "String(44)" test-ns) "44")))
      (test-case "Number called as a function"
        (before
         (reset-javascript-namespace! test-ns)
         (check-equal? (eval-javascript-string "Number('44')" test-ns) 44)))
      (test-case "Boolean called as a function"
        (before
         (reset-javascript-namespace! test-ns)
         (check-equal? (eval-javascript-string "Boolean(0)" test-ns) 'false)))
      (test-case "String.fromCharCode"
        (before
         (reset-javascript-namespace! test-ns)
         (check-equal? (eval-javascript-string "String.fromCharCode(104,101,108,108,111)" test-ns) "hello")))
      (test-case "Array with 0 args"
        (check-output '("")
                      "var a = new Array(); print(a)"))
      (test-case "Array with 1 arg - number"
        (check-output '(",,,,")
                      "var a = new Array(5); print(a)"))
      (test-case "Array with 1 arg - non-number"
        (check-output '("true")
                      "var a = new Array(true); print(a)"))
      (test-case "Array with two args"
        (check-output '("1,2")
                      "var a = new Array(1,2); print(a)"))
      (test-case "Array with three args"
        (check-output '("1,2,3")
                      "var a = new Array(1,2,3); print(a)"))
      ))

  (define eval-tests
    (test-suite "eval tests"
      binding-tests
      prototype-tests
      library-tests
      ))

  (provide eval-tests))