From 04fd3bc96471149fcf36e0ef1e4f547354f3fa8e Mon Sep 17 00:00:00 2001 From: treeform Date: Mon, 25 May 2026 11:47:15 -0700 Subject: [PATCH] Add issue regression tests --- src/flatty.nim | 23 ++++++++++++-- tests/test_issue_objects.nim | 49 ++++++++++++++++++++++++++++++ tests/test_issue_objects_types.nim | 40 ++++++++++++++++++++++++ tests/tests.nim | 2 +- 4 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 tests/test_issue_objects.nim create mode 100644 tests/test_issue_objects_types.nim diff --git a/src/flatty.nim b/src/flatty.nim index 4fbd302..beddd97 100644 --- a/src/flatty.nim +++ b/src/flatty.nim @@ -1,6 +1,6 @@ ## Convert any Nim objects, numbers, strings, refs to and from binary format. import - std/[tables, typetraits, sets], + std/[importutils, tables, typetraits, sets], flatty/objvar when defined(js): @@ -68,6 +68,8 @@ func copyable[T](_: typedesc[T]): bool = false elif skipsFlattyCopyMem(T): false + elif T is range: + false elif (T is pointer) or (T is ptr) or (T is cstring) or (T is proc): false elif T is distinct: @@ -156,6 +158,7 @@ proc toFlatty*(s: var string, x: float32) proc toFlatty*(s: var string, x: float64) proc toFlatty*(s: var string, x: int) proc toFlatty*(s: var string, x: uint) +proc toFlatty*[T: range](s: var string, x: T) proc toFlatty*[T: enum and not range](s: var string, x: T) proc toFlatty*(s: var string, x: string) @@ -182,6 +185,7 @@ proc fromFlatty*(s: string, i: var int, x: var int) proc fromFlatty*(s: string, i: var int, x: var uint) proc fromFlatty*(s: string, i: var int, x: var float32) proc fromFlatty*(s: string, i: var int, x: var float64) +proc fromFlatty*[T: range](s: string, i: var int, x: var T) proc fromFlatty*[T: enum and not range](s: string, i: var int, x: var T) proc fromFlatty*(s: string, i: var int, x: var string) @@ -279,6 +283,17 @@ proc fromFlatty*(s: string, i: var int, x: var float64) = x = s.readFloat64(i) i += 8 +# Ranges +proc toFlatty*[T: range](s: var string, x: T) = + s.toFlatty(x.rangeBase) + +proc fromFlatty*[T: range](s: string, i: var int, x: var T) = + var value: rangeBase(T) + s.fromFlatty(i, value) + if value < low(T).rangeBase or value > high(T).rangeBase: + raise newException(RangeDefect, "value out of range") + x = T(value) + # Enums proc toFlatty*[T: enum and not range](s: var string, x: T) = s.addInt64(x.int) @@ -334,6 +349,7 @@ proc fromFlatty*[T](s: string, i: var int, x: var seq[T]) = # Objects proc toFlatty*[T: object](s: var string, x: T) = + privateAccess(T) when x.isObjectVariant: s.toFlatty(x.discriminatorField) for k, e in x.fieldPairs: @@ -344,6 +360,7 @@ proc toFlatty*[T: object](s: var string, x: T) = s.toFlatty(e) proc fromFlatty*[T: object](s: string, i: var int, x: var T) = + privateAccess(T) when x.isObjectVariant: var discriminator: type(x.discriminatorField) s.fromFlatty(i, discriminator) @@ -412,7 +429,7 @@ proc toFlatty*[N, T](s: var string, x: array[N, T]) = let byteLen = x.len * sizeof(T) let oldLen = s.len s.setLen(oldLen + byteLen) - copyMem(s[oldLen].addr, x[0.N].unsafeAddr, byteLen) + copyMem(s[oldLen].addr, x[low(x)].unsafeAddr, byteLen) else: for e in x: s.toFlatty(e) @@ -420,7 +437,7 @@ proc toFlatty*[N, T](s: var string, x: array[N, T]) = proc fromFlatty*[N, T](s: string, i: var int, x: var array[N, T]) = when not defined(js) and T.copyable: if x.len > 0: - copyMem(x[0.N].addr, s[i].unsafeAddr, sizeof(x)) + copyMem(x[low(x)].addr, s[i].unsafeAddr, sizeof(x)) i += sizeof(x) else: for j in x.mitems: diff --git a/tests/test_issue_objects.nim b/tests/test_issue_objects.nim new file mode 100644 index 0000000..361c72f --- /dev/null +++ b/tests/test_issue_objects.nim @@ -0,0 +1,49 @@ +import + flatty, + test_issue_objects_types + +# #41: imported object variants can keep their discriminator private. +block: + let original = newIssue41Obj(issue41B, "hidden variant payload") + let roundTrip = original.toFlatty.fromFlatty(Issue41Obj) + doAssert roundTrip.kind == issue41B + doAssert roundTrip.value == original.value + +# #34: imported objects can have private fields with public getter procs. +block: + let original = initIssue34Bloom(1024'u64, @[1'u64, 8, 27, 64]) + let roundTrip = original.toFlatty.fromFlatty(Issue34Bloom) + doAssert roundTrip.capacity == original.capacity + doAssert roundTrip.data == original.data + +# #32: arrays with non-zero index ranges use their real lower bound. +block: + var arr: array[-2..0, int] + var arr2: array[10..11, int] + + for i in arr.low .. arr.high: + arr[i] = i + + for i in arr2.low .. arr2.high: + arr2[i] = i + + let arrRoundTrip = arr.toFlatty.fromFlatty(array[-2..0, int]) + let arr2RoundTrip = arr2.toFlatty.fromFlatty(array[10..11, int]) + + for i in arr.low .. arr.high: + doAssert arrRoundTrip[i] == arr[i] + + for i in arr2.low .. arr2.high: + doAssert arr2RoundTrip[i] == arr2[i] + +# #29: range fields reject out-of-range serialized values. +block: + type RangeObj = object + a: 0..3 + + let original = RangeObj(a: 3) + doAssert original.toFlatty.fromFlatty(RangeObj) == original + + let invalid = 10.toFlatty + doAssertRaises(RangeDefect): + discard invalid.fromFlatty(RangeObj) diff --git a/tests/test_issue_objects_types.nim b/tests/test_issue_objects_types.nim new file mode 100644 index 0000000..e19ad8c --- /dev/null +++ b/tests/test_issue_objects_types.nim @@ -0,0 +1,40 @@ +type + Issue41Kind* = enum + issue41A, issue41B, issue41C + + Issue41Obj* = object + case kind: Issue41Kind + of issue41A: + a: string + else: + b: string + + Issue34Bloom* = object + capacity: uint64 + data: seq[uint64] + +proc newIssue41Obj*(kind: Issue41Kind, value: string): Issue41Obj = + case kind + of issue41A: + Issue41Obj(kind: kind, a: value) + else: + Issue41Obj(kind: kind, b: value) + +proc kind*(x: Issue41Obj): Issue41Kind = + x.kind + +proc value*(x: Issue41Obj): string = + case x.kind + of issue41A: + x.a + else: + x.b + +proc initIssue34Bloom*(capacity: uint64, data: seq[uint64]): Issue34Bloom = + Issue34Bloom(capacity: capacity, data: data) + +proc capacity*(x: Issue34Bloom): uint64 = + x.capacity + +proc data*(x: Issue34Bloom): seq[uint64] = + x.data diff --git a/tests/tests.nim b/tests/tests.nim index 8310be1..e0359d6 100644 --- a/tests/tests.nim +++ b/tests/tests.nim @@ -1,4 +1,4 @@ import test_binny, test_datetime, test_flatty, test_hashy, test_hexprint, - test_memoryused, test_modes, test_objvar + test_issue_objects, test_memoryused, test_modes, test_objvar echo "all tests pass"