mirror of
https://github.com/Reuh/candran.git
synced 2025-10-27 17:59:30 +00:00
1119 lines
22 KiB
Lua
1119 lines
22 KiB
Lua
local candran = dofile(arg[1] or "../candran.lua")
|
|
candran.default.indentation = "\t"
|
|
candran.default.mapLines = false
|
|
|
|
local load = require("candran.util").load
|
|
|
|
-- Text formatting
|
|
local colors = {
|
|
black = 30,
|
|
red = 31,
|
|
green = 32,
|
|
yellow = 33,
|
|
blue = 34,
|
|
purple = 35,
|
|
cyan = 36,
|
|
white = 37,
|
|
|
|
bgBlack = 40,
|
|
bgRed = 41,
|
|
bgGreen = 42,
|
|
bgYellow = 43,
|
|
bgBlue = 44,
|
|
bgPurple = 45,
|
|
bgCyan = 46,
|
|
bgWhite = 47,
|
|
|
|
bold = 1,
|
|
underline = 4
|
|
}
|
|
local function c(text, ...)
|
|
local codes = {}
|
|
for _, color in ipairs{...} do
|
|
table.insert(codes, colors[color])
|
|
end
|
|
return ("\027[%sm%s\027[0m"):format(table.concat(codes, ";"), text)
|
|
end
|
|
|
|
-- test helper
|
|
local results = {} -- tests result
|
|
local function test(name, candranCode, expectedResult, options)
|
|
results[name] = { result = "not finished", message = "no info" }
|
|
local self = results[name]
|
|
|
|
-- options
|
|
options = options or {}
|
|
options.chunkname = name
|
|
|
|
-- make code
|
|
local success, code = pcall(function() return assert(candran.make(candranCode, options)) end)
|
|
if not success then
|
|
self.result = "error"
|
|
self.message = c("/!\\ error while making code:\n", "bold", "red")..c(code, "red")
|
|
return
|
|
end
|
|
|
|
-- load code
|
|
local env = {}
|
|
for k, v in pairs(_G) do env[k] = v end
|
|
local success, func = pcall(load, code, nil, env)
|
|
if not success then
|
|
self.result = "error"
|
|
self.message = c("/!\\ error while loading code:\n"..func.."\ngenerated code:\n", "bold", "red")..c(code, "red")
|
|
return
|
|
end
|
|
|
|
-- run code
|
|
local success, output = pcall(func)
|
|
if not success then
|
|
self.result = "error"
|
|
self.message = c("/!\\ error while running code:\n"..output.."\ngenerated code:\n", "bold", "red")..c(code, "red")
|
|
return
|
|
end
|
|
|
|
-- check result
|
|
if output ~= expectedResult then
|
|
self.result = "fail"
|
|
self.message = c("/!\\ invalid result from the code; it returned "..tostring(output).." instead of "..tostring(expectedResult).."; generated code:\n", "bold", "purple")..c(code, "purple")
|
|
return
|
|
else
|
|
self.result = "success"
|
|
return
|
|
end
|
|
end
|
|
|
|
-- tests
|
|
print("Running tests...")
|
|
|
|
------------------
|
|
-- PREPROCESSOR --
|
|
------------------
|
|
|
|
test("preprocessor", [[
|
|
#local foo = true
|
|
return true
|
|
]], true)
|
|
|
|
test("preprocessor condition", [[
|
|
#local foo = true
|
|
#if not foo then
|
|
return false
|
|
#else
|
|
return true
|
|
#end
|
|
]], true)
|
|
|
|
test("preprocessor candran table", [[
|
|
#write(("return %q"):format(candran.VERSION))
|
|
]], candran.VERSION)
|
|
|
|
test("preprocessor output variable", [[
|
|
#output = "return 5"
|
|
]], 5)
|
|
|
|
test("preprocessor import function", [[
|
|
#import("toInclude")
|
|
return toInclude
|
|
]], 5)
|
|
|
|
test("preprocessor include function", [[
|
|
#include('toInclude.lua')
|
|
]], 5)
|
|
|
|
test("preprocessor write function", [[
|
|
#write("local a = true")
|
|
return a
|
|
]], true)
|
|
|
|
test("preprocessor placeholder function", [[
|
|
#placeholder('foo')
|
|
]], 5, { foo = "return 5" })
|
|
|
|
test("preprocessor options", [[
|
|
#if not foo == "sky" then
|
|
# error("Invalid foo argument")
|
|
#end
|
|
return true
|
|
]], true, { foo = "sky" })
|
|
|
|
test("preprocessor long comment", "--[[\n"..[[
|
|
#error("preprocessor should ignore long comments")
|
|
]].."]]"..[[
|
|
return true
|
|
]], true)
|
|
|
|
test("preprocessor long comment in long string", [[
|
|
a=]].."[[--[[\n"..[[
|
|
#error("preprocessor should ignore long strings")
|
|
]].."]]"..[[
|
|
return a
|
|
]], "--[[\n"..[[
|
|
#error("preprocessor should ignore long strings")
|
|
]])
|
|
|
|
test("preprocessor macro remove function", [[
|
|
#define("log(...)", "")
|
|
log("test")
|
|
return true
|
|
]], true)
|
|
|
|
test("preprocessor macro replace function", [[
|
|
#define("log(x)", "a = x")
|
|
log("test")
|
|
return a
|
|
]], "test")
|
|
|
|
test("preprocessor macro identifier replace function", [[
|
|
#define("test(x)", "x = 42")
|
|
test(hello)
|
|
return hello
|
|
]], 42)
|
|
|
|
test("preprocessor macro replace function with vararg", [[
|
|
#define("log(...)", "a, b, c = ...")
|
|
log(1, 2, 3)
|
|
assert(a == 1)
|
|
assert(b == 2)
|
|
assert(c == 3)
|
|
return true
|
|
]], true)
|
|
|
|
test("preprocessor macro replace function with vararg and arg", [[
|
|
#define("log(x, ...)", "a, b, c = x, ...")
|
|
log(1, 2, 3)
|
|
assert(a == 1)
|
|
assert(b == 2)
|
|
assert(c == 3)
|
|
return true
|
|
]], true)
|
|
|
|
test("preprocessor macro replace variable", [[
|
|
#define("a", "42")
|
|
return a
|
|
]], 42)
|
|
|
|
test("preprocessor macro prevent recursive macro", [[
|
|
#define("f(x)", "x")
|
|
local x = 42
|
|
x = f(x)
|
|
return x
|
|
]], 42)
|
|
|
|
test("preprocessor macro replace variable with function", [[
|
|
#define("a", function() return "42" end)
|
|
return a
|
|
]], 42)
|
|
|
|
test("preprocessor macro replace function with function", [[
|
|
#define("test(x)", function(x) return ("%s = 42"):format(x) end)
|
|
test(hello)
|
|
return hello
|
|
]], 42)
|
|
|
|
----------------------
|
|
-- SYNTAX ADDITIONS --
|
|
----------------------
|
|
|
|
-- Assignement operators
|
|
test("+=", [[
|
|
local a = 5
|
|
a += 2
|
|
return a
|
|
]], 7)
|
|
test("-=", [[
|
|
local a = 5
|
|
a -= 2
|
|
return a
|
|
]], 3)
|
|
test("*=", [[
|
|
local a = 5
|
|
a *= 2
|
|
return a
|
|
]], 10)
|
|
test("/=", [[
|
|
local a = 5
|
|
a /= 2
|
|
return a
|
|
]], 5/2)
|
|
test("//=", [[
|
|
local a = 5
|
|
a //= 2
|
|
return a
|
|
]], 2)
|
|
test("^=", [[
|
|
local a = 5
|
|
a ^= 2
|
|
return a
|
|
]], 25)
|
|
test("%=", [[
|
|
local a = 5
|
|
a %= 2
|
|
return a
|
|
]], 5%2)
|
|
test("..=", [[
|
|
local a = "hello"
|
|
a ..= " world"
|
|
return a
|
|
]], "hello world")
|
|
test("and=", [[
|
|
local a = true
|
|
a and= "world"
|
|
return a
|
|
]], "world")
|
|
test("or=", [[
|
|
local a = false
|
|
a or= "world"
|
|
return a
|
|
]], "world")
|
|
test("&=", [[
|
|
local a = 5
|
|
a &= 3
|
|
return a
|
|
]], 1)
|
|
test("|=", [[
|
|
local a = 5
|
|
a |= 3
|
|
return a
|
|
]], 7)
|
|
test("<<=", [[
|
|
local a = 23
|
|
a <<= 2
|
|
return a
|
|
]], 92)
|
|
test(">>=", [[
|
|
local a = 23
|
|
a >>= 2
|
|
return a
|
|
]], 5)
|
|
|
|
test("right assigments operators", [[
|
|
local a = 5
|
|
a =+ 2 assert(a == 7, "=+")
|
|
a =- 2 assert(a == -5, "=-")
|
|
a =* -2 assert(a == 10, "=*")
|
|
a =/ 2 assert(a == 0.2, "=/")
|
|
a =// 2 assert(a == 10, "=//")
|
|
a =^ 2 assert(a == 1024, "=^")
|
|
a =% 2000 assert(a == 976, "=%")
|
|
|
|
a = "world"
|
|
a =.. "hello " assert(a == "hello world", "=..")
|
|
a =and true assert(a == "hello world", "=and")
|
|
|
|
a = false
|
|
a =or nil assert(a == false, "=or")
|
|
|
|
a = 3
|
|
a =& 5 assert(a == 1, '=&')
|
|
a =| 20 assert(a == 21, "=|")
|
|
a =<< 1 assert(a == 2097152, "=<<")
|
|
|
|
a = 2
|
|
a =>> 23 assert(a == 5, "=>>")
|
|
]], nil)
|
|
|
|
test("some left+right assigments operators", [[
|
|
local a = 5
|
|
a -=+ 2 assert(a == 8, "-=+")
|
|
|
|
a = "hello"
|
|
a ..=.. " world " assert(a == "hello world hello", "..=..")
|
|
]], nil)
|
|
|
|
test("left assigments operators priority", [[
|
|
local a = 5
|
|
a *= 2 + 3
|
|
return a
|
|
]], 25)
|
|
test("right assigments operators priority", [[
|
|
local a = 5
|
|
a =/ 2 + 3
|
|
return a
|
|
]], 1)
|
|
test("left+right assigments operators priority", [[
|
|
local a = 5
|
|
a *=/ 2 + 3
|
|
return a
|
|
]], 5)
|
|
|
|
-- Default function parameters
|
|
test("default parameters", [[
|
|
local function test(hey, def="re", no, foo=("bar"):gsub("bar", "batru"))
|
|
return def..foo
|
|
end
|
|
return test(78, "SANDWICH", true)
|
|
]], "SANDWICHbatru")
|
|
|
|
-- @ self alias
|
|
test("@ as self alias", [[
|
|
local a = {}
|
|
function a:hey()
|
|
return @ == self
|
|
end
|
|
return a:hey()
|
|
]], true)
|
|
test("@ as self alias and indexation", [[
|
|
local a = {
|
|
foo = "Hoi"
|
|
}
|
|
function a:hey()
|
|
return @.foo
|
|
end
|
|
return a:hey()
|
|
]], "Hoi")
|
|
test("@name indexation", [[
|
|
local a = {
|
|
foo = "Hoi"
|
|
}
|
|
function a:hey()
|
|
return @foo
|
|
end
|
|
return a:hey()
|
|
]], "Hoi")
|
|
test("@name method call", [[
|
|
local a = {
|
|
foo = "Hoi",
|
|
bar = function(self)
|
|
return self.foo
|
|
end
|
|
}
|
|
function a:hey()
|
|
return @bar()
|
|
end
|
|
return a:hey()
|
|
]], "Hoi")
|
|
test("@[expt] indexation", [[
|
|
local a = {
|
|
foo = "Hoi"
|
|
}
|
|
function a:hey()
|
|
return @["foo"]
|
|
end
|
|
return a:hey()
|
|
]], "Hoi")
|
|
|
|
-- Short anonymous functions declaration
|
|
test("short anonymous function declaration", [[
|
|
local a = (arg1)
|
|
return arg1
|
|
end
|
|
return a(5)
|
|
]], 5)
|
|
test("short anonymous method declaration", [[
|
|
local a = :(arg1)
|
|
return self + arg1
|
|
end
|
|
return a(2, 3)
|
|
]], 5)
|
|
test("short anonymous method parsing edge cases", [[
|
|
-- Taken from the file I used when solving this horror, too tired to make separate tests.
|
|
x = ""
|
|
function a(s)
|
|
x = x .. tostring(s or "+")
|
|
end
|
|
k=true
|
|
while k do
|
|
k=false
|
|
cap = {[0] = op, a}
|
|
a(tostring(h))
|
|
if true then
|
|
a()
|
|
if false then
|
|
a = x, (a)
|
|
c()
|
|
end
|
|
a()
|
|
end
|
|
a()
|
|
end
|
|
a()
|
|
a("l")
|
|
let h = (h)
|
|
a("h")
|
|
end
|
|
h()
|
|
a("lol")
|
|
if false then exit() end
|
|
a("pmo")
|
|
if true then
|
|
if false
|
|
a = (h)
|
|
|
|
a()
|
|
a("pom")
|
|
end
|
|
a("lo")
|
|
a("kol")
|
|
if false then
|
|
j()
|
|
p()
|
|
end
|
|
do
|
|
b = [
|
|
k = () end
|
|
if false
|
|
k = (lol)
|
|
error("niet")
|
|
end
|
|
|
|
k()
|
|
a()]
|
|
end
|
|
if a() then h() end
|
|
local function f (...)
|
|
if select('#', ...) == 1 then
|
|
return (...)
|
|
else
|
|
return "***"
|
|
end
|
|
end
|
|
return f(x)
|
|
]], "nil++++lhlolpmo+pomlokol++")
|
|
|
|
-- let variable declaration
|
|
test("let variable declaration", [[
|
|
let a = {
|
|
foo = function()
|
|
return type(a)
|
|
end
|
|
}
|
|
return a.foo()
|
|
]], "table")
|
|
|
|
-- continue keyword
|
|
test("continue keyword in while", [[
|
|
local a = ""
|
|
local i = 0
|
|
while i < 10 do
|
|
i = i + 1
|
|
if i % 2 == 0 then
|
|
continue
|
|
end
|
|
a = a .. i
|
|
end
|
|
return a
|
|
]], "13579")
|
|
test("continue keyword in while, used with break", [[
|
|
local a = ""
|
|
local i = 0
|
|
while i < 10 do
|
|
i = i + 1
|
|
if i % 2 == 0 then
|
|
continue
|
|
end
|
|
a = a .. i
|
|
if i == 5 then
|
|
break
|
|
end
|
|
end
|
|
return a
|
|
]], "135")
|
|
test("continue keyword in repeat", [[
|
|
local a = ""
|
|
local i = 0
|
|
repeat
|
|
i = i + 1
|
|
if i % 2 == 0 then
|
|
continue
|
|
end
|
|
a = a .. i
|
|
until i == 10
|
|
return a
|
|
]], "13579")
|
|
test("continue keyword in repeat, used with break", [[
|
|
local a = ""
|
|
local i = 0
|
|
repeat
|
|
i = i + 1
|
|
if i % 2 == 0 then
|
|
continue
|
|
end
|
|
a = a .. i
|
|
if i == 5 then
|
|
break
|
|
end
|
|
until i == 10
|
|
return a
|
|
]], "135")
|
|
test("continue keyword in fornum", [[
|
|
local a = ""
|
|
for i=1, 10 do
|
|
if i % 2 == 0 then
|
|
continue
|
|
end
|
|
a = a .. i
|
|
end
|
|
return a
|
|
]], "13579")
|
|
test("continue keyword in fornum, used with break", [[
|
|
local a = ""
|
|
for i=1, 10 do
|
|
if i % 2 == 0 then
|
|
continue
|
|
end
|
|
a = a .. i
|
|
if i == 5 then
|
|
break
|
|
end
|
|
end
|
|
return a
|
|
]], "135")
|
|
test("continue keyword in for", [[
|
|
local t = {1,2,3,4,5,6,7,8,9,10}
|
|
local a = ""
|
|
for _, i in ipairs(t) do
|
|
if i % 2 == 0 then
|
|
continue
|
|
end
|
|
a = a .. i
|
|
end
|
|
return a
|
|
]], "13579")
|
|
test("continue keyword in for, used with break", [[
|
|
local t = {1,2,3,4,5,6,7,8,9,10}
|
|
local a = ""
|
|
for _, i in ipairs(t) do
|
|
if i % 2 == 0 then
|
|
continue
|
|
end
|
|
a = a .. i
|
|
if i == 5 then
|
|
break
|
|
end
|
|
end
|
|
return a
|
|
]], "135")
|
|
|
|
-- push keyword
|
|
test("push keyword", [[
|
|
function a()
|
|
for i=1, 5 do
|
|
push i, "next"
|
|
end
|
|
return "done"
|
|
end
|
|
return table.concat({a()})
|
|
]], "1next2next3next4next5nextdone")
|
|
test("push keyword variable length", [[
|
|
function v()
|
|
return "hey", "hop"
|
|
end
|
|
function w()
|
|
return "foo", "bar"
|
|
end
|
|
function a()
|
|
push 5, v(), w()
|
|
return
|
|
end
|
|
return table.concat({a()})
|
|
]], "5heyfoobar")
|
|
|
|
-- implicit push
|
|
test("implicit push", [[
|
|
function a()
|
|
for i=1, 5 do
|
|
i, "next"
|
|
end
|
|
return "done"
|
|
end
|
|
return table.concat({a()})
|
|
]], "1next2next3next4next5nextdone")
|
|
test("implicit push variable length", [[
|
|
function v()
|
|
return "hey", "hop"
|
|
end
|
|
function w()
|
|
return "foo", "bar"
|
|
end
|
|
function a()
|
|
if true then
|
|
5, v(), w()
|
|
end
|
|
end
|
|
return table.concat({a()})
|
|
]], "5heyfoobar")
|
|
|
|
-- statement expressions
|
|
test("if statement expressions", [[
|
|
a = if false then
|
|
"foo" -- i.e. push "foo", i.e. return "foo"
|
|
else
|
|
"bar"
|
|
end
|
|
return a
|
|
]], "bar")
|
|
test("do statement expressions", [[
|
|
a = do
|
|
"bar"
|
|
end
|
|
return a
|
|
]], "bar")
|
|
test("while statement expressions", [[
|
|
i=0
|
|
a, b, c = while i<2 do i=i+1; i end
|
|
return table.concat({a, b, tostring(c)})
|
|
]], "12nil")
|
|
test("repeat statement expressions", [[
|
|
local i = 0
|
|
a, b, c = repeat i=i+1; i until i==2
|
|
return table.concat({a, b, tostring(c)})
|
|
]], "12nil")
|
|
test("for statement expressions", [[
|
|
a, b, c = for i=1,2 do i end
|
|
return table.concat({a, b, tostring(c)})
|
|
]], "12nil")
|
|
|
|
-- table comprehension
|
|
test("table comprehension sequence", [[
|
|
return table.concat([for i=1,10 do i end])
|
|
]], "12345678910")
|
|
test("table comprehension associative/self", [[
|
|
a = [for i=1, 10 do @[i] = true end]
|
|
return a[1] and a[10]
|
|
]], true)
|
|
test("table comprehension variable length", [[
|
|
local unpack = table.unpack or unpack
|
|
t1 = {"hey", "hop"}
|
|
t2 = {"foo", "bar"}
|
|
return table.concat([push unpack(t1); push unpack(t2)])
|
|
]], "heyhopfoobar")
|
|
|
|
-- one line statements
|
|
test("one line if", [[
|
|
a = 5
|
|
if false
|
|
a = 0
|
|
return a
|
|
]], 5)
|
|
test("one line if-elseif", [[
|
|
a = 5
|
|
if false
|
|
a = 0
|
|
elseif true
|
|
a = 3
|
|
elseif false
|
|
a = -1
|
|
return a
|
|
]], 3)
|
|
test("one line for", [[
|
|
a = 0
|
|
for i=1,5
|
|
a = a + 1
|
|
return a
|
|
]], 5)
|
|
test("one line while", [[
|
|
a = 0
|
|
while a < 5
|
|
a = a + 1
|
|
return a
|
|
]], 5)
|
|
|
|
-- suffixable string litals, table, table comprehension
|
|
test("suffixable string litteral method", [[
|
|
return "foo":len()
|
|
]], 3)
|
|
test("suffixable string litteral method lua conflict", [[
|
|
local s = function() return "four" end
|
|
return s"foo":len()
|
|
]], 4)
|
|
test("suffixable string litteral dot index", [[
|
|
local a = "foo".len
|
|
return a("foo")
|
|
]], 3)
|
|
test("suffixable string litteral dot index lua conflict", [[
|
|
local s = function() return {len=4} end
|
|
local a = s"foo".len
|
|
return a
|
|
]], 4)
|
|
test("suffixable string litteral array index", [[
|
|
local a = "foo"["len"]
|
|
return a("foo")
|
|
]], 3)
|
|
test("suffixable string litteral dot index lua conflict", [[
|
|
local s = function() return {len=4} end
|
|
local a = s"foo"["len"]
|
|
return a
|
|
]], 4)
|
|
|
|
test("suffixable table litteral method", [[
|
|
return {a=3,len=function(t) return t.a end}:len()
|
|
]], 3)
|
|
test("suffixable table litteral method lua conflict", [[
|
|
local s = function() return "four" end
|
|
return s{a=3,len=function(t) return t.a end}:len()
|
|
]], 4)
|
|
test("suffixable table litteral dot index", [[
|
|
return {len=3}.len
|
|
]], 3)
|
|
test("suffixable table litteral dot index lua conflict", [[
|
|
local s = function() return {len=4} end
|
|
return s{len=3}.len
|
|
]], 4)
|
|
test("suffixable table litteral array index", [[
|
|
return {len=3}["len"]
|
|
]], 3)
|
|
test("suffixable table litteral dot index lua conflict", [[
|
|
local s = function() return {len=4} end
|
|
return s{len=3}["len"]
|
|
]], 4)
|
|
|
|
test("suffixable table comprehension method", [[
|
|
return [@len = function() return 3 end]:len()
|
|
]], 3)
|
|
test("suffixable table comprehension dot index", [[
|
|
return [@len = 3].len
|
|
]], 3)
|
|
test("suffixable table comprehension array index", [[
|
|
return [@len=3]["len"]
|
|
]], 3)
|
|
|
|
-- let in condition expression
|
|
test("let in while condition, evaluation each iteration", [[
|
|
local s = ""
|
|
local i = 0
|
|
while (a = i+2) and i < 3 do
|
|
s = s .. tostring(a)
|
|
i = i + 1
|
|
a = 0
|
|
end
|
|
return s
|
|
]], "234")
|
|
test("let in while condition, scope", [[
|
|
local s = ""
|
|
local i = 0
|
|
while (a = i+2) and i < 3 do
|
|
s = s .. tostring(a)
|
|
i = i + 1
|
|
a = 0
|
|
end
|
|
return a
|
|
]], nil)
|
|
test("several let in while condition, evaluation order", [[
|
|
local s = ""
|
|
local i = 0
|
|
while (a = (b=i+1)+1) and i < 3 do
|
|
assert(b==i+1)
|
|
s = s .. tostring(a)
|
|
i = i + 1
|
|
a = 0
|
|
end
|
|
return s
|
|
]], "234")
|
|
test("several let in while condition, only test the first", [[
|
|
local s = ""
|
|
local i = 0
|
|
while (a,b = false,i) and i < 3 do
|
|
s = s .. tostring(a)
|
|
i = i + 1
|
|
end
|
|
return s
|
|
]], "")
|
|
|
|
test("let in if condition", [[
|
|
if a = false then
|
|
error("condition was false")
|
|
elseif b = nil then
|
|
error("condition was nil")
|
|
elseif c = true then
|
|
return "ok"
|
|
elseif d = true then
|
|
error("should not be reachable")
|
|
end
|
|
]], "ok")
|
|
test("let in if condition, scope", [[
|
|
local r
|
|
if a = false then
|
|
error("condition was false")
|
|
elseif b = nil then
|
|
error("condition was nil")
|
|
elseif c = true then
|
|
assert(a == false)
|
|
assert(d == nil)
|
|
r = "ok"
|
|
elseif d = true then
|
|
error("should not be reachable")
|
|
end
|
|
assert(c == nil)
|
|
return r
|
|
]], "ok")
|
|
test("several let in if condition, only test the first", [[
|
|
if a = false then
|
|
error("condition was false")
|
|
elseif b = nil then
|
|
error("condition was nil")
|
|
elseif c, d = false, "ok" then
|
|
error("should have tested against c")
|
|
else
|
|
return d
|
|
end
|
|
]], "ok")
|
|
test("several let in if condition, evaluation order", [[
|
|
local t = { k = "ok" }
|
|
if a = t[b,c = "k", "l"] then
|
|
assert(c == "l")
|
|
assert(b == "k")
|
|
return a
|
|
end
|
|
]], "ok")
|
|
|
|
-- Method stub
|
|
test("method stub, basic", [[
|
|
local t = { s = "ok", m = function(self) return self.s end }
|
|
local f = t:m
|
|
return f()
|
|
]], "ok")
|
|
test("method stub, store method", [[
|
|
local t = { s = "ok", m = function(self) return self.s end }
|
|
local f = t:m
|
|
t.m = function() return "not ok" end
|
|
return f()
|
|
]], "ok")
|
|
test("method stub, store object", [[
|
|
local t = { s = "ok", m = function(self) return self.s end }
|
|
local f = t:m
|
|
t = {}
|
|
return f()
|
|
]], "ok")
|
|
test("method stub, returns nil if method nil", [[
|
|
local t = { m = nil }
|
|
return t:m
|
|
]], nil)
|
|
|
|
-- Safe prefixes
|
|
test("safe method stub, when nil", [[
|
|
return t?:m
|
|
]], nil)
|
|
test("safe method stub, when non-nil", [[
|
|
local t = { s = "ok", m = function(self) return self.s end }
|
|
return t?:m()
|
|
]], "ok")
|
|
|
|
test("safe call, when nil", [[
|
|
return f?()
|
|
]], nil)
|
|
test("safe call, when non nil", [[
|
|
f = function() return "ok" end
|
|
return f?()
|
|
]], "ok")
|
|
|
|
test("safe index, when nil", [[
|
|
return f?.l
|
|
]], nil)
|
|
test("safe index, when non nil", [[
|
|
f = { l = "ok" }
|
|
return f?.l
|
|
]], "ok")
|
|
|
|
test("safe prefixes, random chaining", [[
|
|
f = { l = { m = function(s) return s or "ok" end } }
|
|
assert(f?.l?.m() == "ok")
|
|
assert(f?.l?.o == nil)
|
|
assert(f?.l?.o?() == nil)
|
|
assert(f?.lo?.o?() == nil)
|
|
assert(f?.l?:m?() == f.l)
|
|
assert(f?.l:mo == nil)
|
|
assert(f.l?:o?() == nil)
|
|
]])
|
|
|
|
-- Destructuring assigments
|
|
test("destructuring assignement with an expression", [[
|
|
local {x, y} = { x = 5, y = 1 }
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with local", [[
|
|
t = { x = 5, y = 1 }
|
|
local {x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement", [[
|
|
t = { x = 5, y = 1 }
|
|
{x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with +=", [[
|
|
t = { x = 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{x, y} += t
|
|
return x + y
|
|
]], 20)
|
|
test("destructuring assignement with =-", [[
|
|
t = { x = 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{x, y} =- t
|
|
return x + y
|
|
]], -8)
|
|
test("destructuring assignement with +=-", [[
|
|
t = { x = 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{x, y} +=- t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with =-", [[
|
|
t = { x = 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{x, y} =- t
|
|
return x + y
|
|
]], -8)
|
|
test("destructuring assignement with let", [[
|
|
t = { x = 5, y = 1 }
|
|
let {x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with for in", [[
|
|
t = {{ x = 5, y = 1 }}
|
|
for k, {x, y} in pairs(t) do
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
test("destructuring assignement with if with assignement", [[
|
|
t = { x = 5, y = 1 }
|
|
if {x, y} = t then
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
test("destructuring assignement with if-elseif with assignement", [[
|
|
t = { x = 5, y = 1 }
|
|
if ({u} = t) and u then
|
|
return 0
|
|
elseif {x, y} = t then
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
|
|
test("destructuring assignement with an expression with custom name", [[
|
|
local {o = x, y} = { o = 5, y = 1 }
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with local with custom name", [[
|
|
t = { o = 5, y = 1 }
|
|
local {o = x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with custom name", [[
|
|
t = { o = 5, y = 1 }
|
|
{o = x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with += with custom name", [[
|
|
t = { o = 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{o = x, y} += t
|
|
return x + y
|
|
]], 20)
|
|
test("destructuring assignement with =- with custom name", [[
|
|
t = { o = 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{o = x, y} =- t
|
|
return x + y
|
|
]], -8)
|
|
test("destructuring assignement with +=- with custom name", [[
|
|
t = { o = 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{o = x, y} +=- t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with let with custom name", [[
|
|
t = { o = 5, y = 1 }
|
|
let {o = x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with for in with custom name", [[
|
|
t = {{ o = 5, y = 1 }}
|
|
for k, {o = x, y} in pairs(t) do
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
test("destructuring assignement with if with assignement with custom name", [[
|
|
t = { o = 5, y = 1 }
|
|
if {o = x, y} = t then
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
test("destructuring assignement with if-elseif with assignement with custom name", [[
|
|
t = { o = 5, y = 1 }
|
|
if ({x} = t) and x then
|
|
return 0
|
|
elseif {o = x, y} = t then
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
|
|
test("destructuring assignement with an expression with expression as key", [[
|
|
local {[1] = x, y} = { 5, y = 1 }
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with local with expression as key", [[
|
|
t = { 5, y = 1 }
|
|
local {[1] = x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with expression as key", [[
|
|
t = { 5, y = 1 }
|
|
{[1] = x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with += with expression as key", [[
|
|
t = { 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{[1] = x, y} += t
|
|
return x + y
|
|
]], 20)
|
|
test("destructuring assignement with =- with expression as key", [[
|
|
t = { 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{[1] = x, y} =- t
|
|
return x + y
|
|
]], -8)
|
|
test("destructuring assignement with +=- with expression as key", [[
|
|
t = { 5, y = 1 }
|
|
local x, y = 5, 9
|
|
{[1] = x, y} +=- t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with let with expression as key", [[
|
|
t = { 5, y = 1 }
|
|
let {[1] = x, y} = t
|
|
return x + y
|
|
]], 6)
|
|
test("destructuring assignement with for in with expression as key", [[
|
|
t = {{ 5, y = 1 }}
|
|
for k, {[1] = x, y} in pairs(t) do
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
test("destructuring assignement with if with assignement with expression as key", [[
|
|
t = { 5, y = 1 }
|
|
if {[1] = x, y} = t then
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
test("destructuring assignement with if-elseif with assignement with expression as key", [[
|
|
t = { 5, y = 1 }
|
|
if ({x} = t) and x then
|
|
return 0
|
|
elseif {[1] = x, y} = t then
|
|
return x + y
|
|
end
|
|
]], 6)
|
|
|
|
-- results
|
|
local resultCounter = {}
|
|
local testCounter = 0
|
|
for name, t in pairs(results) do
|
|
-- print errors & fails
|
|
if t.result ~= "success" then
|
|
print(name.." test: "..t.result)
|
|
if t.message then print(t.message) end
|
|
print("----------")
|
|
end
|
|
-- count tests results
|
|
resultCounter[t.result] = (resultCounter[t.result] or 0) + 1
|
|
testCounter = testCounter + 1
|
|
end
|
|
|
|
-- print final results
|
|
for _, name in ipairs{{"error", "red"}, {"fail", "purple"}, {"success", "green"}} do
|
|
local count = resultCounter[name[1]] or 0
|
|
print(c(count, "bold", name[2])..c(" "..name[1].." (" .. math.floor((count / testCounter * 100)*100)/100 .. "%)", name[2]))
|
|
end
|
|
print(c(testCounter.." total", "bold"))
|