2018-11-11 11:45:34 +00:00
|
|
|
import
|
2019-06-24 14:38:37 +00:00
|
|
|
strutils, unittest,
|
2019-07-08 07:57:05 +00:00
|
|
|
serialization/object_serialization,
|
2018-12-17 23:01:06 +00:00
|
|
|
serialization/testing/generic_suite,
|
2019-06-24 14:38:37 +00:00
|
|
|
../json_serialization, ./utils,
|
2019-07-16 10:20:05 +00:00
|
|
|
../json_serialization/std/[options, sets]
|
2018-11-11 11:45:34 +00:00
|
|
|
|
|
|
|
type
|
2019-03-13 21:20:58 +00:00
|
|
|
Meter = distinct int
|
|
|
|
Mile = distinct int
|
|
|
|
|
2018-11-11 11:45:34 +00:00
|
|
|
Simple = object
|
|
|
|
x: int
|
|
|
|
y: string
|
2019-03-13 21:20:58 +00:00
|
|
|
distance: Meter
|
2019-07-08 07:57:05 +00:00
|
|
|
ignored: int
|
2018-11-11 11:45:34 +00:00
|
|
|
|
|
|
|
Foo = object
|
|
|
|
i: int
|
2019-07-08 07:57:05 +00:00
|
|
|
b {.dontSerialize.}: Bar
|
2018-11-11 11:45:34 +00:00
|
|
|
s: string
|
|
|
|
|
|
|
|
Bar = object
|
|
|
|
sf: seq[Foo]
|
|
|
|
z: ref Simple
|
2019-06-24 14:38:37 +00:00
|
|
|
|
|
|
|
HoldsOption = object
|
|
|
|
r: ref Simple
|
|
|
|
o: Option[Simple]
|
2018-11-11 11:45:34 +00:00
|
|
|
|
2019-03-19 23:54:03 +00:00
|
|
|
HoldsArray = object
|
|
|
|
data: seq[int]
|
|
|
|
|
2019-03-13 21:20:58 +00:00
|
|
|
Invalid = object
|
|
|
|
distance: Mile
|
|
|
|
|
2019-05-28 15:42:18 +00:00
|
|
|
Reserved = object
|
|
|
|
# Using Nim reserved keyword
|
|
|
|
`type`: string
|
|
|
|
|
2019-08-01 14:12:31 +00:00
|
|
|
ObjectKind = enum
|
|
|
|
A
|
|
|
|
B
|
|
|
|
|
|
|
|
CaseObject = object
|
|
|
|
case kind: ObjectKind:
|
|
|
|
of A:
|
|
|
|
a: int
|
|
|
|
other: CaseObjectRef
|
|
|
|
else:
|
|
|
|
b: int
|
|
|
|
|
|
|
|
CaseObjectRef = ref CaseObject
|
|
|
|
|
|
|
|
func caseObjectEquals(a, b: CaseObject): bool
|
|
|
|
|
|
|
|
func `==`*(a, b: CaseObjectRef): bool =
|
|
|
|
let nils = ord(a.isNil) + ord(b.isNil)
|
|
|
|
if nils == 0:
|
|
|
|
caseObjectEquals(a[], b[])
|
|
|
|
else:
|
|
|
|
nils == 2
|
|
|
|
|
|
|
|
func caseObjectEquals(a, b: CaseObject): bool =
|
|
|
|
# TODO This is needed to work-around a Nim overload selection issue
|
|
|
|
if a.kind != b.kind: return false
|
|
|
|
|
|
|
|
case a.kind
|
|
|
|
of A:
|
|
|
|
if a.a != b.a: return false
|
|
|
|
a.other == b.other
|
|
|
|
of B:
|
|
|
|
a.b == b.b
|
|
|
|
|
|
|
|
func `==`*(a, b: CaseObject): bool =
|
|
|
|
caseObjectEquals(a, b)
|
|
|
|
|
2019-03-13 21:20:58 +00:00
|
|
|
template reject(code) =
|
2019-03-13 23:39:10 +00:00
|
|
|
static: doAssert(not compiles(code))
|
2019-03-13 21:20:58 +00:00
|
|
|
|
|
|
|
borrowSerialization(Meter, int)
|
|
|
|
|
2019-07-08 07:57:05 +00:00
|
|
|
Simple.setSerializedFields distance, x, y
|
|
|
|
|
2019-06-24 14:38:37 +00:00
|
|
|
proc `==`(lhs, rhs: Meter): bool =
|
|
|
|
int(lhs) == int(rhs)
|
|
|
|
|
|
|
|
proc `==`(lhs, rhs: ref Simple): bool =
|
|
|
|
if lhs.isNil: return rhs.isNil
|
|
|
|
if rhs.isNil: return false
|
|
|
|
return lhs[] == rhs[]
|
|
|
|
|
2018-12-17 23:01:06 +00:00
|
|
|
executeReaderWriterTests Json
|
2018-11-11 11:45:34 +00:00
|
|
|
|
2019-06-24 14:38:37 +00:00
|
|
|
proc newSimple(x: int, y: string, d: Meter): ref Simple =
|
|
|
|
new result
|
|
|
|
result.x = x
|
|
|
|
result.y = y
|
|
|
|
result.distance = d
|
|
|
|
|
2019-03-13 21:20:58 +00:00
|
|
|
when false:
|
|
|
|
# The compiler cannot handle this check at the moment
|
|
|
|
# {.fatal.} seems fatal even in `compiles` context
|
|
|
|
var invalid = Invalid(distance: Mile(100))
|
|
|
|
reject invalid.toJson
|
|
|
|
|
2018-12-17 23:01:06 +00:00
|
|
|
suite "toJson tests":
|
2018-11-11 11:45:34 +00:00
|
|
|
test "encode primitives":
|
|
|
|
check:
|
|
|
|
1.toJson == "1"
|
|
|
|
"".toJson == "\"\""
|
|
|
|
"abc".toJson == "\"abc\""
|
|
|
|
|
|
|
|
test "simple objects":
|
2019-03-13 21:20:58 +00:00
|
|
|
var s = Simple(x: 10, y: "test", distance: Meter(20))
|
2018-11-11 11:45:34 +00:00
|
|
|
|
|
|
|
check:
|
2019-07-08 07:57:05 +00:00
|
|
|
s.toJson == """{"distance":20,"x":10,"y":"test"}"""
|
|
|
|
s.toJson(typeAnnotations = true) == """{"$type":"Simple","distance":20,"x":10,"y":"test"}"""
|
2018-11-11 11:45:34 +00:00
|
|
|
s.toJson(pretty = true) == dedent"""
|
|
|
|
{
|
2019-07-08 07:57:05 +00:00
|
|
|
"distance": 20,
|
2018-11-11 11:45:34 +00:00
|
|
|
"x": 10,
|
2019-07-08 07:57:05 +00:00
|
|
|
"y": "test"
|
2018-11-11 11:45:34 +00:00
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
2019-04-08 12:29:11 +00:00
|
|
|
test "handle missing fields":
|
|
|
|
let json = dedent"""
|
|
|
|
{
|
2019-07-08 07:57:05 +00:00
|
|
|
"distance": 20,
|
|
|
|
"y": "test"
|
2019-04-08 12:29:11 +00:00
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
let decoded = Json.decode(json, Simple)
|
|
|
|
|
|
|
|
check:
|
|
|
|
decoded.x == 0
|
|
|
|
decoded.y == "test"
|
|
|
|
decoded.distance.int == 20
|
|
|
|
|
2019-03-19 23:54:03 +00:00
|
|
|
test "arrays are printed correctly":
|
|
|
|
var x = HoldsArray(data: @[1, 2, 3, 4])
|
|
|
|
|
|
|
|
check:
|
|
|
|
x.toJson(pretty = true) == dedent"""
|
|
|
|
{
|
|
|
|
"data": [
|
|
|
|
1,
|
|
|
|
2,
|
|
|
|
3,
|
|
|
|
4
|
|
|
|
]
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
2019-01-21 17:40:14 +00:00
|
|
|
test "max unsigned value":
|
|
|
|
var uintVal = not uint64(0)
|
|
|
|
let jsonValue = Json.encode(uintVal)
|
|
|
|
check:
|
|
|
|
jsonValue == "18446744073709551615"
|
|
|
|
Json.decode(jsonValue, uint64) == uintVal
|
|
|
|
|
|
|
|
expect JsonReaderError:
|
|
|
|
discard Json.decode(jsonValue, uint64, mode = Portable)
|
|
|
|
|
2019-05-28 15:42:18 +00:00
|
|
|
test "Using Nim reserved keyword `type`":
|
|
|
|
let r = Reserved(`type`: "uint8")
|
|
|
|
check:
|
|
|
|
r.toJSON == """{"type":"uint8"}"""
|
|
|
|
r == Json.decode("""{"type":"uint8"}""", Reserved)
|
2019-06-24 14:38:37 +00:00
|
|
|
|
|
|
|
test "Option types":
|
|
|
|
let
|
|
|
|
h1 = HoldsOption(o: some Simple(x: 1, y: "2", distance: Meter(3)))
|
|
|
|
h2 = HoldsOption(r: newSimple(1, "2", Meter(3)))
|
|
|
|
|
2019-07-08 07:57:05 +00:00
|
|
|
Json.roundtripTest h1, """{"r":null,"o":{"distance":3,"x":1,"y":"2"}}"""
|
|
|
|
Json.roundtripTest h2, """{"r":{"distance":3,"x":1,"y":"2"},"o":null}"""
|
2019-06-24 14:38:37 +00:00
|
|
|
|
2019-07-16 10:20:05 +00:00
|
|
|
test "Set types":
|
|
|
|
type HoldsSet = object
|
|
|
|
a: int
|
|
|
|
s: HashSet[string]
|
|
|
|
|
|
|
|
var s1 = toSet([1, 2, 3, 1, 4, 2])
|
|
|
|
var s2 = HoldsSet(a: 100, s: toSet(["a", "b", "c"]))
|
|
|
|
|
|
|
|
Json.roundtripTest s1
|
|
|
|
Json.roundtripTest s2
|
|
|
|
|
2019-08-01 14:12:31 +00:00
|
|
|
test "Case objects":
|
|
|
|
var
|
|
|
|
c1 = CaseObjectRef(kind: B, b: 100)
|
|
|
|
c2 = CaseObjectRef(kind: A, a: 80, other: CaseObjectRef(kind: B))
|
|
|
|
c3 = CaseObject(kind: A, a: 60, other: nil)
|
|
|
|
|
|
|
|
Json.roundtripTest c1
|
|
|
|
Json.roundtripTest c2
|
|
|
|
Json.roundtripTest c3
|
|
|
|
|