specs.nim 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #
  2. #
  3. # Nim Tester
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. import parseutils, strutils, os, osproc, streams, parsecfg
  10. var compilerPrefix* = "compiler" / "nim" ## built via ./koch tests
  11. let isTravis* = existsEnv("TRAVIS")
  12. let isAppVeyor* = existsEnv("APPVEYOR")
  13. type
  14. TTestAction* = enum
  15. actionRun = "run"
  16. actionCompile = "compile"
  17. actionReject = "reject"
  18. TOutputCheck* = enum
  19. ocIgnore = "ignore"
  20. ocEqual = "equal"
  21. ocSubstr = "substr"
  22. TResultEnum* = enum
  23. reNimcCrash, # nim compiler seems to have crashed
  24. reMsgsDiffer, # error messages differ
  25. reFilesDiffer, # expected and given filenames differ
  26. reLinesDiffer, # expected and given line numbers differ
  27. reOutputsDiffer,
  28. reExitcodesDiffer,
  29. reInvalidPeg,
  30. reCodegenFailure,
  31. reCodeNotFound,
  32. reExeNotFound,
  33. reInstallFailed # package installation failed
  34. reBuildFailed # package building failed
  35. reDisabled, # test is disabled
  36. reJoined, # test is disabled because it was joined into the megatest
  37. reSuccess # test was successful
  38. reInvalidSpec # test had problems to parse the spec
  39. TTarget* = enum
  40. targetC = "C"
  41. targetCpp = "C++"
  42. targetObjC = "ObjC"
  43. targetJS = "JS"
  44. TSpec* = object
  45. action*: TTestAction
  46. file*, cmd*: string
  47. input*: string
  48. outputCheck*: TOutputCheck
  49. sortoutput*: bool
  50. output*: string
  51. line*, column*: int
  52. tfile*: string
  53. tline*, tcolumn*: int
  54. exitCode*: int
  55. msg*: string
  56. ccodeCheck*: string
  57. maxCodeSize*: int
  58. err*: TResultEnum
  59. targets*: set[TTarget]
  60. nimout*: string
  61. parseErrors*: string # when the spec definition is invalid, this is not empty.
  62. unjoinable*: bool
  63. proc getCmd*(s: TSpec): string =
  64. if s.cmd.len == 0:
  65. result = compilerPrefix & " $target --hints:on -d:testing --nimblePath:tests/deps $options $file"
  66. else:
  67. result = s.cmd
  68. const
  69. targetToExt*: array[TTarget, string] = ["c", "cpp", "m", "js"]
  70. targetToCmd*: array[TTarget, string] = ["c", "cpp", "objc", "js"]
  71. when not declared(parseCfgBool):
  72. # candidate for the stdlib:
  73. proc parseCfgBool(s: string): bool =
  74. case normalize(s)
  75. of "y", "yes", "true", "1", "on": result = true
  76. of "n", "no", "false", "0", "off": result = false
  77. else: raise newException(ValueError, "cannot interpret as a bool: " & s)
  78. proc extractSpec(filename: string): string =
  79. const tripleQuote = "\"\"\""
  80. var x = readFile(filename).string
  81. var a = x.find(tripleQuote)
  82. var b = x.find(tripleQuote, a+3)
  83. # look for """ only in the first section
  84. if a >= 0 and b > a and a < 40:
  85. result = x.substr(a+3, b-1).replace("'''", tripleQuote)
  86. else:
  87. #echo "warning: file does not contain spec: " & filename
  88. result = ""
  89. when not defined(nimhygiene):
  90. {.pragma: inject.}
  91. proc parseTargets*(value: string): set[TTarget] =
  92. for v in value.normalize.splitWhitespace:
  93. case v
  94. of "c": result.incl(targetC)
  95. of "cpp", "c++": result.incl(targetCpp)
  96. of "objc": result.incl(targetObjC)
  97. of "js": result.incl(targetJS)
  98. else: echo "target ignored: " & v
  99. proc addLine*(self: var string; a: string) =
  100. self.add a
  101. self.add "\n"
  102. proc addLine*(self: var string; a,b: string) =
  103. self.add a
  104. self.add b
  105. self.add "\n"
  106. proc parseSpec*(filename: string): TSpec =
  107. result.file = filename
  108. let specStr = extractSpec(filename)
  109. var ss = newStringStream(specStr)
  110. var p: CfgParser
  111. open(p, ss, filename, 1)
  112. while true:
  113. var e = next(p)
  114. case e.kind
  115. of cfgKeyValuePair:
  116. case normalize(e.key)
  117. of "action":
  118. case e.value.normalize
  119. of "compile":
  120. result.action = actionCompile
  121. of "run":
  122. result.action = actionRun
  123. of "reject":
  124. result.action = actionReject
  125. else:
  126. result.parseErrors.addLine "cannot interpret as action: ", e.value
  127. of "file":
  128. if result.msg.len == 0 and result.nimout.len == 0:
  129. result.parseErrors.addLine "errormsg or msg needs to be specified before file"
  130. result.file = e.value
  131. of "line":
  132. if result.msg.len == 0 and result.nimout.len == 0:
  133. result.parseErrors.addLine "errormsg, msg or nimout needs to be specified before line"
  134. discard parseInt(e.value, result.line)
  135. of "column":
  136. if result.msg.len == 0 and result.nimout.len == 0:
  137. result.parseErrors.addLine "errormsg or msg needs to be specified before column"
  138. discard parseInt(e.value, result.column)
  139. of "tfile":
  140. result.tfile = e.value
  141. of "tline":
  142. discard parseInt(e.value, result.tline)
  143. of "tcolumn":
  144. discard parseInt(e.value, result.tcolumn)
  145. of "output":
  146. result.outputCheck = ocEqual
  147. result.output = strip(e.value)
  148. of "input":
  149. result.input = e.value
  150. of "outputsub":
  151. result.outputCheck = ocSubstr
  152. result.output = strip(e.value)
  153. of "sortoutput":
  154. try:
  155. result.sortoutput = parseCfgBool(e.value)
  156. except:
  157. result.parseErrors.addLine getCurrentExceptionMsg()
  158. of "exitcode":
  159. discard parseInt(e.value, result.exitCode)
  160. result.action = actionRun
  161. of "msg":
  162. result.msg = e.value
  163. if result.action != actionRun:
  164. result.action = actionCompile
  165. of "errormsg", "errmsg":
  166. result.msg = e.value
  167. result.action = actionReject
  168. of "nimout":
  169. result.nimout = e.value
  170. of "joinable":
  171. result.unjoinable = not parseCfgBool(e.value)
  172. of "disabled":
  173. case e.value.normalize
  174. of "y", "yes", "true", "1", "on": result.err = reDisabled
  175. of "n", "no", "false", "0", "off": discard
  176. of "win", "windows":
  177. when defined(windows): result.err = reDisabled
  178. of "linux":
  179. when defined(linux): result.err = reDisabled
  180. of "bsd":
  181. when defined(bsd): result.err = reDisabled
  182. of "macosx":
  183. when defined(macosx): result.err = reDisabled
  184. of "unix":
  185. when defined(unix): result.err = reDisabled
  186. of "posix":
  187. when defined(posix): result.err = reDisabled
  188. of "travis":
  189. if isTravis: result.err = reDisabled
  190. of "appveyor":
  191. if isAppVeyor: result.err = reDisabled
  192. else:
  193. result.parseErrors.addLine "cannot interpret as a bool: ", e.value
  194. of "cmd":
  195. if e.value.startsWith("nim "):
  196. result.cmd = compilerPrefix & e.value[3..^1]
  197. else:
  198. result.cmd = e.value
  199. of "ccodecheck":
  200. result.ccodeCheck = e.value
  201. of "maxcodesize":
  202. discard parseInt(e.value, result.maxCodeSize)
  203. of "target", "targets":
  204. for v in e.value.normalize.splitWhitespace:
  205. case v
  206. of "c":
  207. result.targets.incl(targetC)
  208. of "cpp", "c++":
  209. result.targets.incl(targetCpp)
  210. of "objc":
  211. result.targets.incl(targetObjC)
  212. of "js":
  213. result.targets.incl(targetJS)
  214. else:
  215. result.parseErrors.addLine "cannot interpret as a target: ", e.value
  216. else:
  217. result.parseErrors.addLine "invalid key for test spec: ", e.key
  218. of cfgSectionStart:
  219. result.parseErrors.addLine "section ignored: ", e.section
  220. of cfgOption:
  221. result.parseErrors.addLine "command ignored: ", e.key & ": " & e.value
  222. of cfgError:
  223. result.parseErrors.addLine e.msg
  224. of cfgEof:
  225. break
  226. close(p)