aboutsummaryrefslogtreecommitdiff
path: root/builtin/common/tests
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/common/tests')
-rw-r--r--builtin/common/tests/misc_helpers_spec.lua99
-rw-r--r--builtin/common/tests/serialize_spec.lua158
-rw-r--r--builtin/common/tests/vector_spec.lua10
3 files changed, 247 insertions, 20 deletions
diff --git a/builtin/common/tests/misc_helpers_spec.lua b/builtin/common/tests/misc_helpers_spec.lua
index b16987f0b..7d046d5b7 100644
--- a/builtin/common/tests/misc_helpers_spec.lua
+++ b/builtin/common/tests/misc_helpers_spec.lua
@@ -1,4 +1,5 @@
_G.core = {}
+_G.vector = {metatable = {}}
dofile("builtin/common/vector.lua")
dofile("builtin/common/misc_helpers.lua")
@@ -66,9 +67,107 @@ describe("pos", function()
end)
end)
+describe("area parsing", function()
+ describe("valid inputs", function()
+ it("accepts absolute numbers", function()
+ local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2 4 -12.53)")
+ assert(p1.x == 10 and p1.y == 5 and p1.z == -2)
+ assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
+ end)
+
+ it("accepts relative numbers", function()
+ local p1, p2 = core.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})
+ assert(type(p1) == "table" and type(p2) == "table")
+ assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
+ assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
+
+ p1, p2 = core.string_to_area("(1 2 3) (~5 ~-5 ~)", {x=10,y=10,z=10})
+ assert(type(p1) == "table" and type(p2) == "table")
+ assert(p1.x == 1 and p1.y == 2 and p1.z == 3)
+ assert(p2.x == 15 and p2.y == 5 and p2.z == 10)
+ end)
+ end)
+ describe("invalid inputs", function()
+ it("rejects too few numbers", function()
+ local p1, p2 = core.string_to_area("(1,1) (1,1,1,1)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+ end)
+
+ it("rejects too many numbers", function()
+ local p1, p2 = core.string_to_area("(1,1,1,1) (1,1,1,1)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+ end)
+
+ it("rejects nan & inf", function()
+ local p1, p2 = core.string_to_area("(1,1,1) (1,1,nan)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(1,1,1) (1,1,~nan)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(1,1,1) (1,~nan,1)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(1,1,1) (1,1,inf)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(1,1,1) (1,1,~inf)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(1,1,1) (1,~inf,1)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(nan,nan,nan) (nan,nan,nan)")
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(inf,inf,inf) (-inf,-inf,-inf)")
+ assert(p1 == nil and p2 == nil)
+ end)
+
+ it("rejects words", function()
+ local p1, p2 = core.string_to_area("bananas", {x=1,y=1,z=1})
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("bananas", "foobar")
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("bananas")
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(bananas,bananas,bananas)")
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(bananas,bananas,bananas) (bananas,bananas,bananas)")
+ assert(p1 == nil and p2 == nil)
+ end)
+
+ it("requires parenthesis & valid numbers", function()
+ local p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
+ assert(p1 == nil and p2 == nil)
+
+ p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
+ assert(p1 == nil and p2 == nil)
+ end)
+ end)
+end)
+
describe("table", function()
it("indexof()", function()
assert.equal(1, table.indexof({"foo", "bar"}, "foo"))
assert.equal(-1, table.indexof({"foo", "bar"}, "baz"))
end)
end)
+
+describe("formspec_escape", function()
+ it("escapes", function()
+ assert.equal(nil, core.formspec_escape(nil))
+ assert.equal("", core.formspec_escape(""))
+ assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\["))
+ end)
+end)
diff --git a/builtin/common/tests/serialize_spec.lua b/builtin/common/tests/serialize_spec.lua
index e46b7dcc5..340e226ee 100644
--- a/builtin/common/tests/serialize_spec.lua
+++ b/builtin/common/tests/serialize_spec.lua
@@ -1,42 +1,97 @@
_G.core = {}
+_G.vector = {metatable = {}}
_G.setfenv = require 'busted.compatibility'.setfenv
dofile("builtin/common/serialize.lua")
dofile("builtin/common/vector.lua")
+-- Supports circular tables; does not support table keys
+-- Correctly checks whether a mapping of references ("same") exists
+-- Is significantly more efficient than assert.same
+local function assert_same(a, b, same)
+ same = same or {}
+ if same[a] or same[b] then
+ assert(same[a] == b and same[b] == a)
+ return
+ end
+ if a == b then
+ return
+ end
+ if type(a) ~= "table" or type(b) ~= "table" then
+ assert(a == b)
+ return
+ end
+ same[a] = b
+ same[b] = a
+ local count = 0
+ for k, v in pairs(a) do
+ count = count + 1
+ assert(type(k) ~= "table")
+ assert_same(v, b[k], same)
+ end
+ for _ in pairs(b) do
+ count = count - 1
+ end
+ assert(count == 0)
+end
+
+local x, y = {}, {}
+local t1, t2 = {x, x, y, y}, {x, y, x, y}
+assert.same(t1, t2) -- will succeed because it only checks whether the depths match
+assert(not pcall(assert_same, t1, t2)) -- will correctly fail because it checks whether the refs match
+
describe("serialize", function()
+ local function assert_preserves(value)
+ local preserved_value = core.deserialize(core.serialize(value))
+ assert_same(value, preserved_value)
+ end
it("works", function()
- local test_in = {cat={sound="nyan", speed=400}, dog={sound="woof"}}
- local test_out = core.deserialize(core.serialize(test_in))
-
- assert.same(test_in, test_out)
+ assert_preserves({cat={sound="nyan", speed=400}, dog={sound="woof"}})
end)
it("handles characters", function()
- local test_in = {escape_chars="\n\r\t\v\\\"\'", non_european="θשׁ٩∂"}
- local test_out = core.deserialize(core.serialize(test_in))
- assert.same(test_in, test_out)
+ assert_preserves({escape_chars="\n\r\t\v\\\"\'", non_european="θשׁ٩∂"})
+ end)
+
+ it("handles NaN & infinities", function()
+ local nan = core.deserialize(core.serialize(0/0))
+ assert(nan ~= nan)
+ assert_preserves(math.huge)
+ assert_preserves(-math.huge)
end)
it("handles precise numbers", function()
- local test_in = 0.2695949158945771
- local test_out = core.deserialize(core.serialize(test_in))
- assert.same(test_in, test_out)
+ assert_preserves(0.2695949158945771)
end)
it("handles big integers", function()
- local test_in = 269594915894577
- local test_out = core.deserialize(core.serialize(test_in))
- assert.same(test_in, test_out)
+ assert_preserves(269594915894577)
end)
it("handles recursive structures", function()
local test_in = { hello = "world" }
test_in.foo = test_in
+ assert_preserves(test_in)
+ end)
+
+ it("handles cross-referencing structures", function()
+ local test_in = {
+ foo = {
+ baz = {
+ {}
+ },
+ },
+ bar = {
+ baz = {},
+ },
+ }
- local test_out = core.deserialize(core.serialize(test_in))
- assert.same(test_in, test_out)
+ test_in.foo.baz[1].foo = test_in.foo
+ test_in.foo.baz[1].bar = test_in.bar
+ test_in.bar.baz[1] = test_in.foo.baz[1]
+
+ assert_preserves(test_in)
end)
it("strips functions in safe mode", function()
@@ -46,6 +101,7 @@ describe("serialize", function()
end,
foo = "bar"
}
+ setfenv(test_in.func, _G)
local str = core.serialize(test_in)
assert.not_nil(str:find("loadstring"))
@@ -57,13 +113,77 @@ describe("serialize", function()
it("vectors work", function()
local v = vector.new(1, 2, 3)
- assert.same({{x = 1, y = 2, z = 3}}, core.deserialize(core.serialize({v})))
- assert.same({x = 1, y = 2, z = 3}, core.deserialize(core.serialize(v)))
+ assert_preserves({v})
+ assert_preserves(v)
-- abuse
v = vector.new(1, 2, 3)
v.a = "bla"
- assert.same({x = 1, y = 2, z = 3, a = "bla"},
- core.deserialize(core.serialize(v)))
+ assert_preserves(v)
+ end)
+
+ it("handles keywords as keys", function()
+ assert_preserves({["and"] = "keyword", ["for"] = "keyword"})
+ end)
+
+ describe("fuzzing", function()
+ local atomics = {true, false, math.huge, -math.huge} -- no NaN or nil
+ local function atomic()
+ return atomics[math.random(1, #atomics)]
+ end
+ local function num()
+ local sign = math.random() < 0.5 and -1 or 1
+ -- HACK math.random(a, b) requires a, b & b - a to fit within a 32-bit int
+ -- Use two random calls to generate a random number from 0 - 2^50 as lower & upper 25 bits
+ local val = math.random(0, 2^25) * 2^25 + math.random(0, 2^25 - 1)
+ local exp = math.random() < 0.5 and 1 or 2^(math.random(-120, 120))
+ return sign * val * exp
+ end
+ local function charcodes(count)
+ if count == 0 then return end
+ return math.random(0, 0xFF), charcodes(count - 1)
+ end
+ local function str()
+ return string.char(charcodes(math.random(0, 100)))
+ end
+ local primitives = {atomic, num, str}
+ local function primitive()
+ return primitives[math.random(1, #primitives)]()
+ end
+ local function tab(max_actions)
+ local root = {}
+ local tables = {root}
+ local function random_table()
+ return tables[math.random(1, #tables)]
+ end
+ for _ = 1, math.random(1, max_actions) do
+ local tab = random_table()
+ local value
+ if math.random() < 0.5 then
+ if math.random() < 0.5 then
+ value = random_table()
+ else
+ value = {}
+ table.insert(tables, value)
+ end
+ else
+ value = primitive()
+ end
+ tab[math.random() < 0.5 and (#tab + 1) or primitive()] = value
+ end
+ return root
+ end
+ it("primitives work", function()
+ for _ = 1, 1e3 do
+ assert_preserves(primitive())
+ end
+ end)
+ it("tables work", function()
+ for _ = 1, 100 do
+ local fuzzed_table = tab(1e3)
+ assert_same(fuzzed_table, table.copy(fuzzed_table))
+ assert_preserves(fuzzed_table)
+ end
+ end)
end)
end)
diff --git a/builtin/common/tests/vector_spec.lua b/builtin/common/tests/vector_spec.lua
index 2f72f3383..6a0b81a89 100644
--- a/builtin/common/tests/vector_spec.lua
+++ b/builtin/common/tests/vector_spec.lua
@@ -1,4 +1,4 @@
-_G.vector = {}
+_G.vector = {metatable = {}}
dofile("builtin/common/vector.lua")
describe("vector", function()
@@ -128,6 +128,14 @@ describe("vector", function()
assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f))
end)
+ it("combine()", function()
+ local a = vector.new(1, 2, 3)
+ local b = vector.new(3, 2, 1)
+ assert.equal(vector.add(a, b), vector.combine(a, b, function(x, y) return x + y end))
+ assert.equal(vector.new(3, 2, 3), vector.combine(a, b, math.max))
+ assert.equal(vector.new(1, 2, 1), vector.combine(a, b, math.min))
+ end)
+
it("equals()", function()
local function assertE(a, b)
assert.is_true(vector.equals(a, b))