mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-07 13:33:22 +00:00
183 lines
5.2 KiB
Nim
183 lines
5.2 KiB
Nim
# Tester for nimsuggest.
|
|
# Every test file can have a #[!]# comment that is deleted from the input
|
|
# before 'nimsuggest' is invoked to ensure this token doesn't make a
|
|
# crucial difference for Nim's parser.
|
|
|
|
import os, osproc, strutils, streams, re
|
|
|
|
type
|
|
Test = object
|
|
cmd, dest: string
|
|
startup: seq[string]
|
|
script: seq[(string, string)]
|
|
|
|
const
|
|
curDir = when defined(windows): "" else: ""
|
|
DummyEof = "!EOF!"
|
|
|
|
template tpath(): untyped = getAppDir() / "tests"
|
|
|
|
proc parseTest(filename: string): Test =
|
|
const cursorMarker = "#[!]#"
|
|
let nimsug = curDir & addFileExt("nimsuggest", ExeExt)
|
|
result.dest = getTempDir() / extractFilename(filename)
|
|
result.cmd = nimsug & " --tester " & result.dest
|
|
result.script = @[]
|
|
result.startup = @[]
|
|
var tmp = open(result.dest, fmWrite)
|
|
var specSection = 0
|
|
var markers = newSeq[string]()
|
|
var i = 1
|
|
for x in lines(filename):
|
|
let marker = x.find(cursorMarker)+1
|
|
if marker > 0:
|
|
markers.add "\"" & filename & "\";\"" & result.dest & "\":" & $i & ":" & $marker
|
|
tmp.writeLine x.replace(cursorMarker, "")
|
|
else:
|
|
tmp.writeLine x
|
|
if x.contains("""""""""):
|
|
inc specSection
|
|
elif specSection == 1:
|
|
if x.startsWith("$nimsuggest"):
|
|
result.cmd = x % ["nimsuggest", nimsug, "file", filename]
|
|
elif x.startsWith("!"):
|
|
if result.cmd.len == 0:
|
|
result.startup.add x
|
|
else:
|
|
result.script.add((x, ""))
|
|
elif x.startsWith(">"):
|
|
# since 'markers' here are not complete yet, we do the $substitutions
|
|
# afterwards
|
|
result.script.add((x.substr(1).replaceWord("$path", tpath()), ""))
|
|
elif x.len > 0:
|
|
# expected output line:
|
|
let x = x % ["file", filename]
|
|
result.script[^1][1].add x.replace(";;", "\t") & '\L'
|
|
# else: ignore empty lines for better readability of the specs
|
|
inc i
|
|
tmp.close()
|
|
# now that we know the markers, substitute them:
|
|
for a in mitems(result.script):
|
|
a[0] = a[0] % markers
|
|
|
|
proc parseCmd(c: string): seq[string] =
|
|
# we don't support double quotes for now so that
|
|
# we can later support them properly with escapes and stuff.
|
|
result = @[]
|
|
var i = 0
|
|
var a = ""
|
|
while true:
|
|
setLen(a, 0)
|
|
# eat all delimiting whitespace
|
|
while c[i] in {' ', '\t', '\l', '\r'}: inc(i)
|
|
case c[i]
|
|
of '"': raise newException(ValueError, "double quotes not yet supported: " & c)
|
|
of '\'':
|
|
var delim = c[i]
|
|
inc(i) # skip ' or "
|
|
while c[i] != '\0' and c[i] != delim:
|
|
add a, c[i]
|
|
inc(i)
|
|
if c[i] != '\0': inc(i)
|
|
of '\0': break
|
|
else:
|
|
while c[i] > ' ':
|
|
add(a, c[i])
|
|
inc(i)
|
|
add(result, a)
|
|
|
|
proc edit(tmpfile: string; x: seq[string]) =
|
|
if x.len != 3 and x.len != 4:
|
|
quit "!edit takes two or three arguments"
|
|
let f = if x.len >= 4: tpath() / x[3] else: tmpfile
|
|
try:
|
|
let content = readFile(f)
|
|
let newcontent = content.replace(x[1], x[2])
|
|
if content == newcontent:
|
|
quit "wrong test case: edit had no effect"
|
|
writeFile(f, newcontent)
|
|
except IOError:
|
|
quit "cannot edit file " & tmpfile
|
|
|
|
proc exec(x: seq[string]) =
|
|
if x.len != 2: quit "!exec takes one argument"
|
|
if execShellCmd(x[1]) != 0:
|
|
quit "External program failed " & x[1]
|
|
|
|
proc copy(x: seq[string]) =
|
|
if x.len != 3: quit "!copy takes two arguments"
|
|
let rel = tpath()
|
|
copyFile(rel / x[1], rel / x[2])
|
|
|
|
proc del(x: seq[string]) =
|
|
if x.len != 2: quit "!del takes one argument"
|
|
removeFile(tpath() / x[1])
|
|
|
|
proc runCmd(cmd, dest: string): bool =
|
|
result = cmd[0] == '!'
|
|
if not result: return
|
|
let x = cmd.parseCmd()
|
|
case x[0]
|
|
of "!edit":
|
|
edit(dest, x)
|
|
of "!exec":
|
|
exec(x)
|
|
of "!copy":
|
|
copy(x)
|
|
of "!del":
|
|
del(x)
|
|
else:
|
|
quit "unkown command: " & cmd
|
|
|
|
proc smartCompare(pattern, x: string): bool =
|
|
if pattern.contains('*'):
|
|
result = match(x, re(escapeRe(pattern).replace("\\x2A","(.*)"), {}))
|
|
|
|
proc runTest(filename: string): int =
|
|
let s = parseTest filename
|
|
for cmd in s.startup:
|
|
if not runCmd(cmd, s.dest):
|
|
quit "invalid command: " & cmd
|
|
let cl = parseCmdLine(s.cmd)
|
|
var p = startProcess(command=cl[0], args=cl[1 .. ^1],
|
|
options={poStdErrToStdOut, poUsePath,
|
|
poInteractive, poDemon})
|
|
let outp = p.outputStream
|
|
let inp = p.inputStream
|
|
var report = ""
|
|
var a = newStringOfCap(120)
|
|
try:
|
|
# read and ignore anything nimsuggest says at startup:
|
|
while outp.readLine(a):
|
|
if a == DummyEof: break
|
|
for req, resp in items(s.script):
|
|
if not runCmd(req, s.dest):
|
|
inp.writeLine(req)
|
|
inp.flush()
|
|
var answer = ""
|
|
while outp.readLine(a):
|
|
if a == DummyEof: break
|
|
answer.add a
|
|
answer.add '\L'
|
|
if resp != answer and not smartCompare(resp, answer):
|
|
report.add "\nTest failed: " & filename
|
|
report.add "\n Expected: " & resp
|
|
report.add "\n But got: " & answer
|
|
finally:
|
|
inp.writeLine("quit")
|
|
inp.flush()
|
|
close(p)
|
|
if report.len > 0:
|
|
echo report
|
|
result = report.len
|
|
|
|
proc main() =
|
|
var failures = 0
|
|
for x in walkFiles(getAppDir() / "tests/t*.nim"):
|
|
echo "Test ", x
|
|
failures += runTest(expandFilename(x))
|
|
if failures > 0:
|
|
quit 1
|
|
|
|
main()
|