More comprehensive test suite; Depends on Fixes in Nim 1.2

This commit is contained in:
Zahary Karadjov 2020-03-25 17:54:27 +02:00 committed by zah
parent 0eab8cfeee
commit 68e9ef7901
2 changed files with 77 additions and 50 deletions

View File

@ -122,7 +122,7 @@ macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
# TODO: This is a work-around for a classic Nim issue: # TODO: This is a work-around for a classic Nim issue:
type `FieldTypeSym` {.used.} = type(`field`) type `FieldTypeSym` {.used.} = type(`field`)
`body` `body`
i += 1 i += 1
template enumAllSerializedFields*(T: type, body): untyped = template enumAllSerializedFields*(T: type, body): untyped =
@ -181,21 +181,20 @@ proc makeFieldReadersTable(RecordType, Reader: distinct type):
when RecordType is tuple: when RecordType is tuple:
const i = fieldName.parseInt const i = fieldName.parseInt
try: try:
type F = FieldTag[RecordType, fieldName, type(FieldType)] type F = FieldTag[RecordType, fieldName, type(FieldType)]
when RecordType is not tuple: when RecordType is tuple:
obj.field(fieldName) = readFieldIMPL(F, reader)
else:
obj[i] = readFieldIMPL(F, reader) obj[i] = readFieldIMPL(F, reader)
else:
field(obj, fieldName) = readFieldIMPL(F, reader)
except SerializationError: except SerializationError:
raise raise
except CatchableError as err: except CatchableError as err:
when RecordType is not tuple: reader.handleReadException(
let field = obj.field(fieldName) `RecordType`,
else: fieldName,
let field = obj[i] when RecordType is tuple: obj[i] else: field(obj, fieldName),
reader.handleReadException(`RecordType`, fieldName, err)
field, err)
result.add((fieldName, readField)) result.add((fieldName, readField))
proc fieldReadersTable*(RecordType, Reader: distinct type): proc fieldReadersTable*(RecordType, Reader: distinct type):

View File

@ -14,30 +14,40 @@ type
ignored*: int ignored*: int
Transaction* = object Transaction* = object
amount: int amount*: int
time: DateTime time*: DateTime
sender: string sender*: string
receiver: string receiver*: string
DerivedFromRoot* = object of RootObj
a*: string
b*: int
DerivedFromRootRef = ref DerivedFromRoot
RefTypeDerivedFromRoot* = ref object of RootObj
a*: int
b*: string
Foo = object Foo = object
x: uint64 x*: uint64
y: string y*: string
z: seq[int] z*: seq[int]
Bar = object Bar = object
b: string b*: string
f: Foo f*: Foo
ListOfLists = object
lists: seq[ListOfLists]
# Baz should use custom serialization # Baz should use custom serialization
# The `i` field should be multiplied by two while deserializing and # The `i` field should be multiplied by two while deserializing and
# `ignored` field should be set to 10 # `ignored` field should be set to 10
Baz = object Baz = object
f: Foo f*: Foo
i: int i*: int
ignored {.dontSerialize.}: int ignored* {.dontSerialize.}: int
ListOfLists = object
lists*: seq[ListOfLists]
NoExpectedResult = distinct int NoExpectedResult = distinct int
@ -46,7 +56,7 @@ type
B B
CaseObject* = object CaseObject* = object
case kind: ObjectKind: case kind*: ObjectKind:
of A: of A:
a*: int a*: int
other*: CaseObjectRef other*: CaseObjectRef
@ -55,6 +65,9 @@ type
CaseObjectRef* = ref CaseObject CaseObjectRef* = ref CaseObject
HoldsCaseObject* = object
value: CaseObject
HoldsSet* = object HoldsSet* = object
a*: int a*: int
s*: HashSet[string] s*: HashSet[string]
@ -111,22 +124,30 @@ func caseObjectEquals(a, b: CaseObject): bool =
func `==`*(a, b: CaseObject): bool = func `==`*(a, b: CaseObject): bool =
caseObjectEquals(a, b) caseObjectEquals(a, b)
template maybeDefer(x: auto): auto =
when type(x) is ref:
x[]
else:
x
template roundtripChecks*(Format: type, value: auto, expectedResult: auto) = template roundtripChecks*(Format: type, value: auto, expectedResult: auto) =
let v = value let origValue = value
let serialized = encode(Format, v) let serialized = encode(Format, origValue)
checkpoint "(encoded value): " & $serialized checkpoint "(encoded value): " & $serialized
when not (expectedResult is NoExpectedResult): when not (expectedResult is NoExpectedResult):
check serialized == expectedResult check serialized == expectedResult
try: try:
let decoded = Format.decode(serialized, type(v)) let decoded = Format.decode(serialized, type(origValue))
checkpoint "(decoded value): " & repr(decoded) checkpoint "(decoded value): " & repr(decoded)
let decodedValueMatchesOriginal = decoded == v let success = maybeDefer(decoded) == maybeDefer(origValue)
check decodedValueMatchesOriginal check success
except SerializationError as err: except SerializationError as err:
checkpoint "(serialization error): " & err.formatMsg("(encoded value)") checkpoint "(serialization error): " & err.formatMsg("(encoded value)")
fail() fail()
except: except:
when compiles($value): when compiles($value):
checkpoint "unexpected failure in roundtrip test for " & $value checkpoint "unexpected failure in roundtrip test for " & $value
@ -158,11 +179,8 @@ proc executeRoundtripTests*(Format: type) =
template intTests(T: untyped) = template intTests(T: untyped) =
roundtrip low(T) roundtrip low(T)
roundtrip high(T) roundtrip high(T)
when false: for i in 0..1000:
# TODO: roundtrip rand(T)
# rand(low..high) produces an overflow error in Nim 1.0.2
for i in 0..1000:
roundtrip rand(low(T)..(high(T) div 2))
intTests int8 intTests int8
intTests int16 intTests int16
@ -194,9 +212,8 @@ proc executeRoundtripTests*(Format: type) =
f: Foo(x: 5'u64, y: "hocus pocus", z: @[100, 200, 300])) f: Foo(x: 5'u64, y: "hocus pocus", z: @[100, 200, 300]))
roundtrip b roundtrip b
when false: when false and supports(Format, Transaction):
# TODO: This requires the test suite of each format to implement # Some formats may not support the DateTime type.
# support for the DateTime type.
var t = Transaction(time: now(), amount: 1000, sender: "Alice", receiver: "Bob") var t = Transaction(time: now(), amount: 1000, sender: "Alice", receiver: "Bob")
roundtrip t roundtrip t
@ -205,21 +222,32 @@ proc executeRoundtripTests*(Format: type) =
# and give it a more proper name. The custom serialization demands # and give it a more proper name. The custom serialization demands
# that the `ignored` field is populated with a value depending on # that the `ignored` field is populated with a value depending on
# the `i` value. `i` itself is doubled on deserialization. # the `i` value. `i` itself is doubled on deserialization.
var origVal = Baz(f: Foo(x: 10'u64, y: "y", z: @[]), ignored: 5) let
bytes = Format.encode(origVal) origVal = Baz(f: Foo(x: 10'u64, y: "y", z: @[]), ignored: 5)
var restored = Format.decode(bytes, Baz) encoded = Format.encode(origVal)
restored = Format.decode(encoded, Baz)
check: check:
origVal.f.x == restored.f.x origVal.f.x == restored.f.x
origVal.f.i == restored.f.i div 2
origVal.f.y.len == restored.f.y.len origVal.f.y.len == restored.f.y.len
origVal.i == restored.i div 2
restored.ignored == 10 restored.ignored == 10
block:
let
a = DerivedFromRoot(a: "test", b: -1000)
b = DerivedFromRootRef(a: "another test", b: 2000)
c = RefTypeDerivedFromRoot(a: high(int), b: "")
roundtrip a
roundtrip b
roundtrip c
test "case objects": test "case objects":
var var
c1 = CaseObjectRef(kind: B, b: 100) c1 = CaseObjectRef(kind: B, b: 100)
c2 = CaseObjectRef(kind: A, a: 80, other: CaseObjectRef(kind: B)) c2 = CaseObjectRef(kind: A, a: 80, other: CaseObjectRef(kind: B))
c3 = CaseObject(kind: A, a: 60, other: nil) c3 = HoldsCaseObject(value: CaseObject(kind: A, a: 60, other: c1))
roundtrip c1 roundtrip c1
roundtrip c2 roundtrip c2
@ -263,11 +291,11 @@ proc executeRoundtripTests*(Format: type) =
roundtrip s1 roundtrip s1
roundtrip s2 roundtrip s2
test "tuple": test "tuple":
var t = (0, "e") var t = (0, "e")
var namedT = (a: 0, b: "e") var namedT = (a: 0, b: "e")
roundtrip t roundtrip t
roundtrip namedT roundtrip namedT
proc executeReaderWriterTests*(Format: type) = proc executeReaderWriterTests*(Format: type) =
mixin init, ReaderType, WriterType mixin init, ReaderType, WriterType