#!/usr/bin/lua -- simple test suite that checks all provided compatibilty functions -- and saves their outputs to a file in such a way, that you can -- easily compare results of different Lua versions side by side! local print, F, writefile local ____________________________________________________________ local outfile = "output_" .. _VERSION:gsub("[%s%.]+", "") .. ".txt" do local separator = ("="):rep(70) local out = assert(io.open(outfile, "w")) local select, tostring = select, tostring function print(...) local n = select('#', ...) for i = 1, n-1 do out:write(tostring((select(i, ...))), "\t") end if n > 0 then out:write(tostring((select(n, ...)))) end out:write("\n") out:flush() end function ____________________________________________________________() out:write(separator, "\n") out:flush() end local type, unpack = type, table.unpack or unpack function F(...) local args, n = { ... }, select('#', ...) for i = 1, n do local t = type(args[i]) if t ~= "string" and t ~= "number" and t ~= "boolean" then args[i] = t end end return unpack(args, 1, n) end local assert, io = assert, io function writefile(name, contents, bin) local f = assert(io.open(name, bin and "wb" or "w")) f:write(contents) f:close() end end package.path = "../?.lua;" .. package.path package.cpath = "../?.so;" .. package.cpath -- load compatibility functions (in Lua 5.1) require("compat52") ____________________________________________________________'' print("bit32.arshift()", bit32.arshift and bit32.arshift(2^32-4, 2)) print("bit32.band()", bit32.band and bit32.band(7, 5, 3)) print("bit32.bnot()", bit32.bnot and bit32.bnot(0)) print("bit32.bor()", bit32.bor and bit32.bor(1, 2, 4)) print("bit32.btest()", bit32.btest and bit32.btest(7, 5, 3)) print("bit32.bxor()", bit32.bxor and bit32.bxor(7, 5, 4, 3, 1)) print("bit32.extract()", bit32.extract and bit32.extract(7, 0, 2)) print("bit32.replace()", bit32.replace and bit32.replace(2^32-1, 0, 3, 29)) print("bit32.lrotate()", bit32.lrotate and bit32.lrotate(2^31, 1)) print("bit32.lshift()", bit32.lshift and bit32.lshift(2^31+2^30, 1)) print("bit32.rrotate()", bit32.rrotate and bit32.rrotate(2, 2)) print("bit32.rshift()", bit32.rshift and bit32.rshift(16, 3)) ____________________________________________________________'' print("debug.getuservalue()", F(debug.getuservalue(false))) print("debug.setuservalue()", pcall(function() debug.setuservalue(false, {}) end)) print("debug.setuservalue()", pcall(function() debug.setuservalue(io.stderr, 1) end)) print("debug.setmetatable()", F(debug.setmetatable({}, {}))) ____________________________________________________________'' do local t = setmetatable({}, { __ipairs = function() return ipairs({ 1, 2, 3 }) end, __pairs = function() return pairs({ a = "a" }) end, }) for k,v in pairs(t) do print("pairs()", k, v) end for i,v in ipairs(t) do print("ipairs()", i, v) end end ____________________________________________________________'' do local code = "print('hello world')\n" local badcode = "print('blub\n" print("load()", pcall(function() load(true) end)) print("load()", F(load(badcode))) print("load()", F(load(code))) print("load()", F(load(code, "[L]"))) print("load()", F(load(code, "[L]", "b"))) print("load()", F(load(code, "[L]", "t"))) print("load()", F(load(code, "[L]", "bt"))) local f = load(code, "[L]", "bt", {}) print("load()", pcall(f)) f = load(code, "[L]", "bt", { print = print }) print("load()", pcall(f)) local bytecode = string.dump(f) print("load()", F(load(bytecode))) print("load()", F(load(bytecode, "[L]"))) print("load()", F(load(bytecode, "[L]", "b"))) print("load()", F(load(bytecode, "[L]", "t"))) print("load()", F(load(bytecode, "[L]", "bt"))) f = load(bytecode, "[L]", "bt", {}) print("load()", pcall(f)) f = load(bytecode, "[L]", "bt", { print = print }) print("load()", pcall(f)) local function make_loader(code) local mid = math.floor( #code/2 ) local array = { code:sub(1, mid), code:sub(mid+1) } local i = 0 return function() i = i + 1 return array[i] end end print("load()", F(load(make_loader(badcode)))) print("load()", F(load(make_loader(code)))) print("load()", F(load(make_loader(code), "[L]"))) print("load()", F(load(make_loader(code), "[L]", "b"))) print("load()", F(load(make_loader(code), "[L]", "t"))) print("load()", F(load(make_loader(code), "[L]", "bt"))) f = load(make_loader(code), "[L]", "bt", {}) print("load()", pcall(f)) f = load(make_loader(code), "[L]", "bt", { print = print }) print("load()", pcall(f)) print("load()", F(load(make_loader(bytecode)))) print("load()", F(load(make_loader(bytecode), "[L]"))) print("load()", F(load(make_loader(bytecode), "[L]", "b"))) print("load()", F(load(make_loader(bytecode), "[L]", "t"))) print("load()", F(load(make_loader(bytecode), "[L]", "bt"))) f = load(make_loader(bytecode), "[L]", "bt", {}) print("load()", pcall(f)) f = load(make_loader(bytecode), "[L]", "bt", { print = print }) print("load()", pcall(f)) writefile("good.lua", code) writefile("bad.lua", badcode) writefile("good.luac", bytecode, true) print("loadfile()", F(loadfile("bad.lua"))) print("loadfile()", F(loadfile("good.lua"))) print("loadfile()", F(loadfile("good.lua", "b"))) print("loadfile()", F(loadfile("good.lua", "t"))) print("loadfile()", F(loadfile("good.lua", "bt"))) f = loadfile("good.lua", "bt", {}) print("loadfile()", pcall(f)) f = loadfile("good.lua", "bt", { print = print }) print("loadfile()", pcall(f)) print("loadfile()", F(loadfile("good.luac"))) print("loadfile()", F(loadfile("good.luac", "b"))) print("loadfile()", F(loadfile("good.luac", "t"))) print("loadfile()", F(loadfile("good.luac", "bt"))) f = loadfile("good.luac", "bt", {}) print("loadfile()", pcall(f)) f = loadfile("good.luac", "bt", { print = print }) print("loadfile()", pcall(f)) os.remove("good.lua") os.remove("bad.lua") os.remove("good.luac") end ____________________________________________________________'' do local function func(throw) if throw then error("argh") else return 1, 2, 3 end end local function tb(err) return "|"..err.."|" end print("xpcall()", xpcall(func, debug.traceback, false)) print("xpcall()", xpcall(func, debug.traceback, true)) print("xpcall()", xpcall(func, tb, true)) local function func2(cb) print("xpcall()", xpcall(cb, debug.traceback, "str")) end local function func3(cb) print("pcall()", pcall( cb, "str" )) end local function cb(arg) coroutine.yield( 2 ) return arg end local c = coroutine.wrap( func2 ) print("xpcall()", c(cb)) print("xpcall()", c()) local c = coroutine.wrap( func3 ) print("pcall()", c(cb)) print("pcall()", c()) end ____________________________________________________________'' do local t = setmetatable({ 1 }, { __len = function() return 5 end }) print("rawlen()", rawlen(t), rawlen("123")) end ____________________________________________________________'' print("collectgarbage()", collectgarbage("isrunning")) print("collectgarbage()", collectgarbage("stop")) print("collectgarbage()", collectgarbage("isrunning")) print("collectgarbage()", collectgarbage("collect")) print("collectgarbage()", collectgarbage("isrunning")) print("collectgarbage()", collectgarbage("step", 10)) print("collectgarbage()", collectgarbage("isrunning")) print("collectgarbage()", collectgarbage("restart")) print("collectgarbage()", collectgarbage("isrunning")) print("collectgarbage()", collectgarbage("count")) print("collectgarbage()", collectgarbage("generational")) print("collectgarbage()", collectgarbage("incremental")) ____________________________________________________________'' print("os.execute()", os.execute("exit 1")) print("os.execute()", os.execute("echo 'hello world'")) print("os.execute()", os.execute("no_such_file")) ____________________________________________________________'' do local t = table.pack("a", nil, "b", nil) print("table.(un)pack()", t.n, table.unpack(t, 1, t.n)) end ____________________________________________________________'' do print("coroutine.running()", F(coroutine.wrap(function() return coroutine.running() end)())) print("coroutine.running()", F(coroutine.running())) local main_co, co1, co2 = coroutine.running() -- coroutine.yield print("coroutine.yield()", pcall(function() coroutine.yield(1, 2, 3) end)) print("coroutine.yield()", coroutine.wrap(function() coroutine.yield(1, 2, 3) end)()) print("coroutine.resume()", coroutine.resume(main_co, 1, 2, 3)) co1 = coroutine.create(function(a, b, c) print("coroutine.resume()", a, b, c) return a, b, c end) print("coroutine.resume()", coroutine.resume(co1, 1, 2, 3)) co1 = coroutine.create(function() print("coroutine.status()", "[co1] main is", coroutine.status(main_co)) print("coroutine.status()", "[co1] co2 is", coroutine.status(co2)) end) co2 = coroutine.create(function() print("coroutine.status()", "[co2] main is", coroutine.status(main_co)) print("coroutine.status()", "[co2] co2 is", coroutine.status(co2)) coroutine.yield() coroutine.resume(co1) end) print("coroutine.status()", coroutine.status(main_co)) print("coroutine.status()", coroutine.status(co2)) coroutine.resume(co2) print("coroutine.status()", F(coroutine.status(co2))) coroutine.resume(co2) print("coroutine.status()", F(coroutine.status(co2))) end ____________________________________________________________'' print("math.log()", math.log(1000)) print("math.log()", math.log(1000, 10)) ____________________________________________________________'' do local path, prefix = "./?.lua;?/init.lua;../?.lua", "package.searchpath()" print(prefix, package.searchpath("no.such.module", path)) print(prefix, package.searchpath("no.such.module", "")) print(prefix, package.searchpath("compat52", path)) print(prefix, package.searchpath("no:such:module", path, ":", "|")) end ____________________________________________________________'' do local function mod_func() return {} end local function my_searcher(name) if name == "my.module" then print("package.searchers", "my.module found") return mod_func end end local function my_searcher2(name) if name == "my.module" then print("package.searchers", "my.module found 2") return mod_func end end table.insert(package.searchers, my_searcher) require("my.module") package.loaded["my.module"] = nil local new_s = { my_searcher2 } for i,f in ipairs(package.searchers) do new_s[i+1] = f end package.searchers = new_s require("my.module") end ____________________________________________________________'' do print("string.find()", ("abc\0abc\0abc"):find("[^a\0]+")) print("string.find()", ("abc\0abc\0abc"):find("%w+\0", 5)) for x in ("abc\0def\0ghi"):gmatch("[^\0]+") do print("string.gmatch()", x) end for x in ("abc\0def\0ghi"):gmatch("%w*\0") do print("string.gmatch()", #x) end print("string.gsub()", ("abc\0def\0ghi"):gsub("[\0]", "X")) print("string.gsub()", ("abc\0def\0ghi"):gsub("%w*\0", "X")) print("string.gsub()", ("abc\0def\0ghi"):gsub("%A", "X")) print("string.match()", ("abc\0abc\0abc"):match("([^\0a]+)")) print("string.match()", #("abc\0abc\0abc"):match(".*\0")) print("string.rep()", string.rep("a", 0)) print("string.rep()", string.rep("b", 1)) print("string.rep()", string.rep("c", 4)) print("string.rep()", string.rep("a", 0, "|")) print("string.rep()", string.rep("b", 1, "|")) print("string.rep()", string.rep("c", 4, "|")) local _tostring = tostring function tostring(v) if type(v) == "number" then return "(".._tostring(v)..")" else return _tostring(v) end end print("string.format()", string.format("%q", "\"\\\0000\0010\r0\n0\t0\"")) print("string.format()", string.format("%12.3fx%%sxx%.6s", 3.1, {})) print("string.format()", string.format("%-3f %%%s %%s", 3.1, true)) print("string.format()", string.format("% 3.2g %%d %%%s", 3.1, nil)) print("string.format()", string.format("%+3d %%d %%%%%10.6s", 3.1, io.stdout)) print("string.format()", pcall(function() print("string.format()", string.format("%d %%s", {})) end)) tostring = _tostring end ____________________________________________________________'' do print("io.write()", io.type(io.write("hello world\n"))) local f = assert(io.tmpfile()) print("file:write()", io.type(f:write("hello world\n"))) f:close() end ____________________________________________________________'' do writefile("data.txt", "123 18.8 hello world\ni'm here\n") for a,b in io.lines("test.lua", 2, "*l") do print("io.lines()", a, b) break end for l in io.lines("test.lua") do print("io.lines()", l) break end for n1,n2,rest in io.lines("data.txt", "*n", "*n", "*a") do print("io.lines()", n1, n2, rest) end for l in io.lines("data.txt") do print("io.lines()", l) end print("io.lines()", pcall(function() for l in io.lines("data.txt", "*x") do print(l) end end)) print("io.lines()", pcall(function() for l in io.lines("no_such_file.txt") do print(l) end end)) local f = assert(io.open("test.lua", "r")) for a,b in f:lines(2, "*l") do print("file:lines()", a, b) break end f:close() f = assert(io.open("data.txt", "r")) for n1,n2,rest in f:lines("*n", "*n", "*a") do print("file:lines()", n1, n2, rest) end f:close() f = assert(io.open("data.txt", "r")) for l in f:lines() do print("file:lines()", l) end f:close() print("file:lines()", pcall(function() for l in f:lines() do print(l) end end)) print("file:lines()", pcall(function() local f = assert(io.open("data.txt", "r")) for l in f:lines("*l", "*x") do print(l) end f:close() end)) os.remove("data.txt") end ____________________________________________________________'' do local modname = _VERSION:gsub("^.*(%d+)%.(%d+).-$", "%1%2-testmod") local ok, mod = pcall(require, modname) if not ok then io.write("### no ", modname, ".so or ", modname, ".dll! ###\n") io.write("'require' claimed: ", mod, "\n") io.write("### Skipping C API tests! ###\n") else print("C API", mod.tonumber(12)) print("C API", mod.tonumber("12")) print("C API", mod.tonumber("0")) print("C API", mod.tonumber(false)) print("C API", mod.tonumber("error")) print("C API", mod.tointeger(12)) print("C API", mod.tointeger("12")) print("C API", mod.tointeger("0")) print("C API", mod.tointeger(math.pi)) print("C API", mod.tointeger(false)) print("C API", mod.tointeger("error")) print("C API", mod.unsigned(1)) print("C API", mod.unsigned(2^31+1)) print("C API", mod.unsigned(2^32+1)) print("C API", mod.unsigned(-1)) print("C API", mod.unsigned(2^56)) print("C API", mod.unsigned("17")) print("C API", pcall(mod.unsigned, "0")) print("C API", pcall(mod.unsigned, true)) print("C API", mod.optunsigned()) print("C API", mod.optunsigned(42)) print("C API", pcall(mod.optunsigned, true)) print("C API", mod.len("123")) print("C API", mod.len({ 1, 2, 3})) print("C API", pcall(mod.len, true)) local ud, meta = mod.newproxy() meta.__len = function() return 5 end print("C API", mod.len(ud)) meta.__len = function() return true end print("C API", pcall(mod.len, ud)) print("C API", mod.copy(true, "string", {}, 1)) print("C API", mod.rawxetp()) print("C API", mod.rawxetp("I'm back")) print("C API", F(mod.globals()), mod.globals() == _G) local t = {} print("C API", F(mod.subtable(t))) local x, msg = mod.subtable(t) print("C API", F(x, msg, x == t.xxx)) print("C API", F(mod.udata())) print("C API", mod.udata("nosuchtype")) print("C API", F(mod.uservalue())) print("C API", mod.getupvalues()) print("C API", mod.absindex("hi", true)) print("C API", mod.arith(2, 1)) print("C API", mod.arith(3, 5)) print("C API", mod.compare(1, 1)) print("C API", mod.compare(2, 1)) print("C API", mod.compare(1, 2)) print("C API", mod.tolstring("string")) local t = setmetatable({}, { __tostring = function(v) return "mytable" end }) print("C API", mod.tolstring( t ) ) local t = setmetatable({}, { __tostring = function(v) return nil end }) print("C API", pcall( mod.tolstring, t ) ) local a, b = mod.requiref() print("C API", type(a), type(b), a.boolean, b.boolean, type(requiref1), type(requiref2)) print("C API", mod.buffer()) print("debug.getuservalue()", F(debug.getuservalue(ud))) print("debug.setuservalue()", F(debug.setuservalue(ud, {}))) print("debug.getuservalue()", F(debug.getuservalue(ud))) print("debug.setuservalue()", F(debug.setuservalue(ud, nil))) print("debug.getuservalue()", F(debug.getuservalue(ud))) end end ____________________________________________________________'' io.write("### Output written to ", outfile, "! ###\n")