2021-01-30 10:38:47 +01:00
|
|
|
# Copyright 2020 Mattia Giambirtone
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
2021-01-29 18:14:57 +01:00
|
|
|
# Just Another Test Suite for running JAPL tests
|
|
|
|
|
2021-01-29 20:11:06 +01:00
|
|
|
import nim/nimtests
|
|
|
|
|
2021-01-29 18:14:57 +01:00
|
|
|
import ../src/vm
|
|
|
|
import testutils
|
|
|
|
|
|
|
|
import os, osproc, strformat, streams
|
|
|
|
|
|
|
|
# Tests that represent not-yet implemented behaviour
|
|
|
|
const exceptions = ["all.jpl", "for_with_function.jpl", "runtime_interning.jpl", "problem4.jpl"]
|
|
|
|
# TODO: for_with_function.jpl and problem4.jpl should already be implemented, check on them
|
|
|
|
|
|
|
|
proc buildTest(path: string): Test =
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Building test {path}")
|
|
|
|
let source = readFile(path)
|
2021-01-29 18:14:57 +01:00
|
|
|
result = Test(
|
|
|
|
path: path,
|
|
|
|
result: if path.extractFilename in exceptions: TestResult.Skip
|
|
|
|
else: TestResult.Unstarted,
|
2021-01-29 19:56:23 +01:00
|
|
|
expectedOutput: compileExpectedOutput(source),
|
|
|
|
expectedError: compileExpectedError(source)
|
2021-01-29 18:14:57 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
proc buildTests(testDir: string): seq[Test] =
|
|
|
|
for candidateObj in walkDir(testDir):
|
|
|
|
let candidate = candidateObj.path
|
|
|
|
if dirExists(candidate):
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Descending into dir {candidate}")
|
2021-01-29 18:14:57 +01:00
|
|
|
result &= buildTests(candidate)
|
|
|
|
else:
|
|
|
|
result.add buildTest(candidate)
|
|
|
|
|
|
|
|
proc runTest(test: Test, runner: string) =
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Starting test {test.path}.")
|
2021-01-29 18:14:57 +01:00
|
|
|
let process = startProcess(runner, args = @[test.path])
|
|
|
|
test.process = process
|
|
|
|
test.result = TestResult.Running
|
|
|
|
|
|
|
|
proc tryFinishTest(test: Test): bool =
|
|
|
|
if test.process.running():
|
|
|
|
return false
|
|
|
|
test.output = test.process.outputStream.readAll()
|
|
|
|
test.error = test.process.errorStream.readAll()
|
|
|
|
if test.process.peekExitCode() == 0:
|
|
|
|
test.result = TestResult.ToEval
|
|
|
|
else:
|
|
|
|
test.result = TestResult.Crash
|
|
|
|
test.process.close()
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Test {test.path} finished.")
|
2021-01-29 18:14:57 +01:00
|
|
|
return true
|
|
|
|
|
|
|
|
const maxAliveTests = 8
|
2021-01-29 19:56:23 +01:00
|
|
|
const testWait = 10
|
2021-01-29 18:14:57 +01:00
|
|
|
|
|
|
|
proc runTests(tests: seq[Test], runner: string) =
|
|
|
|
var
|
|
|
|
aliveTests = 0
|
|
|
|
currentTest = 0
|
2021-01-29 19:56:23 +01:00
|
|
|
finishedTests = 0
|
|
|
|
buffer = newBuffer()
|
|
|
|
let totalTests = tests.len()
|
2021-01-29 18:14:57 +01:00
|
|
|
|
2021-01-29 19:56:23 +01:00
|
|
|
buffer.updateProgressBar(&"", totalTests, finishedTests)
|
|
|
|
buffer.render()
|
|
|
|
while aliveTests > 0 or currentTest < tests.len():
|
|
|
|
buffer.render()
|
|
|
|
sleep(testWait)
|
2021-01-29 18:14:57 +01:00
|
|
|
if aliveTests < maxAliveTests and currentTest < tests.len():
|
|
|
|
if tests[currentTest].result == TestResult.Unstarted:
|
|
|
|
tests[currentTest].runTest(runner)
|
|
|
|
inc aliveTests
|
|
|
|
inc currentTest
|
|
|
|
else:
|
|
|
|
inc currentTest
|
2021-01-29 19:56:23 +01:00
|
|
|
inc finishedTests
|
2021-01-29 18:14:57 +01:00
|
|
|
for i in countup(0, min(currentTest, tests.high())):
|
|
|
|
if tests[i].result == TestResult.Running:
|
|
|
|
if tryFinishTest(tests[i]):
|
2021-01-29 19:56:23 +01:00
|
|
|
inc finishedTests
|
|
|
|
buffer.updateProgressBar(&"", totalTests, finishedTests)
|
2021-01-29 18:14:57 +01:00
|
|
|
dec aliveTests
|
|
|
|
else:
|
|
|
|
inc tests[i].cycles
|
2021-01-29 19:56:23 +01:00
|
|
|
buffer.render()
|
2021-01-29 18:14:57 +01:00
|
|
|
|
|
|
|
proc evalTest(test: Test) =
|
|
|
|
test.output = test.output.strip()
|
|
|
|
test.error = test.error.strip()
|
|
|
|
test.expectedOutput = test.expectedOutput.strip()
|
|
|
|
test.expectedError = test.expectedError.strip()
|
|
|
|
if test.output != test.expectedOutput or test.error != test.expectedError:
|
|
|
|
test.result = TestResult.Mismatch
|
|
|
|
else:
|
|
|
|
test.result = TestResult.Success
|
|
|
|
|
|
|
|
proc evalTests(tests: seq[Test]) =
|
|
|
|
for test in tests:
|
|
|
|
if test.result == TestResult.ToEval:
|
|
|
|
evalTest(test)
|
|
|
|
|
|
|
|
proc printResults(tests: seq[Test]) =
|
2021-01-29 19:56:23 +01:00
|
|
|
var
|
|
|
|
skipped = 0
|
|
|
|
success = 0
|
|
|
|
fail = 0
|
|
|
|
crash = 0
|
|
|
|
|
2021-01-29 18:14:57 +01:00
|
|
|
for test in tests:
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Test {test.path} result: {test.result}")
|
|
|
|
case test.result:
|
|
|
|
of TestResult.Skip:
|
|
|
|
inc skipped
|
|
|
|
of TestResult.Mismatch:
|
|
|
|
inc fail
|
|
|
|
log(LogLevel.Debug, &"[{test.path}\noutput:\n{test.output}\nerror:\n{test.error}\nexpected output:\n{test.expectedOutput}\nexpectedError:\n{test.expectedError}\n]")
|
|
|
|
of TestResult.Crash:
|
|
|
|
inc crash
|
|
|
|
log(LogLevel.Debug, &"{test.path} \ncrash:\n{test.error}")
|
|
|
|
of TestResult.Success:
|
|
|
|
inc success
|
|
|
|
else:
|
|
|
|
log(LogLevel.Error, &"Probably a testing suite bug: test {test.path} has result {test.result}")
|
|
|
|
let finalLevel = if fail == 0 and crash == 0: LogLevel.Info else: LogLevel.Error
|
|
|
|
log(finalLevel, &"{tests.len()} tests: {success} succeeded, {skipped} skipped, {fail} failed, {crash} crashed.")
|
2021-01-29 18:14:57 +01:00
|
|
|
|
|
|
|
when isMainModule:
|
2021-01-29 19:56:23 +01:00
|
|
|
const jatsVersion = "(dev)"
|
|
|
|
|
|
|
|
if paramCount() > 0:
|
|
|
|
if paramStr(1) == "-h":
|
|
|
|
echo "Usage: jats [-h | -v | -i | -o filename.txt]"
|
2021-01-29 20:14:01 +01:00
|
|
|
quit(0)
|
2021-01-29 19:56:23 +01:00
|
|
|
elif paramStr(1) == "-v":
|
|
|
|
echo "JATS v" & $jatsVersion
|
2021-01-29 20:14:01 +01:00
|
|
|
quit(0)
|
2021-01-29 20:15:34 +01:00
|
|
|
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Welcome to JATS")
|
2021-01-29 20:15:34 +01:00
|
|
|
|
|
|
|
runNimTests()
|
2021-01-29 18:14:57 +01:00
|
|
|
var jatr = "jatr"
|
|
|
|
var testDir = "japl"
|
|
|
|
if not fileExists(jatr) and fileExists("tests" / jatr):
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Must be in root: prepending \"tests\" to paths")
|
2021-01-29 18:14:57 +01:00
|
|
|
jatr = "tests" / jatr
|
|
|
|
testDir = "tests" / testDir
|
|
|
|
|
2021-01-29 20:11:06 +01:00
|
|
|
log(LogLevel.Info, &"Running JAPL tests.")
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Info, &"Building tests...")
|
2021-01-29 18:14:57 +01:00
|
|
|
let tests: seq[Test] = buildTests(testDir)
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Tests built.")
|
|
|
|
log(LogLevel.Info, &"Running tests...")
|
2021-01-29 18:14:57 +01:00
|
|
|
tests.runTests(jatr)
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Tests ran.")
|
|
|
|
log(LogLevel.Debug, &"Evaluating tests...")
|
2021-01-29 18:14:57 +01:00
|
|
|
tests.evalTests()
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Tests evaluated.")
|
2021-01-29 18:14:57 +01:00
|
|
|
tests.printResults()
|
2021-01-29 19:56:23 +01:00
|
|
|
log(LogLevel.Debug, &"Quitting JATS.")
|
|
|
|
|
|
|
|
# special options to view the entire debug log
|
|
|
|
|
|
|
|
if paramCount() > 0:
|
|
|
|
if paramStr(1) == "-i":
|
2021-01-30 10:38:47 +01:00
|
|
|
when defined(posix):
|
|
|
|
discard execCmdEx(&"echo {getTotalLog()} | less")
|
|
|
|
else:
|
|
|
|
discard execCmdEx("echo {getTotalLog()} | more")
|
2021-01-29 19:56:23 +01:00
|
|
|
if paramStr(1) == "-o":
|
|
|
|
writeFile(paramStr(2), getTotalLog())
|
|
|
|
|