~technomancy/fennel

ref: 83ccb3c3345e84c59303fcd97db257d533905399 fennel/test/failures.fnl -rw-r--r-- 6.7 KiB
83ccb3c3Phil Hagelberg Fix apropos tests for other Lua versions. 6 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
(local l (require :test.luaunit))
(local fennel (require :fennel))

(local failures {
  "\n\n(+))" "unknown:3: unexpected closing delimiter )"
  "\n\n(let [f (lambda []\n(local))] (f))" "4: expected name and value"
  "\n\n(let [x.y 9] nil)" "unknown:3: unexpected multi"
  "\n(when)" "Compile error in 'when' unknown:2"
  "((fn [] (require-macros \"test.macros\") (global x1 (->1 99 (+ 31)))))
      (->1 23 (+ 1))" "unknown global in strict mode"
  "()" "expected a function, macro, or special"
  "(789)" "cannot call literal value"
  "(do\n\n\n(each \n[x (pairs {})] (when)))" "when' unknown:5:"
  "(do\n\n\n(each \n[x 34 (pairs {})] 21))" "5: unable to bind number 34"
  "(each [k v (pairs {})] (BAD k v))" "BAD"
  "(f" "unknown:1: expected closing delimiter )"
  "(fn [12])" "expected symbol for function parameter"
  "(fn [:huh] 4)" "expected symbol for function parameter"
  "(fn []\n(for [32 34 32] 21))" "2: unable to bind number"
  "(fn [] [...])" "unexpected vararg"
  "(fn [false] 4)" "expected symbol for function parameter"
  "(fn [nil] 4)" "expected symbol for function parameter"
  "(fn global [] 1)" "overshadowed"
  "(fn global-caller [] (hey))" "unknown global"
  "(fn)" "expected parameters"
  "(global + 1)" "overshadowed"
  "(global - 1)" "overshadowed"
  "(global // 1)" "overshadowed"
  "(global 48 :forty-eight)" "unable to bind number 48"
  "(global good (fn [] nil)) (good) (BAD)" "BAD"
  "(global let 1)" "overshadowed"
  "(hey)" "unknown global"
  "(lambda x)" "expected arg list"
  "(let [:x 1] 1)" "unable to bind"
  "(let [[a & c d] [1 2]] c)" "rest argument before last parameter"
  "(let [b 9\nq (.)] q)" "2: expected table argument"
  "(let [bl 8 a bcd] nil)" "unknown global"
  "(let [false 1] 9)" "unable to bind boolean false"
  "(let [global 1] 1)" "overshadowed"
  "(let [next #(next $)] print)" "aliased by a local"
  "(let [nil 1] 9)" "unable to bind"
  "(let [pairs #(pairs $)] pairs)" "aliased by a local"
  "(let [t []] (set t.47 :forty-seven))" "can't start multisym segment with a digit: t.47"
  "(let [t []] (set t.:x :y))" "malformed multisym: t.:x"
  "(let [t []] (set t:.x :y))" "malformed multisym: t:.x"
  "(let [t []] (set t::x :y))" "malformed multisym: t::x"
  "(let [t {:a 1}] (+ t.a BAD))" "BAD"
  "(let [x 1 y] 8)" "expected even number of name/value bindings"
  "(let [x 1] (set-forcibly! x 2) (set x 3) x)" "expected var"
  "(let [x 1])" "expected body"
  "(let [x {:foo (fn [self] self.bar) :bar :baz}] x:foo)" "multisym method calls may only be in call position"
  "(let [x {:y {:foo (fn [self] self.bar) :bar :baz}}] x:y:foo)" "method must be last component of multisym: x:y:foo"
  "(local 47 :forty-seven)" "unable to bind number 47"
  "(local a-b 1) (global [a_b] [2])" "global a_b conflicts with local"
  "(local a-b 1) (global a_b 2)" "global a_b conflicts with local"
  "(local a~b 3)" "illegal character: ~"
  "(local ipairs #(ipairs $))" "aliased by a local"
  "(macros {:m (fn [t] `(each [mykey (pairs ,t)] (print mykey)))}) (m [])" "tried to bind mykey without gensym"
  "(macros {:m (fn [t] `(fn [xabc] (+ xabc 9)))}) ((m 4))" "tried to bind xabc without gensym"
  "(macros {:m (fn [y] `(let [x 1] (+ x ,y)))}) (m 4)" "tried to bind x without gensym"
  "(match [1 2 3] [a & b c] nil)" "rest argument before last parameter"
  "(not true false)" "expected one argument"
  "(print @)" "illegal character: @"
  ;; TODO: this should be an error in 1.0
  ;; "(local abc&d 19)" "illegal character: &"
  "(set [a b c] [1 2 3]) (+ a b c)" "expected local"
  "(set a 19)" "error in 'a' unknown:1: expected local"
  "(set)" "Compile error in 'set' unknown:1: expected name and value"
  "(x(y))" "expected whitespace before opening delimiter ("
  "(x[1 2])" "expected whitespace before opening delimiter ["
  "(fn abc:def [x] (+ x 2))" "unexpected multi symbol abc:def"
  "(macros {:foo {:bar (fn [] `(print :test))}})" "expected each macro to be function"
  "(macro m [] (getmetatable :foo)) (m)" "Illegal metatable"
  "(import-macros test :test.macros) (test.asdf)" "macro not found in imported macro module"
  "(import-macros {: asdf} :test.macros)" "macro asdf not found in module test.macros"
  "(with-open [(x y z) (values 1 2 3)])" "with-open only allows symbols in bindings"
  "#[$ $...] 1 2 3" "$ and $... in hashfn are mutually exclusive"
  "(eval-compiler (assert-compile false \"oh no\" 123))" "oh no"
  "(partial)" "expected a function"
  "(#)" "expected one argument"
  ;; PUC is ridiculous in what it accepts in a string
  "\"\\!\"" (if (or (not= _VERSION "Lua 5.1") _G.jit) "Invalid string")
  ;; macros should shadow locals as values, not just when calling:
  "(let [t {:b 2}] (import-macros t :test.macros) t.b)" "tried to reference a macro"
  ;; strict mode applies to macro modules too
  "(import-macros t :test.bad.unknown-global)" "unknown global in strict mode"
})

(fn test-failures []
  (each [code expected-msg (pairs failures)]
    (let [(ok? msg) (pcall fennel.compileString code
                           {:allowedGlobals ["pairs" "next" "ipairs"]
                            :unfriendly true})]
      (l.assertFalse ok? (.. "Expected compiling " code " to fail."))
      (l.assertStrContains msg expected-msg))))

(fn test-macro []
  (let [code "(import-macros {: fail-one} :test.macros) (fail-one 1)"
        (ok? msg) (pcall fennel.compileString code)]
    (l.assertStrContains msg "test/macros.fnl:2: oh no")
    ;; sometimes it's "in function f" and sometimes "in upvalue f"
    (l.assertStrMatches msg ".*test/macros.fnl:2: in %w+ 'def'.*")
    (l.assertStrMatches msg ".*test/macros.fnl:6: in %w+ 'abc'.*")))

;; automated tests for suggestions are rudimentary because the usefulness of the
;; output is so subjective. to see a full catalog of suggestions, run the script
;; test/bad/friendly.sh and review that output.
(fn test-suggestions []
  (let [(_ msg) (pcall fennel.dofile "test/bad/set-local.fnl")
        (_ parse-msg) (pcall fennel.dofile "test/bad/odd-table.fnl")
        (_ assert-msg) (pcall fennel.eval
                              "(eval-compiler (assert-compile nil \"bad\" 1))")
        (_ msg4) (pcall fennel.eval "(abc] ;; msg4")
        (_ msg5) (pcall fennel.eval "(let) ;; msg5")]
    ;; show the raw error message
    (l.assertStrContains msg "expected var x")
    ;; offer suggestions
    (l.assertStrContains msg "Try declaring x using var")
    ;; show the code and point out the identifier at fault
    (l.assertStrContains msg "(set x 3)")
    (l.assertStrContains msg "\n     ^")
    ;; parse error
    (l.assertStrContains parse-msg "{:a 1 :b 2 :c}")
    ;; non-table AST in assertion
    (l.assertStrContains assert-msg "bad")
    ;; source should be part of the error message
    (l.assertStrContains msg4 "msg4")
    (l.assertStrContains msg5 "msg5")))

{: test-failures : test-suggestions : test-macro}