diff --git a/testparser.nim b/testparser.nim new file mode 100644 index 0000000..13f0644 --- /dev/null +++ b/testparser.nim @@ -0,0 +1,93 @@ +import std/[tables, options] +import compiler/[ast, idents, msgs, syntaxes, options, pathutils, lineinfos] +# import compiler/[renderer, astalgo] + +type + TestInfo* = object + name*: string + line*: uint + + SuiteInfo* = object + name*: string #empty means global suite + tests*: seq[TestInfo] + line*: uint + + TestFileInfo* = object + testFile*: string + suites*: Table[string, SuiteInfo] + hasErrors*: bool + +proc extractTest(n: PNode, conf: ConfigRef): Option[TestInfo] = + if n.kind in nkCallKinds and + n[0].kind == nkIdent and + n[0].ident.s == "test": + if n.len >= 2 and n[1].kind in {nkStrLit .. nkTripleStrLit}: + return some(TestInfo( + name: n[1].strVal, + line: n.info.line + )) + else: + localError(conf, n.info, "'test' requires a string literal name") + return none(TestInfo) + +proc extract(n: PNode, conf: ConfigRef, result: var TestFileInfo) = + case n.kind + of nkStmtList, nkStmtListExpr: + for child in n: + extract(child, conf, result) + of nkCallKinds: + if n[0].kind == nkIdent: + case n[0].ident.s + of "suite": + if n.len >= 2 and n[1].kind in {nkStrLit .. nkTripleStrLit}: + var suite = SuiteInfo( + name: n[1].strVal, + tests: @[], + line: n.info.line + ) + # Extract tests within the suite's body + if n.len > 2 and n[2].kind == nkStmtList: + for testNode in n[2]: + let testInfo = extractTest(testNode, conf) + if testInfo.isSome: + suite.tests.add(testInfo.get) + + result.suites[suite.name] = suite + else: + localError(conf, n.info, "'suite' requires a string literal name") + result.hasErrors = true + of "test": + # Handle top-level tests (not in a suite) + let testInfo = extractTest(n, conf) + if testInfo.isSome: + result.suites.mgetOrPut("", SuiteInfo( + name: "", + tests: @[], + line: 0 + )).tests.add(testInfo.get) + else: + discard + else: + discard + +proc extractTestInfo*(testFile: string): TestFileInfo = + ## Extract test information from a test file. This parses the test file + ## and extracts suite and test names. + result.testFile = testFile + var conf = newConfigRef() + conf.foreignPackageNotes = {} + conf.notes = {} + conf.mainPackageNotes = {} + conf.errorMax = high(int) + conf.structuredErrorHook = proc( + config: ConfigRef, info: TLineInfo, msg: string, severity: Severity + ) {.gcsafe.} = + localError(config, info, warnUser, msg) + + let fileIdx = fileInfoIdx(conf, AbsoluteFile testFile) + var parser: Parser + if setupParser(parser, fileIdx, newIdentCache(), conf): + extract(parseAll(parser), conf, result) + closeParser(parser) + result.hasErrors = result.hasErrors or conf.errorCounter > 0 + diff --git a/tests/all.nim b/tests/all.nim index 1bc2cfe..a1f8d62 100644 --- a/tests/all.nim +++ b/tests/all.nim @@ -3,4 +3,5 @@ import tnimlangserver, tprojectsetup, textensions, - tmisc \ No newline at end of file + tmisc, + ttestrunner \ No newline at end of file diff --git a/tests/projects/nonimbleproject.nim b/tests/projects/nonimbleproject.nim new file mode 100644 index 0000000..41bb9d3 --- /dev/null +++ b/tests/projects/nonimbleproject.nim @@ -0,0 +1 @@ +echo "hello" \ No newline at end of file diff --git a/tests/projects/tasks/tests/testwithsuites.nim b/tests/projects/tasks/tests/testwithsuites.nim new file mode 100644 index 0000000..69b61e8 --- /dev/null +++ b/tests/projects/tasks/tests/testwithsuites.nim @@ -0,0 +1,24 @@ +import unittest + +import testproject + + +suite "test suite": + test "can add": + check add(5, 5) == 10 + + test "can add 2": + check add(5, 5) == 10 + + test "can add 3": + check add(5, 5) == 10 + +suite "test suite 2": + test "can add 4": + check add(5, 5) == 10 + + test "can add 5": + check add(5, 5) == 10 + +test "can add 6": + check add(5, 5) == 10 diff --git a/tests/projects/testproject/src/testproject.nim b/tests/projects/testproject/src/testproject.nim new file mode 100644 index 0000000..b7a2480 --- /dev/null +++ b/tests/projects/testproject/src/testproject.nim @@ -0,0 +1,7 @@ +# This is just an example to get you started. A typical library package +# exports the main API in this file. Note that you cannot rename this file +# but you can remove it if you wish. + +proc add*(x, y: int): int = + ## Adds two numbers together. + return x + y diff --git a/tests/projects/testproject/src/testproject/submodule.nim b/tests/projects/testproject/src/testproject/submodule.nim new file mode 100644 index 0000000..ad4f1b5 --- /dev/null +++ b/tests/projects/testproject/src/testproject/submodule.nim @@ -0,0 +1,12 @@ +# This is just an example to get you started. Users of your library will +# import this file by writing ``import testproject/submodule``. Feel free to rename or +# remove this file altogether. You may create additional modules alongside +# this file as required. + +type + Submodule* = object + name*: string + +proc initSubmodule*(): Submodule = + ## Initialises a new ``Submodule`` object. + Submodule(name: "Anonymous") diff --git a/tests/projects/testproject/testproject.nimble b/tests/projects/testproject/testproject.nimble new file mode 100644 index 0000000..cb62d12 --- /dev/null +++ b/tests/projects/testproject/testproject.nimble @@ -0,0 +1,12 @@ +# Package + +version = "0.1.0" +author = "jmgomez" +description = "A new awesome nimble package" +license = "MIT" +srcDir = "src" + + +# Dependencies + +requires "nim >= 2.0.8" diff --git a/tests/projects/testproject/tests/config.nims b/tests/projects/testproject/tests/config.nims new file mode 100644 index 0000000..3bb69f8 --- /dev/null +++ b/tests/projects/testproject/tests/config.nims @@ -0,0 +1 @@ +switch("path", "$projectDir/../src") \ No newline at end of file diff --git a/tests/projects/testproject/tests/test1.nim b/tests/projects/testproject/tests/test1.nim new file mode 100644 index 0000000..17b6145 --- /dev/null +++ b/tests/projects/testproject/tests/test1.nim @@ -0,0 +1,12 @@ +# This is just an example to get you started. You may wish to put all of your +# tests into a single file, or separate them into multiple `test1`, `test2` +# etc. files (better names are recommended, just make sure the name starts with +# the letter 't'). +# +# To run these tests, simply execute `nimble test`. + +import unittest + +import testproject +test "can add": + check add(5, 5) == 10 diff --git a/tests/ttestrunner.nim b/tests/ttestrunner.nim new file mode 100644 index 0000000..fa9dd8e --- /dev/null +++ b/tests/ttestrunner.nim @@ -0,0 +1,23 @@ +import unittest +import std/[os, tables] +import testparser + +suite "Test Parser": + test "should be able to retrieve a test from a file": + let file = getCurrentDir() / "tests" / "projects" / "testproject" / "tests" / "test1.nim" + let testInfo = extractTestInfo(file) + # echo testInfo + check testInfo.suites.len == 1 + check testInfo.suites[""].tests.len == 1 + check testInfo.suites[""].tests[0].name == "can add" + check testInfo.suites[""].tests[0].line == 11 + + test "should be able to retrieve suites and tests from a file": + let file = getCurrentDir() / "tests" / "projects" / "tasks" / "tests" / "testwithsuites.nim" + let testInfo = extractTestInfo(file) + # echo testInfo + check testInfo.suites.len == 3 # 2 suites and 1 test global + check testInfo.suites["test suite"].tests.len == 3 + check testInfo.suites["test suite 2"].tests.len == 2 + check testInfo.suites[""].tests.len == 1 +