mirror of
https://github.com/apple/pkl.git
synced 2026-07-06 04:55:09 +02:00
Implement Pkl binary renderer and parser (#1203)
Implements a binary renderer for Pkl values, which is a lossless capturing of Pkl data. This follows the pkl binary format that is already used with `pkl server` calls, and is made available as a Java API and also an in-language API. Also, introduces a binary parser into the corresponding `PObject` types in Java.
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
res1 = "bar"
|
||||
res2 = ""
|
||||
res3 = 1
|
||||
res4 = 2.3
|
||||
res5 = true
|
||||
res6 = false
|
||||
res7 = null
|
||||
@@ -1,25 +0,0 @@
|
||||
module com.foo.bar.MyModule
|
||||
|
||||
class Person {
|
||||
firstName: String
|
||||
lastName: String
|
||||
age: Int
|
||||
}
|
||||
|
||||
barnOwl: Person = new {
|
||||
firstName = "Barn Owl"
|
||||
lastName = "Bird"
|
||||
age = 38
|
||||
}
|
||||
|
||||
pigeon: Person = new {
|
||||
firstName = "Pigeon"
|
||||
lastName = "Bird"
|
||||
age = 41
|
||||
}
|
||||
|
||||
typealias MyPerson = Person
|
||||
|
||||
personClass = Person
|
||||
|
||||
personTypeAlias = MyPerson
|
||||
@@ -1,11 +0,0 @@
|
||||
res1: DataSize = 1.b
|
||||
res2: DataSize = 2.kb
|
||||
res3: DataSize = 3.kib
|
||||
res4: DataSize = 4.mb
|
||||
res5: DataSize = 5.mib
|
||||
res6: DataSize = 6.gb
|
||||
res7: DataSize = 7.gib
|
||||
res8: DataSize = 8.tb
|
||||
res9: DataSize = 9.tib
|
||||
res10: DataSize = 10.pb
|
||||
res11: DataSize = 11.pib
|
||||
@@ -1,7 +0,0 @@
|
||||
res1 = 1.ns
|
||||
res2 = 2.us
|
||||
res3 = 3.ms
|
||||
res4 = 4.s
|
||||
res5 = 5.min
|
||||
res6 = 6.h
|
||||
res7 = 7.d
|
||||
@@ -1,2 +0,0 @@
|
||||
res1 = IntSeq(1, 3)
|
||||
res2 = IntSeq(1, 4).step(5)
|
||||
@@ -1,6 +0,0 @@
|
||||
res1: List<Int> = List(1, 3, 5, 7)
|
||||
res2: Listing<Int> = new { 2; 4; 6; 8 }
|
||||
res3: List<Int> = List()
|
||||
res4: Listing<Int> = new {}
|
||||
res5: List<List<Int>> = List(List(1, 2))
|
||||
res6: Listing<Listing<Int>> = new { new { 1; 2 } }
|
||||
@@ -1,21 +0,0 @@
|
||||
res1: Map = Map("foo", 1, "bar", 2)
|
||||
res2: Mapping = new {
|
||||
["foo"] = 1
|
||||
["bar"] = 2
|
||||
}
|
||||
res3: Mapping = new {
|
||||
["childMap"] = new Mapping {
|
||||
["childFoo"] = 3
|
||||
}
|
||||
}
|
||||
res4: Mapping = new {
|
||||
[Map("foo", 1)] = new Mapping {
|
||||
["bar"] = 2
|
||||
}
|
||||
}
|
||||
// https://github.com/apple/pkl/issues/1151
|
||||
res5: Mapping = new {
|
||||
local self = this
|
||||
["foo"] = new Dynamic { name = "foo" }
|
||||
["bar"] = new Dynamic { name = self["foo"].name + "bar" }
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
res1 = Pair(1, 2)
|
||||
res2 = Pair("foo", "bar")
|
||||
@@ -1,3 +0,0 @@
|
||||
res1 = Regex("abc")
|
||||
res2 = Regex("")
|
||||
res3 = Regex("(?m)^abc$")
|
||||
@@ -1,3 +0,0 @@
|
||||
res1: Set<Int> = Set(1, 3, 5, 7)
|
||||
res2: Set<Int> = Set()
|
||||
res3: Set<Any> = Set(1, true, "", null)
|
||||
@@ -1,32 +0,0 @@
|
||||
- 1
|
||||
- basic
|
||||
- file:///$snippetsDir/input/basic.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
- bar
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
- ''
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
- 1
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
- 2.3
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
- true
|
||||
-
|
||||
- 16
|
||||
- res6
|
||||
- false
|
||||
-
|
||||
- 16
|
||||
- res7
|
||||
- null
|
||||
@@ -1,54 +0,0 @@
|
||||
- 1
|
||||
- com.foo.bar.MyModule
|
||||
- file:///$snippetsDir/input/classes.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- barnOwl
|
||||
-
|
||||
- 1
|
||||
- com.foo.bar.MyModule#Person
|
||||
- file:///$snippetsDir/input/classes.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- firstName
|
||||
- Barn Owl
|
||||
-
|
||||
- 16
|
||||
- lastName
|
||||
- Bird
|
||||
-
|
||||
- 16
|
||||
- age
|
||||
- 38
|
||||
-
|
||||
- 16
|
||||
- pigeon
|
||||
-
|
||||
- 1
|
||||
- com.foo.bar.MyModule#Person
|
||||
- file:///$snippetsDir/input/classes.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- firstName
|
||||
- Pigeon
|
||||
-
|
||||
- 16
|
||||
- lastName
|
||||
- Bird
|
||||
-
|
||||
- 16
|
||||
- age
|
||||
- 41
|
||||
-
|
||||
- 16
|
||||
- personClass
|
||||
-
|
||||
- 12
|
||||
-
|
||||
- 16
|
||||
- personTypeAlias
|
||||
-
|
||||
- 13
|
||||
@@ -1,81 +0,0 @@
|
||||
- 1
|
||||
- datasize
|
||||
- file:///$snippetsDir/input/datasize.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 8
|
||||
- 1.0
|
||||
- b
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 8
|
||||
- 2.0
|
||||
- kb
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 8
|
||||
- 3.0
|
||||
- kib
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
-
|
||||
- 8
|
||||
- 4.0
|
||||
- mb
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
-
|
||||
- 8
|
||||
- 5.0
|
||||
- mib
|
||||
-
|
||||
- 16
|
||||
- res6
|
||||
-
|
||||
- 8
|
||||
- 6.0
|
||||
- gb
|
||||
-
|
||||
- 16
|
||||
- res7
|
||||
-
|
||||
- 8
|
||||
- 7.0
|
||||
- gib
|
||||
-
|
||||
- 16
|
||||
- res8
|
||||
-
|
||||
- 8
|
||||
- 8.0
|
||||
- tb
|
||||
-
|
||||
- 16
|
||||
- res9
|
||||
-
|
||||
- 8
|
||||
- 9.0
|
||||
- tib
|
||||
-
|
||||
- 16
|
||||
- res10
|
||||
-
|
||||
- 8
|
||||
- 10.0
|
||||
- pb
|
||||
-
|
||||
- 16
|
||||
- res11
|
||||
-
|
||||
- 8
|
||||
- 11.0
|
||||
- pib
|
||||
@@ -1,53 +0,0 @@
|
||||
- 1
|
||||
- duration
|
||||
- file:///$snippetsDir/input/duration.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 7
|
||||
- 1.0
|
||||
- ns
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 7
|
||||
- 2.0
|
||||
- us
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 7
|
||||
- 3.0
|
||||
- ms
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
-
|
||||
- 7
|
||||
- 4.0
|
||||
- s
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
-
|
||||
- 7
|
||||
- 5.0
|
||||
- min
|
||||
-
|
||||
- 16
|
||||
- res6
|
||||
-
|
||||
- 7
|
||||
- 6.0
|
||||
- h
|
||||
-
|
||||
- 16
|
||||
- res7
|
||||
-
|
||||
- 7
|
||||
- 7.0
|
||||
- d
|
||||
@@ -1,20 +0,0 @@
|
||||
- 1
|
||||
- intseq
|
||||
- file:///$snippetsDir/input/intseq.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 10
|
||||
- 1
|
||||
- 3
|
||||
- 1
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 10
|
||||
- 1
|
||||
- 4
|
||||
- 5
|
||||
@@ -1,58 +0,0 @@
|
||||
- 1
|
||||
- list
|
||||
- file:///$snippetsDir/input/list.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 4
|
||||
-
|
||||
- 1
|
||||
- 3
|
||||
- 5
|
||||
- 7
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 5
|
||||
-
|
||||
- 2
|
||||
- 4
|
||||
- 6
|
||||
- 8
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 4
|
||||
- []
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
-
|
||||
- 5
|
||||
- []
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
-
|
||||
- 4
|
||||
-
|
||||
-
|
||||
- 4
|
||||
-
|
||||
- 1
|
||||
- 2
|
||||
-
|
||||
- 16
|
||||
- res6
|
||||
-
|
||||
- 5
|
||||
-
|
||||
-
|
||||
- 5
|
||||
-
|
||||
- 1
|
||||
- 2
|
||||
@@ -1,68 +0,0 @@
|
||||
- 1
|
||||
- map
|
||||
- file:///$snippetsDir/input/map.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 2
|
||||
-
|
||||
foo: 1
|
||||
bar: 2
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 3
|
||||
-
|
||||
foo: 1
|
||||
bar: 2
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 3
|
||||
-
|
||||
childMap:
|
||||
- 3
|
||||
-
|
||||
childFoo: 3
|
||||
-
|
||||
- 16
|
||||
- res4
|
||||
-
|
||||
- 3
|
||||
-
|
||||
?
|
||||
- 2
|
||||
-
|
||||
foo: 1
|
||||
:
|
||||
- 3
|
||||
-
|
||||
bar: 2
|
||||
-
|
||||
- 16
|
||||
- res5
|
||||
-
|
||||
- 3
|
||||
-
|
||||
foo:
|
||||
- 1
|
||||
- Dynamic
|
||||
- pkl:base
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- name
|
||||
- foo
|
||||
bar:
|
||||
- 1
|
||||
- Dynamic
|
||||
- pkl:base
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- name
|
||||
- foobar
|
||||
@@ -1,18 +0,0 @@
|
||||
- 1
|
||||
- pair
|
||||
- file:///$snippetsDir/input/pair.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 9
|
||||
- 1
|
||||
- 2
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 9
|
||||
- foo
|
||||
- bar
|
||||
@@ -1,22 +0,0 @@
|
||||
- 1
|
||||
- regex
|
||||
- file:///$snippetsDir/input/regex.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 11
|
||||
- abc
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 11
|
||||
- ''
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 11
|
||||
- (?m)^abc$
|
||||
@@ -1,30 +0,0 @@
|
||||
- 1
|
||||
- set
|
||||
- file:///$snippetsDir/input/set.pkl
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- res1
|
||||
-
|
||||
- 6
|
||||
-
|
||||
- 1
|
||||
- 3
|
||||
- 5
|
||||
- 7
|
||||
-
|
||||
- 16
|
||||
- res2
|
||||
-
|
||||
- 6
|
||||
- []
|
||||
-
|
||||
- 16
|
||||
- res3
|
||||
-
|
||||
- 6
|
||||
-
|
||||
- 1
|
||||
- true
|
||||
- ''
|
||||
- null
|
||||
@@ -308,13 +308,13 @@ abstract class AbstractServerTest {
|
||||
)
|
||||
)
|
||||
val evaluateResponse = client.receive<EvaluateResponse>()
|
||||
assertThat(evaluateResponse.result?.debugYaml)
|
||||
assertThat(evaluateResponse.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
- 6
|
||||
-
|
||||
- bird:/foo.txt
|
||||
- bird:/subdir/bar.txt
|
||||
- 'bird:/foo.txt'
|
||||
- 'bird:/subdir/bar.txt'
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -346,7 +346,7 @@ abstract class AbstractServerTest {
|
||||
)
|
||||
)
|
||||
val evaluateResponse = client.receive<EvaluateResponse>()
|
||||
assertThat(evaluateResponse.result?.debugYaml)
|
||||
assertThat(evaluateResponse.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
- 6
|
||||
@@ -547,11 +547,11 @@ abstract class AbstractServerTest {
|
||||
"""
|
||||
- 6
|
||||
-
|
||||
- bird:/Person.pkl
|
||||
- bird:/birds/parrot.pkl
|
||||
- bird:/birds/pigeon.pkl
|
||||
- bird:/majesticBirds/barnOwl.pkl
|
||||
- bird:/majesticBirds/elfOwl.pkl
|
||||
- 'bird:/Person.pkl'
|
||||
- 'bird:/birds/parrot.pkl'
|
||||
- 'bird:/birds/pigeon.pkl'
|
||||
- 'bird:/majesticBirds/barnOwl.pkl'
|
||||
- 'bird:/majesticBirds/elfOwl.pkl'
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -643,7 +643,7 @@ abstract class AbstractServerTest {
|
||||
val response = client.receive<EvaluateResponse>()
|
||||
assertThat(response.error).isNull()
|
||||
val tripleQuote = "\"\"\""
|
||||
assertThat(response.result?.debugYaml)
|
||||
assertThat(response.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
|
||||
@@ -666,6 +666,7 @@ abstract class AbstractServerTest {
|
||||
res3 {
|
||||
ressy = "the module2 output"
|
||||
}
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -713,7 +714,7 @@ abstract class AbstractServerTest {
|
||||
)
|
||||
|
||||
val evaluatorResponse = client.receive<EvaluateResponse>()
|
||||
assertThat(evaluatorResponse.result?.debugYaml).isEqualTo("1")
|
||||
assertThat(evaluatorResponse.result?.debugRendering).isEqualTo("1")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -753,13 +754,14 @@ abstract class AbstractServerTest {
|
||||
|
||||
val evaluateResponse = client.receive<EvaluateResponse>()
|
||||
assertThat(evaluateResponse.result).isNotNull
|
||||
assertThat(evaluateResponse.result?.debugYaml)
|
||||
assertThat(evaluateResponse.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
|
||||
firstName = "Pigeon"
|
||||
lastName = "Bird"
|
||||
fullName = "Pigeon Bird"
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -793,13 +795,14 @@ abstract class AbstractServerTest {
|
||||
|
||||
val response12 = client.receive<EvaluateResponse>()
|
||||
assertThat(response12.result).isNotNull
|
||||
assertThat(response12.result?.debugYaml)
|
||||
assertThat(response12.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
|
||||
firstName = "Pigeon"
|
||||
lastName = "Bird"
|
||||
fullName = "Pigeon Bird"
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -823,13 +826,14 @@ abstract class AbstractServerTest {
|
||||
|
||||
val response22 = client.receive<EvaluateResponse>()
|
||||
assertThat(response22.result).isNotNull
|
||||
assertThat(response22.result?.debugYaml)
|
||||
assertThat(response22.result?.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
|
||||
firstName = "Parrot"
|
||||
lastName = "Bird"
|
||||
fullName = "Parrot Bird"
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
@@ -996,9 +1000,6 @@ abstract class AbstractServerTest {
|
||||
.contains("Rewrite rule must end with '/', but was 'https://example.com'")
|
||||
}
|
||||
|
||||
private val ByteArray.debugYaml
|
||||
get() = MessagePackDebugRenderer(this).output.trimIndent()
|
||||
|
||||
private fun TestTransport.sendCreateEvaluatorRequest(
|
||||
requestId: Long = 123,
|
||||
resourceReaders: List<ResourceReaderSpec> = listOf(),
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.server
|
||||
|
||||
import java.nio.file.Path
|
||||
import kotlin.Pair
|
||||
import kotlin.reflect.KClass
|
||||
import org.junit.platform.commons.annotation.Testable
|
||||
import org.pkl.commons.test.InputOutputTestEngine
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.evaluatorSettings.TraceMode
|
||||
import org.pkl.core.http.HttpClient
|
||||
import org.pkl.core.module.ModuleKeyFactories
|
||||
|
||||
@Testable class BinaryEvaluatorSnippetTests
|
||||
|
||||
class BinaryEvaluatorSnippetTestEngine : InputOutputTestEngine() {
|
||||
override val testClass: KClass<*> = BinaryEvaluatorSnippetTests::class
|
||||
|
||||
private val snippetsDir = rootProjectDir.resolve("pkl-server/src/test/files/SnippetTests")
|
||||
|
||||
private val outputDir = snippetsDir.resolve("output")
|
||||
|
||||
override val inputDir: Path = snippetsDir.resolve("input")
|
||||
|
||||
override val isInputFile: (Path) -> Boolean = { true }
|
||||
|
||||
override fun expectedOutputFileFor(inputFile: Path): Path {
|
||||
val relativePath = inputDir.relativize(inputFile).toString()
|
||||
return outputDir.resolve(relativePath.dropLast(3) + "yaml")
|
||||
}
|
||||
|
||||
private val evaluator =
|
||||
BinaryEvaluator(
|
||||
StackFrameTransformers.empty,
|
||||
SecurityManagers.defaultManager,
|
||||
HttpClient.dummyClient(),
|
||||
Loggers.stdErr(),
|
||||
listOf(ModuleKeyFactories.file),
|
||||
listOf(),
|
||||
mapOf(),
|
||||
mapOf(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TraceMode.COMPACT,
|
||||
)
|
||||
|
||||
private fun String.stripFilePaths() =
|
||||
replace(snippetsDir.toUri().toString(), "file:///\$snippetsDir/")
|
||||
|
||||
override fun generateOutputFor(inputFile: Path): Pair<Boolean, String> {
|
||||
val bytes = evaluator.evaluate(ModuleSource.path(inputFile), null)
|
||||
return true to bytes.debugRendering.stripFilePaths()
|
||||
}
|
||||
}
|
||||
|
||||
val ByteArray.debugRendering: String
|
||||
get() = MessagePackDebugRenderer(this).output
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.server
|
||||
|
||||
import java.nio.file.Path
|
||||
import java.util.regex.Pattern
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.evaluatorSettings.TraceMode
|
||||
import org.pkl.core.http.HttpClient
|
||||
import org.pkl.core.module.ModuleKeyFactories
|
||||
import org.pkl.core.resource.ResourceReaders
|
||||
|
||||
class BinaryEvaluatorTest {
|
||||
private val evaluator =
|
||||
BinaryEvaluator(
|
||||
StackFrameTransformers.defaultTransformer,
|
||||
SecurityManagers.standard(
|
||||
listOf(Pattern.compile(".*")),
|
||||
listOf(Pattern.compile(".*")),
|
||||
SecurityManagers.defaultTrustLevels,
|
||||
Path.of(""),
|
||||
),
|
||||
HttpClient.dummyClient(),
|
||||
Loggers.noop(),
|
||||
listOf(ModuleKeyFactories.standardLibrary),
|
||||
listOf(ResourceReaders.environmentVariable(), ResourceReaders.externalProperty()),
|
||||
mapOf(),
|
||||
mapOf(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TraceMode.COMPACT,
|
||||
)
|
||||
|
||||
private fun evaluate(text: String, expression: String?) =
|
||||
evaluator.evaluate(ModuleSource.text(text), expression)
|
||||
|
||||
@Test
|
||||
fun `evaluate whole module`() {
|
||||
val bytes = evaluate("foo = 1", null)
|
||||
assertThat(bytes.debugRendering)
|
||||
.isEqualTo(
|
||||
"""
|
||||
- 1
|
||||
- text
|
||||
- repl:text
|
||||
-
|
||||
-
|
||||
- 16
|
||||
- foo
|
||||
- 1
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate subpath`() {
|
||||
val bytes =
|
||||
evaluate(
|
||||
"""
|
||||
foo {
|
||||
bar = 2
|
||||
}
|
||||
"""
|
||||
.trimIndent(),
|
||||
"foo.bar",
|
||||
)
|
||||
|
||||
assertThat(bytes.asInt()).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate output text`() {
|
||||
val bytes =
|
||||
evaluate(
|
||||
"""
|
||||
foo {
|
||||
bar = 2
|
||||
}
|
||||
|
||||
output {
|
||||
renderer = new YamlRenderer {}
|
||||
}
|
||||
"""
|
||||
.trimIndent(),
|
||||
"output.text",
|
||||
)
|
||||
|
||||
assertThat(bytes.asString())
|
||||
.isEqualTo(
|
||||
"""
|
||||
foo:
|
||||
bar: 2
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate let expression`() {
|
||||
val bytes = evaluate("foo = 1", "let (bar = 2) foo + bar")
|
||||
|
||||
assertThat(bytes.asInt()).isEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate import expression`() {
|
||||
val bytes = evaluate("", """import("pkl:release").current.documentation.homepage""")
|
||||
|
||||
assertThat(bytes.asString()).startsWith("https://pkl-lang.org/")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate expression with invalid syntax`() {
|
||||
val error = assertThrows<PklException> { evaluate("foo = 1", "<>!!!") }
|
||||
|
||||
assertThat(error).hasMessageContaining("Unexpected token")
|
||||
assertThat(error).hasMessageContaining("<>!!!")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate non-expression`() {
|
||||
val error = assertThrows<PklException> { evaluate("bar = 2", "bar = 15") }
|
||||
|
||||
assertThat(error).hasMessageContaining("Unexpected token")
|
||||
assertThat(error).hasMessageContaining("bar = 15")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluate semantically invalid expression`() {
|
||||
val error = assertThrows<PklException> { evaluate("foo = 1", "foo as String") }
|
||||
|
||||
assertThat(error).hasMessageContaining("Expected value of type `String`, but got type `Int`")
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.server
|
||||
|
||||
import java.lang.IllegalStateException
|
||||
import org.msgpack.core.MessagePack
|
||||
import org.msgpack.core.MessageUnpacker
|
||||
import org.msgpack.value.ValueType
|
||||
import org.pkl.core.util.yaml.YamlEmitter
|
||||
|
||||
/** Renders MessagePack structures in YAML. */
|
||||
class MessagePackDebugRenderer(bytes: ByteArray) {
|
||||
private val unpacker: MessageUnpacker = MessagePack.newDefaultUnpacker(bytes)
|
||||
private val currIndent = StringBuilder("")
|
||||
private val sb = StringBuilder()
|
||||
private val indent = " "
|
||||
private val yamlEmitter = YamlEmitter.create(sb, "1.2", indent)
|
||||
|
||||
private fun incIndent() {
|
||||
currIndent.append(indent)
|
||||
}
|
||||
|
||||
private fun decIndent() {
|
||||
currIndent.setLength(currIndent.length - indent.length)
|
||||
}
|
||||
|
||||
private fun newline() {
|
||||
sb.append("\n")
|
||||
sb.append(currIndent)
|
||||
}
|
||||
|
||||
private fun renderKey() {
|
||||
val mf = unpacker.nextFormat
|
||||
when (mf.valueType!!) {
|
||||
ValueType.STRING -> yamlEmitter.emit(unpacker.unpackString(), currIndent, true)
|
||||
ValueType.MAP,
|
||||
ValueType.ARRAY -> {
|
||||
sb.append("? ")
|
||||
incIndent()
|
||||
renderValue()
|
||||
decIndent()
|
||||
newline()
|
||||
}
|
||||
else -> renderValue()
|
||||
}
|
||||
sb.append(": ")
|
||||
}
|
||||
|
||||
private fun renderValue() {
|
||||
val mf = unpacker.nextFormat
|
||||
when (mf.valueType!!) {
|
||||
ValueType.INTEGER,
|
||||
ValueType.FLOAT,
|
||||
ValueType.BOOLEAN,
|
||||
ValueType.NIL -> sb.append(unpacker.unpackValue().toJson())
|
||||
ValueType.STRING -> yamlEmitter.emit(unpacker.unpackString(), currIndent, false)
|
||||
ValueType.ARRAY -> {
|
||||
val size = unpacker.unpackArrayHeader()
|
||||
if (size == 0) {
|
||||
sb.append("[]")
|
||||
return
|
||||
}
|
||||
for (i in 0 until size) {
|
||||
newline()
|
||||
sb.append("- ")
|
||||
incIndent()
|
||||
renderValue()
|
||||
decIndent()
|
||||
}
|
||||
}
|
||||
ValueType.MAP -> {
|
||||
val size = unpacker.unpackMapHeader()
|
||||
if (size == 0) {
|
||||
sb.append("{}")
|
||||
return
|
||||
}
|
||||
for (i in 0 until size) {
|
||||
newline()
|
||||
renderKey()
|
||||
incIndent()
|
||||
renderValue()
|
||||
decIndent()
|
||||
}
|
||||
}
|
||||
ValueType.BINARY,
|
||||
ValueType.EXTENSION -> throw IllegalStateException("Unexpected value type ${mf.valueType}")
|
||||
}
|
||||
}
|
||||
|
||||
val output: String by lazy {
|
||||
renderValue()
|
||||
sb.toString().removePrefix("\n")
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.msgpack.core.MessagePack
|
||||
import org.msgpack.value.ImmutableValue
|
||||
import org.pkl.commons.test.MessagePackDebugRenderer
|
||||
|
||||
fun ByteArray.unpack(): ImmutableValue = MessagePack.newDefaultUnpacker(this).unpackValue()
|
||||
|
||||
@@ -27,6 +28,9 @@ fun ByteArray.asInt(): Int = unpack().asIntegerValue().asInt()
|
||||
|
||||
fun ByteArray.asString(): String = unpack().asStringValue().asString()
|
||||
|
||||
val ByteArray.debugRendering: String
|
||||
get() = MessagePackDebugRenderer(this).output
|
||||
|
||||
fun createDirectExecutor(): ExecutorService =
|
||||
object : AbstractExecutorService() {
|
||||
override fun execute(command: Runnable) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
org.pkl.server.BinaryEvaluatorSnippetTestEngine
|
||||
Reference in New Issue
Block a user