specs.nim 7.7 KB

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