bencode_spec.lua
Branches > main > latest > files / nvim / pack / plugins / opt / conjure / lua / conjure-spec / remote / transport / bencode_spec.lua
-- [nfnl] fnl/conjure-spec/remote/transport/bencode_spec.fnl
local _local_1_ = require("plenary.busted")
local describe = _local_1_.describe
local it = _local_1_.it
local assert = require("luassert.assert")
local ffi = require("ffi")
local bencode = require("conjure.remote.transport.bencode")
local function buffer_content(bs)
local ptr, blen = bs.buf:ref()
return ffi.string(ptr, blen)
end
local function assert_buffer(bs, expected)
return assert.are.equals(expected, buffer_content(bs))
end
local function assert_stack_depth(bs, expected)
return assert.are.equals(expected, #bs.stack)
end
local function assert_stack_empty(bs)
return assert_stack_depth(bs, 0)
end
local function _2_()
local function _3_()
local bs = bencode.new()
local data = {foo = {"bar"}}
local function _4_()
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("buffer starts empty", _4_)
local function _5_()
return assert.same({data}, bencode["decode-all"](bs, bencode.encode(data)))
end
it("a single bencoded value", _5_)
local function _6_()
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
return it("buffer is empty after a decode", _6_)
end
describe("basic functionality", _3_)
local function _7_()
local function _8_()
return assert.are.equals("d11:client-name7:conjure2:id3:abc2:op5:clonee", bencode.encode({op = "clone", id = "abc", ["client-name"] = "conjure"}))
end
it("encodes dict keys in sorted order", _8_)
local function _9_()
return assert.are.equals("d1:ai1e1:bi2e1:cd1:xi0e1:yi0e1:zi0ee1:di4e1:ei5e1:fi6e1:gi7e1:hi8ee", bencode.encode({h = 8, g = 7, f = 6, e = 5, d = 4, c = {z = 0, y = 0, x = 0}, b = 2, a = 1}))
end
return it("sorts nested dicts and many keys", _9_)
end
describe("dict key ordering", _7_)
local function _10_()
local bs = bencode.new()
local data_a = {foo = {"bar"}}
local data_b = {1, 2, 3}
local function _11_()
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("buffer starts empty", _11_)
local function _12_()
return assert.same({data_a, data_b}, bencode["decode-all"](bs, (bencode.encode(data_a) .. bencode.encode(data_b))))
end
it("two bencoded values", _12_)
local function _13_()
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
return it("buffer is empty after a decode", _13_)
end
describe("multiple-values", _10_)
local function _14_()
local bs = bencode.new()
local data_a = {foo = {"bar"}}
local data_b = {1, 2, 3}
local encoded_b = bencode.encode(data_b)
local function _15_()
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("buffer starts empty", _15_)
local function _16_()
return assert.same({data_a}, bencode["decode-all"](bs, (bencode.encode(data_a) .. string.sub(encoded_b, 1, 3))))
end
it("first value with partial list start", _16_)
local function _17_()
assert_buffer(bs, "i1")
assert_stack_depth(bs, 1)
return assert.are.equals("list", bs.stack[1].t)
end
it("after first, buffer contains partial integer and list is on stack", _17_)
local function _18_()
return assert.same({data_b}, bencode["decode-all"](bs, string.sub(encoded_b, 4)))
end
it("second value completes the list", _18_)
local function _19_()
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
return it("buffer is empty after completion", _19_)
end
describe("partial list parsing", _14_)
local function _20_()
local function _21_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "i"))
assert_buffer(bs, "i")
return assert_stack_empty(bs)
end
it("incomplete integer at start", _21_)
local function _22_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "i123"))
assert_buffer(bs, "i123")
return assert_stack_empty(bs)
end
it("incomplete integer with partial number", _22_)
local function _23_()
local bs = bencode.new()
bencode["decode-all"](bs, "i123")
assert.same({123}, bencode["decode-all"](bs, "e"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("complete integer after partial", _23_)
local function _24_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "i-"))
assert_buffer(bs, "i-")
assert.same({-42}, bencode["decode-all"](bs, "42e"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
return it("negative integer split", _24_)
end
describe("integer edge cases", _20_)
local function _25_()
local function _26_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "3"))
assert_buffer(bs, "3")
return assert_stack_empty(bs)
end
it("incomplete string length", _26_)
local function _27_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "3:"))
assert_buffer(bs, "3:")
return assert_stack_empty(bs)
end
it("incomplete string with colon but no content", _27_)
local function _28_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "3:fo"))
assert_buffer(bs, "3:fo")
return assert_stack_empty(bs)
end
it("incomplete string with partial content", _28_)
local function _29_()
local bs = bencode.new()
bencode["decode-all"](bs, "3:fo")
assert.same({"foo"}, bencode["decode-all"](bs, "o"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("complete string after partial", _29_)
local function _30_()
local bs = bencode.new()
assert.same({""}, bencode["decode-all"](bs, "0:"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("zero-length string", _30_)
local function _31_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "1"))
assert.same({}, bencode["decode-all"](bs, "0"))
assert.same({}, bencode["decode-all"](bs, ":"))
assert.same({}, bencode["decode-all"](bs, "hello"))
assert.same({"helloworld"}, bencode["decode-all"](bs, "world"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
return it("multi-digit length split across chunks", _31_)
end
describe("string edge cases", _25_)
local function _32_()
local function _33_()
local bs = bencode.new()
assert.same({{}}, bencode["decode-all"](bs, "le"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("empty list", _33_)
local function _34_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "l"))
assert_buffer(bs, "")
assert_stack_depth(bs, 1)
return assert.are.equals("list", bs.stack[1].t)
end
it("list start only", _34_)
local function _35_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "ll"))
assert_buffer(bs, "")
assert_stack_depth(bs, 2)
assert.are.equals("list", bs.stack[1].t)
return assert.are.equals("list", bs.stack[2].t)
end
it("nested list start", _35_)
local function _36_()
local bs = bencode.new()
bencode["decode-all"](bs, "ll")
assert.same({{{}}}, bencode["decode-all"](bs, "ee"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("complete nested empty lists", _36_)
local function _37_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "li42"))
assert_buffer(bs, "i42")
assert_stack_depth(bs, 1)
bencode["decode-all"](bs, "e3:fo")
assert_buffer(bs, "3:fo")
assert.same({{42, "foo"}}, bencode["decode-all"](bs, "oe"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
return it("list with mixed incomplete elements", _37_)
end
describe("list edge cases", _32_)
local function _38_()
local function _39_()
local bs = bencode.new()
assert.same({{}}, bencode["decode-all"](bs, "de"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("empty dict", _39_)
local function _40_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "d"))
assert_buffer(bs, "")
assert_stack_depth(bs, 1)
return assert.are.equals("dict", bs.stack[1].t)
end
it("dict start only", _40_)
local function _41_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "d3:fo"))
assert_buffer(bs, "3:fo")
return assert_stack_depth(bs, 1)
end
it("dict with incomplete key", _41_)
local function _42_()
local bs = bencode.new()
bencode["decode-all"](bs, "d3:fo")
assert.same({}, bencode["decode-all"](bs, "o"))
assert_buffer(bs, "")
assert_stack_depth(bs, 1)
return assert.are.equals("foo", bs.stack[1].k)
end
it("dict with complete key but no value", _42_)
local function _43_()
local bs = bencode.new()
bencode["decode-all"](bs, "d3:foo")
assert.same({}, bencode["decode-all"](bs, "i4"))
assert_buffer(bs, "i4")
assert_stack_depth(bs, 1)
return assert.are.equals("foo", bs.stack[1].k)
end
it("dict with incomplete value", _43_)
local function _44_()
local bs = bencode.new()
bencode["decode-all"](bs, "d3:fooi4")
assert.same({{foo = 42}}, bencode["decode-all"](bs, "2ee"))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("complete dict after partial parsing", _44_)
local function _45_()
local bs = bencode.new()
assert.same({}, bencode["decode-all"](bs, "ld"))
assert_stack_depth(bs, 2)
assert.are.equals("list", bs.stack[1].t)
return assert.are.equals("dict", bs.stack[2].t)
end
it("nested dict in list", _45_)
local function _46_()
local bs = bencode.new()
bencode["decode-all"](bs, "d3:fool")
assert_stack_depth(bs, 2)
assert.are.equals("dict", bs.stack[1].t)
assert.are.equals("list", bs.stack[2].t)
return assert.are.equals("foo", bs.stack[1].k)
end
return it("list in dict value", _46_)
end
describe("dict edge cases", _38_)
local function _47_()
local function _48_()
local bs = bencode.new()
local encoded = bencode.encode({key = "value"})
local final_result = {}
for i = 1, #encoded do
local result = bencode["decode-all"](bs, string.sub(encoded, i, i))
for _, v in ipairs(result) do
table.insert(final_result, v)
end
end
assert.same({{key = "value"}}, final_result)
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("single character chunks", _48_)
local function _49_()
local bs = bencode.new()
assert.same({123}, bencode["decode-all"](bs, "i123e3:"))
assert.same({"foo"}, bencode["decode-all"](bs, "foo"))
assert.same({}, bencode["decode-all"](bs, ""))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("exact boundary splits", _49_)
local function _50_()
local bs = bencode.new()
local data = {numbers = {1, 2, 3}, nested = {inner = "value"}}
local encoded = bencode.encode(data)
local mid = math.floor((#encoded / 2))
assert.same({}, bencode["decode-all"](bs, string.sub(encoded, 1, mid)))
assert.same({data}, bencode["decode-all"](bs, string.sub(encoded, (mid + 1))))
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
it("large nested structure split", _50_)
local function _51_()
local bs = bencode.new()
local item = {id = "item", data = "some-long-data-string-that-takes-space", nums = {1, 2, 3, 4, 5}}
local list = {}
for _ = 1, 50 do
table.insert(list, item)
end
local encoded = bencode.encode(list)
local chunk_size = 32
local final_result = {}
for start = 1, #encoded, chunk_size do
local _end = math.min((start + chunk_size + -1), #encoded)
local chunk = string.sub(encoded, start, _end)
local result = bencode["decode-all"](bs, chunk)
for _, v in ipairs(result) do
table.insert(final_result, v)
end
end
assert.same({list}, final_result)
assert_buffer(bs, "")
return assert_stack_empty(bs)
end
return it("very large data structure in 32-char chunks", _51_)
end
return describe("boundary conditions", _47_)
end
return describe("conjure.remote.transport.bencode", _2_)