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(), 3) end end local function run_test_with_different_metatables(name, ...) test.metatable[name]({}, ...) test.metatable[name](effil.table(), ...) end test.metatable.index_p = function (metatable) local share = effil.table() metatable.__index = function(t, key) return "mt_" .. effil.rawget(t, key .. "_origin") end effil.setmetatable(share, metatable) share.table_key_origin = "table_value" test.equal(share.table_key, "mt_table_value") end test.metatable.new_index_p = 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_p = 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_p") run_test_with_different_metatables("new_index_p") run_test_with_different_metatables("call_p") test.metatable.binary_op_p = 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 = table.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_p", ...) 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_p = 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_p", ...) 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_p = function (iterator_type, iterator_trigger) 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_trigger][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_p("pairs", "effil") test.shared_table_with_metatable.iterators_p("ipairs", "effil") if LUA_VERSION > 51 then test.shared_table_with_metatable.iterators_p("pairs", "_G") test.shared_table_with_metatable.iterators_p("ipairs", "_G") end -- LUA_VERSION > 51 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 .. "_origin") end share.table_key_origin = "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_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 test.shared_table_with_metatable.metatable_serialization = function() local table_with_mt = setmetatable({}, {a=1}) local tbl = effil.table(table_with_mt) test.not_equal(effil.getmetatable(tbl), nil) test.equal(effil.getmetatable(tbl).a, 1) end test.shared_table_with_metatable.metatable_with_function_with_upvalues = function() local common_table = {aa=2} local tbl = setmetatable({}, { a = function() return common_table end, b = function() return common_table end }) local mt = effil.getmetatable(effil.table(tbl)) test.equal( select(2, debug.getupvalue(mt.a, 1)), select(2, debug.getupvalue(mt.b, 1)) ) end test.shared_table_with_metatable.check_eq_metamethod = function() local left_table = effil.table() local right_table = effil.table() local left_table_clone = (effil.table{ left_table }[1]) -- userdata will change test.is_false(left_table == right_table) test.is_true(left_table == left_table_clone) test.is_false(left_table == effil.channel()) effil.setmetatable(left_table, { __eq = function() return false end }) test.is_false(left_table == left_table_clone) effil.setmetatable(left_table, { __eq = function() return true end }) test.is_true(left_table == right_table) test.is_false(left_table == effil.channel()) effil.setmetatable(left_table, { __eq = function() return false end }) effil.setmetatable(right_table, { __eq = function() return true end }) test.is_false(left_table == right_table) effil.setmetatable(left_table, nil) test.is_true(left_table == right_table) end test.shared_table_with_metatable.table_as_index = function() local tbl = effil.table{} local mt = effil.table{ a = 1 } local mt2 = effil.table{ b = 2 } local mt3 = effil.table{ c = 2 } effil.setmetatable(tbl, {__index=mt}) test.equal(tbl.a, 1) test.equal(tbl.b, nil) effil.setmetatable(mt, {__index=mt2}) test.equal(tbl.a, 1) test.equal(tbl.b, 2) effil.setmetatable(mt2, {__index=mt3}) test.equal(tbl.a, 1) test.equal(tbl.b, 2) test.equal(tbl.c, 2) end test.shared_table_with_metatable.next_iterator = function() local visited = {a = 1, [2] = 3, [true] = "asd", [2.2] = "qwe"} local share = effil.table(visited) local key, value = effil.next(share) while key do test.equal(visited[key], value) visited[key] = nil key, value = effil.next(share, key) end test.is_true(next(visited) == nil) -- table is empty end