caasdriver.nim 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import osproc, streams, os, strutils, re
  2. {.experimental.}
  3. ## Compiler as a service tester.
  4. ##
  5. ## Please read docs/idetools.txt for information about this.
  6. type
  7. TRunMode = enum
  8. ProcRun, CaasRun, SymbolProcRun
  9. NimSession* = object
  10. nim: Process # Holds the open process for CaasRun sessions, nil otherwise.
  11. mode: TRunMode # Stores the type of run mode the session was started with.
  12. lastOutput: string # Preserves the last output, needed for ProcRun mode.
  13. filename: string # Appended to each command starting with '>'. Also a var.
  14. modname: string # Like filename but without extension.
  15. nimcache: string # Input script based name for the nimcache dir.
  16. const
  17. modes = [CaasRun, ProcRun, SymbolProcRun]
  18. filenameReplaceVar = "$TESTNIM"
  19. moduleReplaceVar = "$MODULE"
  20. silentReplaceVar = "$SILENT"
  21. silentReplaceText = "--verbosity:0 --hints:off"
  22. var
  23. TesterDir = getAppDir() / ".."
  24. NimBin = TesterDir / "../bin/nim"
  25. proc replaceVars(session: var NimSession, text: string): string =
  26. result = text.replace(filenameReplaceVar, session.filename)
  27. result = result.replace(moduleReplaceVar, session.modname)
  28. result = result.replace(silentReplaceVar, silentReplaceText)
  29. proc startNimSession(project, script: string, mode: TRunMode):
  30. NimSession =
  31. let (dir, name, ext) = project.splitFile
  32. result.mode = mode
  33. result.lastOutput = ""
  34. result.filename = name & ext
  35. result.modname = name
  36. let (nimcacheDir, nimcacheName, nimcacheExt) = script.splitFile
  37. result.nimcache = "SymbolProcRun." & nimcacheName
  38. if mode == SymbolProcRun:
  39. removeDir(nimcacheDir / result.nimcache)
  40. else:
  41. removeDir(nimcacheDir / "nimcache")
  42. if mode == CaasRun:
  43. result.nim = startProcess(NimBin, workingDir = dir,
  44. args = ["serve", "--server.type:stdin", name])
  45. proc doCaasCommand(session: var NimSession, command: string): string =
  46. assert session.mode == CaasRun
  47. session.nim.inputStream.write(session.replaceVars(command) & "\n")
  48. session.nim.inputStream.flush
  49. result = ""
  50. while true:
  51. var line = TaintedString("")
  52. if session.nim.outputStream.readLine(line):
  53. if line.string == "": break
  54. result.add(line.string & "\n")
  55. else:
  56. result = "FAILED TO EXECUTE: " & command & "\n" & result
  57. break
  58. proc doProcCommand(session: var NimSession, command: string): string =
  59. try:
  60. assert session.mode == ProcRun or session.mode == SymbolProcRun
  61. except:
  62. result = "FAILED TO EXECUTE: " & command & "\n" & result
  63. var
  64. process = startProcess(NimBin, args = session.replaceVars(command).split)
  65. stream = outputStream(process)
  66. line = TaintedString("")
  67. result = ""
  68. while stream.readLine(line):
  69. if result.len > 0: result &= "\n"
  70. result &= line.string
  71. process.close()
  72. proc doCommand(session: var NimSession, command: string) =
  73. if session.mode == CaasRun:
  74. if not session.nim.running:
  75. session.lastOutput = "FAILED TO EXECUTE: " & command & "\n" &
  76. "Exit code " & $session.nim.peekExitCode
  77. return
  78. session.lastOutput = doCaasCommand(session,
  79. command & " " & session.filename)
  80. else:
  81. var command = command
  82. # For symbol runs we prepend the necessary parameters to avoid clobbering
  83. # the normal nimcache.
  84. if session.mode == SymbolProcRun:
  85. command = "--symbolFiles:on --nimcache:" & session.nimcache &
  86. " " & command
  87. session.lastOutput = doProcCommand(session,
  88. command & " " & session.filename)
  89. proc destroy(session: var NimSession) {.destructor.} =
  90. if session.mode == CaasRun:
  91. session.nim.close
  92. proc doScenario(script: string, output: Stream, mode: TRunMode, verbose: bool): bool =
  93. result = true
  94. var f = open(script)
  95. var project = TaintedString("")
  96. if f.readLine(project):
  97. var
  98. s = startNimSession(script.parentDir / project.string, script, mode)
  99. tline = TaintedString("")
  100. ln = 1
  101. while f.readLine(tline):
  102. var line = tline.string
  103. inc ln
  104. # Filter lines by run mode, removing the prefix if the mode is current.
  105. for testMode in modes:
  106. if line.startsWith($testMode):
  107. if testMode != mode:
  108. line = ""
  109. else:
  110. line = line[len($testMode)..len(line) - 1].strip
  111. break
  112. if line.strip.len == 0: continue
  113. if line.startsWith("#"):
  114. output.writeLine line
  115. continue
  116. elif line.startsWith(">"):
  117. s.doCommand(line.substr(1).strip)
  118. output.writeLine line, "\n", if verbose: s.lastOutput else: ""
  119. else:
  120. var expectMatch = true
  121. var pattern = s.replaceVars(line)
  122. if line.startsWith("!"):
  123. pattern = pattern.substr(1).strip
  124. expectMatch = false
  125. let actualMatch =
  126. s.lastOutput.find(re(pattern, flags = {reStudy})) != -1
  127. if expectMatch == actualMatch:
  128. output.writeLine "SUCCESS ", line
  129. else:
  130. output.writeLine "FAILURE ", line
  131. result = false
  132. iterator caasTestsRunner*(filter = "", verbose = false): tuple[test,
  133. output: string, status: bool,
  134. mode: TRunMode] =
  135. for scenario in os.walkFiles(TesterDir / "caas/*.txt"):
  136. if filter.len > 0 and find(scenario, filter) == -1: continue
  137. for mode in modes:
  138. var outStream = newStringStream()
  139. let r = doScenario(scenario, outStream, mode, verbose)
  140. yield (scenario, outStream.data, r, mode)
  141. when isMainModule:
  142. var
  143. filter = ""
  144. failures = 0
  145. verbose = false
  146. for i in 0..paramCount() - 1:
  147. let param = string(paramStr(i + 1))
  148. case param
  149. of "verbose": verbose = true
  150. else: filter = param
  151. if verbose and len(filter) > 0:
  152. echo "Running only test cases matching filter '$1'" % [filter]
  153. for test, output, result, mode in caasTestsRunner(filter, verbose):
  154. if not result or verbose:
  155. echo "Mode ", $mode, " (", if result: "succeeded)" else: "failed)"
  156. echo test
  157. echo output
  158. echo "---------\n"
  159. if not result:
  160. failures += 1
  161. quit(failures)