/ test / tparser.nim
tparser.nim
  1  #            NimYAML - YAML implementation in Nim
  2  #        (c) Copyright 2015-2023 Felix Krause
  3  #
  4  #    See the file "copying.txt", included in this
  5  #    distribution, for details about the copyright.
  6  
  7  import os, terminal, strutils, streams, macros, unittest, sets
  8  import testEventParser, commonTestUtils
  9  import ../yaml/[ data, parser, stream ]
 10  
 11  const
 12    testSuiteFolder = "yaml-test-suite"
 13  
 14  proc echoError(msg: string) =
 15    styledWriteLine(stdout, fgRed, "[error] ", fgWhite, msg, resetStyle)
 16  
 17  proc doParserTest(expected, actual: YamlStream, errorExpected: bool): bool =
 18    var i = 1
 19    try:
 20      while true:
 21        let actualEvent = actual.next()
 22        let expectedEvent = expected.next()
 23        if expectedEvent != actualEvent:
 24          result = errorExpected
 25          if not result:
 26            echoError("At event #" & $i &
 27                      ": Actual events do not match expected events")
 28            echo ".. expected event:"
 29            echo "  ", expectedEvent
 30            echo ".. actual event:"
 31            echo "  ", actualEvent
 32            echo ".. difference:"
 33            when nimvm: discard
 34            else: stdout.write("  ")
 35            printDifference(expectedEvent, actualEvent)
 36    
 37          return
 38        i.inc()
 39        if actualEvent.kind == yamlEndStream:
 40          break
 41      result = not errorExpected
 42      if not result:
 43        echo "Expected error, but parsed without error."
 44    except CatchableError as e:
 45      result = errorExpected
 46      if not result:
 47        echoError("Caught an exception at event #" & $i &
 48                  " test was not successful")
 49        if e.parent of YamlParserError:
 50          let pe = (ref YamlParserError)(e.parent)
 51          echo "line ", pe.mark.line, ", column ", pe.mark.column, ": ", pe.msg
 52          echo pe.lineContent
 53        else: echo e.msg
 54  
 55  proc parserTest(path: string, errorExpected : bool): bool =
 56    var
 57      parser: YamlParser
 58    parser.init()
 59    var
 60      actualIn = newFileStream(path / "in.yaml")
 61      actual = parser.parse(actualIn)
 62      expectedIn = newFileStream(path / "test.event")
 63      expected = parseEventStream(expectedIn)
 64    defer:
 65      actualIn.close()
 66      expectedIn.close()
 67    result = doParserTest(expected, actual, errorExpected)
 68  
 69  macro genTests(): untyped =
 70    let
 71      pwd = staticExec("pwd").strip
 72      absolutePath = '"' & (pwd / testSuiteFolder) & '"'
 73    echo "[tparser] Generating tests from " & absolutePath
 74    discard staticExec("git submodule init && git submodule update --remote")
 75  
 76    var ignored = toHashSet([".git", "name", "tags", "meta"])
 77    
 78    proc genTest(target: var NimNode, dirPath: string, testId: string) {.compileTime.} =
 79      let title = slurp(dirPath / "===")
 80      let isErrorTest = fileExists(dirPath / "error")
 81      let testName = strip(title) & " [" & testId & ']'
 82      
 83      # TODO: this code executes the test at compile time.
 84      # sadly it doesn't work currently since the VM doesn't support
 85      # closure iterators (in parseEventStream).
 86      #
 87      #let staticIn = slurp(dirPath / "in.yaml")
 88      #let staticExpected = slurp(dirPath / "test.event")
 89      #var parser: YamlParser
 90      #parser.init()
 91      #var actual = parser.parse(staticIn)
 92      #var expected = parseEventString(staticExpected)
 93      #
 94      #if not doParserTest(expected, actual, isErrorTest):
 95      # target.add(newCall("test", newLit(testName & " [comptime]"), newCall("fail")))
 96      
 97      target.add(newCall("test",
 98          newLit(testName), newCall("doAssert", newCall("parserTest",
 99          newLit(dirPath), newLit(isErrorTest)))))
100    
101    result = newStmtList()
102    
103    # walkDir for some crude reason does not work with travis build
104    let dirItems = staticExec("ls -1d " & absolutePath / "*")
105    for dirPath in dirItems.splitLines():
106      if dirPath.strip.len == 0: continue
107      let testId = dirPath[^4..^1]
108      if ignored.contains(testId): continue
109      if fileExists(dirPath / "==="):
110        genTest(result, dirPath, testId)
111      else:
112        let testItems = staticExec("ls -1d " & dirPath / "*")
113        var start = len(dirPath) + 1
114        for itemPath in testItems.splitLines():
115          if itemPath.strip.len == 0: continue
116          let testItemId = testId / itemPath[start..^1]
117          if fileExists(itemPath / "==="):
118            genTest(result, itemPath, testItemId)
119          
120      
121    result = newCall("suite", newLit("Parser Tests (from yaml-test-suite)"), result)
122  
123  genTests()