From 7947d7af17ff80410202b55af0f3f0f7eb3fbf95 Mon Sep 17 00:00:00 2001 From: Ilia Date: Sat, 10 Jun 2017 14:13:31 +0300 Subject: [PATCH] From luaunit to u-test (#46) From luaunit to u-test --- .gitmodules | 3 + CMakeLists.txt | 20 +-- ci/test_all.sh | 2 +- libs/u-test | 1 + tests/lua/bootstrap-tests.lua | 36 +++++ tests/lua/channel-stress.lua | 63 ++++++++ tests/lua/channel.lua | 120 ++++----------- tests/lua/gc.lua | 21 +-- tests/lua/metatable.lua | 177 ++++++++++++++++++++++ tests/lua/run_tests.lua | 55 ------- tests/lua/shared-table.lua | 87 +++++++++++ tests/lua/shared_table.lua | 269 ---------------------------------- tests/lua/test_utils.lua | 75 ---------- tests/lua/tests.lua | 17 +++ tests/lua/thread-stress.lua | 15 ++ tests/lua/thread.lua | 170 ++++++++++----------- tests/lua/type.lua | 16 +- 17 files changed, 534 insertions(+), 613 deletions(-) create mode 160000 libs/u-test create mode 100644 tests/lua/bootstrap-tests.lua create mode 100644 tests/lua/channel-stress.lua create mode 100644 tests/lua/metatable.lua delete mode 100755 tests/lua/run_tests.lua create mode 100644 tests/lua/shared-table.lua delete mode 100644 tests/lua/shared_table.lua delete mode 100644 tests/lua/test_utils.lua create mode 100755 tests/lua/tests.lua create mode 100644 tests/lua/thread-stress.lua diff --git a/.gitmodules b/.gitmodules index 177cb4d..7296607 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "libs/luaunit"] path = libs/luaunit url = https://github.com/bluebird75/luaunit +[submodule "libs/u-test"] + path = libs/u-test + url = https://github.com/IUdalov/u-test.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 78b8dbc..ceea478 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,9 @@ FILE(GLOB LUA_SOURCES src/lua/*.lua) if(APPLE) # Supress warning CMP0042 set(CMAKE_MACOSX_RPATH 1) + set(LIBRARY_EXT dylib) +else() + set(LIBRARY_EXT so) endif() add_library(effil SHARED ${SOURCES}) @@ -30,25 +33,24 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -g0 -DNDEBUG") #---------- # TESTS --- #---------- - FILE(GLOB TEST_SOURCES tests/cpp/*.cpp tests/cpp/*.h) -FILE(GLOB LUA_TEST_SOURCES tests/lua/run_tests.lua) set(GTEST_DIR libs/gtest/googletest) +set(LUA_TESTS tests/lua/tests.lua) include_directories(${GTEST_DIR}/include ${GTEST_DIR}) add_executable(tests ${TEST_SOURCES} ${GTEST_DIR}/src/gtest-all.cc) target_link_libraries(tests effil) -#---------- -# INSTALL - -#---------- -install(FILES ${LUA_SOURCES} ${LUA_TEST_SOURCES} - DESTINATION ${CMAKE_BINARY_DIR} - PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ) - #---------- # FORMAT - #---------- add_custom_target( format COMMAND clang-format -i ${TEST_SOURCES} ${SOURCES} ) + +#---------- +# INSTALL - +#---------- +install(FILES ${LUA_TESTS} + DESTINATION ${CMAKE_BINARY_DIR} + PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ) diff --git a/ci/test_all.sh b/ci/test_all.sh index b705d35..cac50ff 100755 --- a/ci/test_all.sh +++ b/ci/test_all.sh @@ -4,5 +4,5 @@ set -e for build_type in debug release; do mkdir -p $build_type (cd $build_type && cmake -DCMAKE_BUILD_TYPE=$build_type $@ .. && make -j4 install) - (cd $build_type && ./tests && lua run_tests.lua --extra-checks) + (cd $build_type && ./tests && STRESS=1 lua tests.lua) done diff --git a/libs/u-test b/libs/u-test new file mode 160000 index 0000000..34ee5de --- /dev/null +++ b/libs/u-test @@ -0,0 +1 @@ +Subproject commit 34ee5de696c2c12d8ded5793f2c5504f2e6ee168 diff --git a/tests/lua/bootstrap-tests.lua b/tests/lua/bootstrap-tests.lua new file mode 100644 index 0000000..96158cd --- /dev/null +++ b/tests/lua/bootstrap-tests.lua @@ -0,0 +1,36 @@ +effil = require "effil" +test = require "u-test" + +function default_tear_down() + collectgarbage() + effil.gc.collect() + -- effil.G is always present + -- thus, gc has one object + test.equal(effil.gc.count(), 1) +end + +function wait(timeInSec, condition, silent) + local result = false + local startTime = os.time() + while ( (os.time() - startTime) <= timeInSec) do + if condition ~= nil then + if type(condition) == 'function' then + if condition() then + result = true + break + end + else + if condition then + result = true + break + end + end + end + end + return result +end + + +function sleep(timeInSec, silent) + wait(timeInSec, nil, true) +end diff --git a/tests/lua/channel-stress.lua b/tests/lua/channel-stress.lua new file mode 100644 index 0000000..c228b6e --- /dev/null +++ b/tests/lua/channel-stress.lua @@ -0,0 +1,63 @@ +require "bootstrap-tests" + +test.channel_stress.tear_down = default_tear_down + +test.channel_stress.with_multiple_threads = function () + local exchange_channel, result_channel = effil.channel(), effil.channel() + + local threads_number = 1000 + for i = 1, threads_number do + effil.thread(function(exchange_channel, result_channel, indx) + if indx % 2 == 0 then + for i = 1, 10000 do + exchange_channel:push(indx .. "_".. i) + end + else + repeat + local ret = exchange_channel:pop(10) + if ret then + result_channel:push(ret) + end + until ret == nil + end + end + )(exchange_channel, result_channel, i) + end + + local data = {} + for i = 1, (threads_number / 2) * 10000 do + local ret = result_channel:pop(10) + test.is_not_nil(ret) + test.is_string(ret) + test.is_nil(data[ret]) + data[ret] = true + end + + for thr_id = 2, threads_number, 2 do + for iter = 1, 10000 do + test.is_true(data[thr_id .. "_".. iter]) + end + end +end + +test.channel_stress.timed_read = function () + local chan = effil.channel() + local delayed_writer = function(channel, delay) + require("effil").sleep(delay) + channel:push("hello!") + end + effil.thread(delayed_writer)(chan, 70) + + local function check_time(real_time, use_time, metric, result) + local start_time = os.time() + test.equal(chan:pop(use_time, metric), result) + test.almost_equal(os.time(), start_time + real_time, 1) + end + check_time(2, 2, nil, nil) -- second by default + check_time(2, 2, 's', nil) + check_time(60, 1, 'm', nil) + + local start_time = os.time() + test.equal(chan:pop(10), "hello!") + test.is_true(os.time() < start_time + 10) +end diff --git a/tests/lua/channel.lua b/tests/lua/channel.lua index 87a2c8f..431b2ad 100644 --- a/tests/lua/channel.lua +++ b/tests/lua/channel.lua @@ -1,38 +1,40 @@ -TestChannels = {tearDown = tearDown} +require "bootstrap-tests" -function TestChannels:testCapacityUsage() +test.channel.tear_down = default_tear_down + +test.channel.capacity_usage = function() local chan = effil.channel(2) - test.assertTrue(chan:push(14)) - test.assertTrue(chan:push(88)) - test.assertFalse(chan:push(1488)) + test.is_true(chan:push(14)) + test.is_true(chan:push(88)) + test.is_false(chan:push(1488)) - test.assertEquals(chan:pop(), 14) - test.assertEquals(chan:pop(), 88) - test.assertIsNil(chan:pop(0)) + test.equal(chan:pop(), 14) + test.equal(chan:pop(), 88) + test.is_nil(chan:pop(0)) - test.assertTrue(chan:push(14, 88), true) + test.is_true(chan:push(14, 88), true) local ret1, ret2 = chan:pop() - test.assertEquals(ret1, 14) - test.assertEquals(ret2, 88) + test.equal(ret1, 14) + test.equal(ret2, 88) end -function TestChannels:testRecursiveChannels() +test.channel.recursive = function () local chan1 = effil.channel() local chan2 = effil.channel() local msg1, msg2 = "first channel", "second channel" - test.assertTrue(chan1:push(msg1, chan2)) - test.assertTrue(chan2:push(msg2, chan1)) + test.is_true(chan1:push(msg1, chan2)) + test.is_true(chan2:push(msg2, chan1)) local ret1 = { chan1:pop() } - test.assertEquals(ret1[1], msg1) - test.assertEquals(type(ret1[2]), "userdata") + test.equal(ret1[1], msg1) + test.equal(type(ret1[2]), "userdata") local ret2 = { ret1[2]:pop() } - test.assertEquals(ret2[1], msg2) - test.assertEquals(type(ret2[2]), "userdata") + test.equal(ret2[1], msg2) + test.equal(type(ret2[2]), "userdata") end -function TestChannels:testWithThread() +test.channel.with_threads = function () local chan = effil.channel() local thread = effil.thread(function(chan) chan:push("message1") @@ -43,15 +45,15 @@ function TestChannels:testWithThread() )(chan) local start_time = os.time() - test.assertEquals(chan:pop(), "message1") + test.equal(chan:pop(), "message1") thread:wait() - test.assertEquals(chan:pop(0), "message2") - test.assertEquals(chan:pop(1), "message3") - test.assertEquals(chan:pop(1, 'm'), "message4") - test.assertTrue(os.time() < start_time + 1) + test.equal(chan:pop(0), "message2") + test.equal(chan:pop(1), "message3") + test.equal(chan:pop(1, 'm'), "message4") + test.is_true(os.time() < start_time + 1) end -function TestChannels:testWithSharedTable() +test.channel.with_shared_table = function () local chan = effil.channel() local table = effil.table() @@ -59,73 +61,9 @@ function TestChannels:testWithSharedTable() table.test_key = test_value chan:push(table) - test.assertEquals(chan:pop().test_key, test_value) + test.equal(chan:pop().test_key, test_value) table.channel = chan table.channel:push(test_value) - test.assertEquals(table.channel:pop(), test_value) + test.equal(table.channel:pop(), test_value) end - -if WITH_EXTRA_CHECKS then - -function TestChannels:testStressLoadWithMultipleThreads() - local exchange_channel, result_channel = effil.channel(), effil.channel() - - local threads_number = 1000 - for i = 1, threads_number do - effil.thread(function(exchange_channel, result_channel, indx) - if indx % 2 == 0 then - for i = 1, 10000 do - exchange_channel:push(indx .. "_".. i) - end - else - repeat - local ret = exchange_channel:pop(10) - if ret then - result_channel:push(ret) - end - until ret == nil - end - end - )(exchange_channel, result_channel, i) - end - - local data = {} - for i = 1, (threads_number / 2) * 10000 do - local ret = result_channel:pop(10) - test.assertNotIsNil(ret) - test.assertIsString(ret) - test.assertIsNil(data[ret]) - data[ret] = true - end - - for thr_id = 2, threads_number, 2 do - for iter = 1, 10000 do - test.assertTrue(data[thr_id .. "_".. iter]) - end - end -end - -function TestChannels:testTimedRead() - local chan = effil.channel() - local delayedWriter = function(channel, delay) - require("effil").sleep(delay) - channel:push("hello!") - end - effil.thread(delayedWriter)(chan, 70) - - local function check_time(real_time, use_time, metric, result) - local start_time = os.time() - test.assertEquals(chan:pop(use_time, metric), result) - test.assertAlmostEquals(os.time(), start_time + real_time, 1) - end - check_time(2, 2, nil, nil) -- second by default - check_time(2, 2, 's', nil) - check_time(60, 1, 'm', nil) - - local start_time = os.time() - test.assertEquals(chan:pop(10), "hello!") - test.assertTrue(os.time() < start_time + 10) -end - -end -- WITH_EXTRA_CHECKS diff --git a/tests/lua/gc.lua b/tests/lua/gc.lua index c149b03..12ec068 100644 --- a/tests/lua/gc.lua +++ b/tests/lua/gc.lua @@ -1,12 +1,13 @@ -local effil = require "effil" +require "bootstrap-tests" + local gc = effil.gc -TestGC = { tearDown = tearDown } +test.gc.tear_down = default_tear_down -function TestGC:testCleanup() +test.gc.cleanup = function () collectgarbage() gc.collect() - test.assertEquals(gc.count(), 1) + test.equal(gc.count(), 1) for i = 0, 10000 do local tmp = effil.table() @@ -14,27 +15,27 @@ function TestGC:testCleanup() collectgarbage() gc.collect() - test.assertEquals(gc.count(), 1) + test.equal(gc.count(), 1) end -function TestGC:testDisableGC() +test.gc.disable = function () local nobjects = 10000 collectgarbage() gc.collect() - test.assertEquals(gc.count(), 1) + test.equal(gc.count(), 1) gc.pause() - test.assertFalse(gc.enabled()) + test.is_false(gc.enabled()) for i = 1, nobjects do local tmp = effil.table() end - test.assertEquals(gc.count(), nobjects + 1) + test.equal(gc.count(), nobjects + 1) collectgarbage() gc.collect() - test.assertEquals(gc.count(), 1) + test.equal(gc.count(), 1) gc.resume() end \ No newline at end of file diff --git a/tests/lua/metatable.lua b/tests/lua/metatable.lua new file mode 100644 index 0000000..394d000 --- /dev/null +++ b/tests/lua/metatable.lua @@ -0,0 +1,177 @@ +require "bootstrap-tests" + +test.metatable.tear_down = function (metatable) + collectgarbage() + effil.gc.collect() + + -- if metatable is shared_table - it counts as gr object + -- and it will be destroyed after tear_down + if type(metatable) == "table" then + test.equal(effil.gc.count(), 1) + else + test.equal(effil.gc.count(), 2) + end +end + +local function run_test_with_different_metatables(name, ...) + test.metatable[name]({}, ...) + test.metatable[name](effil.table(), ...) +end + +test.metatable.index = function (metatable) + local share = effil.table() + metatable.__index = function(t, key) + return "mt_" .. effil.rawget(t, key) + end + effil.setmetatable(share, metatable) + + share.table_key = "table_value" + test.equal(share.table_key, "mt_table_value") +end + +test.metatable.new_index = function (metatable) + local share = effil.table() + metatable.__newindex = function(t, key, value) + effil.rawset(t, "mt_" .. key, "mt_" .. value) + end + effil.setmetatable(share, metatable) + + share.table_key = "table_value" + test.equal(share.mt_table_key, "mt_table_value") +end + +test.metatable.call = function (metatable) + local share = effil.table() + metatable.__call = function(t, val1, val2, val3) + return tostring(val1) .. "_" .. tostring(val2), tostring(val2) .. "_" .. tostring(val3) + end + effil.setmetatable(share, metatable) + + local first_ret, second_ret = share("val1", "val2", "val3") + test.equal(first_ret, "val1_val2") + test.equal(second_ret, "val2_val3") +end + +run_test_with_different_metatables("index") +run_test_with_different_metatables("new_index") +run_test_with_different_metatables("call") + +test.metatable.binary_op = function (metatable, metamethod, op, exp_value) + local testTable, operand = effil.table(), effil.table() + metatable['__' .. metamethod] = function(left, right) + left.was_called = true + return left.value .. '_'.. right.value + end + effil.setmetatable(testTable, metatable) + testTable.was_called = false + testTable.value = "left" + operand.value = "right" + local left_operand, right_operand = unpack({testTable, operand}) + test.equal(op(left_operand, right_operand), + exp_value == nil and "left_right" or exp_value) + test.is_true(testTable.was_called) +end + +local function test_binary_op(...) + run_test_with_different_metatables("binary_op", ...) +end + +test_binary_op("concat", function(a, b) return a .. b end) +test_binary_op("add", function(a, b) return a + b end) +test_binary_op("sub", function(a, b) return a - b end) +test_binary_op("mul", function(a, b) return a * b end) +test_binary_op("div", function(a, b) return a / b end) +test_binary_op("mod", function(a, b) return a % b end) +test_binary_op("pow", function(a, b) return a ^ b end) +test_binary_op("le", function(a, b) return a <= b end, true) +test_binary_op("lt", function(a, b) return a < b end, true) +test_binary_op("eq", function(a, b) return a == b end, true) + + +test.metatable.unary_op = function(metatable, metamethod, op) + local share = effil.table() + metatable['__' .. metamethod] = function(t) + t.was_called = true + return t.value .. "_suffix" + end + effil.setmetatable(share, metatable) + + share.was_called = false + share.value = "value" + test.equal(op(share), "value_suffix") + test.is_true(share.was_called) +end + +local function test_unary_op(...) + run_test_with_different_metatables("unary_op", ...) +end + +test_unary_op("unm", function(a) return -a end) +test_unary_op("tostring", function(a) return tostring(a) end) +test_unary_op("len", function(a) return #a end) + +test.shared_table_with_metatable.tear_down = default_tear_down + +test.shared_table_with_metatable.iterators = function (iterator_type) + local share = effil.table() + local iterator = iterator_type + effil.setmetatable(share, { + ["__" .. iterator] = function(table) + return function(t, key) + local effil = require 'effil' + local ret = (key and key * 2) or 1 + if ret > 2 ^ 10 then + return nil + end + return ret, effil.rawget(t, ret) + end, table + end + }) + -- Add some values + for i = 0, 10 do + local pow = 2 ^ i + share[pow] = math.random(pow) + end + -- Add some noise + for i = 1, 100 do + share[math.random(1000) * 10 - 1] = math.random(1000) + end + -- Check that *pairs iterator works + local pow_iter = 1 + for k,v in _G[iterator](share) do + test.equal(k, pow_iter) + test.equal(v, share[pow_iter]) + pow_iter = pow_iter * 2 + end + test.equal(pow_iter, 2 ^ 11) +end + +test.shared_table_with_metatable.iterators("pairs") +test.shared_table_with_metatable.iterators("ipairs") + +test.shared_table_with_metatable.as_shared_table = function() + local share = effil.table() + local mt = effil.table() + effil.setmetatable(share, mt) + -- Empty metatable + test.equal(share.table_key, nil) + + -- Only __index metamethod + mt.__index = function(t, key) + return "mt_" .. effil.rawget(t, key) + end + share.table_key = "table_value" + test.equal(share.table_key, "mt_table_value") + + -- Both __index and __newindex metamethods + mt.__newindex = function(t, key, value) + effil.rawset(t, key, "mt_" .. value) + end + share.table_key = "table_value" + test.equal(share.table_key, "mt_mt_table_value") + + -- Remove __index, use only __newindex metamethods + mt.__index = nil + share.table_key = "table_value" + test.equal(share.table_key, "mt_table_value") +end diff --git a/tests/lua/run_tests.lua b/tests/lua/run_tests.lua deleted file mode 100755 index fdc470e..0000000 --- a/tests/lua/run_tests.lua +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env lua - --- TODO: remove hardcode -package.path = package.path .. ";../libs/luaunit/?.lua;../tests/lua/?.lua" - -print("---------------") -print("-- " .. _VERSION .. " --") -print("---------------") - -do - -- Hack input arguments to make tests verbose by default - local make_verbose = true - for i, v in ipairs(arg) do - if v == '-o' or v == '--output' then - make_verbose = false - elseif v == "--extra-checks" then - table.remove(arg, i) - WITH_EXTRA_CHECKS = true - print "# RUN TESTS WITH EXTRA CHECKS" - end - end - if make_verbose then - table.insert(arg, '-o') - table.insert(arg, 'TAP') - end -end - ------------ --- TESTS -- ------------ - -test = require "luaunit" -effil = require 'effil' -require 'test_utils' -require 'thread' -require 'shared_table' -require 'gc' -require 'channel' -require 'type' - --- Hack tests functions to print when test starts -for suite_name, suite in pairs(_G) do - if string.sub(suite_name, 1, 4):lower() == 'test' and type(_G[suite_name]) == 'table' then -- is a test suite - for test_name, test_func in pairs(suite) do - if string.sub(test_name, 1, 4):lower() == 'test' then -- is a test function - suite[test_name] = function(...) - print("# Starting test: " .. suite_name .. '.' .. test_name) - return test_func(...) - end - end - end - end -end - -os.exit( test.LuaUnit.run() ) \ No newline at end of file diff --git a/tests/lua/shared-table.lua b/tests/lua/shared-table.lua new file mode 100644 index 0000000..a4cc507 --- /dev/null +++ b/tests/lua/shared-table.lua @@ -0,0 +1,87 @@ +require "bootstrap-tests" + +test.shared_table.tear_down = default_tear_down + +test.shared_table.pairs = function () + local share = effil.table() + local data = { 0, 0, 0, ["key1"] = 0, ["key2"] = 0, ["key3"] = 0 } + + for k, _ in pairs(data) do + share[k] = k .. "-value" + end + + for k,v in pairs(share) do + test.equal(data[k], 0) + data[k] = 1 + test.equal(v, k .. "-value") + end + + for k,v in pairs(data) do + test.equal(v, 1) + end + + for k,v in ipairs(share) do + test.equal(data[k], 1) + data[k] = 2 + test.equal(v, k .. "-value") + end + + for k,v in ipairs(data) do + test.equal(v, 2) + end +end + +test.shared_table.length = function () + local share = effil.table() + share[1] = 10 + share[2] = 20 + share[3] = 30 + share[4] = 40 + share["str"] = 50 + test.equal(#share, 4) + share[3] = nil + test.equal(#share, 2) + share[1] = nil + test.equal(#share, 0) +end + +test.shared_table.size = function () + local share = effil.table() + test.equal(effil.size(share), 0) + share[1] = 10 + test.equal(effil.size(share), 1) + share[2] = "value1" + share["key1"] = function() end + test.equal(effil.size(share), 3) + share[2] = nil + test.equal(effil.size(share), 2) +end + +test.shared_table.user_data_classification = function () + local share = effil.table() + share.thread = effil.thread(function(a, b) return a + b end)(19, 33) + share.sub_table = effil.table() + share.sub_table.some_key = "some_value" + + local result = share.thread:get() + test.equal(result, 52) + test.equal(share.sub_table.some_key, "some_value") +end + +test.shared_table.global = function () + test.not_equal(effil.G, nil) + effil.G.test_key = "test_value" + local thr = effil.thread(function() + local effil = require "effil" + if effil.G == nil or effil.G.test_key ~= "test_value" then + error("Invalid value of global table: " .. tostring(effil.G and effil.G.test_key or nil)) + end + effil.G.test_key = "checked" + end)() + local status, err = thr:wait() + if status == "failed" then + print("Thread failed with message: " .. err) + end + test.equal(status, "completed") + test.equal(effil.G.test_key, "checked") +end diff --git a/tests/lua/shared_table.lua b/tests/lua/shared_table.lua deleted file mode 100644 index ef1660f..0000000 --- a/tests/lua/shared_table.lua +++ /dev/null @@ -1,269 +0,0 @@ -TestSharedTable = {tearDown = tearDown} - -function TestSharedTable:testPairs() - local share = effil.table() - local data = { 0, 0, 0, ["key1"] = 0, ["key2"] = 0, ["key3"] = 0 } - - for k, _ in pairs(data) do - share[k] = k .. "-value" - end - - for k,v in pairs(share) do - test.assertEquals(data[k], 0) - data[k] = 1 - test.assertEquals(v, k .. "-value") - end - - for k,v in pairs(data) do - log("Check: " .. k) - test.assertEquals(v, 1) - end - - for k,v in ipairs(share) do - test.assertEquals(data[k], 1) - data[k] = 2 - test.assertEquals(v, k .. "-value") - end - - for k,v in ipairs(data) do - log("Check: " .. k) - test.assertEquals(v, 2) - end -end - -function TestSharedTable:testLength() - local share = effil.table() - share[1] = 10 - share[2] = 20 - share[3] = 30 - share[4] = 40 - share["str"] = 50 - log "Check values" - test.assertEquals(#share, 4) - share[3] = nil - test.assertEquals(#share, 2) - share[1] = nil - test.assertEquals(#share, 0) -end - -function TestSharedTable:testSize() - local share = effil.table() - test.assertEquals(effil.size(share), 0) - share[1] = 10 - test.assertEquals(effil.size(share), 1) - share[2] = "value1" - share["key1"] = function() end - test.assertEquals(effil.size(share), 3) - share[2] = nil - test.assertEquals(effil.size(share), 2) -end - -function TestSharedTable:testUserDataClassification() - local share = effil.table() - share.thread = effil.thread(function(a, b) return a + b end)(19, 33) - share.sub_table = effil.table() - share.sub_table.some_key = "some_value" - - local result = share.thread:get() - test.assertEquals(result, 52) - test.assertEquals(share.sub_table.some_key, "some_value") -end - -TestGeneralSharedTableMetaTable = { tearDown = tearDown } - -function TestGeneralSharedTableMetaTable:useMetatable(shared_table, metatable) - local mt = self.test_param() - for k, v in pairs(metatable) do - mt[k] = v - end - effil.setmetatable(shared_table, mt) -end - -function TestGeneralSharedTableMetaTable:testMetamethodIndex() - local share = effil.table() - self:useMetatable(share, { - __index = function(t, key) - return "mt_" .. effil.rawget(t, key) - end - } - ) - share.table_key = "table_value" - test.assertEquals(share.table_key, "mt_table_value") -end - -function TestGeneralSharedTableMetaTable:testMetamethodNewIndex() - local share = effil.table() - self:useMetatable(share, { - __newindex = function(t, key, value) - effil.rawset(t, "mt_" .. key, "mt_" .. value) - end - } - ) - share.table_key = "table_value" - test.assertEquals(share.mt_table_key, "mt_table_value") -end - -function TestGeneralSharedTableMetaTable:testMetamethodCall() - local share = effil.table() - self:useMetatable(share, { - __call = function(t, val1, val2, val3) - return tostring(val1) .. "_" .. tostring(val2), tostring(val2) .. "_" .. tostring(val3) - end - } - ) - local first_ret, second_ret = share("val1", "val2", "val3") - test.assertEquals(first_ret, "val1_val2") - test.assertEquals(second_ret, "val2_val3") -end - -local function CreateMetatableTestForBinaryOperator(method_info, op) - for _, reversed in pairs({true, false}) do - TestGeneralSharedTableMetaTable["testMetamethod" .. method_info.metamethod - .. (reversed and "Reversed" or "")] = - function(self) - local testTable, operand = effil.table(), effil.table() - self:useMetatable(testTable, { - ['__' .. string.lower(method_info.metamethod)] = - reversed and function(left, right) - right.was_called = true - return right.value .. '_'.. left.value - end - or function(left, right) - left.was_called = true - return left.value .. '_'.. right.value - end - } - ) - testTable.was_called = false - testTable.value = "left" - operand.value = "right" - local left_operand, right_operand = unpack(reversed and {operand, testTable} or {testTable, operand}) - test.assertEquals(op(left_operand, right_operand), - method_info.exp_value == nil and "left_right" or method_info.exp_value) - test.assertTrue(testTable.was_called) - end - end -end - -CreateMetatableTestForBinaryOperator({ metamethod = "Concat"}, function(a, b) return a.. b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Add"}, function(a, b) return a + b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Sub"}, function(a, b) return a - b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Mul"}, function(a, b) return a * b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Div"}, function(a, b) return a / b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Mod"}, function(a, b) return a % b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Pow"}, function(a, b) return a ^ b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Le", exp_value = true }, function(a, b) return a <= b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Lt", exp_value = true }, function(a, b) return a < b end) -CreateMetatableTestForBinaryOperator({ metamethod = "Eq", exp_value = true }, - function(a, b) return a == b end) - -local function CreateMetatableTestForUnaryOperator(methodName, op) - TestGeneralSharedTableMetaTable["testMetamethod" .. methodName] = - function(self) - local share = effil.table() - self:useMetatable(share, { - ['__' .. string.lower(methodName)] = function(t) - t.was_called = true - return t.value .. "_suffix" - end - } - ) - share.was_called = false - share.value = "value" - test.assertEquals(op(share), "value_suffix") - test.assertTrue(share.was_called) - end -end - -CreateMetatableTestForUnaryOperator("Unm", function(a) return -a end) -CreateMetatableTestForUnaryOperator("ToString", function(a) return tostring(a) end) -CreateMetatableTestForUnaryOperator("Len", function(a) return #a end) - -make_test_with_param(TestGeneralSharedTableMetaTable, ".+" --[[ Any test in this test suite]], - function() return {} end, - function() return effil.table() end -) - -TestSharedTableWithMetaTable = { tearDown = tearDown } - -function TestSharedTableWithMetaTable:testMetamethodIterators() - local share = effil.table() - local iterator = self.test_param - effil.setmetatable(share, { - ["__" .. iterator] = function(table) - return function(t, key) - local effil = require 'effil' - local ret = (key and key * 2) or 1 - if ret > 2 ^ 10 then - return nil - end - return ret, effil.rawget(t, ret) - end, table - end - } - ) - -- Add some values - for i = 0, 10 do - local pow = 2 ^ i - share[pow] = math.random(pow) - end - -- Add some noise - for i = 1, 100 do - share[math.random(1000) * 10 - 1] = math.random(1000) - end - -- Check that *pairs iterator works - local pow_iter = 1 - for k,v in _G[iterator](share) do - test.assertEquals(k, pow_iter) - test.assertEquals(v, share[pow_iter]) - pow_iter = pow_iter * 2 - end - test.assertEquals(pow_iter, 2 ^ 11) -end - -make_test_with_param(TestSharedTableWithMetaTable, "testMetamethodIterators", "pairs", "ipairs") - -function TestSharedTableWithMetaTable:testMetatableAsSharedTable() - local share = effil.table() - local mt = effil.table() - effil.setmetatable(share, mt) - -- Empty metatable - test.assertEquals(share.table_key, nil) - - -- Only __index metamethod - mt.__index = function(t, key) - return "mt_" .. effil.rawget(t, key) - end - share.table_key = "table_value" - test.assertEquals(share.table_key, "mt_table_value") - - -- Both __index and __newindex metamethods - mt.__newindex = function(t, key, value) - effil.rawset(t, key, "mt_" .. value) - end - share.table_key = "table_value" - test.assertEquals(share.table_key, "mt_mt_table_value") - - -- Remove __index, use only __newindex metamethods - mt.__index = nil - share.table_key = "table_value" - test.assertEquals(share.table_key, "mt_table_value") -end - -function TestSharedTable:testGlobalTable() - test.assertNotEquals(effil.G, nil) - effil.G.test_key = "test_value" - local thr = effil.thread(function() - local effil = require "effil" - if effil.G == nil or effil.G.test_key ~= "test_value" then - error("Invalid value of global table: " .. tostring(effil.G and effil.G.test_key or nil)) - end - effil.G.test_key = "checked" - end)() - local status, err = thr:wait() - if status == "failed" then - print("Thread failed with message: " .. err) - end - test.assertEquals(status, "completed") - test.assertEquals(effil.G.test_key, "checked") -end diff --git a/tests/lua/test_utils.lua b/tests/lua/test_utils.lua deleted file mode 100644 index 24b19f3..0000000 --- a/tests/lua/test_utils.lua +++ /dev/null @@ -1,75 +0,0 @@ -function log(...) - local msg = '@\t\t' .. os.date('%Y-%m-%d %H:%M:%S ',os.time()) - for _, val in ipairs({...}) do - msg = msg .. tostring(val) .. ' ' - end - io.write(msg .. '\n') - io.flush() -end - -function wait(timeInSec, condition, silent) - if not silent then - log("Start waiting for " .. tostring(timeInSec) .. "sec...") - end - local result = false - local startTime = os.time() - while ( (os.time() - startTime) <= timeInSec) do - if condition ~= nil then - if type(condition) == 'function' then - if condition() then - result = true - break - end - else - if condition then - result = true - break - end - end - end - end - if not silent then - log "Give up" - end - return result -end - -function sleep(timeInSec, silent) - if not silent then - log("Start sleep for " .. tostring(timeInSec) .. "sec...") - end - wait(timeInSec, nil, true) - if not silent then - log "Wake up" - end -end - -function tearDown() - collectgarbage() - effil.gc.collect() - -- effil.G is always present - -- thus, gc has one object - test.assertEquals(effil.gc.count(), 1) -end - -function make_test_with_param(test_suite, test_case_pattern, ...) - local tests_to_delete, tests_to_add = {}, {} - for test_name, test_func in pairs(test_suite) do - if string.sub(test_name, 1, 4):lower() == 'test' and string.match(test_name, test_case_pattern) then - table.insert(tests_to_delete, test_name) - for i, param in ipairs({...}) do - tests_to_add[test_name .. "/" .. i] = function(t) - print("# with params: " .. tostring(param)) - t.test_param = param - return test_func(t) - end - end - end - end - for _, test_name in ipairs(tests_to_delete) do - test_suite[test_name] = nil - end - for test_name, test_func in pairs(tests_to_add) do - test_suite[test_name] = test_func - end -end diff --git a/tests/lua/tests.lua b/tests/lua/tests.lua new file mode 100755 index 0000000..400ec62 --- /dev/null +++ b/tests/lua/tests.lua @@ -0,0 +1,17 @@ +#!/usr/bin/env lua + +package.path = ";../tests/lua/?.lua;../libs/u-test/?.lua;../src/lua/?.lua" + +require "type" +require "gc" +require "channel" +require "thread" +require "shared-table" +require "metatable" + +if os.getenv("STRESS") then + require "channel-stress" + require "thread-stress" +end + +test.summary() \ No newline at end of file diff --git a/tests/lua/thread-stress.lua b/tests/lua/thread-stress.lua new file mode 100644 index 0000000..cc51f59 --- /dev/null +++ b/tests/lua/thread-stress.lua @@ -0,0 +1,15 @@ +require "bootstrap-tests" + +test.thread_stress.tear_down = default_tear_down + +test.thread_stress.time = function () + local function check_time(real_time, use_time, metric) + local start_time = os.time() + effil.sleep(use_time, metric) + test.almost_equal(os.time(), start_time + real_time, 1) + end + check_time(4, 4, nil) -- seconds by default + check_time(4, 4, 's') + check_time(4, 4000, 'ms') + check_time(60, 1, 'm') +end diff --git a/tests/lua/thread.lua b/tests/lua/thread.lua index 2a9b859..3f79887 100644 --- a/tests/lua/thread.lua +++ b/tests/lua/thread.lua @@ -1,51 +1,51 @@ -local effil = require 'effil' +require "bootstrap-tests" -TestThread = {tearDown = tearDown } +test.thread.tear_down = default_tear_down -function TestThread:testWait() +test.thread.wait = function () local thread = effil.thread(function() print 'Effil is not that tower' end)() local status = thread:wait() - test.assertNil(thread:get()) - test.assertEquals(status, "completed") - test.assertEquals(thread:status(), "completed") + test.is_nil(thread:get()) + test.equal(status, "completed") + test.equal(thread:status(), "completed") end -function TestThread:testMultipleWaitGet() +test.thread.multiple_wait_get = function () local thread = effil.thread(function() return "test value" end)() local status1 = thread:wait() local status2 = thread:wait() - test.assertEquals(status1, "completed") - test.assertEquals(status2, status1) + test.equal(status1, "completed") + test.equal(status2, status1) local value = thread:get() - test.assertEquals(value, "test value") + test.equal(value, "test value") end -function TestThread:testTimedGet() +test.thread.timed_get = function () local thread = effil.thread(function() require('effil').sleep(2) return "-_-" end)() - test.assertNil(thread:get(1)) - test.assertEquals(thread:get(2), "-_-") + test.is_nil(thread:get(1)) + test.equal(thread:get(2), "-_-") end -function TestThread:testTimedWait() +test.thread.timed_get = function () local thread = effil.thread(function() require('effil').sleep(2) return 8 end)() local status = thread:wait(1) - test.assertEquals(status, "running") + test.equal(status, "running") local value = thread:get(2, "s") - test.assertEquals(value, 8); + test.equal(value, 8); - test.assertEquals(thread:status(), "completed") + test.equal(thread:status(), "completed") end -function TestThread:testAsyncWait() +test.thread.async_wait = function() local thread = effil.thread( function() require('effil').sleep(1) end)() @@ -55,11 +55,11 @@ function TestThread:testAsyncWait() iter = iter + 1 end - test.assertTrue(iter > 10) - test.assertEquals(thread:status(), "completed") + test.is_true(iter > 10) + test.equal(thread:status(), "completed") end -function TestThread:testDetached() +test.thread.detached = function () local st = effil.table() for i = 1, 32 do @@ -73,20 +73,20 @@ function TestThread:testDetached() effil.sleep(1) for i = 1, 32 do - test.assertEquals(st[i], i) + test.equal(st[i], i) end end -function TestThread:testCancel() +test.thread.cancel = function () local thread = effil.thread(function() while true do end end)() - test.assertTrue(thread:cancel()) - test.assertEquals(thread:status(), "canceled") + test.is_true(thread:cancel()) + test.equal(thread:status(), "canceled") end -function TestThread:testAsyncCancel() +test.thread.async_cancel = function () local thread_runner = effil.thread( function() local startTime = os.time() @@ -98,11 +98,11 @@ function TestThread:testAsyncCancel() sleep(2) -- let thread starts working thread:cancel(0) - test.assertTrue(wait(2, function() return thread:status() ~= 'running' end)) - test.assertEquals(thread:status(), 'canceled') + test.is_true(wait(2, function() return thread:status() ~= 'running' end)) + test.equal(thread:status(), 'canceled') end -function TestThread:testPauseResumeCancel() +test.thread.pause_resume_cancel = function () local data = effil.table() data.value = 0 local thread = effil.thread( @@ -112,20 +112,20 @@ function TestThread:testPauseResumeCancel() end end )(data) - test.assertTrue(wait(2, function() return data.value > 100 end)) - test.assertTrue(thread:pause()) - test.assertEquals(thread:status(), "paused") + test.is_true(wait(2, function() return data.value > 100 end)) + test.is_true(thread:pause()) + test.equal(thread:status(), "paused") local savedValue = data.value sleep(1) - test.assertEquals(data.value, savedValue) + test.equal(data.value, savedValue) thread:resume() - test.assertTrue(wait(5, function() return (data.value - savedValue) > 100 end)) - test.assertTrue(thread:cancel()) + test.is_true(wait(5, function() return (data.value - savedValue) > 100 end)) + test.is_true(thread:cancel()) end -function TestThread:testPauseCancel() +test.thread.pause_cancel = function () local data = effil.table() data.value = 0 local thread = effil.thread( @@ -136,17 +136,17 @@ function TestThread:testPauseCancel() end )(data) - test.assertTrue(wait(2, function() return data.value > 100 end)) + test.is_true(wait(2, function() return data.value > 100 end)) thread:pause(0) - test.assertTrue(wait(2, function() return thread:status() == "paused" end)) + test.is_true(wait(2, function() return thread:status() == "paused" end)) local savedValue = data.value sleep(1) - test.assertEquals(data.value, savedValue) + test.equal(data.value, savedValue) - test.assertTrue(thread:cancel(0)) + test.is_true(thread:cancel(0)) end -function TestThread:testAsyncPauseResumeCancel() +test.thread.async_pause_resume_cancel = function () local data = effil.table() data.value = 0 local thread = effil.thread( @@ -157,21 +157,22 @@ function TestThread:testAsyncPauseResumeCancel() end )(data) - test.assertTrue(wait(2, function() return data.value > 100 end)) + test.is_true(wait(2, function() return data.value > 100 end)) thread:pause() local savedValue = data.value sleep(1) - test.assertEquals(data.value, savedValue) + test.equal(data.value, savedValue) thread:resume() - test.assertTrue(wait(5, function() return (data.value - savedValue) > 100 end)) + test.is_true(wait(5, function() return (data.value - savedValue) > 100 end)) thread:cancel(0) - test.assertTrue(wait(5, function() return thread:status() == "canceled" end)) + test.is_true(wait(5, function() return thread:status() == "canceled" end)) + thread:wait() end -function TestThread:testCheckThreadReturns() +test.thread.returns = function () local share = effil.table() share.value = "some value" @@ -184,36 +185,35 @@ function TestThread:testCheckThreadReturns() local status = thread:wait() local returns = { thread:get() } - log "Check values" - test.assertEquals(status, "completed") + test.equal(status, "completed") - test.assertNumber(returns[1]) - test.assertEquals(returns[1], 100500) + test.is_number(returns[1]) + test.equal(returns[1], 100500) - test.assertString(returns[2]) - test.assertEquals(returns[2], "string value") + test.is_string(returns[2]) + test.equal(returns[2], "string value") - test.assertBoolean(returns[3]) - test.assertTrue(returns[3]) + test.is_boolean(returns[3]) + test.is_true(returns[3]) - test.assertUserdata(returns[4]) - test.assertEquals(returns[4].value, share.value) + test.is_userdata(returns[4]) + test.equal(returns[4].value, share.value) - test.assertFunction(returns[5]) - test.assertEquals(returns[5](11, 89), 100) + test.is_function(returns[5]) + test.equal(returns[5](11, 89), 100) end -function TestThread:testTimedCancel() +test.thread.timed_cancel = function () local thread = effil.thread(function() require("effil").sleep(2) end)() - test.assertFalse(thread:cancel(1)) + test.is_false(thread:cancel(1)) thread:wait() end -TestThreadWithTable = {tearDown = tearDown } +test.thread_with_table.tear_down = default_tear_down -function TestThreadWithTable:testSharedTableTypes() +test.thread_with_table.types = function () local share = effil.table() share["number"] = 100500 @@ -232,14 +232,13 @@ function TestThreadWithTable:testSharedTableTypes() local thread = thread_factory(share) thread:wait() - log "Check values" - test.assertEquals(share["child.number"], share["number"]) - test.assertEquals(share["child.string"], share["string"]) - test.assertEquals(share["child.bool"], share["bool"]) - test.assertEquals(share["child.function"], share["function"](11,45)) + test.equal(share["child.number"], share["number"]) + test.equal(share["child.string"], share["string"]) + test.equal(share["child.bool"], share["bool"]) + test.equal(share["child.function"], share["function"](11,45)) end -function TestThreadWithTable:testRecursiveTables() +test.thread_with_table.recursive = function () local share = effil.table() local magic_number = 42 @@ -258,16 +257,15 @@ function TestThreadWithTable:testRecursiveTables() local thread = thread_factory(share) thread:wait() - log "Check values" - test.assertEquals(share["subtable1"]["subtable1"]["magic_number"], magic_number) - test.assertEquals(share["subtable1"]["subtable2"]["magic_number"], magic_number) - test.assertEquals(share["subtable2"]["magic_number"], magic_number) - test.assertEquals(share["magic_number"], nil) + test.equal(share["subtable1"]["subtable1"]["magic_number"], magic_number) + test.equal(share["subtable1"]["subtable2"]["magic_number"], magic_number) + test.equal(share["subtable2"]["magic_number"], magic_number) + test.equal(share["magic_number"], nil) end -TestThisThread = {tearDown = tearDown } +test.this_thread.tear_down = default_tear_down -function TestThisThread:testThisThreadFunctions() +test.this_thread.functions = function () local share = effil.table() local thread_factory = effil.thread( @@ -278,24 +276,8 @@ function TestThisThread:testThisThreadFunctions() local thread = thread_factory(share) thread:get() - test.assertString(share["child.id"]) - test.assertNumber(tonumber(share["child.id"])) - test.assertNotEquals(share["child.id"], effil.thread_id()) + test.is_string(share["child.id"]) + test.is_number(tonumber(share["child.id"])) + test.not_equal(share["child.id"], effil.thread_id()) effil.yield() -- just call it end - -if WITH_EXTRA_CHECKS then - -function TestThisThread:testTime() - local function check_time(real_time, use_time, metric) - local start_time = os.time() - effil.sleep(use_time, metric) - test.assertAlmostEquals(os.time(), start_time + real_time, 1) - end - check_time(4, 4, nil) -- seconds by default - check_time(4, 4, 's') - check_time(4, 4000, 'ms') - check_time(60, 1, 'm') -end - -end -- WITH_EXTRA_CHECKS diff --git a/tests/lua/type.lua b/tests/lua/type.lua index 749c471..ef1a98a 100644 --- a/tests/lua/type.lua +++ b/tests/lua/type.lua @@ -1,11 +1,9 @@ -local effil = require 'effil' +require "bootstrap-tests" -TestType = { tearDown = tearDown } - -function TestType:testType() - test.assertEquals(effil.type(1), "number") - test.assertEquals(effil.type("string"), "string") - test.assertEquals(effil.type(effil.table()), "effil.table") - test.assertEquals(effil.type(effil.channel()), "effil.channel") - test.assertEquals(effil.type(effil.thread(function() end)()), "effil.thread") +test.type = function() + test.equal(effil.type(1), "number") + test.equal(effil.type("string"), "string") + test.equal(effil.type(effil.table()), "effil.table") + test.equal(effil.type(effil.channel()), "effil.channel") + test.equal(effil.type(effil.thread(function() end)()), "effil.thread") end