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