Improved error messages and test them.

This commit is contained in:
Felix Krause 2016-09-24 16:45:49 +02:00
parent 2ad41d349e
commit 3942e80d9b
2 changed files with 33 additions and 26 deletions

View File

@ -69,15 +69,17 @@ template assertStringEqual(expected, actual: string) =
echo "expected:\n", expected, "\nactual:\n", actual echo "expected:\n", expected, "\nactual:\n", actual
assert(false) assert(false)
template expectConstructionError(l, c: int, body: typed) = template expectConstructionError(li, co: int, message: string, body: typed) =
try: try:
body body
echo "Expected YamlConstructionError, but none was raised!" echo "Expected YamlConstructionError, but none was raised!"
fail() fail()
except YamlConstructionError: except YamlConstructionError:
let e = (ref YamlConstructionError)(getCurrentException()) let e = (ref YamlConstructionError)(getCurrentException())
doAssert l == e.line, "Expected error line " & $l & ", was " & $e.line doAssert li == e.line, "Expected error line " & $li & ", was " & $e.line
doAssert c == e.column, "Expected error column " & $c & ", was " & $e.column doAssert co == e.column, "Expected error column " & $co & ", was " & $e.column
doAssert message == e.msg, "Expected error message " & escape(message) &
", got " & escape(e.msg)
proc newNode(v: string): ref Node = proc newNode(v: string): ref Node =
new(result) new(result)
@ -315,19 +317,19 @@ suite "Serialization":
test "Load Tuple - unknown field": test "Load Tuple - unknown field":
let input = "str: value\nfoo: bar\ni: 42\nb: true" let input = "str: value\nfoo: bar\ni: 42\nb: true"
var result: MyTuple var result: MyTuple
expectConstructionError(2, 1): expectConstructionError(2, 1, "While constructing MyTuple: Unknown field: \"foo\""):
load(input, result) load(input, result)
test "Load Tuple - missing field": test "Load Tuple - missing field":
let input = "str: value\nb: true" let input = "str: value\nb: true"
var result: MyTuple var result: MyTuple
expectConstructionError(2, 8): expectConstructionError(2, 8, "While constructing MyTuple: Missing field: \"i\""):
load(input, result) load(input, result)
test "Load Tuple - duplicate field": test "Load Tuple - duplicate field":
let input = "str: value\ni: 42\nb: true\nb: true" let input = "str: value\ni: 42\nb: true\nb: true"
var result: MyTuple var result: MyTuple
expectConstructionError(4, 1): expectConstructionError(4, 1, "While constructing MyTuple: Duplicate field: \"b\""):
load(input, result) load(input, result)
test "Load Multiple Documents": test "Load Multiple Documents":
@ -362,19 +364,19 @@ suite "Serialization":
test "Load custom object - unknown field": test "Load custom object - unknown field":
let input = " firstnamechar: P\n surname: Pan\n age: 12\n occupation: free" let input = " firstnamechar: P\n surname: Pan\n age: 12\n occupation: free"
var result: Person var result: Person
expectConstructionError(4, 3): expectConstructionError(4, 3, "While constructing Person: Unknown field: \"occupation\""):
load(input, result) load(input, result)
test "Load custom object - missing field": test "Load custom object - missing field":
let input = "surname: Pan\nage: 12\n " let input = "surname: Pan\nage: 12\n "
var result: Person var result: Person
expectConstructionError(3, 3): expectConstructionError(3, 3, "While constructing Person: Missing field: \"firstnamechar\""):
load(input, result) load(input, result)
test "Load custom object - duplicate field": test "Load custom object - duplicate field":
let input = "firstnamechar: P\nsurname: Pan\nage: 12\nsurname: Pan" let input = "firstnamechar: P\nsurname: Pan\nage: 12\nsurname: Pan"
var result: Person var result: Person
expectConstructionError(4, 1): expectConstructionError(4, 1, "While constructing Person: Duplicate field: \"surname\""):
load(input, result) load(input, result)
test "Load sequence with explicit tags": test "Load sequence with explicit tags":
@ -445,7 +447,7 @@ suite "Serialization":
test "Load custom variant object - missing field": test "Load custom variant object - missing field":
let input = "[{name: Bastet}, {kind: akCat}]" let input = "[{name: Bastet}, {kind: akCat}]"
var result: Animal var result: Animal
expectConstructionError(1, 32): expectConstructionError(1, 32, "While constructing Animal: Missing field: \"purringIntensity\""):
load(input, result) load(input, result)
test "Dump cyclic data structure": test "Dump cyclic data structure":

View File

@ -510,19 +510,19 @@ macro matchMatrix(t: typedesc): untyped =
for i in 0..<numFields: for i in 0..<numFields:
result.add(newLit(false)) result.add(newLit(false))
proc checkDuplicate(s: NimNode, t: typedesc, name: string, i: int, proc checkDuplicate(s: NimNode, tName: string, name: string, i: int,
matched: NimNode): NimNode {.compileTime.} = matched: NimNode): NimNode {.compileTime.} =
result = newIfStmt((newNimNode(nnkBracketExpr).add(matched, newLit(i)), result = newIfStmt((newNimNode(nnkBracketExpr).add(matched, newLit(i)),
newNimNode(nnkRaiseStmt).add(newCall(bindSym("constructionError"), s, newNimNode(nnkRaiseStmt).add(newCall(bindSym("constructionError"), s,
newLit("While constructing " & typetraits.name(t) & newLit("While constructing " & tName & ": Duplicate field: " &
": Duplicate field: " & escape(name)))))) escape(name))))))
proc checkMissing(s: NimNode, t: typedesc, name: string, i: int, proc checkMissing(s: NimNode, tName: string, name: string, i: int,
matched: NimNode): NimNode {.compileTime.} = matched: NimNode): NimNode {.compileTime.} =
result = newIfStmt((newCall("not", newNimNode(nnkBracketExpr).add(matched, result = newIfStmt((newCall("not", newNimNode(nnkBracketExpr).add(matched,
newLit(i))), newNimNode(nnkRaiseStmt).add(newCall( newLit(i))), newNimNode(nnkRaiseStmt).add(newCall(
bindSym("constructionError"), s, newLit("While constructing " & bindSym("constructionError"), s, newLit("While constructing " &
typetraits.name(t) & ": Missing field: " & escape(name)))))) tName & ": Missing field: " & escape(name))))))
proc markAsFound(i: int, matched: NimNode): NimNode {.compileTime.} = proc markAsFound(i: int, matched: NimNode): NimNode {.compileTime.} =
newAssignment(newNimNode(nnkBracketExpr).add(matched, newLit(i)), newAssignment(newNimNode(nnkBracketExpr).add(matched, newLit(i)),
@ -532,25 +532,30 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
matched: typed): typed = matched: typed): typed =
result = newStmtList() result = newStmtList()
let let
tDesc = getType(getType(t)[1]) tDecl = getType(t)
tName = $tDecl[1]
tDesc = getType(tDecl[1])
var field = 0 var field = 0
for child in tDesc[2].children: for child in tDesc[2].children:
if child.kind == nnkRecCase: if child.kind == nnkRecCase:
result.add(checkMissing(s, t, $child[0], field, matched)) result.add(checkMissing(s, tName, $child[0], field, matched))
for bIndex in 1 .. len(child) - 1: for bIndex in 1 .. len(child) - 1:
let discChecks = newStmtList() let discChecks = newStmtList()
for item in child[bIndex][1].children: for item in child[bIndex][1].children:
inc(field) inc(field)
discChecks.add(checkMissing(s, t, $item, field, matched)) discChecks.add(checkMissing(s, tName, $item, field, matched))
result.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])), result.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
"==", child[bIndex][0]), discChecks))) "==", child[bIndex][0]), discChecks)))
else: else:
result.add(checkMissing(s, t, $child, field, matched)) result.add(checkMissing(s, tName, $child, field, matched))
inc(field) inc(field)
macro constructFieldValue(t: typedesc, stream: untyped, context: untyped, macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
name: untyped, o: untyped, matched: untyped): typed = name: untyped, o: untyped, matched: untyped): typed =
let tDesc = getType(getType(t)[1]) let
tDecl = getType(t)
tName = $tDecl[1]
tDesc = getType(tDecl[1])
result = newNimNode(nnkCaseStmt).add(name) result = newNimNode(nnkCaseStmt).add(name)
var fieldIndex = 0 var fieldIndex = 0
for child in tDesc[2].children: for child in tDesc[2].children:
@ -560,7 +565,7 @@ macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
discType = newCall("type", discriminant) discType = newCall("type", discriminant)
var disOb = newNimNode(nnkOfBranch).add(newStrLitNode($child[0])) var disOb = newNimNode(nnkOfBranch).add(newStrLitNode($child[0]))
disOb.add(newStmtList( disOb.add(newStmtList(
checkDuplicate(stream, t, $child[0], fieldIndex, matched), checkDuplicate(stream, tName, $child[0], fieldIndex, matched),
newNimNode(nnkVarSection).add( newNimNode(nnkVarSection).add(
newNimNode(nnkIdentDefs).add( newNimNode(nnkIdentDefs).add(
newIdentNode("value"), discType, newEmptyNode())), newIdentNode("value"), discType, newEmptyNode())),
@ -581,7 +586,7 @@ macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
newCall(bindSym("constructionError"), stream, newCall(bindSym("constructionError"), stream,
infix(newStrLitNode("Field " & $item & " not allowed for " & infix(newStrLitNode("Field " & $item & " not allowed for " &
$child[0] & " == "), "&", prefix(discriminant, "$")))))) $child[0] & " == "), "&", prefix(discriminant, "$"))))))
ob.add(newStmtList(checkDuplicate(stream, t, $item, fieldIndex, ob.add(newStmtList(checkDuplicate(stream, tName, $item, fieldIndex,
matched), ifStmt, markAsFound(fieldIndex, matched))) matched), ifStmt, markAsFound(fieldIndex, matched)))
result.add(ob) result.add(ob)
else: else:
@ -589,15 +594,15 @@ macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
var ob = newNimNode(nnkOfBranch).add(newStrLitNode($child)) var ob = newNimNode(nnkOfBranch).add(newStrLitNode($child))
let field = newDotExpr(o, newIdentNode($child)) let field = newDotExpr(o, newIdentNode($child))
ob.add(newStmtList( ob.add(newStmtList(
checkDuplicate(stream, t, $child, fieldIndex, matched), checkDuplicate(stream, tName, $child, fieldIndex, matched),
newCall("constructChild", stream, context, field), newCall("constructChild", stream, context, field),
markAsFound(fieldIndex, matched))) markAsFound(fieldIndex, matched)))
result.add(ob) result.add(ob)
inc(fieldIndex) inc(fieldIndex)
result.add(newNimNode(nnkElse).add(newNimNode(nnkRaiseStmt).add( result.add(newNimNode(nnkElse).add(newNimNode(nnkRaiseStmt).add(
newCall(bindSym("constructionError"), stream, newCall(bindSym("constructionError"), stream,
infix(newLit("While constructing " & typetraits.name(t) & infix(newLit("While constructing " & tName & ": Unknown field: "), "&",
": Unknown field: "), "&", name))))) newCall(bindSym("escape"), name))))))
proc isVariantObject(t: typedesc): bool {.compileTime.} = proc isVariantObject(t: typedesc): bool {.compileTime.} =
let tDesc = getType(t) let tDesc = getType(t)
@ -659,7 +664,7 @@ proc constructObject*[O: object|tuple](
for fname, value in fieldPairs(result): for fname, value in fieldPairs(result):
if not matched[i]: if not matched[i]:
raise s.constructionError("While constructing " & raise s.constructionError("While constructing " &
typetraits.name(O) & ": Field missing: " & escape(fname)) typetraits.name(O) & ": Missing field: " & escape(fname))
inc(i) inc(i)
else: ensureAllFieldsPresent(s, O, result, matched) else: ensureAllFieldsPresent(s, O, result, matched)