Added option to validate against YAML test suite

This commit is contained in:
Felix Krause 2016-03-04 19:10:48 +01:00
parent 54703ae577
commit 778799e4ff
6 changed files with 264 additions and 0 deletions

5
.gitignore vendored
View File

@ -3,11 +3,16 @@ test/tests
test/parsing
test/lexing
test/serializing
test/constructingJson
test/yamlTestSuite
test/*.exe
test/*.pdb
test/*.ilk
server/server
bench/jsonBench
yaml.html
libyaml.dylib
libyaml.so
bench/json
docout
yaml-dev-kit

View File

@ -28,6 +28,7 @@ nim server # builds the REST server used for the testing ground
nim bench # runs benchmarks, requires libyaml
nim clean # guess
nim build # build a library
nim yamlTestSuite # execute YAML test suite (git-clones yaml-dev-kit)
```
Project is tested against current develop branch of Nim. Older Nim versions

View File

@ -8,6 +8,11 @@ task tests, "Run all tests":
--verbosity:0
setCommand "c", "test/tests"
task yamlTestSuite, "Run YAML 1.2 test suite":
--r
--verbosity:0
setCommand "c", "test/yamlTestSuite"
task parserTests, "Run parser tests":
--r
--verbosity:0

View File

@ -56,6 +56,7 @@ proc finished*(s: var YamlStream): bool =
raise e
except Exception:
let cur = getCurrentException()
echo cur.getStackTrace()
var e = newException(YamlStreamError, cur.msg)
e.parent = cur
raise e

181
test/tmlParser.nim Normal file
View File

@ -0,0 +1,181 @@
# NimYAML - YAML implementation in Nim
# (c) Copyright 2016 Felix Krause
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
import "../yaml"
import lexbase
type
LexerToken = enum
plusStr, minusStr, plusDoc, minusDoc, plusMap, minusMap, plusSeq,
minusSeq, eqVal, chevTag, quotContent, colonContent, noToken
StreamPos = enum
beforeStream, inStream, afterStream
TmlLexer = object of BaseLexer
content: string
TmlError = object of Exception
proc nextToken(lex: var TmlLexer): LexerToken =
while true:
case lex.buf[lex.bufpos]
of ' ', '\t': lex.bufpos.inc()
of '\r': lex.bufpos = lex.handleCR(lex.bufpos)
of '\l': lex.bufpos = lex.handleLF(lex.bufpos)
else: break
if lex.buf[lex.bufpos] == EndOfFile: return noToken
case lex.buf[lex.bufpos]
of ':', '"':
let t = if lex.buf[lex.bufpos] == ':': colonContent else: quotContent
lex.content = ""
lex.bufpos.inc()
while true:
case lex.buf[lex.bufpos]
of EndOfFile: break
of '\c':
lex.bufpos = lex.handleCR(lex.bufpos)
break
of '\l':
lex.bufpos = lex.handleLF(lex.bufpos)
break
of '\\':
lex.bufpos.inc()
case lex.buf[lex.bufpos]
of 'n': lex.content.add('\l')
of 'r': lex.content.add('\r')
of '0': lex.content.add('\0')
of '\\': lex.content.add('\\')
else: raise newException(TmlError,
"Unknown escape character: " & lex.buf[lex.bufpos])
else: lex.content.add(lex.buf[lex.bufpos])
lex.bufpos.inc()
result = t
of '<':
lex.content = ""
lex.bufpos.inc()
while lex.buf[lex.bufpos] != '>':
lex.content.add(lex.buf[lex.bufpos])
lex.bufpos.inc()
result = chevTag
lex.bufpos.inc()
else:
lex.content = ""
while lex.buf[lex.bufpos] notin {' ', '\t', '\r', '\l', '\0'}:
lex.content.add(lex.buf[lex.bufpos])
lex.bufpos.inc()
case lex.content
of "+STR": result = plusStr
of "-STR": result = minusStr
of "+DOC": result = plusDoc
of "-DOC": result = minusDoc
of "+MAP": result = plusMap
of "-MAP": result = minusMap
of "+SEQ": result = plusSeq
of "-SEQ": result = minusSeq
of "=VAL": result = eqVal
else: raise newException(TmlError, "Invalid token: " & lex.content)
template assertInStream() {.dirty.} =
if streamPos != inStream: raise newException(TmlError, "Missing +STR")
template assertInEvent(name: string) {.dirty.} =
if not inEvent: raise newException(TmlError, "Illegal token: " & name)
template yieldEvent() {.dirty.} =
if inEvent:
yield curEvent
inEvent = false
template setTag(t: TagId) {.dirty.} =
case curEvent.kind
of yamlStartSequence: curEvent.seqTag = t
of yamlStartMap: curEvent.mapTag = t
of yamlScalar: curEvent.scalarTag = t
else: discard
template setAnchor(a: AnchorId) {.dirty.} =
case curEvent.kind
of yamlStartSequence: curEvent.seqAnchor = a
of yamlStartMap: curEvent.mapAnchor = a
of yamlScalar: curEvent.scalarAnchor = a
else: discard
template curTag(): TagId =
var foo: TagId
case curEvent.kind
of yamlStartSequence: foo = curEvent.seqTag
of yamlStartMap: foo = curEvent.mapTag
of yamlScalar: foo = curEvent.scalarTag
else: raise newException(TmlError, $curEvent.kind & " may not have a tag")
foo
template setCurTag(val: TagId) =
case curEvent.kind
of yamlStartSequence: curEvent.seqTag = val
of yamlStartMap: curEvent.mapTag = val
of yamlScalar: curEvent.scalarTag = val
else: raise newException(TmlError, $curEvent.kind & " may not have a tag")
template eventStart(k: YamlStreamEventKind) {.dirty.} =
assertInStream()
yieldEvent()
reset(curEvent)
curEvent.kind = k
setTag(yTagQuestionMark)
setAnchor(yAnchorNone)
inEvent = true
proc parseTmlStream*(input: Stream, tagLib: TagLibrary): YamlStream =
var backend = iterator(): YamlStreamEvent =
var lex: TmlLexer
lex.open(input)
var
inEvent = false
curEvent: YamlStreamEvent
streamPos = beforeStream
while lex.buf[lex.bufpos] != EndOfFile:
let token = lex.nextToken()
case token
of plusStr:
if streamPos != beforeStream:
raise newException(TmlError, "Illegal +STR")
streamPos = inStream
of minusStr:
if streamPos != inStream:
raise newException(TmlError, "Illegal -STR")
if inEvent: yield curEvent
inEvent = false
streamPos = afterStream
of plusDoc: eventStart(yamlStartDocument)
of minusDoc: eventStart(yamlEndDocument)
of plusMap: eventStart(yamlStartMap)
of minusMap: eventStart(yamlEndMap)
of plusSeq: eventStart(yamlStartSequence)
of minusSeq: eventStart(yamlEndSequence)
of eqVal: eventStart(yamlScalar)
of chevTag:
assertInEvent("tag")
if curTag() != yTagQuestionMark:
raise newException(TmlError, "Duplicate tag")
try:
setCurTag(tagLib.tags[lex.content])
except KeyError: setCurTag(tagLib.registerUri(lex.content))
of quotContent:
assertInEvent("scalar content")
if curTag() == yTagQuestionMark: setCurTag(yTagExclamationMark)
if curEvent.kind != yamlScalar:
raise newException(TmlError,
"scalar content in non-scalar tag")
curEvent.scalarContent = lex.content
of colonContent:
assertInEvent("scalar content")
curEvent.scalarContent = lex.content
if curEvent.kind != yamlScalar:
raise newException(TmlError,
"scalar content in non-scalar tag")
of noToken: discard
result = initYamlStream(backend)

71
test/yamlTestSuite.nim Normal file
View File

@ -0,0 +1,71 @@
# NimYAML - YAML implementation in Nim
# (c) Copyright 2016 Felix Krause
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
import os, terminal
import tmlParser, common
import "../yaml"
const gitCmd =
"git clone https://github.com/ingydotnet/yaml-dev-kit.git -b data"
proc echoError(msg: string) =
styledWriteLine(stdout, fgRed, "[error] ", fgWhite, msg, resetStyle)
proc echoInfo(msg: string) =
styledWriteLine(stdout, fgGreen, "[info] ", fgWhite, msg, resetStyle)
removeDir("yaml-dev-kit")
if execShellCmd(gitCmd) != 0:
echoError("Could not check out yaml-dev-kit (no internet connection?)")
quit(1)
var gotErrors = false
try:
for kind, dirPath in walkDir("yaml-dev-kit"):
block curTest:
if kind == pcDir:
if dirPath[^4..^1] in [".git", "name", "tags"]: continue
var
tagLib = initExtendedTagLibrary()
parser = newYamlParser(tagLib)
actual = parser.parse(newFileStream(dirPath / "in.yaml"))
expected = parseTmlStream(
newFileStream(dirPath / "test.event"), tagLib)
styledWriteLine(stdout, fgGreen, "[test] ", fgWhite,
expandSymlink(dirPath / "==="), resetStyle)
while not actual.finished():
let actualEvent = actual.next()
if expected.finished():
echoError("Expected stream end, got " &
$actualEvent.kind)
gotErrors = true
break curTest
let expectedEvent = expected.next()
if expectedEvent != actualEvent:
printDifference(expectedEvent, actualEvent)
echoError("Actual tokens do not match expected tokens")
gotErrors = true
break curTest
if not expected.finished():
echoError(
"Got fewer tokens than expected, first missing token: " &
$expected.next().kind)
gotErrors = true
except YamlStreamError:
let e = getCurrentException()
if e.parent of YamlParserError:
let pe = (ref YamlParserError)(e.parent)
echo "line ", pe.line, ", column ", pe.column, ": ", pe.msg
echo pe.lineContent
raise getCurrentException().parent
if gotErrors:
echoError("There were errors while running the tests")
quit(1)
else:
echoInfo("All tests were successful")
quit(0)