summary refs log tree commit diff stats
path: root/testament/specs.nim
diff options
context:
space:
mode:
Diffstat (limited to 'testament/specs.nim')
-rw-r--r--testament/specs.nim257
1 files changed, 147 insertions, 110 deletions
diff --git a/testament/specs.nim b/testament/specs.nim
index 86fc8bed4..df12db543 100644
--- a/testament/specs.nim
+++ b/testament/specs.nim
@@ -9,21 +9,21 @@
 
 import parseutils, strutils, os, osproc, streams, parsecfg
 
-
 var compilerPrefix* = "compiler" / "nim"
 
 let isTravis* = existsEnv("TRAVIS")
 let isAppVeyor* = existsEnv("APPVEYOR")
 
-proc cmdTemplate*(): string =
-  compilerPrefix & " $target --hints:on -d:testing --nimblePath:tests/deps $options $file"
-
 type
   TTestAction* = enum
     actionRun = "run"
     actionCompile = "compile"
     actionReject = "reject"
-    actionRunNoSpec = "runNoSpec"
+
+  TOutputCheck* = enum
+    ocIgnore = "ignore"
+    ocEqual  = "equal"
+    ocSubstr = "substr"
 
   TResultEnum* = enum
     reNimcCrash,     # nim compiler seems to have crashed
@@ -38,8 +38,10 @@ type
     reExeNotFound,
     reInstallFailed     # package installation failed
     reBuildFailed       # package building failed
-    reIgnored,          # test is ignored
+    reDisabled,         # test is disabled
+    reJoined,           # test is disabled because it was joined into the megatest
     reSuccess           # test was successful
+    reInvalidSpec       # test had problems to parse the spec
 
   TTarget* = enum
     targetC = "C"
@@ -51,7 +53,9 @@ type
     action*: TTestAction
     file*, cmd*: string
     input*: string
-    outp*: string
+    outputCheck*: TOutputCheck
+    sortoutput*: bool
+    output*: string
     line*, column*: int
     tfile*: string
     tline*, tcolumn*: int
@@ -60,9 +64,16 @@ type
     ccodeCheck*: string
     maxCodeSize*: int
     err*: TResultEnum
-    substr*, sortoutput*: bool
     targets*: set[TTarget]
     nimout*: string
+    parseErrors*: string # when the spec definition is invalid, this is not empty.
+    unjoinable*: bool
+
+proc getCmd*(s: TSpec): string =
+  if s.cmd.len == 0:
+    result = compilerPrefix & " $target --hints:on -d:testing --nimblePath:tests/deps $options $file"
+  else:
+    result = s.cmd
 
 const
   targetToExt*: array[TTarget, string] = ["c", "cpp", "m", "js"]
@@ -91,33 +102,6 @@ proc extractSpec(filename: string): string =
 when not defined(nimhygiene):
   {.pragma: inject.}
 
-template parseSpecAux(fillResult: untyped) =
-  var ss = newStringStream(extractSpec(filename))
-  var p {.inject.}: CfgParser
-  open(p, ss, filename, 1)
-  while true:
-    var e {.inject.} = next(p)
-    case e.kind
-    of cfgEof: break
-    of cfgSectionStart, cfgOption, cfgError:
-      echo ignoreMsg(p, e)
-    of cfgKeyValuePair:
-      fillResult
-  close(p)
-
-proc specDefaults*(result: var TSpec) =
-  result.msg = ""
-  result.outp = ""
-  result.nimout = ""
-  result.ccodeCheck = ""
-  result.cmd = cmdTemplate()
-  result.line = 0
-  result.column = 0
-  result.tfile = ""
-  result.tline = 0
-  result.tcolumn = 0
-  result.maxCodeSize = 0
-
 proc parseTargets*(value: string): set[TTarget] =
   for v in value.normalize.splitWhitespace:
     case v
@@ -127,81 +111,134 @@ proc parseTargets*(value: string): set[TTarget] =
     of "js": result.incl(targetJS)
     else: echo "target ignored: " & v
 
+proc addLine*(self: var string; a: string) =
+  self.add a
+  self.add "\n"
+
+proc addLine*(self: var string; a,b: string) =
+  self.add a
+  self.add b
+  self.add "\n"
+
 proc parseSpec*(filename: string): TSpec =
-  specDefaults(result)
   result.file = filename
-  parseSpecAux:
-    case normalize(e.key)
-    of "action":
-      case e.value.normalize
-      of "compile": result.action = actionCompile
-      of "run": result.action = actionRun
-      of "reject": result.action = actionReject
-      else: echo ignoreMsg(p, e)
-    of "file": result.file = e.value
-    of "line": discard parseInt(e.value, result.line)
-    of "column": discard parseInt(e.value, result.column)
-    of "tfile": result.tfile = e.value
-    of "tline": discard parseInt(e.value, result.tline)
-    of "tcolumn": discard parseInt(e.value, result.tcolumn)
-    of "output":
-      result.action = actionRun
-      result.outp = e.value
-    of "input":
-      result.input = e.value
-    of "outputsub":
-      result.action = actionRun
-      result.outp = e.value
-      result.substr = true
-    of "sortoutput":
-      result.sortoutput = parseCfgBool(e.value)
-    of "exitcode":
-      discard parseInt(e.value, result.exitCode)
-      result.action = actionRun
-    of "msg":
-      result.msg = e.value
-      if result.action != actionRun:
-        result.action = actionCompile
-    of "errormsg", "errmsg":
-      result.msg = e.value
-      result.action = actionReject
-    of "nimout":
-      result.nimout = e.value
-    of "disabled":
-      case e.value.normalize
-      of "y", "yes", "true", "1", "on": result.err = reIgnored
-      of "n", "no", "false", "0", "off": discard
-      of "win", "windows":
-        when defined(windows): result.err = reIgnored
-      of "linux":
-        when defined(linux): result.err = reIgnored
-      of "bsd":
-        when defined(bsd): result.err = reIgnored
-      of "macosx":
-        when defined(macosx): result.err = reIgnored
-      of "unix":
-        when defined(unix): result.err = reIgnored
-      of "posix":
-        when defined(posix): result.err = reIgnored
-      of "travis":
-        if isTravis: result.err = reIgnored
-      of "appveyor":
-        if isAppVeyor: result.err = reIgnored
-      else:
-        raise newException(ValueError, "cannot interpret as a bool: " & e.value)
-    of "cmd":
-      if e.value.startsWith("nim "):
-        result.cmd = compilerPrefix & e.value[3..^1]
+  let specStr = extractSpec(filename)
+  var ss = newStringStream(specStr)
+  var p: CfgParser
+  open(p, ss, filename, 1)
+  while true:
+    var e = next(p)
+    case e.kind
+    of cfgKeyValuePair:
+      case normalize(e.key)
+      of "action":
+        case e.value.normalize
+        of "compile":
+          result.action = actionCompile
+        of "run":
+          result.action = actionRun
+        of "reject":
+          result.action = actionReject
+        else:
+          result.parseErrors.addLine "cannot interpret as action: ", e.value
+      of "file":
+        if result.msg.len == 0 and result.nimout.len == 0:
+          result.parseErrors.addLine "errormsg or msg needs to be specified before file"
+        result.file = e.value
+      of "line":
+        if result.msg.len == 0 and result.nimout.len == 0:
+          result.parseErrors.addLine "errormsg, msg or nimout needs to be specified before line"
+        discard parseInt(e.value, result.line)
+      of "column":
+        if result.msg.len == 0 and result.nimout.len == 0:
+          result.parseErrors.addLine "errormsg or msg needs to be specified before column"
+        discard parseInt(e.value, result.column)
+      of "tfile":
+        result.tfile = e.value
+      of "tline":
+        discard parseInt(e.value, result.tline)
+      of "tcolumn":
+        discard parseInt(e.value, result.tcolumn)
+      of "output":
+        result.outputCheck = ocEqual
+        result.output = strip(e.value)
+      of "input":
+        result.input = e.value
+      of "outputsub":
+        result.outputCheck = ocSubstr
+        result.output = strip(e.value)
+      of "sortoutput":
+        try:
+          result.sortoutput  = parseCfgBool(e.value)
+        except:
+          result.parseErrors.addLine getCurrentExceptionMsg()
+      of "exitcode":
+        discard parseInt(e.value, result.exitCode)
+        result.action = actionRun
+      of "msg":
+        result.msg = e.value
+        if result.action != actionRun:
+          result.action = actionCompile
+      of "errormsg", "errmsg":
+        result.msg = e.value
+        result.action = actionReject
+      of "nimout":
+        result.nimout = e.value
+      of "joinable":
+        result.unjoinable = not parseCfgBool(e.value)
+      of "disabled":
+        case e.value.normalize
+        of "y", "yes", "true", "1", "on": result.err = reDisabled
+        of "n", "no", "false", "0", "off": discard
+        of "win", "windows":
+          when defined(windows): result.err = reDisabled
+        of "linux":
+          when defined(linux): result.err = reDisabled
+        of "bsd":
+          when defined(bsd): result.err = reDisabled
+        of "macosx":
+          when defined(macosx): result.err = reDisabled
+        of "unix":
+          when defined(unix): result.err = reDisabled
+        of "posix":
+          when defined(posix): result.err = reDisabled
+        of "travis":
+          if isTravis: result.err = reDisabled
+        of "appveyor":
+          if isAppVeyor: result.err = reDisabled
+        else:
+          result.parseErrors.addLine "cannot interpret as a bool: ", e.value
+      of "cmd":
+        if e.value.startsWith("nim "):
+          result.cmd = compilerPrefix & e.value[3..^1]
+        else:
+          result.cmd = e.value
+      of "ccodecheck":
+        result.ccodeCheck = e.value
+      of "maxcodesize":
+        discard parseInt(e.value, result.maxCodeSize)
+      of "target", "targets":
+        for v in e.value.normalize.splitWhitespace:
+          case v
+          of "c":
+            result.targets.incl(targetC)
+          of "cpp", "c++":
+            result.targets.incl(targetCpp)
+          of "objc":
+            result.targets.incl(targetObjC)
+          of "js":
+            result.targets.incl(targetJS)
+          else:
+            result.parseErrors.addLine "cannot interpret as a target: ", e.value
       else:
-        result.cmd = e.value
-    of "ccodecheck": result.ccodeCheck = e.value
-    of "maxcodesize": discard parseInt(e.value, result.maxCodeSize)
-    of "target", "targets":
-      for v in e.value.normalize.splitWhitespace:
-        case v
-        of "c": result.targets.incl(targetC)
-        of "cpp", "c++": result.targets.incl(targetCpp)
-        of "objc": result.targets.incl(targetObjC)
-        of "js": result.targets.incl(targetJS)
-        else: echo ignoreMsg(p, e)
-    else: echo ignoreMsg(p, e)
+        result.parseErrors.addLine "invalid key for test spec: ", e.key
+
+    of cfgSectionStart:
+      result.parseErrors.addLine "section ignored: ", e.section
+    of cfgOption:
+      result.parseErrors.addLine "command ignored: ", e.key & ": " & e.value
+    of cfgError:
+      result.parseErrors.addLine e.msg
+    of cfgEof:
+      break
+  close(p)