From 984272beb4d26ee667c92ae3778a5080185fcce1 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 7 Nov 2021 17:25:53 +0100 Subject: [PATCH] migrated compilerAst module to KoTest (but not finished with the assertions yet) --- codeGeneration/build.gradle | 1 + compilerAst/build.gradle | 8 +- compilerAst/compilerAst.iml | 4 +- compilerAst/test/ProjectConfig.kt | 8 + compilerAst/test/TestAstToSourceText.kt | 29 +-- compilerAst/test/TestProg8Parser.kt | 199 ++++++---------- compilerAst/test/TestProgram.kt | 109 +++++++++ compilerAst/test/TestSourceCode.kt | 10 +- compilerAst/test/TestSubroutines.kt | 6 +- compilerAst/test/ast/ProgramTests.kt | 118 ---------- compilerAst/test/helpers/paths.kt | 22 +- compilerAst/test/helpers_pathsTests.kt | 291 ++++++++++-------------- docs/source/todo.rst | 2 + scripts/clean.sh | 2 +- 14 files changed, 350 insertions(+), 459 deletions(-) create mode 100644 compilerAst/test/ProjectConfig.kt create mode 100644 compilerAst/test/TestProgram.kt delete mode 100644 compilerAst/test/ast/ProgramTests.kt diff --git a/codeGeneration/build.gradle b/codeGeneration/build.gradle index 78eeb2c84..e4bc7d4a3 100644 --- a/codeGeneration/build.gradle +++ b/codeGeneration/build.gradle @@ -3,6 +3,7 @@ plugins { id 'java' id 'application' id "org.jetbrains.kotlin.jvm" + id "io.kotest" version "0.3.8" } java { diff --git a/compilerAst/build.gradle b/compilerAst/build.gradle index 914a217f2..851af1f91 100644 --- a/compilerAst/build.gradle +++ b/compilerAst/build.gradle @@ -1,9 +1,9 @@ plugins { - id 'java' + id "java" id "org.jetbrains.kotlin.jvm" + id "io.kotest" version "0.3.8" } - java { toolchain { languageVersion = JavaLanguageVersion.of(javaVersion) @@ -15,9 +15,8 @@ dependencies { implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12" implementation project(':parser') + testImplementation 'io.kotest:kotest-runner-junit5-jvm:4.6.3' testImplementation "org.jetbrains.kotlin:kotlin-test-junit5" - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2' - testImplementation 'org.hamcrest:hamcrest:2.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } @@ -42,7 +41,6 @@ sourceSets { } test { - // Enable JUnit 5 (Gradle 4.6+). useJUnitPlatform() // Always run tests, even when nothing changed. diff --git a/compilerAst/compilerAst.iml b/compilerAst/compilerAst.iml index 80bcad8dc..6d79055c8 100644 --- a/compilerAst/compilerAst.iml +++ b/compilerAst/compilerAst.iml @@ -13,8 +13,8 @@ - - + + \ No newline at end of file diff --git a/compilerAst/test/ProjectConfig.kt b/compilerAst/test/ProjectConfig.kt new file mode 100644 index 000000000..b0799e39f --- /dev/null +++ b/compilerAst/test/ProjectConfig.kt @@ -0,0 +1,8 @@ +package prog8tests.ast + +import io.kotest.core.config.AbstractProjectConfig +import kotlin.math.max + +object ProjectConfig : AbstractProjectConfig() { + override val parallelism = max(2, Runtime.getRuntime().availableProcessors() / 2) +} diff --git a/compilerAst/test/TestAstToSourceText.kt b/compilerAst/test/TestAstToSourceText.kt index b322e0b1a..ce2f0517c 100644 --- a/compilerAst/test/TestAstToSourceText.kt +++ b/compilerAst/test/TestAstToSourceText.kt @@ -1,7 +1,7 @@ package prog8tests.ast -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.string.shouldContain import prog8.ast.AstToSourceTextConverter import prog8.ast.Module import prog8.ast.Program @@ -12,11 +12,9 @@ import prog8.parser.SourceCode import prog8tests.ast.helpers.DummyFunctions import prog8tests.ast.helpers.DummyMemsizer import prog8tests.ast.helpers.DummyStringEncoder -import kotlin.test.assertContains -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class TestAstToSourceText { +class TestAstToSourceText: AnnotationSpec() { private fun generateP8(module: Module) : String { val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder) @@ -44,24 +42,21 @@ class TestAstToSourceText { fun testMentionsInternedStringsModule() { val orig = SourceCode.Text("\n") val (txt, _) = roundTrip(parseModule(orig)) - // assertContains has *actual* first! - assertContains(txt, Regex(";.*$internedStringsModuleName")) + txt shouldContain Regex(";.*$internedStringsModuleName") } @Test fun testImportDirectiveWithLib() { val orig = SourceCode.Text("%import textio\n") val (txt, _) = roundTrip(parseModule(orig)) - // assertContains has *actual* first! - assertContains(txt, Regex("%import +textio")) + txt shouldContain Regex("%import +textio") } @Test fun testImportDirectiveWithUserModule() { val orig = SourceCode.Text("%import my_own_stuff\n") val (txt, _) = roundTrip(parseModule(orig)) - // assertContains has *actual* first! - assertContains(txt, Regex("%import +my_own_stuff")) + txt shouldContain Regex("%import +my_own_stuff") } @@ -73,8 +68,7 @@ class TestAstToSourceText { } """) val (txt, _) = roundTrip(parseModule(orig)) - // assertContains has *actual* first! - assertContains(txt, Regex("str +s += +\"fooBar\\\\n\"")) + txt shouldContain Regex("str +s += +\"fooBar\\\\n\"") } @Test @@ -85,8 +79,7 @@ class TestAstToSourceText { } """) val (txt, _) = roundTrip(parseModule(orig)) - // assertContains has *actual* first! - assertContains(txt, Regex("str +sAlt += +@\"fooBar\\\\n\"")) + txt shouldContain Regex("str +sAlt += +@\"fooBar\\\\n\"") } @Test @@ -97,8 +90,7 @@ class TestAstToSourceText { } """) val (txt, _) = roundTrip(parseModule(orig)) - // assertContains has *actual* first! - assertContains(txt, Regex("ubyte +c += +'x'"), "char literal") + txt shouldContain Regex("ubyte +c += +'x'") } @Test @@ -109,8 +101,7 @@ class TestAstToSourceText { } """) val (txt, _) = roundTrip(parseModule(orig)) - // assertContains has *actual* first! - assertContains(txt, Regex("ubyte +cAlt += +@'x'"), "alt char literal") + txt shouldContain Regex("ubyte +cAlt += +@'x'") } } diff --git a/compilerAst/test/TestProg8Parser.kt b/compilerAst/test/TestProg8Parser.kt index 48d35c23d..fbd0afe8c 100644 --- a/compilerAst/test/TestProg8Parser.kt +++ b/compilerAst/test/TestProg8Parser.kt @@ -1,8 +1,6 @@ package prog8tests.ast -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance +import io.kotest.core.spec.style.FunSpec import prog8.ast.IFunctionCall import prog8.ast.Module import prog8.ast.Node @@ -27,36 +25,28 @@ import kotlin.io.path.nameWithoutExtension import kotlin.test.* -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class TestProg8Parser { +class TestProg8Parser: FunSpec( { - @Nested - inner class Newline { + context("Newline at end") { + test("is not required - #40, fixed by #45") { + val nl = "\n" // say, Unix-style (different flavours tested elsewhere) + val src = SourceCode.Text("foo {" + nl + "}") // source ends with '}' (= NO newline, issue #40) - @Nested - inner class AtEnd { - - @Test - fun `is not required - #40, fixed by #45`() { - val nl = "\n" // say, Unix-style (different flavours tested elsewhere) - val src = SourceCode.Text("foo {" + nl + "}") // source ends with '}' (= NO newline, issue #40) - - // #40: Prog8ANTLRParser would report (throw) "missing at ''" - val module = parseModule(src) - assertEquals(1, module.statements.size) - } - - @Test - fun `is still accepted - #40, fixed by #45`() { - val nl = "\n" // say, Unix-style (different flavours tested elsewhere) - val srcText = "foo {" + nl + "}" + nl // source does end with a newline (issue #40) - val module = parseModule(SourceCode.Text(srcText)) - assertEquals(1, module.statements.size) - } + // #40: Prog8ANTLRParser would report (throw) "missing at ''" + val module = parseModule(src) + assertEquals(1, module.statements.size) } - @Test - fun `is required after each block except the last`() { + test("is still accepted - #40, fixed by #45") { + val nl = "\n" // say, Unix-style (different flavours tested elsewhere) + val srcText = "foo {" + nl + "}" + nl // source does end with a newline (issue #40) + val module = parseModule(SourceCode.Text(srcText)) + assertEquals(1, module.statements.size) + } + } + + context("Newline") { + test("is required after each block except the last") { val nl = "\n" // say, Unix-style (different flavours tested elsewhere) // BAD: 2nd block `bar` does NOT start on new line; however, there's is a nl at the very end @@ -70,8 +60,7 @@ class TestProg8Parser { assertEquals(2, module.statements.size) } - @Test - fun `is required between two Blocks or Directives - #47`() { + test("is required between two Blocks or Directives - #47") { // block and block assertFailsWith{ parseModule(SourceCode.Text(""" blockA { @@ -99,8 +88,7 @@ class TestProg8Parser { """)) } } - @Test - fun `can be Win, Unix or mixed, even mixed`() { + test("can be Win, Unix or mixed, even mixed") { val nlWin = "\r\n" val nlUnix = "\n" val nlMac = "\r" @@ -128,11 +116,9 @@ class TestProg8Parser { } } - @Nested - inner class EOLsInterleavedWithComments { + context("EOLsInterleavedWithComments") { - @Test - fun `are ok before first block - #47`() { + test("are ok before first block - #47") { // issue: #47 val srcText = """ ; comment @@ -146,8 +132,7 @@ class TestProg8Parser { assertEquals(1, module.statements.size) } - @Test - fun `are ok between blocks - #47`() { + test("are ok between blocks - #47") { // issue: #47 val srcText = """ blockA { @@ -163,8 +148,7 @@ class TestProg8Parser { assertEquals(2, module.statements.size) } - @Test - fun `are ok after last block - #47`() { + test("are ok after last block - #47") { // issue: #47 val srcText = """ blockA { @@ -179,11 +163,8 @@ class TestProg8Parser { } } - - @Nested - inner class ImportDirectives { - @Test - fun `should not be looked into by the parser`() { + context("ImportDirectives") { + test("should not be looked into by the parser") { val importedNoExt = assumeNotExists(fixturesDir, "i_do_not_exist") assumeNotExists(fixturesDir, "i_do_not_exist.p8") val text = "%import ${importedNoExt.name}" @@ -193,27 +174,21 @@ class TestProg8Parser { } } - - @Nested - inner class EmptySourcecode { - @Test - fun `from an empty string should result in empty Module`() { + context("EmptySourcecode") { + test("from an empty string should result in empty Module") { val module = parseModule(SourceCode.Text("")) assertEquals(0, module.statements.size) } - @Test - fun `from an empty file should result in empty Module`() { + test("from an empty file should result in empty Module") { val path = assumeReadableFile(fixturesDir, "empty.p8") val module = parseModule(SourceCode.File(path)) assertEquals(0, module.statements.size) } } - @Nested - inner class NameOfModule { - @Test - fun `parsed from a string`() { + context("NameOfModule") { + test("parsed from a string") { val srcText = """ main { } @@ -224,18 +199,16 @@ class TestProg8Parser { assertContains(module.name, Regex("^$")) } - @Test - fun `parsed from a file`() { + test("parsed from a file") { val path = assumeReadableFile(fixturesDir, "simple_main.p8") val module = parseModule(SourceCode.File(path)) assertEquals(path.nameWithoutExtension, module.name) } } - @Nested - inner class PositionOfAstNodesAndParseErrors { + context("PositionOfAstNodesAndParseErrors") { - private fun assertPosition( + fun assertPosition( actual: Position, expFile: String? = null, expLine: Int? = null, @@ -249,7 +222,7 @@ class TestProg8Parser { if (expFile != null) assertEquals(expFile, actual.file, ".position.file") } - private fun assertPosition( + fun assertPosition( actual: Position, expFile: Regex? = null, expLine: Int? = null, @@ -264,7 +237,7 @@ class TestProg8Parser { if (expFile != null) assertContains(actual.file, expFile, ".position.file") } - private fun assertPositionOf( + fun assertPositionOf( actual: Node, expFile: String? = null, expLine: Int? = null, @@ -273,7 +246,7 @@ class TestProg8Parser { ) = assertPosition(actual.position, expFile, expLine, expStartCol, expEndCol) - private fun assertPositionOf( + fun assertPositionOf( actual: Node, expFile: Regex? = null, expLine: Int? = null, @@ -283,24 +256,21 @@ class TestProg8Parser { assertPosition(actual.position, expFile, expLine, expStartCol, expEndCol) - @Test - fun `in ParseError from bad string source code`() { + test("in ParseError from bad string source code") { val srcText = "bad * { }\n" val e = assertFailsWith { parseModule(SourceCode.Text(srcText)) } assertPosition(e.position, Regex("^$"), 1, 4, 4) } - @Test - fun `in ParseError from bad file source code`() { + test("in ParseError from bad file source code") { val path = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8") val e = assertFailsWith { parseModule(SourceCode.File(path)) } assertPosition(e.position, SourceCode.relative(path).toString(), 2, 6) } - @Test - fun `of Module parsed from a string`() { + test("of Module parsed from a string") { val srcText = """ main { } @@ -309,15 +279,13 @@ class TestProg8Parser { assertPositionOf(module, Regex("^$"), 1, 0) } - @Test - fun `of Module parsed from a file`() { + test("of Module parsed from a file") { val path = assumeReadableFile(fixturesDir, "simple_main.p8") val module = parseModule(SourceCode.File(path)) assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) } - @Test - fun `of non-root Nodes parsed from file`() { + test("of non-root Nodes parsed from file") { val path = assumeReadableFile(fixturesDir, "simple_main.p8") val module = parseModule(SourceCode.File(path)) @@ -329,9 +297,7 @@ class TestProg8Parser { assertPositionOf(startSub, mpf, 3, 4, 6) } - - @Test - fun `of non-root Nodes parsed from a string`() { + test("of non-root Nodes parsed from a string") { val srcText = """ %zeropage basicsafe main { @@ -369,10 +335,21 @@ class TestProg8Parser { } } - @Nested - inner class PositionFile { - @Test - fun `isn't absolute for filesystem paths`() { + context("PositionFile") { + fun assertSomethingForAllNodes(module: Module, asserter: (Node) -> Unit) { + asserter(module) + module.statements.forEach(asserter) + module.statements.filterIsInstance().forEach { b -> + asserter(b) + b.statements.forEach(asserter) + b.statements.filterIsInstance().forEach { s -> + asserter(s) + s.statements.forEach(asserter) + } + } + } + + test("isn't absolute for filesystem paths") { val path = assumeReadableFile(fixturesDir, "simple_main.p8") val module = parseModule(SourceCode.File(path)) assertSomethingForAllNodes(module) { @@ -381,8 +358,7 @@ class TestProg8Parser { } } - @Test - fun `is mangled string id for string sources`() + test("is mangled string id for string sources") { val srcText=""" %zeropage basicsafe @@ -399,8 +375,7 @@ class TestProg8Parser { } } - @Test - fun `is library prefixed path for resources`() + test("is library prefixed path for resources") { val resource = SourceCode.Resource("prog8lib/math.p8") val module = parseModule(resource) @@ -408,25 +383,11 @@ class TestProg8Parser { assertTrue(it.position.file.startsWith(SourceCode.libraryFilePrefix)) } } + } - private fun assertSomethingForAllNodes(module: Module, asserter: (Node) -> Unit) { - asserter(module) - module.statements.forEach(asserter) - module.statements.filterIsInstance().forEach { b -> - asserter(b) - b.statements.forEach(asserter) - b.statements.filterIsInstance().forEach { s -> - asserter(s) - s.statements.forEach(asserter) - } - } - } } + context("CharLiterals") { - @Nested - inner class CharLiterals { - - @Test - fun `in argument position, no altEnc`() { + test("in argument position, no altEnc") { val src = SourceCode.Text(""" main { sub start() { @@ -446,8 +407,7 @@ class TestProg8Parser { assertEquals('\n', char.value) } - @Test - fun `on rhs of block-level var decl, no AltEnc`() { + test("on rhs of block-level var decl, no AltEnc") { val src = SourceCode.Text(""" main { ubyte c = 'x' @@ -463,8 +423,7 @@ class TestProg8Parser { assertEquals(false, rhs.altEncoding, "char literal's .altEncoding") } - @Test - fun `on rhs of block-level const decl, with AltEnc`() { + test("on rhs of block-level const decl, with AltEnc") { val src = SourceCode.Text(""" main { const ubyte c = @'x' @@ -480,8 +439,7 @@ class TestProg8Parser { assertEquals(true, rhs.altEncoding, "char literal's .altEncoding") } - @Test - fun `on rhs of subroutine-level var decl, no AltEnc`() { + test("on rhs of subroutine-level var decl, no AltEnc") { val src = SourceCode.Text(""" main { sub start() { @@ -500,8 +458,7 @@ class TestProg8Parser { assertEquals(false, rhs.altEncoding, "char literal's .altEncoding") } - @Test - fun `on rhs of subroutine-level const decl, with AltEnc`() { + test("on rhs of subroutine-level const decl, with AltEnc") { val src = SourceCode.Text(""" main { sub start() { @@ -521,11 +478,9 @@ class TestProg8Parser { } } - @Nested - inner class Ranges { + context("Ranges") { - @Test - fun `in for-loops`() { + test("in for-loops") { val module = parseModule(SourceCode.Text(""" main { sub start() { @@ -572,8 +527,7 @@ class TestProg8Parser { } } - @Test - fun testCharLiteralConstValue() { + test("testCharLiteralConstValue") { val char1 = CharLiteral('A', false, Position.DUMMY) val char2 = CharLiteral('z', true, Position.DUMMY) @@ -582,8 +536,7 @@ class TestProg8Parser { assertEquals(122, char2.constValue(program).number.toInt()) } - @Test - fun testLiteralValueComparisons() { + test("testLiteralValueComparisons") { val ten = NumericLiteralValue(DataType.UWORD, 10, Position.DUMMY) val nine = NumericLiteralValue(DataType.UBYTE, 9, Position.DUMMY) assertEquals(ten, ten) @@ -608,8 +561,7 @@ class TestProg8Parser { assertFalse(abc!=abc) } - @Test - fun testAnonScopeStillContainsVarsDirectlyAfterParse() { + test("testAnonScopeStillContainsVarsDirectlyAfterParse") { val src = SourceCode.Text(""" main { sub start() { @@ -633,8 +585,7 @@ class TestProg8Parser { // the ast processing steps used in the compiler, will eventually move the var up to the containing scope (subroutine). } - @Test - fun testLabelsWithAnonScopesParsesFine() { + test("testLabelsWithAnonScopesParsesFine") { val src = SourceCode.Text(""" main { sub start() { @@ -663,4 +614,4 @@ class TestProg8Parser { val labels = start.statements.filterIsInstance