Files
lua/tests/01-sanity.lua
zeekay 91103511be Finish capnp->ZAP rebrand; vendor lunitx; decouple cjson; bump rockspec
Rename the Cap'n Proto Lua port to ZAP, brand-neutral throughout:
- capnp.lua -> zap.lua, capnp/ -> zap/, *.capnp -> *.zap, compiler bins
  capnpc-* -> zapc-*, schema/handwritten modules -> *_zap.lua. Zero
  capnp/Cap'n Proto leftovers in zap-native source/build (upstream-fork
  Kenton Varda copyright headers in proto/c++.zap and bin/schema.zap stay).
  Generated-code header and doc links point at github.com/zap-proto/lua and
  zap-proto.io.

Tests now run with no extra install:
- Vendor the lunitx test framework (Michael Roth / Doug Currie, MIT) under
  vendor/ (lunit, lunit.console, lunitx, lunitx.atexit). Its __gc atexit hook
  drives each test file under LuaJIT and exits nonzero on any failure.
- tests/run_tests.sh and test.sh are self-contained (set LUA_PATH incl
  vendor/), generate proto/example_zap.lua via the `zap` compiler when present,
  and skip 01/10 with a clear note (never a false pass) when it is absent.
  02-util and 03-compile are pure-Lua and always run.
- Verified under LuaJIT 2.1: 01(20) + 02(7) + 03(1) + 10(31) = 59 tests pass,
  0 failed; runner returns nonzero on an injected failure.

Decouple optional cjson so the runtime/compiler load under a bare LuaJIT:
- zap/compile.lua loads cjson lazily and only for debug logging (guarded by
  the debug flag); bin/zapc-llua makes the schema JSON dump optional.

Rockspec: bump lua-zap to 0.1.3-4, declare lua-cjson >= 2.1.0, and install the
vendored lunitx modules. .gitignore: scope the rockspec-pack ignore to repo
root so rockspec/ specs are tracked; ignore the `dump` test artifact.

TODO trimmed to the genuinely remaining items (AnyPointer set/decode, packed
serialization, schema_zap regeneration, multi-file compile, perf).
2026-06-20 00:12:16 -07:00

399 lines
16 KiB
Lua

local ffi = require "ffi"
local lunit = require "lunitx"
local zap = require "zap"
local util = require "zap.util"
local handwritten = require "handwritten_zap"
local example = require "example_zap"
local mod = example
local format = string.format
local T1 = mod.T1
local T2 = mod.T1.T2
if _VERSION >= 'Lua 5.2' then
_ENV = lunit.module('simple','seeall')
else
module( "simple", package.seeall, lunit.testcase )
end
function assert_equalf(expected, actual)
if math.abs(expected - actual) < 0.000001 then
assert_true(true)
return
end
error(format("expected %s, got %s", expected, actual))
end
local to_hex_string = function(seg)
local t = {}
for i = 1, seg.pos do
table.insert(t, bit.tohex(seg.data[i - 1], 2))
end
return table.concat(t, " ")
end
local assert_hex = function (expected, actual)
assert_equal(expected, to_hex_string(actual))
end
local function assert_hex_string(expected, actual)
local t = {}
for i = 1, #actual do
table.insert(t, bit.tohex(string.byte(actual, i), 2))
end
assert_equal(expected, table.concat(t, " "))
end
-----------------------------------------------------------------
function test_write_plain_val()
local seg = { len = 32, pos = 0 }
seg.data = ffi.new("char[?]", 32) -- 32 bytes
zap.write_struct_field(seg.data, true, "bool", 1, 0)
zap.write_struct_field(seg.data, 8, "uint8", 8, 1)
zap.write_struct_field(seg.data, 65535, "uint16", 16, 1)
zap.write_struct_field(seg.data, 1048576, "uint32", 32, 1)
zap.write_struct_field(seg.data, 4294967296, "uint64", 64, 1)
zap.write_struct_field(seg.data, 3.14, "float32", 32, 4)
zap.write_struct_field(seg.data, 1.41421, "float32", 32, 5)
zap.write_struct_field(seg.data, 3.14159265358979, "float64", 64, 3)
seg.pos = seg.pos + 32
assert_hex("01 08 ff ff 00 00 10 00 00 00 00 00 01 00 00 00 c3 f5 48 40 d5 04 b5 3f 11 2d 44 54 fb 21 09 40", seg)
end
function test_fix_float_default()
assert_equal(0, zap.fix_float32_default(3.14, 3.14))
assert_equal(0, zap.fix_float64_default(3.1415926, 3.1415926))
assert_equalf(3.141592, zap.fix_float32_default(3.141592, 0))
end
function test_write_plain_val_with_default()
local seg = { len = 32, pos = 0 }
seg.data = ffi.new("char[?]", 32) -- 32 bytes
zap.write_struct_field(seg.data, true, "bool", 1, 0, 1)
zap.write_struct_field(seg.data, 8, "uint8", 8, 1, 8)
zap.write_struct_field(seg.data, 65535, "uint16", 16, 1, 65535)
zap.write_struct_field(seg.data, 1048576, "uint32", 32, 1, 1048576)
zap.write_struct_field(seg.data, 4294967296, "uint64", 64, 1, 4294967296)
zap.write_struct_field(seg.data, 3.14, "float32", 32, 4, 3.14)
zap.write_struct_field(seg.data, 1.41421, "float32", 32, 5, 1.41421)
zap.write_struct_field(seg.data, 3.14159265358979, "float64", 64, 3, 3.14159265358979)
seg.pos = seg.pos + 32
assert_hex("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", seg)
end
function test_write_plain_val1()
local seg = { len = 32, pos = 0 }
seg.data = ffi.new("char[?]", 32) -- 32 bytes
zap.write_struct_field(seg.data, -1, "int8", 8, 0)
zap.write_struct_field(seg.data, 3.14159, "float32", 32, 1, 0)
seg.pos = seg.pos + 32
assert_hex("ff 00 00 00 d0 0f 49 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", seg)
end
function test_read_struct_field()
local seg = { len = 32, pos = 0 }
seg.data = ffi.new("char[?]", 32) -- 32 bytes
-- write_struct_field(buf, val, size, off)
zap.write_struct_field(seg.data, true, "bool", 1, 0)
zap.write_struct_field(seg.data, 8, "uint8", 8, 1)
zap.write_struct_field(seg.data, 65535, "uint16", 16, 1)
zap.write_struct_field(seg.data, 1048576, "uint32", 32, 1)
zap.write_struct_field(seg.data, 4294967296ULL, "uint64", 64, 1)
zap.write_struct_field(seg.data, 3.14, "float32", 32, 4)
zap.write_struct_field(seg.data, 1.41421, "float32", 32, 5)
zap.write_struct_field(seg.data, 3.14159265358979, "float64", 64, 3)
assert_equal(true, zap.read_struct_field(seg.data, "bool", 1, 0))
assert_equal(8, zap.read_struct_field(seg.data, "int8", 8, 1))
assert_equal(65535, zap.read_struct_field(seg.data, "uint16", 16, 1))
assert_equal(1048576, zap.read_struct_field(seg.data, "int32", 32, 1))
assert_equal(4294967296ULL, zap.read_struct_field(seg.data, "uint64", 64, 1))
assert_equalf(3.14, zap.read_struct_field(seg.data, "float32", 32, 4))
assert_equalf(1.41421, zap.read_struct_field(seg.data, "float32", 32, 5))
assert_equalf(3.14159265358979, zap.read_struct_field(seg.data, "float64", 64, 3))
end
function test_read_struct_field_with_default()
local seg = { len = 32, pos = 0 }
seg.data = ffi.new("char[?]", 32) -- 32 bytes
assert_equal(true, zap.read_struct_field(seg.data, "bool", 1, 0, 1))
assert_equal(8, zap.read_struct_field(seg.data, "int8", 8, 1, 8))
assert_equal(65535, zap.read_struct_field(seg.data, "uint16", 16, 1, 65535))
assert_equal(1048576, zap.read_struct_field(seg.data, "int32", 32, 1, 1048576))
assert_equal(3456789012, zap.read_struct_field(seg.data, "uint32", 32, 1, 3456789012))
assert_equal(-123456789012345LL, zap.read_struct_field(seg.data, "int64", 64, 1, -123456789012345LL))
assert_equal(345678901234567890ULL, zap.read_struct_field(seg.data, "uint64", 64, 1, 345678901234567890ULL))
assert_equalf(3.14, zap.read_struct_field(seg.data, "float32", 32, 4, 3.14))
assert_equalf(1.41421, zap.read_struct_field(seg.data, "float32", 32, 5, 1.41421))
assert_equal(3.14159265358, zap.read_struct_field(seg.data, "float64", 64, 3, 3.14159265358))
end
--[[
function test_write_structp()
local seg = zap.new_segment() -- 32 bytes
zap.write_structp(seg.data, T1, 0)
seg.pos = seg.pos + 8
assert_hex("00 00 00 00 02 00 03 00", seg)
end
function test_write_structp1()
local seg = zap.new_segment()
zap.write_structp(seg.data + 8, T1, 2)
seg.pos = seg.pos + 16
assert_hex("00 00 00 00 00 00 00 00 08 00 00 00 02 00 03 00", seg)
end
function test_write_listp()
local seg = zap.new_segment(8)
-- write_listp = function (buf, size, num, data_off)
zap.write_listp(seg.data, 2, 1, 0)
seg.pos = seg.pos + 8
assert_hex("01 00 00 00 0a 00 00 00", seg)
end
]]
function test_read_struct_buf()
local buf = util.new_buf({ 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00 })
local off, dw, pw = zap.read_struct_buf(buf)
assert_equal(4, off)
assert_equal(2, dw)
assert_equal(4, pw)
end
function test_far_pointer_to_struct()
local buf = util.new_buf({
01, 00, 00, 00, 01, 00, 00, 00, -- 2 segs seg1: 1 word
02, 00, 00, 00, 00, 00, 00, 00, -- seg2: 4 word padding
0x0a, 00, 00, 00, 01, 00, 00, 00, -- far pointer seg:1, offset:1
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -- start of seg 2
08, 00, 00, 00, 02, 00, 04, 00, -- struct pointer
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -- start of seg 2
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -- start of seg 2
})
local header = {
base = buf,
header_size = 2,
seg_sizes = { 1, 2, },
}
local off, dw, pw = zap.read_struct_buf(buf + 4, header)
assert_equal(4, off)
assert_equal(2, dw)
assert_equal(4, pw)
end
function test_far_pointer_to_list()
local buf = util.new_buf({
01, 00, 00, 00, 01, 00, 00, 00, -- 2 segs seg1: 1 word
03, 00, 00, 00, 00, 00, 00, 00, -- seg2: 3 word padding
02, 00, 00, 00, 01, 00, 00, 00, -- far pointer seg 1
05, 00, 00, 00, 0x27, 00, 00, 00, -- list pointer
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00, 00, 00, 00, 00, 00, 00, 00,
})
local header = {
base = buf,
header_size = 2,
seg_sizes = { 1, 2, },
}
local T = {
dataWordCount = 0
}
local off, size_type, num = zap.read_listp_struct(buf + 4, header, T, 0)
assert_equal(2, off)
assert_equal(7, size_type)
assert_equal(4, num)
end
function test_write_text()
local buf = ffi.new("char[?]", 8 * 4)
local p32 = ffi.cast("int32_t *", buf)
local n = zap.write_text(p32, "12345678", 1)
assert_equal(16, n)
assert_equal("05 00 00 00 4a 00 00 00 00 00 00 00 00 00 00 00 31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00", util.hex_buf_str(buf, 32))
end
function test_write_list_data_num()
local size = 8
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
local n = zap.write_list_data(p32, { -1, -2, -3, -4, 0, 1, 2, 3 }, 0, "int8")
assert_equal("ff fe fd fc 00 01 02 03", util.hex_buf_str(buf, size))
assert_equal(size, n)
end
function test_write_list_data_bool()
local size = 8
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
local n = zap.write_list_data(p32, { true, false, true }, 0, "bool")
assert_equal("05 00 00 00 00 00 00 00", util.hex_buf_str(buf, size))
assert_equal(size, n)
end
function test_write_list_data_data()
local size = 8 * 7
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
local n = zap.write_list_data(p32, { "\1\2\3", "\4\5\6\7", "\9\10\11\12\13\14\15\16\17" }, 0, "data") -- writes 3 list pointer first, then list data
assert_equal("09 00 00 00 1a 00 00 00 09 00 00 00 22 00 00 00 09 00 00 00 4a 00 00 00 01 02 03 00 00 00 00 00 04 05 06 07 00 00 00 00 09 0a 0b 0c 0d 0e 0f 10 11 00 00 00 00 00 00 00", util.hex_buf_str(buf, size))
assert_equal(size, n)
end
function test_write_list_data_text()
local size = 8 * 7
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
local n = zap.write_list_data(p32, { "ab", "def", "ijklmnop" }, 0, "text") -- writes 3 list pointer first, then list data
assert_equal(size, n)
assert_equal("09 00 00 00 1a 00 00 00 09 00 00 00 22 00 00 00 09 00 00 00 4a 00 00 00 61 62 00 00 00 00 00 00 64 65 66 00 00 00 00 00 69 6a 6b 6c 6d 6e 6f 70 00 00 00 00 00 00 00 00", util.hex_buf_str(buf, size))
end
function test_write_list_data_list()
local size = 8 *9
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
-- list of text
local n = zap.write_list_data(p32, { {"ab", "def"}, {"ijklmnop"} }, 0, "list", "text") -- writes 3 list pointer first, then list data
assert_equal(size, n)
assert_equal("05 00 00 00 16 00 00 00 11 00 00 00 0e 00 00 00 05 00 00 00 1a 00 00 00 05 00 00 00 22 00 00 00 61 62 00 00 00 00 00 00 64 65 66 00 00 00 00 00 01 00 00 00 4a 00 00 00 69 6a 6b 6c 6d 6e 6f 70 00 00 00 00 00 00 00 00", util.hex_buf_str(buf, size))
end
function test_write_list_data_struct()
local size = 8 * (1 + 3 * 2)
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
local n = zap.write_list_data(p32, { { f0 = 1.1, f1 = 1.1111 }, {f0 = 1.2, f1 = 1.2222 } }, 0, "struct", T2) -- writes 3 list pointer first, then list data
assert_equal(size, n)
assert_equal("08 00 00 00 02 00 01 00 cd cc 8c 3f 00 00 00 00 9e 5e 29 cb 10 c7 f1 3f 00 00 00 00 00 00 00 00 9a 99 99 3f 00 00 00 00 3c bd 52 96 21 8e f3 3f 00 00 00 00 00 00 00 00", util.hex_buf_str(buf, size))
end
function test_write_list()
local size = 8 * (1 + 1 + 3 * 2) -- listp + tag + struct_size * 2
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
local data = { { f0 = 1.1, f1 = 1.1111 }, {f0 = 1.2, f1 = 1.2222 } }
local n = zap.write_list(p32, data, 8, "list", "struct", T2) -- writes 3 list pointer first, then list data
assert_equal(size, n + 8)
assert_equal("01 00 00 00 37 00 00 00 08 00 00 00 02 00 01 00 cd cc 8c 3f 00 00 00 00 9e 5e 29 cb 10 c7 f1 3f 00 00 00 00 00 00 00 00 9a 99 99 3f 00 00 00 00 3c bd 52 96 21 8e f3 3f 00 00 00 00 00 00 00 00", util.hex_buf_str(buf, size))
end
function test_write_list()
local size = 8 * (1 + 1 + 3 * 2 + 1 + 1) -- listp + tag + struct_size * 2 + sd0 data + sd0 data
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
--ffi.fill(buf, size, 0xff)
local data = { { f0 = 1.1, f1 = 1.1111, sd0 = "\1\2\3\4", }, {f0 = 1.2, f1 = 1.2222, sd0 = "\5\6\7\8\9\10\11\12" } }
local n = zap.write_list(p32, data, 8, "list", "struct", T2)
assert_equal(size, n + 8) -- write_list return size doesn't count list pointer size
assert_equal("01 00 00 00 37 00 00 00 08 00 00 00 02 00 01 00 cd cc 8c 3f 00 00 00 00 9e 5e 29 cb 10 c7 f1 3f 0d 00 00 00 22 00 00 00 9a 99 99 3f 00 00 00 00 3c bd 52 96 21 8e f3 3f 05 00 00 00 42 00 00 00 01 02 03 04 00 00 00 00 05 06 07 08 09 0a 0b 0c", util.hex_buf_str(buf, size))
end
function test_write_list()
local size = 8 * (1 + 1 * 2 + 1 * 2 + 3 * 3) -- listp + listp * 2 + tag * 2 + struct_size * 3
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
--print("test_write_list", p32)
local data = {
{
{ f0 = 1.1, f1 = 1.1111 },
{ f0 = 1.2, f1 = 1.2222 },
},
{
{ f0 = 1.3, f1 = 1.3333 },
}
}
local n = zap.write_list(p32, data, 8, "list", "list", "struct", T2) -- writes 3 list pointer first, then list data
assert_equal(size, n + 8) -- write_list return size doesn't count list pointer size
assert_equal("01 00 00 00 16 00 00 00 05 00 00 00 37 00 00 00 1d 00 00 00 1f 00 00 00 08 00 00 00 02 00 01 00 cd cc 8c 3f 00 00 00 00 9e 5e 29 cb 10 c7 f1 3f 00 00 00 00 00 00 00 00 9a 99 99 3f 00 00 00 00 3c bd 52 96 21 8e f3 3f 00 00 00 00 00 00 00 00 04 00 00 00 02 00 01 00 66 66 a6 3f 00 00 00 00 da 1b 7c 61 32 55 f5 3f 00 00 00 00 00 00 00 00", util.hex_buf_str(buf, size))
end
function test_read_list_data()
local buf = ffi.new("char[?]", 8 * 9)
local p32 = ffi.cast("int32_t *", buf)
local data = { {"ab", "def"}, {"ijklmnop"} }
-- list of text
zap.write_list_data(p32, data, 0, "list", "text")
local copy = zap.read_list_data(p32, {}, 2, "list", "text")
assert_not_nil(copy)
assert_not_nil(copy[1])
assert_not_nil(copy[2])
assert_equal(2, #copy)
assert_equal(data[1][1], copy[1][1])
assert_equal(data[1][2], copy[1][2])
assert_equal(copy[2][1], copy[2][1])
end
function test_read_list_data_struct()
local size = (1 + 3 * 2) * 8
local buf = ffi.new("char[?]", size)
local p32 = ffi.cast("int32_t *", buf)
local data = { { f0 = 1.1, f1 = 1.1111 }, {f0 = 1.2, f1 = 1.2222 } }
zap.write_list_data(p32, data, 0, "struct", T2)
local copy = zap.read_list_data(p32, {}, 4, "struct", T2)
assert_not_nil(copy)
assert_equal(2, #copy)
assert_equalf(data[1].f0, copy[1].f0)
assert_equalf(data[1].f1, copy[1].f1)
assert_equalf(data[2].f0, copy[2].f0)
assert_equalf(data[2].f1, copy[2].f1)
end
-- 64 bit number in cdata
function test_write_read_plain_uint64()
local seg = { len = 32, pos = 0 }
seg.data = ffi.new("char[?]", 32) -- 32 bytes
local p32 = ffi.cast("uint32_t *", seg.data)
local uint64p = ffi.new("uint64_t[?]", 1)
local uint32p = ffi.cast("uint32_t *", uint64p)
uint32p[0] = 1
uint32p[1] = 2
zap.write_struct_field(p32, 1393891543746000128, "int64", 64, 0, 0)
zap.write_struct_field(p32, uint64p[0], "uint64", 64, 1, 0)
--zap.write_struct_field(seg.data, 3.14159, "float32", 32, 1, 0)
seg.pos = seg.pos + 32
assert_hex("00 b5 66 50 f9 18 58 13 01 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", seg)
assert_equal(1393891543746000128, zap.read_struct_field(p32, "int64", 64, 0))
local uint64 = zap.read_struct_field(p32, "uint64", 64, 1)
local uint64_str = tostring(uint64)
assert_equal("cdata", type(uint64))
assert_equal("8589934593ULL", uint64_str)
end