Copyright (c) 2009-12 Scott Vokes Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This is a library for randomized testing with Lua. For usage and examples, see README and the test suite.
+Test that the function raises an error when called.
+
+
+
Parameters
+
+
+
+ f:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_false (got, msg)
+
+got == false.
+
+
+
Parameters
+
+
+
+ got:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_function (val, msg)
+
+Test that val is a function.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_gt (lim, val, msg)
+
+val > lim.
+
+
+
Parameters
+
+
+
+ lim:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_gte (lim, val, msg)
+
+val >= lim.
+
+
+
Parameters
+
+
+
+ lim:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_len (len, val, msg)
+
+#val == len.
+
+
+
Parameters
+
+
+
+ len:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_lt (lim, val, msg)
+
+val < lim.
+
+
+
Parameters
+
+
+
+ lim:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_lte (lim, val, msg)
+
+val <= lim.
+
+
+
Parameters
+
+
+
+ lim:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_match (pat, s, msg)
+
+Test that the string s matches the pattern exp.
+
+
+
Parameters
+
+
+
+ pat:
+
+
+
+ s:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_metatable (exp, val, msg)
+
+Test that a value has the expected metatable.
+
+
+
Parameters
+
+
+
+ exp:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_boolean (val, msg)
+
+Test that val is not a boolean.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_equal (exp, got, msg)
+
+exp ~= got.
+
+
+
Parameters
+
+
+
+ exp:
+
+
+
+ got:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_function (val, msg)
+
+Test that val is not a function.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_len (len, val, msg)
+
+#val ~= len.
+
+
+
Parameters
+
+
+
+ len:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_match (pat, s, msg)
+
+Test that the string s doesn't match the pattern exp.
+
+
+
Parameters
+
+
+
+ pat:
+
+
+
+ s:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_metatable (exp, val, msg)
+
+Test that a value does not have a given metatable.
+
+
+
Parameters
+
+
+
+ exp:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_number (val, msg)
+
+Test that val is not a number.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_string (val, msg)
+
+Test that val is not a string.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_table (val, msg)
+
+Test that val is not a table.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_thread (val, msg)
+
+Test that val is not a thread (coroutine).
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_userdata (val, msg)
+
+Test that val is not a userdata (light or heavy).
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_number (val, msg)
+
+Test that val is a number.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_random (opt, f, ...)
+
+Run a test case with randomly instantiated arguments, running the test function f opt.count (default: 100) times.
+
+
+
Parameters
+
+
+
+ opt: A table with options, or just a test name string. opt.count: how many random trials to perform opt.seed: Start the batch of trials with a specific seed opt.always: Always test these seeds (for regressions) opt.show_progress: Whether to print a . after every opt.tick trials. opt.seed_limit: Max seed to allow. opt.max_failures, max_errors, max_skips: Give up after X of each.
+
+
+
+ f: A test function, run as f(unpack(randomized_args(...)))
+
+
+
+ ...: the arg specification. For each argument, creates a random instance of that type. boolean: return true or false number n: returns 0 <= x < n, or -n <= x < n if negative. If n has a decimal component, so will the result. string: Specifiedd as "(len[,maxlen]) (pattern)". "10 %l" means 10 random lowercase letters. "10,30 [aeiou]" means between 10-30 vowels. function: Just call (as f()) and return result. table or userdata: Call v.__random() and return result. @usage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_string (val, msg)
+
+Test that val is a string.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_table (val, msg)
+
+Test that val is a table.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_thread (val, msg)
+
+Test that val is a thread (coroutine).
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_true (got, msg)
+
+got == true. (Named "assert_true" to not conflict with standard assert.)
+
+
+
Parameters
+
+
+
+ got:
+
+
+
+ msg: Message to display with the result.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_userdata (val, msg)
+
+Test that val is a userdata (light or heavy).
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
fail (msg, no_exit)
+
+Fail a test.
+
+
+
Parameters
+
+
+
+ msg:
+
+
+
+ no_exit: Unless set to true, the presence of any failures causes the test suite to terminate with an exit status of 1.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
is_test_key (k)
+
+Check if a function name should be considered a test key. Defaults to functions starting or ending with "test", with leading underscores allowed.
+
+
+
Parameters
+
+
+
+ k:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
random_bool ()
+
+Get a random bool.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
random_float (low, high)
+
+Get a random float low <= x < high.
+
+
+
Parameters
+
+
+
+ low:
+
+
+
+ high:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
random_int (low, high)
+
+Get a random value low <= x <= high.
+
+
+
Parameters
+
+
+
+ low:
+
+
+
+ high:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
run (hooks, suite_filter)
+
+Run all known test suites, with given configuration hooks.
+
+
+
Parameters
+
+
+
+ hooks: Override the default hooks.
+
+
+
+ suite_filter: If set, only run suite(s) with names matching this pattern.
+
+
+
+
+
+
+
+
Usage:
+If no hooks are provided and arg[1] == "-v", the verbose_hooks will be used.
+
+
+
+
+
+
+
+
+
+
+
set_seed (s)
+
+Set random seed.
+
+
+
Parameters
+
+
+
+ s:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
skip (msg)
+
+Skip a test, with a note, e.g. "TODO".
+
+
+
Parameters
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
suite (modname)
+
+Add a file as a test suite.
+
+
+
Parameters
+
+
+
+ modname: The module to load as a suite. The file is interpreted in the same manner as require "modname". Which functions are tests is determined by is_test_key(name).
+
+Test that the function raises an error when called.
+
+
+
Parameters
+
+
+
+ f:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_false (got, msg)
+
+got == false.
+
+
+
Parameters
+
+
+
+ got:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_function (val, msg)
+
+Test that val is a function.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_gt (lim, val, msg)
+
+val > lim.
+
+
+
Parameters
+
+
+
+ lim:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_gte (lim, val, msg)
+
+val >= lim.
+
+
+
Parameters
+
+
+
+ lim:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_len (len, val, msg)
+
+#val == len.
+
+
+
Parameters
+
+
+
+ len:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_lt (lim, val, msg)
+
+val < lim.
+
+
+
Parameters
+
+
+
+ lim:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_lte (lim, val, msg)
+
+val <= lim.
+
+
+
Parameters
+
+
+
+ lim:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_match (pat, s, msg)
+
+Test that the string s matches the pattern exp.
+
+
+
Parameters
+
+
+
+ pat:
+
+
+
+ s:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_metatable (exp, val, msg)
+
+Test that a value has the expected metatable.
+
+
+
Parameters
+
+
+
+ exp:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_boolean (val, msg)
+
+Test that val is not a boolean.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_equal (exp, got, msg)
+
+exp ~= got.
+
+
+
Parameters
+
+
+
+ exp:
+
+
+
+ got:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_function (val, msg)
+
+Test that val is not a function.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_len (len, val, msg)
+
+#val ~= len.
+
+
+
Parameters
+
+
+
+ len:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_match (pat, s, msg)
+
+Test that the string s doesn't match the pattern exp.
+
+
+
Parameters
+
+
+
+ pat:
+
+
+
+ s:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_metatable (exp, val, msg)
+
+Test that a value does not have a given metatable.
+
+
+
Parameters
+
+
+
+ exp:
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_number (val, msg)
+
+Test that val is not a number.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_string (val, msg)
+
+Test that val is not a string.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_table (val, msg)
+
+Test that val is not a table.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_thread (val, msg)
+
+Test that val is not a thread (coroutine).
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_not_userdata (val, msg)
+
+Test that val is not a userdata (light or heavy).
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_number (val, msg)
+
+Test that val is a number.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_random (opt, f, ...)
+
+Run a test case with randomly instantiated arguments, running the test function f opt.count (default: 100) times.
+
+
+
Parameters
+
+
+
+ opt: A table with options, or just a test name string. opt.count: how many random trials to perform opt.seed: Start the batch of trials with a specific seed opt.always: Always test these seeds (for regressions) opt.show_progress: Whether to print a . after every opt.tick trials. opt.seed_limit: Max seed to allow. opt.max_failures, max_errors, max_skips: Give up after X of each.
+
+
+
+ f: A test function, run as f(unpack(randomized_args(...)))
+
+
+
+ ...: the arg specification. For each argument, creates a random instance of that type. boolean: return true or false number n: returns 0 <= x < n, or -n <= x < n if negative. If n has a decimal component, so will the result. string: Specifiedd as "(len[,maxlen]) (pattern)". "10 %l" means 10 random lowercase letters. "10,30 [aeiou]" means between 10-30 vowels. function: Just call (as f()) and return result. table or userdata: Call v.__random() and return result. @usage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_string (val, msg)
+
+Test that val is a string.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_table (val, msg)
+
+Test that val is a table.
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_thread (val, msg)
+
+Test that val is a thread (coroutine).
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_true (got, msg)
+
+got == true. (Named "assert_true" to not conflict with standard assert.)
+
+
+
Parameters
+
+
+
+ got:
+
+
+
+ msg: Message to display with the result.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
assert_userdata (val, msg)
+
+Test that val is a userdata (light or heavy).
+
+
+
Parameters
+
+
+
+ val:
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
fail (msg, no_exit)
+
+Fail a test.
+
+
+
Parameters
+
+
+
+ msg:
+
+
+
+ no_exit: Unless set to true, the presence of any failures causes the test suite to terminate with an exit status of 1.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
is_test_key (k)
+
+Check if a function name should be considered a test key. Defaults to functions starting or ending with "test", with leading underscores allowed.
+
+
+
Parameters
+
+
+
+ k:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
random_bool ()
+
+Get a random bool.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
random_float (low, high)
+
+Get a random float low <= x < high.
+
+
+
Parameters
+
+
+
+ low:
+
+
+
+ high:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
random_int (low, high)
+
+Get a random value low <= x <= high.
+
+
+
Parameters
+
+
+
+ low:
+
+
+
+ high:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
run (hooks, suite_filter)
+
+Run all known test suites, with given configuration hooks.
+
+
+
Parameters
+
+
+
+ hooks: Override the default hooks.
+
+
+
+ suite_filter: If set, only run suite(s) with names matching this pattern.
+
+
+
+
+
+
+
+
Usage:
+If no hooks are provided and arg[1] == "-v", the verbose_hooks will be used.
+
+
+
+
+
+
+
+
+
+
+
set_seed (s)
+
+Set random seed.
+
+
+
Parameters
+
+
+
+ s:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
skip (msg)
+
+Skip a test, with a note, e.g. "TODO".
+
+
+
Parameters
+
+
+
+ msg:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
suite (modname)
+
+Add a file as a test suite.
+
+
+
Parameters
+
+
+
+ modname: The module to load as a suite. The file is interpreted in the same manner as require "modname". Which functions are tests is determined by is_test_key(name).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rapanui-test/lunatest/suite-hooks-fail.lua b/rapanui-test/lunatest/suite-hooks-fail.lua
new file mode 100644
index 0000000..c71d3cf
--- /dev/null
+++ b/rapanui-test/lunatest/suite-hooks-fail.lua
@@ -0,0 +1,13 @@
+module(..., package.seeall)
+
+-- Either returning false or erroring out in suite_setup()
+-- will prevent the suite from running.
+function suite_setup()
+ print "\n\n-- (about to fail and abort suite)"
+ if true then return false end
+ error("don't run this suite")
+end
+
+function test_never_run()
+ assert_true(false, "this suite should never be run")
+end
diff --git a/rapanui-test/lunatest/suite-hooks.lua b/rapanui-test/lunatest/suite-hooks.lua
new file mode 100644
index 0000000..514d838
--- /dev/null
+++ b/rapanui-test/lunatest/suite-hooks.lua
@@ -0,0 +1,13 @@
+module(..., package.seeall)
+
+function suite_setup()
+ print "\n\n-- running suite setup hook"
+end
+
+function suite_teardown()
+ print "\n\n-- running suite teardown hook"
+end
+
+function test_ok()
+ assert_true(true)
+end
diff --git a/rapanui-test/lunatest/suite-with-random-tests.lua b/rapanui-test/lunatest/suite-with-random-tests.lua
new file mode 100644
index 0000000..5ba4103
--- /dev/null
+++ b/rapanui-test/lunatest/suite-with-random-tests.lua
@@ -0,0 +1,172 @@
+module(..., package.seeall)
+
+-- Several random tests, with arguments specified both
+-- via functions and via consts.
+
+local seed = os.time()
+lunatest.set_seed(seed)
+print("Seed is ", seed)
+
+local random_bool, random_int =
+ lunatest.random_bool, lunatest.random_int
+local random_float, random_string =
+ lunatest.random_float, lunatest.random_string
+
+function test_random_bool()
+ assert_random("bool is t or f",
+ function (b)
+ return b == true or b == false
+ end, true)
+end
+
+
+function test_random_bool2()
+ assert_random("bool is t or f",
+ function ()
+ return random_bool()
+ end, true)
+end
+
+
+function test_random_int()
+ assert_random({name="pos int", count=1000 },
+ function ()
+ local ri = random_int(1, 10)
+ return ri >= 1 and ri <= 10
+ end)
+end
+
+function test_random_int2()
+ assert_random({ name="pos int", count=1000 },
+ function (ri)
+ return ri >= 0 and ri <= 10
+ end, 10)
+end
+
+function test_neg_int()
+ assert_random({ name="neg int", count=1000 },
+ function ()
+ local ni = random_int(-math.huge, 0)
+ return ni < 0
+ end)
+end
+
+function test_neg_or_pos_int()
+ assert_random({ name="neg int", count=1000 },
+ function (i)
+ assert_number(i)
+ assert_gte(-400, i)
+ assert_lte(400, i)
+ end, -400)
+end
+
+
+function test_random_int_trio()
+ assert_random({ name="three ints", count=1000 },
+ function ()
+ local ri = random_int
+ local x, y, z = ri(0, 5), ri(10, 50), ri(50, 100)
+ assert_gt(x, y)
+ assert_gte(y, z)
+ assert_lt(z, x)
+ end)
+end
+
+function test_random_float()
+ assert_random({ name="float", count=1000 },
+ function ()
+ local rf = random_float
+ assert_gt(21.5, rf(30, 40))
+ end)
+end
+
+function test_random_float2()
+ assert_random({ name="float", count=1000 },
+ function (x)
+ assert_gt(0, x)
+ end, 55.5)
+end
+
+
+function test_random_string()
+ assert_random("vowels",
+ function (x)
+ local len = string.len(x)
+ assert_lte(10, len)
+ assert_match("[aeiou]+", x)
+ end, "10 aeiou")
+end
+
+function test_random_string2()
+ -- In the first argument, .always={...} is a list of seeds
+ -- that will be run every trial.
+ assert_random( { name="alpha",
+ always={ 6483877599982, 9948212639558 }},
+ function (x)
+ local len = string.len(x)
+ assert_gte(10, len)
+ assert_lte(30, len)
+ assert_match(".+", x)
+ end, "10,30 %a")
+end
+
+function test_coinflip()
+ local function rbool() return random_bool() end
+ -- typically, this would go in a metatable...
+ local coin = { __random = rbool }
+ assert_random("flip",
+ function(flip)
+ assert_boolean(flip)
+ end, coin)
+end
+
+
+function test_random_int_bounds()
+ for run, pair in ipairs{ {1, 2}, {2, 10}, {-1, 1},
+ {-100, 100}, {-1, 0}, {0, 1} } do
+ assert_random("int_bounds",
+ function()
+ local ri = random_int(pair[1], pair[2])
+ assert_gte(pair[1], ri)
+ assert_lte(pair[2], ri)
+ end)
+ end
+end
+
+function test_random_int_bounds()
+ for run, pair in ipairs{ {1, 2}, {2, 10}, {-1, 1},
+ {-100, 100}, {-1, 0}, {0, 1} } do
+ assert_random("float_bounds",
+ function()
+ local ri = random_float(pair[1], pair[2])
+ assert_gte(pair[1], ri)
+ assert_lt(pair[2], ri)
+ end)
+ end
+end
+
+
+function test_random_str_len()
+ local fmt = string.format
+ for _,l in ipairs{ 1, 5, 10, 15, 30 } do
+ assert_random("strlen",
+ function()
+ local pat = fmt("%d %%l", l)
+ local rs = random_string(pat)
+ assert_len(l, rs)
+ assert_match("^%l+$", rs)
+ end)
+ end
+end
+
+
+function test_random_str_range()
+ local fmt = string.format
+ assert_random("strlen2",
+ function()
+ local pat = fmt("%d a-z", 15)
+ local rs = random_string(pat)
+ assert_len(15, rs)
+ assert_match("^%l+$", rs)
+ end)
+end
diff --git a/rapanui-test/lunatest/test-error_handler.lua b/rapanui-test/lunatest/test-error_handler.lua
new file mode 100644
index 0000000..333bc29
--- /dev/null
+++ b/rapanui-test/lunatest/test-error_handler.lua
@@ -0,0 +1,9 @@
+require "lunatest"
+
+-- This should error out with a more meaningful error message
+-- than "./lunatest.lua:608: attempt to index local 'e' (a function value)".
+function test_foo()
+ error(function() return 2 end)
+end
+
+lunatest.run()
diff --git a/rapanui-test/lunatest/test-teardown_fail.lua b/rapanui-test/lunatest/test-teardown_fail.lua
new file mode 100644
index 0000000..96b0ea7
--- /dev/null
+++ b/rapanui-test/lunatest/test-teardown_fail.lua
@@ -0,0 +1,39 @@
+require "lunatest"
+
+function busywait(n)
+ n = n or 10000
+ for i=1,n do
+ for j=1,n do
+ -- no-op
+ end
+ end
+end
+
+function setup(n)
+ -- show that test case time does not include setup
+ busywait()
+end
+
+local teardown_count = 0
+
+function teardown(n)
+ teardown_count = teardown_count + 1
+ if teardown_count > 1 then error("*boom*") end
+end
+
+function test_fail_but_expect_teardown()
+ error("fail whale")
+end
+
+function test_fail_but_expect_teardown_2()
+ error("boom")
+end
+
+local caught_teardown_error = false
+
+xpcall(lunatest.run(), function(x)
+ print("ARGH", x)
+ caught_teardown_error = true
+ end)
+
+assert(caught_teardown_error, "Didn't catch teardown failure.")
diff --git a/rapanui-test/lunatest/test.lua b/rapanui-test/lunatest/test.lua
new file mode 100644
index 0000000..71b4b77
--- /dev/null
+++ b/rapanui-test/lunatest/test.lua
@@ -0,0 +1,203 @@
+pcall(require, "luacov") --measure code coverage, if luacov is present
+require "lunatest"
+
+print '=============================='
+print('To test just the random suite, add "-s random" to the command line')
+print('To run just some tests, add "-t [pattern]"')
+print '=============================='
+
+
+lunatest.suite("suite-with-random-tests")
+lunatest.suite("suite-hooks")
+lunatest.suite("suite-hooks-fail")
+
+function test_fail()
+ -- the true here is so the test run as a whole still succeeds.
+ fail("This one *should* fail.", true)
+end
+
+function test_assert()
+ assert_true(true)
+end
+
+function test_skip()
+ skip("(reason why this test was skipped)")
+end
+
+function test_assert_false()
+ assert_false(false)
+end
+
+function test_assert_nil()
+ assert_nil(nil)
+end
+
+
+function test_assert_not_nil()
+ assert_not_nil("foo")
+end
+
+function test_assert_equal()
+ assert_equal(4, 4)
+end
+
+function test_assert_equal_tolerance()
+ assert_equal(4, 4.0001, 0.0001, "Should approximately match")
+end
+
+function test_assert_not_equal()
+ assert_not_equal("perl", "quality")
+end
+
+function test_assert_gt()
+ assert_gt(8, 400)
+end
+
+function test_assert_gte()
+ assert_gte(8, 400)
+ assert_gte(8, 8)
+end
+
+function test_assert_lt()
+ assert_lt(8, -2)
+end
+
+function test_assert_lte()
+ assert_lte(8, -2)
+ assert_lte(8, 8)
+end
+
+function test_assert_len()
+ assert_len(3, { "foo", "bar", "baz" })
+end
+
+function test_assert_not_len()
+ assert_not_len(23, { "foo", "bar", "baz" })
+end
+
+function test_assert_match()
+ assert_match("oo", "string with foo in it")
+end
+
+function test_assert_not_match()
+ assert_not_match("abba zabba", "foo")
+end
+
+function test_assert_boolean()
+ assert_boolean(true)
+ assert_boolean(false)
+end
+
+function test_assert_not_boolean()
+ assert_not_boolean("cheesecake")
+end
+
+function test_assert_number()
+ assert_number(47)
+ assert_number(0)
+ assert_number(math.huge)
+ assert_number(-math.huge)
+end
+
+function test_assert_not_number()
+ assert_not_number(_G)
+ assert_not_number("abc")
+ assert_not_number({1, 2, 3})
+ assert_not_number(false)
+ assert_not_number(function () return 3 end)
+end
+
+function test_assert_string()
+ assert_string("yarn")
+ assert_string("")
+end
+
+function test_assert_not_string()
+ assert_not_string(23)
+ assert_not_string(true)
+ assert_not_string(false)
+ assert_not_string({"1", "2", "3"})
+end
+
+function test_assert_table()
+ assert_table({})
+ assert_table({"1", "2", "3"})
+ assert_table({ foo=true, bar=true, baz=true })
+end
+
+function test_assert_not_table()
+ assert_not_table(nil)
+ assert_not_table(23)
+ assert_not_table("lapdesk")
+ assert_not_table(false)
+ assert_not_table(function () return 3 end)
+end
+
+function test_assert_function()
+ assert_function(function() return "*splat*" end)
+ assert_function(string.format)
+end
+
+function test_assert_not_function()
+ assert_not_function(nil)
+ assert_not_function(23)
+ assert_not_function("lapdesk")
+ assert_not_function(false)
+ assert_not_function(coroutine.create(function () return 3 end))
+ assert_not_function({"1", "2", "3"})
+ assert_not_function({ foo=true, bar=true, baz=true })
+end
+
+function test_assert_thread()
+ assert_thread(coroutine.create(function () return 3 end))
+end
+
+function test_assert_not_thread()
+ assert_not_thread(nil)
+ assert_not_thread(23)
+ assert_not_thread("lapdesk")
+ assert_not_thread(false)
+ assert_not_thread(function () return 3 end)
+ assert_not_thread({"1", "2", "3"})
+ assert_not_thread({ foo=true, bar=true, baz=true })
+end
+
+function test_assert_userdata()
+ assert_userdata(io.open("test.lua", "r"))
+end
+
+function test_assert_not_userdata()
+ assert_not_userdata(nil)
+ assert_not_userdata(23)
+ assert_not_userdata("lapdesk")
+ assert_not_userdata(false)
+ assert_not_userdata(function () return 3 end)
+ assert_not_userdata({"1", "2", "3"})
+ assert_not_userdata({ foo=true, bar=true, baz=true })
+end
+
+function test_assert_metatable()
+ assert_metatable(getmetatable("any string"), "foo")
+ local t = { __index=string }
+ local val = setmetatable( { 1 }, t)
+ assert_metatable(t, val)
+end
+
+function test_assert_not_metatable()
+ assert_not_metatable(getmetatable("any string"), 23)
+end
+
+function test_assert_error()
+ assert_error(function ()
+ error("*crash!*")
+ end)
+end
+
+-- This caused a crash when matching a string with invalid % escapes.
+-- Thanks to Diab Jerius for the bugfix.
+function test_failure_formatting()
+ local inv_esc = "str with invalid escape %( in it"
+ assert_match(inv_esc, inv_esc, "Should fail but not crash")
+end
+
+lunatest.run()
diff --git a/rapanui-test/lunatest/test_hamcrest.lua b/rapanui-test/lunatest/test_hamcrest.lua
new file mode 100644
index 0000000..8090340
--- /dev/null
+++ b/rapanui-test/lunatest/test_hamcrest.lua
@@ -0,0 +1,48 @@
+--
+-- Author: Janne Sinivirta
+-- Date: 9/19/12
+--
+require('lunatest')
+require('lunahamcrest')
+
+function test_equal_to()
+ assert_that(4, equal_to(4))
+ assert_that("ok", equal_to("ok"))
+end
+
+function test_not()
+ assert_that(4, is_not(equal_to(5)))
+ assert_that(5, is_not(is_nil()))
+end
+
+function test_syntactic_sugars()
+ assert_that(4, is(equal_to(4)))
+ assert_that(nil, is_nil())
+end
+
+function test_of_type()
+ assert_that("test", is(of_type("string")))
+ assert_that(4, is(of_type("number")))
+end
+
+function test_contains_string()
+ assert_that("testable", contains_string("test"))
+ assert_that("testable", contains_string("tab"))
+ assert_that("testable", contains_string("testable"))
+end
+
+function test_starts_with()
+ assert_that("testable", starts_with("tes"))
+ assert_that("testable", starts_with("testable"))
+end
+
+function test_starts_with()
+ assert_that("testable", equals_ignoring_case("tesTaBlE"))
+end
+
+function test_greater_than()
+ assert_that(3, greater_than(2))
+ assert_that(-1, greater_than(-2))
+ end
+
+lunatest.run()
diff --git a/rapanui-test/mockobjects/MockConstants.lua b/rapanui-test/mockobjects/MockConstants.lua
new file mode 100644
index 0000000..f470518
--- /dev/null
+++ b/rapanui-test/mockobjects/MockConstants.lua
@@ -0,0 +1,15 @@
+-- Author: Marko Pukari
+-- Date: 12/2/12
+
+MockConstants = {
+ WIDTH = 1,
+ HEIGHT = 2,
+ SCREENWIDTH = 3,
+ SCREENHEIGHT = 4,
+ OFFSET_X = -1,
+ OFFSET_Y = 1,
+ ORIGINALWIDTH = 4,
+ ORIGINALHEIGHT = 6,
+ WINDOWNAME = "mainwindow",
+ MAIN_LAYER = "mainlayer"
+}
\ No newline at end of file
diff --git a/rapanui-test/mockobjects/MockLayer.lua b/rapanui-test/mockobjects/MockLayer.lua
new file mode 100644
index 0000000..f38bf0f
--- /dev/null
+++ b/rapanui-test/mockobjects/MockLayer.lua
@@ -0,0 +1,49 @@
+-- Author: Marko Pukari
+-- Date: 11/30/12
+
+function createTestLayer(name,viewport,partition)
+ local MockLayer = {
+ setPartitionCalled = 0,
+ setViewportCalled = 0,
+ clearCalled = 0,
+ insertPropCalled = 0,
+ MOAIVIEWPORT = viewport,
+ MOAIPARTITION = partition,
+ name = name
+ }
+
+ function MockLayer:setViewport(viewport)
+ MockLayer.setViewportCalled = MockLayer.setViewportCalled + 1
+ assert_that(viewport.name,is(equal_to(MockLayer.MOAIVIEWPORT.name)))
+ end
+
+ function MockLayer:setPartition(partition)
+ assert_that(partition.name,is(equal_to(MockLayer.MOAIPARTITION.name)))
+ MockLayer.setPartitionCalled = MockLayer.setPartitionCalled + 1
+ end
+
+ function MockLayer:clear()
+ MockLayer.clearCalled=MockLayer.clearCalled + 1
+ end
+
+ function MockLayer:insertProp(prop)
+ self.insertPropCalled = self.insertPropCalled + 1
+ self.MOAIPARTITION:insertProp(prop)
+ end
+
+ function MockLayer:reset()
+ self.setPartitionCalled = 0
+ self.setViewportCalled = 0
+ self.clearCalled = 0
+ self.insertPropCalled = 0
+ self.removePropCalled = 0
+ end
+
+ function MockLayer:removeProp(prop)
+ self.removePropCalled = self.removePropCalled + 1
+ self.MOAIPARTITION:removeProp(prop)
+ end
+
+
+ return MockLayer
+end
diff --git a/rapanui-test/mockobjects/MockMOAILayer2D.lua b/rapanui-test/mockobjects/MockMOAILayer2D.lua
new file mode 100644
index 0000000..f87cc40
--- /dev/null
+++ b/rapanui-test/mockobjects/MockMOAILayer2D.lua
@@ -0,0 +1,24 @@
+-- Author: Marko Pukari
+-- Date: 11/30/12
+
+function createMockMOAILayer2D(...)
+
+ local MockMOAILayer2D = {
+ newCalled = 0,
+ i=0,
+ layers = arg
+ }
+
+ MockMOAILayer2D.new = function()
+ MockMOAILayer2D.newCalled = MockMOAILayer2D.newCalled + 1
+ MockMOAILayer2D.i = MockMOAILayer2D.i + 1
+ return MockMOAILayer2D.layers[MockMOAILayer2D.i]
+ end
+
+ function MockMOAILayer2D:reset()
+ self.newCalled = 0;
+ self.i=0
+ end
+
+ return MockMOAILayer2D
+end
\ No newline at end of file
diff --git a/rapanui-test/mockobjects/MockMOAIPartition.lua b/rapanui-test/mockobjects/MockMOAIPartition.lua
new file mode 100644
index 0000000..a5ebed1
--- /dev/null
+++ b/rapanui-test/mockobjects/MockMOAIPartition.lua
@@ -0,0 +1,21 @@
+-- Author: Marko Pukari
+-- Date: 11/30/12
+
+function createMockMOAIPartition(partition)
+ local MockMOAIPartition = {
+ newCalled = 0,
+ PARTITION = partition
+ }
+
+ function MockMOAIPartition.new()
+ MockMOAIPartition.newCalled = MockMOAIPartition.newCalled + 1
+ return MockMOAIPartition.PARTITION
+ end
+
+ function MockMOAIPartition:reset()
+ self.newCalled = 0
+ end
+
+ return MockMOAIPartition
+end
+
diff --git a/rapanui-test/mockobjects/MockMOAISim.lua b/rapanui-test/mockobjects/MockMOAISim.lua
new file mode 100644
index 0000000..9cf87a2
--- /dev/null
+++ b/rapanui-test/mockobjects/MockMOAISim.lua
@@ -0,0 +1,29 @@
+-- Author: Marko Pukari
+-- Date: 11/30/12
+
+function createMockMOAISim()
+
+ local MockMOAISim = {
+ pushRenderPassCalled = 0,
+ openWindowCalled = 0
+ }
+
+ MockMOAISim.pushRenderPass = function(layer)
+ assert_not_nil(layer)
+ MockMOAISim.pushRenderPassCalled = MOAISim.pushRenderPassCalled + 1
+ end
+
+ function MockMOAISim.openWindow(name, screenlwidth, screenHeight)
+ assert_that(screenlwidth,is(equal_to(MockConstants.SCREENWIDTH)))
+ assert_that(screenHeight,is(equal_to(MockConstants.SCREENHEIGHT)))
+ assert_that(name,is(equal_to(MockConstants.WINDOWNAME)))
+ MockMOAISim.openWindowCalled = MockMOAISim.openWindowCalled + 1
+ end
+
+ function MockMOAISim:reset()
+ self.pushRenderPassCalled = 0
+ self.openWindowCalled = 0
+ end
+
+ return MockMOAISim
+end
\ No newline at end of file
diff --git a/rapanui-test/mockobjects/MockMOAIViewport.lua b/rapanui-test/mockobjects/MockMOAIViewport.lua
new file mode 100644
index 0000000..eb5ca2b
--- /dev/null
+++ b/rapanui-test/mockobjects/MockMOAIViewport.lua
@@ -0,0 +1,22 @@
+-- Author: Marko Pukari
+-- Date: 11/30/12
+
+function createMockMOAIViewport(viewport)
+
+ MockMOAIViewport = {
+ newCalled = 0,
+ VIEWPORT = viewport,
+ name="testViewport"
+ }
+
+ MockMOAIViewport.new = function()
+ MockMOAIViewport.newCalled = MockMOAIViewport.newCalled + 1
+ return MockMOAIViewport.VIEWPORT
+ end
+
+ function MockMOAIViewport:reset()
+ self.newCalled = 0
+ end
+
+ return MockMOAIViewport
+end
\ No newline at end of file
diff --git a/rapanui-test/mockobjects/MockPartition.lua b/rapanui-test/mockobjects/MockPartition.lua
new file mode 100644
index 0000000..0f21243
--- /dev/null
+++ b/rapanui-test/mockobjects/MockPartition.lua
@@ -0,0 +1,28 @@
+-- Author: Marko Pukari
+-- Date: 11/30/12
+
+function createPartition(name)
+ local MockPartition = {
+ insertPropCalled = 0,
+ removePropCalled = 0,
+ name="TEST_PARTITION"
+ }
+
+ function MockPartition:insertProp(prop)
+ self.insertPropCalled = self.insertPropCalled + 1
+ end
+
+ function MockPartition:removeProp(prop)
+ self.removePropCalled = self.removePropCalled + 1
+ end
+
+ MockPartition.name = name
+
+ function MockPartition:reset()
+ self.insertPropCalled = 0
+ self.removePropCalled = 0
+ end
+
+ return MockPartition
+end
+
diff --git a/rapanui-test/mockobjects/MockRNGroup.lua b/rapanui-test/mockobjects/MockRNGroup.lua
new file mode 100644
index 0000000..fd6c390
--- /dev/null
+++ b/rapanui-test/mockobjects/MockRNGroup.lua
@@ -0,0 +1,26 @@
+-- Author: Marko Pukari
+-- Date: 12/2/12
+
+function createMockRNGroup()
+ MockRNGroup = {
+ newCalled = 0,
+ insertCalled = 0
+ }
+
+ function MockRNGroup:new()
+ self.newCalled = self.newCalled + 1
+ return self
+ end
+
+ function MockRNGroup:insert(rnobject)
+ assert_equal(rnobject,RNObject)
+ self.insertCalled = self.insertCalled + 1
+ end
+
+ function MockRNGroup:reset()
+ self.newCalled = 0
+ self.insertCalled = 0
+ end
+
+ return MockRNGroup
+end
\ No newline at end of file
diff --git a/rapanui-test/mockobjects/MockRNInputManager.lua b/rapanui-test/mockobjects/MockRNInputManager.lua
new file mode 100644
index 0000000..74fa6fc
--- /dev/null
+++ b/rapanui-test/mockobjects/MockRNInputManager.lua
@@ -0,0 +1,20 @@
+-- Author: Marko Pukari
+-- Date: 12/2/12
+
+function createMockRNInputManager()
+ MockRNInputManager = {
+ setGlobalRNScreenCalled = 0
+ }
+
+ function MockRNInputManager.setGlobalRNScreen(screen)
+ assert_true(screen == RNScreen)
+ MockRNInputManager.setGlobalRNScreenCalled = MockRNInputManager.setGlobalRNScreenCalled + 1
+ return self
+ end
+
+ function MockRNInputManager:reset()
+ MockRNInputManager.setGlobalRNScreenCalled = 0
+ end
+
+ return MockRNInputManager
+end
\ No newline at end of file
diff --git a/rapanui-test/mockobjects/MockRNLayer.lua b/rapanui-test/mockobjects/MockRNLayer.lua
new file mode 100644
index 0000000..943fa58
--- /dev/null
+++ b/rapanui-test/mockobjects/MockRNLayer.lua
@@ -0,0 +1,19 @@
+function createMockRNLayer(expectGetBytName)
+
+ MockRNLayer = {
+ getCalled = 0,
+ expectName = expectGetBytName,
+ MAIN_LAYER = "mainlayer"
+ }
+
+ function MockRNLayer.new()
+ return MockRNLayer
+ end
+ function MockRNLayer:get(name)
+ assert_that(name,is(equal_to(self.expectName)))
+ return createTestLayer("main",{},{})
+ end
+
+ return MockRNLayer
+end
+
diff --git a/rapanui-test/mockobjects/MockRNObject.lua b/rapanui-test/mockobjects/MockRNObject.lua
new file mode 100644
index 0000000..6a4e2e2
--- /dev/null
+++ b/rapanui-test/mockobjects/MockRNObject.lua
@@ -0,0 +1,52 @@
+-- Author: Marko Pukari
+-- Date: 11/30/12
+
+function createRNObject(name,prop)
+ local MockRNObject = {
+ setLocatingModeCalled = 0,
+ setParentSceneCalled = 0,
+ updateLocationCalled = 0,
+ newCalled = 0,
+ initWithImage2Called = 0,
+ MOAIPROP = prop,
+ name = name,
+ originalWidth = MockConstants.ORIGINALWIDTH,
+ originalHeight = MockConstants.ORIGINALHEIGHT
+ }
+
+ function MockRNObject:new()
+ self.newCalled = self.newCalled + 1
+ return self
+ end
+
+ function MockRNObject:initWithImage2(image)
+ self.initWithImage2Called = self.initWithImage2Called + 1
+ return self,{}
+
+ end
+
+ function MockRNObject:getProp()
+ return self.MOAIPROP
+ end
+
+ function MockRNObject:setLocatingMode(mode)
+ self.setLocatingModeCalled = self.setLocatingModeCalled + 1
+ end
+
+ function MockRNObject:setParentScene(object)
+ self.setParentSceneCalled = self.setParentSceneCalled + 1
+ end
+
+ function MockRNObject:updateLocation()
+ self.updateLocationCalled = self.updateLocationCalled + 1
+ end
+
+ function MockRNObject:reset()
+ self.setLocatingModeCalled = 0
+ self.setParentSceneCalled = 0
+ self.updateLocationCalled = 0
+ self.newCalled = 0
+ end
+
+ return MockRNObject
+end
diff --git a/rapanui-test/mockobjects/MockRNScreen.lua b/rapanui-test/mockobjects/MockRNScreen.lua
new file mode 100644
index 0000000..b3b0154
--- /dev/null
+++ b/rapanui-test/mockobjects/MockRNScreen.lua
@@ -0,0 +1,39 @@
+-- Author: Marko Pukari
+-- Date: 12/2/12
+
+function createMockRNScreen()
+ MockRNScreen = {
+ newCalled = 0,
+ initWithCalled = 0,
+ addRNObjectCalled = 0,
+ layers = MockRNLayer.new()
+ }
+
+ function MockRNScreen:new()
+ self.newCalled = self.newCalled + 1
+ return self
+ end
+
+ function MockRNScreen:initWith(lwidth, lheight, screenlwidth, screenHeight)
+ assert_that(lwidth,is(equal_to(MockConstants.SCREENWIDTH)))
+ assert_that(lheight,is(equal_to(MockConstants.SCREENHEIGHT)))
+ assert_that(screenlwidth,is(equal_to(MockConstants.SCREENWIDTH)))
+ assert_that(screenHeight,is(equal_to(MockConstants.SCREENHEIGHT)))
+
+ self.initWithCalled = self.initWithCalled + 1
+ end
+
+ function MockRNScreen:addRNObject(rnobject,mode,layer)
+ assert_true(rnobject == RNObject)
+ assert_nil(mode)
+ self.addRNObjectCalled = self.addRNObjectCalled + 1
+ end
+
+ function MockRNScreen:reset()
+ --MockRNScreen.newCalled = 0
+ MockRNScreen.initWithCalled = 0
+ self.addRNObjectCalled = 0
+ end
+
+ return MockRNScreen
+end
\ No newline at end of file
diff --git a/rapanui-test/mockobjects/MockViewport.lua b/rapanui-test/mockobjects/MockViewport.lua
new file mode 100644
index 0000000..8e9c396
--- /dev/null
+++ b/rapanui-test/mockobjects/MockViewport.lua
@@ -0,0 +1,39 @@
+-- Author: Marko Pukari
+-- Date: 11/30/12
+
+require('MockConstants')
+function createViewport(name)
+ local MockViewport = {
+ setSizeCalled = 0,
+ setScaleCalled = 0,
+ setOffsetCalled = 0
+ }
+
+ function MockViewport:setSize(screenWidth,screenHeight)
+ assert_that(screenWidth,is(equal_to(MockConstants.SCREENWIDTH)))
+ assert_that(screenHeight,is(equal_to(MockConstants.SCREENHEIGHT)))
+ MockViewport.setSizeCalled = MockViewport.setSizeCalled + 1
+ end
+
+ function MockViewport:setScale(width,height)
+ assert_that(width,is(equal_to(MockConstants.WIDTH)))
+ assert_that(height,is(equal_to(-MockConstants.HEIGHT)))
+ MockViewport.setScaleCalled = MockViewport.setScaleCalled + 1
+ end
+
+ function MockViewport:setOffset(offset_x,offset_y)
+ assert_that(offset_x,is(equal_to(MockConstants.OFFSET_X)))
+ assert_that(offset_y,is(equal_to(MockConstants.OFFSET_Y)))
+ MockViewport.setOffsetCalled = MockViewport.setOffsetCalled + 1
+ end
+
+ function MockViewport:reset()
+ MockViewport.setSizeCalled = 0
+ MockViewport.setScaleCalled = 0
+ MockViewport.setOffsetCalled = 0
+ end
+
+ MockViewport.name=name
+
+ return MockViewport
+end
\ No newline at end of file