Typecheck Mapping/Listing members lazily (#628)

This changes how the language performs typechecks for mappings and
listings.

Currently, Pkl will shallow-force any Mapping and Listing to check it
the type parameter (e.g. Listing<Person> means each element is checked
to be an instance of Person).

This changes the language to check each member's type when the member
is accessed.

This also adjust test runner to handle thrown errors from within tests.

With the change to make mapping/listing typechecks lazy, we can now
correctly handle thrown errors from within a single test case.

This adjusts the test runner to consider any thrown errors as a failure
for that specific test case.
This commit is contained in:
Daniel Chao
2024-09-06 15:05:23 -07:00
committed by GitHub
parent 7001a42149
commit 7868d9d9c8
86 changed files with 3342 additions and 385 deletions

View File

@@ -107,6 +107,140 @@ class CliTestRunnerTest {
assertThat(err.toString()).isEqualTo("")
}
@Test
fun `CliTestRunner with thrown error in facts`(@TempDir tempDir: Path) {
val code =
"""
amends "pkl:test"
facts {
["fail"] {
throw("uh oh")
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(code).toString()
val out = StringWriter()
val err = StringWriter()
val opts = CliBaseOptions(sourceModules = listOf(input.toUri()), settings = URI("pkl:settings"))
val testOpts = CliTestOptions()
val runner = CliTestRunner(opts, testOpts, consoleWriter = out, errWriter = err)
assertThatCode { runner.run() }.hasMessage("Tests failed.")
assertThat(out.toString().stripFileAndLines(tempDir))
.isEqualToNormalizingNewlines(
"""
module test
fail ❌
Error:
Pkl Error
uh oh
5 | throw("uh oh")
^^^^^^^^^^^^^^
at test#facts["fail"][#1]
"""
.trimIndent()
)
assertThat(err.toString()).isEqualTo("")
}
@Test
fun `CliTestRunner with thrown error in examples -- no expected output`(@TempDir tempDir: Path) {
val code =
"""
amends "pkl:test"
examples {
["fail"] {
throw("uh oh")
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(code).toString()
val out = StringWriter()
val err = StringWriter()
val opts = CliBaseOptions(sourceModules = listOf(input.toUri()), settings = URI("pkl:settings"))
val testOpts = CliTestOptions()
val runner = CliTestRunner(opts, testOpts, consoleWriter = out, errWriter = err)
assertThatCode { runner.run() }.hasMessage("Tests failed.")
assertThat(out.toString().stripFileAndLines(tempDir))
.isEqualTo(
"""
module test
fail ❌
Error:
Pkl Error
uh oh
5 | throw("uh oh")
^^^^^^^^^^^^^^
at test#examples["fail"][#1]
"""
.trimIndent()
)
assertThat(err.toString()).isEqualTo("")
}
@Test
fun `CliTestRunner with thrown error in examples -- existing expected output`(
@TempDir tempDir: Path
) {
val code =
"""
amends "pkl:test"
examples {
["fail"] {
throw("uh oh")
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(code).toString()
tempDir
.resolve("test.pkl-expected.pcf")
.writeString(
"""
examples {
["fail"] {
"never compared to"
}
}
"""
.trimIndent()
)
val out = StringWriter()
val err = StringWriter()
val opts = CliBaseOptions(sourceModules = listOf(input.toUri()), settings = URI("pkl:settings"))
val testOpts = CliTestOptions()
val runner = CliTestRunner(opts, testOpts, consoleWriter = out, errWriter = err)
assertThatCode { runner.run() }.hasMessage("Tests failed.")
assertThat(out.toString().stripFileAndLines(tempDir))
.isEqualToNormalizingNewlines(
"""
module test
fail ❌
Error:
Pkl Error
uh oh
5 | throw("uh oh")
^^^^^^^^^^^^^^
at test#examples["fail"][#1]
"""
.trimIndent()
)
assertThat(err.toString()).isEqualTo("")
}
@Test
fun `CliTestRunner JUnit reports`(@TempDir tempDir: Path) {
val code =
@@ -149,6 +283,54 @@ class CliTestRunnerTest {
)
}
@Test
fun `CliTestRunner JUnit reports, with thrown error`(@TempDir tempDir: Path) {
val code =
"""
amends "pkl:test"
facts {
["foo"] {
9 == trace(9)
"foo" == "foo"
}
["fail"] {
throw("uh oh")
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(code).toString()
val opts = CliBaseOptions(sourceModules = listOf(input.toUri()), settings = URI("pkl:settings"))
val testOpts = CliTestOptions(junitDir = tempDir)
val runner = CliTestRunner(opts, testOpts)
assertThatCode { runner.run() }.hasMessageContaining("failed")
val junitReport = tempDir.resolve("test.xml").readString().stripFileAndLines(tempDir)
assertThat(junitReport)
.isEqualTo(
"""
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="test" tests="2" failures="0">
<testcase classname="test" name="foo"></testcase>
<testcase classname="test" name="fail">
<error message="uh oh"> Pkl Error
uh oh
9 | throw(&quot;uh oh&quot;)
^^^^^^^^^^^^^^
at test#facts[&quot;fail&quot;][#1]
</error>
</testcase>
<system-err><![CDATA[9 = 9
]]></system-err>
</testsuite>
"""
.trimIndent()
)
}
@Test
fun `CliTestRunner duplicated JUnit reports`(@TempDir tempDir: Path) {
val foo =