trunner.nim 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. discard """
  2. targets: "c cpp"
  3. joinable: false
  4. """
  5. ## tests that don't quite fit the mold and are easier to handle via `execCmdEx`
  6. ## A few others could be added to here to simplify code.
  7. ## Note: this test is a bit slow but tests a lot of things; please don't disable.
  8. ## Note: if needed, we could use `matrix: "-d:case1; -d:case2"` to split this
  9. ## into several independent tests while retaining the common test helpers.
  10. import std/[strformat,os,osproc,unittest,compilesettings]
  11. from std/sequtils import toSeq,mapIt
  12. from std/algorithm import sorted
  13. import stdtest/[specialpaths, unittest_light]
  14. from std/private/globs import nativeToUnixPath
  15. from strutils import startsWith, strip, removePrefix
  16. from std/sugar import dup
  17. import "$lib/../compiler/nimpaths"
  18. proc isDots(a: string): bool =
  19. ## test for `hintProcessing` dots
  20. a.startsWith(".") and a.strip(chars = {'.'}) == ""
  21. const
  22. nim = getCurrentCompilerExe()
  23. mode = querySetting(backend)
  24. nimcache = buildDir / "nimcacheTrunner"
  25. # instead of `querySetting(nimcacheDir)`, avoids stomping on other parallel tests
  26. proc runNimCmd(file, options = "", rtarg = ""): auto =
  27. let fileabs = testsDir / file.unixToNativePath
  28. # doAssert fileabs.fileExists, fileabs # disabled because this allows passing `nim r --eval:code fakefile`
  29. let cmd = fmt"{nim} {mode} --hint:all:off {options} {fileabs} {rtarg}"
  30. result = execCmdEx(cmd)
  31. when false: # for debugging
  32. echo cmd
  33. echo result[0] & "\n" & $result[1]
  34. proc runNimCmdChk(file, options = "", rtarg = "", status = 0): string =
  35. let (ret, status2) = runNimCmd(file, options, rtarg = rtarg)
  36. doAssert status2 == status, $(file, options, status, status2) & "\n" & ret
  37. ret
  38. proc genShellCmd(filename: string): string =
  39. let filename = filename.quoteShell
  40. when defined(windows): "cmd /c " & filename # or "cmd /c " ?
  41. else: "sh " & filename
  42. when defined(nimTrunnerFfi):
  43. block: # mevalffi
  44. when defined(openbsd):
  45. #[
  46. openbsd defines `#define stderr (&__sF[2])` which makes it cumbersome
  47. for dlopen'ing inside `importcSymbol`. Instead of adding special rules
  48. inside `importcSymbol` to handle this, we disable just the part that's
  49. not working and will provide a more general, clean fix in future PR.
  50. ]#
  51. var opt = "-d:nimEvalffiStderrWorkaround"
  52. let prefix = ""
  53. else:
  54. var opt = ""
  55. let prefix = """
  56. hello world stderr
  57. hi stderr
  58. """
  59. let output = runNimCmdChk("vm/mevalffi.nim", fmt"{opt} --warnings:off --experimental:compiletimeFFI")
  60. doAssert output == fmt"""
  61. {prefix}foo
  62. foo:100
  63. foo:101
  64. foo:102:103
  65. foo:102:103:104
  66. foo:0.03:asdf:103:105
  67. ret=[s1:foobar s2:foobar age:25 pi:3.14]
  68. """, output
  69. elif not defined(nimTestsTrunnerDebugging):
  70. # don't run twice the same test with `nimTrunnerFfi`
  71. # use `-d:nimTestsTrunnerDebugging` for debugging convenience when you want to just run 1 test
  72. import std/strutils
  73. import std/json
  74. template check2(msg) = doAssert msg in output, output
  75. block: # tests with various options `nim doc --project --index --docroot`
  76. # regression tests for issues and PRS: #14376 #13223 #6583 ##13647
  77. let file = testsDir / "nimdoc/sub/mmain.nim"
  78. let mainFname = "mmain.html"
  79. let htmldocsDirCustom = nimcache / "htmldocsCustom"
  80. let docroot = testsDir / "nimdoc"
  81. let options = [
  82. 0: "--project",
  83. 1: "--project --docroot",
  84. 2: "",
  85. 3: fmt"--outDir:{htmldocsDirCustom}",
  86. 4: fmt"--docroot:{docroot}",
  87. 5: "--project --useNimcache",
  88. 6: "--index:off",
  89. ]
  90. for i in 0..<options.len:
  91. let htmldocsDir = case i
  92. of 3: htmldocsDirCustom
  93. of 5: nimcache / htmldocsDirname
  94. else: file.parentDir / htmldocsDirname
  95. var cmd = fmt"{nim} doc --index:on --filenames:abs --hint:successX:on --nimcache:{nimcache} {options[i]} {file}"
  96. removeDir(htmldocsDir)
  97. let (outp, exitCode) = execCmdEx(cmd)
  98. check exitCode == 0
  99. let ret = toSeq(walkDirRec(htmldocsDir, relative=true)).mapIt(it.nativeToUnixPath).sorted.join("\n")
  100. let context = $(i, ret, cmd)
  101. case i
  102. of 0,5:
  103. let htmlFile = htmldocsDir/mainFname
  104. check htmlFile in outp # sanity check for `hintSuccessX`
  105. assertEquals ret, fmt"""
  106. {dotdotMangle}/imp.html
  107. {dotdotMangle}/imp.idx
  108. {docHackJsFname}
  109. imp.html
  110. imp.idx
  111. imp2.html
  112. imp2.idx
  113. {mainFname}
  114. mmain.idx
  115. {nimdocOutCss}
  116. {theindexFname}""", context
  117. of 1: assertEquals ret, fmt"""
  118. {docHackJsFname}
  119. {nimdocOutCss}
  120. tests/nimdoc/imp.html
  121. tests/nimdoc/imp.idx
  122. tests/nimdoc/sub/imp.html
  123. tests/nimdoc/sub/imp.idx
  124. tests/nimdoc/sub/imp2.html
  125. tests/nimdoc/sub/imp2.idx
  126. tests/nimdoc/sub/{mainFname}
  127. tests/nimdoc/sub/mmain.idx
  128. {theindexFname}"""
  129. of 2, 3: assertEquals ret, fmt"""
  130. {docHackJsFname}
  131. {mainFname}
  132. mmain.idx
  133. {nimdocOutCss}""", context
  134. of 4: assertEquals ret, fmt"""
  135. {docHackJsFname}
  136. {nimdocOutCss}
  137. sub/{mainFname}
  138. sub/mmain.idx""", context
  139. of 6: assertEquals ret, fmt"""
  140. {mainFname}
  141. {nimdocOutCss}""", context
  142. else: doAssert false
  143. block: # mstatic_assert
  144. let (output, exitCode) = runNimCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad")
  145. check2 "sizeof(bool) == 2"
  146. check exitCode != 0
  147. block: # ABI checks
  148. let file = "misc/msizeof5.nim"
  149. block:
  150. discard runNimCmdChk(file, "-d:checkAbi")
  151. block:
  152. let (output, exitCode) = runNimCmd(file, "-d:checkAbi -d:caseBad")
  153. # on platforms that support _StaticAssert natively, errors will show full context, e.g.:
  154. # error: static_assert failed due to requirement 'sizeof(unsigned char) == 8'
  155. # "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]"
  156. check2 "sizeof(unsigned char) == 8"
  157. check2 "sizeof(struct Foo2) == 1"
  158. check2 "sizeof(Foo5) == 16"
  159. check2 "sizeof(Foo5) == 3"
  160. check2 "sizeof(struct Foo6) == "
  161. check exitCode != 0
  162. import streams
  163. block: # stdin input
  164. let nimcmd = fmt"""{nim} r --hints:off - -firstparam "-second param" """
  165. let expected = """@["-firstparam", "-second param"]"""
  166. block:
  167. let p = startProcess(nimcmd, options = {poEvalCommand})
  168. p.inputStream.write("import os; echo commandLineParams()")
  169. p.inputStream.close
  170. var output = p.outputStream.readAll
  171. let error = p.errorStream.readAll
  172. doAssert p.waitForExit == 0
  173. doAssert error.len == 0, $error
  174. output.stripLineEnd
  175. check output == expected
  176. p.errorStream.close
  177. p.outputStream.close
  178. block:
  179. when defined posix:
  180. # xxx on windows, `poEvalCommand` should imply `/cmd`, (which should
  181. # make this work), but currently doesn't
  182. let cmd = fmt"""echo "import os; echo commandLineParams()" | {nimcmd}"""
  183. var (output, exitCode) = execCmdEx(cmd)
  184. output.stripLineEnd
  185. check output == expected
  186. doAssert exitCode == 0
  187. block: # nim doc --backend:$backend --doccmd:$cmd
  188. # test for https://github.com/nim-lang/Nim/issues/13129
  189. # test for https://github.com/nim-lang/Nim/issues/13891
  190. let file = testsDir / "nimdoc/m13129.nim"
  191. for backend in fmt"{mode} js".split:
  192. # pending #14343 this fails on windows: --doccmd:"-d:m13129Foo2 --hints:off"
  193. let cmd = fmt"""{nim} doc -b:{backend} --nimcache:{nimcache} -d:m13129Foo1 "--doccmd:-d:m13129Foo2 --hints:off" --usenimcache --hints:off {file}"""
  194. check execCmdEx(cmd) == (&"ok1:{backend}\nok2: backend: {backend}\n", 0)
  195. # checks that --usenimcache works with `nim doc`
  196. check fileExists(nimcache / "htmldocs/m13129.html")
  197. block: # mak sure --backend works with `nim r`
  198. let cmd = fmt"{nim} r --backend:{mode} --hints:off --nimcache:{nimcache} {file}"
  199. check execCmdEx(cmd) == ("ok3\n", 0)
  200. block: # nim jsondoc # bug #20132
  201. let file = testsDir / "misc/mjsondoc.nim"
  202. let output = "nimcache_tjsondoc.json"
  203. defer: removeFile(output)
  204. let (msg, exitCode) = execCmdEx(fmt"{nim} jsondoc -o:{output} {file}")
  205. doAssert exitCode == 0, msg
  206. let data = parseJson(readFile(output))["entries"]
  207. doAssert data.len == 5
  208. let doSomething = data[0]
  209. doAssert doSomething["name"].getStr == "doSomething"
  210. doAssert doSomething["type"].getStr == "skProc"
  211. doAssert doSomething["line"].getInt == 1
  212. doAssert doSomething["col"].getInt == 0
  213. doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}"
  214. let foo2 = data[4]
  215. doAssert $foo2["signature"] == """{"arguments":[{"name":"x","type":"T"},{"name":"y","type":"U"},{"name":"z","type":"M"}],"genericParams":[{"name":"T","types":"int"},{"name":"M","types":"string"},{"name":"U"}]}"""
  216. block: # nim jsondoc # bug #11953
  217. let file = testsDir / "misc/mjsondoc.nim"
  218. let destDir = testsDir / "misc/htmldocs"
  219. removeDir(destDir)
  220. defer: removeDir(destDir)
  221. let (msg, exitCode) = execCmdEx(fmt"{nim} jsondoc {file}")
  222. doAssert exitCode == 0, msg
  223. let data = parseJson(readFile(destDir / "mjsondoc.json"))["entries"]
  224. doAssert data.len == 5
  225. let doSomething = data[0]
  226. doAssert doSomething["name"].getStr == "doSomething"
  227. doAssert doSomething["type"].getStr == "skProc"
  228. doAssert doSomething["line"].getInt == 1
  229. doAssert doSomething["col"].getInt == 0
  230. doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}"
  231. block: # further issues with `--backend`
  232. let file = testsDir / "misc/mbackend.nim"
  233. var cmd = fmt"{nim} doc -b:cpp --hints:off --nimcache:{nimcache} {file}"
  234. check execCmdEx(cmd) == ("", 0)
  235. cmd = fmt"{nim} check -b:c -b:cpp --hints:off --nimcache:{nimcache} {file}"
  236. check execCmdEx(cmd) == ("", 0)
  237. # issue https://github.com/timotheecour/Nim/issues/175
  238. cmd = fmt"{nim} c -b:js -b:cpp --hints:off --nimcache:{nimcache} {file}"
  239. check execCmdEx(cmd) == ("", 0)
  240. block: # some importc tests
  241. # issue #14314
  242. let file = testsDir / "misc/mimportc.nim"
  243. let cmd = fmt"{nim} r -b:cpp --hints:off --nimcache:{nimcache} --warningAsError:ProveInit {file}"
  244. check execCmdEx(cmd) == ("witness\n", 0)
  245. block: # bug #20149
  246. let file = testsDir / "misc/m20149.nim"
  247. let cmd = fmt"{nim} r --hints:off --nimcache:{nimcache} --hintAsError:XDeclaredButNotUsed {file}"
  248. check execCmdEx(cmd) == ("12\n", 0)
  249. block: # bug #15316
  250. when not defined(windows):
  251. # This never worked reliably on Windows. Needs further investigation but it is hard to reproduce.
  252. # Looks like a mild stack corruption when bailing out of nested exception handling.
  253. let file = testsDir / "misc/m15316.nim"
  254. let cmd = fmt"{nim} check --hints:off --nimcache:{nimcache} {file}"
  255. check execCmdEx(cmd) == ("m15316.nim(1, 15) Error: expression expected, but found \')\'\nm15316.nim(2, 1) Error: expected: \':\', but got: \'[EOF]\'\nm15316.nim(2, 1) Error: expression expected, but found \'[EOF]\'\nm15316.nim(2, 1) " &
  256. "Error: expected: \')\', but got: \'[EOF]\'\nError: illformed AST: \n", 1)
  257. block: # config.nims, nim.cfg, hintConf, bug #16557
  258. let cmd = fmt"{nim} r --hint:all:off --hint:conf tests/newconfig/bar/mfoo.nim"
  259. let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut})
  260. doAssert exitCode == 0
  261. let dir = getCurrentDir()
  262. let files = """
  263. tests/config.nims
  264. tests/newconfig/bar/nim.cfg
  265. tests/newconfig/bar/config.nims
  266. tests/newconfig/bar/mfoo.nim.cfg
  267. tests/newconfig/bar/mfoo.nims""".splitLines
  268. var expected = ""
  269. for a in files:
  270. let b = dir / a
  271. expected.add &"Hint: used config file '{b}' [Conf]\n"
  272. doAssert outp.endsWith expected, outp & "\n" & expected
  273. block: # bug #8219
  274. let file = "tests/newconfig/mconfigcheck.nims"
  275. let cmd = fmt"{nim} check --hints:off {file}"
  276. check execCmdEx(cmd) == ("", 0)
  277. block: # mfoo2.customext
  278. let filename = testsDir / "newconfig/foo2/mfoo2.customext"
  279. let cmd = fmt"{nim} e --hint:conf {filename}"
  280. let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut})
  281. doAssert exitCode == 0
  282. var expected = &"Hint: used config file '{filename}' [Conf]\n"
  283. doAssert outp.endsWith "123" & "\n" & expected
  284. block: # nim --eval
  285. let opt = "--hints:off"
  286. check fmt"""{nim} {opt} --eval:"echo defined(nimscript)"""".execCmdEx == ("true\n", 0)
  287. check fmt"""{nim} r {opt} --eval:"echo defined(c)"""".execCmdEx == ("true\n", 0)
  288. check fmt"""{nim} r -b:js {opt} --eval:"echo defined(js)"""".execCmdEx == ("true\n", 0)
  289. block: # `hintProcessing` dots should not interfere with `static: echo` + friends
  290. let cmd = fmt"""{nim} r --hint:all:off --hint:processing -f --eval:"static: echo 1+1""""
  291. let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut})
  292. template check3(cond) = doAssert cond, $(outp,)
  293. doAssert exitCode == 0
  294. let lines = outp.splitLines
  295. check3 lines.len == 3
  296. when not defined(windows): # xxx: on windows, dots not properly handled, gives: `....2\n\n`
  297. check3 lines[0].isDots
  298. check3 lines[1] == "2"
  299. check3 lines[2] == ""
  300. else:
  301. check3 "2" in outp
  302. block: # nim secret
  303. let opt = "--hint:all:off --hint:processing"
  304. template check3(cond) = doAssert cond, $(outp,)
  305. for extra in ["", "--stdout"]:
  306. let cmd = fmt"""{nim} secret {opt} {extra}"""
  307. # xxx minor bug: `nim --hint:QuitCalled:off secret` ignores the hint cmdline flag
  308. template run(input2): untyped =
  309. execCmdEx(cmd, options = {poStdErrToStdOut}, input = input2)
  310. block:
  311. let (outp, exitCode) = run """echo 1+2; import strutils; echo strip(" ab "); quit()"""
  312. let lines = outp.splitLines
  313. when not defined(windows):
  314. check3 lines.len == 5
  315. check3 lines[0].isDots
  316. # check3 lines[1].isDots # todo nim secret might use parsing pipeline
  317. check3 lines[2].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise`
  318. check3 lines[3] == "ab"
  319. check3 lines[4] == ""
  320. else:
  321. check3 "3" in outp
  322. check3 "ab" in outp
  323. doAssert exitCode == 0
  324. block:
  325. let (outp, exitCode) = run "echo 1+2; quit(2)"
  326. check3 "3" in outp
  327. doAssert exitCode == 2
  328. block: # nimBetterRun
  329. let file = "misc/mbetterrun.nim"
  330. const nimcache2 = buildDir / "D20210423T185116"
  331. removeDir nimcache2
  332. # related to `-d:nimBetterRun`
  333. let opt = fmt"-r --usenimcache --nimcache:{nimcache2}"
  334. var ret = ""
  335. for a in @["v1", "v2", "v1", "v3"]:
  336. ret.add runNimCmdChk(file, fmt"{opt} -d:mbetterrunVal:{a}")
  337. ret.add runNimCmdChk(file, fmt"{opt} -d:mbetterrunVal:v2", rtarg = "arg1 arg2")
  338. # rt arguments should not cause a recompilation
  339. doAssert ret == """
  340. compiling: v1
  341. running: v1
  342. compiling: v2
  343. running: v2
  344. running: v1
  345. compiling: v3
  346. running: v3
  347. running: v2
  348. """, ret
  349. block: # nim dump
  350. let cmd = fmt"{nim} dump --dump.format:json -d:D20210428T161003 --hints:off ."
  351. let (ret, status) = execCmdEx(cmd)
  352. doAssert status == 0
  353. let j = ret.parseJson
  354. # sanity checks
  355. doAssert "D20210428T161003" in j["defined_symbols"].to(seq[string])
  356. doAssert j["version"].to(string) == NimVersion
  357. doAssert j["nimExe"].to(string) == getCurrentCompilerExe()
  358. block: # genscript
  359. const nimcache2 = buildDir / "D20210524T212851"
  360. removeDir(nimcache2)
  361. let input = "tgenscript_fakefile" # no need for a real file, --eval is good enough
  362. let output = runNimCmdChk(input, fmt"""--genscript --nimcache:{nimcache2.quoteShell} --eval:"echo(12345)" """)
  363. doAssert output.len == 0, output
  364. let ext = when defined(windows): ".bat" else: ".sh"
  365. let filename = fmt"compile_{input}{ext}" # synchronize with `generateScript`
  366. doAssert fileExists(nimcache2/filename), nimcache2/filename
  367. let (outp, status) = execCmdEx(genShellCmd(filename), options = {poStdErrToStdOut}, workingDir = nimcache2)
  368. doAssert status == 0, outp
  369. let (outp2, status2) = execCmdEx(nimcache2 / input, options = {poStdErrToStdOut})
  370. doAssert outp2 == "12345\n", outp2
  371. doAssert status2 == 0
  372. block: # UnusedImport
  373. proc fn(opt: string, expected: string) =
  374. let output = runNimCmdChk("msgs/mused3.nim", fmt"--warning:all:off --warning:UnusedImport --hint:DuplicateModuleImport {opt}")
  375. doAssert output == expected, opt & "\noutput:\n" & output & "expected:\n" & expected
  376. fn("-d:case1"): """
  377. mused3.nim(13, 8) Warning: imported and not used: 'mused3b' [UnusedImport]
  378. """
  379. fn("-d:case2"): ""
  380. fn("-d:case3"): ""
  381. fn("-d:case4"): ""
  382. fn("-d:case5"): ""
  383. fn("-d:case6"): ""
  384. fn("-d:case7"): ""
  385. fn("-d:case8"): ""
  386. fn("-d:case9"): ""
  387. fn("-d:case10"): ""
  388. when false:
  389. fn("-d:case11"): """
  390. Warning: imported and not used: 'm2' [UnusedImport]
  391. """
  392. fn("-d:case12"): """
  393. mused3.nim(75, 10) Hint: duplicate import of 'mused3a'; previous import here: mused3.nim(74, 10) [DuplicateModuleImport]
  394. """
  395. block: # FieldDefect
  396. proc fn(opt: string, expected: string) =
  397. let output = runNimCmdChk("misc/mfield_defect.nim", fmt"-r --warning:all:off --declaredlocs {opt}", status = 1)
  398. doAssert expected in output, opt & "\noutput:\n" & output & "expected:\n" & expected
  399. fn("-d:case1"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
  400. fn("-d:case2 --gc:refc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
  401. fn("-d:case1 -b:js"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
  402. fn("-d:case2 -b:js"): """field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
  403. fn("-d:case2 --gc:arc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
  404. else:
  405. discard # only during debugging, tests added here will run with `-d:nimTestsTrunnerDebugging` enabled