-- Metatable tests
-- 1. __index table and function
local proto = { answer = 42, method = function(self) return self.val * 2 end }
local t = { val = 21 }
setmetatable(t, { __index = proto })
assert(t.answer == 42, "metatable __index table fallback")
assert(t:method() == 42, "metatable __index method call")
local dyn = {}
setmetatable(dyn, {
__index = function(tbl, key)
return key .. "_dynamic"
end
})
assert(dyn.hello == "hello_dynamic", "metatable __index function")
-- 2. __newindex table and function
local sink = {}
local tracker = {}
setmetatable(tracker, { __newindex = sink })
tracker.new_prop = 100
assert(sink.new_prop == 100, "__newindex table redirect")
assert(tracker.new_prop == nil, "tracker itself does not receive new prop")
local intercepted = {}
local record = { key = "", val = 0 }
setmetatable(intercepted, {
__newindex = function(tbl, key, val)
record.key = key
record.val = val
end
})
intercepted.foo = 999
assert(record.key == "foo" and record.val == 999, "__newindex function call")
-- 3. Arithmetic and operators metamethods
local vec_mt = {
__add = function(a, b)
return setmetatable({ x = a.x + b.x, y = a.y + b.y }, vec_mt)
end,
__sub = function(a, b)
return setmetatable({ x = a.x - b.x, y = a.y - b.y }, vec_mt)
end,
__tostring = function(v)
return "(" .. v.x .. ", " .. v.y .. ")"
end,
__call = function(v, factor)
return setmetatable({ x = v.x * factor, y = v.y * factor }, vec_mt)
end
}
local v1 = setmetatable({ x = 1, y = 2 }, vec_mt)
local v2 = setmetatable({ x = 10, y = 20 }, vec_mt)
local v3 = v1 + v2
assert(v3.x == 11 and v3.y == 22, "__add metamethod")
local v4 = v2 - v1
assert(v4.x == 9 and v4.y == 18, "__sub metamethod")
assert(tostring(v1) == "(1, 2)", "__tostring metamethod")
local v5 = v1(5)
assert(v5.x == 5 and v5.y == 10, "__call metamethod")
-- 4. Raw functions
local raw_t = {}
rawset(raw_t, "name", "Neyuki")
assert(rawget(raw_t, "name") == "Neyuki", "rawset and rawget")
assert(rawequal(raw_t, raw_t) == true, "rawequal identity")
print("[pass] metatables and raw functions")