migrated compilerAst module to KoTest (but not finished with the assertions yet)

This commit is contained in:
Irmen de Jong 2021-11-07 17:25:53 +01:00
parent b9ce94bb68
commit 984272beb4
14 changed files with 350 additions and 459 deletions

View File

@ -3,6 +3,7 @@ plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.8"
}
java {

View File

@ -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.

View File

@ -13,8 +13,8 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="parser" />
<orderEntry type="library" name="antlr.antlr4" level="project" />
<orderEntry type="library" name="hamcrest" level="project" />
<orderEntry type="library" name="junit.jupiter" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
</component>
</module>

View File

@ -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)
}

View File

@ -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'")
}
}

View File

@ -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 <EOL> at '<EOF>'"
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 <EOL> at '<EOF>'"
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<ParseError>{ 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("^<String@[0-9a-f\\-]+>$"))
}
@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<ParseError> { parseModule(SourceCode.Text(srcText)) }
assertPosition(e.position, Regex("^<String@[0-9a-f\\-]+>$"), 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<ParseError> { 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("^<String@[0-9a-f\\-]+>$"), 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<Block>().forEach { b ->
asserter(b)
b.statements.forEach(asserter)
b.statements.filterIsInstance<Subroutine>().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<Block>().forEach { b ->
asserter(b)
b.statements.forEach(asserter)
b.statements.filterIsInstance<Subroutine>().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<Label>()
assertEquals(1, labels.size, "only one label in subroutine scope")
}
}
})

View File

@ -0,0 +1,109 @@
package prog8tests.ast
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldBeIn
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.types.shouldBeSameInstanceAs
import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.Position
import prog8.ast.internedStringsModuleName
import prog8.parser.SourceCode
import prog8tests.ast.helpers.DummyFunctions
import prog8tests.ast.helpers.DummyMemsizer
import prog8tests.ast.helpers.DummyStringEncoder
class TestProgram: FunSpec({
context("Constructor") {
test("withNameBuiltinsAndMemsizer") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
program.modules.size shouldBe 1
program.modules[0].name shouldBe internedStringsModuleName
program.modules[0].program shouldBeSameInstanceAs program
program.modules[0].parent shouldBeSameInstanceAs program.namespace
}
}
context("AddModule") {
test("withEmptyModule") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val retVal = program.addModule(m1)
retVal shouldBeSameInstanceAs program
program.modules.size shouldBe 2
m1 shouldBeIn program.modules
m1.program shouldBeSameInstanceAs program
m1.parent shouldBeSameInstanceAs program.namespace
withClue("module may not occur multiple times") {
val ex = shouldThrow<IllegalArgumentException> { program.addModule(m1) }
ex.message shouldContain m1.name
}
val m2 = Module(mutableListOf(), m1.position, m1.source)
withClue("other module but with same name may not occur multiple times") {
val ex = shouldThrow<IllegalArgumentException> { program.addModule(m2) }
ex.message shouldContain m1.name
}
}
}
context("MoveModuleToFront") {
test("withInternedStringsModule") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m = program.modules[0]
m.name shouldBe internedStringsModuleName
val retVal = program.moveModuleToFront(m)
retVal shouldBeSameInstanceAs program
program.modules[0] shouldBeSameInstanceAs m
}
test("withForeignModule") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
shouldThrow<IllegalArgumentException> { program.moveModuleToFront(m) }
}
test("withFirstOfPreviouslyAddedModules") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
program.addModule(m1)
program.addModule(m2)
val retVal = program.moveModuleToFront(m1)
retVal shouldBeSameInstanceAs program
program.modules.indexOf(m1) shouldBe 0
}
test("withSecondOfPreviouslyAddedModules") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
program.addModule(m1)
program.addModule(m2)
val retVal = program.moveModuleToFront(m2)
retVal shouldBeSameInstanceAs program
program.modules.indexOf(m2) shouldBe 0
}
}
context("Properties") {
test("modules") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val ms1 = program.modules
val ms2 = program.modules
ms2 shouldBeSameInstanceAs ms1
}
}
})

View File

@ -1,9 +1,6 @@
package prog8tests.ast
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.StringStartsWith
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import io.kotest.core.spec.style.AnnotationSpec
import prog8.parser.SourceCode
import prog8.parser.SourceCode.Companion.libraryFilePrefix
import prog8tests.ast.helpers.assumeNotExists
@ -14,8 +11,7 @@ import kotlin.io.path.Path
import kotlin.test.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestSourceCode {
class TestSourceCode: AnnotationSpec() {
@Test
fun testFromString() {
@ -29,7 +25,7 @@ class TestSourceCode {
assertEquals(text, actualText)
assertFalse(src.isFromResources)
assertFalse(src.isFromFilesystem)
assertThat(src.toString(), StringStartsWith("prog8.parser.SourceCode"))
assertTrue(src.toString().startsWith("prog8.parser.SourceCode"))
}
@Test

View File

@ -1,7 +1,6 @@
package prog8tests.ast
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import io.kotest.core.spec.style.AnnotationSpec
import prog8.ast.base.DataType
import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine
@ -12,8 +11,7 @@ import kotlin.test.assertFalse
import kotlin.test.assertTrue
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestSubroutines {
class TestSubroutines: AnnotationSpec() {
@Test
fun stringParameterAcceptedInParser() {

View File

@ -1,118 +0,0 @@
package prog8tests.ast.ast
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.containsString
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.Position
import prog8.ast.internedStringsModuleName
import prog8.parser.SourceCode
import prog8tests.ast.helpers.DummyFunctions
import prog8tests.ast.helpers.DummyMemsizer
import prog8tests.ast.helpers.DummyStringEncoder
import kotlin.test.assertContains
import kotlin.test.assertFailsWith
import kotlin.test.assertSame
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ProgramTests {
@Nested
inner class Constructor {
@Test
fun withNameBuiltinsAndMemsizer() {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
assertThat(program.modules.size, equalTo(1))
assertThat(program.modules[0].name, equalTo(internedStringsModuleName))
assertSame(program, program.modules[0].program)
assertSame(program.namespace, program.modules[0].parent)
}
}
@Nested
inner class AddModule {
@Test
fun withEmptyModule() {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val retVal = program.addModule(m1)
assertSame(program, retVal)
assertThat(program.modules.size, equalTo(2))
assertContains(program.modules, m1)
assertSame(program, m1.program)
assertSame(program.namespace, m1.parent)
assertThat("module may not occur multiple times",
assertFailsWith<IllegalArgumentException> { program.addModule(m1) }.message, containsString(m1.name))
val m2 = Module(mutableListOf(), m1.position, m1.source)
assertThat("other module but with same name may not occur multiple times",
assertFailsWith<IllegalArgumentException> { program.addModule(m2) }.message, containsString(m2.name))
}
}
@Nested
inner class MoveModuleToFront {
@Test
fun withInternedStringsModule() {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m = program.modules[0]
assertThat(m.name, equalTo(internedStringsModuleName))
val retVal = program.moveModuleToFront(m)
assertSame(program, retVal)
assertSame(m, program.modules[0])
}
@Test
fun withForeignModule() {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
assertFailsWith<IllegalArgumentException> { program.moveModuleToFront(m) }
}
@Test
fun withFirstOfPreviouslyAddedModules() {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
program.addModule(m1)
program.addModule(m2)
val retVal = program.moveModuleToFront(m1)
assertSame(program, retVal)
assertThat(program.modules.indexOf(m1), equalTo(0))
}
@Test
fun withSecondOfPreviouslyAddedModules() {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
program.addModule(m1)
program.addModule(m2)
val retVal = program.moveModuleToFront(m2)
assertSame(program, retVal)
assertThat(program.modules.indexOf(m2), equalTo(0))
}
}
@Nested
inner class Properties {
@Test
fun modules() {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val ms1 = program.modules
val ms2 = program.modules
assertSame(ms1, ms2)
}
}
}

View File

@ -1,5 +1,6 @@
package prog8tests.ast.helpers
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.*
import kotlin.test.assertFalse
@ -9,7 +10,14 @@ import kotlin.test.assertTrue
val workingDir = assumeDirectory("").absolute() // Note: "." does NOT work..!
val fixturesDir = assumeDirectory(workingDir,"test/fixtures")
val resourcesDir = assumeDirectory(workingDir,"res")
val outputDir = assumeDirectory(workingDir, "build/tmp/test")
val outputDir: Path = createIfNotExists(workingDir, "build/tmp/test").also{ assumeDirectory(workingDir, "build/tmp/test") }
fun createIfNotExists(workingDir: Path, path: String): Path {
val dir = workingDir / path
if(!dir.toFile().isDirectory)
Files.createDirectories(dir)
return dir
}
fun assumeNotExists(path: Path): Path {
assertFalse(path.exists(), "sanity check: should not exist: ${path.absolute()}")
@ -17,7 +25,7 @@ fun assumeNotExists(path: Path): Path {
}
fun assumeNotExists(pathStr: String): Path = assumeNotExists(Path(pathStr))
fun assumeNotExists(path: Path, other: String): Path = assumeNotExists(path.div(other))
fun assumeNotExists(path: Path, other: String): Path = assumeNotExists(path / other)
fun assumeReadable(path: Path): Path {
assertTrue(path.isReadable(), "sanity check: should be readable: ${path.absolute()}")
@ -32,8 +40,8 @@ fun assumeReadableFile(path: Path): Path {
fun assumeReadableFile(pathStr: String): Path = assumeReadableFile(Path(pathStr))
fun assumeReadableFile(pathStr: String, other: Path): Path = assumeReadableFile(Path(pathStr), other)
fun assumeReadableFile(pathStr: String, other: String): Path = assumeReadableFile(Path(pathStr), other)
fun assumeReadableFile(path: Path, other: String): Path = assumeReadableFile(path.div(other))
fun assumeReadableFile(path: Path, other: Path): Path = assumeReadableFile(path.div(other))
fun assumeReadableFile(path: Path, other: String): Path = assumeReadableFile(path / other)
fun assumeReadableFile(path: Path, other: Path): Path = assumeReadableFile(path / other)
fun assumeDirectory(path: Path): Path {
assertTrue(path.isDirectory(), "sanity check; should be directory: $path")
@ -41,9 +49,9 @@ fun assumeDirectory(path: Path): Path {
}
fun assumeDirectory(pathStr: String): Path = assumeDirectory(Path(pathStr))
fun assumeDirectory(path: Path, other: String): Path = assumeDirectory(path.div(other))
fun assumeDirectory(pathStr: String, other: String): Path = assumeDirectory(Path(pathStr).div(other))
fun assumeDirectory(pathStr: String, other: Path): Path = assumeDirectory(Path(pathStr).div(other))
fun assumeDirectory(path: Path, other: String): Path = assumeDirectory(path / other)
fun assumeDirectory(pathStr: String, other: String): Path = assumeDirectory(Path(pathStr) / other)
fun assumeDirectory(pathStr: String, other: Path): Path = assumeDirectory(Path(pathStr) / other)
@Deprecated("Directories are checked automatically at init.",

View File

@ -1,13 +1,11 @@
package prog8tests.ast
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import prog8tests.ast.helpers.*
import kotlin.io.path.Path
import kotlin.io.path.div
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@ -15,82 +13,71 @@ import kotlin.test.assertFailsWith
// This folder is also used by compiler/test
// but the testing of the helpers themselves must be performed ONLY HERE.
//
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class PathsHelpersTests {
class PathsHelpersTests: FunSpec({
@Nested
inner class AssumeNotExists {
context("AssumeNotExists") {
@Nested
inner class WithOnePathArg {
context("WithOnePathArg") {
@Test
fun `on non-existing path`() {
val path = fixturesDir.div("i_do_not_exist")
assertThat("should return the path",
assumeNotExists(path), `is`(path))
}
@Test
fun `on existing file`() {
assertFailsWith<java.lang.AssertionError> {
assumeNotExists(fixturesDir.div("simple_main.p8"))
test("on non-existing path") {
val path = fixturesDir / "i_do_not_exist"
withClue("should return the path") {
assertEquals(path, assumeNotExists(path))
}
}
@Test
fun `on existing directory`() {
test("on existing file") {
assertFailsWith<java.lang.AssertionError> {
assumeNotExists(fixturesDir / "simple_main.p8")
}
}
test("on existing directory") {
assertFailsWith<java.lang.AssertionError> {
assumeNotExists(fixturesDir)
}
}
}
@Nested
inner class WithOneStringArg {
context("WithOneStringArg") {
@Test
fun `on non-existing path`() {
val path = fixturesDir.div("i_do_not_exist")
assertThat("should return the path",
assumeNotExists("$path"), `is`(path))
test("on non-existing path") {
val path = fixturesDir / "i_do_not_exist"
withClue("should return the path") {
assertEquals(path, assumeNotExists("$path"))
}
}
@Test
fun `on existing file`() {
val path = fixturesDir.div("simple_main.p8")
test("on existing file") {
val path = fixturesDir / "simple_main.p8"
assertFailsWith<java.lang.AssertionError> {
assumeNotExists("$path")
}
}
@Test
fun `on existing directory`() {
test("on existing directory") {
assertFailsWith<java.lang.AssertionError> {
assumeNotExists("$fixturesDir")
}
}
}
@Nested
inner class WithPathAndStringArgs {
context("WithPathAndStringArgs") {
@Test
fun `on non-existing path`() {
val path = fixturesDir.div("i_do_not_exist")
assertThat("should return the path",
assumeNotExists(fixturesDir, "i_do_not_exist"), `is`(path))
test("on non-existing path") {
val path = fixturesDir / "i_do_not_exist"
withClue("should return the path") {
assertEquals(path, assumeNotExists(fixturesDir, "i_do_not_exist"))
}
}
@Test
fun `on existing file`() {
test("on existing file") {
assertFailsWith<java.lang.AssertionError> {
assumeNotExists(fixturesDir, "simple_main.p8")
}
}
@Test
fun `on existing directory`() {
test("on existing directory") {
assertFailsWith<java.lang.AssertionError> {
assumeNotExists(fixturesDir, "..")
}
@ -98,265 +85,225 @@ class PathsHelpersTests {
}
}
@Nested
inner class AssumeDirectory {
context("AssumeDirectory") {
@Nested
inner class WithOnePathArg {
@Test
fun `on non-existing path`() {
val path = fixturesDir.div("i_do_not_exist")
context("WithOnePathArg") {
test("on non-existing path") {
val path = fixturesDir / "i_do_not_exist"
assertFailsWith<AssertionError> {
assumeDirectory(path)
}
}
@Test
fun `on existing file`() {
val path = fixturesDir.div("simple_main.p8")
test("on existing file") {
val path = fixturesDir / "simple_main.p8"
assertFailsWith<AssertionError> {
assumeDirectory(path)
}
}
@Test
fun `on existing directory`() {
test("on existing directory") {
val path = workingDir
assertThat("should return the path", assumeDirectory(path), `is`(path))
withClue("should return the path") {
assertEquals(path, assumeDirectory(path))
}
}
}
@Nested
inner class WithOneStringArg {
@Test
fun `on non-existing path`() {
val path = fixturesDir.div("i_do_not_exist")
context("WithOneStringArg") {
test("on non-existing path") {
val path = fixturesDir / "i_do_not_exist"
assertFailsWith<AssertionError> {
assumeDirectory("$path")
}
}
@Test
fun `on existing file`() {
val path = fixturesDir.div("simple_main.p8")
test("on existing file") {
val path = fixturesDir / "simple_main.p8"
assertFailsWith<AssertionError> {
assumeDirectory("$path")
}
}
@Test
fun `on existing directory`() {
test("on existing directory") {
val path = workingDir
assertThat("should return the path",
assumeDirectory("$path"), `is`(path))
withClue("should return the path") {
assertEquals(path, assumeDirectory("$path"))
}
}
}
@Nested
inner class WithPathAndStringArgs {
@Test
fun `on non-existing path`() {
context("WithPathAndStringArgs") {
test("on non-existing path") {
assertFailsWith<AssertionError> {
assumeDirectory(fixturesDir, "i_do_not_exist")
}
}
@Test
fun `on existing file`() {
test("on existing file") {
assertFailsWith<AssertionError> {
assumeDirectory(fixturesDir, "simple_main.p8")
}
}
@Test
fun `on existing directory`() {
val path = workingDir.div("..")
assertThat(
"should return resulting path",
assumeDirectory(workingDir, ".."), `is`(path)
)
test("on existing directory") {
val path = workingDir / ".."
withClue("should return resulting path") {
assertEquals(path, assumeDirectory(workingDir, ".."))
}
}
}
@Nested
inner class WithStringAndStringArgs {
@Test
fun `on non-existing path`() {
context("WithStringAndStringArgs") {
test("on non-existing path") {
assertFailsWith<AssertionError> {
assumeDirectory("$fixturesDir", "i_do_not_exist")
}
}
@Test
fun `on existing file`() {
test("on existing file") {
assertFailsWith<AssertionError> {
assumeDirectory("$fixturesDir", "simple_main.p8")
}
}
@Test
fun `on existing directory`() {
val path = workingDir.div("..")
assertThat(
"should return resulting path",
assumeDirectory("$workingDir", ".."), `is`(path)
)
test("on existing directory") {
val path = workingDir / ".."
withClue("should return resulting path") {
assertEquals(path, assumeDirectory("$workingDir", ".."))
}
}
}
@Nested
inner class WithStringAndPathArgs {
@Test
fun `on non-existing path`() {
context("WithStringAndPathArgs") {
test("on non-existing path") {
assertFailsWith<AssertionError> {
assumeDirectory("$fixturesDir", Path("i_do_not_exist"))
}
}
@Test
fun `on existing file`() {
test("on existing file") {
assertFailsWith<AssertionError> {
assumeDirectory("$fixturesDir", Path("simple_main.p8"))
}
}
@Test
fun `on existing directory`() {
val path = workingDir.div("..")
assertThat(
"should return resulting path",
assumeDirectory("$workingDir", Path("..")), `is`(path)
)
test("on existing directory") {
val path = workingDir / ".."
withClue("should return resulting path") {
assertEquals(path, assumeDirectory("$workingDir", Path("..")))
}
}
}
}
@Nested
inner class AssumeReadableFile {
context("AssumeReadableFile") {
@Nested
inner class WithOnePathArg {
context("WithOnePathArg") {
@Test
fun `on non-existing path`() {
val path = fixturesDir.div("i_do_not_exist")
test("on non-existing path") {
val path = fixturesDir / "i_do_not_exist"
assertFailsWith<AssertionError> {
assumeReadableFile(path)
}
}
@Test
fun `on readable file`() {
val path = fixturesDir.div("simple_main.p8")
assertThat("should return the path",
assumeReadableFile(path), `is`(path))
test("on readable file") {
val path = fixturesDir / "simple_main.p8"
withClue("should return the path") {
assertEquals(path, assumeReadableFile(path))
}
}
@Test
fun `on directory`() {
test("on directory") {
assertFailsWith<AssertionError> {
assumeReadableFile(fixturesDir)
}
}
}
@Nested
inner class WithOneStringArg {
context("WithOneStringArg") {
@Test
fun `on non-existing path`() {
val path = fixturesDir.div("i_do_not_exist")
test("on non-existing path") {
val path = fixturesDir / "i_do_not_exist"
assertFailsWith<AssertionError> {
assumeReadableFile("$path")
}
}
@Test
fun `on readable file`() {
val path = fixturesDir.div("simple_main.p8")
assertThat("should return the resulting path",
assumeReadableFile("$path"), `is`(path))
test("on readable file") {
val path = fixturesDir / "simple_main.p8"
withClue("should return the resulting path") {
assertEquals(path, assumeReadableFile("$path"))
}
}
@Test
fun `on directory`() {
test("on directory") {
assertFailsWith<AssertionError> {
assumeReadableFile("$fixturesDir")
}
}
}
@Nested
inner class WithPathAndStringArgs {
@Test
fun `on non-existing path`() {
context("WithPathAndStringArgs") {
test("on non-existing path") {
assertFailsWith<java.lang.AssertionError> {
assumeReadableFile(fixturesDir, "i_do_not_exist")
}
}
@Test
fun `on readable file`() {
val path = fixturesDir.div("simple_main.p8")
assertThat("should return the resulting path",
assumeReadableFile(fixturesDir, "simple_main.p8"), `is`(path))
test("on readable file") {
val path = fixturesDir / "simple_main.p8"
withClue("should return the resulting path") {
assertEquals(path, assumeReadableFile(fixturesDir, "simple_main.p8"))
}
}
@Test
fun `on directory`() {
test("on directory") {
assertFailsWith<AssertionError> {
assumeReadableFile(fixturesDir, "..")
}
}
}
@Nested
inner class WithPathAndPathArgs {
@Test
fun `on non-existing path`() {
context("WithPathAndPathArgs") {
test("on non-existing path") {
assertFailsWith<java.lang.AssertionError> {
assumeReadableFile(fixturesDir, Path("i_do_not_exist"))
}
}
@Test fun `on readable file`() {
assertThat("should return the resulting path",
assumeReadableFile(fixturesDir, Path("simple_main.p8")),
`is`(fixturesDir.div("simple_main.p8"))
)
test("on readable file") {
withClue("should return the resulting path") {
assertEquals(fixturesDir / "simple_main.p8", assumeReadableFile(fixturesDir, Path("simple_main.p8")))
}
}
@Test
fun `on directory`() {
test("on directory") {
assertFailsWith<AssertionError> {
assumeReadableFile(fixturesDir, Path(".."))
}
}
}
@Nested
inner class WithStringAndStringArgs {
@Test
fun `on non-existing path`() {
context("WithStringAndStringArgs") {
test("on non-existing path") {
assertFailsWith<java.lang.AssertionError> {
assumeReadableFile("$fixturesDir", "i_do_not_exist")
}
}
@Test
fun `on readable file`() {
assertThat("should return the resulting path",
assumeReadableFile(fixturesDir.toString(), "simple_main.p8"),
`is`(fixturesDir.div("simple_main.p8"))
)
test("on readable file") {
withClue("should return the resulting path") {
assertEquals( fixturesDir / "simple_main.p8", assumeReadableFile(fixturesDir.toString(), "simple_main.p8"))
}
}
@Test
fun `on directory`() {
test("on directory") {
assertFailsWith<AssertionError> {
assumeReadableFile("$fixturesDir", "..")
}
}
}
}
}
})

View File

@ -3,6 +3,8 @@ TODO
For next compiler release (7.3)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- replace all old asserts in compilerAst tests by kotest equivalents
- remove kotlin.test and junit dependencies in compilerAst module
...

View File

@ -2,5 +2,5 @@
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list a.out
rm -rf build out
rm -rf compiler/build compilerAst/build dbusCompilerService/build httpCompilerService/build parser/build
rm -rf compiler/build codeGeneration/build codeOptimizers/build compilerInterfaces/build compilerAst/build dbusCompilerService/build httpCompilerService/build parser/build