From b7ba6a8649cac881af0605e491ac7e5663b3c059 Mon Sep 17 00:00:00 2001 From: Jen Basch Date: Mon, 4 May 2026 12:25:15 -0700 Subject: [PATCH] Fix `pkl:test` fact power assertions when member source section is unavailable (#1571) Power assertions only work when the source section is available. If it is unavailable, power assertions throw a ParserError (unexpected EOF on an empty input) when re-parsing the expression for presentation. --- .../org/pkl/core/runtime/PowerAssertions.java | 5 ++++ .../java/org/pkl/core/runtime/TestRunner.java | 2 +- .../test/kotlin/org/pkl/core/EvaluatorTest.kt | 27 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/PowerAssertions.java b/pkl-core/src/main/java/org/pkl/core/runtime/PowerAssertions.java index 76182d20..1593b513 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/PowerAssertions.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/PowerAssertions.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.stream.Collectors; +import org.pkl.core.PklBugException; import org.pkl.core.ast.ConstantValueNode; import org.pkl.core.ast.expression.member.InferParentWithinMethodNode; import org.pkl.core.ast.expression.member.InferParentWithinObjectMethodNode; @@ -83,6 +84,10 @@ public class PowerAssertions { SourceSection sourceSection, Map> trackedValues, @Nullable Consumer firstFrameSuffix) { + if (!sourceSection.isAvailable()) { + throw new PklBugException("Power assertions require an available source section"); + } + out.appendSandboxed( () -> { var lines = lines(sourceSection); diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/TestRunner.java b/pkl-core/src/main/java/org/pkl/core/runtime/TestRunner.java index 3a80fc43..d9309e24 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/TestRunner.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/TestRunner.java @@ -102,7 +102,7 @@ public final class TestRunner { try { var factValue = VmUtils.readMember(listing, idx); if (factValue == Boolean.FALSE) { - if (PowerAssertions.isEnabled()) { + if (PowerAssertions.isEnabled() && member.getSourceSection().isAvailable()) { try (var valueTracker = valueTrackerFactory.create()) { listing.cachedValues.clear(); VmUtils.readMember(listing, idx); diff --git a/pkl-core/src/test/kotlin/org/pkl/core/EvaluatorTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/EvaluatorTest.kt index a731341a..619523aa 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/EvaluatorTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/EvaluatorTest.kt @@ -32,6 +32,7 @@ import org.assertj.core.api.Assertions.assertThatCode import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Test import org.junit.jupiter.api.Timeout +import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.condition.EnabledOnOs import org.junit.jupiter.api.condition.OS @@ -695,6 +696,32 @@ class EvaluatorTest { } } + @Test + fun `power assertions work with test facts with unavailable source section`() { + val evaluator = + with(EvaluatorBuilder.preconfigured()) { + powerAssertionsEnabled = true + build() + } + + assertDoesNotThrow { + evaluator.evaluateTest( + text( + """ + amends "pkl:test" + facts { + ["foo"] { + ...List(false) + } + } + """ + .trimIndent() + ), + false, + ) + } + } + private fun checkModule(module: PModule) { assertThat(module.properties.size).isEqualTo(2) assertThat(module.getProperty("name")).isEqualTo("pigeon")