effil/tests/lua/metatable.lua
mihacooper 616fdb86cc
table support in __index metamethod (#143)
* table support in __index metamethod

* fix after merge
2020-03-03 19:34:56 +03:00

217 lines
6.7 KiB
Lua

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.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