From 30fb134a74d813f8d53c22b3ad10ccc9a5dc4111 Mon Sep 17 00:00:00 2001 From: Productive2 <48047721+Productive2@users.noreply.github.com> Date: Sun, 10 Jan 2021 19:32:28 +0100 Subject: [PATCH] More tests --- .gitignore | 4 +- tests/japl/for_with_function.jpl | 86 ++++++++++++++++++++++++++++++++ tests/japl/ifchain.jpl | 22 ++++++++ tests/japl/reassignment.japl | 25 ++++++++++ tests/japl/shadowing.jpl | 76 ++++++++++++++++++++++++++++ tests/runtests.nim | 81 ++++++++++++++++++------------ 6 files changed, 260 insertions(+), 34 deletions(-) create mode 100644 tests/japl/for_with_function.jpl create mode 100644 tests/japl/ifchain.jpl create mode 100644 tests/japl/reassignment.japl create mode 100644 tests/japl/shadowing.jpl diff --git a/.gitignore b/.gitignore index 9f128c0..d805329 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ src/lexer src/vm tests/runtests testresults.txt +testoutput.txt # MacOS @@ -36,6 +37,3 @@ main config.nim -# test results - -testresults.txt diff --git a/tests/japl/for_with_function.jpl b/tests/japl/for_with_function.jpl new file mode 100644 index 0000000..6ad0376 --- /dev/null +++ b/tests/japl/for_with_function.jpl @@ -0,0 +1,86 @@ + +var y = 0; //a global to keep track of state +//does not need closures for this to work yet + +fun next(x) { + if (x == 10) + { + y = y + 1; + x = 0; + } + if (y == 10) + return -1; + return x+y+1; +} + +var i = 0; +for (; i != -1; i = next(i)) + print(i); +// before using next +//output:0 +// y = 0 +//output:1 +//output:2 +//output:3 +//output:4 +//output:5 +//output:6 +//output:7 +//output:8 +//output:9 +//output:10 +// y = 1 +//output:2 +//output:3 +//output:4 +//output:5 +//output:6 +//output:7 +//output:8 +//output:9 +//output:10 +// y = 2 +//output:3 +//output:4 +//output:5 +//output:6 +//output:7 +//output:8 +//output:9 +//output:10 +// y = 3 +//output:4 +//output:5 +//output:6 +//output:7 +//output:8 +//output:9 +//output:10 +// y = 4 +//output:5 +//output:6 +//output:7 +//output:8 +//output:9 +//output:10 +// y = 5 +//output:6 +//output:7 +//output:8 +//output:9 +//output:10 +// y = 6 +//output:7 +//output:8 +//output:9 +//output:10 +// y = 7 +//output:8 +//output:9 +//output:10 +// y = 8 +//output:9 +//output:10 +// y = 9 +//output:10 +// y = 10 diff --git a/tests/japl/ifchain.jpl b/tests/japl/ifchain.jpl new file mode 100644 index 0000000..820854b --- /dev/null +++ b/tests/japl/ifchain.jpl @@ -0,0 +1,22 @@ +fun printInt(x) { + if (x == 1) + print("one"); + else if (x == 2) + print("two"); + else if (x == 3) + print("three"); + else if (x == 4) + print("four"); + else if (x == 5) + print("five"); + else if (x == 6) + print("six"); +} +var x = 3; +printInt(x);//output:three +x = 5; +printInt(x);//output:five +x = 7; +printInt(x); +x = 1; +printInt(x);//output:one diff --git a/tests/japl/reassignment.japl b/tests/japl/reassignment.japl new file mode 100644 index 0000000..fb68419 --- /dev/null +++ b/tests/japl/reassignment.japl @@ -0,0 +1,25 @@ +{ + var x = 5; + var y = x; + y = 6; + print(x);//output:5 +} + +var g = 7; +var p = g; +{ + var k = g; + p = 3; + k = 9; + print(g);//output:7 +} +print(g);//output:7 + +fun resetter(x) { + x = 7; + print(x); +} + +var q = 5; +resetter(q);//output:7 +print(q);//output:5 diff --git a/tests/japl/shadowing.jpl b/tests/japl/shadowing.jpl new file mode 100644 index 0000000..55b068c --- /dev/null +++ b/tests/japl/shadowing.jpl @@ -0,0 +1,76 @@ +//similar to vars.jpl, but more focused on shadowing + +// simple shadowing +var x = 4; +{ + var x = 5; + print(x);//output:5 +} +print(x);//output:4 + +// type changing shadowing +var y = true; +{ + var y = 2; + print(y);//output:2 +} +print(y);//output:true + +// no shadowing here +var z = 3; +{ + z = true; + print(z);//output:true +} +print(z);//output:true + +//in-function shadowing +fun shadow(x) { + //will be called once with the input 3 + print(x);//output:3 + { + var x = 4; + print(x);//output:4 + } + print(x);//output:3 + x = nil; + print(x);//output:nil + return x; +} + +print(shadow(3));//output:nil + +//shadowing functions +fun hello() { + print("hello"); +} +hello();//output:hello +{ + fun hello() { + print("hello in"); + } + hello();//output:hello in + { + fun hello() { + print("hello inmost"); + } + hello();//output:hello inmost + } + hello();//output:hello in +} +hello();//output:hello + +//functions shadowing with type change +fun eat() { + print("nom nom nom"); +} +eat();//output:nom nom nom +{ + var eat = 4; + print(eat);//output:4 + {{{{{ + eat = 5; + }}}}} //multiple scopes haha + print(eat);//output:5 +} +eat();//output:nom nom nom diff --git a/tests/runtests.nim b/tests/runtests.nim index 508517f..7c4e551 100644 --- a/tests/runtests.nim +++ b/tests/runtests.nim @@ -22,12 +22,22 @@ # Imports nim tests as well -import multibyte, os, strformat, times, re +import multibyte, os, strformat, times, re, terminal # Exceptions for tests that represent not-yet implemented behaviour const exceptions = ["all.jpl"] +type LogLevel {.pure.} = enum + Debug, # always written to file only (large outputs, such as the entire output of the failing test or stacktrace) + Info, # important information about the progress of the test suite + Error, # failing tests (printed with red) + Stdout, # always printed to stdout only (for cli experience) + + +const echoedLogs = { LogLevel.Info, LogLevel.Error, LogLevel.Stdout } +const savedLogs = { LogLevel.Debug, LogLevel.Info, LogLevel.Error } + proc compileExpectedOutput(path: string): string = for line in path.lines(): @@ -50,22 +60,29 @@ proc deepComp(left, right: string): tuple[same: bool, place: int] = return -# Quick logging levels using procs +proc logWithLevel(level: LogLevel, file: File, msg: string) = + let msg = &"[{$level} - {$getTime()}] {msg}" -proc log(file: File, msg: string, toFile: bool = true) = - ## Logs to stdout and to the log file unless - ## toFile == false - if toFile: - file.writeLine(&"[LOG - {$getTime()}] {msg}") - echo &"[LOG - {$getTime()}] {msg}" + if level in savedLogs: + file.writeLine(msg) + if level in echoedLogs: + if level == LogLevel.Error: + setForegroundColor(fgRed) + echo msg + if level == LogLevel.Error: + setForegroundColor(fgDefault) -proc detail(file: File, msg: string) = - ## Logs only to the log file - file.writeLine(&"[DETAIL - {$getTime()}] {msg}") proc main(testsDir: string, japlExec: string, testResultsFile: File): tuple[numOfTests: int, successTests: int, failedTests: int, skippedTests: int] = + template detail(msg: string) = + logWithLevel(LogLevel.Debug, testResultsFile, msg) + template log(msg: string) = + logWithLevel(LogLevel.Info, testResultsFile, msg) + template error(msg: string) = + logWithLevel(LogLevel.Error, testResultsFile, msg) + var numOfTests = 0 var successTests = 0 var failedTests = 0 @@ -74,26 +91,26 @@ proc main(testsDir: string, japlExec: string, testResultsFile: File): tuple[numO for file in walkDir(testsDir): block singleTest: if file.path.extractFilename in exceptions: - detail(testResultsFile, &"Skipping '{file.path}'") + detail(&"Skipping '{file.path}'") numOfTests += 1 skippedTests += 1 break singleTest elif file.path.dirExists(): - detail(testResultsFile, "Descending into '" & file.path & "'") + detail(&"Descending into '" & file.path & "'") var subTestResult = main(file.path, japlExec, testResultsFile) numOfTests += subTestResult.numOfTests successTests += subTestResult.successTests failedTests += subTestResult.failedTests skippedTests += subTestResult.skippedTests break singleTest - detail(testResultsFile, &"Running test '{file.path}'") + detail(&"Running test '{file.path}'") if fileExists("testoutput.txt"): removeFile("testoutput.txt") # in case this crashed let retCode = execShellCmd(&"{japlExec} {file.path} >> testoutput.txt") numOfTests += 1 if retCode != 0: failedTests += 1 - log(testResultsFile, &"Test '{file.path}' has crashed!") + error(&"Test '{file.path}' has crashed!") else: let expectedOutput = compileExpectedOutput(file.path).replace(re"(\n*)$", "") let realOutputFile = open("testoutput.txt", fmRead) @@ -103,17 +120,17 @@ proc main(testsDir: string, japlExec: string, testResultsFile: File): tuple[numO let comparison = deepComp(expectedOutput, realOutput) if comparison.same: successTests += 1 - log(testResultsFile, &"Test '{file.path}' was successful") + log(&"Test '{file.path}' was successful") else: failedTests += 1 - detail(testResultsFile, &"Expected output:\n{expectedOutput}\n") - detail(testResultsFile, &"Received output:\n{realOutput}\n") - detail(testResultsFile, &"Mismatch at pos {comparison.place}") + detail(&"Expected output:\n{expectedOutput}\n") + detail(&"Received output:\n{realOutput}\n") + detail(&"Mismatch at pos {comparison.place}") if comparison.place > expectedOutput.high() or comparison.place > realOutput.high(): - detail(testResultsFile, &"Length mismatch") + detail(&"Length mismatch") else: - detail(testResultsFile, &"Expected is '{expectedOutput[comparison.place]}' while received '{realOutput[comparison.place]}'") - log(testResultsFile, &"Test '{file.path}' failed") + detail(&"Expected is '{expectedOutput[comparison.place]}' while received '{realOutput[comparison.place]}'") + error(&"Test '{file.path}' failed") result = (numOfTests: numOfTests, successTests: successTests, failedTests: failedTests, skippedTests: skippedTests) except IOError: stderr.write(&"Fatal IO error encountered while running tests -> {getCurrentExceptionMsg()}") @@ -121,12 +138,14 @@ proc main(testsDir: string, japlExec: string, testResultsFile: File): tuple[numO when isMainModule: let testResultsFile = open("testresults.txt", fmWrite) - log(testResultsFile, "Running Nim tests") + template log (msg: string) = + logWithLevel(LogLevel.Info, testResultsFile, msg) + log("Running Nim tests") # Nim tests - detail(testResultsFile, "Running testMultiByte") + logWithLevel(LogLevel.Debug, testResultsFile, "Running testMultiByte") testMultiByte() # JAPL tests - log(testResultsFile, "Running JAPL tests") + log("Running JAPL tests") var testsDir = "tests" / "japl" var japlExec = "src" / "japl" var currentDir = getCurrentDir() @@ -134,16 +153,16 @@ when isMainModule: if currentDir.lastPathPart() == "tests": testsDir = "japl" japlExec = ".." / japlExec - log(testResultsFile, &"Looking for JAPL tests in {testsDir}") - log(testResultsFile, &"Looking for JAPL executable at {japlExec}") + log(&"Looking for JAPL tests in {testsDir}") + log(&"Looking for JAPL executable at {japlExec}") if not fileExists(japlExec): - log(testResultsFile, "JAPL executable not found") + log("JAPL executable not found") quit(1) if not dirExists(testsDir): - log(testResultsFile, "Tests dir not found") + log("Tests dir not found") quit(1) let testResult = main(testsDir, japlExec, testResultsFile) - log(testResultsFile, &"Found {testResult.numOfTests} tests: {testResult.successTests} were successful, {testResult.failedTests} failed and {testResult.skippedTests} were skipped.") - log(testResultsFile, "Check 'testresults.txt' for details", toFile=false) + log(&"Found {testResult.numOfTests} tests: {testResult.successTests} were successful, {testResult.failedTests} failed and {testResult.skippedTests} were skipped.") + logWithLevel(LogLevel.Stdout, testResultsFile, "Check 'testresults.txt' for details") testResultsfile.close()