nim-leopard/tests/testleopard.nim
Chrysostomos Nanakos f1878d6aaa
chore: orc support
Refactor public API to use `var openArray[seq[byte]]` instead of raw
pointers for ORC compatibility.

The previous API required callers to extract raw pointers from seqs
before calling encode/decode. With refc and shallowCopy, reference
counting kept buffers alive. ORC doesn't track raw pointers and can
reallocate seq buffers at yield points, leaving pointers dangling.

The new openArray API borrows data safely.

Also updates CI to Nim 2.2.4 and bumps version to 0.2.0.

Part of https://github.com/logos-storage/nim-leopard/issues/24

Signed-off-by: Chrysostomos Nanakos <chris@include.gr>
2025-12-24 01:43:25 +02:00

337 lines
9.3 KiB
Nim

import std/random
import std/sets
import pkg/unittest2
import pkg/results
import ../leopard
import ./helpers
randomize()
suite "Leopard Parametrization":
test "Should not allow invalid buffer multiples":
check:
LeoEncoder.init(63, 4, 2).error == "bufSize should be multiples of 64 bytes!"
LeoEncoder.init(65, 4, 2).error == "bufSize should be multiples of 64 bytes!"
test "Should not allow invalid data/parity buffer counts":
check:
LeoEncoder.init(64, 1, 2).error ==
"number of parity buffers cannot exceed number of data buffers!"
test "Should not allow data + parity to exceed 65536":
check:
LeoEncoder.init(64, 65536 + 1, 0).error ==
"number of parity and data buffers cannot exceed 65536!"
LeoEncoder.init(64, 32768 + 1, 32768).error ==
"number of parity and data buffers cannot exceed 65536!"
test "Should not allow encoding with invalid data buffer counts":
var
leo = LeoEncoder.init(64, 4, 2).tryGet()
data = newSeq[seq[byte]](3)
parity = newSeq[seq[byte]](2)
check:
leo.encode(data, parity).error == "Number of data buffers should match!"
test "Should not allow encoding with invalid parity buffer counts":
var
leo = LeoEncoder.init(64, 4, 2).tryGet()
data = newSeq[seq[byte]](4)
parity = newSeq[seq[byte]](3)
check:
leo.encode(data, parity).error == "Number of parity buffers should match!"
test "Should not allow decoding with invalid data buffer counts":
var
leo = LeoDecoder.init(64, 4, 2).tryGet()
data = newSeq[seq[byte]](3)
parity = newSeq[seq[byte]](2)
recovered = newSeq[seq[byte]](3)
check:
leo.decode(data, parity, recovered).error == "Number of data buffers should match!"
test "Should not allow decoding with invalid data buffer counts":
var
leo = LeoDecoder.init(64, 4, 2).tryGet()
data = newSeq[seq[byte]](4)
parity = newSeq[seq[byte]](1)
recovered = newSeq[seq[byte]](3)
check:
leo.decode(data, parity, recovered).error ==
"Number of parity buffers should match!"
test "Should not allow decoding with invalid data buffer counts":
var
leo = LeoDecoder.init(64, 4, 2).tryGet()
data = newSeq[seq[byte]](4)
parity = newSeq[seq[byte]](2)
recovered = newSeq[seq[byte]](3)
check:
leo.decode(data, parity, recovered).error ==
"Number of recovered buffers should match buffers!"
suite "Leopard simple Encode/Decode":
const
TestString = "Hello World!"
DataCount = 4
ParityCount = 2
BufferSize = 64
var
encoder: LeoEncoder
decoder: LeoDecoder
data: seq[seq[byte]]
parity: seq[seq[byte]]
recovered: seq[seq[byte]]
setup:
encoder = LeoEncoder.init(BufferSize, DataCount, ParityCount).tryGet()
decoder = LeoDecoder.init(BufferSize, DataCount, ParityCount).tryGet()
data = newSeq[seq[byte]](DataCount)
parity = newSeq[seq[byte]](ParityCount)
recovered = newSeq[seq[byte]](DataCount)
teardown:
encoder.free()
decoder.free()
test "Test 2 data loses out of 4 possible":
for i in 0 ..< DataCount:
data[i] = newSeq[byte](BufferSize)
recovered[i] = newSeq[byte](BufferSize)
var str = TestString & " " & $i
copyMem(addr data[i][0], addr str[0], str.len)
for i in 0 ..< ParityCount:
parity[i] = newSeq[byte](BufferSize)
encoder.encode(data, parity).tryGet()
var
data1 = data[0]
data2 = data[1]
data[0].setLen(0)
data[1].setLen(0)
decoder.decode(data, parity, recovered).tryGet()
check recovered[0] == data1
check recovered[1] == data2
test "Test 1 data and 1 parity loss out of 4 possible":
for i in 0 ..< DataCount:
data[i] = newSeq[byte](BufferSize)
recovered[i] = newSeq[byte](BufferSize)
var str = TestString & " " & $i
copyMem(addr data[i][0], addr str[0], str.len)
for i in 0 ..< ParityCount:
parity[i] = newSeq[byte](BufferSize)
encoder.encode(data, parity).tryGet()
var data1 = data[0]
data[0].setLen(0)
parity[0].setLen(0)
decoder.decode(data, parity, recovered).tryGet()
check recovered[0] == data1
suite "Leopard Encode/Decode":
test "bufSize = 4096, K = 800, M = 200 - drop data = 200 data":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 800
parity = 200
bufSize = 4096
dataLoses = 200
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, dataLoses, 0, encoder, decoder).tryGet()
finally:
encoder.free()
decoder.free()
test "bufSize = 4096, K = 800, M = 200 - drop parity = 200":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 800
parity = 200
bufSize = 4096
parityLoses = 200
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, parityLoses, 0, encoder, decoder).tryGet()
finally:
encoder.free()
decoder.free()
test "bufSize = 4096, K = 800, M = 200 - drop data = 100, drop parity = 100":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 800
parity = 200
bufSize = 4096
dataLoses = 100
parityLoses = 100
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
.tryGet()
finally:
encoder.free()
decoder.free()
test "bufSize = 4096, K = 8000, M = 2000 - drop data = 2000":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 8000
parity = 2000
bufSize = 4096
dataLoses = 2000
parityLoses = 0
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
.tryGet()
finally:
encoder.free()
decoder.free()
test "bufSize = 4096, K = 8000, M = 2000 - drop parity = 2000":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 8000
parity = 2000
bufSize = 4096
dataLoses = 0
parityLoses = 2000
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
.tryGet()
finally:
encoder.free()
decoder.free()
test "bufSize = 4096, K = 8000, M = 2000 - drop data = 1000, parity = 1000":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 8000
parity = 2000
bufSize = 4096
dataLoses = 1000
parityLoses = 1000
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
.tryGet()
finally:
encoder.free()
decoder.free()
test "bufSize = 4096, K = 8000, M = 8000 - drop data = 8000":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 8000
parity = 8000
bufSize = 4096
dataLoses = 8000
parityLoses = 0
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
.tryGet()
finally:
encoder.free()
decoder.free()
test "bufSize = 4096, K = 8000, M = 8000 - drop parity = 8000":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 8000
parity = 8000
bufSize = 4096
dataLoses = 0
parityLoses = 8000
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
.tryGet()
finally:
encoder.free()
decoder.free()
test "bufSize = 4096, K = 8000, M = 8000 - drop data = 4000, parity = 4000":
var
encoder: LeoEncoder
decoder: LeoDecoder
buffers = 8000
parity = 8000
bufSize = 4096
dataLoses = 4000
parityLoses = 4000
try:
encoder = LeoEncoder.init(bufSize, buffers, parity).tryGet()
decoder = LeoDecoder.init(bufSize, buffers, parity).tryGet()
testPackets(buffers, parity, bufSize, dataLoses, parityLoses, encoder, decoder)
.tryGet()
finally:
encoder.free()
decoder.free()
suite "Leopard use same encoder/decoder multiple times":
var
encoder: LeoEncoder
decoder: LeoDecoder
try:
encoder = LeoEncoder.init(4096, 800, 800).tryGet()
decoder = LeoDecoder.init(4096, 800, 800).tryGet()
for i in 0 .. 10:
let lost = 40 * i
test "Encode/Decode using same encoder/decoder - lost data = " & $lost &
" lost parity = " & $lost:
testPackets(800, 800, 4096, 40 * i, 40 * i, encoder, decoder).tryGet()
finally:
encoder.free()
decoder.free()