123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- import osproc, streams, os, strutils, re
- {.experimental.}
- ## Compiler as a service tester.
- ##
- ## Please read docs/idetools.txt for information about this.
- type
- TRunMode = enum
- ProcRun, CaasRun, SymbolProcRun
- NimSession* = object
- nim: Process # Holds the open process for CaasRun sessions, nil otherwise.
- mode: TRunMode # Stores the type of run mode the session was started with.
- lastOutput: string # Preserves the last output, needed for ProcRun mode.
- filename: string # Appended to each command starting with '>'. Also a var.
- modname: string # Like filename but without extension.
- nimcache: string # Input script based name for the nimcache dir.
- const
- modes = [CaasRun, ProcRun, SymbolProcRun]
- filenameReplaceVar = "$TESTNIM"
- moduleReplaceVar = "$MODULE"
- silentReplaceVar = "$SILENT"
- silentReplaceText = "--verbosity:0 --hints:off"
- var
- TesterDir = getAppDir() / ".."
- NimBin = TesterDir / "../bin/nim"
- proc replaceVars(session: var NimSession, text: string): string =
- result = text.replace(filenameReplaceVar, session.filename)
- result = result.replace(moduleReplaceVar, session.modname)
- result = result.replace(silentReplaceVar, silentReplaceText)
- proc startNimSession(project, script: string, mode: TRunMode):
- NimSession =
- let (dir, name, ext) = project.splitFile
- result.mode = mode
- result.lastOutput = ""
- result.filename = name & ext
- result.modname = name
- let (nimcacheDir, nimcacheName, nimcacheExt) = script.splitFile
- result.nimcache = "SymbolProcRun." & nimcacheName
- if mode == SymbolProcRun:
- removeDir(nimcacheDir / result.nimcache)
- else:
- removeDir(nimcacheDir / "nimcache")
- if mode == CaasRun:
- result.nim = startProcess(NimBin, workingDir = dir,
- args = ["serve", "--server.type:stdin", name])
- proc doCaasCommand(session: var NimSession, command: string): string =
- assert session.mode == CaasRun
- session.nim.inputStream.write(session.replaceVars(command) & "\n")
- session.nim.inputStream.flush
- result = ""
- while true:
- var line = TaintedString("")
- if session.nim.outputStream.readLine(line):
- if line.string == "": break
- result.add(line.string & "\n")
- else:
- result = "FAILED TO EXECUTE: " & command & "\n" & result
- break
- proc doProcCommand(session: var NimSession, command: string): string =
- try:
- assert session.mode == ProcRun or session.mode == SymbolProcRun
- except:
- result = "FAILED TO EXECUTE: " & command & "\n" & result
- var
- process = startProcess(NimBin, args = session.replaceVars(command).split)
- stream = outputStream(process)
- line = TaintedString("")
- result = ""
- while stream.readLine(line):
- if result.len > 0: result &= "\n"
- result &= line.string
- process.close()
- proc doCommand(session: var NimSession, command: string) =
- if session.mode == CaasRun:
- if not session.nim.running:
- session.lastOutput = "FAILED TO EXECUTE: " & command & "\n" &
- "Exit code " & $session.nim.peekExitCode
- return
- session.lastOutput = doCaasCommand(session,
- command & " " & session.filename)
- else:
- var command = command
- # For symbol runs we prepend the necessary parameters to avoid clobbering
- # the normal nimcache.
- if session.mode == SymbolProcRun:
- command = "--symbolFiles:on --nimcache:" & session.nimcache &
- " " & command
- session.lastOutput = doProcCommand(session,
- command & " " & session.filename)
- proc destroy(session: var NimSession) {.destructor.} =
- if session.mode == CaasRun:
- session.nim.close
- proc doScenario(script: string, output: Stream, mode: TRunMode, verbose: bool): bool =
- result = true
- var f = open(script)
- var project = TaintedString("")
- if f.readLine(project):
- var
- s = startNimSession(script.parentDir / project.string, script, mode)
- tline = TaintedString("")
- ln = 1
- while f.readLine(tline):
- var line = tline.string
- inc ln
- # Filter lines by run mode, removing the prefix if the mode is current.
- for testMode in modes:
- if line.startsWith($testMode):
- if testMode != mode:
- line = ""
- else:
- line = line[len($testMode)..len(line) - 1].strip
- break
- if line.strip.len == 0: continue
- if line.startsWith("#"):
- output.writeLine line
- continue
- elif line.startsWith(">"):
- s.doCommand(line.substr(1).strip)
- output.writeLine line, "\n", if verbose: s.lastOutput else: ""
- else:
- var expectMatch = true
- var pattern = s.replaceVars(line)
- if line.startsWith("!"):
- pattern = pattern.substr(1).strip
- expectMatch = false
- let actualMatch =
- s.lastOutput.find(re(pattern, flags = {reStudy})) != -1
- if expectMatch == actualMatch:
- output.writeLine "SUCCESS ", line
- else:
- output.writeLine "FAILURE ", line
- result = false
- iterator caasTestsRunner*(filter = "", verbose = false): tuple[test,
- output: string, status: bool,
- mode: TRunMode] =
- for scenario in os.walkFiles(TesterDir / "caas/*.txt"):
- if filter.len > 0 and find(scenario, filter) == -1: continue
- for mode in modes:
- var outStream = newStringStream()
- let r = doScenario(scenario, outStream, mode, verbose)
- yield (scenario, outStream.data, r, mode)
- when isMainModule:
- var
- filter = ""
- failures = 0
- verbose = false
- for i in 0..paramCount() - 1:
- let param = string(paramStr(i + 1))
- case param
- of "verbose": verbose = true
- else: filter = param
- if verbose and len(filter) > 0:
- echo "Running only test cases matching filter '$1'" % [filter]
- for test, output, result, mode in caasTestsRunner(filter, verbose):
- if not result or verbose:
- echo "Mode ", $mode, " (", if result: "succeeded)" else: "failed)"
- echo test
- echo output
- echo "---------\n"
- if not result:
- failures += 1
- quit(failures)
|