mirror of
https://github.com/irmen/prog8.git
synced 2025-11-02 13:16:07 +00:00
This fixes a bug where syslib and such gets optimized away when it is loaded from an alternative library location using the configurable target library path property setting.
277 lines
13 KiB
Kotlin
277 lines
13 KiB
Kotlin
package prog8tests.compiler
|
|
|
|
import com.github.michaelbull.result.getErrorOrElse
|
|
import com.github.michaelbull.result.getOrElse
|
|
import io.kotest.assertions.fail
|
|
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 prog8.ast.Program
|
|
import prog8.code.core.IErrorReporter
|
|
import prog8.code.source.SourceCode
|
|
import prog8.code.internedStringsModuleName
|
|
import prog8.compiler.ModuleImporter
|
|
import prog8.parser.ParseError
|
|
import prog8tests.helpers.*
|
|
import kotlin.io.path.*
|
|
|
|
|
|
class TestModuleImporter: FunSpec({
|
|
val count = listOf("1st", "2nd", "3rd", "4th", "5th")
|
|
|
|
lateinit var program: Program
|
|
|
|
beforeTest {
|
|
program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
|
}
|
|
|
|
fun makeImporter(errors: IErrorReporter? = null, searchIn: Iterable<String>) =
|
|
ModuleImporter(program, "blah", errors ?: ErrorReporterForTests(false), searchIn.toList(), emptyList())
|
|
|
|
fun makeImporter(errors: IErrorReporter?, vararg searchIn: String): ModuleImporter {
|
|
return makeImporter(errors, searchIn.asList())
|
|
}
|
|
|
|
context("ImportModule") {
|
|
|
|
context("WithInvalidPath") {
|
|
test("testNonexisting") {
|
|
val dirRel = assumeDirectory(".", workingDir.relativize(fixturesDir))
|
|
val importer = makeImporter(null, dirRel.invariantSeparatorsPathString)
|
|
val srcPathRel = assumeNotExists(dirRel, "i_do_not_exist")
|
|
val srcPathAbs = srcPathRel.absolute()
|
|
val error1 = importer.importMainModule(srcPathRel).getErrorOrElse { fail("should have import error") }
|
|
withClue(".file should be normalized") {
|
|
"${error1.file}" shouldBe "${error1.file.normalize()}"
|
|
}
|
|
withClue(".file should point to specified path") {
|
|
error1.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
|
}
|
|
program.modules.size shouldBe 1
|
|
val error2 = importer.importMainModule(srcPathAbs).getErrorOrElse { fail("should have import error") }
|
|
withClue(".file should be normalized") {
|
|
"${error2.file}" shouldBe "${error2.file.normalize()}"
|
|
}
|
|
withClue(".file should point to specified path") {
|
|
error2.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
|
}
|
|
program.modules.size shouldBe 1
|
|
}
|
|
|
|
test("testDirectory") {
|
|
val srcPathRel = assumeDirectory(workingDir.relativize(fixturesDir))
|
|
val srcPathAbs = srcPathRel.absolute()
|
|
val searchIn = Path(".", "$srcPathRel").invariantSeparatorsPathString
|
|
val importer = makeImporter(null, searchIn)
|
|
|
|
shouldThrow<FileSystemException> { importer.importMainModule(srcPathRel) }
|
|
.let {
|
|
withClue(".file should be normalized") {
|
|
"${it.file}" shouldBe "${it.file.normalize()}"
|
|
}
|
|
withClue(".file should point to specified path") {
|
|
it.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
|
}
|
|
}
|
|
program.modules.size shouldBe 1
|
|
|
|
shouldThrow<FileSystemException> { importer.importMainModule(srcPathAbs) }
|
|
.let {
|
|
withClue(".file should be normalized") {
|
|
"${it.file}" shouldBe "${it.file.normalize()}"
|
|
}
|
|
withClue(".file should point to specified path") {
|
|
it.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
|
}
|
|
}
|
|
program.modules.size shouldBe 1
|
|
}
|
|
}
|
|
|
|
context("WithValidPath") {
|
|
|
|
test("testAbsolute") {
|
|
val searchIn = listOf(
|
|
Path(".").div(workingDir.relativize(fixturesDir)), // we do want a dot "." in front
|
|
).map { it.invariantSeparatorsPathString }
|
|
val importer = makeImporter(null, searchIn)
|
|
val fileName = "ast_simple_main.p8"
|
|
val path = assumeReadableFile(searchIn[0], fileName)
|
|
|
|
val module = importer.importMainModule(path.absolute()).getOrElse { throw it }
|
|
program.modules.size shouldBe 2
|
|
module shouldBeIn program.modules
|
|
module.program shouldBe program
|
|
}
|
|
|
|
test("testRelativeToWorkingDir") {
|
|
val searchIn = listOf(
|
|
Path(".").div(workingDir.relativize(fixturesDir)), // we do want a dot "." in front
|
|
).map { it.invariantSeparatorsPathString }
|
|
val importer = makeImporter(null, searchIn)
|
|
val fileName = "ast_simple_main.p8"
|
|
val path = assumeReadableFile(searchIn[0], fileName)
|
|
withClue("sanity check: path should NOT be absolute") {
|
|
path.isAbsolute shouldBe false
|
|
}
|
|
|
|
val module = importer.importMainModule(path).getOrElse { throw it }
|
|
program.modules.size shouldBe 2
|
|
module shouldBeIn program.modules
|
|
module.program shouldBe program
|
|
}
|
|
|
|
test("testRelativeTo1stDirInSearchList") {
|
|
val searchIn = Path(".")
|
|
.div(workingDir.relativize(fixturesDir))
|
|
.invariantSeparatorsPathString
|
|
val importer = makeImporter(null, searchIn)
|
|
val fileName = "ast_simple_main.p8"
|
|
val path = Path(".", fileName)
|
|
assumeReadableFile(searchIn, path)
|
|
|
|
val module = importer.importMainModule(path).getOrElse { throw it }
|
|
program.modules.size shouldBe 2
|
|
module shouldBeIn program.modules
|
|
module.program shouldBe program
|
|
}
|
|
|
|
context("WithBadFile") {
|
|
test("testWithSyntaxError") {
|
|
val searchIn = assumeDirectory("./", workingDir.relativize(fixturesDir))
|
|
val importer = makeImporter(null, searchIn.invariantSeparatorsPathString)
|
|
val srcPath = assumeReadableFile(fixturesDir, "ast_file_with_syntax_error.p8")
|
|
|
|
val act = { importer.importMainModule(srcPath) }
|
|
|
|
repeat(2) { n -> withClue(count[n] + " call") {
|
|
shouldThrow<ParseError> { act() }.let {
|
|
it.position.file shouldBe SourceCode.relative(srcPath).toString()
|
|
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
|
|
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
|
|
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
|
}
|
|
}
|
|
program.modules.size shouldBe 1
|
|
}
|
|
}
|
|
|
|
fun doTestImportingFileWithSyntaxError(repetitions: Int) {
|
|
val searchIn = assumeDirectory("./", workingDir.relativize(fixturesDir))
|
|
val importer = makeImporter(null, searchIn.invariantSeparatorsPathString)
|
|
val importing = assumeReadableFile(fixturesDir, "import_file_with_syntax_error.p8")
|
|
val imported = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
|
|
|
|
val act = { importer.importMainModule(importing) }
|
|
|
|
repeat(repetitions) { n -> withClue(count[n] + " call") {
|
|
shouldThrow<ParseError> { act() }.let {
|
|
it.position.file shouldBe SourceCode.relative(imported).toString()
|
|
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
|
|
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
|
|
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
|
}
|
|
}
|
|
withClue("imported module with error in it should not be present") { program.modules.size shouldBe 1 }
|
|
program.modules[0].name shouldBe internedStringsModuleName
|
|
}
|
|
}
|
|
|
|
test("testImportingFileWithSyntaxError_once") {
|
|
doTestImportingFileWithSyntaxError(1)
|
|
}
|
|
|
|
test("testImportingFileWithSyntaxError_twice") {
|
|
doTestImportingFileWithSyntaxError(2)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
context("ImportLibraryModule") {
|
|
context("WithInvalidName") {
|
|
test("testWithNonExistingName") {
|
|
val searchIn = assumeDirectory("./", workingDir.relativize(fixturesDir))
|
|
val errors = ErrorReporterForTests(false)
|
|
val importer = makeImporter(errors, searchIn.invariantSeparatorsPathString)
|
|
val filenameNoExt = assumeNotExists(fixturesDir, "i_do_not_exist").name
|
|
val filenameWithExt = assumeNotExists(fixturesDir, "i_do_not_exist.p8").name
|
|
|
|
repeat(2) { n ->
|
|
val result = importer.importImplicitLibraryModule(filenameNoExt)
|
|
withClue(count[n] + " call / NO .p8 extension") { result shouldBe null }
|
|
withClue(count[n] + " call / NO .p8 extension") { errors.noErrors() shouldBe false }
|
|
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist"
|
|
errors.report()
|
|
program.modules.size shouldBe 1
|
|
|
|
val result2 = importer.importImplicitLibraryModule(filenameWithExt)
|
|
withClue(count[n] + " call / with .p8 extension") { result2 shouldBe null }
|
|
withClue(count[n] + " call / with .p8 extension") { importer.errors.noErrors() shouldBe false }
|
|
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist.p8"
|
|
errors.report()
|
|
program.modules.size shouldBe 1
|
|
}
|
|
}
|
|
}
|
|
|
|
context("WithValidName") {
|
|
context("WithBadFile") {
|
|
test("testWithSyntaxError") {
|
|
val searchIn = assumeDirectory("./", workingDir.relativize(fixturesDir))
|
|
val importer = makeImporter(null, searchIn.invariantSeparatorsPathString)
|
|
val srcPath = assumeReadableFile(fixturesDir, "ast_file_with_syntax_error.p8")
|
|
|
|
repeat(2) { n -> withClue(count[n] + " call") {
|
|
shouldThrow<ParseError>()
|
|
{
|
|
importer.importImplicitLibraryModule(srcPath.nameWithoutExtension) }.let {
|
|
it.position.file shouldBe SourceCode.relative(srcPath).toString()
|
|
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
|
|
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
|
|
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
|
}
|
|
}
|
|
program.modules.size shouldBe 1
|
|
}
|
|
}
|
|
|
|
|
|
fun doTestImportingFileWithSyntaxError(repetitions: Int) {
|
|
val searchIn = assumeDirectory("./", workingDir.relativize(fixturesDir))
|
|
val importer = makeImporter(null, searchIn.invariantSeparatorsPathString)
|
|
val importing = assumeReadableFile(fixturesDir, "import_file_with_syntax_error.p8")
|
|
val imported = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
|
|
|
|
val act = { importer.importImplicitLibraryModule(importing.nameWithoutExtension) }
|
|
|
|
repeat(repetitions) { n -> withClue(count[n] + " call") {
|
|
shouldThrow<ParseError> {
|
|
act() }.let {
|
|
it.position.file shouldBe SourceCode.relative(imported).toString()
|
|
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
|
|
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
|
|
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
|
}
|
|
}
|
|
withClue("imported module with error in it should not be present") { program.modules.size shouldBe 1 }
|
|
program.modules[0].name shouldBe internedStringsModuleName
|
|
importer.errors.report()
|
|
}
|
|
}
|
|
|
|
test("testImportingFileWithSyntaxError_once") {
|
|
doTestImportingFileWithSyntaxError(1)
|
|
}
|
|
|
|
test("testImportingFileWithSyntaxError_twice") {
|
|
doTestImportingFileWithSyntaxError(2)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|