test/lunit.lua

changeset 35
4f81725a1e5f
equal deleted inserted replaced
34:64a79d8ee224 35:4f81725a1e5f
1
2 --[[--------------------------------------------------------------------------
3
4 This file is part of lunit 0.4pre (alpha).
5
6 For Details about lunit look at: http://www.nessie.de/mroth/lunit/
7
8 Author: Michael Roth <mroth@nessie.de>
9
10 Copyright (c) 2004 Michael Roth <mroth@nessie.de>
11
12 Permission is hereby granted, free of charge, to any person
13 obtaining a copy of this software and associated documentation
14 files (the "Software"), to deal in the Software without restriction,
15 including without limitation the rights to use, copy, modify, merge,
16 publish, distribute, sublicense, and/or sell copies of the Software,
17 and to permit persons to whom the Software is furnished to do so,
18 subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be
21 included in all copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
27 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
28 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
31 --]]--------------------------------------------------------------------------
32
33
34
35
36 -----------------------
37 -- Intialize package --
38 -----------------------
39
40 local P = { }
41 lunit = P
42
43 -- Import
44 local type = type
45 local print = print
46 local ipairs = ipairs
47 local pairs = pairs
48 local string = string
49 local table = table
50 local pcall = pcall
51 local xpcall = xpcall
52 local traceback = debug.traceback
53 local error = error
54 local setmetatable = setmetatable
55 local rawset = rawset
56 local orig_assert = assert
57 local getfenv = getfenv
58 local setfenv = setfenv
59 local tostring = tostring
60
61
62 -- Start package scope
63 setfenv(1, P)
64
65
66
67
68 --------------------------------
69 -- Private data and functions --
70 --------------------------------
71
72 local run_testcase
73 local do_assert, check_msg
74 local stats = { }
75 local testcases = { }
76 local stats_inc, tc_mt
77
78
79
80
81 --------------------------
82 -- Type check functions --
83 --------------------------
84
85 function is_nil(x)
86 return type(x) == "nil"
87 end
88
89 function is_boolean(x)
90 return type(x) == "boolean"
91 end
92
93 function is_number(x)
94 return type(x) == "number"
95 end
96
97 function is_string(x)
98 return type(x) == "string"
99 end
100
101 function is_table(x)
102 return type(x) == "table"
103 end
104
105 function is_function(x)
106 return type(x) == "function"
107 end
108
109 function is_thread(x)
110 return type(x) == "thread"
111 end
112
113 function is_userdata(x)
114 return type(x) == "userdata"
115 end
116
117
118
119
120 ----------------------
121 -- Assert functions --
122 ----------------------
123
124 function assert(assertion, msg)
125 stats_inc("assertions")
126 check_msg("assert", msg)
127 do_assert(not not assertion, "assertion failed (was: "..tostring(assertion)..")", msg) -- (convert assertion to bool)
128 return assertion
129 end
130
131
132 function assert_fail(msg)
133 stats_inc("assertions")
134 check_msg("assert_fail", msg)
135 do_assert(false, "failure", msg)
136 end
137
138
139 function assert_true(actual, msg)
140 stats_inc("assertions")
141 check_msg("assert_true", msg)
142 do_assert(is_boolean(actual), "true expected but was a "..type(actual), msg)
143 do_assert(actual == true, "true expected but was false", msg)
144 return actual
145 end
146
147
148 function assert_false(actual, msg)
149 stats_inc("assertions")
150 check_msg("assert_false", msg)
151 do_assert(is_boolean(actual), "false expected but was a "..type(actual), msg)
152 do_assert(actual == false, "false expected but was true", msg)
153 return actual
154 end
155
156
157 function assert_equal(expected, actual, msg)
158 stats_inc("assertions")
159 check_msg("assert_equal", msg)
160 do_assert(expected == actual, "expected '"..tostring(expected).."' but was '"..tostring(actual).."'", msg)
161 return actual
162 end
163
164
165 function assert_not_equal(unexpected, actual, msg)
166 stats_inc("assertions")
167 check_msg("assert_not_equal", msg)
168 do_assert(unexpected ~= actual, "'"..tostring(expected).."' not expected but was one", msg)
169 return actual
170 end
171
172
173 function assert_match(pattern, actual, msg)
174 stats_inc("assertions")
175 check_msg("assert_match", msg)
176 do_assert(is_string(pattern), "assert_match expects the pattern as a string")
177 do_assert(is_string(actual), "expected a string to match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
178 do_assert(not not string.find(actual, pattern), "expected '"..actual.."' to match pattern '"..pattern.."' but doesn't", msg)
179 return actual
180 end
181
182
183 function assert_not_match(pattern, actual, msg)
184 stats_inc("assertions")
185 check_msg("assert_not_match", msg)
186 do_assert(is_string(actual), "expected a string to not match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
187 do_assert(string.find(actual, pattern) == nil, "expected '"..actual.."' to not match pattern '"..pattern.."' but it does", msg)
188 return actual
189 end
190
191
192 function assert_nil(actual, msg)
193 stats_inc("assertions")
194 check_msg("assert_nil", msg)
195 do_assert(is_nil(actual), "nil expected but was a "..type(actual), msg)
196 return actual
197 end
198
199
200 function assert_not_nil(actual, msg)
201 stats_inc("assertions")
202 check_msg("assert_not_nil", msg)
203 do_assert(not is_nil(actual), "nil not expected but was one", msg)
204 return actual
205 end
206
207
208 function assert_boolean(actual, msg)
209 stats_inc("assertions")
210 check_msg("assert_boolean", msg)
211 do_assert(is_boolean(actual), "boolean expected but was a "..type(actual), msg)
212 return actual
213 end
214
215
216 function assert_not_boolean(actual, msg)
217 stats_inc("assertions")
218 check_msg("assert_not_boolean", msg)
219 do_assert(not is_boolean(actual), "boolean not expected but was one", msg)
220 return actual
221 end
222
223
224 function assert_number(actual, msg)
225 stats_inc("assertions")
226 check_msg("assert_number", msg)
227 do_assert(is_number(actual), "number expected but was a "..type(actual), msg)
228 return actual
229 end
230
231
232 function assert_not_number(actual, msg)
233 stats_inc("assertions")
234 check_msg("assert_not_number", msg)
235 do_assert(not is_number(actual), "number not expected but was one", msg)
236 return actual
237 end
238
239
240 function assert_string(actual, msg)
241 stats_inc("assertions")
242 check_msg("assert_string", msg)
243 do_assert(is_string(actual), "string expected but was a "..type(actual), msg)
244 return actual
245 end
246
247
248 function assert_not_string(actual, msg)
249 stats_inc("assertions")
250 check_msg("assert_not_string", msg)
251 do_assert(not is_string(actual), "string not expected but was one", msg)
252 return actual
253 end
254
255
256 function assert_table(actual, msg)
257 stats_inc("assertions")
258 check_msg("assert_table", msg)
259 do_assert(is_table(actual), "table expected but was a "..type(actual), msg)
260 return actual
261 end
262
263
264 function assert_not_table(actual, msg)
265 stats_inc("assertions")
266 check_msg("assert_not_table", msg)
267 do_assert(not is_table(actual), "table not expected but was one", msg)
268 return actual
269 end
270
271
272 function assert_function(actual, msg)
273 stats_inc("assertions")
274 check_msg("assert_function", msg)
275 do_assert(is_function(actual), "function expected but was a "..type(actual), msg)
276 return actual
277 end
278
279
280 function assert_not_function(actual, msg)
281 stats_inc("assertions")
282 check_msg("assert_not_function", msg)
283 do_assert(not is_function(actual), "function not expected but was one", msg)
284 return actual
285 end
286
287
288 function assert_thread(actual, msg)
289 stats_inc("assertions")
290 check_msg("assert_thread", msg)
291 do_assert(is_thread(actual), "thread expected but was a "..type(actual), msg)
292 return actual
293 end
294
295
296 function assert_not_thread(actual, msg)
297 stats_inc("assertions")
298 check_msg("assert_not_thread", msg)
299 do_assert(not is_thread(actual), "thread not expected but was one", msg)
300 return actual
301 end
302
303
304 function assert_userdata(actual, msg)
305 stats_inc("assertions")
306 check_msg("assert_userdata", msg)
307 do_assert(is_userdata(actual), "userdata expected but was a "..type(actual), msg)
308 return actual
309 end
310
311
312 function assert_not_userdata(actual, msg)
313 stats_inc("assertions")
314 check_msg("assert_not_userdata", msg)
315 do_assert(not is_userdata(actual), "userdata not expected but was one", msg)
316 return actual
317 end
318
319
320 function assert_error(msg, func)
321 stats_inc("assertions")
322 if is_nil(func) then func, msg = msg, nil end
323 check_msg("assert_error", msg)
324 do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func))
325 local ok, errmsg = pcall(func)
326 do_assert(ok == false, "error expected but no error occurred", msg)
327 end
328
329
330 function assert_pass(msg, func)
331 stats_inc("assertions")
332 if is_nil(func) then func, msg = msg, nil end
333 check_msg("assert_pass", msg)
334 do_assert(is_function(func), "assert_pass expects a function as the last argument but it was a "..type(func))
335 local ok, errmsg = pcall(func)
336 if not ok then do_assert(ok == true, "no error expected but error was: "..errmsg, msg) end
337 end
338
339
340
341
342 -----------------------------------------------------------
343 -- Assert implementation that assumes it was called from --
344 -- lunit code which was called directly from user code. --
345 -----------------------------------------------------------
346
347 function do_assert(assertion, base_msg, user_msg)
348 orig_assert(is_boolean(assertion))
349 orig_assert(is_string(base_msg))
350 orig_assert(is_string(user_msg) or is_nil(user_msg))
351 if not assertion then
352 if user_msg then
353 error(base_msg..": "..user_msg, 3)
354 else
355 error(base_msg.."!", 3)
356 end
357 end
358 end
359
360 -------------------------------------------
361 -- Checks the msg argument in assert_xxx --
362 -------------------------------------------
363
364 function check_msg(name, msg)
365 orig_assert(is_string(name))
366 if not (is_nil(msg) or is_string(msg)) then
367 error("lunit."..name.."() expects the optional message as a string but it was a "..type(msg).."!" ,3)
368 end
369 end
370
371
372
373
374 -------------------------------------
375 -- Creates a new TestCase 'Object' --
376 -------------------------------------
377
378 function TestCase(name)
379 do_assert(is_string(name), "lunit.TestCase() needs a string as an argument")
380 local tc = {
381 __lunit_name = name;
382 __lunit_setup = nil;
383 __lunit_tests = { };
384 __lunit_teardown = nil;
385 }
386 setmetatable(tc, tc_mt)
387 table.insert(testcases, tc)
388 return tc
389 end
390
391 tc_mt = {
392 __newindex = function(tc, key, value)
393 rawset(tc, key, value)
394 if is_string(key) and is_function(value) then
395 local name = string.lower(key)
396 if string.find(name, "^test") or string.find(name, "test$") then
397 table.insert(tc.__lunit_tests, key)
398 elseif name == "setup" then
399 tc.__lunit_setup = value
400 elseif name == "teardown" then
401 tc.__lunit_teardown = value
402 end
403 end
404 end
405 }
406
407
408
409 -----------------------------------------
410 -- Wrap Functions in a TestCase object --
411 -----------------------------------------
412
413 function wrap(name, ...)
414 if is_function(name) then
415 table.insert(arg, 1, name)
416 name = "Anonymous Testcase"
417 end
418
419 local tc = TestCase(name)
420 for index, test in ipairs(arg) do
421 tc["Test #"..index] = test
422 end
423 return tc
424 end
425
426
427
428
429
430
431 ----------------------------------
432 -- Runs the complete Test Suite --
433 ----------------------------------
434
435 function run()
436
437 ---------------------------
438 -- Initialize statistics --
439 ---------------------------
440
441 stats.testcases = 0 -- Total number of Test Cases
442 stats.tests = 0 -- Total number of all Tests in all Test Cases
443 stats.run = 0 -- Number of Tests run
444 stats.notrun = 0 -- Number of Tests not run
445 stats.failed = 0 -- Number of Tests failed
446 stats.warnings = 0 -- Number of Warnings (teardown)
447 stats.errors = 0 -- Number of Errors (setup)
448 stats.passed = 0 -- Number of Test passed
449 stats.assertions = 0 -- Number of all assertions made in all Test in all Test Cases
450
451 --------------------------------
452 -- Count Test Cases and Tests --
453 --------------------------------
454
455 stats.testcases = table.getn(testcases)
456
457 for _, tc in ipairs(testcases) do
458 stats_inc("tests" , table.getn(tc.__lunit_tests))
459 end
460
461 ------------------
462 -- Print Header --
463 ------------------
464
465 print()
466 print("#### Test Suite with "..stats.tests.." Tests in "..stats.testcases.." Test Cases loaded.")
467
468 ------------------------
469 -- Run all Test Cases --
470 ------------------------
471
472 for _, tc in ipairs(testcases) do
473 run_testcase(tc)
474 end
475
476 ------------------
477 -- Print Footer --
478 ------------------
479
480 print()
481 print("#### Test Suite finished.")
482
483 local msg_assertions = stats.assertions.." Assertions checked. "
484 local msg_passed = stats.passed == stats.tests and "All Tests passed" or stats.passed.." Tests passed"
485 local msg_failed = stats.failed > 0 and ", "..stats.failed.." failed" or ""
486 local msg_run = stats.notrun > 0 and ", "..stats.notrun.." not run" or ""
487 local msg_warn = stats.warnings > 0 and ", "..stats.warnings.." warnings" or ""
488
489 print()
490 print(msg_assertions..msg_passed..msg_failed..msg_run..msg_warn.."!")
491
492 -----------------
493 -- Return code --
494 -----------------
495
496 if stats.passed == stats.tests then
497 return 0
498 else
499 return 1
500 end
501 end
502
503
504
505
506 -----------------------------
507 -- Runs a single Test Case --
508 -----------------------------
509
510 function run_testcase(tc)
511
512 orig_assert(is_table(tc))
513 orig_assert(is_table(tc.__lunit_tests))
514 orig_assert(is_string(tc.__lunit_name))
515 orig_assert(is_nil(tc.__lunit_setup) or is_function(tc.__lunit_setup))
516 orig_assert(is_nil(tc.__lunit_teardown) or is_function(tc.__lunit_teardown))
517
518 ----------------------------------
519 -- Protected call to a function --
520 ----------------------------------
521
522 local function call(errprefix, func)
523 orig_assert(is_string(errprefix))
524 orig_assert(is_function(func))
525 local ok, errmsg = xpcall(function() func(tc) end, traceback)
526 if not ok then
527 print()
528 print(errprefix..": "..errmsg)
529 end
530 return ok
531 end
532
533 ------------------------------------
534 -- Calls setup() on the Test Case --
535 ------------------------------------
536
537 local function setup(testname)
538 if tc.__lunit_setup then
539 return call("ERROR: "..testname..": setup() failed", tc.__lunit_setup)
540 else
541 return true
542 end
543 end
544
545 ------------------------------------------
546 -- Calls a single Test on the Test Case --
547 ------------------------------------------
548
549 local function run(testname)
550 orig_assert(is_string(testname))
551 orig_assert(is_function(tc[testname]))
552 local ok = call("FAIL: "..testname, tc[testname])
553 if not ok then
554 stats_inc("failed")
555 else
556 stats_inc("passed")
557 end
558 return ok
559 end
560
561 ---------------------------------------
562 -- Calls teardown() on the Test Case --
563 ---------------------------------------
564
565 local function teardown(testname)
566 if tc.__lunit_teardown then
567 if not call("WARNING: "..testname..": teardown() failed", tc.__lunit_teardown) then
568 stats_inc("warnings")
569 end
570 end
571 end
572
573 ---------------------------------
574 -- Run all Tests on a TestCase --
575 ---------------------------------
576
577 print()
578 print("#### Running '"..tc.__lunit_name.."' ("..table.getn(tc.__lunit_tests).." Tests)...")
579
580 for _, testname in ipairs(tc.__lunit_tests) do
581 if setup(testname) then
582 run(testname)
583 stats_inc("run")
584 teardown(testname)
585 else
586 print("WARN: Skipping '"..testname.."'...")
587 stats_inc("notrun")
588 end
589 end
590
591 end
592
593
594
595
596 ---------------------
597 -- Import function --
598 ---------------------
599
600 function import(name)
601
602 do_assert(is_string(name), "lunit.import() expects a single string as argument")
603
604 local user_env = getfenv(2)
605
606 --------------------------------------------------
607 -- Installs a specific function in the user env --
608 --------------------------------------------------
609
610 local function install(funcname)
611 user_env[funcname] = P[funcname]
612 end
613
614
615 ----------------------------------------------------------
616 -- Install functions matching a pattern in the user env --
617 ----------------------------------------------------------
618
619 local function install_pattern(pattern)
620 for funcname, _ in pairs(P) do
621 if string.find(funcname, pattern) then
622 install(funcname)
623 end
624 end
625 end
626
627 ------------------------------------------------------------
628 -- Installs assert() and all assert_xxx() in the user env --
629 ------------------------------------------------------------
630
631 local function install_asserts()
632 install_pattern("^assert.*")
633 end
634
635 -------------------------------------------
636 -- Installs all is_xxx() in the user env --
637 -------------------------------------------
638
639 local function install_tests()
640 install_pattern("^is_.+")
641 end
642
643 if name == "asserts" or name == "assertions" then
644 install_asserts()
645 elseif name == "tests" or name == "checks" then
646 install_tests()
647 elseif name == "all" then
648 install_asserts()
649 install_tests()
650 install("TestCase")
651 elseif string.find(name, "^assert.*") and P[name] then
652 install(name)
653 elseif string.find(name, "^is_.+") and P[name] then
654 install(name)
655 elseif name == "TestCase" then
656 install("TestCase")
657 else
658 error("luniit.import(): invalid function '"..name.."' to import", 2)
659 end
660 end
661
662
663
664
665 --------------------------------------------------
666 -- Installs a private environment on the caller --
667 --------------------------------------------------
668
669 function setprivfenv()
670 local new_env = { }
671 local new_env_mt = { __index = getfenv(2) }
672 setmetatable(new_env, new_env_mt)
673 setfenv(2, new_env)
674 end
675
676
677
678
679 --------------------------------------------------
680 -- Increments a counter in the statistics table --
681 --------------------------------------------------
682
683 function stats_inc(varname, value)
684 orig_assert(is_table(stats))
685 orig_assert(is_string(varname))
686 orig_assert(is_nil(value) or is_number(value))
687 if not stats[varname] then return end
688 stats[varname] = stats[varname] + (value or 1)
689 end
690
691
692
693

mercurial