From e18d962033d749e8f5c612d212b2730cdf41bc27 Mon Sep 17 00:00:00 2001 From: Aagam Shah <69311837+AAGAM17@users.noreply.github.com> Date: Sat, 17 Oct 2026 00:32:33 +0530 Subject: [PATCH] Force the returned value in test.catch() and test.catchOrNull() (#1910) --- .../org/pkl/core/stdlib/test/TestNodes.java | 4 ++-- .../input/api/testModule.pkl | 19 +++++++++++++++++++ .../output/api/testModule.pcf | 13 +++++++++++++ .../LanguageSnippetTests/output/basic/as3.pcf | 4 ++-- stdlib/test.pkl | 4 ++++ 5 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/api/testModule.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/api/testModule.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/test/TestNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/test/TestNodes.java index bebb40edf..ecd413a42 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/test/TestNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/test/TestNodes.java @@ -39,7 +39,7 @@ public final class TestNodes { @Specialization protected String eval(@SuppressWarnings("unused") VmTyped self, VmFunction function) { try { - applyLambdaNode.execute(function); + VmValue.force(applyLambdaNode.execute(function), false); } catch (VmException e) { return render(e); } @@ -54,7 +54,7 @@ public final class TestNodes { @Specialization protected Object eval(@SuppressWarnings("unused") VmTyped self, VmFunction function) { try { - applyLambdaNode.execute(function); + VmValue.force(applyLambdaNode.execute(function), false); } catch (VmException e) { return render(e); } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/testModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/testModule.pkl new file mode 100644 index 000000000..d122dd1c1 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/testModule.pkl @@ -0,0 +1,19 @@ +amends "../snippetTest.pkl" + +import "pkl:test" + +examples { + ["catch"] { + test.catch(() -> throw("ouch")) + // the returned value is fully evaluated, so these are caught too + test.catch(() -> new Listing { 1 } as Listing) + test.catch(() -> new Dynamic { foo { bar = throw("nested") } }) + } + ["catchOrNull"] { + test.catchOrNull(() -> 42) + test.catchOrNull(() -> throw("ouch")) + // the returned value is fully evaluated, so these are caught too + test.catchOrNull(() -> new Listing { 1 } as Listing) + test.catchOrNull(() -> new Dynamic { foo { bar = throw("nested") } }) + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/testModule.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/testModule.pcf new file mode 100644 index 000000000..f06018414 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/testModule.pcf @@ -0,0 +1,13 @@ +examples { + ["catch"] { + "ouch" + "Expected value of type `String`, but got type `Int`. Value: 1" + "nested" + } + ["catchOrNull"] { + null + "ouch" + "Expected value of type `String`, but got type `Int`. Value: 1" + "nested" + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/as3.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/as3.pcf index c20245700..052f5ff2e 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/as3.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/as3.pcf @@ -5,11 +5,11 @@ examples { "Expected value of type `Int`, but got type `String`. Value: \"one\"" } ["listing"] { - true + false "Expected value of type `String`, but got type `Int`. Value: 1" } ["mapping"] { - true + false "Expected value of type `String`, but got type `Int`. Value: 1" "Expected value of type `String`, but got type `Int`. Value: 2" "Expected value of type `Int`, but got type `String`. Value: \"hi\"" diff --git a/stdlib/test.pkl b/stdlib/test.pkl index 9f998ac03..67d558343 100644 --- a/stdlib/test.pkl +++ b/stdlib/test.pkl @@ -88,10 +88,14 @@ examples: Mapping>? /// Applies [fun] and returns the error thrown. /// +/// The value returned by [fun] is fully evaluated, so an error inside a nested object member will be caught too. +/// /// Throws if [fun] did not throw an error. external const function catch(fun: () -> Any): String /// Applies [fun] and returns the error thrown. /// +/// The value returned by [fun] is fully evaluated, so an error inside a nested object member will be caught too. +/// /// Returns [null] if [fun] did not throw an error. external const function catchOrNull(fun: () -> Any): String?