1before: |
2  base_module  = "debug"
3  this_module  = "std.debug"
4  global_table = "_G"
5
6  extend_base  = { "DEPRECATED", "DEPRECATIONMSG", "argcheck", "argerror",
7                   "argscheck", "extramsg_mismatch", "extramsg_toomany",
8                   "getfenv", "parsetypes", "resulterror", "setfenv", "say",
9                   "toomanyargmsg", "typesplit", "trace", "_setdebug" }
10
11  M = require (this_module)
12
13specify std.debug:
14- context when required:
15  - context by name:
16    - it does not touch the global table:
17        expect (show_apis {added_to=global_table, by=this_module}).
18          to_equal {}
19    - it does not touch the core debug table:
20        expect (show_apis {added_to=base_module, by=this_module}).
21          to_equal {}
22    - it contains apis from the core debug table:
23        expect (show_apis {from=base_module, not_in=this_module}).
24          to_contain.a_permutation_of (extend_base)
25
26  - context via the std module:
27    - it does not touch the global table:
28        expect (show_apis {added_to=global_table, by="std"}).
29          to_equal {}
30    - it does not touch the core debug table:
31        expect (show_apis {added_to=base_module, by="std"}).
32          to_equal {}
33
34
35- describe DEPRECATED:
36  - before: |
37      function runscript (body, name, args)
38        return luaproc (
39          "require '" .. this_module .. "'.DEPRECATED ('0', '" ..
40            (name or "runscript") .. "', function (...)" ..
41          " " .. body ..
42          " end) " ..
43          "('" .. table.concat (args or {}, "', '") .. "')"
44        )
45      end
46
47      f, badarg = init (M, this_module, "DEPRECATED")
48
49  - it returns a function:
50      expect (type (f ("0", "deprecated", nop))).to_be "function"
51      expect (f ("0", "deprecated", nop)).not_to_be (nop)
52  - context with deprecated function:
53    - it executes the deprecated function:
54        expect (runscript 'error "oh noes!"').to_contain_error "oh noes!"
55    - it passes arguments to the deprecated function:
56        expect (runscript ("print (table.concat ({...}, ', '))", nil,
57                           {"foo", "bar", "baz"})).to_output "foo, bar, baz\n"
58    - it returns deprecated function results: |
59        script = [[
60          DEPRECATED = require "std.debug".DEPRECATED
61          fn = DEPRECATED ("0", "fn", function () return "foo", "bar", "baz" end)
62          print (fn ())
63        ]]
64        expect (luaproc (script)).to_output "foo\tbar\tbaz\n"
65    - it writes a warning to stderr:
66        expect (runscript 'error "oh noes!"').
67          to_match_error "deprecated.*, and will be removed"
68    - it writes the version string to stderr:
69        expect (runscript 'error "oh noes!"').
70          to_contain_error "in release 0"
71    - it writes the call location to stderr: |
72        expect (runscript 'error "oh noes!"').
73          to_match_error "^%S+:1: "
74    - context with _DEBUG:
75      - before: |
76          script = [[
77            DEPRECATED = require "]] .. this_module .. [[".DEPRECATED
78            fn = DEPRECATED ("0", "fn", function () io.stderr:write "oh noes!\n" end)
79            fn ()  -- line 3
80            fn ()  -- line 4
81          ]]
82      - it warns every call by default:
83          expect (luaproc (script)).to_match_error "^%S+:3:.*deprecated"
84          expect (luaproc (script)).to_match_error "\n%S+:4:.*deprecated"
85      - it does not warn at all with _DEBUG set to false:
86          script = "_DEBUG = false " .. script
87          expect (luaproc (script)).not_to_match_error "%d:.*deprecated"
88      - it does not define the function with _DEBUG set to true: |
89          script = "_DEBUG = true " .. script
90          expect (luaproc (script)).to_contain_error.any_of {
91            ":3: attempt to call global 'fn'",
92            ":3: attempt to call a nil value (global 'fn')",
93          }
94      - it warns on every call with _DEBUG.deprecate unset:
95          script = "_DEBUG = {} " .. script
96          expect (luaproc (script)).to_match_error "^%S+:3:.*deprecated"
97          expect (luaproc (script)).to_match_error "\n%S+:4:.*deprecated"
98      - it does not warn at all with _DEBUG.deprecate set to false:
99          script = "_DEBUG = { deprecate = false } " .. script
100          expect (luaproc (script)).not_to_match_error "%d:.*deprecated"
101      - it warns on every call with _DEBUG.deprecate set to true: |
102          script = "_DEBUG = { deprecate = true } " .. script
103          expect (luaproc (script)).to_contain_error.any_of {
104            ":3: attempt to call global 'fn'",
105            ":3: attempt to call a nil value (global 'fn')",
106          }
107
108
109- describe DEPRECATIONMSG:
110  - before: |
111      function mkscript (lvl)
112        return [[
113          DEPRECATIONMSG = require "]] .. this_module .. [[".DEPRECATIONMSG
114          function fn ()
115            io.stderr:write (DEPRECATIONMSG ("42", "spec file", ]] .. lvl .. [[))
116          end
117          fn () -- line 5
118          fn () -- line 6
119        ]]
120       end
121
122       f = M.DEPRECATIONMSG
123
124  - it contains deprecating the release version:
125      expect (f ("41", "foo", 2)).to_contain "41"
126  - it contains the deprecation function name:
127      expect (f ("41", "some.module.fname", 2)).to_contain "some.module.fname"
128  - it appends an optional extra message:
129      expect (f ("41", "wuh", "ah boo", 2)).to_contain ", ah boo."
130  - it blames the given stack level:
131      expect (luaproc (mkscript (1))).to_match_error "^%S+:3:.*deprecated"
132      expect (luaproc (mkscript (2))).to_match_error "^%S+:5:.*deprecated"
133
134
135- describe resulterror:
136  - before: |
137      function mkstack (level)
138        return string.format ([[
139          _DEBUG = true                              -- line 1
140          local debug = require "std.debug"          -- line 2
141          function ohnoes ()                         -- line 3
142            debug.resulterror ("ohnoes", 1, nil, %s) -- line 4
143          end                                        -- line 5
144          function caller ()                         -- line 6
145            local r = ohnoes ()                      -- line 7
146            return "not a tail call"                 -- line 8
147          end                                        -- line 9
148          caller ()                                  -- line 10
149        ]], tostring (level))
150      end
151
152      f = M.resulterror
153
154  - it blames the call site by default: |
155      expect (luaproc (mkstack ())).to_contain_error ":4: bad result"
156  - it honors optional call stack level reporting: |
157      expect (luaproc (mkstack (1))).to_contain_error ":4: bad result"
158      expect (luaproc (mkstack (2))).to_contain_error ":7: bad result"
159  - it reports the calling function name:
160      expect (f ('expect', 1)).to_raise "'expect'"
161  - it reports the argument number: |
162      expect (f ('expect', 12345)).to_raise "#12345"
163  - it reports extra message in parentheses:
164      expect (f ('expect', 1, "extramsg")).to_raise " (extramsg)"
165
166
167- describe argerror:
168  - before: |
169      function mkstack (level)
170        return string.format ([[
171          _DEBUG = true                           -- line 1
172          local debug = require "std.debug"       -- line 2
173          function ohnoes ()                      -- line 3
174            debug.argerror ("ohnoes", 1, nil, %s) -- line 4
175          end                                     -- line 5
176          function caller ()                      -- line 6
177            local r = ohnoes ()                   -- line 7
178            return "not a tail call"              -- line 8
179          end                                     -- line 9
180          caller ()                               -- line 10
181        ]], tostring (level))
182      end
183
184      f, badarg = init (M, this_module,  "argerror")
185
186  - it diagnoses missing arguments:
187      pending "Lua 5.1 support is dropped"
188      expect (f ()).to_raise (badarg (1, "string"))
189      expect (f "foo").to_raise (badarg (2, "int"))
190  - it diagnoses wrong argument types:
191      pending "Lua 5.1 support is dropped"
192      expect (f (false)).to_raise (badarg (1, "string", "boolean"))
193      expect (f ("foo", false)).to_raise (badarg (2, "int", "boolean"))
194      expect (f ("foo", 1, false)).
195        to_raise (badarg (3, "string or nil", "boolean"))
196      expect (f ("foo", 1, "bar", false)).
197        to_raise (badarg (4, "int or nil", "boolean"))
198  - it diagnoses too many arguments:
199      pending "Lua 5.1 support is dropped"
200      expect (f ("foo", 1, "bar", 2, false)).to_raise (badarg (5))
201
202  - it blames the call site by default: |
203      expect (luaproc (mkstack ())).to_contain_error ":4: bad argument"
204  - it honors optional call stack level reporting: |
205      expect (luaproc (mkstack (1))).to_contain_error ":4: bad argument"
206      expect (luaproc (mkstack (2))).to_contain_error ":7: bad argument"
207  - it reports the calling function name:
208      expect (f ('expect', 1)).to_raise "'expect'"
209  - it reports the argument number: |
210      expect (f ('expect', 12345)).to_raise "#12345"
211  - it reports extra message in parentheses:
212      expect (f ('expect', 1, "extramsg")).to_raise " (extramsg)"
213
214
215- describe argcheck:
216  - before: |
217      Object = require 'std.object'
218      List = Object { _type = "List" }
219      Foo = Object { _type = "Foo" }
220
221      function fn (...) return M.argcheck ('expect', 1, ...) end
222
223      function mkstack (level, debugp)
224        return string.format ([[
225          _DEBUG = %s                                    -- line 1
226          local debug = require "std.debug"              -- line 2
227          function ohnoes (t)                            -- line 3
228            debug.argcheck ("ohnoes", 1, "table", t, %s) -- line 4
229          end                                            -- line 5
230          function caller ()                             -- line 6
231            local r = ohnoes "not a table"               -- line 7
232            return "not a tail call"                     -- line 8
233          end                                            -- line 9
234          caller ()                                      -- line 10
235        ]], tostring (debugp), tostring (level))
236      end
237
238      f, badarg = init (M, this_module,  "argcheck")
239
240  - it diagnoses missing arguments:
241      pending "Lua 5.1 support is dropped"
242      expect (f ()).to_raise (badarg (1, "string"))
243      expect (f "foo").to_raise (badarg (2, "int"))
244      expect (f ("foo", 1)).to_raise (badarg (3, "string"))
245  - it diagnoses wrong argument types:
246      pending "Lua 5.1 support is dropped"
247      expect (f (false)).to_raise (badarg (1, "string", "boolean"))
248      expect (f ("foo", false)).to_raise (badarg (2, "int", "boolean"))
249      expect (f ("foo", 1, false)).to_raise (badarg (3, "string", "boolean"))
250      expect (f ("foo", 1, "bar", 2, false)).
251        to_raise (badarg (5, "int or nil", "boolean"))
252  - it diagnoses too many arguments:
253      pending "Lua 5.1 support is dropped"
254      expect (f ("foo", 1, "bar", 2, 3, false)).to_raise (badarg (6))
255
256  - it blames the calling function by default: |
257      expect (luaproc (mkstack ())).to_contain_error ":7: bad argument"
258  - it honors optional call stack level reporting: |
259      expect (luaproc (mkstack (1))).to_contain_error ":4: bad argument"
260      expect (luaproc (mkstack (2))).to_contain_error ":7: bad argument"
261      expect (luaproc (mkstack (3))).to_contain_error ":10: bad argument"
262  - it can be disabled by setting _DEBUG to false:
263      expect (luaproc (mkstack (nil, false))).
264        not_to_contain_error "bad argument"
265  - it can be disabled by setting _DEBUG.argcheck to false:
266      expect (luaproc (mkstack (nil, "{ argcheck = false }"))).
267        not_to_contain_error "bad argument"
268  - it is not disabled by setting _DEBUG.argcheck to true:
269      expect (luaproc (mkstack (nil, "{ argcheck = true }"))).
270        to_contain_error "bad argument"
271  - it is not disabled by leaving _DEBUG.argcheck unset:
272      expect (luaproc (mkstack (nil, "{}"))).
273        to_contain_error "bad argument"
274
275  - context with primitives:
276    - it diagnoses missing types:
277        expect (fn ("bool", nil)).to_raise "boolean expected, got no value"
278        expect (fn ("boolean", nil)).to_raise "boolean expected, got no value"
279        expect (fn ("file", nil)).to_raise "FILE* expected, got no value"
280        expect (fn ("number", nil)).to_raise "number expected, got no value"
281        expect (fn ("string", nil)).to_raise "string expected, got no value"
282        expect (fn ("table", nil)).to_raise "table expected, got no value"
283    - it diagnoses mismatched types:
284        expect (fn ("bool", {0})).to_raise "boolean expected, got table"
285        expect (fn ("boolean", {0})).to_raise "boolean expected, got table"
286        expect (fn ("file", {0})).to_raise "FILE* expected, got table"
287        expect (fn ("number", {0})).to_raise "number expected, got table"
288        expect (fn ("string", {0})).to_raise "string expected, got table"
289        expect (fn ("table", false)).to_raise "table expected, got boolean"
290    - it matches types:
291        expect (fn ("bool", true)).not_to_raise "any error"
292        expect (fn ("boolean", true)).not_to_raise "any error"
293        expect (fn ("file", io.stderr)).not_to_raise "any error"
294        expect (fn ("number", 1)).not_to_raise "any error"
295        expect (fn ("string", "s")).not_to_raise "any error"
296        expect (fn ("table", {})).not_to_raise "any error"
297        expect (fn ("table", require "std.object")).not_to_raise "any error"
298
299  - context with int:
300    - it diagnoses missing types:
301        expect (fn ("int", nil)).to_raise "int expected, got no value"
302    - it diagnoses mismatched types:
303        expect (fn ("int", false)).to_raise "int expected, got boolean"
304        expect (fn ("int", 1.234)).to_raise "int expected, got number"
305        expect (fn ("int", 1234e-3)).to_raise "int expected, got number"
306    - it matches types:
307        expect (fn ("int", 1)).not_to_raise "any error"
308        expect (fn ("int", 1.0)).not_to_raise "any error"
309        expect (fn ("int", 0x1234)).not_to_raise "any error"
310        expect (fn ("int", 1.234e3)).not_to_raise "any error"
311  - context with constant string:
312    - it diagnoses missing types:
313        expect (fn (":foo", nil)).to_raise ":foo expected, got no value"
314    - it diagnoses mismatched types:
315        expect (fn (":foo", false)).to_raise ":foo expected, got boolean"
316        expect (fn (":foo", ":bar")).to_raise ":foo expected, got :bar"
317        expect (fn (":foo", "foo")).to_raise ":foo expected, got string"
318    - it matches types:
319        expect (fn (":foo", ":foo")).not_to_raise "any error"
320  - context with callable types:
321    - it diagnoses missing types:
322        expect (fn ("func", nil)).to_raise "function expected, got no value"
323        expect (fn ("function", nil)).to_raise "function expected, got no value"
324    - it diagnoses mismatched types:
325        expect (fn ("func", {0})).to_raise "function expected, got table"
326        expect (fn ("function", {0})).to_raise "function expected, got table"
327    - it matches types:
328        expect (fn ("func", function () end)).not_to_raise "any error"
329        expect (fn ("func", setmetatable ({}, {__call = function () end}))).
330          not_to_raise "any error"
331        expect (fn ("function", function () end)).not_to_raise "any error"
332        expect (fn ("function", setmetatable ({}, {__call = function () end}))).
333          not_to_raise "any error"
334  - context with table of homogenous elements:
335    - it diagnoses missing types:
336        expect (fn ("table of boolean", nil)).
337          to_raise "table expected, got no value"
338        expect (fn ("table of booleans", nil)).
339          to_raise "table expected, got no value"
340    - it diagnoses mismatched types:
341        expect (fn ("table of file", io.stderr)).
342          to_raise "table expected, got file"
343        expect (fn ("table of files", io.stderr)).
344          to_raise "table expected, got file"
345    - it diagnoses mismatched element types:
346        expect (fn ("table of number", {false})).
347          to_raise "table of numbers expected, got boolean at index 1"
348        expect (fn ("table of numbers", {1, 2, "3"})).
349          to_raise "table of numbers expected, got string at index 3"
350        expect (fn ("table of numbers", {a=1, b=2, c="3"})).
351          to_raise "table of numbers expected, got string at index c"
352    - it matches types:
353        expect (fn ("table of string", {})).not_to_raise "any error"
354        expect (fn ("table of string", {"foo"})).not_to_raise "any error"
355        expect (fn ("table of string", {"f", "o", "o"})).not_to_raise "any error"
356        expect (fn ("table of string", {b="b", a="a", r="r"})).not_to_raise "any error"
357  - context with non-empty table types:
358    - it diagnoses missing types:
359        expect (fn ("#table", nil)).
360          to_raise "non-empty table expected, got no value"
361    - it diagnoses mismatched types:
362        expect (fn ("#table", false)).
363          to_raise "non-empty table expected, got boolean"
364        expect (fn ("#table", {})).
365          to_raise "non-empty table expected, got empty table"
366    - it matches types:
367        expect (fn ("#table", {0})).not_to_raise "any error"
368  - context with non-empty table of homogenous elements:
369    - it diagnoses missing types:
370        expect (fn ("#table of boolean", nil)).
371          to_raise "non-empty table expected, got no value"
372        expect (fn ("#table of booleans", nil)).
373          to_raise "non-empty table expected, got no value"
374    - it diagnoses mismatched types:
375        expect (fn ("#table of file", {})).
376          to_raise "non-empty table expected, got empty table"
377        expect (fn ("#table of file", io.stderr)).
378          to_raise "non-empty table expected, got file"
379    - it diagnoses mismatched element types:
380        expect (fn ("#table of number", {false})).
381          to_raise "non-empty table of numbers expected, got boolean at index 1"
382        expect (fn ("#table of numbers", {1, 2, "3"})).
383          to_raise "non-empty table of numbers expected, got string at index 3"
384        expect (fn ("#table of numbers", {a=1, b=2, c="3"})).
385          to_raise "non-empty table of numbers expected, got string at index c"
386    - it matches types:
387        expect (fn ("#table of string", {"foo"})).not_to_raise "any error"
388        expect (fn ("#table of string", {"f", "o", "o"})).not_to_raise "any error"
389        expect (fn ("#table of string", {b="b", a="a", r="r"})).not_to_raise "any error"
390  - context with list:
391    - it diagnonses missing types:
392        expect (fn ("list", nil)).
393          to_raise "list expected, got no value"
394    - it diagnoses mismatched types:
395        expect (fn ("list", false)).
396          to_raise "list expected, got boolean"
397        expect (fn ("list", {foo=1})).
398          to_raise "list expected, got table"
399        expect (fn ("list", Object)).
400          to_raise "list expected, got Object"
401    - it matches types:
402        expect (fn ("list", {})).not_to_raise "any error"
403        expect (fn ("list", {1})).not_to_raise "any error"
404  - context with list of homogenous elements:
405    - it diagnoses missing types:
406        expect (fn ("list of boolean", nil)).
407          to_raise "list expected, got no value"
408        expect (fn ("list of booleans", nil)).
409          to_raise "list expected, got no value"
410    - it diagnoses mismatched types:
411        expect (fn ("list of file", io.stderr)).
412          to_raise "list expected, got file"
413        expect (fn ("list of files", io.stderr)).
414          to_raise "list expected, got file"
415        expect (fn ("list of files", {file=io.stderr})).
416          to_raise "list expected, got table"
417    - it diagnoses mismatched element types:
418        expect (fn ("list of number", {false})).
419          to_raise "list of numbers expected, got boolean at index 1"
420        expect (fn ("list of numbers", {1, 2, "3"})).
421          to_raise "list of numbers expected, got string at index 3"
422    - it matches types:
423        expect (fn ("list of string", {})).not_to_raise "any error"
424        expect (fn ("list of string", {"foo"})).not_to_raise "any error"
425        expect (fn ("list of string", {"f", "o", "o"})).not_to_raise "any error"
426  - context with non-empty list:
427    - it diagnonses missing types:
428        expect (fn ("#list", nil)).
429          to_raise "non-empty list expected, got no value"
430    - it diagnoses mismatched types:
431        expect (fn ("#list", false)).
432          to_raise "non-empty list expected, got boolean"
433        expect (fn ("#list", {})).
434          to_raise "non-empty list expected, got empty list"
435        expect (fn ("#list", {foo=1})).
436          to_raise "non-empty list expected, got table"
437        expect (fn ("#list", Object)).
438          to_raise "non-empty list expected, got empty Object"
439        expect (fn ("#list", List {})).
440          to_raise "non-empty list expected, got empty List"
441    - it matches types:
442        expect (fn ("#list", {1})).not_to_raise "any error"
443  - context with non-empty list of homogenous elements:
444    - it diagnoses missing types:
445        expect (fn ("#list of boolean", nil)).
446          to_raise "non-empty list expected, got no value"
447        expect (fn ("#list of booleans", nil)).
448          to_raise "non-empty list expected, got no value"
449    - it diagnoses mismatched types:
450        expect (fn ("#list of file", {})).
451          to_raise "non-empty list expected, got empty table"
452        expect (fn ("#list of file", io.stderr)).
453          to_raise "non-empty list expected, got file"
454        expect (fn ("#list of files", {file=io.stderr})).
455          to_raise "non-empty list expected, got table"
456    - it diagnoses mismatched element types:
457        expect (fn ("#list of number", {false})).
458          to_raise "non-empty list of numbers expected, got boolean at index 1"
459        expect (fn ("#list of numbers", {1, 2, "3"})).
460          to_raise "non-empty list of numbers expected, got string at index 3"
461    - it matches types:
462        expect (fn ("#list of string", {"foo"})).not_to_raise "any error"
463        expect (fn ("#list of string", {"f", "o", "o"})).not_to_raise "any error"
464  - context with container:
465    - it diagnoses missing types:
466        expect (fn ("List of boolean", nil)).
467          to_raise "List expected, got no value"
468        expect (fn ("List of booleans", nil)).
469          to_raise "List expected, got no value"
470    - it diagnoses mismatched types:
471        expect (fn ("List of file", io.stderr)).
472          to_raise "List expected, got file"
473        expect (fn ("List of files", io.stderr)).
474          to_raise "List expected, got file"
475        expect (fn ("List of files", {file=io.stderr})).
476          to_raise "List expected, got table"
477    - it diagnoses mismatched element types:
478        expect (fn ("List of number", List {false})).
479          to_raise "List of numbers expected, got boolean at index 1"
480        expect (fn ("List of numbers", List {1, 2, "3"})).
481          to_raise "List of numbers expected, got string at index 3"
482    - it matches types:
483        expect (fn ("list of string", List {})).not_to_raise "any error"
484        expect (fn ("list of string", List {"foo"})).not_to_raise "any error"
485        expect (fn ("list of string", List {"f", "o", "o"})).not_to_raise "any error"
486  - context with object:
487    - it diagnoses missing types:
488        expect (fn ("object", nil)).to_raise "object expected, got no value"
489        expect (fn ("Object", nil)).to_raise "Object expected, got no value"
490        expect (fn ("Foo", nil)).to_raise "Foo expected, got no value"
491        expect (fn ("any", nil)).to_raise "any value expected, got no value"
492    - it diagnoses mismatched types:
493        expect (fn ("object", {0})).to_raise "object expected, got table"
494        expect (fn ("Object", {0})).to_raise "Object expected, got table"
495        expect (fn ("object", {_type="Object"})).to_raise "object expected, got table"
496        expect (fn ("Object", {_type="Object"})).to_raise "Object expected, got table"
497        expect (fn ("Object", Foo)).to_raise "Object expected, got Foo"
498        expect (fn ("Foo", {0})).to_raise "Foo expected, got table"
499        expect (fn ("Foo", Object)).to_raise "Foo expected, got Object"
500    - it matches types:
501        expect (fn ("object", Object)).not_to_raise "any error"
502        expect (fn ("object", Object {})).not_to_raise "any error"
503        expect (fn ("object", Foo)).not_to_raise "any error"
504        expect (fn ("object", Foo {})).not_to_raise "any error"
505  - it matches anything:
506      expect (fn ("any", true)).not_to_raise "any error"
507      expect (fn ("any", {})).not_to_raise "any error"
508      expect (fn ("any", Object)).not_to_raise "any error"
509      expect (fn ("any", Foo {})).not_to_raise "any error"
510  - context with a list of valid types:
511    - it diagnoses missing elements:
512        expect (fn ("string|table", nil)).
513          to_raise "string or table expected, got no value"
514        expect (fn ("string|list|#table", nil)).
515          to_raise "string, list or non-empty table expected, got no value"
516        expect (fn ("string|number|list|object", nil)).
517          to_raise "string, number, list or object expected, got no value"
518    - it diagnoses mismatched elements:
519        expect (fn ("string|table", false)).
520          to_raise "string or table expected, got boolean"
521        expect (fn ("string|#table", {})).
522          to_raise "string or non-empty table expected, got empty table"
523        expect (fn ("string|number|#list|object", {})).
524          to_raise "string, number, non-empty list or object expected, got empty table"
525    - it matches any type from a list:
526        expect (fn ("string|table", "foo")).not_to_raise "any error"
527        expect (fn ("string|table", {})).not_to_raise "any error"
528        expect (fn ("string|table", {0})).not_to_raise "any error"
529        expect (fn ("table|table", {})).not_to_raise "any error"
530        expect (fn ("#table|table", {})).not_to_raise "any error"
531  - context with an optional type element:
532    - it diagnoses mismatched elements:
533        expect (fn ("?boolean", "string")).
534          to_raise "boolean or nil expected, got string"
535        expect (fn ("?boolean|:symbol", {})).
536          to_raise "boolean, :symbol or nil expected, got empty table"
537    - it matches nil against a single type:
538        expect (fn ("?any", nil)).not_to_raise "any error"
539        expect (fn ("?boolean", nil)).not_to_raise "any error"
540        expect (fn ("?string", nil)).not_to_raise "any error"
541    - it matches nil against a list of types:
542        expect (fn ("?boolean|table", nil)).not_to_raise "any error"
543        expect (fn ("?string|table", nil)).not_to_raise "any error"
544        expect (fn ("?table|#table", nil)).not_to_raise "any error"
545        expect (fn ("?#table|table", nil)).not_to_raise "any error"
546    - it matches nil against a list of optional types:
547        expect (fn ("?boolean|?table", nil)).not_to_raise "any error"
548        expect (fn ("?string|?table", nil)).not_to_raise "any error"
549        expect (fn ("?table|?#table", nil)).not_to_raise "any error"
550        expect (fn ("?#table|?table", nil)).not_to_raise "any error"
551    - it matches any named type:
552        expect (fn ("?any", false)).not_to_raise "any error"
553        expect (fn ("?boolean", false)).not_to_raise "any error"
554        expect (fn ("?string", "string")).not_to_raise "any error"
555    - it matches any type from a list:
556        expect (fn ("?boolean|table", {})).not_to_raise "any error"
557        expect (fn ("?string|table", {0})).not_to_raise "any error"
558        expect (fn ("?table|#table", {})).not_to_raise "any error"
559        expect (fn ("?#table|table", {})).not_to_raise "any error"
560    - it matches any type from a list with several optional specifiers:
561        expect (fn ("?boolean|?table", {})).not_to_raise "any error"
562        expect (fn ("?string|?table", {0})).not_to_raise "any error"
563        expect (fn ("?table|?table", {})).not_to_raise "any error"
564        expect (fn ("?#table|?table", {})).not_to_raise "any error"
565
566
567- describe debug:
568  - before: |
569      function mkwrap (k, v)
570        local fmt = "%s"
571        if type (v) == "string" then fmt = "%q" end
572        return k, string.format (fmt, require "std".tostring (v))
573      end
574
575      function mkdebug (debugp, ...)
576        return string.format ([[
577          _DEBUG = %s
578          require "std.debug" (%s)
579        ]],
580        require "std".tostring (debugp),
581        table.concat (require "std.functional".map (mkwrap, {...}), ", "))
582      end
583
584  - it does nothing when _DEBUG is disabled:
585      expect (luaproc (mkdebug (false, "nothing to see here"))).
586        not_to_contain_error "nothing to see here"
587  - it writes to stderr when _DEBUG is not set:
588      expect (luaproc (mkdebug (nil, "debugging"))).
589        to_contain_error "debugging"
590  - it writes to stderr when _DEBUG is enabled:
591      expect (luaproc (mkdebug (true, "debugging"))).
592        to_contain_error "debugging"
593  - it writes to stderr when _DEBUG.level is not set:
594      expect (luaproc (mkdebug ({}, "debugging"))).
595        to_contain_error "debugging"
596  - it writes to stderr when _DEBUG.level is specified:
597      expect (luaproc (mkdebug ({level = 0}, "debugging"))).
598        to_contain_error "debugging"
599      expect (luaproc (mkdebug ({level = 1}, "debugging"))).
600        to_contain_error "debugging"
601      expect (luaproc (mkdebug ({level = 2}, "debugging"))).
602        to_contain_error "debugging"
603
604
605- describe argscheck:
606  - before: |
607      function mkstack (name, spec)
608        return string.format ([[
609          local argscheck = require "std.debug".argscheck -- line 1
610          local function caller ()                        -- line 2
611            argscheck ("%s", function () end)             -- line 3
612          end                                             -- line 4
613          caller ()                                       -- line 5
614        ]], tostring (name), tostring (spec))
615      end
616
617      f = M.argscheck
618
619      mkmagic = function () return "MAGIC" end
620      wrapped = f ("inner ()", mkmagic)
621
622      _, badarg, badresult = init (M, "", "inner")
623      id = function (...) return unpack {...} end
624
625  - it returns the wrapped function:
626      expect (wrapped).not_to_be (inner)
627      expect (wrapped ()).to_be "MAGIC"
628  - it does not wrap the function when _ARGCHECK is disabled: |
629      script = [[
630        _DEBUG = false
631        local debug = require "std.debug_init"
632        local argscheck = require "std.debug".argscheck
633        local function inner () return "MAGIC" end
634        local wrapped = argscheck ("inner (?any)", inner)
635        os.exit (wrapped == inner and 0 or 1)
636      ]]
637      expect (luaproc (script)).to_succeed ()
638
639  - context when checking zero argument function:
640    - it diagnoses too many arguments:
641        expect (wrapped (false)).to_raise (badarg (1))
642    - it accepts correct argument types:
643        expect (wrapped ()).to_be "MAGIC"
644
645  - context when checking single argument function:
646    - before:
647        wrapped = f ("inner (#table)", mkmagic)
648    - it diagnoses missing arguments:
649        expect (wrapped ()).to_raise (badarg (1, "non-empty table"))
650    - it diagnoses wrong argument types:
651        expect (wrapped {}).to_raise (badarg (1, "non-empty table", "empty table"))
652    - it diagnoses too many arguments:
653        expect (wrapped ({1}, 2, nop, "", false)).to_raise (badarg (1, 5))
654    - it accepts correct argument types:
655        expect (wrapped ({1})).to_be "MAGIC"
656
657  - context when checking multi-argument function:
658    - before:
659        wrapped = f ("inner (table, function)", mkmagic)
660    - it diagnoses missing arguments:
661        expect (wrapped ()).to_raise (badarg (1, "table"))
662        expect (wrapped ({})).to_raise (badarg (2, "function"))
663    - it diagnoses wrong argument types:
664        expect (wrapped (false)).to_raise (badarg (1, "table", "boolean"))
665        expect (wrapped ({}, false)).to_raise (badarg (2, "function", "boolean"))
666    - it diagnoses too many arguments:
667        expect (wrapped ({}, nop, false)).to_raise (badarg (3))
668    - it accepts correct argument types:
669        expect (wrapped ({}, nop)).to_be "MAGIC"
670
671  - context when checking nil argument function:
672    - before:
673        wrapped = f ("inner (?int, string)", mkmagic)
674    - it diagnoses wrong argument types:
675        expect (wrapped (false)).to_raise (badarg (1, "int or nil", "boolean"))
676        expect (wrapped (1, false)).to_raise (badarg (2, "string", "boolean"))
677        expect (wrapped (nil, false)).to_raise (badarg (2, "string", "boolean"))
678    - it diagnoses too many arguments:
679        expect (wrapped (1, "foo", nop)).to_raise (badarg (3))
680        expect (wrapped (nil, "foo", nop)).to_raise (badarg (3))
681    - it accepts correct argument types:
682        expect (wrapped (1, "foo")).to_be "MAGIC"
683        expect (wrapped (nil, "foo")).to_be "MAGIC"
684
685  - context when checking optional multi-argument function:
686    - before:
687        wrapped = f ("inner ([int], string)", mkmagic)
688    - it diagnoses missing arguments:
689        expect (wrapped ()).to_raise (badarg (1, "int or string"))
690        expect (wrapped (1)).to_raise (badarg (2, "string"))
691    - it diagnoses wrong argument types:
692        expect (wrapped (false)).to_raise (badarg (1, "int or string", "boolean"))
693    - it diagnoses too many arguments:
694        expect (wrapped (1, "two", nop)).to_raise (badarg (3))
695    - it accepts correct argument types:
696        expect (wrapped ("two")).to_be "MAGIC"
697        expect (wrapped (1, "two")).to_be "MAGIC"
698
699  - context when checking final optional multi-argument function:
700    - before:
701        wrapped = f ("inner (?any, ?string, [any])", mkmagic)
702    - it diagnoses wrong argument types:
703        expect (wrapped (1, false)).to_raise (badarg (2, "string or nil", "boolean"))
704        expect (wrapped (nil, false)).to_raise (badarg (2, "string or nil", "boolean"))
705    - it diagnoses too many arguments:
706        expect (wrapped (1, "two", 3, false)).to_raise (badarg (4))
707        expect (wrapped (nil, "two", 3, false)).to_raise (badarg (4))
708        expect (wrapped (1, nil, 3, false)).to_raise (badarg (4))
709        expect (wrapped (nil, nil, 3, false)).to_raise (badarg (4))
710    - it accepts correct argument types:
711        expect (wrapped ()).to_be "MAGIC"
712        expect (wrapped (1)).to_be "MAGIC"
713        expect (wrapped (nil, "two")).to_be "MAGIC"
714        expect (wrapped (1, "two")).to_be "MAGIC"
715        expect (wrapped (nil, nil, 3)).to_be "MAGIC"
716        expect (wrapped (1, nil, 3)).to_be "MAGIC"
717        expect (wrapped (nil, "two", 3)).to_be "MAGIC"
718        expect (wrapped ("one", "two", 3)).to_be "MAGIC"
719
720  - context when checking final ellipsis function:
721    - before:
722        wrapped = f ("inner (string, int...)", mkmagic)
723    - it diagnoses missing arguments:
724        expect (wrapped ()).to_raise (badarg (1, "string"))
725        expect (wrapped ("foo")).to_raise (badarg (2, "int"))
726    - it diagnoses wrong argument types:
727        expect (wrapped (false)).to_raise (badarg (1, "string", "boolean"))
728        expect (wrapped ("foo", false)).to_raise (badarg (2, "int", "boolean"))
729        expect (wrapped ("foo", 1, false)).to_raise (badarg (3, "int", "boolean"))
730        expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)).
731          to_raise (badarg (12, "int", "boolean"))
732    - it accepts correct argument types:
733        expect (wrapped ("foo", 1)).to_be "MAGIC"
734        expect (wrapped ("foo", 1, 2)).to_be "MAGIC"
735        expect (wrapped ("foo", 1, 2, 5)).to_be "MAGIC"
736
737  - context when checking optional final parameter:
738    - context with single argument:
739      - before:
740          wrapped = f ("inner ([int])", mkmagic)
741      - it diagnoses wrong argument types:
742          expect (wrapped (false)).to_raise (badarg (1, "int", "boolean"))
743      - it diagnoses too many arguments:
744          expect (wrapped (1, nop)).to_raise (badarg (2))
745      - it accepts correct argument types:
746          expect (wrapped ()).to_be "MAGIC"
747          expect (wrapped (1)).to_be "MAGIC"
748    - context with trailing ellipsis:
749      - before:
750          wrapped = f ("inner (string, [int]...)", mkmagic)
751      - it diagnoses missing arguments:
752          expect (wrapped ()).to_raise (badarg (1, "string"))
753      - it diagnoses wrong argument types:
754          expect (wrapped (false)).to_raise (badarg (1, "string", "boolean"))
755          expect (wrapped ("foo", false)).to_raise (badarg (2, "int", "boolean"))
756          expect (wrapped ("foo", 1, false)).to_raise (badarg (3, "int", "boolean"))
757          expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)).
758            to_raise (badarg (12, "int", "boolean"))
759      - it accepts correct argument types:
760          expect (wrapped ("foo")).to_be "MAGIC"
761          expect (wrapped ("foo", 1)).to_be "MAGIC"
762          expect (wrapped ("foo", 1, 2)).to_be "MAGIC"
763          expect (wrapped ("foo", 1, 2, 5)).to_be "MAGIC"
764    - context with inner ellipsis:
765      - before:
766          wrapped = f ("inner (string, [int...])", mkmagic)
767      - it diagnoses missing arguments:
768          expect (wrapped ()).to_raise (badarg (1, "string"))
769      - it diagnoses wrong argument types:
770          expect (wrapped (false)).to_raise (badarg (1, "string", "boolean"))
771          expect (wrapped ("foo", false)).to_raise (badarg (2, "int", "boolean"))
772          expect (wrapped ("foo", 1, false)).to_raise (badarg (3, "int", "boolean"))
773          expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)).
774            to_raise (badarg (12, "int", "boolean"))
775      - it accepts correct argument types:
776          expect (wrapped ("foo")).to_be "MAGIC"
777          expect (wrapped ("foo", 1)).to_be "MAGIC"
778          expect (wrapped ("foo", 1, 2)).to_be "MAGIC"
779          expect (wrapped ("foo", 1, 2, 5)).to_be "MAGIC"
780
781  - context when omitting self type:
782    - before:
783        me = {
784          wrapped = f ("me:inner (string)", mkmagic)
785        }
786        _, badarg, badresult = init (M, "", "me:inner")
787    - it diagnoses missing arguments:
788        expect (me:wrapped ()).to_raise (badarg (1, "string"))
789    - it diagnoses wrong argument types:
790        expect (me:wrapped (false)).to_raise (badarg (1, "string", "boolean"))
791    - it diagnoses too many arguments:
792        expect (me:wrapped ("foo", false)).to_raise (badarg (2))
793    - it accepts correct argument types:
794        expect (me:wrapped ("foo")).to_be "MAGIC"
795
796  - context with too many args:
797    - before:
798        wrapped = f ("inner ([string], int)", mkmagic)
799    - it diagnoses missing arguments:
800        expect (wrapped ()).to_raise (badarg (1, "string or int"))
801        expect (wrapped ("one")).to_raise (badarg (2, "int"))
802    - it diagnoses wrong argument types:
803        expect (wrapped (false)).to_raise (badarg (1, "string or int", "boolean"))
804        expect (wrapped ("one", false)).to_raise (badarg (2, "int", "boolean"))
805    - it diagnoses too many arguments:
806        expect (wrapped ("one", 2, false)).to_raise (badarg (3))
807        expect (wrapped (1, false)).to_raise (badarg (2))
808    - it accepts correct argument types:
809        expect (wrapped (1)).to_be "MAGIC"
810        expect (wrapped ("one", 2)).to_be "MAGIC"
811
812  - context when checking single return value function:
813    - before: |
814        wrapped = f ("inner (?any...) => #table", id)
815    - it diagnoses missing results:
816        expect (wrapped ()).to_raise (badresult (1, "non-empty table"))
817    - it diagnoses wrong result types:
818        expect (wrapped {}).
819          to_raise (badresult (1, "non-empty table", "empty table"))
820    - it diagnoses too many results:
821        expect (wrapped ({1}, 2, nop, "", false)).to_raise (badresult (1, 5))
822    - it accepts correct results:
823        expect ({wrapped {1}}).to_equal {{1}}
824
825  - context with variant single return value function:
826    - before:
827        wrapped = f ("inner (?any...) => int or nil", id)
828    - it diagnoses wrong result types:
829        expect (wrapped (false)).to_raise (badresult (1, "int or nil", "boolean"))
830    - it diagnoses too many results:
831        expect (wrapped (1, nop)).to_raise (badresult (2))
832    - it accepts correct result types:
833        expect ({wrapped ()}).to_equal {}
834        expect ({wrapped (1)}).to_equal {1}
835
836  - context when checking multi-return value function:
837    - before:
838        wrapped = f ("inner (?any...) => int, string", id)
839    - it diagnoses missing results:
840        expect (wrapped ()).to_raise (badresult (1, "int"))
841        expect (wrapped (1)).to_raise (badresult (2, "string"))
842    - it diagnoses wrong result types:
843        expect (wrapped (false)).to_raise (badresult (1, "int", "boolean"))
844        expect (wrapped (1, false)).to_raise (badresult (2, "string", "boolean"))
845    - it diagnoses too many results:
846        expect (wrapped (1, "two", false)).to_raise (badresult (3))
847    - it accepts correct argument types:
848        expect ({wrapped (1, "two")}).to_equal {1, "two"}
849
850  - context when checking nil return specifier:
851    - before:
852        wrapped = f ("inner (?any...) => ?int, string", id)
853    - it diagnoses wrong result types:
854        expect (wrapped (false)).to_raise (badresult (1, "int or nil", "boolean"))
855        expect (wrapped (1, false)).to_raise (badresult (2, "string", "boolean"))
856        expect (wrapped (nil, false)).to_raise (badresult (2, "string", "boolean"))
857    - it diagnoses too many results:
858        expect (wrapped (1, "foo", nop)).to_raise (badresult (3))
859        expect (wrapped (nil, "foo", nop)).to_raise (badresult (3))
860    - it accepts correct result types:
861        expect ({wrapped (1, "foo")}).to_equal {1, "foo"}
862        expect ({wrapped (nil, "foo")}).to_equal {[2] = "foo"}
863
864  - context when checking variant multi-return value function:
865    - before:
866        wrapped = f ("inner (?any...) => int, string or string", id)
867    - it diagnoses missing results:
868        expect (wrapped ()).to_raise (badresult (1, "int or string"))
869        expect (wrapped (1)).to_raise (badresult (2, "string"))
870    - it diagnoses wrong result types:
871        expect (wrapped (false)).to_raise (badresult (1, "int or string", "boolean"))
872    - it diagnoses too many results:
873        expect (wrapped (1, "two", nop)).to_raise (badresult (3))
874    - it accepts correct result types:
875        expect ({wrapped ("two")}).to_equal {"two"}
876        expect ({wrapped (1, "two")}).to_equal {1, "two"}
877
878  - context when checking variant nil,errmsg pattern function:
879    - before:
880        wrapped = f ("inner (?any...) => int, string or nil, string", id)
881    - it diagnoses missing results:
882        expect (wrapped ()).to_raise (badresult (2, "string"))
883        expect (wrapped (1)).to_raise (badresult (2, "string"))
884    - it diagnoses wrong result types:
885        expect (wrapped (false)).to_raise (badresult (1, "int or nil", "boolean"))
886        expect (wrapped (1, false)).to_raise (badresult (2, "string", "boolean"))
887    - it diagnoses too many results:
888        expect (wrapped (1, "two", nop)).to_raise (badresult (3))
889        expect (wrapped (nil, "errmsg", nop)).to_raise (badresult (3))
890    - it accepts correct result types:
891        expect ({wrapped (1, "two")}).to_equal {1, "two"}
892        expect ({wrapped (nil, "errmsg")}).to_equal {[2] = "errmsg"}
893
894  - context when checking optional multi-return value function:
895    - before:
896        wrapped = f ("inner (?any...) => [int], string", id)
897    - it diagnoses missing results:
898        expect (wrapped ()).to_raise (badresult (1, "int or string"))
899        expect (wrapped (1)).to_raise (badresult (2, "string"))
900    - it diagnoses wrong result types:
901        expect (wrapped (false)).to_raise (badresult (1, "int or string", "boolean"))
902    - it diagnoses too many results:
903        expect (wrapped (1, "two", nop)).to_raise (badresult (3))
904    - it accepts correct result types:
905        expect ({wrapped ("two")}).to_equal {"two"}
906        expect ({wrapped (1, "two")}).to_equal {1, "two"}
907
908  - context when checking final optional multi-return value function:
909    - before:
910        wrapped = f ("inner (?any...) => ?any, ?string, [any]", id)
911    - it diagnoses wrong result types:
912        expect (wrapped (1, false)).to_raise (badresult (2, "string or nil", "boolean"))
913        expect (wrapped (nil, false)).to_raise (badresult (2, "string or nil", "boolean"))
914    - it diagnoses too many results:
915        expect (wrapped (1, "two", 3, false)).to_raise (badresult (4))
916        expect (wrapped (nil, "two", 3, false)).to_raise (badresult (4))
917        expect (wrapped (1, nil, 3, false)).to_raise (badresult (4))
918        expect (wrapped (nil, nil, 3, false)).to_raise (badresult (4))
919    - it accepts correct result types:
920        expect ({wrapped ()}).to_equal {}
921        expect ({wrapped (1)}).to_equal {1}
922        expect ({wrapped (nil, "two")}).to_equal {[2]="two"}
923        expect ({wrapped (1, "two")}).to_equal {1, "two"}
924        expect ({wrapped (nil, nil, 3)}).to_equal {[3]=3}
925        expect ({wrapped (1, nil, 3)}).to_equal {1, [3]=3}
926        expect ({wrapped (nil, "two", 3)}).to_equal {[2]="two", [3]=3}
927        expect ({wrapped ("one", "two", 3)}).to_equal {"one", "two", 3}
928
929  - context when checking optional final result:
930    - context with single result:
931      - before:
932          wrapped = f ("inner (?any...) => [int]", id)
933      - it diagnoses wrong result types:
934          expect (wrapped (false)).to_raise (badresult (1, "int", "boolean"))
935      - it diagnoses too many results:
936          expect (wrapped (1, nop)).to_raise (badresult (2))
937      - it accepts correct result types:
938          expect ({wrapped ()}).to_equal {}
939          expect ({wrapped (1)}).to_equal {1}
940    - context with trailing ellipsis:
941      - before:
942          wrapped = f ("inner (?any...) => string, [int]...", id)
943      - it diagnoses missing results:
944          expect (wrapped ()).to_raise (badresult (1, "string"))
945      - it diagnoses wrong result types:
946          expect (wrapped (false)).to_raise (badresult (1, "string", "boolean"))
947          expect (wrapped ("foo", false)).to_raise (badresult (2, "int", "boolean"))
948          expect (wrapped ("foo", 1, false)).to_raise (badresult (3, "int", "boolean"))
949          expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)).
950            to_raise (badresult (12, "int", "boolean"))
951      - it accepts correct result types:
952          expect ({wrapped ("foo")}).to_equal {"foo"}
953          expect ({wrapped ("foo", 1)}).to_equal {"foo", 1}
954          expect ({wrapped ("foo", 1, 2)}).to_equal {"foo", 1, 2}
955          expect ({wrapped ("foo", 1, 2, 5)}).to_equal {"foo", 1, 2, 5}
956    - context with inner ellipsis:
957      - before:
958          wrapped = f ("inner (?any...) => string, [int...]", id)
959      - it diagnoses missing results:
960          expect (wrapped ()).to_raise (badresult (1, "string"))
961      - it diagnoses wrong result types:
962          expect (wrapped (false)).to_raise (badresult (1, "string", "boolean"))
963          expect (wrapped ("foo", false)).to_raise (badresult (2, "int", "boolean"))
964          expect (wrapped ("foo", 1, false)).to_raise (badresult (3, "int", "boolean"))
965          expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)).
966            to_raise (badresult (12, "int", "boolean"))
967      - it accepts correct result types:
968          expect ({wrapped ("foo")}).to_equal {"foo"}
969          expect ({wrapped ("foo", 1)}).to_equal {"foo", 1}
970          expect ({wrapped ("foo", 1, 2)}).to_equal {"foo", 1, 2}
971          expect ({wrapped ("foo", 1, 2, 5)}).to_equal {"foo", 1, 2, 5}
972
973  - context with too many results:
974    - before:
975        wrapped = f ("inner (?any...) => [string], int", id)
976    - it diagnoses missing results:
977        expect (wrapped ()).to_raise (badresult (1, "string or int"))
978        expect (wrapped "one").to_raise (badresult (2, "int"))
979    - it diagnoses wrong result types:
980        expect (wrapped (false)).
981          to_raise (badresult (1, "string or int", "boolean"))
982        expect (wrapped ("one", false)).
983          to_raise (badresult (2, "int", "boolean"))
984    - it diagnoses too many results:
985        expect (wrapped ("one", 2, false)).to_raise (badresult (3))
986        expect (wrapped (1, false)).to_raise (badresult (2))
987    - it accepts correct argument types:
988        expect ({wrapped (1)}).to_equal {1}
989        expect ({wrapped ("one", 2)}).to_equal {"one", 2}
990
991
992- describe extramsg_mismatch:
993  - before:
994      f = M.extramsg_mismatch
995
996  - it returns the expected types:
997      expect (f "nil").to_contain "nil expected, "
998      expect (f "bool").to_contain "boolean expected, "
999      expect (f "?bool").to_contain "boolean or nil expected, "
1000      expect (f "string|table").to_contain "string or table expected, "
1001  - it returns expected container types:
1002      expect (f ("table of int", nil, 1)).to_contain "table of ints expected, "
1003      expect (f ("table of int|bool", nil, 1)).
1004        to_contain "table of ints or booleans expected, "
1005      expect (f ("table of int|bool|string", nil, 1)).
1006        to_contain "table of ints, booleans or strings expected, "
1007      expect (f ("table of int|bool|string|table", nil, 1)).
1008        to_contain "table of ints, booleans, strings or tables expected, "
1009  - it returns the actual type:
1010      expect (f ("int")).to_contain ", got no value"
1011      expect (f ("int", false)).to_contain ", got boolean"
1012      expect (f ("int", {})).to_contain ", got empty table"
1013  - it returns table field type:
1014      expect (f ("table of int", nil, 1)).to_contain ", got no value at index 1"
1015      expect (f ("table of int", "two", 2)).to_contain ", got string at index 2"
1016      expect (f ("table of int|bool", "five", 3)).to_contain ", got string at index 3"
1017
1018
1019- describe extramsg_toomany:
1020  - before:
1021      f = M.extramsg_toomany
1022
1023  - it returns the expected thing:
1024      expect (f ("mojo", 1, 2)).to_contain "no more than 1 mojo"
1025  - it uses singular thing when 1 is expected:
1026      expect (f ("argument", 1, 2)).to_contain "no more than 1 argument"
1027  - it uses plural thing otherwise:
1028      expect (f ("thing", 0, 3)).to_contain "no more than 0 things"
1029      expect (f ("result", 2, 3)).to_contain "no more than 2 results"
1030  - it returns the actual count:
1031      expect (f ("bad", 0, 1)).to_contain ", got 1"
1032      expect (f ("bad", 99, 999)).to_contain ", got 999"
1033
1034
1035- context function environments:
1036  - before:
1037      env = {
1038        tostring = function (x) return '"' .. tostring (x) .. '"' end,
1039      }
1040      fn = function (x) return tostring (x) end
1041      ft = setmetatable ({}, { __call = function (_, ...) return fn (...) end })
1042
1043  - describe getfenv:
1044    - before:
1045        f = M.getfenv
1046    - it returns a table:
1047        expect (type (f (fn))).to_be "table"
1048    - it gets a function execution environment:
1049        M.setfenv (fn, env)
1050        expect (f (fn)).to_be (env)
1051    - it understands functables:
1052        M.setfenv (ft, env)
1053        expect (f (ft)).to_be (env)
1054
1055  - describe setfenv:
1056    - before:
1057        f = M.setfenv
1058    - it returns the passed function:
1059        expect (f (fn, env)).to_be (fn)
1060    - it sets a function execution environment:
1061        f (fn, env)
1062        expect (fn (42)).to_be '"42"'
1063    - it understands functables:
1064        f (ft, env)
1065        expect (fn (5)).to_be '"5"'
1066
1067
1068- describe say:
1069  - before: |
1070      function mkwrap (k, v)
1071        local fmt = "%s"
1072        if type (v) == "string" then fmt = "%q" end
1073        return k, string.format (fmt, require "std".tostring (v))
1074      end
1075
1076      function mksay (debugp, ...)
1077        return string.format ([[
1078          _DEBUG = %s
1079          require "std.debug".say (%s)
1080        ]],
1081        require "std".tostring (debugp),
1082        table.concat (require "std.functional".map (mkwrap, {...}), ", "))
1083      end
1084
1085      f = M.say
1086
1087  - it uses stdlib tostring:
1088      expect (luaproc [[require "std.debug".say {"debugging"}]]).
1089        to_contain_error (require "std".tostring {"debugging"})
1090  - context when _DEBUG is disabled:
1091    - it does nothing when message level is not set:
1092        expect (luaproc (mksay (false, "nothing to see here"))).
1093          not_to_contain_error "nothing to see here"
1094    - it does nothing when message is set:
1095        expect (luaproc (mksay (false, -999, "nothing to see here"))).
1096          not_to_contain_error "nothing to see here"
1097        expect (luaproc (mksay (false, 0, "nothing to see here"))).
1098          not_to_contain_error "nothing to see here"
1099        expect (luaproc (mksay (false, 1, "nothing to see here"))).
1100          not_to_contain_error "nothing to see here"
1101        expect (luaproc (mksay (false, 2, "nothing to see here"))).
1102          not_to_contain_error "nothing to see here"
1103        expect (luaproc (mksay (false, 999, "nothing to see here"))).
1104          not_to_contain_error "nothing to see here"
1105  - context when _DEBUG is not set:
1106    - it writes to stderr when message level is not set:
1107        expect (luaproc (mksay (nil, "debugging"))).
1108          to_contain_error "debugging"
1109    - it writes to stderr when message level is 1 or lower:
1110        expect (luaproc (mksay (nil, -999, "debugging"))).
1111          to_contain_error "debugging"
1112        expect (luaproc (mksay (nil, 0, "debugging"))).
1113          to_contain_error "debugging"
1114        expect (luaproc (mksay (nil, 1, "debugging"))).
1115          to_contain_error "debugging"
1116    - it does nothing when message level is 2 or higher:
1117        expect (luaproc (mksay (nil, 2, "nothing to see here"))).
1118          not_to_contain_error "nothing to see here"
1119        expect (luaproc (mksay (nil, 999, "nothing to see here"))).
1120          not_to_contain_error "nothing to see here"
1121  - context when _DEBUG is enabled:
1122    - it writes to stderr when message level is not set:
1123        expect (luaproc (mksay (true, "debugging"))).
1124          to_contain_error "debugging"
1125    - it writes to stderr when message level is 1 or lower:
1126        expect (luaproc (mksay (true, -999, "debugging"))).
1127          to_contain_error "debugging"
1128        expect (luaproc (mksay (true, 0, "debugging"))).
1129          to_contain_error "debugging"
1130        expect (luaproc (mksay (true, 1, "debugging"))).
1131          to_contain_error "debugging"
1132    - it does nothing when message level is 2 or higher:
1133        expect (luaproc (mksay (true, 2, "nothing to see here"))).
1134          not_to_contain_error "nothing to see here"
1135        expect (luaproc (mksay (true, 999, "nothing to see here"))).
1136          not_to_contain_error "nothing to see here"
1137  - context when _DEBUG.level is not set:
1138    - it writes to stderr when message level is not set:
1139        expect (luaproc (mksay ({}, "debugging"))).
1140          to_contain_error "debugging"
1141    - it writes to stderr when message level is 1 or lower:
1142        expect (luaproc (mksay ({}, -999, "debugging"))).
1143          to_contain_error "debugging"
1144        expect (luaproc (mksay ({}, 0, "debugging"))).
1145          to_contain_error "debugging"
1146        expect (luaproc (mksay ({}, 1, "debugging"))).
1147          to_contain_error "debugging"
1148    - it does nothing when message level is 2 or higher:
1149        expect (luaproc (mksay ({}, 2, "nothing to see here"))).
1150          not_to_contain_error "nothing to see here"
1151        expect (luaproc (mksay ({}, 999, "nothing to see here"))).
1152          not_to_contain_error "nothing to see here"
1153  - context when _DEBUG.level is specified:
1154    - it writes to stderr when message level is 1 or lower:
1155        expect (luaproc (mksay ({level = 0}, "debugging"))).
1156          to_contain_error "debugging"
1157        expect (luaproc (mksay ({level = 1}, "debugging"))).
1158          to_contain_error "debugging"
1159        expect (luaproc (mksay ({level = 2}, "debugging"))).
1160          to_contain_error "debugging"
1161    - it does nothing when message level is higher than debug level:
1162        expect (luaproc (mksay ({level = 2}, 3, "nothing to see here"))).
1163          not_to_contain_error "nothing to see here"
1164    - it writes to stderr when message level equals debug level:
1165        expect (luaproc (mksay ({level = 2}, 2, "debugging"))).
1166          to_contain_error "debugging"
1167    - it writes to stderr when message level is lower than debug level:
1168        expect (luaproc (mksay ({level = 2}, 1, "debugging"))).
1169          to_contain_error "debugging"
1170
1171
1172- describe trace:
1173  - before:
1174      f = init (M, this_module,  "trace")
1175
1176  - it does nothing when _DEBUG is disabled:
1177      expect (luaproc [[
1178        _DEBUG = false
1179        require "std.debug"
1180        os.exit (0)
1181      ]]).to_succeed_with ""
1182  - it does nothing when _DEBUG is not set:
1183      expect (luaproc [[
1184        require "std.debug"
1185        os.exit (0)
1186      ]]).to_succeed_with ""
1187  - it does nothing when _DEBUG is enabled:
1188      expect (luaproc [[
1189        _DEBUG = true
1190        require "std.debug"
1191        os.exit (0)
1192      ]]).to_succeed_with ""
1193  - it enables automatically when _DEBUG.call is set: |
1194      expect (luaproc [[
1195        _DEBUG = {call = true}
1196        local debug = require "std.debug"
1197        os.exit (1)
1198      ]]).to_fail_while_containing ":3 call exit"
1199  - it is enabled manually with debug.sethook: |
1200      expect (luaproc [[
1201        local debug = require "std.debug"
1202        debug.sethook (debug.trace, "cr")
1203        os.exit (1)
1204      ]]).to_fail_while_containing ":3 call exit"
1205  - it writes call trace log to standard error: |
1206      expect (luaproc [[
1207        local debug = require "std.debug"
1208        debug.sethook (debug.trace, "cr")
1209        os.exit (0)
1210      ]]).to_contain_error ":3 call exit"
1211  - it traces lua calls: |
1212      expect (luaproc [[
1213        local debug = require "std.debug"         -- line 1
1214        local function incr (i) return i + 1 end  -- line 2
1215        debug.sethook (debug.trace, "cr")         -- line 3
1216        os.exit (incr (41))                       -- line 4
1217      ]]).to_fail_while_matching ".*:4 call incr <2:.*:4 return incr <2:.*"
1218  - it traces C api calls: |
1219      expect (luaproc [[
1220        local debug = require "std.debug"
1221        local function incr (i) return i + 1 end
1222        debug.sethook (debug.trace, "cr")
1223        os.exit (incr (41))
1224      ]]).to_fail_while_matching ".*:4 call exit %[C%]%s$"
1225