luau/tests/conformance/events.lua
2022-07-28 20:41:13 -07:00

481 lines
13 KiB
Lua

-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
-- This file is based on Lua 5.x tests -- https://github.com/lua/lua/tree/master/testes
print('testing metatables')
local unpack = table.unpack
X = 20; B = 30
local _G = getfenv()
setfenv(1, setmetatable({}, {__index=_G}))
collectgarbage()
X = X+10
assert(X == 30 and _G.X == 20)
B = false
assert(B == false)
B = nil
assert(B == 30)
assert(getmetatable{} == nil)
assert(getmetatable(4) == nil)
assert(getmetatable(nil) == nil)
a={}; setmetatable(a, {__metatable = "xuxu",
__tostring=function(x) return x.name end})
assert(getmetatable(a) == "xuxu")
local res,err = pcall(tostring, a)
assert(not res and err == "'__tostring' must return a string")
-- cannot change a protected metatable
assert(pcall(setmetatable, a, {}) == false)
a.name = "gororoba"
assert(tostring(a) == "gororoba")
local a, t = {10,20,30; x="10", y="20"}, {}
assert(setmetatable(a,t) == a)
assert(getmetatable(a) == t)
assert(setmetatable(a,nil) == a)
assert(getmetatable(a) == nil)
assert(setmetatable(a,t) == a)
function f (t, i, e)
assert(not e)
local p = rawget(t, "parent")
return (p and p[i]+3), "dummy return"
end
t.__index = f
a.parent = {z=25, x=12, [4] = 24}
assert(a[1] == 10 and a.z == 28 and a[4] == 27 and a.x == "10")
collectgarbage()
a = setmetatable({}, t)
function f(t, i, v) rawset(t, i, v-3) end
t.__newindex = f
a[1] = 30; a.x = "101"; a[5] = 200
assert(a[1] == 27 and a.x == 98 and a[5] == 197)
local c = {}
a = setmetatable({}, t)
t.__newindex = c
a[1] = 10; a[2] = 20; a[3] = 90
assert(c[1] == 10 and c[2] == 20 and c[3] == 90)
do
local a;
a = setmetatable({}, {__index = setmetatable({},
{__index = setmetatable({},
{__index = function (_,n) return a[n-3]+4, "lixo" end})})})
a[0] = 20
for i=0,10 do
assert(a[i*3] == 20 + i*4)
end
end
do -- newindex
local foi
local a = {}
for i=1,10 do a[i] = 0; a['a'..i] = 0; end
setmetatable(a, {__newindex = function (t,k,v) foi=true; rawset(t,k,v) end})
foi = false; a[1]=0; assert(not foi)
foi = false; a['a1']=0; assert(not foi)
foi = false; a['a11']=0; assert(foi)
foi = false; a[11]=0; assert(foi)
foi = false; a[1]=nil; assert(not foi)
foi = false; a[1]=nil; assert(foi)
end
function f (t, ...) return t, {...} end
t.__call = f
do
local x,y = a(unpack{'a', 1})
assert(x==a and y[1]=='a' and y[2]==1 and y[3]==nil)
x,y = a()
assert(x==a and y[1]==nil)
end
local b = setmetatable({}, t)
setmetatable(b,t)
function f(op)
return function (...) cap = {[0] = op, ...} ; return (...) end
end
t.__add = f("add")
t.__sub = f("sub")
t.__mul = f("mul")
t.__div = f("div")
t.__mod = f("mod")
t.__unm = f("unm")
t.__pow = f("pow")
assert(b+5 == b)
assert(cap[0] == "add" and cap[1] == b and cap[2] == 5 and cap[3]==nil)
assert(b+'5' == b)
assert(cap[0] == "add" and cap[1] == b and cap[2] == '5' and cap[3]==nil)
assert(5+b == 5)
assert(cap[0] == "add" and cap[1] == 5 and cap[2] == b and cap[3]==nil)
assert('5'+b == '5')
assert(cap[0] == "add" and cap[1] == '5' and cap[2] == b and cap[3]==nil)
b=b-3; assert(getmetatable(b) == t)
assert(5-a == 5)
assert(cap[0] == "sub" and cap[1] == 5 and cap[2] == a and cap[3]==nil)
assert('5'-a == '5')
assert(cap[0] == "sub" and cap[1] == '5' and cap[2] == a and cap[3]==nil)
assert(a*a == a)
assert(cap[0] == "mul" and cap[1] == a and cap[2] == a and cap[3]==nil)
assert(a/0 == a)
assert(cap[0] == "div" and cap[1] == a and cap[2] == 0 and cap[3]==nil)
assert(a%2 == a)
assert(cap[0] == "mod" and cap[1] == a and cap[2] == 2 and cap[3]==nil)
assert(-a == a)
assert(cap[0] == "unm" and cap[1] == a)
assert(a^4 == a)
assert(cap[0] == "pow" and cap[1] == a and cap[2] == 4 and cap[3]==nil)
assert(a^'4' == a)
assert(cap[0] == "pow" and cap[1] == a and cap[2] == '4' and cap[3]==nil)
assert(4^a == 4)
assert(cap[0] == "pow" and cap[1] == 4 and cap[2] == a and cap[3]==nil)
assert('4'^a == '4')
assert(cap[0] == "pow" and cap[1] == '4' and cap[2] == a and cap[3]==nil)
t = {}
t.__lt = function (a,b,c)
collectgarbage()
assert(c == nil)
if type(a) == 'table' then a = a.x end
if type(b) == 'table' then b = b.x end
return a<b, "dummy"
end
function Op(x) return setmetatable({x=x}, t) end
local function test ()
assert(not(Op(1)<Op(1)) and (Op(1)<Op(2)) and not(Op(2)<Op(1)))
assert(not(Op('a')<Op('a')) and (Op('a')<Op('b')) and not(Op('b')<Op('a')))
assert((Op(1)<=Op(1)) and (Op(1)<=Op(2)) and not(Op(2)<=Op(1)))
assert((Op('a')<=Op('a')) and (Op('a')<=Op('b')) and not(Op('b')<=Op('a')))
assert(not(Op(1)>Op(1)) and not(Op(1)>Op(2)) and (Op(2)>Op(1)))
assert(not(Op('a')>Op('a')) and not(Op('a')>Op('b')) and (Op('b')>Op('a')))
assert((Op(1)>=Op(1)) and not(Op(1)>=Op(2)) and (Op(2)>=Op(1)))
assert((Op('a')>=Op('a')) and not(Op('a')>=Op('b')) and (Op('b')>=Op('a')))
end
test()
t.__le = function (a,b,c)
assert(c == nil)
if type(a) == 'table' then a = a.x end
if type(b) == 'table' then b = b.x end
return a<=b, "dummy"
end
test() -- retest comparisons, now using both `lt' and `le'
-- test `partial order'
local function Set(x)
local y = {}
for _,k in pairs(x) do y[k] = 1 end
return setmetatable(y, t)
end
t.__lt = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
b[k] = nil
end
return next(b) ~= nil
end
t.__le = nil
assert(Set{1,2,3} < Set{1,2,3,4})
assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
assert((Set{1,2,3,4} <= Set{1,2,3,4}))
assert((Set{1,2,3,4} >= Set{1,2,3,4}))
assert((Set{1,3} <= Set{3,5})) -- wrong!! model needs a `le' method ;-)
t.__le = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
assert(not (Set{1,3} <= Set{3,5})) -- now its OK!
assert(not(Set{1,3} <= Set{3,5}))
assert(not(Set{1,3} >= Set{3,5}))
t.__eq = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
b[k] = nil
end
return next(b) == nil
end
local s = Set{1,3,5}
assert(s == Set{3,5,1})
assert(not rawequal(s, Set{3,5,1}))
assert(rawequal(s, s))
assert(Set{1,3,5,1} == Set{3,5,1})
assert(Set{1,3,5} ~= Set{3,5,1,6})
t[Set{1,3,5}] = 1
assert(t[Set{1,3,5}] == nil) -- `__eq' is not valid for table accesses
t.__concat = function (a,b,c)
assert(c == nil)
if type(a) == 'table' then a = a.val end
if type(b) == 'table' then b = b.val end
if A then return a..b
else
return setmetatable({val=a..b}, t)
end
end
c = {val="c"}; setmetatable(c, t)
d = {val="d"}; setmetatable(d, t)
A = true
assert(c..d == 'cd')
assert(0 .."a".."b"..c..d.."e".."f"..(5+3).."g" == "0abcdef8g")
A = false
x = c..d
assert(getmetatable(x) == t and x.val == 'cd')
x = 0 .."a".."b"..c..d.."e".."f".."g"
assert(x.val == "0abcdefg")
-- test comparison compatibilities
local t1, t2, c, d
t1 = {}; c = {}; setmetatable(c, t1)
d = {}
t1.__eq = function () return true end
t1.__lt = function () return true end
assert(c ~= d and not pcall(function () return c < d end))
setmetatable(d, t1)
assert(c == d and c < d and not(d <= c))
t2 = {}
t2.__eq = t1.__eq
t2.__lt = t1.__lt
setmetatable(d, t2)
assert(c == d and c < d and not(d <= c))
-- test for several levels of calls
local i
local tt = {
__call = function (t, ...)
i = i+1
if t.f then return t.f(...)
else return {...}
end
end
}
local a = setmetatable({}, tt)
local b = setmetatable({f=a}, tt)
local c = setmetatable({f=b}, tt)
i = 0
x = c(3,4,5)
assert(i == 3 and x[1] == 3 and x[3] == 5)
assert(_G.X == 20)
assert(_G == getfenv(0))
print'+'
local _g = _G
setfenv(1, setmetatable({}, {__index=function (_,k) return _g[k] end}))
-- testing proxies
assert(getmetatable(newproxy()) == nil)
assert(getmetatable(newproxy(false)) == nil)
assert(getmetatable(newproxy(nil)) == nil)
local u = newproxy(true)
getmetatable(u).__newindex = function (u,k,v)
getmetatable(u)[k] = v
end
getmetatable(u).__index = function (u,k)
return getmetatable(u)[k]
end
for i=1,10 do u[i] = i end
for i=1,10 do assert(u[i] == i) end
-- local k = newproxy(u)
-- assert(getmetatable(k) == getmetatable(u))
a = {}
rawset(a, "x", 1, 2, 3)
assert(a.x == 1 and rawget(a, "x", 3) == 1)
print '+'
--[[
-- testing metatables for basic types
mt = {}
debug.setmetatable(10, mt)
assert(getmetatable(-2) == mt)
mt.__index = function (a,b) return a+b end
assert((10)[3] == 13)
assert((10)["3"] == 13)
debug.setmetatable(23, nil)
assert(getmetatable(-2) == nil)
debug.setmetatable(true, mt)
assert(getmetatable(false) == mt)
mt.__index = function (a,b) return a or b end
assert((true)[false] == true)
assert((false)[false] == false)
debug.setmetatable(false, nil)
assert(getmetatable(true) == nil)
debug.setmetatable(nil, mt)
assert(getmetatable(nil) == mt)
mt.__add = function (a,b) return (a or 0) + (b or 0) end
assert(10 + nil == 10)
assert(nil + 23 == 23)
assert(nil + nil == 0)
debug.setmetatable(nil, nil)
assert(getmetatable(nil) == nil)
debug.setmetatable(nil, {})
]]--
do
-- == must not do ref equality for tables and userdata in presence of __eq
local t = {}
local u = newproxy(true)
-- print() returns nil which is converted to false
setmetatable(t, { __eq = print })
getmetatable(u).__eq = print
assert(t ~= t)
assert(u ~= u)
end
do
-- verify that internal mt flags are set correctly after two table assignments
local mt = {
{}, -- mixed metatable
__index = {X = true},
}
local t = setmetatable({}, mt)
assert(t.X) -- fails if table flags are set incorrectly
end
do
-- verify __len behavior & error handling
local t = {1}
setmetatable(t, {})
assert(#t == 1)
setmetatable(t, { __len = rawlen })
assert(#t == 1)
setmetatable(t, { __len = function() return 42 end })
assert(#t == 42)
setmetatable(t, { __len = 42 })
local ok, err = pcall(function() return #t end)
assert(not ok and err:match("attempt to call a number value"))
setmetatable(t, { __len = function() end })
local ok, err = pcall(function() return #t end)
assert(not ok and err:match("'__len' must return a number"))
setmetatable(t, { __len = error })
local ok, err = pcall(function() return #t end)
assert(not ok and err == t)
end
-- verify rawlen behavior
do
local t = {1}
setmetatable(t, { __len = 42 })
assert(rawlen(t) == 1)
assert(rawlen("foo") == 3)
local ok, err = pcall(function() return rawlen(42) end)
assert(not ok and err:match("table or string expected"))
end
-- verify that NaN/nil keys are passed to __newindex even though table assignment with them anywhere in the chain fails
do
assert(pcall(function() local t = {} t[nil] = 5 end) == false)
assert(pcall(function() local t = {} setmetatable(t, { __newindex = {} }) t[nil] = 5 end) == false)
assert(pcall(function() local t = {} setmetatable(t, { __newindex = function() end }) t[nil] = 5 end) == true)
assert(pcall(function() local t = {} t[0/0] = 5 end) == false)
assert(pcall(function() local t = {} setmetatable(t, { __newindex = {} }) t[0/0] = 5 end) == false)
assert(pcall(function() local t = {} setmetatable(t, { __newindex = function() end }) t[0/0] = 5 end) == true)
end
-- verify that __newindex gets called for frozen tables but only if the assignment is to a key absent from the table
do
local ni = {}
local t = table.create(2)
t[1] = 42
-- t[2] is semantically absent with storage allocated for it
t.a = 1
t.b = 2
t.b = nil -- this sets 'b' value to nil but leaves key as is to exercise more internal paths -- no observable behavior change expected between b and other absent keys
setmetatable(t, { __newindex = function(_, k, v)
assert(v == 42)
table.insert(ni, k)
end })
table.freeze(t)
-- "redundant" combinations are there to test all three of SETTABLEN/SETTABLEKS/SETTABLE
assert(pcall(function() t.a = 42 end) == false)
assert(pcall(function() t[1] = 42 end) == false)
assert(pcall(function() local key key = "a" t[key] = 42 end) == false)
assert(pcall(function() local key key = 1 t[key] = 42 end) == false)
-- now repeat the same for keys absent from the table: b (semantically absent), c (physically absent), 2 (semantically absent), 3 (physically absent)
assert(pcall(function() t.b = 42 end) == true)
assert(pcall(function() t.c = 42 end) == true)
assert(pcall(function() local key key = "b" t[key] = 42 end) == true)
assert(pcall(function() local key key = "c" t[key] = 42 end) == true)
assert(pcall(function() t[2] = 42 end) == true)
assert(pcall(function() t[3] = 42 end) == true)
assert(pcall(function() local key key = 2 t[key] = 42 end) == true)
assert(pcall(function() local key key = 3 t[key] = 42 end) == true)
-- validate the assignment sequence
local ei = { "b", "c", "b", "c", 2, 3, 2, 3 }
assert(#ni == #ei)
for k,v in ni do
assert(ei[k] == v)
end
end
return 'OK'