Run spotless apply

This commit is contained in:
Dan Chao
2024-07-01 09:08:06 -07:00
committed by Daniel Chao
parent 7a9b571f6e
commit 8c1c10528f
127 changed files with 5325 additions and 3450 deletions

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
plugins { plugins {
pklAllProjects pklAllProjects
pklJavaLibrary pklJavaLibrary
@@ -18,14 +33,15 @@ dependencies {
} }
jmh { jmh {
//include = ["fib_class_java"] // include = ["fib_class_java"]
//include = ["fib_class_constrained1", "fib_class_constrained2"] // include = ["fib_class_constrained1", "fib_class_constrained2"]
jmhVersion.set(libs.versions.jmh) jmhVersion.set(libs.versions.jmh)
// jvmArgsAppend = "-Dgraal.TruffleCompilationExceptionsAreFatal=true " + // jvmArgsAppend = "-Dgraal.TruffleCompilationExceptionsAreFatal=true " +
// "-Dgraal.Dump=Truffle,TruffleTree -Dgraal.TraceTruffleCompilation=true " + // "-Dgraal.Dump=Truffle,TruffleTree -Dgraal.TraceTruffleCompilation=true " +
// "-Dgraal.TruffleFunctionInlining=false" // "-Dgraal.TruffleFunctionInlining=false"
jvm.set("${buildInfo.graalVmAmd64.baseDir}/bin/java") jvm.set("${buildInfo.graalVmAmd64.baseDir}/bin/java")
// see: https://docs.oracle.com/en/graalvm/enterprise/20/docs/graalvm-as-a-platform/implement-language/#disable-class-path-separation // see:
// https://docs.oracle.com/en/graalvm/enterprise/20/docs/graalvm-as-a-platform/implement-language/#disable-class-path-separation
jvmArgs.set( jvmArgs.set(
listOf( listOf(
// one JVM arg per list element doesn't work, but the following does // one JVM arg per list element doesn't work, but the following does
@@ -33,16 +49,13 @@ jmh {
) )
) )
includeTests.set(false) includeTests.set(false)
//threads = Runtime.runtime.availableProcessors() / 2 + 1 // threads = Runtime.runtime.availableProcessors() / 2 + 1
//synchronizeIterations = false // synchronizeIterations = false
} }
tasks.named("jmh") { tasks.named("jmh") { dependsOn(":installGraalVmAmd64") }
dependsOn(":installGraalVmAmd64")
}
// Prevent this error which occurs when building in IntelliJ: // Prevent this error which occurs when building in IntelliJ:
// "Entry org/pkl/core/fib_class_typed.pkl is a duplicate but no duplicate handling strategy has been set." // "Entry org/pkl/core/fib_class_typed.pkl is a duplicate but no duplicate handling strategy has
tasks.processJmhResources { // been set."
duplicatesStrategy = DuplicatesStrategy.EXCLUDE tasks.processJmhResources { duplicatesStrategy = DuplicatesStrategy.EXCLUDE }
}

View File

@@ -1,5 +1,20 @@
/**
* Copyright © 2024 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.
*/
// https://youtrack.jetbrains.com/issue/KTIJ-19369 // https://youtrack.jetbrains.com/issue/KTIJ-19369
@file:Suppress("DSL_SCOPE_VIOLATION") @file:Suppress("DSL_SCOPE_VIOLATION")
import org.jetbrains.gradle.ext.ActionDelegationConfig import org.jetbrains.gradle.ext.ActionDelegationConfig
import org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.PLATFORM import org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.PLATFORM
@@ -40,15 +55,12 @@ idea {
} }
} }
val clean by tasks.existing { val clean by tasks.existing { delete(layout.buildDirectory) }
delete(layout.buildDirectory)
}
val printVersion by tasks.registering { val printVersion by tasks.registering { doFirst { println(buildInfo.pklVersion) } }
doFirst { println(buildInfo.pklVersion) }
}
val message = """ val message =
"""
==== ====
Gradle version : ${gradle.gradleVersion} Gradle version : ${gradle.gradleVersion}
Java version : ${System.getProperty("java.version")} Java version : ${System.getProperty("java.version")}
@@ -63,5 +75,7 @@ Git Commit ID : ${buildInfo.commitId}
==== ====
""" """
val formattedMessage = message.replace("\n====", "\n" + "=".repeat(message.lines().maxByOrNull { it.length }!!.length)) val formattedMessage =
message.replace("\n====", "\n" + "=".repeat(message.lines().maxByOrNull { it.length }!!.length))
logger.info(formattedMessage) logger.info(formattedMessage)

View File

@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
plugins { plugins { `kotlin-dsl` }
`kotlin-dsl`
}
dependencies { dependencies {
implementation(libs.downloadTaskPlugin) implementation(libs.downloadTaskPlugin)

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
@file:Suppress("UnstableApiUsage") @file:Suppress("UnstableApiUsage")
rootProject.name = "buildSrc" rootProject.name = "buildSrc"
@@ -12,11 +27,7 @@ pluginManagement {
// makes ~/.gradle/init.gradle unnecessary and ~/.gradle/gradle.properties optional // makes ~/.gradle/init.gradle unnecessary and ~/.gradle/gradle.properties optional
dependencyResolutionManagement { dependencyResolutionManagement {
// use same version catalog as main build // use same version catalog as main build
versionCatalogs { versionCatalogs { register("libs") { from(files("../gradle/libs.versions.toml")) } }
register("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
repositories { repositories {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
@file:Suppress("MemberVisibilityCanBePrivate") @file:Suppress("MemberVisibilityCanBePrivate")
import java.io.File import java.io.File
@@ -14,13 +29,9 @@ open class BuildInfo(project: Project) {
System.getenv("GRAALVM_HOME") ?: "${System.getProperty("user.home")}/.graalvm" System.getenv("GRAALVM_HOME") ?: "${System.getProperty("user.home")}/.graalvm"
} }
val version: String by lazy { val version: String by lazy { libs.findVersion("graalVm").get().toString() }
libs.findVersion("graalVm").get().toString()
}
val graalVmJdkVersion: String by lazy { val graalVmJdkVersion: String by lazy { libs.findVersion("graalVmJdkVersion").get().toString() }
libs.findVersion("graalVmJdkVersion").get().toString()
}
val osName: String by lazy { val osName: String by lazy {
when { when {
@@ -31,9 +42,7 @@ open class BuildInfo(project: Project) {
} }
} }
val baseName: String by lazy { val baseName: String by lazy { "graalvm-jdk-${graalVmJdkVersion}_${osName}-${arch}_bin" }
"graalvm-jdk-${graalVmJdkVersion}_${osName}-${arch}_bin"
}
val downloadUrl: String by lazy { val downloadUrl: String by lazy {
val jdkMajor = graalVmJdkVersion.takeWhile { it != '.' } val jdkMajor = graalVmJdkVersion.takeWhile { it != '.' }
@@ -41,18 +50,14 @@ open class BuildInfo(project: Project) {
"https://download.oracle.com/graalvm/$jdkMajor/archive/$baseName.$extension" "https://download.oracle.com/graalvm/$jdkMajor/archive/$baseName.$extension"
} }
val installDir: File by lazy { val installDir: File by lazy { File(homeDir, baseName) }
File(homeDir, baseName)
}
val baseDir: String by lazy { val baseDir: String by lazy {
if (os.isMacOsX) "$installDir/Contents/Home" else installDir.toString() if (os.isMacOsX) "$installDir/Contents/Home" else installDir.toString()
} }
} }
/** /** Same logic as [org.gradle.internal.os.OperatingSystem#arch], which is protected. */
* Same logic as [org.gradle.internal.os.OperatingSystem#arch], which is protected.
*/
val arch: String by lazy { val arch: String by lazy {
when (val arch = System.getProperty("os.arch")) { when (val arch = System.getProperty("os.arch")) {
"x86" -> "i386" "x86" -> "i386"
@@ -66,13 +71,9 @@ open class BuildInfo(project: Project) {
val graalVmAmd64: GraalVm = GraalVm("x64") val graalVmAmd64: GraalVm = GraalVm("x64")
val isCiBuild: Boolean by lazy { val isCiBuild: Boolean by lazy { System.getenv("CI") != null }
System.getenv("CI") != null
}
val isReleaseBuild: Boolean by lazy { val isReleaseBuild: Boolean by lazy { java.lang.Boolean.getBoolean("releaseBuild") }
java.lang.Boolean.getBoolean("releaseBuild")
}
val hasMuslToolchain: Boolean by lazy { val hasMuslToolchain: Boolean by lazy {
// see "install musl" in .circleci/jobs/BuildNativeJob.pkl // see "install musl" in .circleci/jobs/BuildNativeJob.pkl
@@ -87,10 +88,11 @@ open class BuildInfo(project: Project) {
val commitId: String by lazy { val commitId: String by lazy {
// only run command once per build invocation // only run command once per build invocation
if (project === project.rootProject) { if (project === project.rootProject) {
val process = ProcessBuilder() val process =
.command("git", "rev-parse", "--short", "HEAD") ProcessBuilder()
.directory(project.rootDir) .command("git", "rev-parse", "--short", "HEAD")
.start() .directory(project.rootDir)
.start()
process.waitFor().also { exitCode -> process.waitFor().also { exitCode ->
if (exitCode == -1) throw RuntimeException(process.errorStream.reader().readText()) if (exitCode == -1) throw RuntimeException(process.errorStream.reader().readText())
} }
@@ -100,9 +102,7 @@ open class BuildInfo(project: Project) {
} }
} }
val commitish: String by lazy { val commitish: String by lazy { if (isReleaseBuild) project.version.toString() else commitId }
if (isReleaseBuild) project.version.toString() else commitId
}
val pklVersion: String by lazy { val pklVersion: String by lazy {
if (isReleaseBuild) { if (isReleaseBuild) {

View File

@@ -1,39 +1,52 @@
/**
* Copyright © 2024 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.
*/
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
/** /**
* Builds a self-contained Pkl CLI Jar that is directly executable on *nix * Builds a self-contained Pkl CLI Jar that is directly executable on *nix and executable with `java
* and executable with `java -jar` on Windows. * -jar` on Windows.
* *
* For direct execution, the `java` command must be on the PATH. * For direct execution, the `java` command must be on the PATH.
* *
* https://skife.org/java/unix/2011/06/20/really_executable_jars.html * https://skife.org/java/unix/2011/06/20/really_executable_jars.html
*/ */
abstract class ExecutableJar : DefaultTask() { abstract class ExecutableJar : DefaultTask() {
@get:InputFile @get:InputFile abstract val inJar: RegularFileProperty
abstract val inJar: RegularFileProperty
@get:OutputFile @get:OutputFile abstract val outJar: RegularFileProperty
abstract val outJar: RegularFileProperty
@get:Input @get:Input abstract val jvmArgs: ListProperty<String>
abstract val jvmArgs: ListProperty<String>
@TaskAction @TaskAction
fun buildJar() { fun buildJar() {
val inFile = inJar.get().asFile val inFile = inJar.get().asFile
val outFile = outJar.get().asFile val outFile = outJar.get().asFile
val escapedJvmArgs = jvmArgs.get().joinToString(separator = " ") { "\"$it\"" } val escapedJvmArgs = jvmArgs.get().joinToString(separator = " ") { "\"$it\"" }
val startScript = """ val startScript =
"""
#!/bin/sh #!/bin/sh
exec java $escapedJvmArgs -jar $0 "$@" exec java $escapedJvmArgs -jar $0 "$@"
""".trimIndent() + "\n\n\n" """
.trimIndent() + "\n\n\n"
outFile.outputStream().use { outStream -> outFile.outputStream().use { outStream ->
startScript.byteInputStream().use { it.copyTo(outStream) } startScript.byteInputStream().use { it.copyTo(outStream) }
inFile.inputStream().use { it.copyTo(outStream) } inFile.inputStream().use { it.copyTo(outStream) }

View File

@@ -1,7 +1,22 @@
/**
* Copyright © 2024 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.
*/
import org.gradle.util.GradleVersion import org.gradle.util.GradleVersion
open class GradlePluginTests { open class GradlePluginTests {
lateinit var minGradleVersion: GradleVersion lateinit var minGradleVersion: GradleVersion
lateinit var maxGradleVersion: GradleVersion lateinit var maxGradleVersion: GradleVersion
var skippedGradleVersions: List<GradleVersion> = listOf() var skippedGradleVersions: List<GradleVersion> = listOf()
} }

View File

@@ -1,6 +1,21 @@
import org.gradle.util.GradleVersion /**
* Copyright © 2024 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.
*/
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
import java.net.URI import java.net.URI
import org.gradle.util.GradleVersion
@Suppress("unused") @Suppress("unused")
class GradleVersionInfo(json: Map<String, Any>) { class GradleVersionInfo(json: Map<String, Any>) {
@@ -38,15 +53,19 @@ class GradleVersionInfo(json: Map<String, Any>) {
val wrapperChecksumUrl: String by json val wrapperChecksumUrl: String by json
companion object { companion object {
private fun fetchAll(): List<GradleVersionInfo> = fetchMultiple("https://services.gradle.org/versions/all") private fun fetchAll(): List<GradleVersionInfo> =
fetchMultiple("https://services.gradle.org/versions/all")
fun fetchReleases(): List<GradleVersionInfo> = fetchAll().filter { it.isReleaseVersion } fun fetchReleases(): List<GradleVersionInfo> = fetchAll().filter { it.isReleaseVersion }
fun fetchCurrent(): GradleVersionInfo = fetchSingle("https://services.gradle.org/versions/current") fun fetchCurrent(): GradleVersionInfo =
fetchSingle("https://services.gradle.org/versions/current")
fun fetchRc(): GradleVersionInfo? = fetchSingleOrNull("https://services.gradle.org/versions/release-candidate") fun fetchRc(): GradleVersionInfo? =
fetchSingleOrNull("https://services.gradle.org/versions/release-candidate")
fun fetchNightly(): GradleVersionInfo = fetchSingle("https://services.gradle.org/versions/nightly") fun fetchNightly(): GradleVersionInfo =
fetchSingle("https://services.gradle.org/versions/nightly")
private fun fetchSingle(url: String): GradleVersionInfo { private fun fetchSingle(url: String): GradleVersionInfo {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -61,8 +80,9 @@ class GradleVersionInfo(json: Map<String, Any>) {
private fun fetchMultiple(url: String): List<GradleVersionInfo> { private fun fetchMultiple(url: String): List<GradleVersionInfo> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return (JsonSlurper().parse(URI(url).toURL()) as List<Map<String, Any>>) return (JsonSlurper().parse(URI(url).toURL()) as List<Map<String, Any>>).map {
.map { GradleVersionInfo(it) } GradleVersionInfo(it)
}
} }
} }
} }

View File

@@ -1,6 +1,21 @@
/**
* Copyright © 2024 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.
*/
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
open class HtmlValidator(project: Project) { open class HtmlValidator(project: Project) {
var sources: FileCollection = project.files() var sources: FileCollection = project.files()
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
import java.io.File import java.io.File
import java.util.regex.Matcher import java.util.regex.Matcher
import java.util.regex.Pattern import java.util.regex.Pattern
@@ -15,21 +30,18 @@ import org.gradle.kotlin.dsl.listProperty
import org.gradle.kotlin.dsl.mapProperty import org.gradle.kotlin.dsl.mapProperty
open class MergeSourcesJars : DefaultTask() { open class MergeSourcesJars : DefaultTask() {
@get:InputFiles @get:InputFiles val inputJars: ConfigurableFileCollection = project.objects.fileCollection()
val inputJars: ConfigurableFileCollection = project.objects.fileCollection()
@get:InputFiles @get:InputFiles
val mergedBinaryJars: ConfigurableFileCollection = project.objects.fileCollection() val mergedBinaryJars: ConfigurableFileCollection = project.objects.fileCollection()
@get:Input @get:Input val relocatedPackages: MapProperty<String, String> = project.objects.mapProperty()
val relocatedPackages: MapProperty<String, String> = project.objects.mapProperty()
@get:Input @get:Input
var sourceFileExtensions: ListProperty<String> = project.objects.listProperty<String>() var sourceFileExtensions: ListProperty<String> =
.convention(listOf(".java", ".kt")) project.objects.listProperty<String>().convention(listOf(".java", ".kt"))
@get:OutputFile @get:OutputFile val outputJar: RegularFileProperty = project.objects.fileProperty()
val outputJar: RegularFileProperty = project.objects.fileProperty()
@TaskAction @TaskAction
@Suppress("unused") @Suppress("unused")
@@ -38,12 +50,15 @@ open class MergeSourcesJars : DefaultTask() {
val relocatedPkgs = relocatedPackages.get() val relocatedPkgs = relocatedPackages.get()
val relocatedPaths = relocatedPkgs.entries.associate { (key, value) -> toPath(key) to toPath(value) } val relocatedPaths =
relocatedPkgs.entries.associate { (key, value) -> toPath(key) to toPath(value) }
// use negative lookbehind to match any that don't precede with // use negative lookbehind to match any that don't precede with
// a word or a period character. should catch most cases. // a word or a period character. should catch most cases.
val importPattern = Pattern.compile("(?<!(\\w|\\.))(" + val importPattern =
relocatedPkgs.keys.joinToString("|") { it.replace(".", "\\.") } + ")") Pattern.compile(
"(?<!(\\w|\\.))(" + relocatedPkgs.keys.joinToString("|") { it.replace(".", "\\.") } + ")"
)
val sourceFileExts = sourceFileExtensions.get() val sourceFileExts = sourceFileExtensions.get()

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.result.ResolvedArtifactResult import org.gradle.api.artifacts.result.ResolvedArtifactResult
@@ -12,27 +27,29 @@ import org.gradle.kotlin.dsl.property
import org.gradle.language.base.artifact.SourcesArtifact import org.gradle.language.base.artifact.SourcesArtifact
open class ResolveSourcesJars : DefaultTask() { open class ResolveSourcesJars : DefaultTask() {
@get:InputFiles @get:InputFiles val configuration: Property<Configuration> = project.objects.property()
val configuration: Property<Configuration> = project.objects.property()
@get:OutputDirectory @get:OutputDirectory val outputDir: DirectoryProperty = project.objects.directoryProperty()
val outputDir: DirectoryProperty = project.objects.directoryProperty()
@TaskAction @TaskAction
@Suppress("UnstableApiUsage", "unused") @Suppress("UnstableApiUsage", "unused")
fun resolve() { fun resolve() {
val componentIds = configuration.get().incoming.resolutionResult.allDependencies.map { val componentIds =
(it as ResolvedDependencyResult).selected.id configuration.get().incoming.resolutionResult.allDependencies.map {
} (it as ResolvedDependencyResult).selected.id
}
val resolutionResult = project.dependencies.createArtifactResolutionQuery() val resolutionResult =
.forComponents(componentIds) project.dependencies
.withArtifacts(JvmLibrary::class.java, SourcesArtifact::class.java) .createArtifactResolutionQuery()
.execute() .forComponents(componentIds)
.withArtifacts(JvmLibrary::class.java, SourcesArtifact::class.java)
.execute()
val resolvedJars = resolutionResult.resolvedComponents val resolvedJars =
.flatMap { it.getArtifacts(SourcesArtifact::class.java) } resolutionResult.resolvedComponents
.map { (it as ResolvedArtifactResult).file } .flatMap { it.getArtifacts(SourcesArtifact::class.java) }
.map { (it as ResolvedArtifactResult).file }
// copying to an output dir because I don't know how else to describe task outputs // copying to an output dir because I don't know how else to describe task outputs
project.sync { project.sync {

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.component.AdhocComponentWithVariants import org.gradle.api.component.AdhocComponentWithVariants
@@ -18,41 +33,43 @@ val fatJarConfiguration: Configuration = configurations.create("fatJar")
val fatJarPublication: MavenPublication = publishing.publications.create<MavenPublication>("fatJar") val fatJarPublication: MavenPublication = publishing.publications.create<MavenPublication>("fatJar")
// ideally we'd configure this automatically based on project dependencies // ideally we'd configure this automatically based on project dependencies
val firstPartySourcesJarsConfiguration: Configuration = configurations.create("firstPartySourcesJars") val firstPartySourcesJarsConfiguration: Configuration =
configurations.create("firstPartySourcesJars")
val relocations = mapOf( val relocations =
// pkl-core dependencies mapOf(
"org.antlr.v4." to "org.pkl.thirdparty.antlr.v4.", // pkl-core dependencies
"com.oracle.truffle" to "org.pkl.thirdparty.truffle", "org.antlr.v4." to "org.pkl.thirdparty.antlr.v4.",
"org.graalvm." to "org.pkl.thirdparty.graalvm.", "com.oracle.truffle" to "org.pkl.thirdparty.truffle",
"org.organicdesign.fp." to "org.pkl.thirdparty.paguro.", "org.graalvm." to "org.pkl.thirdparty.graalvm.",
"org.snakeyaml.engine." to "org.pkl.thirdparty.snakeyaml.engine.", "org.organicdesign.fp." to "org.pkl.thirdparty.paguro.",
"org.msgpack." to "org.pkl.thirdparty.msgpack.", "org.snakeyaml.engine." to "org.pkl.thirdparty.snakeyaml.engine.",
"org.w3c.dom." to "org.pkl.thirdparty.w3c.dom", "org.msgpack." to "org.pkl.thirdparty.msgpack.",
"com.oracle.svm.core." to "org.pkl.thirdparty.svm.", "org.w3c.dom." to "org.pkl.thirdparty.w3c.dom",
"com.oracle.svm.core." to "org.pkl.thirdparty.svm.",
// pkl-cli dependencies // pkl-cli dependencies
"org.jline." to "org.pkl.thirdparty.jline.", "org.jline." to "org.pkl.thirdparty.jline.",
"com.github.ajalt.clikt." to "org.pkl.thirdparty.clikt.", "com.github.ajalt.clikt." to "org.pkl.thirdparty.clikt.",
"kotlin." to "org.pkl.thirdparty.kotlin.", "kotlin." to "org.pkl.thirdparty.kotlin.",
"kotlinx." to "org.pkl.thirdparty.kotlinx.", "kotlinx." to "org.pkl.thirdparty.kotlinx.",
"org.intellij." to "org.pkl.thirdparty.intellij.", "org.intellij." to "org.pkl.thirdparty.intellij.",
"org.fusesource.jansi." to "org.pkl.thirdparty.jansi", "org.fusesource.jansi." to "org.pkl.thirdparty.jansi",
"org.fusesource.hawtjni." to "org.pkl.thirdparty.hawtjni", "org.fusesource.hawtjni." to "org.pkl.thirdparty.hawtjni",
// pkl-doc dependencies // pkl-doc dependencies
"org.commonmark." to "org.pkl.thirdparty.commonmark.", "org.commonmark." to "org.pkl.thirdparty.commonmark.",
"org.jetbrains." to "org.pkl.thirdparty.jetbrains.", "org.jetbrains." to "org.pkl.thirdparty.jetbrains.",
// pkl-config-java dependencies
"io.leangen.geantyref." to "org.pkl.thirdparty.geantyref.",
// pkl-codegen-java dependencies // pkl-config-java dependencies
"com.squareup.javapoet." to "org.pkl.thirdparty.javapoet.", "io.leangen.geantyref." to "org.pkl.thirdparty.geantyref.",
// pkl-codegen-kotlin dependencies // pkl-codegen-java dependencies
"com.squareup.kotlinpoet." to "org.pkl.thirdparty.kotlinpoet.", "com.squareup.javapoet." to "org.pkl.thirdparty.javapoet.",
)
// pkl-codegen-kotlin dependencies
"com.squareup.kotlinpoet." to "org.pkl.thirdparty.kotlinpoet.",
)
val nonRelocations = listOf("com/oracle/truffle/") val nonRelocations = listOf("com/oracle/truffle/")
@@ -82,82 +99,81 @@ tasks.shadowJar {
// workaround for https://github.com/johnrengelman/shadow/issues/651 // workaround for https://github.com/johnrengelman/shadow/issues/651
components.withType(AdhocComponentWithVariants::class.java).forEach { c -> components.withType(AdhocComponentWithVariants::class.java).forEach { c ->
c.withVariantsFromConfiguration(project.configurations.shadowRuntimeElements.get()) { c.withVariantsFromConfiguration(project.configurations.shadowRuntimeElements.get()) { skip() }
skip() }
val testFatJar by
tasks.registering(Test::class) {
testClassesDirs = files(tasks.test.get().testClassesDirs)
classpath =
// compiled test classes
sourceSets.test.get().output +
// fat Jar
tasks.shadowJar.get().outputs.files +
// test-only dependencies
// (test dependencies that are also main dependencies must already be contained in fat Jar;
// to verify that, we don't want to include them here)
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get())
} }
}
val testFatJar by tasks.registering(Test::class) { tasks.check { dependsOn(testFatJar) }
testClassesDirs = files(tasks.test.get().testClassesDirs)
classpath =
// compiled test classes
sourceSets.test.get().output +
// fat Jar
tasks.shadowJar.get().outputs.files +
// test-only dependencies
// (test dependencies that are also main dependencies must already be contained in fat Jar;
// to verify that, we don't want to include them here)
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get())
}
tasks.check { val validateFatJar by
dependsOn(testFatJar) tasks.registering {
} val outputFile = layout.buildDirectory.file("validateFatJar/result.txt")
inputs.files(tasks.shadowJar)
inputs.property("nonRelocations", nonRelocations)
outputs.file(outputFile)
val validateFatJar by tasks.registering { doLast {
val outputFile = layout.buildDirectory.file("validateFatJar/result.txt") val unshadowedFiles = mutableListOf<String>()
inputs.files(tasks.shadowJar) zipTree(tasks.shadowJar.get().outputs.files.singleFile).visit {
inputs.property("nonRelocations", nonRelocations) val fileDetails = this
outputs.file(outputFile) val path = fileDetails.relativePath.pathString
if (
doLast { !(fileDetails.isDirectory ||
val unshadowedFiles = mutableListOf<String>() path.startsWith("org/pkl/") ||
zipTree(tasks.shadowJar.get().outputs.files.singleFile).visit { path.startsWith("META-INF/") ||
val fileDetails = this nonRelocations.any { path.startsWith(it) })
val path = fileDetails.relativePath.pathString ) {
if (!(fileDetails.isDirectory || // don't throw exception inside `visit`
path.startsWith("org/pkl/") || // as this gives a misleading "Could not expand ZIP" error message
path.startsWith("META-INF/") || unshadowedFiles.add(path)
nonRelocations.any { path.startsWith(it) })) { }
// don't throw exception inside `visit` }
// as this gives a misleading "Could not expand ZIP" error message if (unshadowedFiles.isEmpty()) {
unshadowedFiles.add(path) outputFile.get().asFile.writeText("SUCCESS")
} else {
outputFile.get().asFile.writeText("FAILURE")
throw GradleException("Found unshadowed files:\n" + unshadowedFiles.joinToString("\n"))
} }
} }
if (unshadowedFiles.isEmpty()) {
outputFile.get().asFile.writeText("SUCCESS")
} else {
outputFile.get().asFile.writeText("FAILURE")
throw GradleException("Found unshadowed files:\n" + unshadowedFiles.joinToString("\n"))
}
} }
}
tasks.check {
dependsOn(validateFatJar)
}
val resolveSourcesJars by tasks.registering(ResolveSourcesJars::class) { tasks.check { dependsOn(validateFatJar) }
configuration.set(configurations.runtimeClasspath)
outputDir.set(layout.buildDirectory.dir("resolveSourcesJars"))
}
val fatSourcesJar by tasks.registering(MergeSourcesJars::class) { val resolveSourcesJars by
plugins.withId("pklJavaLibrary") { tasks.registering(ResolveSourcesJars::class) {
inputJars.from(tasks.named("sourcesJar")) configuration.set(configurations.runtimeClasspath)
outputDir.set(layout.buildDirectory.dir("resolveSourcesJars"))
} }
inputJars.from(firstPartySourcesJarsConfiguration)
inputJars.from(resolveSourcesJars.map { fileTree(it.outputDir) })
mergedBinaryJars.from(tasks.shadowJar) val fatSourcesJar by
relocatedPackages.set(relocations) tasks.registering(MergeSourcesJars::class) {
outputJar.fileProvider(provider { plugins.withId("pklJavaLibrary") { inputJars.from(tasks.named("sourcesJar")) }
file(tasks.shadowJar.get().archiveFile.get().asFile.path.replace(".jar", "-sources.jar")) inputJars.from(firstPartySourcesJarsConfiguration)
}) inputJars.from(resolveSourcesJars.map { fileTree(it.outputDir) })
}
artifacts { mergedBinaryJars.from(tasks.shadowJar)
add("fatJar", tasks.shadowJar) relocatedPackages.set(relocations)
} outputJar.fileProvider(
provider {
file(tasks.shadowJar.get().archiveFile.get().asFile.path.replace(".jar", "-sources.jar"))
}
)
}
artifacts { add("fatJar", tasks.shadowJar) }
publishing { publishing {
publications { publications {
@@ -165,16 +181,12 @@ publishing {
project.shadow.component(this) project.shadow.component(this)
// sources Jar is fat // sources Jar is fat
artifact(fatSourcesJar.flatMap { it.outputJar.asFile }) { artifact(fatSourcesJar.flatMap { it.outputJar.asFile }) { classifier = "sources" }
classifier = "sources"
}
plugins.withId("pklJavaLibrary") { plugins.withId("pklJavaLibrary") {
val javadocJar by tasks.existing(Jar::class) val javadocJar by tasks.existing(Jar::class)
// Javadoc Jar is not fat (didn't invest effort) // Javadoc Jar is not fat (didn't invest effort)
artifact(javadocJar.flatMap { it.archiveFile }) { artifact(javadocJar.flatMap { it.archiveFile }) { classifier = "javadoc" }
classifier = "javadoc"
}
} }
} }
} }

View File

@@ -1,36 +1,44 @@
import java.nio.file.* /**
import java.util.UUID * Copyright © 2024 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.
*/
import de.undercouch.gradle.tasks.download.Download import de.undercouch.gradle.tasks.download.Download
import de.undercouch.gradle.tasks.download.Verify import de.undercouch.gradle.tasks.download.Verify
import java.nio.file.*
import java.util.UUID
import kotlin.io.path.createDirectories import kotlin.io.path.createDirectories
plugins { plugins { id("de.undercouch.download") }
id("de.undercouch.download")
}
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
val BuildInfo.GraalVm.downloadFile get(): File { val BuildInfo.GraalVm.downloadFile
val extension = if (buildInfo.os.isWindows) "zip" else "tar.gz" get(): File {
return file(homeDir).resolve("${baseName}.$extension") val extension = if (buildInfo.os.isWindows) "zip" else "tar.gz"
} return file(homeDir).resolve("${baseName}.$extension")
}
// tries to minimize chance of corruption by download-to-temp-file-and-move // tries to minimize chance of corruption by download-to-temp-file-and-move
val downloadGraalVmAarch64 by tasks.registering(Download::class) { val downloadGraalVmAarch64 by
configureDownloadGraalVm(buildInfo.graalVmAarch64) tasks.registering(Download::class) { configureDownloadGraalVm(buildInfo.graalVmAarch64) }
}
val downloadGraalVmAmd64 by tasks.registering(Download::class) { val downloadGraalVmAmd64 by
configureDownloadGraalVm(buildInfo.graalVmAmd64) tasks.registering(Download::class) { configureDownloadGraalVm(buildInfo.graalVmAmd64) }
}
fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) { fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) {
onlyIf { onlyIf { !graalvm.installDir.exists() }
!graalvm.installDir.exists() doLast { println("Downloaded GraalVm to ${graalvm.downloadFile}") }
}
doLast {
println("Downloaded GraalVm to ${graalvm.downloadFile}")
}
src(graalvm.downloadUrl) src(graalvm.downloadUrl)
dest(graalvm.downloadFile) dest(graalvm.downloadFile)
@@ -38,42 +46,44 @@ fun Download.configureDownloadGraalVm(graalvm: BuildInfo.GraalVm) {
tempAndMove(true) tempAndMove(true)
} }
val verifyGraalVmAarch64 by tasks.registering(Verify::class) { val verifyGraalVmAarch64 by
configureVerifyGraalVm(buildInfo.graalVmAarch64) tasks.registering(Verify::class) {
dependsOn(downloadGraalVmAarch64) configureVerifyGraalVm(buildInfo.graalVmAarch64)
} dependsOn(downloadGraalVmAarch64)
val verifyGraalVmAmd64 by tasks.registering(Verify::class) {
configureVerifyGraalVm(buildInfo.graalVmAmd64)
dependsOn(downloadGraalVmAmd64)
}
fun Verify.configureVerifyGraalVm(graalvm: BuildInfo.GraalVm) {
onlyIf {
!graalvm.installDir.exists()
} }
val verifyGraalVmAmd64 by
tasks.registering(Verify::class) {
configureVerifyGraalVm(buildInfo.graalVmAmd64)
dependsOn(downloadGraalVmAmd64)
}
fun Verify.configureVerifyGraalVm(graalvm: BuildInfo.GraalVm) {
onlyIf { !graalvm.installDir.exists() }
src(graalvm.downloadFile) src(graalvm.downloadFile)
checksum(buildInfo.libs.findVersion("graalVmSha256-${graalvm.osName}-${graalvm.arch}").get().toString()) checksum(
buildInfo.libs.findVersion("graalVmSha256-${graalvm.osName}-${graalvm.arch}").get().toString()
)
algorithm("SHA-256") algorithm("SHA-256")
} }
// minimize chance of corruption by extract-to-random-dir-and-flip-symlink // minimize chance of corruption by extract-to-random-dir-and-flip-symlink
val installGraalVmAarch64 by tasks.registering { val installGraalVmAarch64 by
dependsOn(verifyGraalVmAarch64) tasks.registering {
configureInstallGraalVm(buildInfo.graalVmAarch64) dependsOn(verifyGraalVmAarch64)
} configureInstallGraalVm(buildInfo.graalVmAarch64)
}
// minimize chance of corruption by extract-to-random-dir-and-flip-symlink // minimize chance of corruption by extract-to-random-dir-and-flip-symlink
val installGraalVmAmd64 by tasks.registering { val installGraalVmAmd64 by
dependsOn(verifyGraalVmAmd64) tasks.registering {
configureInstallGraalVm(buildInfo.graalVmAmd64) dependsOn(verifyGraalVmAmd64)
} configureInstallGraalVm(buildInfo.graalVmAmd64)
}
fun Task.configureInstallGraalVm(graalVm: BuildInfo.GraalVm) { fun Task.configureInstallGraalVm(graalVm: BuildInfo.GraalVm) {
onlyIf { onlyIf { !graalVm.installDir.exists() }
!graalVm.installDir.exists()
}
doLast { doLast {
val distroDir = Paths.get(graalVm.homeDir, UUID.randomUUID().toString()) val distroDir = Paths.get(graalVm.homeDir, UUID.randomUUID().toString())
@@ -88,7 +98,9 @@ fun Task.configureInstallGraalVm(graalVm: BuildInfo.GraalVm) {
args("--strip-components=1", "-xzf", graalVm.downloadFile) args("--strip-components=1", "-xzf", graalVm.downloadFile)
} }
val distroBinDir = if (buildInfo.os.isMacOsX) distroDir.resolve("Contents/Home/bin") else distroDir.resolve("bin") val distroBinDir =
if (buildInfo.os.isMacOsX) distroDir.resolve("Contents/Home/bin")
else distroDir.resolve("bin")
println("Installing native-image into $distroDir") println("Installing native-image into $distroDir")
exec { exec {
@@ -103,11 +115,15 @@ fun Task.configureInstallGraalVm(graalVm: BuildInfo.GraalVm) {
try { try {
Files.move(tempLink, graalVm.installDir.toPath(), StandardCopyOption.ATOMIC_MOVE) Files.move(tempLink, graalVm.installDir.toPath(), StandardCopyOption.ATOMIC_MOVE)
} catch (e: Exception) { } catch (e: Exception) {
try { delete(tempLink.toFile()) } catch (ignored: Exception) {} try {
delete(tempLink.toFile())
} catch (ignored: Exception) {}
throw e throw e
} }
} catch (e: Exception) { } catch (e: Exception) {
try { delete(distroDir) } catch (ignored: Exception) {} try {
delete(distroDir)
} catch (ignored: Exception) {}
throw e throw e
} }
} }

View File

@@ -1,19 +1,19 @@
/** /**
* Allows to run Gradle plugin tests against different Gradle versions. * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Adds a `compatibilityTestX` task for every Gradle version X * Licensed under the Apache License, Version 2.0 (the "License");
* between `ext.minSupportedGradleVersion` and `ext.maxSupportedGradleVersion` * you may not use this file except in compliance with the License.
* that is not in `ext.gradleVersionsExcludedFromTesting`. * You may obtain a copy of the License at
* The list of available Gradle versions is obtained from services.gradle.org. *
* Adds lifecycle tasks to test against multiple Gradle versions at once, for example all Gradle release versions. * https://www.apache.org/licenses/LICENSE-2.0
* Compatibility test tasks run the same tests and use the same task configuration as the project's `test` task. *
* They set system properties for the Gradle version and distribution URL to be used. * Unless required by applicable law or agreed to in writing, software
* These properties are consumed by the `AbstractTest` class. * 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.
*/ */
plugins { java }
plugins {
java
}
val gradlePluginTests = extensions.create<GradlePluginTests>("gradlePluginTests") val gradlePluginTests = extensions.create<GradlePluginTests>("gradlePluginTests")
@@ -24,18 +24,23 @@ tasks.addRule("Pattern: compatibilityTest[All|Releases|Latest|Candidate|Nightly|
when (val taskNameSuffix = matchResult.groupValues[1]) { when (val taskNameSuffix = matchResult.groupValues[1]) {
"All" -> "All" ->
task("compatibilityTestAll") { task("compatibilityTestAll") {
dependsOn("compatibilityTestReleases", "compatibilityTestCandidate", "compatibilityTestNightly") dependsOn(
"compatibilityTestReleases",
"compatibilityTestCandidate",
"compatibilityTestNightly"
)
} }
// releases in configured range // releases in configured range
"Releases" -> "Releases" ->
task("compatibilityTestReleases") { task("compatibilityTestReleases") {
val versionInfos = GradleVersionInfo.fetchReleases() val versionInfos = GradleVersionInfo.fetchReleases()
val versionsToTestAgainst = versionInfos.filter { versionInfo -> val versionsToTestAgainst =
val v = versionInfo.gradleVersion versionInfos.filter { versionInfo ->
!versionInfo.broken && val v = versionInfo.gradleVersion
!versionInfo.broken &&
v in gradlePluginTests.minGradleVersion..gradlePluginTests.maxGradleVersion && v in gradlePluginTests.minGradleVersion..gradlePluginTests.maxGradleVersion &&
v !in gradlePluginTests.skippedGradleVersions v !in gradlePluginTests.skippedGradleVersions
} }
dependsOn(versionsToTestAgainst.map { createCompatibilityTestTask(it) }) dependsOn(versionsToTestAgainst.map { createCompatibilityTestTask(it) })
} }
@@ -45,8 +50,10 @@ tasks.addRule("Pattern: compatibilityTest[All|Releases|Latest|Candidate|Nightly|
val versionInfo = GradleVersionInfo.fetchCurrent() val versionInfo = GradleVersionInfo.fetchCurrent()
if (versionInfo.version == gradle.gradleVersion) { if (versionInfo.version == gradle.gradleVersion) {
doLast { doLast {
println("No new Gradle release available. " + println(
"(Run `gradlew test` to test against ${versionInfo.version}.)") "No new Gradle release available. " +
"(Run `gradlew test` to test against ${versionInfo.version}.)"
)
} }
} else { } else {
dependsOn(createCompatibilityTestTask(versionInfo)) dependsOn(createCompatibilityTestTask(versionInfo))
@@ -59,9 +66,7 @@ tasks.addRule("Pattern: compatibilityTest[All|Releases|Latest|Candidate|Nightly|
if (versionInfo?.activeRc == true) { if (versionInfo?.activeRc == true) {
dependsOn(createCompatibilityTestTask(versionInfo)) dependsOn(createCompatibilityTestTask(versionInfo))
} else { } else {
doLast { doLast { println("No active Gradle release candidate available.") }
println("No active Gradle release candidate available.")
}
} }
} }
// latest nightly // latest nightly
@@ -73,14 +78,14 @@ tasks.addRule("Pattern: compatibilityTest[All|Releases|Latest|Candidate|Nightly|
// explicit version // explicit version
else -> else ->
createCompatibilityTestTask( createCompatibilityTestTask(
taskNameSuffix, taskNameSuffix,
"https://services.gradle.org/distributions-snapshots/gradle-$taskNameSuffix-bin.zip" "https://services.gradle.org/distributions-snapshots/gradle-$taskNameSuffix-bin.zip"
) )
} }
} }
fun createCompatibilityTestTask(versionInfo: GradleVersionInfo): Task = fun createCompatibilityTestTask(versionInfo: GradleVersionInfo): Task =
createCompatibilityTestTask(versionInfo.version, versionInfo.downloadUrl) createCompatibilityTestTask(versionInfo.version, versionInfo.downloadUrl)
fun createCompatibilityTestTask(version: String, downloadUrl: String): Task { fun createCompatibilityTestTask(version: String, downloadUrl: String): Task {
return tasks.create("compatibilityTest$version", Test::class.java) { return tasks.create("compatibilityTest$version", Test::class.java) {

View File

@@ -1,20 +1,33 @@
plugins { /**
base * Copyright © 2024 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.
*/
plugins { base }
val htmlValidator = extensions.create<HtmlValidator>("htmlValidator", project) val htmlValidator = extensions.create<HtmlValidator>("htmlValidator", project)
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
val validatorConfiguration: Configuration = configurations.create("validator") { val validatorConfiguration: Configuration =
resolutionStrategy.eachDependency { configurations.create("validator") {
if (requested.group == "log4j" && requested.name == "log4j") { resolutionStrategy.eachDependency {
@Suppress("UnstableApiUsage") if (requested.group == "log4j" && requested.name == "log4j") {
useTarget(buildInfo.libs.findLibrary("log4j12Api").get()) @Suppress("UnstableApiUsage") useTarget(buildInfo.libs.findLibrary("log4j12Api").get())
because("mitigate critical security vulnerabilities") because("mitigate critical security vulnerabilities")
}
} }
} }
}
dependencies { dependencies {
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
@@ -32,27 +45,29 @@ dependencies {
} }
} }
val validateHtml by tasks.registering(JavaExec::class) { val validateHtml by
val resultFile = layout.buildDirectory.file("validateHtml/result.txt") tasks.registering(JavaExec::class) {
inputs.files(htmlValidator.sources) val resultFile = layout.buildDirectory.file("validateHtml/result.txt")
outputs.file(resultFile) inputs.files(htmlValidator.sources)
outputs.file(resultFile)
classpath = validatorConfiguration classpath = validatorConfiguration
mainClass.set("nu.validator.client.SimpleCommandLineValidator") mainClass.set("nu.validator.client.SimpleCommandLineValidator")
args("--skip-non-html") // --also-check-css doesn't work (still checks css as html), so limit to html files args(
args("--filterpattern", "(.*)Consider adding “lang=(.*)") "--skip-non-html"
args("--filterpattern", "(.*)Consider adding a “lang” attribute(.*)") ) // --also-check-css doesn't work (still checks css as html), so limit to html files
args("--filterpattern", "(.*)unrecognized media “amzn-kf8”(.*)") // kindle args("--filterpattern", "(.*)Consider adding “lang=(.*)")
// for debugging args("--filterpattern", "(.*)Consider adding a “lang” attribute(.*)")
// args "--verbose" args("--filterpattern", "(.*)unrecognized media “amzn-kf8”(.*)") // kindle
args(htmlValidator.sources) // for debugging
// args "--verbose"
args(htmlValidator.sources)
// write a basic result file s.t. gradle can consider task up-to-date // write a basic result file s.t. gradle can consider task up-to-date
// writing a result file in case validation fails is not easily possible with JavaExec, but also not strictly necessary // writing a result file in case validation fails is not easily possible with JavaExec, but also
doFirst { project.delete(resultFile) } // not strictly necessary
doLast { resultFile.get().asFile.writeText("Success.") } doFirst { project.delete(resultFile) }
} doLast { resultFile.get().asFile.writeText("Success.") }
}
tasks.check { tasks.check { dependsOn(validateHtml) }
dependsOn(validateHtml)
}

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
import org.gradle.accessors.dm.LibrariesForLibs import org.gradle.accessors.dm.LibrariesForLibs
plugins { plugins {

View File

@@ -1,9 +1,22 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat /**
* Copyright © 2024 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.
*/
import java.net.URI import java.net.URI
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
plugins { plugins { kotlin("jvm") }
kotlin("jvm")
}
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
@@ -23,38 +36,40 @@ tasks.withType<Test>().configureEach {
// enable checking of stdlib return types // enable checking of stdlib return types
systemProperty("org.pkl.testMode", "true") systemProperty("org.pkl.testMode", "true")
// Disable colour output in tests // Disable colour output in tests
systemProperty("org.fusesource.jansi.Ansi.disable", "true") systemProperty("org.fusesource.jansi.Ansi.disable", "true")
reports.named("html") { reports.named("html") { enabled = true }
enabled = true
}
testLogging { testLogging { exceptionFormat = TestExceptionFormat.FULL }
exceptionFormat = TestExceptionFormat.FULL
}
addTestListener(object : TestListener { addTestListener(
override fun beforeSuite(suite: TestDescriptor) {} object : TestListener {
override fun beforeTest(testDescriptor: TestDescriptor) {} override fun beforeSuite(suite: TestDescriptor) {}
override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {}
// print report link at end of task, not just at end of build override fun beforeTest(testDescriptor: TestDescriptor) {}
override fun afterSuite(descriptor: TestDescriptor, result: TestResult) {
if (descriptor.parent != null) return // only interested in overall result
if (result.resultType == TestResult.ResultType.FAILURE) { override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {}
println("\nThere were failing tests. See the report at: ${fixFileUri(testTask.reports.html.entryPoint.toURI())}")
// print report link at end of task, not just at end of build
override fun afterSuite(descriptor: TestDescriptor, result: TestResult) {
if (descriptor.parent != null) return // only interested in overall result
if (result.resultType == TestResult.ResultType.FAILURE) {
println(
"\nThere were failing tests. See the report at: ${fixFileUri(testTask.reports.html.entryPoint.toURI())}"
)
}
}
// makes links clickable on macOS
private fun fixFileUri(uri: URI): URI {
if ("file" == uri.scheme && !uri.schemeSpecificPart.startsWith("//")) {
return URI.create("file://" + uri.schemeSpecificPart)
}
return uri
} }
} }
)
// makes links clickable on macOS
private fun fixFileUri(uri: URI): URI {
if ("file" == uri.scheme && !uri.schemeSpecificPart.startsWith("//")) {
return URI.create("file://" + uri.schemeSpecificPart)
}
return uri
}
})
} }

View File

@@ -1,11 +1,22 @@
/**
* Copyright © 2024 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.
*/
val assembleNative by tasks.registering {} val assembleNative by tasks.registering {}
val testNative by tasks.registering {} val testNative by tasks.registering {}
val checkNative by tasks.registering { val checkNative by tasks.registering { dependsOn(testNative) }
dependsOn(testNative)
}
val buildNative by tasks.registering { val buildNative by tasks.registering { dependsOn(assembleNative, checkNative) }
dependsOn(assembleNative, checkNative)
}

View File

@@ -1,6 +1,21 @@
import org.gradle.api.publish.maven.tasks.GenerateMavenPom /**
* Copyright © 2024 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.
*/
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.Base64 import java.util.Base64
import org.gradle.api.publish.maven.tasks.GenerateMavenPom
plugins { plugins {
`maven-publish` `maven-publish`
@@ -10,9 +25,7 @@ plugins {
publishing { publishing {
publications { publications {
components.findByName("java")?.let { javaComponent -> components.findByName("java")?.let { javaComponent ->
create<MavenPublication>("library") { create<MavenPublication>("library") { from(javaComponent) }
from(javaComponent)
}
} }
withType<MavenPublication>().configureEach { withType<MavenPublication>().configureEach {
pom { pom {
@@ -49,63 +62,66 @@ publishing {
} }
} }
val validatePom by tasks.registering { val validatePom by
if (tasks.findByName("generatePomFileForLibraryPublication") == null) { tasks.registering {
return@registering if (tasks.findByName("generatePomFileForLibraryPublication") == null) {
} return@registering
val generatePomFileForLibraryPublication by tasks.existing(GenerateMavenPom::class) }
val outputFile = layout.buildDirectory.file("validatePom") // dummy output to satisfy up-to-date check val generatePomFileForLibraryPublication by tasks.existing(GenerateMavenPom::class)
val outputFile =
layout.buildDirectory.file("validatePom") // dummy output to satisfy up-to-date check
dependsOn(generatePomFileForLibraryPublication) dependsOn(generatePomFileForLibraryPublication)
inputs.file(generatePomFileForLibraryPublication.get().destination) inputs.file(generatePomFileForLibraryPublication.get().destination)
outputs.file(outputFile) outputs.file(outputFile)
doLast { doLast {
outputFile.get().asFile.delete() outputFile.get().asFile.delete()
val pomFile = generatePomFileForLibraryPublication.get().destination val pomFile = generatePomFileForLibraryPublication.get().destination
assert(pomFile.exists()) assert(pomFile.exists())
val text = pomFile.readText() val text = pomFile.readText()
run { run {
val unresolvedVersion = Regex("<version>.*[+,()\\[\\]].*</version>") val unresolvedVersion = Regex("<version>.*[+,()\\[\\]].*</version>")
val matches = unresolvedVersion.findAll(text).toList() val matches = unresolvedVersion.findAll(text).toList()
if (matches.isNotEmpty()) { if (matches.isNotEmpty()) {
throw GradleException( throw GradleException(
""" """
Found unresolved version selector(s) in generated POM: Found unresolved version selector(s) in generated POM:
${matches.joinToString("\n") { it.groupValues[0] }} ${matches.joinToString("\n") { it.groupValues[0] }}
""".trimIndent() """
) .trimIndent()
)
}
} }
}
val buildInfo = project.extensions.getByType<BuildInfo>() val buildInfo = project.extensions.getByType<BuildInfo>()
if (buildInfo.isReleaseBuild) { if (buildInfo.isReleaseBuild) {
val snapshotVersion = Regex("<version>.*-SNAPSHOT</version>") val snapshotVersion = Regex("<version>.*-SNAPSHOT</version>")
val matches = snapshotVersion.findAll(text).toList() val matches = snapshotVersion.findAll(text).toList()
if (matches.isNotEmpty()) { if (matches.isNotEmpty()) {
throw GradleException( throw GradleException(
""" """
Found snapshot version(s) in generated POM of Pkl release version: Found snapshot version(s) in generated POM of Pkl release version:
${matches.joinToString("\n") { it.groupValues[0] }} ${matches.joinToString("\n") { it.groupValues[0] }}
""".trimIndent() """
) .trimIndent()
)
}
} }
outputFile.get().asFile.writeText("OK")
} }
outputFile.get().asFile.writeText("OK")
} }
}
tasks.publish { tasks.publish { dependsOn(validatePom) }
dependsOn(validatePom)
}
// Workaround for maven publish plugin not setting up dependencies correctly. // Workaround for maven publish plugin not setting up dependencies correctly.
// Taken from https://github.com/gradle/gradle/issues/26091#issuecomment-1798137734 // Taken from https://github.com/gradle/gradle/issues/26091#issuecomment-1798137734
val dependsOnTasks = mutableListOf<String>() val dependsOnTasks = mutableListOf<String>()
tasks.withType<AbstractPublishToMaven>().configureEach { tasks.withType<AbstractPublishToMaven>().configureEach {
dependsOnTasks.add(name.replace("publish", "sign").replaceAfter("Publication", "")) dependsOnTasks.add(name.replace("publish", "sign").replaceAfter("Publication", ""))
dependsOn(dependsOnTasks) dependsOn(dependsOnTasks)
@@ -114,8 +130,10 @@ tasks.withType<AbstractPublishToMaven>().configureEach {
signing { signing {
// provided as env vars `ORG_GRADLE_PROJECT_signingKey` and `ORG_GRADLE_PROJECT_signingPassword` // provided as env vars `ORG_GRADLE_PROJECT_signingKey` and `ORG_GRADLE_PROJECT_signingPassword`
// in CI. // in CI.
val signingKey = (findProperty("signingKey") as String?) val signingKey =
?.let { Base64.getDecoder().decode(it).toString(StandardCharsets.US_ASCII) } (findProperty("signingKey") as String?)?.let {
Base64.getDecoder().decode(it).toString(StandardCharsets.US_ASCII)
}
val signingPassword = findProperty("signingPassword") as String? val signingPassword = findProperty("signingPassword") as String?
if (signingKey != null && signingPassword != null) { if (signingKey != null && signingPassword != null) {
useInMemoryPgpKeys(signingKey, signingPassword) useInMemoryPgpKeys(signingKey, signingPassword)

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
plugins { plugins {
@@ -12,10 +27,7 @@ sourceSets {
srcDir(file("modules/pkl-config-java/examples")) srcDir(file("modules/pkl-config-java/examples"))
srcDir(file("modules/java-binding/examples")) srcDir(file("modules/java-binding/examples"))
} }
val kotlin = project.extensions val kotlin = project.extensions.getByType<KotlinJvmProjectExtension>().sourceSets[name].kotlin
.getByType<KotlinJvmProjectExtension>()
.sourceSets[name]
.kotlin
kotlin.srcDir(file("modules/kotlin-binding/examples")) kotlin.srcDir(file("modules/kotlin-binding/examples"))
} }
} }
@@ -30,7 +42,8 @@ dependencies {
} }
tasks.test { tasks.test {
inputs.files(fileTree("modules").matching { inputs
include("**/pages/*.adoc") .files(fileTree("modules").matching { include("**/pages/*.adoc") })
}).withPropertyName("asciiDocFiles").withPathSensitivity(PathSensitivity.RELATIVE) .withPropertyName("asciiDocFiles")
.withPathSensitivity(PathSensitivity.RELATIVE)
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
plugins { plugins {
pklAllProjects pklAllProjects
pklKotlinLibrary pklKotlinLibrary
@@ -7,11 +22,9 @@ plugins {
// already on build script class path (see buildSrc/build.gradle.kts), // already on build script class path (see buildSrc/build.gradle.kts),
// hence must only specify plugin ID here // hence must only specify plugin ID here
@Suppress("DSL_SCOPE_VIOLATION") @Suppress("DSL_SCOPE_VIOLATION") id(libs.plugins.shadow.get().pluginId)
id(libs.plugins.shadow.get().pluginId)
@Suppress("DSL_SCOPE_VIOLATION") @Suppress("DSL_SCOPE_VIOLATION") alias(libs.plugins.checksum)
alias(libs.plugins.checksum)
} }
// make Java executable available to other subprojects // make Java executable available to other subprojects
@@ -54,7 +67,7 @@ dependencies {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8") exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common") exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
} }
testImplementation(projects.pklCommonsTest) testImplementation(projects.pklCommonsTest)
testImplementation(libs.wiremock) testImplementation(libs.wiremock)
@@ -69,17 +82,13 @@ dependencies {
} }
tasks.jar { tasks.jar {
manifest { manifest { attributes += mapOf("Main-Class" to "org.pkl.cli.Main") }
attributes += mapOf("Main-Class" to "org.pkl.cli.Main")
}
// not required at runtime // not required at runtime
exclude("org/pkl/cli/svm/**") exclude("org/pkl/cli/svm/**")
} }
tasks.javadoc { tasks.javadoc { enabled = false }
enabled = false
}
tasks.shadowJar { tasks.shadowJar {
archiveFileName.set("jpkl") archiveFileName.set("jpkl")
@@ -93,72 +102,74 @@ tasks.shadowJar {
exclude("module-info.*") exclude("module-info.*")
} }
val javaExecutable by tasks.registering(ExecutableJar::class) { val javaExecutable by
inJar.set(tasks.shadowJar.flatMap { it.archiveFile }) tasks.registering(ExecutableJar::class) {
outJar.set(layout.buildDirectory.file("executable/jpkl")) inJar.set(tasks.shadowJar.flatMap { it.archiveFile })
outJar.set(layout.buildDirectory.file("executable/jpkl"))
// uncomment for debugging // uncomment for debugging
//jvmArgs.addAll("-ea", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005") // jvmArgs.addAll("-ea", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005")
} }
val testJavaExecutable by tasks.registering(Test::class) { val testJavaExecutable by
testClassesDirs = tasks.test.get().testClassesDirs tasks.registering(Test::class) {
classpath = testClassesDirs = tasks.test.get().testClassesDirs
// compiled test classes classpath =
sourceSets.test.get().output + // compiled test classes
// java executable sourceSets.test.get().output +
javaExecutable.get().outputs.files + // java executable
// test-only dependencies javaExecutable.get().outputs.files +
// (test dependencies that are also main dependencies must already be contained in java executable; // test-only dependencies
// to verify that, we don't want to include them here) // (test dependencies that are also main dependencies must already be contained in java
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get()) // executable;
} // to verify that, we don't want to include them here)
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get())
}
tasks.check { tasks.check { dependsOn(testJavaExecutable) }
dependsOn(testJavaExecutable)
}
// 0.14 Java executable was broken because javaExecutable.jvmArgs wasn't commented out. // 0.14 Java executable was broken because javaExecutable.jvmArgs wasn't commented out.
// To catch this and similar problems, test that Java executable starts successfully. // To catch this and similar problems, test that Java executable starts successfully.
val testStartJavaExecutable by tasks.registering(Exec::class) { val testStartJavaExecutable by
dependsOn(javaExecutable) tasks.registering(Exec::class) {
val outputFile = dependsOn(javaExecutable)
layout.buildDirectory.file("testStartJavaExecutable") // dummy output to satisfy up-to-date check val outputFile =
outputs.file(outputFile) layout.buildDirectory.file(
"testStartJavaExecutable"
) // dummy output to satisfy up-to-date check
outputs.file(outputFile)
if (buildInfo.os.isWindows) { if (buildInfo.os.isWindows) {
executable = "java" executable = "java"
args("-jar", javaExecutable.get().outputs.files.singleFile.toString(), "--version") args("-jar", javaExecutable.get().outputs.files.singleFile.toString(), "--version")
} else { } else {
executable = javaExecutable.get().outputs.files.singleFile.toString() executable = javaExecutable.get().outputs.files.singleFile.toString()
args("--version") args("--version")
}
doFirst { outputFile.get().asFile.delete() }
doLast { outputFile.get().asFile.writeText("OK") }
} }
doFirst { outputFile.get().asFile.delete() } tasks.check { dependsOn(testStartJavaExecutable) }
doLast { outputFile.get().asFile.writeText("OK") }
}
tasks.check {
dependsOn(testStartJavaExecutable)
}
fun Exec.configureExecutable( fun Exec.configureExecutable(
graalVm: BuildInfo.GraalVm, graalVm: BuildInfo.GraalVm,
outputFile: Provider<RegularFile>, outputFile: Provider<RegularFile>,
extraArgs: List<String> = listOf() extraArgs: List<String> = listOf()
) { ) {
inputs.files(sourceSets.main.map { it.output }) inputs
.files(sourceSets.main.map { it.output })
.withPropertyName("mainSourceSets") .withPropertyName("mainSourceSets")
.withPathSensitivity(PathSensitivity.RELATIVE) .withPathSensitivity(PathSensitivity.RELATIVE)
inputs.files(configurations.runtimeClasspath) inputs
.files(configurations.runtimeClasspath)
.withPropertyName("runtimeClasspath") .withPropertyName("runtimeClasspath")
.withNormalizer(ClasspathNormalizer::class) .withNormalizer(ClasspathNormalizer::class)
val nativeImageCommandName = if (buildInfo.os.isWindows) "native-image.cmd" else "native-image" val nativeImageCommandName = if (buildInfo.os.isWindows) "native-image.cmd" else "native-image"
inputs.files( inputs
file(graalVm.baseDir) .files(file(graalVm.baseDir).resolve("bin/$nativeImageCommandName"))
.resolve("bin/$nativeImageCommandName")
)
.withPropertyName("graalVmNativeImage") .withPropertyName("graalVmNativeImage")
.withPathSensitivity(PathSensitivity.ABSOLUTE) .withPathSensitivity(PathSensitivity.ABSOLUTE)
outputs.file(outputFile) outputs.file(outputFile)
@@ -170,99 +181,108 @@ fun Exec.configureExecutable(
// JARs to exclude from the class path for the native-image build. // JARs to exclude from the class path for the native-image build.
val exclusions = listOf(libs.truffleApi, libs.graalSdk).map { it.get().module.name } val exclusions = listOf(libs.truffleApi, libs.graalSdk).map { it.get().module.name }
// https://www.graalvm.org/22.0/reference-manual/native-image/Options/ // https://www.graalvm.org/22.0/reference-manual/native-image/Options/
argumentProviders.add(CommandLineArgumentProvider { argumentProviders.add(
buildList { CommandLineArgumentProvider {
// currently gives a deprecation warning, but we've been told buildList {
// that the "initialize everything at build time" *CLI* option is likely here to stay // currently gives a deprecation warning, but we've been told
add("--initialize-at-build-time=") // that the "initialize everything at build time" *CLI* option is likely here to stay
// needed for messagepack-java (see https://github.com/msgpack/msgpack-java/issues/600) add("--initialize-at-build-time=")
// needed for jansi (see https://github.com/fusesource/jansi/issues/199#issuecomment-1252268229) // needed for messagepack-java (see https://github.com/msgpack/msgpack-java/issues/600)
add("--initialize-at-run-time=org.msgpack.core.buffer.DirectBufferAccess,org.fusesource.jansi.internal") // needed for jansi (see
add("--no-fallback") // https://github.com/fusesource/jansi/issues/199#issuecomment-1252268229)
add("-H:IncludeResources=org/pkl/core/stdlib/.*\\.pkl") add(
add("-H:IncludeResources=org/jline/utils/.*") "--initialize-at-run-time=org.msgpack.core.buffer.DirectBufferAccess,org.fusesource.jansi.internal"
add("-H:IncludeResourceBundles=org.pkl.core.errorMessages") )
add("-H:IncludeResources=org/pkl/commons/cli/PklCARoots.pem") add("--no-fallback")
add("--macro:truffle") add("-H:IncludeResources=org/pkl/core/stdlib/.*\\.pkl")
add("-H:Class=org.pkl.cli.Main") add("-H:IncludeResources=org/jline/utils/.*")
add("-H:Name=${outputFile.get().asFile.name}") add("-H:IncludeResourceBundles=org.pkl.core.errorMessages")
// the actual limit (currently) used by native-image is this number + 1400 (idea is to compensate for Truffle's own nodes) add("-H:IncludeResources=org/pkl/commons/cli/PklCARoots.pem")
add("-H:MaxRuntimeCompileMethods=1800") add("--macro:truffle")
add("-H:+EnforceMaxRuntimeCompileMethods") add("-H:Class=org.pkl.cli.Main")
add("--enable-url-protocols=http,https") add("-H:Name=${outputFile.get().asFile.name}")
add("-H:+ReportExceptionStackTraces") // the actual limit (currently) used by native-image is this number + 1400 (idea is to
// disable automatic support for JVM CLI options (puts our main class in full control of argument parsing) // compensate for Truffle's own nodes)
add("-H:-ParseRuntimeOptions") add("-H:MaxRuntimeCompileMethods=1800")
// quick build mode: 40% faster compilation, 20% smaller (but presumably also slower) executable add("-H:+EnforceMaxRuntimeCompileMethods")
if (!buildInfo.isReleaseBuild) { add("--enable-url-protocols=http,https")
add("-Ob") add("-H:+ReportExceptionStackTraces")
// disable automatic support for JVM CLI options (puts our main class in full control of
// argument parsing)
add("-H:-ParseRuntimeOptions")
// quick build mode: 40% faster compilation, 20% smaller (but presumably also slower)
// executable
if (!buildInfo.isReleaseBuild) {
add("-Ob")
}
add("-march=compatibility")
// native-image rejects non-existing class path entries -> filter
add("--class-path")
val pathInput =
sourceSets.main.get().output +
configurations.runtimeClasspath.get().filter {
it.exists() && !exclusions.any { exclude -> it.name.contains(exclude) }
}
add(pathInput.asPath)
// make sure dev machine stays responsive (15% slowdown on my laptop)
val processors =
Runtime.getRuntime().availableProcessors() /
if (buildInfo.os.isMacOsX && !buildInfo.isCiBuild) 4 else 1
add("-J-XX:ActiveProcessorCount=${processors}")
// Pass through all `HOMEBREW_` prefixed environment variables to allow build with shimmed
// tools.
addAll(environment.keys.filter { it.startsWith("HOMEBREW_") }.map { "-E$it" })
addAll(extraArgs)
} }
add("-march=compatibility")
// native-image rejects non-existing class path entries -> filter
add("--class-path")
val pathInput = sourceSets.main.get().output + configurations.runtimeClasspath.get()
.filter { it.exists() && !exclusions.any { exclude -> it.name.contains(exclude) } }
add(pathInput.asPath)
// make sure dev machine stays responsive (15% slowdown on my laptop)
val processors = Runtime.getRuntime().availableProcessors() /
if (buildInfo.os.isMacOsX && !buildInfo.isCiBuild) 4 else 1
add("-J-XX:ActiveProcessorCount=${processors}")
// Pass through all `HOMEBREW_` prefixed environment variables to allow build with shimmed tools.
addAll(environment.keys.filter { it.startsWith("HOMEBREW_") }.map { "-E$it" })
addAll(extraArgs)
} }
})
}
/**
* Builds the pkl CLI for macOS/amd64.
*/
val macExecutableAmd64: TaskProvider<Exec> by tasks.registering(Exec::class) {
dependsOn(":installGraalVmAmd64")
configureExecutable(
buildInfo.graalVmAmd64,
layout.buildDirectory.file("executable/pkl-macos-amd64")
) )
} }
/** /** Builds the pkl CLI for macOS/amd64. */
* Builds the pkl CLI for macOS/aarch64. val macExecutableAmd64: TaskProvider<Exec> by
*/ tasks.registering(Exec::class) {
val macExecutableAarch64: TaskProvider<Exec> by tasks.registering(Exec::class) { dependsOn(":installGraalVmAmd64")
dependsOn(":installGraalVmAarch64") configureExecutable(
configureExecutable( buildInfo.graalVmAmd64,
buildInfo.graalVmAarch64, layout.buildDirectory.file("executable/pkl-macos-amd64")
layout.buildDirectory.file("executable/pkl-macos-aarch64"),
listOf(
"-H:+AllowDeprecatedBuilderClassesOnImageClasspath"
) )
) }
}
/** /** Builds the pkl CLI for macOS/aarch64. */
* Builds the pkl CLI for linux/amd64. val macExecutableAarch64: TaskProvider<Exec> by
*/ tasks.registering(Exec::class) {
val linuxExecutableAmd64: TaskProvider<Exec> by tasks.registering(Exec::class) { dependsOn(":installGraalVmAarch64")
dependsOn(":installGraalVmAmd64") configureExecutable(
configureExecutable( buildInfo.graalVmAarch64,
buildInfo.graalVmAmd64, layout.buildDirectory.file("executable/pkl-macos-aarch64"),
layout.buildDirectory.file("executable/pkl-linux-amd64") listOf("-H:+AllowDeprecatedBuilderClassesOnImageClasspath")
) )
} }
/** Builds the pkl CLI for linux/amd64. */
val linuxExecutableAmd64: TaskProvider<Exec> by
tasks.registering(Exec::class) {
dependsOn(":installGraalVmAmd64")
configureExecutable(
buildInfo.graalVmAmd64,
layout.buildDirectory.file("executable/pkl-linux-amd64")
)
}
/** /**
* Builds the pkl CLI for linux/aarch64. * Builds the pkl CLI for linux/aarch64.
* *
* Right now, this is built within a container on Mac using emulation because CI does not have * Right now, this is built within a container on Mac using emulation because CI does not have ARM
* ARM instances. * instances.
*/ */
val linuxExecutableAarch64: TaskProvider<Exec> by tasks.registering(Exec::class) { val linuxExecutableAarch64: TaskProvider<Exec> by
dependsOn(":installGraalVmAarch64") tasks.registering(Exec::class) {
configureExecutable( dependsOn(":installGraalVmAarch64")
buildInfo.graalVmAarch64, configureExecutable(
layout.buildDirectory.file("executable/pkl-linux-aarch64") buildInfo.graalVmAarch64,
) layout.buildDirectory.file("executable/pkl-linux-aarch64")
} )
}
/** /**
* Builds a statically linked CLI for linux/amd64. * Builds a statically linked CLI for linux/amd64.
@@ -270,23 +290,25 @@ val linuxExecutableAarch64: TaskProvider<Exec> by tasks.registering(Exec::class)
* Note: we don't publish the same for linux/aarch64 because native-image doesn't support this. * Note: we don't publish the same for linux/aarch64 because native-image doesn't support this.
* Details: https://www.graalvm.org/22.0/reference-manual/native-image/ARM64/ * Details: https://www.graalvm.org/22.0/reference-manual/native-image/ARM64/
*/ */
val alpineExecutableAmd64: TaskProvider<Exec> by tasks.registering(Exec::class) { val alpineExecutableAmd64: TaskProvider<Exec> by
dependsOn(":installGraalVmAmd64") tasks.registering(Exec::class) {
configureExecutable( dependsOn(":installGraalVmAmd64")
buildInfo.graalVmAmd64, configureExecutable(
layout.buildDirectory.file("executable/pkl-alpine-linux-amd64"), buildInfo.graalVmAmd64,
listOf("--static", "--libc=musl") layout.buildDirectory.file("executable/pkl-alpine-linux-amd64"),
) listOf("--static", "--libc=musl")
} )
}
val windowsExecutableAmd64: TaskProvider<Exec> by tasks.registering(Exec::class) { val windowsExecutableAmd64: TaskProvider<Exec> by
dependsOn(":installGraalVmAmd64") tasks.registering(Exec::class) {
configureExecutable( dependsOn(":installGraalVmAmd64")
buildInfo.graalVmAmd64, configureExecutable(
layout.buildDirectory.file("executable/pkl-windows-amd64"), buildInfo.graalVmAmd64,
listOf("-Dfile.encoding=UTF-8") layout.buildDirectory.file("executable/pkl-windows-amd64"),
) listOf("-Dfile.encoding=UTF-8")
} )
}
tasks.assembleNative { tasks.assembleNative {
when { when {
@@ -296,15 +318,12 @@ tasks.assembleNative {
dependsOn(macExecutableAarch64) dependsOn(macExecutableAarch64)
} }
} }
buildInfo.os.isWindows -> { buildInfo.os.isWindows -> {
dependsOn(windowsExecutableAmd64) dependsOn(windowsExecutableAmd64)
} }
buildInfo.os.isLinux && buildInfo.arch == "aarch64" -> { buildInfo.os.isLinux && buildInfo.arch == "aarch64" -> {
dependsOn(linuxExecutableAarch64) dependsOn(linuxExecutableAarch64)
} }
buildInfo.os.isLinux && buildInfo.arch == "amd64" -> { buildInfo.os.isLinux && buildInfo.arch == "amd64" -> {
dependsOn(linuxExecutableAmd64) dependsOn(linuxExecutableAmd64)
if (buildInfo.hasMuslToolchain) { if (buildInfo.hasMuslToolchain) {
@@ -315,7 +334,8 @@ tasks.assembleNative {
} }
// make Java executable available to other subprojects // make Java executable available to other subprojects
// (we don't do the same for native executables because we don't want tasks assemble/build to build them) // (we don't do the same for native executables because we don't want tasks assemble/build to build
// them)
artifacts { artifacts {
add("javaExecutable", javaExecutable.map { it.outputs.files.singleFile }) { add("javaExecutable", javaExecutable.map { it.outputs.files.singleFile }) {
name = "pkl-cli-java" name = "pkl-cli-java"
@@ -325,7 +345,7 @@ artifacts {
} }
} }
//region Maven Publishing // region Maven Publishing
publishing { publishing {
publications { publications {
register<MavenPublication>("javaExecutable") { register<MavenPublication>("javaExecutable") {
@@ -344,7 +364,8 @@ publishing {
Pkl CLI executable for Java. Pkl CLI executable for Java.
Can be executed directly on *nix (if the `java` command is found on the PATH) and with `java -jar` otherwise. Can be executed directly on *nix (if the `java` command is found on the PATH) and with `java -jar` otherwise.
Requires Java 17 or higher. Requires Java 17 or higher.
""".trimIndent() """
.trimIndent()
) )
} }
} }
@@ -438,5 +459,4 @@ signing {
sign(publishing.publications["macExecutableAmd64"]) sign(publishing.publications["macExecutableAmd64"])
sign(publishing.publications["alpineLinuxExecutableAmd64"]) sign(publishing.publications["alpineLinuxExecutableAmd64"])
sign(publishing.publications["windowsExecutableAmd64"]) sign(publishing.publications["windowsExecutableAmd64"])
} } // endregion
//endregion

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
plugins { plugins {
pklAllProjects pklAllProjects
pklKotlinLibrary pklKotlinLibrary
@@ -19,25 +34,22 @@ dependencies {
// with `org.gradle.parallel=true` and without the line below, `test` strangely runs into: // with `org.gradle.parallel=true` and without the line below, `test` strangely runs into:
// java.lang.NoClassDefFoundError: Lorg/pkl/config/java/ConfigEvaluator; // java.lang.NoClassDefFoundError: Lorg/pkl/config/java/ConfigEvaluator;
// perhaps somehow related to InMemoryJavaCompiler? // perhaps somehow related to InMemoryJavaCompiler?
tasks.test { tasks.test { mustRunAfter(":pkl-config-java:testFatJar") }
mustRunAfter(":pkl-config-java:testFatJar")
}
tasks.jar { tasks.jar { manifest { attributes += mapOf("Main-Class" to "org.pkl.codegen.java.Main") } }
manifest {
attributes += mapOf("Main-Class" to "org.pkl.codegen.java.Main")
}
}
publishing { publishing {
publications { publications {
named<MavenPublication>("library") { named<MavenPublication>("library") {
pom { pom {
url.set("https://github.com/apple/pkl/tree/main/pkl-codegen-java") url.set("https://github.com/apple/pkl/tree/main/pkl-codegen-java")
description.set(""" description.set(
"""
Java source code generator that generates corresponding Java classes for Pkl classes, Java source code generator that generates corresponding Java classes for Pkl classes,
simplifying consumption of Pkl configuration as statically typed Java objects. simplifying consumption of Pkl configuration as statically typed Java objects.
""".trimIndent()) """
.trimIndent()
)
} }
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
plugins { plugins {
pklAllProjects pklAllProjects
pklKotlinLibrary pklKotlinLibrary
@@ -9,26 +24,25 @@ publishing {
named<MavenPublication>("library") { named<MavenPublication>("library") {
pom { pom {
url.set("https://github.com/apple/pkl/tree/main/pkl-codegen-kotlin") url.set("https://github.com/apple/pkl/tree/main/pkl-codegen-kotlin")
description.set(""" description.set(
"""
Kotlin source code generator that generates corresponding Kotlin classes for Pkl classes, Kotlin source code generator that generates corresponding Kotlin classes for Pkl classes,
simplifying consumption of Pkl configuration as statically typed Kotlin objects. simplifying consumption of Pkl configuration as statically typed Kotlin objects.
""".trimIndent()) """
.trimIndent()
)
} }
} }
} }
} }
tasks.jar { tasks.jar { manifest { attributes += mapOf("Main-Class" to "org.pkl.codegen.kotlin.Main") } }
manifest {
attributes += mapOf("Main-Class" to "org.pkl.codegen.kotlin.Main")
}
}
dependencies { dependencies {
implementation(projects.pklCommons) implementation(projects.pklCommons)
api(projects.pklCommonsCli) api(projects.pklCommonsCli)
api(projects.pklCore) api(projects.pklCore)
implementation(libs.kotlinPoet) implementation(libs.kotlinPoet)
implementation(libs.kotlinReflect) implementation(libs.kotlinReflect)

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
plugins { plugins {
pklAllProjects pklAllProjects
pklKotlinLibrary pklKotlinLibrary

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
import java.security.MessageDigest import java.security.MessageDigest
plugins { plugins {
@@ -19,8 +34,8 @@ dependencies {
/** /**
* Creates test packages from the `src/test/files/packages` directory. * Creates test packages from the `src/test/files/packages` directory.
* *
* These packages are used by PackageServer to serve assets when running * These packages are used by PackageServer to serve assets when running LanguageSnippetTests and
* LanguageSnippetTests and PackageResolversTest. * PackageResolversTest.
*/ */
val createTestPackages by tasks.registering val createTestPackages by tasks.registering
@@ -38,38 +53,38 @@ for (packageDir in file("src/main/files/packages").listFiles()!!) {
val zipFileName = "${packageDir.name}.zip" val zipFileName = "${packageDir.name}.zip"
val archiveFile = destinationDir.map { it.file(zipFileName) } val archiveFile = destinationDir.map { it.file(zipFileName) }
val zipTask = tasks.register("zip-${packageDir.name}", Zip::class) { val zipTask =
destinationDirectory.set(destinationDir) tasks.register("zip-${packageDir.name}", Zip::class) {
archiveFileName.set(zipFileName) destinationDirectory.set(destinationDir)
from(packageContents) archiveFileName.set(zipFileName)
// required so that checksums are reproducible from(packageContents)
isPreserveFileTimestamps = false // required so that checksums are reproducible
isReproducibleFileOrder = true isPreserveFileTimestamps = false
} isReproducibleFileOrder = true
val copyTask = tasks.register("copy-${packageDir.name}", Copy::class) {
dependsOn(zipTask)
from(metadataJson)
into(destinationDir)
val shasumFile = destinationDir.map { it.file("${packageDir.name}.json.sha256") }
outputs.file(shasumFile)
filter { line ->
line.replaceFirst("\$computedChecksum", archiveFile.get().asFile.computeChecksum())
} }
doLast {
val outputFile = destinationDir.get().asFile.resolve("${packageDir.name}.json") val copyTask =
if (buildInfo.os.isWindows) { tasks.register("copy-${packageDir.name}", Copy::class) {
val contents = outputFile.readText() dependsOn(zipTask)
// workaround for https://github.com/gradle/gradle/issues/1151 from(metadataJson)
outputFile.writeText(contents.replace("\r\n", "\n")) into(destinationDir)
val shasumFile = destinationDir.map { it.file("${packageDir.name}.json.sha256") }
outputs.file(shasumFile)
filter { line ->
line.replaceFirst("\$computedChecksum", archiveFile.get().asFile.computeChecksum())
}
doLast {
val outputFile = destinationDir.get().asFile.resolve("${packageDir.name}.json")
if (buildInfo.os.isWindows) {
val contents = outputFile.readText()
// workaround for https://github.com/gradle/gradle/issues/1151
outputFile.writeText(contents.replace("\r\n", "\n"))
}
shasumFile.get().asFile.writeText(outputFile.computeChecksum())
} }
shasumFile.get().asFile.writeText(outputFile.computeChecksum())
} }
}
createTestPackages.configure { createTestPackages.configure { dependsOn(copyTask) }
dependsOn(copyTask)
}
} }
val keystoreDir = layout.buildDirectory.dir("keystore") val keystoreDir = layout.buildDirectory.dir("keystore")
@@ -77,47 +92,61 @@ val keystoreName = "localhost.p12"
val keystoreFile = keystoreDir.map { it.file(keystoreName) } val keystoreFile = keystoreDir.map { it.file(keystoreName) }
val certsFileName = "localhost.pem" val certsFileName = "localhost.pem"
val generateKeys by tasks.registering(JavaExec::class) { val generateKeys by
outputs.file(keystoreFile) tasks.registering(JavaExec::class) {
mainClass.set("sun.security.tools.keytool.Main") outputs.file(keystoreFile)
args = listOf( mainClass.set("sun.security.tools.keytool.Main")
"-genkeypair", args =
"-keyalg", "RSA", listOf(
"-alias", "integ_tests", "-genkeypair",
"-keystore", keystoreName, "-keyalg",
"-storepass", "password", "RSA",
"-dname", "CN=localhost" "-alias",
) "integ_tests",
workingDir(keystoreDir) "-keystore",
doFirst { keystoreName,
workingDir.mkdirs() "-storepass",
keystoreFile.get().asFile.delete() "password",
"-dname",
"CN=localhost"
)
workingDir(keystoreDir)
doFirst {
workingDir.mkdirs()
keystoreFile.get().asFile.delete()
}
} }
}
val exportCerts by tasks.registering(JavaExec::class) { val exportCerts by
val outputFile = keystoreDir.map { it.file(certsFileName) } tasks.registering(JavaExec::class) {
dependsOn(generateKeys) val outputFile = keystoreDir.map { it.file(certsFileName) }
inputs.file(keystoreFile) dependsOn(generateKeys)
outputs.file(outputFile) inputs.file(keystoreFile)
mainClass.set("sun.security.tools.keytool.Main") outputs.file(outputFile)
args = listOf( mainClass.set("sun.security.tools.keytool.Main")
"-exportcert", args =
"-alias", "integ_tests", listOf(
"-storepass", "password", "-exportcert",
"-keystore", keystoreName, "-alias",
"-rfc", "integ_tests",
"-file", certsFileName "-storepass",
) "password",
workingDir(keystoreDir) "-keystore",
doFirst { keystoreName,
workingDir.mkdirs() "-rfc",
outputFile.get().asFile.delete() "-file",
certsFileName
)
workingDir(keystoreDir)
doFirst {
workingDir.mkdirs()
outputFile.get().asFile.delete()
}
} }
}
fun toHex(hash: ByteArray): String { fun toHex(hash: ByteArray): String {
val hexDigitTable = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') val hexDigitTable =
charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')
return buildString(hash.size * 2) { return buildString(hash.size * 2) {
for (b in hash) { for (b in hash) {
append(hexDigitTable[b.toInt() shr 4 and 0xF]) append(hexDigitTable[b.toInt() shr 4 and 0xF])

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
plugins { plugins {
pklAllProjects pklAllProjects
pklKotlinLibrary pklKotlinLibrary

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
plugins { plugins {
pklAllProjects pklAllProjects
pklJavaLibrary pklJavaLibrary
@@ -9,55 +24,53 @@ plugins {
val pklCodegenJava: Configuration by configurations.creating val pklCodegenJava: Configuration by configurations.creating
val firstPartySourcesJars by configurations.existing val firstPartySourcesJars by configurations.existing
val generateTestConfigClasses by tasks.registering(JavaExec::class) { val generateTestConfigClasses by
val outputDir = layout.buildDirectory.dir("testConfigClasses") tasks.registering(JavaExec::class) {
outputs.dir(outputDir) val outputDir = layout.buildDirectory.dir("testConfigClasses")
inputs.dir("src/test/resources/codegenPkl") outputs.dir(outputDir)
inputs.dir("src/test/resources/codegenPkl")
classpath = pklCodegenJava classpath = pklCodegenJava
mainClass.set("org.pkl.codegen.java.Main") mainClass.set("org.pkl.codegen.java.Main")
argumentProviders.add(CommandLineArgumentProvider { argumentProviders.add(
listOf( CommandLineArgumentProvider {
"--output-dir", outputDir.get().asFile.path, listOf("--output-dir", outputDir.get().asFile.path, "--generate-javadoc") +
"--generate-javadoc" fileTree("src/test/resources/codegenPkl").map { it.path }
) + fileTree("src/test/resources/codegenPkl").map { it.path } }
}) )
} }
tasks.processTestResources { tasks.processTestResources { dependsOn(generateTestConfigClasses) }
dependsOn(generateTestConfigClasses)
}
tasks.compileTestKotlin { tasks.compileTestKotlin { dependsOn(generateTestConfigClasses) }
dependsOn(generateTestConfigClasses)
}
val bundleTests by tasks.registering(Jar::class) { val bundleTests by tasks.registering(Jar::class) { from(sourceSets.test.get().output) }
from(sourceSets.test.get().output)
}
// Runs unit tests using jar'd class files as a source. // Runs unit tests using jar'd class files as a source.
// This is to test loading the ClassRegistry from within a jar, as opposed to directly from the file system. // This is to test loading the ClassRegistry from within a jar, as opposed to directly from the file
val testFromJar by tasks.registering(Test::class) { // system.
dependsOn(bundleTests) val testFromJar by
tasks.registering(Test::class) {
dependsOn(bundleTests)
testClassesDirs = files(tasks.test.get().testClassesDirs) testClassesDirs = files(tasks.test.get().testClassesDirs)
classpath = classpath =
// compiled test classes // compiled test classes
bundleTests.get().outputs.files + bundleTests.get().outputs.files +
// fat Jar // fat Jar
tasks.shadowJar.get().outputs.files + tasks.shadowJar.get().outputs.files +
// test-only dependencies // test-only dependencies
// (test dependencies that are also main dependencies must already be contained in fat Jar; // (test dependencies that are also main dependencies must already be contained in fat Jar;
// to verify that, we don't want to include them here) // to verify that, we don't want to include them here)
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get()) (configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get())
} }
// TODO: the below snippet causes `./gradlew check` to fail specifically on `pkl-codegen-java:check`. Why? // TODO: the below snippet causes `./gradlew check` to fail specifically on
//tasks.test { // `pkl-codegen-java:check`. Why?
// tasks.test {
// dependsOn(testFromJar) // dependsOn(testFromJar)
//} // }
sourceSets.getByName("test") { sourceSets.getByName("test") {
java.srcDir(layout.buildDirectory.dir("testConfigClasses/java")) java.srcDir(layout.buildDirectory.dir("testConfigClasses/java"))
@@ -77,9 +90,7 @@ dependencies {
pklCodegenJava(projects.pklCodegenJava) pklCodegenJava(projects.pklCodegenJava)
} }
tasks.shadowJar { tasks.shadowJar { archiveBaseName.set("pkl-config-java-all") }
archiveBaseName.set("pkl-config-java-all")
}
publishing { publishing {
publications { publications {
@@ -94,12 +105,12 @@ publishing {
artifactId = "pkl-config-java-all" artifactId = "pkl-config-java-all"
pom { pom {
url.set("https://github.com/apple/pkl/tree/main/pkl-config-java") url.set("https://github.com/apple/pkl/tree/main/pkl-config-java")
description.set("Shaded fat Jar for pkl-config-java, a Java config library based on the Pkl config language.") description.set(
"Shaded fat Jar for pkl-config-java, a Java config library based on the Pkl config language."
)
} }
} }
} }
} }
signing { signing { sign(publishing.publications["fatJar"]) }
sign(publishing.publications["fatJar"])
}

View File

@@ -1,19 +1,37 @@
/**
* Copyright © 2024 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.config.java.mapper package org.pkl.config.java.mapper
import org.pkl.config.java.ConfigEvaluator
import org.pkl.core.ModuleSource
import com.example.Lib import com.example.Lib
import com.example.PolymorphicModuleTest import com.example.PolymorphicModuleTest
import com.example.PolymorphicModuleTest.Strudel import com.example.PolymorphicModuleTest.Strudel
import com.example.PolymorphicModuleTest.TurkishDelight import com.example.PolymorphicModuleTest.TurkishDelight
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.config.java.ConfigEvaluator
import org.pkl.core.ModuleSource
class PolymorphicTest { class PolymorphicTest {
@Test @Test
fun `deserializing polymorphic objects`() { fun `deserializing polymorphic objects`() {
val evaluator = ConfigEvaluator.preconfigured() val evaluator = ConfigEvaluator.preconfigured()
val module = evaluator.evaluate(ModuleSource.modulePath("/codegenPkl/PolymorphicModuleTest.pkl")).`as`(PolymorphicModuleTest::class.java) val module =
evaluator
.evaluate(ModuleSource.modulePath("/codegenPkl/PolymorphicModuleTest.pkl"))
.`as`(PolymorphicModuleTest::class.java)
assertThat(module.desserts[0]).isInstanceOf(Strudel::class.java) assertThat(module.desserts[0]).isInstanceOf(Strudel::class.java)
assertThat(module.desserts[1]).isInstanceOf(TurkishDelight::class.java) assertThat(module.desserts[1]).isInstanceOf(TurkishDelight::class.java)
assertThat(module.planes[0]).isInstanceOf(Lib.Jet::class.java) assertThat(module.planes[0]).isInstanceOf(Lib.Jet::class.java)

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
plugins { plugins {
pklAllProjects pklAllProjects
pklKotlinLibrary pklKotlinLibrary
@@ -23,61 +38,61 @@ dependencies {
pklCodegenKotlin(projects.pklCodegenKotlin) pklCodegenKotlin(projects.pklCodegenKotlin)
implementation(libs.kotlinReflect) implementation(libs.kotlinReflect)
testImplementation(libs.geantyref) testImplementation(libs.geantyref)
} }
val generateTestConfigClasses by tasks.registering(JavaExec::class) { val generateTestConfigClasses by
val outputDir = layout.buildDirectory.dir("testConfigClasses") tasks.registering(JavaExec::class) {
outputs.dir(outputDir) val outputDir = layout.buildDirectory.dir("testConfigClasses")
inputs.dir("src/test/resources/codegenPkl") outputs.dir(outputDir)
inputs.dir("src/test/resources/codegenPkl")
classpath = pklCodegenKotlin classpath = pklCodegenKotlin
mainClass.set("org.pkl.codegen.kotlin.Main") mainClass.set("org.pkl.codegen.kotlin.Main")
argumentProviders.add(CommandLineArgumentProvider { argumentProviders.add(
listOf("--output-dir", outputDir.get().asFile.absolutePath) + CommandLineArgumentProvider {
fileTree("src/test/resources/codegenPkl").map { it.absolutePath } listOf("--output-dir", outputDir.get().asFile.absolutePath) +
}) fileTree("src/test/resources/codegenPkl").map { it.absolutePath }
} }
)
}
sourceSets.getByName("test") { sourceSets.getByName("test") {
java.srcDir(layout.buildDirectory.dir("testConfigClasses/kotlin")) java.srcDir(layout.buildDirectory.dir("testConfigClasses/kotlin"))
resources.srcDir(layout.buildDirectory.dir("testConfigClasses/resources")) resources.srcDir(layout.buildDirectory.dir("testConfigClasses/resources"))
} }
tasks.processTestResources { tasks.processTestResources { dependsOn(generateTestConfigClasses) }
dependsOn(generateTestConfigClasses)
}
tasks.compileTestKotlin { tasks.compileTestKotlin { dependsOn(generateTestConfigClasses) }
dependsOn(generateTestConfigClasses)
}
// use pkl-config-java-all for testing (same as for publishing) // use pkl-config-java-all for testing (same as for publishing)
tasks.test { tasks.test { classpath = classpath - pklConfigJava + pklConfigJavaAll }
classpath = classpath - pklConfigJava + pklConfigJavaAll
}
// disable publishing of .module until we find a way to manipulate it like POM (or ideally both together) // disable publishing of .module until we find a way to manipulate it like POM (or ideally both
tasks.withType<GenerateModuleMetadata> { // together)
enabled = false tasks.withType<GenerateModuleMetadata> { enabled = false }
}
publishing { publishing {
publications { publications {
named<MavenPublication>("library") { named<MavenPublication>("library") {
pom { pom {
url.set("https://github.com/apple/pkl/tree/main/pkl-config-kotlin") url.set("https://github.com/apple/pkl/tree/main/pkl-config-kotlin")
description.set("Kotlin extensions for pkl-config-java, a Java config library based on the Pkl config language.") description.set(
"Kotlin extensions for pkl-config-java, a Java config library based on the Pkl config language."
)
// change dependency pkl-config-java to pkl-config-java-all // change dependency pkl-config-java to pkl-config-java-all
withXml { withXml {
val projectElement = asElement() val projectElement = asElement()
val dependenciesElement = projectElement.getElementsByTagName("dependencies").item(0) as org.w3c.dom.Element val dependenciesElement =
projectElement.getElementsByTagName("dependencies").item(0) as org.w3c.dom.Element
val dependencyElements = dependenciesElement.getElementsByTagName("dependency") val dependencyElements = dependenciesElement.getElementsByTagName("dependency")
for (idx in 0 until dependencyElements.length) { for (idx in 0 until dependencyElements.length) {
val dependencyElement = dependencyElements.item(idx) as org.w3c.dom.Element val dependencyElement = dependencyElements.item(idx) as org.w3c.dom.Element
val artifactIdElement = dependencyElement.getElementsByTagName("artifactId").item(0) as org.w3c.dom.Element val artifactIdElement =
dependencyElement.getElementsByTagName("artifactId").item(0) as org.w3c.dom.Element
if (artifactIdElement.textContent == "pkl-config-java") { if (artifactIdElement.textContent == "pkl-config-java") {
artifactIdElement.textContent = "pkl-config-java-all" artifactIdElement.textContent = "pkl-config-java-all"
return@withXml return@withXml

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.
*/
import org.apache.tools.ant.filters.ReplaceTokens import org.apache.tools.ant.filters.ReplaceTokens
plugins { plugins {
@@ -12,13 +27,7 @@ plugins {
val generatorSourceSet = sourceSets.register("generator") val generatorSourceSet = sourceSets.register("generator")
sourceSets { sourceSets { main { java { srcDir(file("generated/antlr")) } } }
main {
java {
srcDir(file("generated/antlr"))
}
}
}
idea { idea {
module { module {
@@ -51,16 +60,14 @@ dependencies {
implementation(libs.truffleApi) implementation(libs.truffleApi)
implementation(libs.graalSdk) implementation(libs.graalSdk)
implementation(libs.paguro) { implementation(libs.paguro) { exclude(group = "org.jetbrains", module = "annotations") }
exclude(group = "org.jetbrains", module = "annotations")
}
implementation(libs.snakeYaml) implementation(libs.snakeYaml)
implementation(libs.jansi) implementation(libs.jansi)
testImplementation(projects.pklCommonsTest) testImplementation(projects.pklCommonsTest)
add("generatorImplementation", libs.javaPoet) add("generatorImplementation", libs.javaPoet)
add("generatorImplementation", libs.truffleApi) add("generatorImplementation", libs.truffleApi)
add("generatorImplementation", libs.kotlinStdLib) add("generatorImplementation", libs.kotlinStdLib)
@@ -73,11 +80,14 @@ publishing {
named<MavenPublication>("library") { named<MavenPublication>("library") {
pom { pom {
url.set("https://github.com/apple/pkl/tree/main/pkl-core") url.set("https://github.com/apple/pkl/tree/main/pkl-core")
description.set(""" description.set(
"""
Core implementation of the Pkl configuration language. Core implementation of the Pkl configuration language.
Includes Java APIs for embedding the language into JVM applications, Includes Java APIs for embedding the language into JVM applications,
and for building libraries and tools on top of the language. and for building libraries and tools on top of the language.
""".trimIndent()) """
.trimIndent()
)
} }
} }
} }
@@ -97,63 +107,52 @@ tasks.generateGrammarSource {
outputDirectory = file("generated/antlr/org/pkl/core/parser/antlr") outputDirectory = file("generated/antlr/org/pkl/core/parser/antlr")
} }
tasks.compileJava { tasks.compileJava { dependsOn(tasks.generateGrammarSource) }
dependsOn(tasks.generateGrammarSource)
}
tasks.sourcesJar { tasks.sourcesJar { dependsOn(tasks.generateGrammarSource) }
dependsOn(tasks.generateGrammarSource)
}
tasks.generateTestGrammarSource { tasks.generateTestGrammarSource { enabled = false }
enabled = false
} tasks.named("generateGeneratorGrammarSource") { enabled = false }
tasks.named("generateGeneratorGrammarSource") {
enabled = false
}
// Satisfy expectations of IntelliJ ANTLR plugin, // Satisfy expectations of IntelliJ ANTLR plugin,
// which can't otherwise cope with our ANTLR setup. // which can't otherwise cope with our ANTLR setup.
val makeIntelliJAntlrPluginHappy by tasks.registering(Copy::class) { val makeIntelliJAntlrPluginHappy by
dependsOn(tasks.generateGrammarSource) tasks.registering(Copy::class) {
into("src/main/antlr") dependsOn(tasks.generateGrammarSource)
from("generated/antlr/org/pkl/core/parser/antlr") { into("src/main/antlr")
include("PklLexer.tokens") from("generated/antlr/org/pkl/core/parser/antlr") { include("PklLexer.tokens") }
} }
}
tasks.processResources { tasks.processResources {
inputs.property("version", buildInfo.pklVersion) inputs.property("version", buildInfo.pklVersion)
inputs.property("commitId", buildInfo.commitId) inputs.property("commitId", buildInfo.commitId)
filesMatching("org/pkl/core/Release.properties") { filesMatching("org/pkl/core/Release.properties") {
val stdlibModules = fileTree("$rootDir/stdlib") { val stdlibModules =
include("*.pkl") fileTree("$rootDir/stdlib") {
exclude("doc-package-info.pkl") include("*.pkl")
}.map { "pkl:" + it.nameWithoutExtension } exclude("doc-package-info.pkl")
.sortedBy { it.lowercase() } }
.map { "pkl:" + it.nameWithoutExtension }
filter<ReplaceTokens>("tokens" to mapOf( .sortedBy { it.lowercase() }
"version" to buildInfo.pklVersion,
"commitId" to buildInfo.commitId, filter<ReplaceTokens>(
"stdlibModules" to stdlibModules.joinToString(",") "tokens" to
)) mapOf(
"version" to buildInfo.pklVersion,
"commitId" to buildInfo.commitId,
"stdlibModules" to stdlibModules.joinToString(",")
)
)
} }
into("org/pkl/core/stdlib") { into("org/pkl/core/stdlib") { from("$rootDir/stdlib") { include("*.pkl") } }
from("$rootDir/stdlib") {
include("*.pkl")
}
}
} }
tasks.compileJava { tasks.compileJava { options.generatedSourceOutputDirectory.set(file("generated/truffle")) }
options.generatedSourceOutputDirectory.set(file("generated/truffle"))
}
tasks.compileKotlin { tasks.compileKotlin { enabled = false }
enabled = false
}
tasks.test { tasks.test {
configureTest() configureTest()
@@ -166,52 +165,58 @@ tasks.test {
} }
} }
val testJavaExecutable by tasks.registering(Test::class) { val testJavaExecutable by
configureExecutableTest("LanguageSnippetTestsEngine") tasks.registering(Test::class) {
classpath = configureExecutableTest("LanguageSnippetTestsEngine")
// compiled test classes classpath =
sourceSets.test.get().output + // compiled test classes
// java executable sourceSets.test.get().output +
javaExecutableConfiguration + // java executable
// test-only dependencies javaExecutableConfiguration +
// (test dependencies that are also main dependencies must already be contained in java executable; // test-only dependencies
// to verify that we don't want to include them here) // (test dependencies that are also main dependencies must already be contained in java
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get()) // executable;
} // to verify that we don't want to include them here)
(configurations.testRuntimeClasspath.get() - configurations.runtimeClasspath.get())
}
tasks.check { tasks.check { dependsOn(testJavaExecutable) }
dependsOn(testJavaExecutable)
}
val testMacExecutableAmd64 by tasks.registering(Test::class) { val testMacExecutableAmd64 by
dependsOn(":pkl-cli:macExecutableAmd64") tasks.registering(Test::class) {
configureExecutableTest("MacAmd64LanguageSnippetTestsEngine") dependsOn(":pkl-cli:macExecutableAmd64")
} configureExecutableTest("MacAmd64LanguageSnippetTestsEngine")
}
val testMacExecutableAarch64 by tasks.registering(Test::class) { val testMacExecutableAarch64 by
dependsOn(":pkl-cli:macExecutableAarch64") tasks.registering(Test::class) {
configureExecutableTest("MacAarch64LanguageSnippetTestsEngine") dependsOn(":pkl-cli:macExecutableAarch64")
} configureExecutableTest("MacAarch64LanguageSnippetTestsEngine")
}
val testLinuxExecutableAmd64 by tasks.registering(Test::class) { val testLinuxExecutableAmd64 by
dependsOn(":pkl-cli:linuxExecutableAmd64") tasks.registering(Test::class) {
configureExecutableTest("LinuxAmd64LanguageSnippetTestsEngine") dependsOn(":pkl-cli:linuxExecutableAmd64")
} configureExecutableTest("LinuxAmd64LanguageSnippetTestsEngine")
}
val testLinuxExecutableAarch64 by tasks.registering(Test::class) { val testLinuxExecutableAarch64 by
dependsOn(":pkl-cli:linuxExecutableAarch64") tasks.registering(Test::class) {
configureExecutableTest("LinuxAarch64LanguageSnippetTestsEngine") dependsOn(":pkl-cli:linuxExecutableAarch64")
} configureExecutableTest("LinuxAarch64LanguageSnippetTestsEngine")
}
val testAlpineExecutableAmd64 by tasks.registering(Test::class) { val testAlpineExecutableAmd64 by
dependsOn(":pkl-cli:alpineExecutableAmd64") tasks.registering(Test::class) {
configureExecutableTest("AlpineLanguageSnippetTestsEngine") dependsOn(":pkl-cli:alpineExecutableAmd64")
} configureExecutableTest("AlpineLanguageSnippetTestsEngine")
}
val testWindowsExecutableAmd64 by tasks.registering(Test::class) { val testWindowsExecutableAmd64 by
dependsOn(":pkl-cli:windowsExecutableAmd64") tasks.registering(Test::class) {
configureExecutableTest("WindowsLanguageSnippetTestsEngine") dependsOn(":pkl-cli:windowsExecutableAmd64")
} configureExecutableTest("WindowsLanguageSnippetTestsEngine")
}
tasks.testNative { tasks.testNative {
when { when {
@@ -249,13 +254,16 @@ spotless {
} }
private fun Test.configureTest() { private fun Test.configureTest() {
inputs.dir("src/test/files/LanguageSnippetTests/input") inputs
.dir("src/test/files/LanguageSnippetTests/input")
.withPropertyName("languageSnippetTestsInput") .withPropertyName("languageSnippetTestsInput")
.withPathSensitivity(PathSensitivity.RELATIVE) .withPathSensitivity(PathSensitivity.RELATIVE)
inputs.dir("src/test/files/LanguageSnippetTests/input-helper") inputs
.dir("src/test/files/LanguageSnippetTests/input-helper")
.withPropertyName("languageSnippetTestsInputHelper") .withPropertyName("languageSnippetTestsInputHelper")
.withPathSensitivity(PathSensitivity.RELATIVE) .withPathSensitivity(PathSensitivity.RELATIVE)
inputs.dir("src/test/files/LanguageSnippetTests/output") inputs
.dir("src/test/files/LanguageSnippetTests/output")
.withPropertyName("languageSnippetTestsOutput") .withPropertyName("languageSnippetTestsOutput")
.withPathSensitivity(PathSensitivity.RELATIVE) .withPathSensitivity(PathSensitivity.RELATIVE)
} }
@@ -264,7 +272,5 @@ private fun Test.configureExecutableTest(engineName: String) {
configureTest() configureTest()
testClassesDirs = files(tasks.test.get().testClassesDirs) testClassesDirs = files(tasks.test.get().testClassesDirs)
classpath = tasks.test.get().classpath classpath = tasks.test.get().classpath
useJUnitPlatform { useJUnitPlatform { includeEngines(engineName) }
includeEngines(engineName)
}
} }

View File

@@ -1,26 +1,41 @@
/**
* Copyright © 2024 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.core.generator package org.pkl.core.generator
import com.oracle.truffle.api.dsl.GeneratedBy import com.oracle.truffle.api.dsl.GeneratedBy
import com.squareup.javapoet.ClassName import com.squareup.javapoet.ClassName
import javax.lang.model.SourceVersion
import javax.annotation.processing.RoundEnvironment
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.JavaFile import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec import com.squareup.javapoet.TypeSpec
import javax.annotation.processing.AbstractProcessor import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.* import javax.lang.model.element.*
import javax.lang.model.type.TypeMirror import javax.lang.model.type.TypeMirror
/** /**
* Generates a subclass of `org.pkl.core.stdlib.registry.ExternalMemberRegistry` * Generates a subclass of `org.pkl.core.stdlib.registry.ExternalMemberRegistry` for each stdlib
* for each stdlib module and a factory to instantiate them. * module and a factory to instantiate them. Generated classes are written to
* Generated classes are written to `generated/truffle/org/pkl/core/stdlib/registry`. * `generated/truffle/org/pkl/core/stdlib/registry`.
* *
* Inputs: * Inputs:
* - Generated Truffle node classes for stdlib members. * - Generated Truffle node classes for stdlib members. These classes are located in subpackages of
* These classes are located in subpackages of `org.pkl.core.stdlib` * `org.pkl.core.stdlib` and identified via their `@GeneratedBy` annotations.
* and identified via their `@GeneratedBy` annotations. * - `@PklName` annotations on handwritten node classes from which Truffle node classes are
* - `@PklName` annotations on handwritten node classes from which Truffle node classes are generated. * generated.
*/ */
class MemberRegistryGenerator : AbstractProcessor() { class MemberRegistryGenerator : AbstractProcessor() {
private val truffleNodeClassSuffix = "NodeGen" private val truffleNodeClassSuffix = "NodeGen"
@@ -35,10 +50,8 @@ class MemberRegistryGenerator : AbstractProcessor() {
ClassName.get(registryPackageName, "EmptyMemberRegistry") ClassName.get(registryPackageName, "EmptyMemberRegistry")
private val memberRegistryFactoryClassName: ClassName = private val memberRegistryFactoryClassName: ClassName =
ClassName.get(registryPackageName, "MemberRegistryFactory") ClassName.get(registryPackageName, "MemberRegistryFactory")
private val moduleKeyClassName: ClassName = private val moduleKeyClassName: ClassName = ClassName.get(modulePackageName, "ModuleKey")
ClassName.get(modulePackageName, "ModuleKey") private val moduleKeysClassName: ClassName = ClassName.get(modulePackageName, "ModuleKeys")
private val moduleKeysClassName: ClassName =
ClassName.get(modulePackageName, "ModuleKeys")
override fun getSupportedAnnotationTypes(): Set<String> = setOf(GeneratedBy::class.java.name) override fun getSupportedAnnotationTypes(): Set<String> = setOf(GeneratedBy::class.java.name)
@@ -54,19 +67,27 @@ class MemberRegistryGenerator : AbstractProcessor() {
return true return true
} }
private fun collectNodeClasses(roundEnv: RoundEnvironment) = roundEnv private fun collectNodeClasses(roundEnv: RoundEnvironment) =
.getElementsAnnotatedWith(GeneratedBy::class.java) roundEnv
.asSequence() .getElementsAnnotatedWith(GeneratedBy::class.java)
.filterIsInstance<TypeElement>() .asSequence()
.filter { it.qualifiedName.toString().startsWith(stdLibPackageName) } .filterIsInstance<TypeElement>()
.filter { it.simpleName.toString().endsWith(truffleNodeClassSuffix) } .filter { it.qualifiedName.toString().startsWith(stdLibPackageName) }
.sortedWith(compareBy( .filter { it.simpleName.toString().endsWith(truffleNodeClassSuffix) }
{ if (it.enclosingElement.kind == ElementKind.PACKAGE) "" else it.enclosingElement.simpleName.toString() }, .sortedWith(
{ it.simpleName.toString() } compareBy(
)) {
.groupBy { processingEnv.elementUtils.getPackageOf(it) } if (it.enclosingElement.kind == ElementKind.PACKAGE) ""
else it.enclosingElement.simpleName.toString()
},
{ it.simpleName.toString() }
)
)
.groupBy { processingEnv.elementUtils.getPackageOf(it) }
private fun generateRegistryClasses(nodeClassesByPackage: Map<PackageElement, List<TypeElement>>) { private fun generateRegistryClasses(
nodeClassesByPackage: Map<PackageElement, List<TypeElement>>
) {
for ((pkg, nodeClasses) in nodeClassesByPackage) { for ((pkg, nodeClasses) in nodeClassesByPackage) {
generateRegistryClass(pkg, nodeClasses) generateRegistryClass(pkg, nodeClasses)
} }
@@ -75,32 +96,38 @@ class MemberRegistryGenerator : AbstractProcessor() {
private fun generateRegistryClass(pkg: PackageElement, nodeClasses: List<TypeElement>) { private fun generateRegistryClass(pkg: PackageElement, nodeClasses: List<TypeElement>) {
val pklModuleName = getAnnotatedPklName(pkg) ?: pkg.simpleName.toString() val pklModuleName = getAnnotatedPklName(pkg) ?: pkg.simpleName.toString()
val pklModuleNameCapitalized = pklModuleName.capitalize() val pklModuleNameCapitalized = pklModuleName.capitalize()
val registryClassName = ClassName.get(registryPackageName, "${pklModuleNameCapitalized}MemberRegistry") val registryClassName =
ClassName.get(registryPackageName, "${pklModuleNameCapitalized}MemberRegistry")
val registryClass = TypeSpec.classBuilder(registryClassName) val registryClass =
.addJavadoc("Generated by {@link ${this::class.qualifiedName}}.") TypeSpec.classBuilder(registryClassName)
.addModifiers(Modifier.FINAL) .addJavadoc("Generated by {@link ${this::class.qualifiedName}}.")
.superclass(externalMemberRegistryClassName) .addModifiers(Modifier.FINAL)
.superclass(externalMemberRegistryClassName)
val registryClassConstructor = MethodSpec.constructorBuilder() val registryClassConstructor = MethodSpec.constructorBuilder()
for (nodeClass in nodeClasses) { for (nodeClass in nodeClasses) {
val enclosingClass = nodeClass.enclosingElement val enclosingClass = nodeClass.enclosingElement
val pklClassName = getAnnotatedPklName(enclosingClass) val pklClassName =
?: enclosingClass.simpleName.toString().removeSuffix(truffleNodeFactorySuffix) getAnnotatedPklName(enclosingClass)
val pklMemberName = getAnnotatedPklName(nodeClass) ?: enclosingClass.simpleName.toString().removeSuffix(truffleNodeFactorySuffix)
?: nodeClass.simpleName.toString().removeSuffix(truffleNodeClassSuffix) val pklMemberName =
val pklMemberNameQualified = when (pklClassName) { getAnnotatedPklName(nodeClass)
// By convention, the top-level class containing node classes ?: nodeClass.simpleName.toString().removeSuffix(truffleNodeClassSuffix)
// for *module* members is named `<SimpleModuleName>Nodes`. val pklMemberNameQualified =
// Example: `BaseNodes` for pkl.base when (pklClassName) {
pklModuleNameCapitalized -> // By convention, the top-level class containing node classes
"pkl.$pklModuleName#$pklMemberName" // for *module* members is named `<SimpleModuleName>Nodes`.
else -> // Example: `BaseNodes` for pkl.base
"pkl.$pklModuleName#$pklClassName.$pklMemberName" pklModuleNameCapitalized -> "pkl.$pklModuleName#$pklMemberName"
} else -> "pkl.$pklModuleName#$pklClassName.$pklMemberName"
}
registryClass.addOriginatingElement(nodeClass) registryClass.addOriginatingElement(nodeClass)
registryClassConstructor registryClassConstructor.addStatement(
.addStatement("register(\$S, \$T::create)", pklMemberNameQualified, nodeClass) "register(\$S, \$T::create)",
pklMemberNameQualified,
nodeClass
)
} }
registryClass.addMethod(registryClassConstructor.build()) registryClass.addMethod(registryClassConstructor.build())
@@ -109,25 +136,27 @@ class MemberRegistryGenerator : AbstractProcessor() {
} }
private fun generateRegistryFactoryClass(packages: Collection<PackageElement>) { private fun generateRegistryFactoryClass(packages: Collection<PackageElement>) {
val registryFactoryClass = TypeSpec.classBuilder(memberRegistryFactoryClassName) val registryFactoryClass =
.addJavadoc("Generated by {@link ${this::class.qualifiedName}}.") TypeSpec.classBuilder(memberRegistryFactoryClassName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addJavadoc("Generated by {@link ${this::class.qualifiedName}}.")
val registryFactoryConstructor = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addModifiers(Modifier.PRIVATE) val registryFactoryConstructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE)
registryFactoryClass.addMethod(registryFactoryConstructor.build()) registryFactoryClass.addMethod(registryFactoryConstructor.build())
val registryFactoryGetMethod = MethodSpec.methodBuilder("get") val registryFactoryGetMethod =
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) MethodSpec.methodBuilder("get")
.addParameter(moduleKeyClassName, "moduleKey") .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(externalMemberRegistryClassName) .addParameter(moduleKeyClassName, "moduleKey")
.beginControlFlow("if (!\$T.isStdLibModule(moduleKey))", moduleKeysClassName) .returns(externalMemberRegistryClassName)
.addStatement("return \$T.INSTANCE", emptyMemberRegistryClassName) .beginControlFlow("if (!\$T.isStdLibModule(moduleKey))", moduleKeysClassName)
.endControlFlow() .addStatement("return \$T.INSTANCE", emptyMemberRegistryClassName)
.beginControlFlow("switch (moduleKey.getUri().getSchemeSpecificPart())") .endControlFlow()
.beginControlFlow("switch (moduleKey.getUri().getSchemeSpecificPart())")
for (pkg in packages) { for (pkg in packages) {
val pklModuleName = getAnnotatedPklName(pkg) ?: pkg.simpleName.toString() val pklModuleName = getAnnotatedPklName(pkg) ?: pkg.simpleName.toString()
val pklModuleNameCapitalized = pklModuleName.capitalize() val pklModuleNameCapitalized = pklModuleName.capitalize()
val registryClassName = ClassName.get(registryPackageName, "${pklModuleNameCapitalized}MemberRegistry") val registryClassName =
ClassName.get(registryPackageName, "${pklModuleNameCapitalized}MemberRegistry")
// declare dependency on package-info.java (for `@PklName`) // declare dependency on package-info.java (for `@PklName`)
registryFactoryClass.addOriginatingElement(pkg) registryFactoryClass.addOriginatingElement(pkg)
@@ -148,8 +177,7 @@ class MemberRegistryGenerator : AbstractProcessor() {
private fun getAnnotatedPklName(element: Element): String? { private fun getAnnotatedPklName(element: Element): String? {
for (annotation in element.annotationMirrors) { for (annotation in element.annotationMirrors) {
when (annotation.annotationType.asElement().simpleName.toString()) { when (annotation.annotationType.asElement().simpleName.toString()) {
"PklName" -> "PklName" -> return annotation.elementValues.values.iterator().next().value.toString()
return annotation.elementValues.values.iterator().next().value.toString()
"GeneratedBy" -> { "GeneratedBy" -> {
val annotationValue = annotation.elementValues.values.first().value as TypeMirror val annotationValue = annotation.elementValues.values.first().value as TypeMirror
return getAnnotatedPklName(processingEnv.typeUtils.asElement(annotationValue)) return getAnnotatedPklName(processingEnv.typeUtils.asElement(annotationValue))

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@@ -8,9 +23,10 @@ class ClassInheritanceTest {
@Test @Test
fun `property override without type annotation is considered an object property definition`() { fun `property override without type annotation is considered an object property definition`() {
val module = evaluator.evaluateSchema( val module =
ModuleSource.text( evaluator.evaluateSchema(
""" ModuleSource.text(
"""
class Thing class Thing
open class Base { open class Base {
hidden thing: Thing hidden thing: Thing
@@ -18,9 +34,10 @@ class ClassInheritanceTest {
class Derived extends Base { class Derived extends Base {
thing {} thing {}
} }
""".trimIndent() """
.trimIndent()
)
) )
)
val derivedClass = module.classes["Derived"]!! val derivedClass = module.classes["Derived"]!!
assertThat(derivedClass.properties["thing"]).isNull() assertThat(derivedClass.properties["thing"]).isNull()
@@ -33,9 +50,10 @@ class ClassInheritanceTest {
@Test @Test
fun `property override with type annotation is considered a class property definition`() { fun `property override with type annotation is considered a class property definition`() {
val module = evaluator.evaluateSchema( val module =
ModuleSource.text( evaluator.evaluateSchema(
""" ModuleSource.text(
"""
class Thing class Thing
open class Base { open class Base {
hidden thing: Thing hidden thing: Thing
@@ -43,9 +61,10 @@ class ClassInheritanceTest {
class Derived extends Base { class Derived extends Base {
thing: Thing = new {} thing: Thing = new {}
} }
""".trimIndent() """
.trimIndent()
)
) )
)
val derivedClass = module.classes["Derived"]!! val derivedClass = module.classes["Derived"]!!
val thingProperty = derivedClass.properties["thing"] val thingProperty = derivedClass.properties["thing"]

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import kotlin.math.nextDown import kotlin.math.nextDown
@@ -68,10 +83,9 @@ class DurationTest {
assertThat(duration1.convertTo(MILLIS)).isEqualTo(duration2) assertThat(duration1.convertTo(MILLIS)).isEqualTo(duration2)
assertThat(duration2.convertTo(SECONDS)).isEqualTo(duration1) assertThat(duration2.convertTo(SECONDS)).isEqualTo(duration1)
assertThat(duration4.convertTo(NANOS)) assertThat(duration4.convertTo(NANOS)).isEqualTo(Duration(0.0, NANOS))
.isEqualTo(Duration(0.0, NANOS))
} }
@Test @Test
fun toIsoString() { fun toIsoString() {
assertThat(duration1.toIsoString()).isEqualTo("PT0.3S") assertThat(duration1.toIsoString()).isEqualTo("PT0.3S")
@@ -79,8 +93,10 @@ class DurationTest {
assertThat(duration3.toIsoString()).isEqualTo("PT0.3001S") assertThat(duration3.toIsoString()).isEqualTo("PT0.3001S")
assertThat(duration4.toIsoString()).isEqualTo("PT0S") assertThat(duration4.toIsoString()).isEqualTo("PT0S")
assertThat(Duration(1.0, NANOS).toIsoString()).isEqualTo("PT0.000000001S") assertThat(Duration(1.0, NANOS).toIsoString()).isEqualTo("PT0.000000001S")
// Although ISO8601 allows for durations (P) denoted in days, months and years, it is not recommended. // Although ISO8601 allows for durations (P) denoted in days, months and years, it is not
// The day notation can express an hour more or less, depending on whether it crosses a daylight savings transition, // recommended.
// The day notation can express an hour more or less, depending on whether it crosses a daylight
// savings transition,
// when added to "now" (at the time of evaluation). // when added to "now" (at the time of evaluation).
assertThat(Duration(100.0, DAYS).toIsoString()).isEqualTo("PT2400H") assertThat(Duration(100.0, DAYS).toIsoString()).isEqualTo("PT2400H")
} }
@@ -96,165 +112,132 @@ class DurationTest {
@Test @Test
fun `toJavaDuration() - positive`() { fun `toJavaDuration() - positive`() {
assertThat(Duration(999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(999)) assertThat(Duration(999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(999))
assertThat(Duration(999999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(999999)) assertThat(Duration(999999.0, NANOS).toJavaDuration())
assertThat(Duration(999999999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(999999999)) .isEqualTo(java.time.Duration.ofNanos(999999))
assertThat(Duration(999999999999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(999999999999)) assertThat(Duration(999999999.0, NANOS).toJavaDuration())
assertThat( .isEqualTo(java.time.Duration.ofNanos(999999999))
Duration( assertThat(Duration(999999999999.0, NANOS).toJavaDuration())
999999999999999.0, .isEqualTo(java.time.Duration.ofNanos(999999999999))
NANOS assertThat(Duration(999999999999999.0, NANOS).toJavaDuration())
).toJavaDuration() .isEqualTo(java.time.Duration.ofNanos(999999999999999))
).isEqualTo(java.time.Duration.ofNanos(999999999999999)) assertThat(Duration(9999999999999999.0, NANOS).toJavaDuration())
assertThat(Duration(9999999999999999.0, NANOS).toJavaDuration()).isNotEqualTo( .isNotEqualTo(java.time.Duration.ofNanos(9999999999999999))
java.time.Duration.ofNanos(
9999999999999999
)
)
assertThat(Duration(999.0, SECONDS).toJavaDuration()).isEqualTo(java.time.Duration.ofSeconds(999)) assertThat(Duration(999.0, SECONDS).toJavaDuration())
assertThat(Duration(999999.0, SECONDS).toJavaDuration()).isEqualTo(java.time.Duration.ofSeconds(999999)) .isEqualTo(java.time.Duration.ofSeconds(999))
assertThat(Duration(999999999.0, SECONDS).toJavaDuration()).isEqualTo(java.time.Duration.ofSeconds(999999999)) assertThat(Duration(999999.0, SECONDS).toJavaDuration())
assertThat(Duration(999999999999.0, SECONDS).toJavaDuration()).isEqualTo(java.time.Duration.ofSeconds(999999999999)) .isEqualTo(java.time.Duration.ofSeconds(999999))
assertThat(Duration(999999999999999.0, SECONDS).toJavaDuration()).isEqualTo( assertThat(Duration(999999999.0, SECONDS).toJavaDuration())
java.time.Duration.ofSeconds( .isEqualTo(java.time.Duration.ofSeconds(999999999))
999999999999999 assertThat(Duration(999999999999.0, SECONDS).toJavaDuration())
) .isEqualTo(java.time.Duration.ofSeconds(999999999999))
) assertThat(Duration(999999999999999.0, SECONDS).toJavaDuration())
assertThat(Duration(9999999999999999.0, SECONDS).toJavaDuration()).isNotEqualTo( .isEqualTo(java.time.Duration.ofSeconds(999999999999999))
java.time.Duration.ofSeconds( assertThat(Duration(9999999999999999.0, SECONDS).toJavaDuration())
9999999999999999 .isNotEqualTo(java.time.Duration.ofSeconds(9999999999999999))
)
)
assertThat(Duration(999.0, MINUTES).toJavaDuration()).isEqualTo(java.time.Duration.ofMinutes(999)) assertThat(Duration(999.0, MINUTES).toJavaDuration())
assertThat(Duration(999999.0, MINUTES).toJavaDuration()).isEqualTo(java.time.Duration.ofMinutes(999999)) .isEqualTo(java.time.Duration.ofMinutes(999))
assertThat(Duration(999999999.0, MINUTES).toJavaDuration()).isEqualTo(java.time.Duration.ofMinutes(999999999)) assertThat(Duration(999999.0, MINUTES).toJavaDuration())
assertThat(Duration(999999999999.0, MINUTES).toJavaDuration()).isEqualTo(java.time.Duration.ofMinutes(999999999999)) .isEqualTo(java.time.Duration.ofMinutes(999999))
assertThat(Duration(999999999999999.0, MINUTES).toJavaDuration()).isEqualTo( assertThat(Duration(999999999.0, MINUTES).toJavaDuration())
java.time.Duration.ofMinutes( .isEqualTo(java.time.Duration.ofMinutes(999999999))
999999999999999 assertThat(Duration(999999999999.0, MINUTES).toJavaDuration())
) .isEqualTo(java.time.Duration.ofMinutes(999999999999))
) assertThat(Duration(999999999999999.0, MINUTES).toJavaDuration())
assertThat(Duration(9999999999999999.0, MINUTES).toJavaDuration()).isNotEqualTo( .isEqualTo(java.time.Duration.ofMinutes(999999999999999))
java.time.Duration.ofMinutes( assertThat(Duration(9999999999999999.0, MINUTES).toJavaDuration())
9999999999999999 .isNotEqualTo(java.time.Duration.ofMinutes(9999999999999999))
)
)
assertThat(Duration(999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(999)) assertThat(Duration(999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(999))
assertThat(Duration(999999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(999999)) assertThat(Duration(999999.0, HOURS).toJavaDuration())
assertThat(Duration(999999999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(999999999)) .isEqualTo(java.time.Duration.ofHours(999999))
assertThat(Duration(999999999999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(999999999999)) assertThat(Duration(999999999.0, HOURS).toJavaDuration())
assertThat( .isEqualTo(java.time.Duration.ofHours(999999999))
Duration( assertThat(Duration(999999999999.0, HOURS).toJavaDuration())
999999999999999.0, .isEqualTo(java.time.Duration.ofHours(999999999999))
HOURS assertThat(Duration(999999999999999.0, HOURS).toJavaDuration())
).toJavaDuration() .isEqualTo(java.time.Duration.ofHours(999999999999999))
).isEqualTo(java.time.Duration.ofHours(999999999999999))
assertThrows<ArithmeticException> { Duration(9999999999999999.0, HOURS).toJavaDuration() } assertThrows<ArithmeticException> { Duration(9999999999999999.0, HOURS).toJavaDuration() }
assertThat(Duration(999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(999)) assertThat(Duration(999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(999))
assertThat(Duration(999999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(999999)) assertThat(Duration(999999.0, DAYS).toJavaDuration())
assertThat(Duration(999999999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(999999999)) .isEqualTo(java.time.Duration.ofDays(999999))
assertThat(Duration(999999999999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(999999999999)) assertThat(Duration(999999999.0, DAYS).toJavaDuration())
.isEqualTo(java.time.Duration.ofDays(999999999))
assertThat(Duration(999999999999.0, DAYS).toJavaDuration())
.isEqualTo(java.time.Duration.ofDays(999999999999))
assertThrows<ArithmeticException> { Duration(999999999999999.0, DAYS).toJavaDuration() } assertThrows<ArithmeticException> { Duration(999999999999999.0, DAYS).toJavaDuration() }
} }
@Test @Test
fun `toJavaDuration() - negative`() { fun `toJavaDuration() - negative`() {
assertThat(Duration(-999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(-999)) assertThat(Duration(-999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(-999))
assertThat(Duration(-999999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(-999999)) assertThat(Duration(-999999.0, NANOS).toJavaDuration())
assertThat(Duration(-999999999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(-999999999)) .isEqualTo(java.time.Duration.ofNanos(-999999))
assertThat(Duration(-999999999999.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(-999999999999)) assertThat(Duration(-999999999.0, NANOS).toJavaDuration())
assertThat( .isEqualTo(java.time.Duration.ofNanos(-999999999))
Duration( assertThat(Duration(-999999999999.0, NANOS).toJavaDuration())
-999999999999999.0, .isEqualTo(java.time.Duration.ofNanos(-999999999999))
NANOS assertThat(Duration(-999999999999999.0, NANOS).toJavaDuration())
).toJavaDuration() .isEqualTo(java.time.Duration.ofNanos(-999999999999999))
).isEqualTo(java.time.Duration.ofNanos(-999999999999999)) assertThat(Duration(-9999999999999999.0, NANOS).toJavaDuration())
assertThat( .isNotEqualTo(java.time.Duration.ofNanos(-9999999999999999))
Duration(
-9999999999999999.0,
NANOS
).toJavaDuration()
).isNotEqualTo(java.time.Duration.ofNanos(-9999999999999999))
assertThat(Duration(-999.0, SECONDS).toJavaDuration()).isEqualTo(java.time.Duration.ofSeconds(-999)) assertThat(Duration(-999.0, SECONDS).toJavaDuration())
assertThat(Duration(-999999.0, SECONDS).toJavaDuration()).isEqualTo(java.time.Duration.ofSeconds(-999999)) .isEqualTo(java.time.Duration.ofSeconds(-999))
assertThat(Duration(-999999999.0, SECONDS).toJavaDuration()).isEqualTo(java.time.Duration.ofSeconds(-999999999)) assertThat(Duration(-999999.0, SECONDS).toJavaDuration())
assertThat( .isEqualTo(java.time.Duration.ofSeconds(-999999))
Duration( assertThat(Duration(-999999999.0, SECONDS).toJavaDuration())
-999999999999.0, .isEqualTo(java.time.Duration.ofSeconds(-999999999))
SECONDS assertThat(Duration(-999999999999.0, SECONDS).toJavaDuration())
).toJavaDuration() .isEqualTo(java.time.Duration.ofSeconds(-999999999999))
).isEqualTo(java.time.Duration.ofSeconds(-999999999999)) assertThat(Duration(-999999999999999.0, SECONDS).toJavaDuration())
assertThat( .isEqualTo(java.time.Duration.ofSeconds(-999999999999999))
Duration( assertThat(Duration(-9999999999999999.0, SECONDS).toJavaDuration())
-999999999999999.0, .isNotEqualTo(java.time.Duration.ofSeconds(-9999999999999999))
SECONDS
).toJavaDuration()
).isEqualTo(java.time.Duration.ofSeconds(-999999999999999))
assertThat(
Duration(
-9999999999999999.0,
SECONDS
).toJavaDuration()
).isNotEqualTo(java.time.Duration.ofSeconds(-9999999999999999))
assertThat(Duration(-999.0, MINUTES).toJavaDuration()).isEqualTo(java.time.Duration.ofMinutes(-999)) assertThat(Duration(-999.0, MINUTES).toJavaDuration())
assertThat(Duration(-999999.0, MINUTES).toJavaDuration()).isEqualTo(java.time.Duration.ofMinutes(-999999)) .isEqualTo(java.time.Duration.ofMinutes(-999))
assertThat(Duration(-999999999.0, MINUTES).toJavaDuration()).isEqualTo(java.time.Duration.ofMinutes(-999999999)) assertThat(Duration(-999999.0, MINUTES).toJavaDuration())
assertThat( .isEqualTo(java.time.Duration.ofMinutes(-999999))
Duration( assertThat(Duration(-999999999.0, MINUTES).toJavaDuration())
-999999999999.0, .isEqualTo(java.time.Duration.ofMinutes(-999999999))
MINUTES assertThat(Duration(-999999999999.0, MINUTES).toJavaDuration())
).toJavaDuration() .isEqualTo(java.time.Duration.ofMinutes(-999999999999))
).isEqualTo(java.time.Duration.ofMinutes(-999999999999)) assertThat(Duration(-999999999999999.0, MINUTES).toJavaDuration())
assertThat( .isEqualTo(java.time.Duration.ofMinutes(-999999999999999))
Duration( assertThat(Duration(-9999999999999999.0, MINUTES).toJavaDuration())
-999999999999999.0, .isNotEqualTo(java.time.Duration.ofMinutes(-9999999999999999))
MINUTES
).toJavaDuration()
).isEqualTo(java.time.Duration.ofMinutes(-999999999999999))
assertThat(
Duration(
-9999999999999999.0,
MINUTES
).toJavaDuration()
).isNotEqualTo(java.time.Duration.ofMinutes(-9999999999999999))
assertThat(Duration(-999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(-999)) assertThat(Duration(-999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(-999))
assertThat(Duration(-999999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(-999999)) assertThat(Duration(-999999.0, HOURS).toJavaDuration())
assertThat(Duration(-999999999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(-999999999)) .isEqualTo(java.time.Duration.ofHours(-999999))
assertThat(Duration(-999999999999.0, HOURS).toJavaDuration()).isEqualTo(java.time.Duration.ofHours(-999999999999)) assertThat(Duration(-999999999.0, HOURS).toJavaDuration())
assertThat( .isEqualTo(java.time.Duration.ofHours(-999999999))
Duration( assertThat(Duration(-999999999999.0, HOURS).toJavaDuration())
-999999999999999.0, .isEqualTo(java.time.Duration.ofHours(-999999999999))
HOURS assertThat(Duration(-999999999999999.0, HOURS).toJavaDuration())
).toJavaDuration() .isEqualTo(java.time.Duration.ofHours(-999999999999999))
).isEqualTo(java.time.Duration.ofHours(-999999999999999))
assertThrows<ArithmeticException> { Duration(-9999999999999999.0, HOURS).toJavaDuration() } assertThrows<ArithmeticException> { Duration(-9999999999999999.0, HOURS).toJavaDuration() }
assertThat(Duration(-999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(-999)) assertThat(Duration(-999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(-999))
assertThat(Duration(-999999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(-999999)) assertThat(Duration(-999999.0, DAYS).toJavaDuration())
assertThat(Duration(-999999999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(-999999999)) .isEqualTo(java.time.Duration.ofDays(-999999))
assertThat(Duration(-999999999999.0, DAYS).toJavaDuration()).isEqualTo(java.time.Duration.ofDays(-999999999999)) assertThat(Duration(-999999999.0, DAYS).toJavaDuration())
.isEqualTo(java.time.Duration.ofDays(-999999999))
assertThat(Duration(-999999999999.0, DAYS).toJavaDuration())
.isEqualTo(java.time.Duration.ofDays(-999999999999))
assertThrows<ArithmeticException> { Duration(-999999999999999.0, DAYS).toJavaDuration() } assertThrows<ArithmeticException> { Duration(-999999999999999.0, DAYS).toJavaDuration() }
} }
@Test @Test
fun `toJavaDuration() - edge cases`() { fun `toJavaDuration() - edge cases`() {
assertThat(Duration(0.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(0)) assertThat(Duration(0.0, NANOS).toJavaDuration()).isEqualTo(java.time.Duration.ofNanos(0))
assertThat(Duration(Long.MAX_VALUE.toDouble(), SECONDS).toJavaDuration()).isEqualTo( assertThat(Duration(Long.MAX_VALUE.toDouble(), SECONDS).toJavaDuration())
java.time.Duration.ofSeconds( .isEqualTo(java.time.Duration.ofSeconds(Long.MAX_VALUE))
Long.MAX_VALUE assertThat(Duration(Long.MIN_VALUE.toDouble(), SECONDS).toJavaDuration())
) .isEqualTo(java.time.Duration.ofSeconds(Long.MIN_VALUE))
)
assertThat(Duration(Long.MIN_VALUE.toDouble(), SECONDS).toJavaDuration()).isEqualTo(
java.time.Duration.ofSeconds(
Long.MIN_VALUE
)
)
val justTooLarge = Duration(Long.MAX_VALUE.toDouble().nextUp(), SECONDS) val justTooLarge = Duration(Long.MAX_VALUE.toDouble().nextUp(), SECONDS)
assertThrows<ArithmeticException> { justTooLarge.toJavaDuration() } assertThrows<ArithmeticException> { justTooLarge.toJavaDuration() }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@@ -5,19 +20,21 @@ import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
class DynamicTest { class DynamicTest {
private fun brokenInput(value: String): String = """ private fun brokenInput(value: String): String =
"""
class Person { name: String; age: Int } class Person { name: String; age: Int }
person: Person = new { name = 42; age = "Pigeon" } // oops person: Person = new { name = 42; age = "Pigeon" } // oops
output { value = $value } output { value = $value }
""".trimIndent() """
.trimIndent()
@Test @Test
fun `property access respects type`() { fun `property access respects type`() {
// Does not involve Dynamic, but is a baseline for the other cases. // Does not involve Dynamic, but is a baseline for the other cases.
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
assertThrows<PklException> { assertThrows<PklException> {
evaluator.evaluateOutputText(ModuleSource.text(brokenInput("person.name"))) evaluator.evaluateOutputText(ModuleSource.text(brokenInput("person.name")))
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@@ -18,33 +33,41 @@ class ErrorColoringTest {
private fun evaluate(program: String, expression: String): Any { private fun evaluate(program: String, expression: String): Any {
return evaluator.evaluateExpression(ModuleSource.text(program), expression) return evaluator.evaluateExpression(ModuleSource.text(program), expression)
} }
@BeforeEach @BeforeEach
fun setup() { fun setup() {
// Enable colouring before each test // Enable colouring before each test
Ansi.setEnabled(true) Ansi.setEnabled(true)
} }
@AfterEach @AfterEach
fun teardown() { fun teardown() {
// Disable colouring after each test // Disable colouring after each test
Ansi.setEnabled(false) Ansi.setEnabled(false)
} }
@Test @Test
fun `simple error`() { fun `simple error`() {
val error = assertThrows<PklException> { evaluate("bar = 2", "bar = 15") } val error = assertThrows<PklException> { evaluate("bar = 2", "bar = 15") }
assertThat(error).message() assertThat(error)
.message()
.contains("\u001B[31m Pkl Error \u001B[m") .contains("\u001B[31m Pkl Error \u001B[m")
.contains("\u001B[94m1 | \u001B[m") .contains("\u001B[94m1 | \u001B[m")
.contains("\u001B[0;31m^") .contains("\u001B[0;31m^")
} }
@Test @Test
fun `repeated error`() { fun `repeated error`() {
val error = assertThrows<PklException> { evaluate("""self: String = "Strings; if they were lazy, you could tie the knot on \(self.take(7))"""", "self") } val error =
assertThat(error).message() assertThrows<PklException> {
evaluate(
"""self: String = "Strings; if they were lazy, you could tie the knot on \(self.take(7))"""",
"self"
)
}
assertThat(error)
.message()
.contains("A stack overflow occurred.") .contains("A stack overflow occurred.")
.contains("┌─ ") .contains("┌─ ")
.contains(" repetitions of:") .contains(" repetitions of:")

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@@ -22,13 +37,15 @@ class EvaluateExpressionTest {
@Test @Test
fun `evaluate expression`() { fun `evaluate expression`() {
val program = """ val program =
"""
res1 = 1 res1 = 1
res2 { res2 {
res3 = 3 res3 = 3
res4 = 4 res4 = 4
} }
""".trimIndent() """
.trimIndent()
assertThat(evaluate(program, "res1")).isEqualTo(1L) assertThat(evaluate(program, "res1")).isEqualTo(1L)
val res2 = evaluate(program, "res2") val res2 = evaluate(program, "res2")
assertThat(res2).isInstanceOf(PObject::class.java) assertThat(res2).isInstanceOf(PObject::class.java)
@@ -39,18 +56,25 @@ class EvaluateExpressionTest {
@Test @Test
fun `evaluate subpath`() { fun `evaluate subpath`() {
val resp = evaluate(""" val resp =
evaluate(
"""
foo { foo {
bar = 2 bar = 2
} }
""".trimIndent(), "foo.bar") """
.trimIndent(),
"foo.bar"
)
assertThat(resp).isEqualTo(2L) assertThat(resp).isEqualTo(2L)
} }
@Test @Test
fun `evaluate output text`() { fun `evaluate output text`() {
val result = evaluate(""" val result =
evaluate(
"""
foo { foo {
bar = 2 bar = 2
} }
@@ -58,13 +82,20 @@ class EvaluateExpressionTest {
output { output {
renderer = new YamlRenderer {} renderer = new YamlRenderer {}
} }
""".trimIndent(), "output.text") """
.trimIndent(),
"output.text"
)
assertThat(result).isEqualTo(""" assertThat(result)
.isEqualTo(
"""
foo: foo:
bar: 2 bar: 2
""".trimIndent()) """
.trimIndent()
)
} }
@Test @Test
@@ -76,10 +107,7 @@ class EvaluateExpressionTest {
@Test @Test
fun `evaluate import expression`() { fun `evaluate import expression`() {
val result = evaluate( val result = evaluate("", """import("pkl:release").current.documentation.homepage""")
"",
"""import("pkl:release").current.documentation.homepage"""
)
assertThat(result as String).startsWith("https://pkl-lang.org/") assertThat(result as String).startsWith("https://pkl-lang.org/")
} }

View File

@@ -1,9 +1,24 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.pkl.core.ModuleSource.text
import org.assertj.core.api.Assertions.* import org.assertj.core.api.Assertions.*
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.pkl.core.ModuleSource.text
class EvaluateMultipleFileOutputTest { class EvaluateMultipleFileOutputTest {
@@ -11,7 +26,8 @@ class EvaluateMultipleFileOutputTest {
@Test @Test
fun `output files`() { fun `output files`() {
val program = """ val program =
"""
output { output {
files { files {
["foo.yml"] { ["foo.yml"] {
@@ -28,14 +44,10 @@ class EvaluateMultipleFileOutputTest {
} }
} }
} }
""".trimIndent() """
.trimIndent()
val output = evaluator.evaluateOutputFiles(text(program)) val output = evaluator.evaluateOutputFiles(text(program))
assertThat(output.keys).isEqualTo(setOf( assertThat(output.keys).isEqualTo(setOf("foo.yml", "bar.yml", "bar/biz.yml", "bar/../bark.yml"))
"foo.yml",
"bar.yml",
"bar/biz.yml",
"bar/../bark.yml"
))
assertThat(output["foo.yml"]?.text).isEqualTo("foo: foo text") assertThat(output["foo.yml"]?.text).isEqualTo("foo: foo text")
assertThat(output["bar.yml"]?.text).isEqualTo("bar: bar text") assertThat(output["bar.yml"]?.text).isEqualTo("bar: bar text")
assertThat(output["bar/biz.yml"]?.text).isEqualTo("biz: bar biz") assertThat(output["bar/biz.yml"]?.text).isEqualTo("biz: bar biz")
@@ -45,7 +57,8 @@ class EvaluateMultipleFileOutputTest {
@Test @Test
fun `using a renderer`() { fun `using a renderer`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val program = """ val program =
"""
output { output {
files { files {
["foo.json"] { ["foo.json"] {
@@ -57,21 +70,27 @@ class EvaluateMultipleFileOutputTest {
} }
} }
} }
""".trimIndent() """
.trimIndent()
val output = evaluator.evaluateOutputFiles(text(program)) val output = evaluator.evaluateOutputFiles(text(program))
assertThat(output["foo.json"]?.text).isEqualTo(""" assertThat(output["foo.json"]?.text)
.isEqualTo(
"""
{ {
"foo": "fooey", "foo": "fooey",
"bar": "barrey" "bar": "barrey"
} }
""".trimIndent()) """
.trimIndent()
)
} }
@Test @Test
fun `reading files after the evaluator is closed`() { fun `reading files after the evaluator is closed`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val program = """ val program =
"""
output { output {
files { files {
["foo.json"] { ["foo.json"] {
@@ -83,7 +102,8 @@ class EvaluateMultipleFileOutputTest {
} }
} }
} }
""".trimIndent() """
.trimIndent()
val output = evaluator.evaluateOutputFiles(text(program)) val output = evaluator.evaluateOutputFiles(text(program))
evaluator.close() evaluator.close()
assertThrows<PklException> { output["foo.json"]!!.text } assertThrows<PklException> { output["foo.json"]!!.text }

View File

@@ -1,8 +1,23 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.pkl.core.util.IoUtils
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.core.util.IoUtils
// tests language-internal renderers // tests language-internal renderers
// uses same input/output files as Pcf/Json/Yaml/PListRendererTest // uses same input/output files as Pcf/Json/Yaml/PListRendererTest
@@ -28,13 +43,10 @@ class EvaluateOutputTextTest {
} }
private fun checkRenderedOutput(format: OutputFormat) { private fun checkRenderedOutput(format: OutputFormat) {
val evaluator = EvaluatorBuilder.preconfigured() val evaluator = EvaluatorBuilder.preconfigured().setOutputFormat(format).build()
.setOutputFormat(format)
.build()
val output = evaluator.evaluateOutputText( val output =
ModuleSource.modulePath("org/pkl/core/rendererTest.pkl") evaluator.evaluateOutputText(ModuleSource.modulePath("org/pkl/core/rendererTest.pkl"))
)
val expected = IoUtils.readClassPathResourceAsString(javaClass, "rendererTest.$format") val expected = IoUtils.readClassPathResourceAsString(javaClass, "rendererTest.$format")
assertThat(output.trim()).isEqualTo(expected.trim()) assertThat(output.trim()).isEqualTo(expected.trim())

View File

@@ -1,10 +1,25 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.pkl.core.ModuleSource.*
import java.net.URI import java.net.URI
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.core.ModuleSource.*
import org.pkl.core.runtime.BaseModule import org.pkl.core.runtime.BaseModule
class EvaluateSchemaTest { class EvaluateSchemaTest {
@@ -17,9 +32,7 @@ class EvaluateSchemaTest {
@Test @Test
fun `evaluate test schema`() { fun `evaluate test schema`() {
val module = evaluator.evaluateSchema( val module = evaluator.evaluateSchema(modulePath("org/pkl/core/EvaluateSchemaTest.pkl"))
modulePath("org/pkl/core/EvaluateSchemaTest.pkl")
)
checkModuleMetadata(module) checkModuleMetadata(module)
@@ -35,28 +48,27 @@ class EvaluateSchemaTest {
@Test @Test
fun `evaluate pkl_base schema`() { fun `evaluate pkl_base schema`() {
val module = evaluator.evaluateSchema(uri(URI("pkl:base"))) val module = evaluator.evaluateSchema(uri(URI("pkl:base")))
assertThat(module.moduleClass.superclass) assertThat(module.moduleClass.superclass).isEqualTo(BaseModule.getModuleClass().export())
.isEqualTo(BaseModule.getModuleClass().export())
} }
@Test @Test
fun `does not export local classes`() { fun `does not export local classes`() {
val module = evaluator.evaluateSchema( val module =
text( evaluator.evaluateSchema(
""" text(
"""
class Foo {} class Foo {}
local class Baz {} local class Baz {}
""".trimIndent() """
.trimIndent()
)
) )
)
assertThat(module.classes.keys) assertThat(module.classes.keys).containsExactly("Foo")
.containsExactly("Foo")
} }
private fun checkModuleMetadata(module: ModuleSchema) { private fun checkModuleMetadata(module: ModuleSchema) {
assertThat(module.moduleUri) assertThat(module.moduleUri).isEqualTo(URI("modulepath:/org/pkl/core/EvaluateSchemaTest.pkl"))
.isEqualTo(URI("modulepath:/org/pkl/core/EvaluateSchemaTest.pkl"))
assertThat(module.moduleName).isEqualTo("test") assertThat(module.moduleName).isEqualTo("test")
@@ -79,9 +91,8 @@ class EvaluateSchemaTest {
val paramType = propertyb2.type val paramType = propertyb2.type
assertThat(paramType).isInstanceOf(PType.Class::class.java) assertThat(paramType).isInstanceOf(PType.Class::class.java)
paramType as PType.Class paramType as PType.Class
assertThat(paramType.pClass) assertThat(paramType.pClass).isEqualTo(BaseModule.getIntClass().export())
.isEqualTo(BaseModule.getIntClass().export())
val propertyb3 = properties.getValue("propertyb3") val propertyb3 = properties.getValue("propertyb3")
assertThat(propertyb3.sourceLocation.startLine).isEqualTo(24) assertThat(propertyb3.sourceLocation.startLine).isEqualTo(24)
assertThat(propertyb3.sourceLocation.endLine).isEqualTo(24) assertThat(propertyb3.sourceLocation.endLine).isEqualTo(24)
@@ -106,10 +117,8 @@ class EvaluateSchemaTest {
val paramBaseType = paramType.baseType val paramBaseType = paramType.baseType
assertThat(paramBaseType).isInstanceOf(PType.Class::class.java) assertThat(paramBaseType).isInstanceOf(PType.Class::class.java)
paramBaseType as PType.Class paramBaseType as PType.Class
assertThat(paramBaseType.pClass) assertThat(paramBaseType.pClass).isEqualTo(BaseModule.getStringClass().export())
.isEqualTo(BaseModule.getStringClass().export()) assertThat(paramType.constraints).isEqualTo(listOf("!isEmpty", "startsWith(\"a\")"))
assertThat(paramType.constraints)
.isEqualTo(listOf("!isEmpty", "startsWith(\"a\")"))
val returnType = methodb2.returnType val returnType = methodb2.returnType
assertThat(returnType).isInstanceOf(PType.Constrained::class.java) assertThat(returnType).isInstanceOf(PType.Constrained::class.java)
@@ -119,7 +128,7 @@ class EvaluateSchemaTest {
returnBaseType as PType.Class returnBaseType as PType.Class
assertThat(returnBaseType.pClass).isEqualTo(BaseModule.getIntClass().export()) assertThat(returnBaseType.pClass).isEqualTo(BaseModule.getIntClass().export())
assertThat(returnType.constraints).isEqualTo(listOf("isPositive")) assertThat(returnType.constraints).isEqualTo(listOf("isPositive"))
val methodb3 = methods.getValue("methodb3") val methodb3 = methods.getValue("methodb3")
assertThat(methodb3.sourceLocation.startLine).isEqualTo(26) assertThat(methodb3.sourceLocation.startLine).isEqualTo(26)
assertThat(methodb3.sourceLocation.endLine).isEqualTo(26) assertThat(methodb3.sourceLocation.endLine).isEqualTo(26)
@@ -141,10 +150,8 @@ class EvaluateSchemaTest {
assertThat(supermodule).isNotNull assertThat(supermodule).isNotNull
assertThat(supermodule!!.supermodule).isNull() assertThat(supermodule!!.supermodule).isNull()
assertThat(module.moduleClass.superclass) assertThat(module.moduleClass.superclass).isEqualTo(supermodule.moduleClass)
.isEqualTo(supermodule.moduleClass) assertThat(supermodule.moduleClass.superclass).isEqualTo(BaseModule.getModuleClass().export())
assertThat(supermodule.moduleClass.superclass)
.isEqualTo(BaseModule.getModuleClass().export())
assertThat(supermodule.moduleUri) assertThat(supermodule.moduleUri)
.isEqualTo(URI("modulepath:/org/pkl/core/EvaluateSchemaTestBaseModule.pkl")) .isEqualTo(URI("modulepath:/org/pkl/core/EvaluateSchemaTestBaseModule.pkl"))

View File

@@ -1,15 +1,30 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.pkl.commons.createTempFile import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.createFile
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.createTempFile
import org.pkl.commons.writeString import org.pkl.commons.writeString
import org.pkl.core.ModuleSource.* import org.pkl.core.ModuleSource.*
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.createFile
class EvaluateTestsTest { class EvaluateTestsTest {
@@ -17,7 +32,10 @@ class EvaluateTestsTest {
@Test @Test
fun `test successful module`() { fun `test successful module`() {
val results = evaluator.evaluateTest(text(""" val results =
evaluator.evaluateTest(
text(
"""
amends "pkl:test" amends "pkl:test"
facts { facts {
@@ -26,7 +44,11 @@ class EvaluateTestsTest {
"foo" == "foo" "foo" == "foo"
} }
} }
""".trimIndent()), true) """
.trimIndent()
),
true
)
assertThat(results.moduleName).isEqualTo("text") assertThat(results.moduleName).isEqualTo("text")
assertThat(results.displayUri).isEqualTo("repl:text") assertThat(results.displayUri).isEqualTo("repl:text")
@@ -38,9 +60,10 @@ class EvaluateTestsTest {
@Test @Test
fun `test module failure`() { fun `test module failure`() {
val results = evaluator.evaluateTest( val results =
text( evaluator.evaluateTest(
""" text(
"""
amends "pkl:test" amends "pkl:test"
facts { facts {
@@ -49,19 +72,20 @@ class EvaluateTestsTest {
"foo" == "bar" "foo" == "bar"
} }
} }
""".trimIndent() """
), .trimIndent()
true ),
) true
)
assertThat(results.totalTests()).isEqualTo(1) assertThat(results.totalTests()).isEqualTo(1)
assertThat(results.totalFailures()).isEqualTo(2) assertThat(results.totalFailures()).isEqualTo(2)
assertThat(results.failed()).isTrue assertThat(results.failed()).isTrue
val res = results.results[0] val res = results.results[0]
assertThat(res.name).isEqualTo("should fail") assertThat(res.name).isEqualTo("should fail")
assertThat(res.errors).isEmpty() assertThat(res.errors).isEmpty()
val fail1 = res.failures[0] val fail1 = res.failures[0]
assertThat(fail1.rendered).isEqualTo("1 == 2 ❌ (repl:text)") assertThat(fail1.rendered).isEqualTo("1 == 2 ❌ (repl:text)")
@@ -71,9 +95,10 @@ class EvaluateTestsTest {
@Test @Test
fun `test module error`() { fun `test module error`() {
val results = evaluator.evaluateTest( val results =
text( evaluator.evaluateTest(
""" text(
"""
amends "pkl:test" amends "pkl:test"
facts { facts {
@@ -82,10 +107,11 @@ class EvaluateTestsTest {
throw("got an error") throw("got an error")
} }
} }
""".trimIndent() """
), .trimIndent()
true ),
) true
)
assertThat(results.totalTests()).isEqualTo(1) assertThat(results.totalTests()).isEqualTo(1)
assertThat(results.totalFailures()).isEqualTo(0) assertThat(results.totalFailures()).isEqualTo(0)
@@ -95,10 +121,12 @@ class EvaluateTestsTest {
assertThat(res.name).isEqualTo("text") assertThat(res.name).isEqualTo("text")
assertThat(res.failures).isEmpty() assertThat(res.failures).isEmpty()
assertThat(res.errors.size).isEqualTo(1) assertThat(res.errors.size).isEqualTo(1)
val error = res.errors[0] val error = res.errors[0]
assertThat(error.message).isEqualTo("got an error") assertThat(error.message).isEqualTo("got an error")
assertThat(error.exception.message).isEqualTo(""" assertThat(error.exception.message)
.isEqualTo(
"""
Pkl Error Pkl Error
got an error got an error
@@ -110,13 +138,17 @@ class EvaluateTestsTest {
^^^^^^^ ^^^^^^^
at text#facts (repl:text) at text#facts (repl:text)
""".trimIndent()) """
.trimIndent()
)
} }
@Test @Test
fun `test successful example`(@TempDir tempDir: Path) { fun `test successful example`(@TempDir tempDir: Path) {
val file = tempDir.createTempFile(prefix = "example", suffix = ".pkl") val file = tempDir.createTempFile(prefix = "example", suffix = ".pkl")
Files.writeString(file, """ Files.writeString(
file,
"""
amends "pkl:test" amends "pkl:test"
examples { examples {
@@ -127,9 +159,13 @@ class EvaluateTestsTest {
} }
} }
} }
""".trimIndent()) """
.trimIndent()
Files.writeString(createExpected(file), """ )
Files.writeString(
createExpected(file),
"""
examples { examples {
["user"] { ["user"] {
new { new {
@@ -138,7 +174,9 @@ class EvaluateTestsTest {
} }
} }
} }
""".trimIndent()) """
.trimIndent()
)
val results = evaluator.evaluateTest(path(file), false) val results = evaluator.evaluateTest(path(file), false)
assertThat(results.moduleName).startsWith("example") assertThat(results.moduleName).startsWith("example")
@@ -151,7 +189,9 @@ class EvaluateTestsTest {
@Test @Test
fun `test example failure`(@TempDir tempDir: Path) { fun `test example failure`(@TempDir tempDir: Path) {
val file = tempDir.createTempFile(prefix = "example", suffix = ".pkl") val file = tempDir.createTempFile(prefix = "example", suffix = ".pkl")
Files.writeString(file, """ Files.writeString(
file,
"""
amends "pkl:test" amends "pkl:test"
examples { examples {
@@ -162,9 +202,13 @@ class EvaluateTestsTest {
} }
} }
} }
""".trimIndent()) """
.trimIndent()
)
Files.writeString(createExpected(file), """ Files.writeString(
createExpected(file),
"""
examples { examples {
["user"] { ["user"] {
new { new {
@@ -173,7 +217,9 @@ class EvaluateTestsTest {
} }
} }
} }
""".trimIndent()) """
.trimIndent()
)
val results = evaluator.evaluateTest(path(file), false) val results = evaluator.evaluateTest(path(file), false)
assertThat(results.moduleName).startsWith("example") assertThat(results.moduleName).startsWith("example")
@@ -187,7 +233,9 @@ class EvaluateTestsTest {
assertThat(res.errors.isEmpty()).isTrue assertThat(res.errors.isEmpty()).isTrue
val fail1 = res.failures[0] val fail1 = res.failures[0]
assertThat(fail1.rendered.stripFileAndLines(tempDir)).isEqualTo(""" assertThat(fail1.rendered.stripFileAndLines(tempDir))
.isEqualTo(
"""
(/tempDir/example.pkl) (/tempDir/example.pkl)
Expected: (/tempDir/example.pkl-expected.pcf) Expected: (/tempDir/example.pkl-expected.pcf)
new { new {
@@ -199,13 +247,17 @@ class EvaluateTestsTest {
name = "Bob" name = "Bob"
age = 33 age = 33
} }
""".trimIndent()) """
.trimIndent()
)
} }
@Test @Test
fun `written examples use custom string delimiters`(@TempDir tempDir: Path) { fun `written examples use custom string delimiters`(@TempDir tempDir: Path) {
val file = tempDir.createTempFile(prefix = "example", suffix = ".pkl") val file = tempDir.createTempFile(prefix = "example", suffix = ".pkl")
Files.writeString(file, """ Files.writeString(
file,
"""
amends "pkl:test" amends "pkl:test"
examples { examples {
@@ -213,25 +265,33 @@ class EvaluateTestsTest {
"my \"string\"" "my \"string\""
} }
} }
""".trimIndent()) """
.trimIndent()
)
evaluator.evaluateTest(path(file), false) evaluator.evaluateTest(path(file), false)
val expectedFile = file.parent.resolve(file.fileName.toString() + "-expected.pcf") val expectedFile = file.parent.resolve(file.fileName.toString() + "-expected.pcf")
assertThat(expectedFile).exists() assertThat(expectedFile).exists()
assertThat(expectedFile).hasContent(""" assertThat(expectedFile)
.hasContent(
"""
examples { examples {
["myStr"] { ["myStr"] {
#"my "string""# #"my "string""#
} }
} }
""".trimIndent()) """
.trimIndent()
)
} }
// test for backwards compatibility // test for backwards compatibility
@Test @Test
fun `examples that don't use custom string delimiters still pass`(@TempDir tempDir: Path) { fun `examples that don't use custom string delimiters still pass`(@TempDir tempDir: Path) {
val file = tempDir.createTempFile(prefix = "example", suffix = ".pkl") val file = tempDir.createTempFile(prefix = "example", suffix = ".pkl")
Files.writeString(file, """ Files.writeString(
file,
"""
amends "pkl:test" amends "pkl:test"
examples { examples {
@@ -239,25 +299,28 @@ class EvaluateTestsTest {
"my \"string\"" "my \"string\""
} }
} }
""".trimIndent()) """
createExpected(file).writeString(""" .trimIndent()
)
createExpected(file)
.writeString(
"""
examples { examples {
["myStr"] { ["myStr"] {
"my \"string\"" "my \"string\""
} }
} }
""".trimIndent()) """
.trimIndent()
)
val result = evaluator.evaluateTest(path(file), false) val result = evaluator.evaluateTest(path(file), false)
assertFalse(result.failed()) assertFalse(result.failed())
} }
companion object { companion object {
private fun createExpected(path: Path): Path { private fun createExpected(path: Path): Path {
return path return path.parent.resolve(path.fileName.toString() + "-expected.pcf").createFile()
.parent
.resolve(path.fileName.toString() + "-expected.pcf")
.createFile()
} }
private fun String.stripFileAndLines(tmpDir: Path) = private fun String.stripFileAndLines(tmpDir: Path) =

View File

@@ -1,11 +1,26 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.nio.file.Path
import java.time.Duration
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.pkl.core.project.Project import org.pkl.core.project.Project
import java.nio.file.Path
import java.time.Duration
class EvaluatorBuilderTest { class EvaluatorBuilderTest {
@Test @Test
@@ -34,40 +49,32 @@ class EvaluatorBuilderTest {
@Test @Test
fun `enforces that security manager is set`() { fun `enforces that security manager is set`() {
val e1 = assertThrows<IllegalStateException> { val e1 =
EvaluatorBuilder.unconfigured() assertThrows<IllegalStateException> {
.setStackFrameTransformer(StackFrameTransformers.empty) EvaluatorBuilder.unconfigured()
.build() .setStackFrameTransformer(StackFrameTransformers.empty)
} .build()
}
assertThat(e1).hasMessage("No security manager set.") assertThat(e1).hasMessage("No security manager set.")
} }
@Test @Test
fun `enforces that stack frame transformer is set`() { fun `enforces that stack frame transformer is set`() {
val e1 = assertThrows<IllegalStateException> { val e1 =
EvaluatorBuilder.unconfigured() assertThrows<IllegalStateException> {
.setSecurityManager(SecurityManagers.defaultManager) EvaluatorBuilder.unconfigured().setSecurityManager(SecurityManagers.defaultManager).build()
.build() }
}
assertThat(e1).hasMessage("No stack frame transformer set.") assertThat(e1).hasMessage("No stack frame transformer set.")
} }
@Test @Test
fun `sets evaluator settings from project`() { fun `sets evaluator settings from project`() {
val projectPath = Path.of(javaClass.getResource("project/project1/PklProject")!!.toURI()) val projectPath = Path.of(javaClass.getResource("project/project1/PklProject")!!.toURI())
val project = Project.loadFromPath( val project = Project.loadFromPath(projectPath, SecurityManagers.defaultManager, null)
projectPath,
SecurityManagers.defaultManager,
null
)
val projectDir = Path.of(javaClass.getResource("project/project1/PklProject")!!.toURI()).parent val projectDir = Path.of(javaClass.getResource("project/project1/PklProject")!!.toURI()).parent
val builder = EvaluatorBuilder val builder = EvaluatorBuilder.unconfigured().applyFromProject(project)
.unconfigured() assertThat(builder.allowedResources.map { it.pattern() }).isEqualTo(listOf("foo:", "bar:"))
.applyFromProject(project) assertThat(builder.allowedModules.map { it.pattern() }).isEqualTo(listOf("baz:", "biz:"))
assertThat(builder.allowedResources.map { it.pattern() })
.isEqualTo(listOf("foo:", "bar:"))
assertThat(builder.allowedModules.map { it.pattern() })
.isEqualTo(listOf("baz:", "biz:"))
assertThat(builder.externalProperties).isEqualTo(mapOf("one" to "1")) assertThat(builder.externalProperties).isEqualTo(mapOf("one" to "1"))
assertThat(builder.environmentVariables).isEqualTo(mapOf("two" to "2")) assertThat(builder.environmentVariables).isEqualTo(mapOf("two" to "2"))
assertThat(builder.moduleCacheDir).isEqualTo(projectDir.resolve("my-cache-dir/")) assertThat(builder.moduleCacheDir).isEqualTo(projectDir.resolve("my-cache-dir/"))

View File

@@ -1,32 +1,47 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.pkl.commons.createParentDirectories
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.FileSystems
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.util.*
import java.util.regex.Pattern
import kotlin.io.path.writeText
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.createParentDirectories
import org.pkl.commons.createTempFile import org.pkl.commons.createTempFile
import org.pkl.commons.test.PackageServer
import org.pkl.commons.toPath import org.pkl.commons.toPath
import org.pkl.commons.writeString import org.pkl.commons.writeString
import org.pkl.core.ModuleSource.* import org.pkl.core.ModuleSource.*
import org.pkl.core.util.IoUtils
import org.junit.jupiter.api.AfterAll
import org.pkl.commons.test.PackageServer
import org.pkl.core.module.ModuleKey import org.pkl.core.module.ModuleKey
import org.pkl.core.module.ModuleKeyFactories import org.pkl.core.module.ModuleKeyFactories
import org.pkl.core.module.ModuleKeyFactory import org.pkl.core.module.ModuleKeyFactory
import org.pkl.core.module.ResolvedModuleKey import org.pkl.core.module.ResolvedModuleKey
import org.pkl.core.project.Project import org.pkl.core.project.Project
import java.nio.charset.StandardCharsets import org.pkl.core.util.IoUtils
import java.nio.file.FileSystems
import java.util.*
import java.util.regex.Pattern
import kotlin.io.path.writeText
class EvaluatorTest { class EvaluatorTest {
companion object { companion object {
@@ -50,8 +65,10 @@ class EvaluatorTest {
override fun getUri(): URI = uri override fun getUri(): URI = uri
override fun loadSource(): String = javaClass.classLoader.getResourceAsStream(uri.path.drop(1))!!.use { it.readAllBytes().toString( override fun loadSource(): String =
StandardCharsets.UTF_8) } javaClass.classLoader.getResourceAsStream(uri.path.drop(1))!!.use {
it.readAllBytes().toString(StandardCharsets.UTF_8)
}
override fun resolve(securityManager: SecurityManager): ResolvedModuleKey = this override fun resolve(securityManager: SecurityManager): ResolvedModuleKey = this
} }
@@ -71,28 +88,20 @@ class EvaluatorTest {
@Test @Test
fun `evaluate text with relative import`() { fun `evaluate text with relative import`() {
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(text("import \"foo.bar\"")) }
evaluator.evaluate(text("import \"foo.bar\"")) assertThat(e).hasMessageContaining("Module `repl:text` cannot have a relative import URI.")
}
assertThat(e)
.hasMessageContaining("Module `repl:text` cannot have a relative import URI.")
} }
@Test @Test
fun `evaluate named module`() { fun `evaluate named module`() {
val module = evaluator.evaluate( val module = evaluator.evaluate(modulePath("org/pkl/core/EvaluatorTest.pkl"))
modulePath("org/pkl/core/EvaluatorTest.pkl")
)
checkModule(module) checkModule(module)
} }
@Test @Test
fun `evaluate non-existing named module`() { fun `evaluate non-existing named module`() {
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(modulePath("non/existing.pkl")) }
evaluator.evaluate(modulePath("non/existing.pkl")) assertThat(e).hasMessageContaining("Cannot find module `modulepath:/non/existing.pkl`.")
}
assertThat(e)
.hasMessageContaining("Cannot find module `modulepath:/non/existing.pkl`.")
} }
@Test @Test
@@ -107,11 +116,8 @@ class EvaluatorTest {
@Test @Test
fun `evaluate non-existing file`() { fun `evaluate non-existing file`() {
val file = File("/non/existing") val file = File("/non/existing")
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(file(file)) }
evaluator.evaluate(file(file)) assertThat(e).hasMessageContaining("Cannot find module `${file.toPath().toUri()}`.")
}
assertThat(e)
.hasMessageContaining("Cannot find module `${file.toPath().toUri()}`.")
} }
@Test @Test
@@ -126,11 +132,8 @@ class EvaluatorTest {
@Test @Test
fun `evaluate non-existing path`() { fun `evaluate non-existing path`() {
val path = "/non/existing".toPath() val path = "/non/existing".toPath()
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(path(path)) }
evaluator.evaluate(path(path)) assertThat(e).hasMessageContaining("Cannot find module `${path.toUri()}`.")
}
assertThat(e)
.hasMessageContaining("Cannot find module `${path.toUri()}`.")
} }
@Test @Test
@@ -150,9 +153,7 @@ class EvaluatorTest {
// cast required to compile on JDK 14+ (which adds new overload) // cast required to compile on JDK 14+ (which adds new overload)
FileSystems.newFileSystem(zipFile, null as ClassLoader?).use { zipFs -> FileSystems.newFileSystem(zipFile, null as ClassLoader?).use { zipFs ->
val file = zipFs.getPath("non/existing") val file = zipFs.getPath("non/existing")
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(path(file)) }
evaluator.evaluate(path(file))
}
assertThat(e) assertThat(e)
.hasMessageContaining("Cannot find module `jar:file:") .hasMessageContaining("Cannot find module `jar:file:")
.hasMessageContaining("non/existing") .hasMessageContaining("non/existing")
@@ -170,11 +171,9 @@ class EvaluatorTest {
@Test @Test
fun `evaluate non-existing URI`() { fun `evaluate non-existing URI`() {
val e = assertThrows<PklException> { val e =
evaluator.evaluate(uri(URI("https://localhost/non/existing"))) assertThrows<PklException> { evaluator.evaluate(uri(URI("https://localhost/non/existing"))) }
} assertThat(e).hasMessageContaining("I/O error loading module `https://localhost/non/existing`.")
assertThat(e)
.hasMessageContaining("I/O error loading module `https://localhost/non/existing`.")
} }
@Test @Test
@@ -187,112 +186,117 @@ class EvaluatorTest {
@Test @Test
fun `evaluate jar URI with non-existing archive`() { fun `evaluate jar URI with non-existing archive`() {
val moduleUri = URI("jar:file:///non/existing!/bar.pkl") val moduleUri = URI("jar:file:///non/existing!/bar.pkl")
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(uri(moduleUri)) }
evaluator.evaluate(uri(moduleUri)) assertThat(e).hasMessageContaining("Cannot find module `$moduleUri`.")
}
assertThat(e)
.hasMessageContaining("Cannot find module `$moduleUri`.")
} }
@Test @Test
fun `evaluate jar URI with non-existing archive path`(@TempDir tempDir: Path) { fun `evaluate jar URI with non-existing archive path`(@TempDir tempDir: Path) {
val zipFile = createModulesZip(tempDir) val zipFile = createModulesZip(tempDir)
val moduleUri = URI("jar:${zipFile.toUri()}!/non/existing") val moduleUri = URI("jar:${zipFile.toUri()}!/non/existing")
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(uri(moduleUri)) }
evaluator.evaluate(uri(moduleUri)) assertThat(e).hasMessageContaining("Cannot find module `$moduleUri`.")
}
assertThat(e)
.hasMessageContaining("Cannot find module `$moduleUri`.")
} }
@Test @Test
fun `evaluate module with relative URI`() { fun `evaluate module with relative URI`() {
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(create(URI("foo.bar"), "")) }
evaluator.evaluate(create(URI("foo.bar"), ""))
}
assertThat(e) assertThat(e).hasMessageContaining("Cannot evaluate relative module URI `foo.bar`.")
.hasMessageContaining("Cannot evaluate relative module URI `foo.bar`.")
} }
@Test @Test
fun `evaluating a broken module multiple times results in the same error every time`() { fun `evaluating a broken module multiple times results in the same error every time`() {
val e1 = assertThrows<PklException> { val e1 =
evaluator.evaluate(modulePath("org/pkl/core/brokenModule1.pkl")) assertThrows<PklException> {
} evaluator.evaluate(modulePath("org/pkl/core/brokenModule1.pkl"))
val e2 = assertThrows<PklException> { }
evaluator.evaluate(modulePath("org/pkl/core/brokenModule1.pkl")) val e2 =
} assertThrows<PklException> {
evaluator.evaluate(modulePath("org/pkl/core/brokenModule1.pkl"))
}
assertThat(e2.message).isEqualTo(e1.message) assertThat(e2.message).isEqualTo(e1.message)
val e3 = assertThrows<PklException> { val e3 =
evaluator.evaluate(modulePath("org/pkl/core/brokenModule2.pkl")) assertThrows<PklException> {
} evaluator.evaluate(modulePath("org/pkl/core/brokenModule2.pkl"))
val e4 = assertThrows<PklException> { }
evaluator.evaluate(modulePath("org/pkl/core/brokenModule2.pkl")) val e4 =
} assertThrows<PklException> {
evaluator.evaluate(modulePath("org/pkl/core/brokenModule2.pkl"))
}
assertThat(e4.message).isEqualTo(e3.message) assertThat(e4.message).isEqualTo(e3.message)
} }
@Test @Test
fun `evaluation timeout`() { fun `evaluation timeout`() {
val evaluator = EvaluatorBuilder.preconfigured() val evaluator =
.setTimeout(java.time.Duration.ofMillis(100)) EvaluatorBuilder.preconfigured().setTimeout(java.time.Duration.ofMillis(100)).build()
.build() val e =
val e = assertThrows<PklException> { assertThrows<PklException> {
evaluator.evaluate(text( evaluator.evaluate(
""" text(
"""
function fib(n) = if (n < 2) 0 else fib(n - 1) + fib(n - 2) function fib(n) = if (n < 2) 0 else fib(n - 1) + fib(n - 2)
x = fib(100) x = fib(100)
""".trimIndent() """
)) .trimIndent()
} )
)
}
assertThat(e.message).contains("timed out") assertThat(e.message).contains("timed out")
} }
@Test @Test
fun `stack overflow`() { fun `stack overflow`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val e = assertThrows<PklException> { val e =
evaluator.evaluate(text(""" assertThrows<PklException> {
evaluator.evaluate(
text(
"""
a = b a = b
b = c b = c
c = a c = a
""".trimIndent())) """
} .trimIndent()
)
)
}
assertThat(e.message).contains("A stack overflow occurred.") assertThat(e.message).contains("A stack overflow occurred.")
} }
@Test @Test
fun `cannot import module located outside root dir`(@TempDir tempDir: Path) { fun `cannot import module located outside root dir`(@TempDir tempDir: Path) {
val evaluator = EvaluatorBuilder.preconfigured() val evaluator =
.setSecurityManager( EvaluatorBuilder.preconfigured()
SecurityManagers.standard( .setSecurityManager(
SecurityManagers.defaultAllowedModules, SecurityManagers.standard(
SecurityManagers.defaultAllowedResources, SecurityManagers.defaultAllowedModules,
SecurityManagers.defaultTrustLevels, SecurityManagers.defaultAllowedResources,
tempDir SecurityManagers.defaultTrustLevels,
tempDir
)
) )
) .build()
.build()
val module = tempDir.resolve("test.pkl") val module = tempDir.resolve("test.pkl")
module.writeString( module.writeString(
""" """
amends "/non/existing.pkl" amends "/non/existing.pkl"
""".trimIndent() """
.trimIndent()
) )
val e = assertThrows<PklException> { val e = assertThrows<PklException> { evaluator.evaluate(path(module)) }
evaluator.evaluate(path(module))
}
assertThat(e.message).contains("Refusing to load module `file:///non/existing.pkl`") assertThat(e.message).contains("Refusing to load module `file:///non/existing.pkl`")
} }
@Test @Test
fun `multiple-file output`() { fun `multiple-file output`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val program = """ val program =
"""
output { output {
files { files {
["foo.yml"] { ["foo.yml"] {
@@ -309,14 +313,10 @@ class EvaluatorTest {
} }
} }
} }
""".trimIndent() """
.trimIndent()
val output = evaluator.evaluateOutputFiles(ModuleSource.text(program)) val output = evaluator.evaluateOutputFiles(ModuleSource.text(program))
assertThat(output.keys).isEqualTo(setOf( assertThat(output.keys).isEqualTo(setOf("foo.yml", "bar.yml", "bar/biz.yml", "bar/../bark.yml"))
"foo.yml",
"bar.yml",
"bar/biz.yml",
"bar/../bark.yml"
))
assertThat(output["foo.yml"]?.text).isEqualTo("foo: foo text") assertThat(output["foo.yml"]?.text).isEqualTo("foo: foo text")
assertThat(output["bar.yml"]?.text).isEqualTo("bar: bar text") assertThat(output["bar.yml"]?.text).isEqualTo("bar: bar text")
assertThat(output["bar/biz.yml"]?.text).isEqualTo("biz: bar biz") assertThat(output["bar/biz.yml"]?.text).isEqualTo("biz: bar biz")
@@ -328,10 +328,13 @@ class EvaluatorTest {
PackageServer.populateCacheDir(cacheDir) PackageServer.populateCacheDir(cacheDir)
val evaluatorBuilder = EvaluatorBuilder.preconfigured().setModuleCacheDir(cacheDir) val evaluatorBuilder = EvaluatorBuilder.preconfigured().setModuleCacheDir(cacheDir)
val project = Project.load(modulePath("/org/pkl/core/project/project5/PklProject")) val project = Project.load(modulePath("/org/pkl/core/project/project5/PklProject"))
val result = evaluatorBuilder.setProjectDependencies(project.dependencies).build().use { evaluator -> val result =
evaluator.evaluateOutputText(modulePath("/org/pkl/core/project/project5/main.pkl")) evaluatorBuilder.setProjectDependencies(project.dependencies).build().use { evaluator ->
} evaluator.evaluateOutputText(modulePath("/org/pkl/core/project/project5/main.pkl"))
assertThat(result).isEqualTo(""" }
assertThat(result)
.isEqualTo(
"""
prop1 { prop1 {
name = "Apple" name = "Apple"
} }
@@ -339,30 +342,37 @@ class EvaluatorTest {
res = 1 res = 1
} }
""".trimIndent()) """
.trimIndent()
)
} }
@Test @Test
fun `project set from custom ModuleKeyFactory`(@TempDir cacheDir: Path) { fun `project set from custom ModuleKeyFactory`(@TempDir cacheDir: Path) {
PackageServer.populateCacheDir(cacheDir) PackageServer.populateCacheDir(cacheDir)
val evaluatorBuilder = with(EvaluatorBuilder.preconfigured()) { val evaluatorBuilder =
setAllowedModules(SecurityManagers.defaultAllowedModules + Pattern.compile("custom:")) with(EvaluatorBuilder.preconfigured()) {
setAllowedResources(SecurityManagers.defaultAllowedResources + Pattern.compile("custom:")) setAllowedModules(SecurityManagers.defaultAllowedModules + Pattern.compile("custom:"))
setModuleCacheDir(cacheDir) setAllowedResources(SecurityManagers.defaultAllowedResources + Pattern.compile("custom:"))
setModuleKeyFactories( setModuleCacheDir(cacheDir)
listOf( setModuleKeyFactories(
CustomModuleKeyFactory, listOf(
ModuleKeyFactories.standardLibrary, CustomModuleKeyFactory,
ModuleKeyFactories.pkg, ModuleKeyFactories.standardLibrary,
ModuleKeyFactories.projectpackage, ModuleKeyFactories.pkg,
ModuleKeyFactories.file ModuleKeyFactories.projectpackage,
ModuleKeyFactories.file
)
) )
) }
} val project =
val project = evaluatorBuilder.build().use { Project.load(it, uri("custom:/org/pkl/core/project/project5/PklProject")) } evaluatorBuilder.build().use {
Project.load(it, uri("custom:/org/pkl/core/project/project5/PklProject"))
}
val evaluator = evaluatorBuilder.setProjectDependencies(project.dependencies).build() val evaluator = evaluatorBuilder.setProjectDependencies(project.dependencies).build()
val output = evaluator.use { it.evaluateOutputText(uri("custom:/org/pkl/core/project/project5/main.pkl")) } val output =
evaluator.use { it.evaluateOutputText(uri("custom:/org/pkl/core/project/project5/main.pkl")) }
assertThat(output) assertThat(output)
.isEqualTo( .isEqualTo(
""" """
@@ -382,45 +392,55 @@ class EvaluatorTest {
fun `project base path set to non-hierarchical scheme`() { fun `project base path set to non-hierarchical scheme`() {
class FooBarModuleKey(val moduleUri: URI) : ModuleKey, ResolvedModuleKey { class FooBarModuleKey(val moduleUri: URI) : ModuleKey, ResolvedModuleKey {
override fun hasHierarchicalUris(): Boolean = false override fun hasHierarchicalUris(): Boolean = false
override fun isGlobbable(): Boolean = false override fun isGlobbable(): Boolean = false
override fun getOriginal(): ModuleKey = this override fun getOriginal(): ModuleKey = this
override fun getUri(): URI = moduleUri override fun getUri(): URI = moduleUri
override fun loadSource(): String = override fun loadSource(): String =
if (uri.schemeSpecificPart.endsWith("PklProject")) { if (uri.schemeSpecificPart.endsWith("PklProject")) {
""" """
amends "pkl:Project" amends "pkl:Project"
""".trimIndent() """
} else """ .trimIndent()
} else
"""
birds = import("@birds/catalog/Ostritch.pkl") birds = import("@birds/catalog/Ostritch.pkl")
""".trimIndent() """
.trimIndent()
override fun resolve(securityManager: SecurityManager): ResolvedModuleKey { override fun resolve(securityManager: SecurityManager): ResolvedModuleKey {
return this return this
} }
} }
val fooBayModuleKeyFactory = ModuleKeyFactory { uri -> val fooBayModuleKeyFactory = ModuleKeyFactory { uri ->
if (uri.scheme == "foobar") Optional.of(FooBarModuleKey(uri)) if (uri.scheme == "foobar") Optional.of(FooBarModuleKey(uri)) else Optional.empty()
else Optional.empty()
} }
val evaluatorBuilder = with(EvaluatorBuilder.preconfigured()) { val evaluatorBuilder =
setAllowedModules(SecurityManagers.defaultAllowedModules + Pattern.compile("foobar:")) with(EvaluatorBuilder.preconfigured()) {
setAllowedResources(SecurityManagers.defaultAllowedResources + Pattern.compile("foobar:")) setAllowedModules(SecurityManagers.defaultAllowedModules + Pattern.compile("foobar:"))
setModuleKeyFactories( setAllowedResources(SecurityManagers.defaultAllowedResources + Pattern.compile("foobar:"))
listOf( setModuleKeyFactories(
fooBayModuleKeyFactory, listOf(
ModuleKeyFactories.standardLibrary, fooBayModuleKeyFactory,
ModuleKeyFactories.pkg, ModuleKeyFactories.standardLibrary,
ModuleKeyFactories.projectpackage, ModuleKeyFactories.pkg,
ModuleKeyFactories.file ModuleKeyFactories.projectpackage,
ModuleKeyFactories.file
)
) )
) }
}
val project = evaluatorBuilder.build().use { Project.load(it, uri("foobar:foo/PklProject")) } val project = evaluatorBuilder.build().use { Project.load(it, uri("foobar:foo/PklProject")) }
val evaluator = evaluatorBuilder.setProjectDependencies(project.dependencies).build() val evaluator = evaluatorBuilder.setProjectDependencies(project.dependencies).build()
assertThatCode { evaluator.use { it.evaluateOutputText(uri("foobar:baz")) } } assertThatCode { evaluator.use { it.evaluateOutputText(uri("foobar:baz")) } }
.hasMessageContaining("Cannot import dependency because project URI `foobar:foo/PklProject` does not have a hierarchical path.") .hasMessageContaining(
"Cannot import dependency because project URI `foobar:foo/PklProject` does not have a hierarchical path."
)
} }
@Test @Test
@@ -430,22 +450,34 @@ class EvaluatorTest {
val project = Project.load(modulePath("/org/pkl/core/project/project6/PklProject")) val project = Project.load(modulePath("/org/pkl/core/project/project6/PklProject"))
evaluatorBuilder.setProjectDependencies(project.dependencies).build().use { evaluator -> evaluatorBuilder.setProjectDependencies(project.dependencies).build().use { evaluator ->
assertThatCode { assertThatCode {
evaluator.evaluateOutputText(modulePath("/org/pkl/core/project/project6/globWithinDependency.pkl")) evaluator.evaluateOutputText(
}.hasMessageContaining(""" modulePath("/org/pkl/core/project/project6/globWithinDependency.pkl")
)
}
.hasMessageContaining(
"""
Cannot resolve import in local dependency because scheme `modulepath` is not globbable. Cannot resolve import in local dependency because scheme `modulepath` is not globbable.
1 | res = import*("*.pkl") 1 | res = import*("*.pkl")
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
""".trimIndent()) """
.trimIndent()
)
assertThatCode { assertThatCode {
evaluator.evaluateOutputText(modulePath("/org/pkl/core/project/project6/globIntoDependency.pkl")) evaluator.evaluateOutputText(
}.hasMessageContaining(""" modulePath("/org/pkl/core/project/project6/globIntoDependency.pkl")
)
}
.hasMessageContaining(
"""
Pkl Error Pkl Error
Cannot resolve import in local dependency because scheme `modulepath` is not globbable. Cannot resolve import in local dependency because scheme `modulepath` is not globbable.
1 | import* "@project7/*.pkl" as proj7Files 1 | import* "@project7/*.pkl" as proj7Files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
""".trimIndent()) """
.trimIndent()
)
} }
} }
@@ -460,13 +492,16 @@ class EvaluatorTest {
val module1 = modulesDir.resolve("foo/bar/module1.pkl").createParentDirectories() val module1 = modulesDir.resolve("foo/bar/module1.pkl").createParentDirectories()
val module2 = modulesDir.resolve("foo/baz/module2.pkl").createParentDirectories() val module2 = modulesDir.resolve("foo/baz/module2.pkl").createParentDirectories()
module1.writeText(""" module1.writeText(
"""
// verify that relative import is resolved correctly // verify that relative import is resolved correctly
import "../baz/module2.pkl" import "../baz/module2.pkl"
name = module2.name name = module2.name
age = module2.age age = module2.age
""".trimIndent()) """
.trimIndent()
)
module2.writeText(sourceText) module2.writeText(sourceText)

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.io.StringWriter import java.io.StringWriter
@@ -12,9 +27,7 @@ class JsonRendererTest {
@Test @Test
fun `render document`() { fun `render document`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val module = evaluator.evaluate( val module = evaluator.evaluate(ModuleSource.modulePath("org/pkl/core/rendererTest.pkl"))
ModuleSource.modulePath("org/pkl/core/rendererTest.pkl")
)
val writer = StringWriter() val writer = StringWriter()
val renderer = ValueRenderers.json(writer, " ", true) val renderer = ValueRenderers.json(writer, " ", true)
@@ -23,14 +36,13 @@ class JsonRendererTest {
val expected = IoUtils.readClassPathResourceAsString(javaClass, "rendererTest.json") val expected = IoUtils.readClassPathResourceAsString(javaClass, "rendererTest.json")
assertThat(output).isEqualTo(expected) assertThat(output).isEqualTo(expected)
assertThatCode { JsonParser(object : JsonHandler<Any, Any>() {}).parse(output) }.doesNotThrowAnyException() assertThatCode { JsonParser(object : JsonHandler<Any, Any>() {}).parse(output) }
.doesNotThrowAnyException()
} }
@Test @Test
fun `rendered document ends in newline`() { fun `rendered document ends in newline`() {
val module: PModule = Evaluator val module: PModule = Evaluator.preconfigured().evaluate(ModuleSource.text("foo { bar = 0 }"))
.preconfigured()
.evaluate(ModuleSource.text("foo { bar = 0 }"))
for (omitNullProperties in listOf(false, true)) { for (omitNullProperties in listOf(false, true)) {
val writer = StringWriter() val writer = StringWriter()

View File

@@ -1,18 +1,28 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.junit.platform.commons.annotation.Testable import org.junit.platform.commons.annotation.Testable
@Testable @Testable class LanguageSnippetTests
class LanguageSnippetTests
@Testable @Testable class MacLanguageSnippetTests
class MacLanguageSnippetTests
@Testable @Testable class LinuxLanguageSnippetTests
class LinuxLanguageSnippetTests
@Testable @Testable class AlpineLanguageSnippetTests
class AlpineLanguageSnippetTests
@Testable @Testable class WindowsLanguageSnippetTests
class WindowsLanguageSnippetTests

View File

@@ -1,5 +1,27 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.io.PrintWriter
import java.io.StringWriter
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.isRegularFile
import kotlin.reflect.KClass
import org.junit.platform.engine.EngineDiscoveryRequest import org.junit.platform.engine.EngineDiscoveryRequest
import org.junit.platform.engine.TestDescriptor import org.junit.platform.engine.TestDescriptor
import org.junit.platform.engine.UniqueId import org.junit.platform.engine.UniqueId
@@ -11,13 +33,6 @@ import org.pkl.commons.test.PklExecutablePaths
import org.pkl.core.http.HttpClient import org.pkl.core.http.HttpClient
import org.pkl.core.project.Project import org.pkl.core.project.Project
import org.pkl.core.util.IoUtils import org.pkl.core.util.IoUtils
import java.io.PrintWriter
import java.io.StringWriter
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.isRegularFile
import kotlin.reflect.KClass
abstract class AbstractLanguageSnippetTestsEngine : InputOutputTestEngine() { abstract class AbstractLanguageSnippetTestsEngine : InputOutputTestEngine() {
private val lineNumberRegex = Regex("(?m)^(( ║ )*)(\\d+) \\|") private val lineNumberRegex = Regex("(?m)^(( ║ )*)(\\d+) \\|")
@@ -31,17 +46,17 @@ abstract class AbstractLanguageSnippetTestsEngine : InputOutputTestEngine() {
private val expectedOutputDir: Path = snippetsDir.resolve("output") private val expectedOutputDir: Path = snippetsDir.resolve("output")
/** /**
* Convenience for development; this selects which snippet test(s) to run. * Convenience for development; this selects which snippet test(s) to run. There is a
* There is a (non-language-snippet) test to make sure this is `""` before commit. * (non-language-snippet) test to make sure this is `""` before commit.
*/ */
//language=regexp // language=regexp
internal val selection: String = "" internal val selection: String = ""
protected val packageServer: PackageServer = PackageServer() protected val packageServer: PackageServer = PackageServer()
override val includedTests: List<Regex> = listOf(Regex(".*$selection\\.pkl")) override val includedTests: List<Regex> = listOf(Regex(".*$selection\\.pkl"))
override val excludedTests: List<Regex> = buildList { override val excludedTests: List<Regex> = buildList {
add(Regex(".*/native/.*")) add(Regex(".*/native/.*"))
if (IoUtils.isWindows()) { if (IoUtils.isWindows()) {
addAll(windowsExcludedTests) addAll(windowsExcludedTests)
@@ -53,8 +68,7 @@ abstract class AbstractLanguageSnippetTestsEngine : InputOutputTestEngine() {
override val isInputFile: (Path) -> Boolean = { it.isRegularFile() } override val isInputFile: (Path) -> Boolean = { it.isRegularFile() }
protected tailrec fun Path.getProjectDir(): Path? = protected tailrec fun Path.getProjectDir(): Path? =
if (Files.exists(this.resolve("PklProject"))) this if (Files.exists(this.resolve("PklProject"))) this else parent?.getProjectDir()
else parent?.getProjectDir()
override fun expectedOutputFileFor(inputFile: Path): Path { override fun expectedOutputFileFor(inputFile: Path): Path {
val relativePath = IoUtils.relativize(inputFile, inputDir).toString() val relativePath = IoUtils.relativize(inputFile, inputDir).toString()
@@ -75,17 +89,19 @@ abstract class AbstractLanguageSnippetTestsEngine : InputOutputTestEngine() {
private val replacement by lazy { private val replacement by lazy {
if (snippetsDir.root.toString() != "/") "\$snippetsDir" else "/\$snippetsDir" if (snippetsDir.root.toString() != "/") "\$snippetsDir" else "/\$snippetsDir"
} }
protected fun String.stripFilePaths(): String = protected fun String.stripFilePaths(): String =
replace(IoUtils.toNormalizedPathString(snippetsDir), replacement) replace(IoUtils.toNormalizedPathString(snippetsDir), replacement)
protected fun String.stripLineNumbers() = replace(lineNumberRegex) { result -> protected fun String.stripLineNumbers() =
// replace line number with equivalent number of 'x' characters to keep formatting intact replace(lineNumberRegex) { result ->
(result.groups[1]!!.value) + "x".repeat(result.groups[3]!!.value.length) + " |" // replace line number with equivalent number of 'x' characters to keep formatting intact
} (result.groups[1]!!.value) + "x".repeat(result.groups[3]!!.value.length) + " |"
}
protected fun String.stripWebsite() = replace(Release.current().documentation().homepage(), "https://\$pklWebsite/") protected fun String.stripWebsite() =
replace(Release.current().documentation().homepage(), "https://\$pklWebsite/")
// can't think of a better solution right now // can't think of a better solution right now
protected fun String.stripVersionCheckErrorMessage() = protected fun String.stripVersionCheckErrorMessage() =
@@ -103,8 +119,7 @@ abstract class AbstractLanguageSnippetTestsEngine : InputOutputTestEngine() {
} }
protected fun String.withUnixLineEndings(): String { protected fun String.withUnixLineEndings(): String {
return if (System.lineSeparator() == "\r\n") replace("\r\n", "\n") return if (System.lineSeparator() == "\r\n") replace("\r\n", "\n") else this
else this
} }
} }
@@ -123,16 +138,16 @@ class LanguageSnippetTestsEngine : AbstractLanguageSnippetTestsEngine() {
"file:///foo/bar" to "file:///foo/bar" "file:///foo/bar" to "file:///foo/bar"
) )
) )
.setExternalProperties(mapOf( .setExternalProperties(
"name1" to "value1", mapOf("name1" to "value1", "name2" to "value2", "/foo/bar" to "foobar")
"name2" to "value2", )
"/foo/bar" to "foobar"
))
.setModuleCacheDir(null) .setModuleCacheDir(null)
.setHttpClient(HttpClient.builder() .setHttpClient(
.setTestPort(packageServer.port) HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate) .setTestPort(packageServer.port)
.buildLazily()) .addCertificates(FileTestUtils.selfSignedCertificate)
.buildLazily()
)
} }
override val testClass: KClass<*> = LanguageSnippetTests::class override val testClass: KClass<*> = LanguageSnippetTests::class
@@ -140,60 +155,65 @@ class LanguageSnippetTestsEngine : AbstractLanguageSnippetTestsEngine() {
override fun generateOutputFor(inputFile: Path): kotlin.Pair<Boolean, String> { override fun generateOutputFor(inputFile: Path): kotlin.Pair<Boolean, String> {
val logWriter = StringWriter() val logWriter = StringWriter()
val (success, output) = try { val (success, output) =
val evaluator = evaluatorBuilder() try {
.setLogger(Loggers.writer(PrintWriter(logWriter))) val evaluator =
.apply { evaluatorBuilder()
if (inputFile.startsWith(projectsDir)) { .setLogger(Loggers.writer(PrintWriter(logWriter)))
val projectDir = inputFile.getProjectDir() ?: return@apply .apply {
val project = Project.loadFromPath( if (inputFile.startsWith(projectsDir)) {
projectDir.resolve("PklProject"), val projectDir = inputFile.getProjectDir() ?: return@apply
SecurityManagers.defaultManager, val project =
null, Project.loadFromPath(
StackFrameTransformers.empty, projectDir.resolve("PklProject"),
mapOf() SecurityManagers.defaultManager,
) null,
securityManager = null StackFrameTransformers.empty,
applyFromProject(project) mapOf()
} )
} securityManager = null
.build() applyFromProject(project)
evaluator.use { true to it.evaluateOutputText(ModuleSource.path(inputFile)) } }
} catch (e: PklBugException) { }
false to e.stackTraceToString() .build()
} catch (e: PklException) { evaluator.use { true to it.evaluateOutputText(ModuleSource.path(inputFile)) }
false to e.message!! } catch (e: PklBugException) {
.stripLineNumbers() false to e.stackTraceToString()
.stripVersionCheckErrorMessage() } catch (e: PklException) {
} false to e.message!!.stripLineNumbers().stripVersionCheckErrorMessage()
}
val stderr = logWriter.toString().withUnixLineEndings() val stderr = logWriter.toString().withUnixLineEndings()
return (success && stderr.isBlank()) to (output + stderr).stripFilePaths().stripWebsite().stripStdlibLocationSha() return (success && stderr.isBlank()) to
(output + stderr).stripFilePaths().stripWebsite().stripStdlibLocationSha()
} }
} }
abstract class AbstractNativeLanguageSnippetTestsEngine : AbstractLanguageSnippetTestsEngine() { abstract class AbstractNativeLanguageSnippetTestsEngine : AbstractLanguageSnippetTestsEngine() {
abstract val pklExecutablePath: Path abstract val pklExecutablePath: Path
override val excludedTests: List<Regex> = listOf( override val excludedTests: List<Regex> =
// exclude test that loads module from class path (there is no class path when using native executable) listOf(
// on the other hand, don't exclude /native/ // exclude test that loads module from class path (there is no class path when using native
Regex(".*/import1b\\.pkl"), // executable)
) // on the other hand, don't exclude /native/
Regex(".*/import1b\\.pkl"),
)
/** /** Avoid running tests for native binaries when those native binaries have not been built. */
* Avoid running tests for native binaries when those native binaries have not been built. override fun discover(
*/ discoveryRequest: EngineDiscoveryRequest,
override fun discover(discoveryRequest: EngineDiscoveryRequest, uniqueId: UniqueId): TestDescriptor { uniqueId: UniqueId
): TestDescriptor {
if (!pklExecutablePath.exists()) { if (!pklExecutablePath.exists()) {
// return empty descriptor w/o children // return empty descriptor w/o children
return EngineDescriptor(uniqueId, javaClass.simpleName) return EngineDescriptor(uniqueId, javaClass.simpleName)
} }
return super.discover(discoveryRequest, uniqueId) return super.discover(discoveryRequest, uniqueId)
} }
override fun generateOutputFor(inputFile: Path): kotlin.Pair<Boolean, String> { override fun generateOutputFor(inputFile: Path): kotlin.Pair<Boolean, String> {
val args = buildList { val args = buildList {
add(pklExecutablePath.toString()) add(pklExecutablePath.toString())
@@ -234,20 +254,22 @@ abstract class AbstractNativeLanguageSnippetTestsEngine : AbstractLanguageSnippe
add(inputFile.toString()) add(inputFile.toString())
} }
val builder = ProcessBuilder() val builder = ProcessBuilder().command(args)
.command(args)
val process = builder.start() val process = builder.start()
return try { return try {
val (out, err) = listOf(process.inputStream, process.errorStream) val (out, err) =
.map { it.reader().readText().withUnixLineEndings() } listOf(process.inputStream, process.errorStream).map {
it.reader().readText().withUnixLineEndings()
}
val success = process.waitFor() == 0 && err.isBlank() val success = process.waitFor() == 0 && err.isBlank()
success to (out + err) success to
.stripFilePaths() (out + err)
.stripLineNumbers() .stripFilePaths()
.stripWebsite() .stripLineNumbers()
.stripVersionCheckErrorMessage() .stripWebsite()
.stripStdlibLocationSha() .stripVersionCheckErrorMessage()
.stripStdlibLocationSha()
} finally { } finally {
process.destroy() process.destroy()
} }
@@ -280,11 +302,12 @@ class AlpineLanguageSnippetTestsEngine : AbstractNativeLanguageSnippetTestsEngin
} }
// error message contains different file path on Windows // error message contains different file path on Windows
private val windowsExcludedTests get() = listOf(Regex(".*missingProjectDeps/bug\\.pkl")) private val windowsExcludedTests
get() = listOf(Regex(".*missingProjectDeps/bug\\.pkl"))
class WindowsLanguageSnippetTestsEngine : AbstractNativeLanguageSnippetTestsEngine() { class WindowsLanguageSnippetTestsEngine : AbstractNativeLanguageSnippetTestsEngine() {
override val pklExecutablePath: Path = PklExecutablePaths.windowsAmd64 override val pklExecutablePath: Path = PklExecutablePaths.windowsAmd64
override val testClass: KClass<*> = WindowsLanguageSnippetTests::class override val testClass: KClass<*> = WindowsLanguageSnippetTests::class
override val excludedTests: List<Regex> override val excludedTests: List<Regex>
get() = super.excludedTests + windowsExcludedTests get() = super.excludedTests + windowsExcludedTests
} }

View File

@@ -1,8 +1,23 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.net.URI
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.net.URI
class PClassInfoTest { class PClassInfoTest {
@Test @Test

View File

@@ -1,13 +1,28 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.io.StringWriter import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory import javax.xml.parsers.DocumentBuilderFactory
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.core.util.IoUtils
import org.xml.sax.ErrorHandler import org.xml.sax.ErrorHandler
import org.xml.sax.InputSource import org.xml.sax.InputSource
import org.xml.sax.SAXParseException import org.xml.sax.SAXParseException
import org.pkl.core.util.IoUtils
class PListRendererTest { class PListRendererTest {
@Test @Test
@@ -28,8 +43,7 @@ class PListRendererTest {
@Test @Test
fun `rendered document ends in newline`() { fun `rendered document ends in newline`() {
val module = Evaluator.preconfigured() val module = Evaluator.preconfigured().evaluate(ModuleSource.text("foo { bar = 0 }"))
.evaluate(ModuleSource.text("foo { bar = 0 }"))
val writer = StringWriter() val writer = StringWriter()
ValueRenderers.plist(writer, " ").renderDocument(module) ValueRenderers.plist(writer, " ").renderDocument(module)
@@ -37,28 +51,29 @@ class PListRendererTest {
} }
private fun parseAndValidateRenderedDocument(output: String) { private fun parseAndValidateRenderedDocument(output: String) {
val builderFactory = DocumentBuilderFactory.newInstance().apply { val builderFactory = DocumentBuilderFactory.newInstance().apply { isValidating = true }
isValidating = true
}
val builder = builderFactory.newDocumentBuilder().apply { val builder =
setEntityResolver { _, _ -> builderFactory.newDocumentBuilder().apply {
InputSource(PListRendererTest::class.java.getResourceAsStream("PropertyList-1.0.dtd")) setEntityResolver { _, _ ->
InputSource(PListRendererTest::class.java.getResourceAsStream("PropertyList-1.0.dtd"))
}
setErrorHandler(
object : ErrorHandler {
override fun warning(exception: SAXParseException) {
throw exception
}
override fun error(exception: SAXParseException) {
throw exception
}
override fun fatalError(exception: SAXParseException) {
throw exception
}
}
)
} }
setErrorHandler(object : ErrorHandler {
override fun warning(exception: SAXParseException) {
throw exception
}
override fun error(exception: SAXParseException) {
throw exception
}
override fun fatalError(exception: SAXParseException) {
throw exception
}
})
}
builder.parse(output.byteInputStream()) builder.parse(output.byteInputStream())
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.net.URI import java.net.URI
@@ -25,14 +40,12 @@ class PModuleTest {
@Test @Test
fun `get unknown property`() { fun `get unknown property`() {
val e = assertThrows<NoSuchPropertyException> { val e = assertThrows<NoSuchPropertyException> { pigeon.getProperty("other") }
pigeon.getProperty("other")
}
assertThat(e) assertThat(e)
.hasMessage( .hasMessage(
"Module `test.module` does not have a property " + "Module `test.module` does not have a property " +
"named `other`. Available properties: [name, age]" "named `other`. Available properties: [name, age]"
) )
} }
@@ -48,15 +61,16 @@ class PModuleTest {
var objectVisited = false var objectVisited = false
var moduleVisited = false var moduleVisited = false
val visitor = object : ValueVisitor { val visitor =
override fun visitObject(value: PObject) { object : ValueVisitor {
objectVisited = true override fun visitObject(value: PObject) {
} objectVisited = true
}
override fun visitModule(value: PModule) { override fun visitModule(value: PModule) {
moduleVisited = true moduleVisited = true
}
} }
}
pigeon.accept(visitor) pigeon.accept(visitor)
@@ -69,12 +83,7 @@ class PModuleTest {
assertThat(pigeon).isEqualTo(pigeon) assertThat(pigeon).isEqualTo(pigeon)
assertThat(pigeon.hashCode()).isEqualTo(pigeon.hashCode()) assertThat(pigeon.hashCode()).isEqualTo(pigeon.hashCode())
val pigeon2 = PModule( val pigeon2 = PModule(moduleUri, moduleName, classInfo, HashMap(properties))
moduleUri,
moduleName,
classInfo,
HashMap(properties)
)
assertThat(pigeon2).isEqualTo(pigeon) assertThat(pigeon2).isEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isEqualTo(pigeon.hashCode())
@@ -82,12 +91,7 @@ class PModuleTest {
@Test @Test
fun `non-equal - different module uri`() { fun `non-equal - different module uri`() {
val pigeon2 = PModule( val pigeon2 = PModule(URI("other/module"), moduleName, classInfo, properties)
URI("other/module"),
moduleName,
classInfo,
properties
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -95,12 +99,7 @@ class PModuleTest {
@Test @Test
fun `non-equal - different module name`() { fun `non-equal - different module name`() {
val pigeon2 = PModule( val pigeon2 = PModule(moduleUri, "other.module", classInfo, properties)
moduleUri,
"other.module",
classInfo,
properties
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -108,12 +107,7 @@ class PModuleTest {
@Test @Test
fun `non-equal - different property value`() { fun `non-equal - different property value`() {
val pigeon2 = PModule( val pigeon2 = PModule(moduleUri, moduleName, classInfo, mapOf("name" to "Pigeon", "age" to 21))
moduleUri,
moduleName,
classInfo,
mapOf("name" to "Pigeon", "age" to 21)
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -121,12 +115,7 @@ class PModuleTest {
@Test @Test
fun `non-equal - missing property`() { fun `non-equal - missing property`() {
val pigeon2 = PModule( val pigeon2 = PModule(moduleUri, moduleName, classInfo, mapOf("name" to "Pigeon"))
moduleUri,
moduleName,
classInfo,
mapOf("name" to "Pigeon")
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -134,12 +123,13 @@ class PModuleTest {
@Test @Test
fun `non-equal - extra property`() { fun `non-equal - extra property`() {
val pigeon2 = PModule( val pigeon2 =
moduleUri, PModule(
moduleName, moduleUri,
classInfo, moduleName,
mapOf("name" to "Pigeon", "age" to 42, "other" to true) classInfo,
) mapOf("name" to "Pigeon", "age" to 42, "other" to true)
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -147,7 +137,6 @@ class PModuleTest {
@Test @Test
fun `toString()`() { fun `toString()`() {
assertThat(pigeon.toString()) assertThat(pigeon.toString()).isEqualTo("test.module { name = Pigeon; age = 42 }")
.isEqualTo("test.module { name = Pigeon; age = 42 }")
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@@ -1,9 +1,24 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.net.URI
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import java.net.URI
class PObjectTest { class PObjectTest {
private val testUri = URI("repl:test") private val testUri = URI("repl:test")
@@ -29,14 +44,12 @@ class PObjectTest {
@Test @Test
fun `get unknown property`() { fun `get unknown property`() {
val e = assertThrows<NoSuchPropertyException> { val e = assertThrows<NoSuchPropertyException> { pigeon.getProperty("other") }
pigeon.getProperty("other")
}
assertThat(e) assertThat(e)
.hasMessage( .hasMessage(
"Object of type `test#Person` does not have a property " + "Object of type `test#Person` does not have a property " +
"named `other`. Available properties: [name, age]" "named `other`. Available properties: [name, age]"
) )
} }
@@ -52,15 +65,16 @@ class PObjectTest {
var objectVisited = false var objectVisited = false
var moduleVisited = false var moduleVisited = false
val visitor = object : ValueVisitor { val visitor =
override fun visitObject(value: PObject) { object : ValueVisitor {
objectVisited = true override fun visitObject(value: PObject) {
} objectVisited = true
}
override fun visitModule(value: PModule) { override fun visitModule(value: PModule) {
moduleVisited = true moduleVisited = true
}
} }
}
pigeon.accept(visitor) pigeon.accept(visitor)
@@ -73,10 +87,7 @@ class PObjectTest {
assertThat(pigeon).isEqualTo(pigeon) assertThat(pigeon).isEqualTo(pigeon)
assertThat(pigeon.hashCode()).isEqualTo(pigeon.hashCode()) assertThat(pigeon.hashCode()).isEqualTo(pigeon.hashCode())
val pigeon2 = PObject( val pigeon2 = PObject(PClassInfo.get("test", "Person", URI("repl:test")), HashMap(properties))
PClassInfo.get("test", "Person", URI("repl:test")),
HashMap(properties)
)
assertThat(pigeon2).isEqualTo(pigeon) assertThat(pigeon2).isEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isEqualTo(pigeon.hashCode())
@@ -84,10 +95,7 @@ class PObjectTest {
@Test @Test
fun `non-equal - different type`() { fun `non-equal - different type`() {
val pigeon2 = PObject( val pigeon2 = PObject(PClassInfo.get("test", "Other", URI("repl:Other")), properties)
PClassInfo.get("test", "Other", URI("repl:Other")),
properties
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -95,10 +103,11 @@ class PObjectTest {
@Test @Test
fun `non-equal - different property value`() { fun `non-equal - different property value`() {
val pigeon2 = PObject( val pigeon2 =
PClassInfo.get("test", "Person", URI("repl:test")), PObject(
mapOf("name" to "Pigeon", "age" to 21) PClassInfo.get("test", "Person", URI("repl:test")),
) mapOf("name" to "Pigeon", "age" to 21)
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -106,10 +115,8 @@ class PObjectTest {
@Test @Test
fun `non-equal - missing property`() { fun `non-equal - missing property`() {
val pigeon2 = PObject( val pigeon2 =
PClassInfo.get("test", "Person", URI("repl:test")), PObject(PClassInfo.get("test", "Person", URI("repl:test")), mapOf("name" to "Pigeon"))
mapOf("name" to "Pigeon")
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -117,10 +124,11 @@ class PObjectTest {
@Test @Test
fun `non-equal - extra property`() { fun `non-equal - extra property`() {
val pigeon2 = PObject( val pigeon2 =
PClassInfo.get("test", "Person", URI("repl:test")), PObject(
mapOf("name" to "Pigeon", "age" to 42, "other" to true) PClassInfo.get("test", "Person", URI("repl:test")),
) mapOf("name" to "Pigeon", "age" to 42, "other" to true)
)
assertThat(pigeon2).isNotEqualTo(pigeon) assertThat(pigeon2).isNotEqualTo(pigeon)
assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode()) assertThat(pigeon2.hashCode()).isNotEqualTo(pigeon.hashCode())
@@ -128,7 +136,6 @@ class PObjectTest {
@Test @Test
fun `toString()`() { fun `toString()`() {
assertThat(pigeon.toString()) assertThat(pigeon.toString()).isEqualTo("test#Person { name = Pigeon; age = 42 }")
.isEqualTo("test#Person { name = Pigeon; age = 42 }")
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.io.StringWriter import java.io.StringWriter
@@ -21,14 +36,13 @@ class PcfRendererTest {
assertThat(output.trim()).isEqualTo(expected.trim()) assertThat(output.trim()).isEqualTo(expected.trim())
// TODO: make pcf a pkl subset again // TODO: make pcf a pkl subset again
//assertThatCode { evaluator.evaluateText(output) }.doesNotThrowAnyException() // assertThatCode { evaluator.evaluateText(output) }.doesNotThrowAnyException()
} }
@Test @Test
fun `rendered document ends in newline`() { fun `rendered document ends in newline`() {
val module = EvaluatorBuilder.preconfigured() val module =
.build() EvaluatorBuilder.preconfigured().build().evaluate(ModuleSource.text("foo { bar = 0 }"))
.evaluate(ModuleSource.text("foo { bar = 0 }"))
val writer = StringWriter() val writer = StringWriter()
ValueRenderers.pcf(writer, " ", false, false).renderDocument(module) ValueRenderers.pcf(writer, " ", false, false).renderDocument(module)
@@ -37,16 +51,19 @@ class PcfRendererTest {
@Test @Test
fun `rendering with and without null properties`() { fun `rendering with and without null properties`() {
val cases = listOf( val cases =
true to """ listOf(
true to
"""
baz { baz {
qux = 42 qux = 42
corge = List(null, 1337, null, "Hello World") corge = List(null, 1337, null, "Hello World")
grault = Map("garply", null, "waldo", 42, "pigeon", null) grault = Map("garply", null, "waldo", 42, "pigeon", null)
} }
""".trimIndent(), """
.trimIndent(),
false to """ false to
"""
foo = null foo = null
bar = null bar = null
baz { baz {
@@ -55,12 +72,15 @@ class PcfRendererTest {
corge = List(null, 1337, null, "Hello World") corge = List(null, 1337, null, "Hello World")
grault = Map("garply", null, "waldo", 42, "pigeon", null) grault = Map("garply", null, "waldo", 42, "pigeon", null)
} }
""".trimIndent()
)
val module = Evaluator.preconfigured().evaluate(
ModuleSource.text(
""" """
.trimIndent()
)
val module =
Evaluator.preconfigured()
.evaluate(
ModuleSource.text(
"""
foo = null foo = null
bar = null bar = null
baz { baz {
@@ -78,9 +98,10 @@ class PcfRendererTest {
["pigeon"] = null ["pigeon"] = null
} }
} }
""".trimIndent() """
) .trimIndent()
) )
)
for ((omitNullProperties, expected) in cases) { for ((omitNullProperties, expected) in cases) {
val writer = StringWriter() val writer = StringWriter()
ValueRenderers.pcf(writer, " ", omitNullProperties, false).renderDocument(module) ValueRenderers.pcf(writer, " ", omitNullProperties, false).renderDocument(module)
@@ -90,7 +111,7 @@ class PcfRendererTest {
// TODO: ada // TODO: ada
// can happen in REPL or when rendering manually constructed container // can happen in REPL or when rendering manually constructed container
/* @Test /* @Test
fun `render container with unevaluated element`() { fun `render container with unevaluated element`() {
renderer.renderValue(PObject(PClassInfo.Mapping, mapOf("one" to 1L, "two" to null))) renderer.renderValue(PObject(PClassInfo.Mapping, mapOf("one" to 1L, "two" to null)))

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.* import org.assertj.core.api.Assertions.*

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.* import org.assertj.core.api.Assertions.*

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.io.StringWriter import java.io.StringWriter
@@ -10,23 +25,31 @@ class PropertiesRendererTest {
@Test @Test
fun `render document`() { fun `render document`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val module = evaluator.evaluate(ModuleSource.modulePath("org/pkl/core/propertiesRendererTest.pkl")) val module =
evaluator.evaluate(ModuleSource.modulePath("org/pkl/core/propertiesRendererTest.pkl"))
val writer = StringWriter() val writer = StringWriter()
val renderer = ValueRenderers.properties(writer, true, false) val renderer = ValueRenderers.properties(writer, true, false)
renderer.renderDocument(module) renderer.renderDocument(module)
val output = writer.toString() val output = writer.toString()
val expected = IoUtils.readClassPathResourceAsString(javaClass, "propertiesRendererTest.properties") val expected =
IoUtils.readClassPathResourceAsString(javaClass, "propertiesRendererTest.properties")
assertThat(output).isEqualTo(expected) assertThat(output).isEqualTo(expected)
} }
@Test @Test
fun `render unsupported document values`() { fun `render unsupported document values`() {
val unsupportedValues = listOf( val unsupportedValues =
"List()", "new Listing {}", "Map()", "new Mapping {}", "Set()", listOf(
"new PropertiesRenderer {}", "new Dynamic {}" "List()",
) "new Listing {}",
"Map()",
"new Mapping {}",
"Set()",
"new PropertiesRenderer {}",
"new Dynamic {}"
)
unsupportedValues.forEach { unsupportedValues.forEach {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
@@ -39,13 +62,13 @@ class PropertiesRendererTest {
@Test @Test
fun `rendered document ends in newline`() { fun `rendered document ends in newline`() {
val module = Evaluator.preconfigured() val module = Evaluator.preconfigured().evaluate(ModuleSource.text("foo { bar = 0 }"))
.evaluate(ModuleSource.text("foo { bar = 0 }"))
for (omitNullProperties in listOf(false, true)) { for (omitNullProperties in listOf(false, true)) {
for (restrictCharSet in listOf(false, true)) { for (restrictCharSet in listOf(false, true)) {
val writer = StringWriter() val writer = StringWriter()
ValueRenderers.properties(writer, omitNullProperties, restrictCharSet).renderDocument(module) ValueRenderers.properties(writer, omitNullProperties, restrictCharSet)
.renderDocument(module)
assertThat(writer.toString()).endsWith("\n") assertThat(writer.toString()).endsWith("\n")
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.* import org.assertj.core.api.Assertions.*

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@@ -11,36 +26,30 @@ import org.pkl.core.repl.ReplServer
import org.pkl.core.resource.ResourceReaders import org.pkl.core.resource.ResourceReaders
class ReplServerTest { class ReplServerTest {
private val server = ReplServer( private val server =
SecurityManagers.defaultManager, ReplServer(
HttpClient.dummyClient(), SecurityManagers.defaultManager,
Loggers.stdErr(), HttpClient.dummyClient(),
listOf( Loggers.stdErr(),
ModuleKeyFactories.standardLibrary, listOf(
ModuleKeyFactories.classPath(this::class.java.classLoader), ModuleKeyFactories.standardLibrary,
ModuleKeyFactories.file ModuleKeyFactories.classPath(this::class.java.classLoader),
), ModuleKeyFactories.file
listOf( ),
ResourceReaders.environmentVariable(), listOf(ResourceReaders.environmentVariable(), ResourceReaders.externalProperty()),
ResourceReaders.externalProperty() mapOf("NAME1" to "value1", "NAME2" to "value2"),
), mapOf("name1" to "value1", "name2" to "value2"),
mapOf("NAME1" to "value1", "NAME2" to "value2"), null,
mapOf("name1" to "value1", "name2" to "value2"), null,
null, null,
null, "/".toPath(),
null, StackFrameTransformers.defaultTransformer
"/".toPath(), )
StackFrameTransformers.defaultTransformer
)
@Test @Test
fun `complete members of local property`() { fun `complete members of local property`() {
server.handleRequest( server.handleRequest(ReplRequest.Eval("id", "local foo = new { bar = 10 }", false, false))
ReplRequest.Eval("id", "local foo = new { bar = 10 }", false, false) val responses = server.handleRequest(ReplRequest.Completion("id", "foo"))
)
val responses = server.handleRequest(
ReplRequest.Completion("id", "foo")
)
assertThat(responses.size).isEqualTo(1) assertThat(responses.size).isEqualTo(1)
@@ -51,9 +60,18 @@ class ReplServerTest {
assertThat(completionResponse.members.toSortedSet()) assertThat(completionResponse.members.toSortedSet())
.isEqualTo( .isEqualTo(
sortedSetOf( sortedSetOf(
"default", "bar", "toList()", "toMap()", "getProperty(", "default",
"getPropertyOrNull(", "hasProperty(", "ifNonNull(", "bar",
"length()", "getClass()", "toString()", "toTyped(" "toList()",
"toMap()",
"getProperty(",
"getPropertyOrNull(",
"hasProperty(",
"ifNonNull(",
"length()",
"getClass()",
"toString()",
"toTyped("
) )
) )
} }
@@ -61,9 +79,7 @@ class ReplServerTest {
@Test @Test
fun `complete members of module import`() { fun `complete members of module import`() {
server.handleRequest(ReplRequest.Eval("id", "import \"pkl:test\"", false, false)) server.handleRequest(ReplRequest.Eval("id", "import \"pkl:test\"", false, false))
val responses = server.handleRequest( val responses = server.handleRequest(ReplRequest.Completion("id", "test"))
ReplRequest.Completion("id", "test")
)
assertThat(responses.size).isEqualTo(1) assertThat(responses.size).isEqualTo(1)
@@ -79,14 +95,11 @@ class ReplServerTest {
@Test @Test
fun `complete members of 'this' expression`() { fun `complete members of 'this' expression`() {
val responses1 = server.handleRequest( val responses1 =
ReplRequest.Eval("id", "x = 1; function f() = 3", false, false) server.handleRequest(ReplRequest.Eval("id", "x = 1; function f() = 3", false, false))
)
assertThat(responses1.size).isEqualTo(0) assertThat(responses1.size).isEqualTo(0)
val responses2 = server.handleRequest( val responses2 = server.handleRequest(ReplRequest.Completion("id", "this"))
ReplRequest.Completion("id", "this")
)
assertThat(responses2.size).isEqualTo(1) assertThat(responses2.size).isEqualTo(1)
val response = responses2[0] val response = responses2[0]
@@ -96,8 +109,18 @@ class ReplServerTest {
assertThat(completionResponse.members.toSortedSet()) assertThat(completionResponse.members.toSortedSet())
.isEqualTo( .isEqualTo(
sortedSetOf( sortedSetOf(
"output", "toDynamic()", "toMap()", "f()", "x", "ifNonNull(", "getClass()", "output",
"getProperty(", "getPropertyOrNull(", "hasProperty(", "relativePathTo(", "toString()" "toDynamic()",
"toMap()",
"f()",
"x",
"ifNonNull(",
"getClass()",
"getProperty(",
"getPropertyOrNull(",
"hasProperty(",
"relativePathTo(",
"toString()"
) )
) )
} }
@@ -156,7 +179,10 @@ class ReplServerTest {
val result2 = makeEvalRequest("""greet(42)""") val result2 = makeEvalRequest("""greet(42)""")
assertThat(result2).isEqualTo("\"Hello, 42!\"") assertThat(result2).isEqualTo("\"Hello, 42!\"")
val result3 = makeEvalRequest("function greet(name: String): String = \"Hello, \\(name)!\"; greet(\"Pigeon\") ") val result3 =
makeEvalRequest(
"function greet(name: String): String = \"Hello, \\(name)!\"; greet(\"Pigeon\") "
)
assertThat(result3).isEqualTo("\"Hello, Pigeon!\"") assertThat(result3).isEqualTo("\"Hello, Pigeon!\"")
val result4 = makeFailingEvalRequest("""greet(44)""") val result4 = makeFailingEvalRequest("""greet(44)""")
@@ -164,8 +190,7 @@ class ReplServerTest {
} }
private fun makeEvalRequest(text: String): String { private fun makeEvalRequest(text: String): String {
val responses = val responses = server.handleRequest(ReplRequest.Eval("id", text, false, false))
server.handleRequest(ReplRequest.Eval("id", text, false, false))
assertThat(responses).hasSize(1) assertThat(responses).hasSize(1)
val response = responses[0] val response = responses[0]
@@ -176,8 +201,7 @@ class ReplServerTest {
} }
private fun makeFailingEvalRequest(text: String): String { private fun makeFailingEvalRequest(text: String): String {
val responses = val responses = server.handleRequest(ReplRequest.Eval("id", text, false, false))
server.handleRequest(ReplRequest.Eval("id", text, false, false))
assertThat(responses).hasSize(1) assertThat(responses).hasSize(1)
val response = responses[0] val response = responses[0]

View File

@@ -1,13 +1,28 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.walk
import java.nio.file.Path import java.nio.file.Path
import java.util.stream.Collectors import java.util.stream.Collectors
import kotlin.io.path.extension import kotlin.io.path.extension
import kotlin.io.path.isRegularFile import kotlin.io.path.isRegularFile
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.walk
/** /**
* These tests don't assert Pkl's implementation correctness, but rather that no debugging settings * These tests don't assert Pkl's implementation correctness, but rather that no debugging settings
@@ -22,18 +37,24 @@ class RepositoryHygiene {
@Test @Test
fun `no output files exists for language snippets without an input`() { fun `no output files exists for language snippets without an input`() {
val input = snippetsFolder.resolve("input") val input = snippetsFolder.resolve("input")
val inputs = input.walk().filter { val inputs =
it.extension == "pkl" input
}.map { .walk()
val path = input.relativize(it).toString() .filter { it.extension == "pkl" }
inputRegex.replace(path, "$1$2") .map {
}.collect(Collectors.toSet()) val path = input.relativize(it).toString()
inputRegex.replace(path, "$1$2")
}
.collect(Collectors.toSet())
val output = snippetsFolder.resolve("output") val output = snippetsFolder.resolve("output")
output.walk().filter { it.isRegularFile() }.forEach { output
val out = output.relativize(it).toString() .walk()
checkOutputHasInput(inputs, out) .filter { it.isRegularFile() }
} .forEach {
val out = output.relativize(it).toString()
checkOutputHasInput(inputs, out)
}
} }
private fun checkOutputHasInput(inputs: Set<String>, output: String) { private fun checkOutputHasInput(inputs: Set<String>, output: String) {

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.net.URI import java.net.URI
@@ -12,116 +27,84 @@ import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.toPath import org.pkl.commons.toPath
class SecurityManagersTest { class SecurityManagersTest {
private val manager = SecurityManagers.standard( private val manager =
listOf(Pattern.compile("test:foo/bar")), SecurityManagers.standard(
listOf(Pattern.compile("env:FOO_BAR")), listOf(Pattern.compile("test:foo/bar")),
{ uri -> if (uri.scheme == "one") 1 else if (uri.scheme == "two") 2 else 0 }, listOf(Pattern.compile("env:FOO_BAR")),
null { uri -> if (uri.scheme == "one") 1 else if (uri.scheme == "two") 2 else 0 },
) null
)
@Test @Test
fun `checkResolveModule() - complete match`() { fun `checkResolveModule() - complete match`() {
val e = catchThrowable { val e = catchThrowable { manager.checkResolveModule(URI("test:foo/bar")) }
manager.checkResolveModule(URI("test:foo/bar"))
}
assertThat(e).doesNotThrowAnyException() assertThat(e).doesNotThrowAnyException()
} }
@Test @Test
fun `checkResolveModule() - partial match from start`() { fun `checkResolveModule() - partial match from start`() {
val e = catchThrowable { val e = catchThrowable { manager.checkResolveModule(URI("test:foo/bar/baz")) }
manager.checkResolveModule(URI("test:foo/bar/baz"))
}
assertThat(e).doesNotThrowAnyException() assertThat(e).doesNotThrowAnyException()
} }
@Test @Test
fun `checkResolveModule() - partial match not from start`() { fun `checkResolveModule() - partial match not from start`() {
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> { manager.checkResolveModule(URI("other:test:foo/bar")) }
manager.checkResolveModule(URI("other:test:foo/bar"))
}
} }
@Test @Test
fun `checkResolveModule() - no match`() { fun `checkResolveModule() - no match`() {
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> { manager.checkResolveModule(URI("other:uri")) }
manager.checkResolveModule(URI("other:uri"))
}
} }
@Test @Test
fun `checkResolveModule() - no match #2`() { fun `checkResolveModule() - no match #2`() {
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> { manager.checkResolveModule(URI("test:foo/baz")) }
manager.checkResolveModule(URI("test:foo/baz"))
}
} }
@Test @Test
fun `checkReadResource() - complete match`() { fun `checkReadResource() - complete match`() {
val e = catchThrowable { val e = catchThrowable { manager.checkReadResource(URI("env:FOO_BAR")) }
manager.checkReadResource(URI("env:FOO_BAR"))
}
assertThat(e).doesNotThrowAnyException() assertThat(e).doesNotThrowAnyException()
} }
@Test @Test
fun `checkReadResource() - partial match from start`() { fun `checkReadResource() - partial match from start`() {
val e = catchThrowable { val e = catchThrowable { manager.checkReadResource(URI("env:FOO_BAR_BAZ")) }
manager.checkReadResource(URI("env:FOO_BAR_BAZ"))
}
assertThat(e).doesNotThrowAnyException() assertThat(e).doesNotThrowAnyException()
} }
@Test @Test
fun `checkReadResource() - partial match not from start`() { fun `checkReadResource() - partial match not from start`() {
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> { manager.checkReadResource(URI("other:env:FOO_BAR")) }
manager.checkReadResource(URI("other:env:FOO_BAR"))
}
} }
@Test @Test
fun `checkReadResource() - no match`() { fun `checkReadResource() - no match`() {
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> { manager.checkReadResource(URI("other:uri")) }
manager.checkReadResource(URI("other:uri"))
}
} }
@Test @Test
fun `checkReadResource() - no match #2`() { fun `checkReadResource() - no match #2`() {
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> { manager.checkReadResource(URI("env:FOO_BAZ")) }
manager.checkReadResource(URI("env:FOO_BAZ"))
}
} }
@Test @Test
fun `checkImportModule() - same trust level`() { fun `checkImportModule() - same trust level`() {
val e = catchThrowable { val e = catchThrowable { manager.checkImportModule(URI("one:foo"), URI("one:bar")) }
manager.checkImportModule(
URI("one:foo"),
URI("one:bar")
)
}
assertThat(e).doesNotThrowAnyException() assertThat(e).doesNotThrowAnyException()
} }
@Test @Test
fun `checkImportModule() - higher trust level`() { fun `checkImportModule() - higher trust level`() {
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> {
manager.checkImportModule( manager.checkImportModule(URI("one:foo"), URI("two:bar"))
URI("one:foo"),
URI("two:bar")
)
} }
} }
@Test @Test
fun `checkImportModule() - lower trust level`() { fun `checkImportModule() - lower trust level`() {
val e = catchThrowable { val e = catchThrowable { manager.checkImportModule(URI("two:foo"), URI("one:bar")) }
manager.checkImportModule(
URI("two:foo"),
URI("one:bar")
)
}
assertThat(e).doesNotThrowAnyException() assertThat(e).doesNotThrowAnyException()
} }
@@ -154,12 +137,13 @@ class SecurityManagersTest {
val rootDir = tempDir.resolve("root") val rootDir = tempDir.resolve("root")
Files.createDirectory(rootDir) Files.createDirectory(rootDir)
val manager = SecurityManagers.standard( val manager =
listOf(Pattern.compile("file")), SecurityManagers.standard(
listOf(Pattern.compile("file")), listOf(Pattern.compile("file")),
SecurityManagers.defaultTrustLevels, listOf(Pattern.compile("file")),
rootDir SecurityManagers.defaultTrustLevels,
) rootDir
)
val path = rootDir.resolve("baz.pkl") val path = rootDir.resolve("baz.pkl")
Files.createFile(path) Files.createFile(path)
@@ -174,12 +158,13 @@ class SecurityManagersTest {
fun `can resolve modules and resources under root dir - files don't exist`() { fun `can resolve modules and resources under root dir - files don't exist`() {
val rootDir = "/foo/bar".toPath() val rootDir = "/foo/bar".toPath()
val manager = SecurityManagers.standard( val manager =
listOf(Pattern.compile("file")), SecurityManagers.standard(
listOf(Pattern.compile("file")), listOf(Pattern.compile("file")),
SecurityManagers.defaultTrustLevels, listOf(Pattern.compile("file")),
rootDir SecurityManagers.defaultTrustLevels,
) rootDir
)
manager.checkResolveModule(Path.of("/foo/bar/baz.pkl").toUri()) manager.checkResolveModule(Path.of("/foo/bar/baz.pkl").toUri())
manager.checkReadResource(Path.of("/foo/bar/baz.pkl").toUri()) manager.checkReadResource(Path.of("/foo/bar/baz.pkl").toUri())
@@ -189,48 +174,44 @@ class SecurityManagersTest {
} }
@Test @Test
fun `cannot resolve modules and resources outside root dir - files do exist`(@TempDir tempDir: Path) { fun `cannot resolve modules and resources outside root dir - files do exist`(
@TempDir tempDir: Path
) {
val rootDir = tempDir.resolve("root") val rootDir = tempDir.resolve("root")
Files.createDirectory(rootDir) Files.createDirectory(rootDir)
val manager = SecurityManagers.standard( val manager =
listOf(Pattern.compile("file")), SecurityManagers.standard(
listOf(Pattern.compile("file")), listOf(Pattern.compile("file")),
SecurityManagers.defaultTrustLevels, listOf(Pattern.compile("file")),
rootDir SecurityManagers.defaultTrustLevels,
) rootDir
)
val path = rootDir.resolve("../baz.pkl") val path = rootDir.resolve("../baz.pkl")
Files.createFile(path) Files.createFile(path)
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> { manager.checkResolveModule(path.toUri()) }
manager.checkResolveModule(path.toUri()) assertThrows<SecurityManagerException> { manager.checkReadResource(path.toUri()) }
}
assertThrows<SecurityManagerException> {
manager.checkReadResource(path.toUri())
}
val symlink = rootDir.resolve("qux") val symlink = rootDir.resolve("qux")
Files.createSymbolicLink(symlink, tempDir) Files.createSymbolicLink(symlink, tempDir)
val path2 = symlink.resolve("baz2.pkl") val path2 = symlink.resolve("baz2.pkl")
Files.createFile(path2) Files.createFile(path2)
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> { manager.checkResolveModule(path2.toUri()) }
manager.checkResolveModule(path2.toUri()) assertThrows<SecurityManagerException> { manager.checkReadResource(path2.toUri()) }
}
assertThrows<SecurityManagerException> {
manager.checkReadResource(path2.toUri())
}
} }
@Test @Test
fun `cannot resolve modules and resources outside root dir - files don't exist`() { fun `cannot resolve modules and resources outside root dir - files don't exist`() {
val rootDir = "/foo/bar".toPath() val rootDir = "/foo/bar".toPath()
val manager = SecurityManagers.standard( val manager =
listOf(Pattern.compile("file")), SecurityManagers.standard(
listOf(Pattern.compile("file")), listOf(Pattern.compile("file")),
SecurityManagers.defaultTrustLevels, listOf(Pattern.compile("file")),
rootDir SecurityManagers.defaultTrustLevels,
) rootDir
)
assertThrows<SecurityManagerException> { assertThrows<SecurityManagerException> {
manager.checkResolveModule(Path.of("/foo/baz.pkl").toUri()) manager.checkResolveModule(Path.of("/foo/baz.pkl").toUri())

View File

@@ -1,9 +1,24 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.pkl.commons.test.PackageServer
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.commons.test.PackageServer
import org.pkl.core.http.HttpClient import org.pkl.core.http.HttpClient
class StackFrameTransformersTest { class StackFrameTransformersTest {
@@ -13,21 +28,13 @@ class StackFrameTransformersTest {
fun replacePackageUriWithSourceCodeUrl() { fun replacePackageUriWithSourceCodeUrl() {
PackageServer().use { server -> PackageServer().use { server ->
val httpClient = HttpClient.builder().setTestPort(server.port).build() val httpClient = HttpClient.builder().setTestPort(server.port).build()
EvaluatorBuilder.preconfigured() EvaluatorBuilder.preconfigured().setHttpClient(httpClient).build().use {
.setHttpClient(httpClient) val frame =
.build().use { StackFrame("package://localhost:0/birds@0.5.0#/Bird.pkl", null, listOf(), 1, 1, 2, 2)
val frame = StackFrame( val transformed = StackFrameTransformers.replacePackageUriWithSourceCodeUrl.apply(frame)
"package://localhost:0/birds@0.5.0#/Bird.pkl", assertThat(transformed.moduleUri)
null, .isEqualTo("https://example.com/birds/v0.5.0/blob/Bird.pkl#L1-L2")
listOf(), }
1,
1,
2,
2)
val transformed =
StackFrameTransformers.replacePackageUriWithSourceCodeUrl.apply(frame)
assertThat(transformed.moduleUri).isEqualTo("https://example.com/birds/v0.5.0/blob/Bird.pkl#L1-L2")
}
} }
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@@ -37,23 +52,16 @@ class VersionTest {
@Test @Test
fun `parse invalid version`() { fun `parse invalid version`() {
assertThat(Version.parseOrNull("not a version number")) assertThat(Version.parseOrNull("not a version number")).isNull()
.isNull()
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> { Version.parse("not a version number") }
Version.parse("not a version number")
}
} }
@Test @Test
fun `parse too large version`() { fun `parse too large version`() {
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> { Version.parse("not a version number") }
Version.parse("not a version number")
}
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> { Version.parse("999999999999999.0.0") }
Version.parse("999999999999999.0.0")
}
} }
@Test @Test
@@ -66,137 +74,80 @@ class VersionTest {
@Test @Test
fun withMethods() { fun withMethods() {
val version = Version.parse("0.0.0") val version =
.withMajor(1) Version.parse("0.0.0")
.withMinor(2) .withMajor(1)
.withPatch(3) .withMinor(2)
.withPreRelease("rc.1") .withPatch(3)
.withBuild("456.789") .withPreRelease("rc.1")
.withBuild("456.789")
assertThat(version).isEqualTo(Version.parse("1.2.3-rc.1+456.789")) assertThat(version).isEqualTo(Version.parse("1.2.3-rc.1+456.789"))
val version2 = Version.parse("0.0.0") val version2 =
.withBuild("456.789") Version.parse("0.0.0")
.withPreRelease("rc.1") .withBuild("456.789")
.withPatch(3) .withPreRelease("rc.1")
.withMinor(2) .withPatch(3)
.withMajor(1) .withMinor(2)
.withMajor(1)
assertThat(version2).isEqualTo(version) assertThat(version2).isEqualTo(version)
} }
@Test @Test
fun `compareTo()`() { fun `compareTo()`() {
assertThat( assertThat(Version(1, 2, 3, null, null).compareTo(Version(1, 2, 3, null, null))).isEqualTo(0)
Version(1, 2, 3, null, null).compareTo( assertThat(Version(1, 2, 3, "SNAPSHOT", null).compareTo(Version(1, 2, 3, "SNAPSHOT", null)))
Version(1, 2, 3, null, null) .isEqualTo(0)
) assertThat(Version(1, 2, 3, "alpha", null).compareTo(Version(1, 2, 3, "alpha", null)))
).isEqualTo(0) .isEqualTo(0)
assertThat( assertThat(Version(1, 2, 3, "alpha", null).compareTo(Version(1, 2, 3, "alpha", "build123")))
Version(1, 2, 3, "SNAPSHOT", null).compareTo( .isEqualTo(0)
Version(1, 2, 3, "SNAPSHOT", null)
)
).isEqualTo(0)
assertThat(
Version(1, 2, 3, "alpha", null).compareTo(
Version(1, 2, 3, "alpha", null)
)
).isEqualTo(0)
assertThat(
Version(1, 2, 3, "alpha", null).compareTo(
Version(1, 2, 3, "alpha", "build123")
)
).isEqualTo(0)
assertThat( assertThat(Version(1, 2, 3, null, null).compareTo(Version(2, 2, 3, null, null))).isLessThan(0)
Version(1, 2, 3, null, null).compareTo( assertThat(Version(1, 2, 3, null, null).compareTo(Version(1, 3, 3, null, null))).isLessThan(0)
Version(2, 2, 3, null, null) assertThat(Version(1, 2, 3, null, null).compareTo(Version(1, 2, 4, null, null))).isLessThan(0)
)
).isLessThan(0)
assertThat(
Version(1, 2, 3, null, null).compareTo(
Version(1, 3, 3, null, null)
)
).isLessThan(0)
assertThat(
Version(1, 2, 3, null, null).compareTo(
Version(1, 2, 4, null, null)
)
).isLessThan(0)
assertThat( assertThat(Version(2, 2, 3, null, null).compareTo(Version(1, 2, 3, null, null)))
Version(2, 2, 3, null, null).compareTo( .isGreaterThan(0)
Version(1, 2, 3, null, null) assertThat(Version(1, 3, 3, null, null).compareTo(Version(1, 2, 3, null, null)))
) .isGreaterThan(0)
).isGreaterThan(0) assertThat(Version(1, 2, 4, null, null).compareTo(Version(1, 2, 3, null, null)))
assertThat( .isGreaterThan(0)
Version(1, 3, 3, null, null).compareTo(
Version(1, 2, 3, null, null)
)
).isGreaterThan(0)
assertThat(
Version(1, 2, 4, null, null).compareTo(
Version(1, 2, 3, null, null)
)
).isGreaterThan(0)
assertThat( assertThat(Version(1, 2, 3, "SNAPSHOT", null).compareTo(Version(1, 2, 3, null, null)))
Version(1, 2, 3, "SNAPSHOT", null).compareTo( .isLessThan(0)
Version(1, 2, 3, null, null) assertThat(Version(1, 2, 3, "alpha", null).compareTo(Version(1, 2, 3, "beta", null)))
) .isLessThan(0)
).isLessThan(0) assertThat(Version(1, 2, 3, "alpha", "build123").compareTo(Version(1, 2, 3, "beta", null)))
assertThat( .isLessThan(0)
Version(1, 2, 3, "alpha", null).compareTo(
Version(1, 2, 3, "beta", null)
)
).isLessThan(0)
assertThat(
Version(1, 2, 3, "alpha", "build123").compareTo(
Version(1, 2, 3, "beta", null)
)
).isLessThan(0)
assertThat( assertThat(Version(1, 2, 3, null, null).compareTo(Version(1, 2, 3, "SNAPSHOT", null)))
Version(1, 2, 3, null, null).compareTo( .isGreaterThan(0)
Version(1, 2, 3, "SNAPSHOT", null) assertThat(Version(1, 2, 3, "beta", null).compareTo(Version(1, 2, 3, "alpha", "build123")))
) .isGreaterThan(0)
).isGreaterThan(0)
assertThat(
Version(1, 2, 3, "beta", null).compareTo(
Version(1, 2, 3, "alpha", "build123")
)
).isGreaterThan(0)
} }
@Test @Test
fun `compare version with too large numeric pre-release identifier`() { fun `compare version with too large numeric pre-release identifier`() {
// error is deferred until compareTo(), but should be good enough // error is deferred until compareTo(), but should be good enough
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> {
Version(1, 2, 3, "999", null).compareTo( Version(1, 2, 3, "999", null).compareTo(Version(1, 2, 3, "9999999999999999999", null))
Version(1, 2, 3, "9999999999999999999", null)
)
} }
} }
@Test @Test
fun `equals()`() { fun `equals()`() {
assertThat(Version(1, 2, 3, null, null)) assertThat(Version(1, 2, 3, null, null)).isEqualTo(Version(1, 2, 3, null, null))
.isEqualTo(Version(1, 2, 3, null, null)) assertThat(Version(1, 2, 3, "SNAPSHOT", null)).isEqualTo(Version(1, 2, 3, "SNAPSHOT", null))
assertThat(Version(1, 2, 3, "SNAPSHOT", null)) assertThat(Version(1, 2, 3, "alpha", null)).isEqualTo(Version(1, 2, 3, "alpha", null))
.isEqualTo(Version(1, 2, 3, "SNAPSHOT", null)) assertThat(Version(1, 2, 3, "beta", "build123")).isEqualTo(Version(1, 2, 3, "beta", "build456"))
assertThat(Version(1, 2, 3, "alpha", null))
.isEqualTo(Version(1, 2, 3, "alpha", null))
assertThat(Version(1, 2, 3, "beta", "build123"))
.isEqualTo(Version(1, 2, 3, "beta", "build456"))
assertThat(Version(1, 3, 3, null, null)) assertThat(Version(1, 3, 3, null, null)).isNotEqualTo(Version(1, 2, 3, null, null))
.isNotEqualTo(Version(1, 2, 3, null, null)) assertThat(Version(1, 2, 4, null, null)).isNotEqualTo(Version(1, 2, 3, null, null))
assertThat(Version(1, 2, 4, null, null)) assertThat(Version(1, 2, 3, "SNAPSHOT", null)).isNotEqualTo(Version(1, 2, 3, null, null))
.isNotEqualTo(Version(1, 2, 3, null, null)) assertThat(Version(1, 2, 3, "beta", null)).isNotEqualTo(Version(1, 2, 3, "alpha", null))
assertThat(Version(1, 2, 3, "SNAPSHOT", null))
.isNotEqualTo(Version(1, 2, 3, null, null))
assertThat(Version(1, 2, 3, "beta", null))
.isNotEqualTo(Version(1, 2, 3, "alpha", null))
} }
@Test @Test

View File

@@ -1,12 +1,27 @@
/**
* Copyright © 2024 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.core package org.pkl.core
import java.io.StringWriter import java.io.StringWriter
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.core.util.IoUtils
import org.snakeyaml.engine.v2.api.Load import org.snakeyaml.engine.v2.api.Load
import org.snakeyaml.engine.v2.api.LoadSettings import org.snakeyaml.engine.v2.api.LoadSettings
import org.pkl.core.util.IoUtils
class YamlRendererTest { class YamlRendererTest {
@Test @Test
@@ -21,15 +36,17 @@ class YamlRendererTest {
val expected = IoUtils.readClassPathResourceAsString(javaClass, "rendererTest.yaml") val expected = IoUtils.readClassPathResourceAsString(javaClass, "rendererTest.yaml")
assertThat(output.trim()).isEqualTo(expected.trim()) assertThat(output.trim()).isEqualTo(expected.trim())
assertThatCode { Load(LoadSettings.builder().build()).loadFromString(output) }.doesNotThrowAnyException() assertThatCode { Load(LoadSettings.builder().build()).loadFromString(output) }
.doesNotThrowAnyException()
} }
@Test @Test
fun `render YAML stream`() { fun `render YAML stream`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val module = evaluator.evaluate( val module =
ModuleSource.text( evaluator.evaluate(
""" ModuleSource.text(
"""
stream = new Listing { stream = new Listing {
new Dynamic { new Dynamic {
name = "Pigeon" name = "Pigeon"
@@ -48,9 +65,10 @@ class YamlRendererTest {
"Blue Rock Ltd." "Blue Rock Ltd."
12345 12345
} }
""".trimIndent() """
.trimIndent()
)
) )
)
val writer = StringWriter() val writer = StringWriter()
val renderer = ValueRenderers.yaml(writer, 2, true, true) val renderer = ValueRenderers.yaml(writer, 2, true, true)
@@ -58,8 +76,9 @@ class YamlRendererTest {
renderer.renderDocument(module.getProperty("stream")) renderer.renderDocument(module.getProperty("stream"))
val output = writer.toString() val output = writer.toString()
assertThat(output.trim()).isEqualTo( assertThat(output.trim())
""" .isEqualTo(
"""
name: Pigeon name: Pigeon
age: 42 age: 42
--- ---
@@ -72,14 +91,14 @@ class YamlRendererTest {
three: 3 three: 3
--- Blue Rock Ltd. --- Blue Rock Ltd.
--- 12345 --- 12345
""".trimIndent() """
) .trimIndent()
)
} }
@Test @Test
fun `rendered document ends in newline`() { fun `rendered document ends in newline`() {
val module = Evaluator.preconfigured() val module = Evaluator.preconfigured().evaluate(ModuleSource.text("foo { bar = 0 }"))
.evaluate(ModuleSource.text("foo { bar = 0 }"))
for (omitNullProperties in listOf(false, true)) { for (omitNullProperties in listOf(false, true)) {
for (isStream in listOf(false, true)) { for (isStream in listOf(false, true)) {
@@ -93,18 +112,20 @@ class YamlRendererTest {
@Test @Test
fun `render truthy strings, octals and number-like strings`() { fun `render truthy strings, octals and number-like strings`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val module = evaluator.evaluate( val module =
ModuleSource.text( evaluator.evaluate(
""" ModuleSource.text(
"""
num1 = "50" num1 = "50"
num2 = "50.123" num2 = "50.123"
`60.123` = "60.123" `60.123` = "60.123"
yes = "yes" yes = "yes"
truth = "true" truth = "true"
octalNumber = "0777" octalNumber = "0777"
""".trimIndent() """
.trimIndent()
)
) )
)
val writer = StringWriter() val writer = StringWriter()
val renderer = ValueRenderers.yaml(writer, 2, true, false) val renderer = ValueRenderers.yaml(writer, 2, true, false)
@@ -112,15 +133,17 @@ class YamlRendererTest {
renderer.renderDocument(module) renderer.renderDocument(module)
val output = writer.toString() val output = writer.toString()
assertThat(output.trim()).isEqualTo( assertThat(output.trim())
""" .isEqualTo(
"""
num1: '50' num1: '50'
num2: '50.123' num2: '50.123'
'60.123': '60.123' '60.123': '60.123'
'yes': 'yes' 'yes': 'yes'
truth: 'true' truth: 'true'
octalNumber: '0777' octalNumber: '0777'
""".trimIndent() """
) .trimIndent()
)
} }
} }

View File

@@ -1,15 +1,31 @@
/**
* Copyright © 2024 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.core.ast.builder package org.pkl.core.ast.builder
import org.pkl.core.SecurityManagers import java.net.URI
import org.pkl.core.module.ModuleKeys
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.net.URI import org.pkl.core.SecurityManagers
import org.pkl.core.module.ModuleKeys
class ImportsAndReadsParserTest { class ImportsAndReadsParserTest {
@Test @Test
fun parse() { fun parse() {
val moduleText = """ val moduleText =
"""
amends "foo.pkl" amends "foo.pkl"
import "bar.pkl" import "bar.pkl"
@@ -27,18 +43,23 @@ class ImportsAndReadsParserTest {
} }
} }
} }
""".trimIndent() """
.trimIndent()
val moduleKey = ModuleKeys.synthetic(URI("repl:text"), moduleText) val moduleKey = ModuleKeys.synthetic(URI("repl:text"), moduleText)
val imports = ImportsAndReadsParser.parse(moduleKey, moduleKey.resolve(SecurityManagers.defaultManager)) val imports =
assertThat(imports?.map { it.first }).hasSameElementsAs(listOf( ImportsAndReadsParser.parse(moduleKey, moduleKey.resolve(SecurityManagers.defaultManager))
"foo.pkl", assertThat(imports?.map { it.first })
"bar.pkl", .hasSameElementsAs(
"bazzy/buz.pkl", listOf(
"qux.pkl", "foo.pkl",
"qux/*.pkl", "bar.pkl",
"/some/dir/chown.txt", "bazzy/buz.pkl",
"/some/dir/chowner.txt", "qux.pkl",
"/some/dir/*.txt" "qux/*.pkl",
)) "/some/dir/chown.txt",
"/some/dir/chowner.txt",
"/some/dir/*.txt"
)
)
} }
} }

View File

@@ -1,11 +1,26 @@
/**
* Copyright © 2024 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.core.http package org.pkl.core.http
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
import java.net.URI import java.net.URI
import java.net.http.HttpRequest import java.net.http.HttpRequest
import java.net.http.HttpResponse import java.net.http.HttpResponse
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
class DummyHttpClientTest { class DummyHttpClientTest {
@Test @Test
@@ -13,13 +28,9 @@ class DummyHttpClientTest {
val client = HttpClient.dummyClient() val client = HttpClient.dummyClient()
val request = HttpRequest.newBuilder(URI("https://example.com")).build() val request = HttpRequest.newBuilder(URI("https://example.com")).build()
assertThrows<AssertionError> { assertThrows<AssertionError> { client.send(request, HttpResponse.BodyHandlers.discarding()) }
client.send(request, HttpResponse.BodyHandlers.discarding())
}
assertThrows<AssertionError> { assertThrows<AssertionError> { client.send(request, HttpResponse.BodyHandlers.discarding()) }
client.send(request, HttpResponse.BodyHandlers.discarding())
}
} }
@Test @Test

View File

@@ -1,12 +1,20 @@
/**
* Copyright © 2024 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.core.http package org.pkl.core.http
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.test.FileTestUtils
import org.pkl.core.Release
import java.net.URI import java.net.URI
import java.net.http.HttpRequest import java.net.http.HttpRequest
import java.net.http.HttpResponse import java.net.http.HttpResponse
@@ -14,19 +22,25 @@ import java.nio.file.Path
import java.time.Duration import java.time.Duration
import kotlin.io.path.createFile import kotlin.io.path.createFile
import kotlin.io.path.readBytes import kotlin.io.path.readBytes
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.test.FileTestUtils
import org.pkl.core.Release
class HttpClientTest { class HttpClientTest {
@Test @Test
fun `can build default client`() { fun `can build default client`() {
val client = assertDoesNotThrow { val client = assertDoesNotThrow { HttpClient.builder().build() }
HttpClient.builder().build()
}
assertThat(client).isInstanceOf(RequestRewritingClient::class.java) assertThat(client).isInstanceOf(RequestRewritingClient::class.java)
client as RequestRewritingClient client as RequestRewritingClient
val release = Release.current() val release = Release.current()
assertThat(client.userAgent).isEqualTo("Pkl/${release.version()} (${release.os()}; ${release.flavor()})") assertThat(client.userAgent)
.isEqualTo("Pkl/${release.version()} (${release.os()}; ${release.flavor()})")
assertThat(client.requestTimeout).isEqualTo(Duration.ofSeconds(60)) assertThat(client.requestTimeout).isEqualTo(Duration.ofSeconds(60))
assertThat(client.delegate).isInstanceOf(JdkHttpClient::class.java) assertThat(client.delegate).isInstanceOf(JdkHttpClient::class.java)
@@ -37,11 +51,12 @@ class HttpClientTest {
@Test @Test
fun `can build custom client`() { fun `can build custom client`() {
val client = HttpClient.builder() val client =
.setUserAgent("Agent 1") HttpClient.builder()
.setRequestTimeout(Duration.ofHours(86)) .setUserAgent("Agent 1")
.setConnectTimeout(Duration.ofMinutes(42)) .setRequestTimeout(Duration.ofHours(86))
.build() as RequestRewritingClient .setConnectTimeout(Duration.ofMinutes(42))
.build() as RequestRewritingClient
assertThat(client.userAgent).isEqualTo("Agent 1") assertThat(client.userAgent).isEqualTo("Agent 1")
assertThat(client.requestTimeout).isEqualTo(Duration.ofHours(86)) assertThat(client.requestTimeout).isEqualTo(Duration.ofHours(86))
@@ -68,27 +83,24 @@ class HttpClientTest {
fun `certificate file cannot be empty`(@TempDir tempDir: Path) { fun `certificate file cannot be empty`(@TempDir tempDir: Path) {
val file = tempDir.resolve("certs.pem").createFile() val file = tempDir.resolve("certs.pem").createFile()
val e = assertThrows<HttpClientInitException> { val e =
HttpClient.builder().addCertificates(file).build() assertThrows<HttpClientInitException> { HttpClient.builder().addCertificates(file).build() }
}
assertThat(e).hasMessageContaining("empty") assertThat(e).hasMessageContaining("empty")
} }
@Test @Test
fun `can load built-in certificates`() { fun `can load built-in certificates`() {
assertDoesNotThrow { assertDoesNotThrow { HttpClient.builder().build() }
HttpClient.builder().build()
}
} }
@Test @Test
fun `can be closed multiple times`() { fun `can be closed multiple times`() {
val client = HttpClient.builder().build() val client = HttpClient.builder().build()
assertDoesNotThrow { assertDoesNotThrow {
client.close() client.close()
client.close() client.close()
} }
} }

View File

@@ -1,37 +1,46 @@
/**
* Copyright © 2024 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.core.http package org.pkl.core.http
import java.net.URI
import java.net.http.HttpRequest
import java.net.http.HttpResponse.BodyHandlers
import java.nio.file.Path
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.createTempFile import org.pkl.commons.createTempFile
import org.pkl.commons.writeString import org.pkl.commons.writeString
import java.net.URI
import java.net.http.HttpRequest
import java.net.http.HttpResponse.BodyHandlers
import java.nio.file.Path
class LazyHttpClientTest { class LazyHttpClientTest {
@Test @Test
fun `builds underlying client on first send`(@TempDir tempDir: Path) { fun `builds underlying client on first send`(@TempDir tempDir: Path) {
val certFile = tempDir.resolve("cert.pem").apply { writeString("broken") } val certFile = tempDir.resolve("cert.pem").apply { writeString("broken") }
val client = HttpClient.builder() val client = HttpClient.builder().addCertificates(certFile).buildLazily()
.addCertificates(certFile)
.buildLazily()
val request = HttpRequest.newBuilder(URI("https://example.com")).build() val request = HttpRequest.newBuilder(URI("https://example.com")).build()
assertThrows<HttpClientInitException> { assertThrows<HttpClientInitException> { client.send(request, BodyHandlers.discarding()) }
client.send(request, BodyHandlers.discarding())
}
} }
@Test @Test
fun `does not build underlying client unnecessarily`(@TempDir tempDir: Path) { fun `does not build underlying client unnecessarily`(@TempDir tempDir: Path) {
val certFile = tempDir.createTempFile().apply { writeString("broken") } val certFile = tempDir.createTempFile().apply { writeString("broken") }
val client = HttpClient.builder() val client = HttpClient.builder().addCertificates(certFile).buildLazily()
.addCertificates(certFile)
.buildLazily()
assertDoesNotThrow { assertDoesNotThrow {
client.close() client.close()
client.close() client.close()

View File

@@ -1,9 +1,24 @@
/**
* Copyright © 2024 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.core.http package org.pkl.core.http
import java.net.URI
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.net.URI
@Suppress("HttpUrlsUsage") @Suppress("HttpUrlsUsage")
class NoProxyRuleTest { class NoProxyRuleTest {
@@ -44,7 +59,7 @@ class NoProxyRuleTest {
assertFalse(noProxyRule.matches(URI("https://ooofoo.com"))) assertFalse(noProxyRule.matches(URI("https://ooofoo.com")))
assertFalse(noProxyRule.matches(URI("pkl:foo.com"))) assertFalse(noProxyRule.matches(URI("pkl:foo.com")))
} }
@Test @Test
fun `hostname matching, with port`() { fun `hostname matching, with port`() {
val noProxyRule = NoProxyRule("foo.com:5000") val noProxyRule = NoProxyRule("foo.com:5000")
@@ -52,7 +67,7 @@ class NoProxyRuleTest {
assertFalse(noProxyRule.matches(URI("https://foo.com"))) assertFalse(noProxyRule.matches(URI("https://foo.com")))
assertFalse(noProxyRule.matches(URI("https://foo.com:3000"))) assertFalse(noProxyRule.matches(URI("https://foo.com:3000")))
} }
@Test @Test
fun `ipv4 address literal matching`() { fun `ipv4 address literal matching`() {
val noProxyRule = NoProxyRule("192.168.1.1") val noProxyRule = NoProxyRule("192.168.1.1")
@@ -95,7 +110,7 @@ class NoProxyRuleTest {
assertFalse(noProxyRule.matches(URI("https://[::2]"))) assertFalse(noProxyRule.matches(URI("https://[::2]")))
assertFalse(noProxyRule.matches(URI("https://[::2]:5000"))) assertFalse(noProxyRule.matches(URI("https://[::2]:5000")))
} }
@Test @Test
fun `ipv4 port from protocol`() { fun `ipv4 port from protocol`() {
val noProxyRuleHttp = NoProxyRule("192.168.1.1:80") val noProxyRuleHttp = NoProxyRule("192.168.1.1:80")
@@ -104,7 +119,7 @@ class NoProxyRuleTest {
assertTrue(noProxyRuleHttp.matches(URI("https://192.168.1.1:80"))) assertTrue(noProxyRuleHttp.matches(URI("https://192.168.1.1:80")))
assertFalse(noProxyRuleHttp.matches(URI("https://192.168.1.1"))) assertFalse(noProxyRuleHttp.matches(URI("https://192.168.1.1")))
assertFalse(noProxyRuleHttp.matches(URI("https://192.168.1.1:5000"))) assertFalse(noProxyRuleHttp.matches(URI("https://192.168.1.1:5000")))
val noProxyRuleHttps = NoProxyRule("192.168.1.1:443") val noProxyRuleHttps = NoProxyRule("192.168.1.1:443")
assertTrue(noProxyRuleHttps.matches(URI("https://192.168.1.1"))) assertTrue(noProxyRuleHttps.matches(URI("https://192.168.1.1")))
assertTrue(noProxyRuleHttps.matches(URI("http://192.168.1.1:443"))) assertTrue(noProxyRuleHttps.matches(URI("http://192.168.1.1:443")))

View File

@@ -1,12 +1,27 @@
/**
* Copyright © 2024 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.core.http package org.pkl.core.http
import org.pkl.commons.test.FakeHttpResponse
import java.net.http.HttpRequest import java.net.http.HttpRequest
import java.net.http.HttpResponse import java.net.http.HttpResponse
import org.pkl.commons.test.FakeHttpResponse
class RequestCapturingClient : HttpClient { class RequestCapturingClient : HttpClient {
lateinit var request: HttpRequest lateinit var request: HttpRequest
override fun <T : Any> send( override fun <T : Any> send(
request: HttpRequest, request: HttpRequest,
responseBodyHandler: HttpResponse.BodyHandler<T> responseBodyHandler: HttpResponse.BodyHandler<T>

View File

@@ -1,14 +1,29 @@
/**
* Copyright © 2024 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.core.http package org.pkl.core.http
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatList
import org.junit.jupiter.api.Test
import java.net.URI import java.net.URI
import java.net.http.HttpClient as JdkHttpClient
import java.net.http.HttpRequest import java.net.http.HttpRequest
import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpRequest.BodyPublishers
import java.net.http.HttpResponse.BodyHandlers import java.net.http.HttpResponse.BodyHandlers
import java.time.Duration import java.time.Duration
import java.net.http.HttpClient as JdkHttpClient import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatList
import org.junit.jupiter.api.Test
class RequestRewritingClientTest { class RequestRewritingClientTest {
private val captured = RequestCapturingClient() private val captured = RequestCapturingClient()
@@ -19,17 +34,18 @@ class RequestRewritingClientTest {
@Test @Test
fun `fills in missing User-Agent header`() { fun `fills in missing User-Agent header`() {
client.send(exampleRequest, BodyHandlers.discarding()) client.send(exampleRequest, BodyHandlers.discarding())
assertThatList(captured.request.headers().allValues("User-Agent")).containsOnly("Pkl") assertThatList(captured.request.headers().allValues("User-Agent")).containsOnly("Pkl")
} }
@Test @Test
fun `overrides existing User-Agent headers`() { fun `overrides existing User-Agent headers`() {
val request = HttpRequest.newBuilder(exampleUri) val request =
.header("User-Agent", "Agent 1") HttpRequest.newBuilder(exampleUri)
.header("User-Agent", "Agent 2") .header("User-Agent", "Agent 1")
.build() .header("User-Agent", "Agent 2")
.build()
client.send(request, BodyHandlers.discarding()) client.send(request, BodyHandlers.discarding())
assertThatList(captured.request.headers().allValues("User-Agent")).containsOnly("Pkl") assertThatList(captured.request.headers().allValues("User-Agent")).containsOnly("Pkl")
@@ -38,16 +54,14 @@ class RequestRewritingClientTest {
@Test @Test
fun `fills in missing request timeout`() { fun `fills in missing request timeout`() {
client.send(exampleRequest, BodyHandlers.discarding()) client.send(exampleRequest, BodyHandlers.discarding())
assertThat(captured.request.timeout()).hasValue(Duration.ofSeconds(42)) assertThat(captured.request.timeout()).hasValue(Duration.ofSeconds(42))
} }
@Test @Test
fun `leaves existing request timeout intact`() { fun `leaves existing request timeout intact`() {
val request = HttpRequest.newBuilder(exampleUri) val request = HttpRequest.newBuilder(exampleUri).timeout(Duration.ofMinutes(33)).build()
.timeout(Duration.ofMinutes(33))
.build()
client.send(request, BodyHandlers.discarding()) client.send(request, BodyHandlers.discarding())
assertThat(captured.request.timeout()).hasValue(Duration.ofMinutes(33)) assertThat(captured.request.timeout()).hasValue(Duration.ofMinutes(33))
@@ -62,12 +76,10 @@ class RequestRewritingClientTest {
@Test @Test
fun `leaves existing HTTP version intact`() { fun `leaves existing HTTP version intact`() {
val request = HttpRequest.newBuilder(exampleUri) val request = HttpRequest.newBuilder(exampleUri).version(JdkHttpClient.Version.HTTP_1_1).build()
.version(JdkHttpClient.Version.HTTP_1_1)
.build()
client.send(request, BodyHandlers.discarding()) client.send(request, BodyHandlers.discarding())
assertThat(captured.request.version()).hasValue(JdkHttpClient.Version.HTTP_1_1) assertThat(captured.request.version()).hasValue(JdkHttpClient.Version.HTTP_1_1)
} }
@@ -79,30 +91,26 @@ class RequestRewritingClientTest {
assertThat(captured.request.method()).isEqualTo("GET") assertThat(captured.request.method()).isEqualTo("GET")
} }
@Test @Test
fun `leaves explicit method intact`() { fun `leaves explicit method intact`() {
val request = HttpRequest.newBuilder(exampleUri) val request = HttpRequest.newBuilder(exampleUri).DELETE().build()
.DELETE()
.build()
client.send(request, BodyHandlers.discarding()) client.send(request, BodyHandlers.discarding())
assertThat(captured.request.method()).isEqualTo("DELETE") assertThat(captured.request.method()).isEqualTo("DELETE")
} }
@Test @Test
fun `leaves body publisher intact`() { fun `leaves body publisher intact`() {
val publisher = BodyPublishers.ofString("body") val publisher = BodyPublishers.ofString("body")
val request = HttpRequest.newBuilder(exampleUri) val request = HttpRequest.newBuilder(exampleUri).PUT(publisher).build()
.PUT(publisher)
.build()
client.send(request, BodyHandlers.discarding()) client.send(request, BodyHandlers.discarding())
assertThat(captured.request.bodyPublisher().get()).isSameAs(publisher) assertThat(captured.request.bodyPublisher().get()).isSameAs(publisher)
} }
@Test @Test
fun `rewrites port 0 if test port is set`() { fun `rewrites port 0 if test port is set`() {
val captured = RequestCapturingClient() val captured = RequestCapturingClient()
@@ -113,15 +121,13 @@ class RequestRewritingClientTest {
assertThat(captured.request.uri().port).isEqualTo(5000) assertThat(captured.request.uri().port).isEqualTo(5000)
} }
@Test @Test
fun `leaves port 0 intact if no test port is set`() { fun `leaves port 0 intact if no test port is set`() {
val request = HttpRequest.newBuilder(URI("https://example.com:0")).build() val request = HttpRequest.newBuilder(URI("https://example.com:0")).build()
client.send(request, BodyHandlers.discarding()) client.send(request, BodyHandlers.discarding())
assertThat(captured.request.uri().port).isEqualTo(0) assertThat(captured.request.uri().port).isEqualTo(0)
} }
} }

View File

@@ -1,5 +1,24 @@
/**
* Copyright © 2024 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.core.module package org.pkl.core.module
import java.net.URI
import java.nio.file.Path
import kotlin.io.path.createDirectories
import kotlin.io.path.outputStream
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.api.io.TempDir
@@ -7,10 +26,6 @@ import org.pkl.commons.createParentDirectories
import org.pkl.commons.toPath import org.pkl.commons.toPath
import org.pkl.commons.writeString import org.pkl.commons.writeString
import org.pkl.core.SecurityManagers import org.pkl.core.SecurityManagers
import java.net.URI
import java.nio.file.Path
import kotlin.io.path.createDirectories
import kotlin.io.path.outputStream
class ModuleKeyFactoriesTest { class ModuleKeyFactoriesTest {
@Test @Test
@@ -78,9 +93,7 @@ class ModuleKeyFactoriesTest {
fun `module path - jar files`(@TempDir tempDir: Path) { fun `module path - jar files`(@TempDir tempDir: Path) {
val jarFile = tempDir.resolve("test.jar") val jarFile = tempDir.resolve("test.jar")
jarFile.outputStream().use { outStream -> jarFile.outputStream().use { outStream ->
javaClass.getResourceAsStream("test.jar")!!.use { inStream -> javaClass.getResourceAsStream("test.jar")!!.use { inStream -> inStream.copyTo(outStream) }
inStream.copyTo(outStream)
}
} }
val factory = ModuleKeyFactories.modulePath(ModulePathResolver(listOf(jarFile))) val factory = ModuleKeyFactories.modulePath(ModulePathResolver(listOf(jarFile)))

View File

@@ -1,19 +1,34 @@
/**
* Copyright © 2024 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.core.module package org.pkl.core.module
import org.pkl.commons.createParentDirectories
import org.pkl.commons.toPath
import org.pkl.commons.writeString
import org.pkl.core.SecurityManagers
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URI import java.net.URI
import java.net.URISyntaxException import java.net.URISyntaxException
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.createFile import kotlin.io.path.createFile
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.createParentDirectories
import org.pkl.commons.toPath
import org.pkl.commons.writeString
import org.pkl.core.SecurityManagers
class ModuleKeysTest { class ModuleKeysTest {
private val securityManager = SecurityManagers.defaultManager private val securityManager = SecurityManagers.defaultManager
@@ -46,9 +61,7 @@ class ModuleKeysTest {
@Test @Test
fun `standard library - wrong scheme`() { fun `standard library - wrong scheme`() {
assertThrows<IllegalArgumentException> { assertThrows<IllegalArgumentException> { ModuleKeys.standardLibrary(URI("other:base")) }
ModuleKeys.standardLibrary(URI("other:base"))
}
} }
@Test @Test
@@ -97,18 +110,18 @@ class ModuleKeysTest {
@Test @Test
fun `class path - module not found`() { fun `class path - module not found`() {
val key = ModuleKeys.classPath(URI("modulepath:/non/existing"), ModuleKeysTest::class.java.classLoader) val key =
ModuleKeys.classPath(URI("modulepath:/non/existing"), ModuleKeysTest::class.java.classLoader)
assertThrows<FileNotFoundException> { assertThrows<FileNotFoundException> { key.resolve(SecurityManagers.defaultManager) }
key.resolve(SecurityManagers.defaultManager)
}
} }
@Test @Test
fun `class path - missing leading slash`() { fun `class path - missing leading slash`() {
val e = assertThrows<IllegalArgumentException> { val e =
ModuleKeys.classPath(URI("modulepath:foo/bar.pkl"), ModuleKeysTest::class.java.classLoader) assertThrows<IllegalArgumentException> {
} ModuleKeys.classPath(URI("modulepath:foo/bar.pkl"), ModuleKeysTest::class.java.classLoader)
}
assertThat(e).hasMessageContaining("`/`") assertThat(e).hasMessageContaining("`/`")
} }
@@ -127,10 +140,8 @@ class ModuleKeysTest {
val resolvedKey = key.resolve(securityManager) val resolvedKey = key.resolve(securityManager)
assertThat(resolvedKey.uri.scheme) assertThat(resolvedKey.uri.scheme).isEqualTo("file")
.isEqualTo("file") assertThat(resolvedKey.loadSource()).isEqualTo("age = 40")
assertThat(resolvedKey.loadSource())
.isEqualTo("age = 40")
} }
@Test @Test
@@ -144,61 +155,55 @@ class ModuleKeysTest {
fun `module path - module not found`() { fun `module path - module not found`() {
val key = ModuleKeys.modulePath(URI("modulepath:/non/existing"), ModulePathResolver(listOf())) val key = ModuleKeys.modulePath(URI("modulepath:/non/existing"), ModulePathResolver(listOf()))
assertThrows<FileNotFoundException> { assertThrows<FileNotFoundException> { key.resolve(SecurityManagers.defaultManager) }
key.resolve(SecurityManagers.defaultManager)
}
} }
@Test @Test
fun `module path - missing leading slash`() { fun `module path - missing leading slash`() {
val e = assertThrows<IllegalArgumentException> { val e =
ModuleKeys.modulePath(URI("modulepath:foo/bar.pkl"), ModulePathResolver(listOf())) assertThrows<IllegalArgumentException> {
} ModuleKeys.modulePath(URI("modulepath:foo/bar.pkl"), ModulePathResolver(listOf()))
}
assertThat(e).hasMessageContaining("`/`") assertThat(e).hasMessageContaining("`/`")
} }
@Test @Test
fun `package - no version`() { fun `package - no version`() {
val e = assertThrows<URISyntaxException> { val e =
ModuleKeys.pkg(URI("package://localhost:0/birds#/Bird.pkl")) assertThrows<URISyntaxException> {
} ModuleKeys.pkg(URI("package://localhost:0/birds#/Bird.pkl"))
}
assertThat(e).hasMessageContaining("A package URI must have its path suffixed by its version") assertThat(e).hasMessageContaining("A package URI must have its path suffixed by its version")
} }
@Test @Test
fun `package - invalid semver`() { fun `package - invalid semver`() {
val e = assertThrows<URISyntaxException> { val e =
ModuleKeys.pkg(URI("package://localhost:0/birds@notAVersion#/Bird.pkl")) assertThrows<URISyntaxException> {
} ModuleKeys.pkg(URI("package://localhost:0/birds@notAVersion#/Bird.pkl"))
}
assertThat(e).hasMessageContaining("`notAVersion` could not be parsed") assertThat(e).hasMessageContaining("`notAVersion` could not be parsed")
} }
@Test @Test
fun `package - missing leading slash`() { fun `package - missing leading slash`() {
val e = assertThrows<URISyntaxException> { val e = assertThrows<URISyntaxException> { ModuleKeys.pkg(URI("package:invalid")) }
ModuleKeys.pkg(URI("package:invalid")) assertThat(e)
} .hasMessageContaining("Module URI `package:invalid` is missing a `/` after `package:`")
assertThat(e).hasMessageContaining("Module URI `package:invalid` is missing a `/` after `package:`")
} }
@Test @Test
fun `package - missing authority`() { fun `package - missing authority`() {
val e = assertThrows<URISyntaxException> { val e = assertThrows<URISyntaxException> { ModuleKeys.pkg(URI("package:/not/a/valid/path")) }
ModuleKeys.pkg(URI("package:/not/a/valid/path"))
}
assertThat(e).hasMessageContaining("Package URIs must have an authority component") assertThat(e).hasMessageContaining("Package URIs must have an authority component")
val e2 = assertThrows<URISyntaxException> { val e2 = assertThrows<URISyntaxException> { ModuleKeys.pkg(URI("package:///not/a/valid/path")) }
ModuleKeys.pkg(URI("package:///not/a/valid/path"))
}
assertThat(e2).hasMessageContaining("Package URIs must have an authority component") assertThat(e2).hasMessageContaining("Package URIs must have an authority component")
} }
@Test @Test
fun `package - missing path`() { fun `package - missing path`() {
val e = assertThrows<URISyntaxException> { val e = assertThrows<URISyntaxException> { ModuleKeys.pkg(URI("package://example.com")) }
ModuleKeys.pkg(URI("package://example.com"))
}
assertThat(e).hasMessageContaining("Package URIs must have a path component") assertThat(e).hasMessageContaining("Package URIs must have a path component")
} }
@@ -234,11 +239,8 @@ class ModuleKeysTest {
val uri = URI("repl:foo") val uri = URI("repl:foo")
val key = ModuleKeys.genericUrl(uri) val key = ModuleKeys.genericUrl(uri)
val e = assertThrows<MalformedURLException> { val e = assertThrows<MalformedURLException> { key.resolve(securityManager) }
key.resolve(securityManager)
}
assertThat(e) assertThat(e).hasMessage("unknown protocol: repl")
.hasMessage("unknown protocol: repl")
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.module package org.pkl.core.module
import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatCode

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.module package org.pkl.core.module
import java.net.URI import java.net.URI
@@ -26,9 +41,7 @@ class ResolvedModuleKeysTest {
fun `url()`(@TempDir tempDir: Path) { fun `url()`(@TempDir tempDir: Path) {
val path = tempDir.createTempFile().writeString("x = 1") val path = tempDir.createTempFile().writeString("x = 1")
val resolvedUri = URI("test:resolved.uri") val resolvedUri = URI("test:resolved.uri")
val resolved = ResolvedModuleKeys.url( val resolved = ResolvedModuleKeys.url(module, resolvedUri, path.toUri().toURL())
module, resolvedUri, path.toUri().toURL()
)
assertThat(resolved.original).isSameAs(module) assertThat(resolved.original).isSameAs(module)
assertThat(resolved.uri).isEqualTo(resolvedUri) assertThat(resolved.uri).isEqualTo(resolvedUri)

View File

@@ -1,28 +1,42 @@
/**
* Copyright © 2024 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.core.module package org.pkl.core.module
import java.net.URI
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import org.pkl.core.Evaluator import org.pkl.core.Evaluator
import org.pkl.core.ModuleSource import org.pkl.core.ModuleSource
import org.pkl.core.PClassInfo import org.pkl.core.PClassInfo
import org.pkl.core.PModule import org.pkl.core.PModule
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import java.net.URI
class ServiceProviderTest { class ServiceProviderTest {
@Test @Test
fun `load module through service provider`() { fun `load module through service provider`() {
val module = Evaluator val module = Evaluator.preconfigured().evaluate(ModuleSource.uri(URI("test:foo")))
.preconfigured()
.evaluate(ModuleSource.uri(URI("test:foo")))
val uri = URI("modulepath:/org/pkl/core/module/testFactoryTest.pkl") val uri = URI("modulepath:/org/pkl/core/module/testFactoryTest.pkl")
Assertions.assertThat(module).isEqualTo( Assertions.assertThat(module)
PModule( .isEqualTo(
uri, PModule(
"testFactoryTest", uri,
PClassInfo.forModuleClass("testFactoryTest", uri), "testFactoryTest",
mapOf("name" to "Pigeon", "age" to 40L) PClassInfo.forModuleClass("testFactoryTest", uri),
mapOf("name" to "Pigeon", "age" to 40L)
)
) )
)
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.module package org.pkl.core.module
import java.net.URI import java.net.URI

View File

@@ -1,33 +1,54 @@
package org.pkl.core.packages; /**
* Copyright © 2024 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.core.packages
import org.pkl.core.Version
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.net.URI import java.net.URI
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.pkl.core.Version
class DependencyMetadataTest { class DependencyMetadataTest {
private val dependencyMetadata = DependencyMetadata( private val dependencyMetadata =
"my-proj-name", DependencyMetadata(
PackageUri("package://example.com/my-proj-name@0.10.0"), "my-proj-name",
Version.parse("0.10.0"), PackageUri("package://example.com/my-proj-name@0.10.0"),
URI("https://example.com/foo/bar@0.5.3.zip"), Version.parse("0.10.0"),
Checksums("abc123"), URI("https://example.com/foo/bar@0.5.3.zip"),
mapOf( Checksums("abc123"),
"foo" to Dependency.RemoteDependency(PackageUri("package://example.com/foo@0.5.3"), Checksums("abc123")), mapOf(
), "foo" to
"https://example.com/my/source/0.5.3/blob%{path}#L%{line}-L%{endLine}", Dependency.RemoteDependency(
URI("https://example.com/my/source"), PackageUri("package://example.com/foo@0.5.3"),
URI("https://example.com/my/docs"), Checksums("abc123")
"MIT", ),
"The MIT License, you know it", ),
listOf("birdy@bird.com"), "https://example.com/my/source/0.5.3/blob%{path}#L%{line}-L%{endLine}",
URI("https://example.com/issues"), URI("https://example.com/my/source"),
"Some package description" URI("https://example.com/my/docs"),
) "MIT",
"The MIT License, you know it",
listOf("birdy@bird.com"),
URI("https://example.com/issues"),
"Some package description"
)
private val dependencyMetadataStr = """ private val dependencyMetadataStr =
"""
{ {
"name": "my-proj-name", "name": "my-proj-name",
"packageUri": "package://example.com/my-proj-name@0.10.0", "packageUri": "package://example.com/my-proj-name@0.10.0",
@@ -55,7 +76,8 @@ class DependencyMetadataTest {
"issueTracker": "https://example.com/issues", "issueTracker": "https://example.com/issues",
"description": "Some package description" "description": "Some package description"
} }
""".trimIndent() """
.trimIndent()
@Test @Test
fun parse() { fun parse() {
@@ -64,10 +86,11 @@ class DependencyMetadataTest {
} }
@Test @Test
fun writeTo(){ fun writeTo() {
val str = ByteArrayOutputStream() val str =
.apply { dependencyMetadata.writeTo(this) } ByteArrayOutputStream()
.toString(StandardCharsets.UTF_8) .apply { dependencyMetadata.writeTo(this) }
.toString(StandardCharsets.UTF_8)
assertThat(str).isEqualTo(dependencyMetadataStr) assertThat(str).isEqualTo(dependencyMetadataStr)
} }
} }

View File

@@ -1,5 +1,25 @@
/**
* Copyright © 2024 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.core.packages package org.pkl.core.packages
import java.io.FileNotFoundException
import java.io.IOException
import java.nio.charset.StandardCharsets
import kotlin.io.path.exists
import kotlin.io.path.readBytes
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterAll
@@ -11,31 +31,27 @@ import org.pkl.commons.readString
import org.pkl.commons.test.FileTestUtils import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.test.PackageServer import org.pkl.commons.test.PackageServer
import org.pkl.commons.test.listFilesRecursively import org.pkl.commons.test.listFilesRecursively
import org.pkl.core.http.HttpClient
import org.pkl.core.SecurityManagers import org.pkl.core.SecurityManagers
import org.pkl.core.http.HttpClient
import org.pkl.core.module.PathElement import org.pkl.core.module.PathElement
import java.io.FileNotFoundException
import java.io.IOException
import java.nio.charset.StandardCharsets
import kotlin.io.path.exists
import kotlin.io.path.readBytes
class PackageResolversTest { class PackageResolversTest {
abstract class AbstractPackageResolverTest { abstract class AbstractPackageResolverTest {
abstract val resolver: PackageResolver abstract val resolver: PackageResolver
private val packageRoot = FileTestUtils.rootProjectDir.resolve("pkl-commons-test/src/main/files/packages") private val packageRoot =
FileTestUtils.rootProjectDir.resolve("pkl-commons-test/src/main/files/packages")
companion object { companion object {
private val packageServer = PackageServer() private val packageServer = PackageServer()
@JvmStatic @JvmStatic
@AfterAll @AfterAll
fun afterAll() { fun afterAll() {
packageServer.close() packageServer.close()
} }
val httpClient: HttpClient by lazy { val httpClient: HttpClient by lazy {
HttpClient.builder() HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate) .addCertificates(FileTestUtils.selfSignedCertificate)
@@ -46,105 +62,107 @@ class PackageResolversTest {
@Test @Test
fun `get module bytes`() { fun `get module bytes`() {
val expectedBirdModule = packageRoot.resolve("birds@0.5.0/package/Bird.pkl").readString(StandardCharsets.UTF_8) val expectedBirdModule =
packageRoot.resolve("birds@0.5.0/package/Bird.pkl").readString(StandardCharsets.UTF_8)
val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/Bird.pkl") val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/Bird.pkl")
val birdModule = resolver val birdModule = resolver.getBytes(assetUri, false, null).toString(StandardCharsets.UTF_8)
.getBytes(assetUri, false, null)
.toString(StandardCharsets.UTF_8)
assertThat(birdModule).isEqualTo(expectedBirdModule) assertThat(birdModule).isEqualTo(expectedBirdModule)
} }
@Test @Test
fun `get directory`() { fun `get directory`() {
val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/") val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/")
val err = assertThrows<IOException> { val err =
resolver assertThrows<IOException> {
.getBytes(assetUri, false, null) resolver.getBytes(assetUri, false, null).toString(StandardCharsets.UTF_8)
.toString(StandardCharsets.UTF_8) }
}
assertThat(err).hasMessage("Is a directory") assertThat(err).hasMessage("Is a directory")
} }
@Test @Test
fun `get directory, allowing directory reads`() { fun `get directory, allowing directory reads`() {
val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/") val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/")
val bytes = resolver val bytes = resolver.getBytes(assetUri, true, null).toString(StandardCharsets.UTF_8)
.getBytes(assetUri, true, null) assertThat(bytes)
.toString(StandardCharsets.UTF_8) .isEqualTo(
assertThat(bytes).isEqualTo(""" """
Bird.pkl Bird.pkl
allFruit.pkl allFruit.pkl
catalog catalog
catalog.pkl catalog.pkl
some some
""".trimIndent()) """
.trimIndent()
)
} }
@Test @Test
fun `get module bytes resolving path`() { fun `get module bytes resolving path`() {
val expectedBirdModule = packageRoot.resolve("birds@0.5.0/package/Bird.pkl").readString(StandardCharsets.UTF_8) val expectedBirdModule =
packageRoot.resolve("birds@0.5.0/package/Bird.pkl").readString(StandardCharsets.UTF_8)
val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/foo/../Bird.pkl") val assetUri = PackageAssetUri("package://localhost:0/birds@0.5.0#/foo/../Bird.pkl")
val birdModule = resolver val birdModule = resolver.getBytes(assetUri, false, null).toString(StandardCharsets.UTF_8)
.getBytes(assetUri, false, null)
.toString(StandardCharsets.UTF_8)
assertThat(birdModule).isEqualTo(expectedBirdModule) assertThat(birdModule).isEqualTo(expectedBirdModule)
} }
@Test @Test
fun `list path elements at root`() { fun `list path elements at root`() {
// cast to set to avoid sort issues // cast to set to avoid sort issues
val elements = resolver val elements =
.listElements(PackageAssetUri("package://localhost:0/birds@0.5.0#/"), null) resolver.listElements(PackageAssetUri("package://localhost:0/birds@0.5.0#/"), null).toSet()
.toSet() assertThat(elements)
assertThat(elements).isEqualTo( .isEqualTo(
setOf( setOf(
PathElement("some", true), PathElement("some", true),
PathElement("catalog", true), PathElement("catalog", true),
PathElement("Bird.pkl", false), PathElement("Bird.pkl", false),
PathElement("allFruit.pkl", false), PathElement("allFruit.pkl", false),
PathElement("catalog.pkl", false) PathElement("catalog.pkl", false)
)
) )
)
} }
@Test @Test
fun `get multiple assets`() { fun `get multiple assets`() {
val bird = resolver.getBytes( val bird =
PackageAssetUri("package://localhost:0/birds@0.5.0#/Bird.pkl"), resolver.getBytes(
false, PackageAssetUri("package://localhost:0/birds@0.5.0#/Bird.pkl"),
null false,
) null
val swallow = resolver.getBytes( )
PackageAssetUri("package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"), val swallow =
false, resolver.getBytes(
null PackageAssetUri("package://localhost:0/birds@0.5.0#/catalog/Swallow.pkl"),
) false,
null
)
assertThat(bird).isEqualTo(packageRoot.resolve("birds@0.5.0/package/Bird.pkl").readBytes()) assertThat(bird).isEqualTo(packageRoot.resolve("birds@0.5.0/package/Bird.pkl").readBytes())
assertThat(swallow).isEqualTo(packageRoot.resolve("birds@0.5.0/package/catalog/Swallow.pkl").readBytes()) assertThat(swallow)
.isEqualTo(packageRoot.resolve("birds@0.5.0/package/catalog/Swallow.pkl").readBytes())
} }
@Test @Test
fun `list path elements in nested directory`() { fun `list path elements in nested directory`() {
// cast to set to avoid sort issues // cast to set to avoid sort issues
val elements = resolver.listElements(PackageAssetUri("package://localhost:0/birds@0.5.0#/catalog/"), null).toSet() val elements =
assertThat(elements).isEqualTo( resolver
setOf( .listElements(PackageAssetUri("package://localhost:0/birds@0.5.0#/catalog/"), null)
PathElement("Ostritch.pkl", false), .toSet()
PathElement("Swallow.pkl", false), assertThat(elements)
.isEqualTo(
setOf(
PathElement("Ostritch.pkl", false),
PathElement("Swallow.pkl", false),
)
) )
)
} }
@Test @Test
fun `getBytes() throws FileNotFound if package exists but path does not`() { fun `getBytes() throws FileNotFound if package exists but path does not`() {
assertThrows<FileNotFoundException> { assertThrows<FileNotFoundException> {
resolver resolver
.getBytes( .getBytes(PackageAssetUri("package://localhost:0/birds@0.5.0#/Horse.pkl"), false, null)
PackageAssetUri("package://localhost:0/birds@0.5.0#/Horse.pkl"),
false,
null
)
.toString(StandardCharsets.UTF_8) .toString(StandardCharsets.UTF_8)
} }
} }
@@ -156,7 +174,8 @@ class PackageResolversTest {
.getBytes( .getBytes(
PackageAssetUri("package://localhost:0/not-a-package@0.5.0#/Horse.pkl"), PackageAssetUri("package://localhost:0/not-a-package@0.5.0#/Horse.pkl"),
false, false,
null) null
)
.toString(StandardCharsets.UTF_8) .toString(StandardCharsets.UTF_8)
} }
} }
@@ -164,26 +183,35 @@ class PackageResolversTest {
@Test @Test
fun `requires package zip to be an HTTPS URI`() { fun `requires package zip to be an HTTPS URI`() {
assertThatCode { assertThatCode {
resolver.getBytes( resolver.getBytes(
PackageAssetUri("package://localhost:0/badPackageZipUrl@1.0.0#/Bug.pkl"), PackageAssetUri("package://localhost:0/badPackageZipUrl@1.0.0#/Bug.pkl"),
false, false,
null) null
} )
.hasMessage("Expected the zip asset for package `package://localhost:0/badPackageZipUrl@1.0.0` to be an HTTPS URI, but got `ftp://wait/a/minute`.") }
.hasMessage(
"Expected the zip asset for package `package://localhost:0/badPackageZipUrl@1.0.0` to be an HTTPS URI, but got `ftp://wait/a/minute`."
)
} }
@Test @Test
fun `throws if package checksum is invalid`() { fun `throws if package checksum is invalid`() {
val error = assertThrows<PackageLoadError> { val error =
resolver.getBytes( assertThrows<PackageLoadError> {
PackageAssetUri("package://localhost:0/badChecksum@1.0.0#/Bug.pkl"), resolver.getBytes(
false, PackageAssetUri("package://localhost:0/badChecksum@1.0.0#/Bug.pkl"),
null) false,
} null
assertThat(error).hasMessageContaining(""" )
}
assertThat(error)
.hasMessageContaining(
"""
Computed checksum: "a6bf858cdd1c09da475c2abe50525902580910ee5cc1ff624999170591bf8f69" Computed checksum: "a6bf858cdd1c09da475c2abe50525902580910ee5cc1ff624999170591bf8f69"
Expected checksum: "intentionally bogus checksum" Expected checksum: "intentionally bogus checksum"
""".trimIndent()) """
.trimIndent()
)
} }
} }
@@ -205,12 +233,16 @@ class PackageResolversTest {
} }
} }
override val resolver: PackageResolver = PackageResolvers.DiskCachedPackageResolver( override val resolver: PackageResolver =
SecurityManagers.defaultManager, httpClient, cacheDir) PackageResolvers.DiskCachedPackageResolver(
SecurityManagers.defaultManager,
httpClient,
cacheDir
)
} }
class InMemoryPackageResolverTest : AbstractPackageResolverTest() { class InMemoryPackageResolverTest : AbstractPackageResolverTest() {
override val resolver: PackageResolver = PackageResolvers.InMemoryPackageResolver( override val resolver: PackageResolver =
SecurityManagers.defaultManager, httpClient) PackageResolvers.InMemoryPackageResolver(SecurityManagers.defaultManager, httpClient)
} }
} }

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.parser package org.pkl.core.parser
import org.antlr.v4.runtime.CommonToken import org.antlr.v4.runtime.CommonToken

View File

@@ -1,30 +1,42 @@
/**
* Copyright © 2024 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.core.parser package org.pkl.core.parser
import org.pkl.core.Evaluator
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.core.Evaluator
import org.pkl.core.ModuleSource import org.pkl.core.ModuleSource
// tests whitespace handling in multi-line string literals that cannot be reliably tested via snippets // tests whitespace handling in multi-line string literals that cannot be reliably tested via
// snippets
// (e.g. due to editors not displaying and/or automatically removing whitespace) // (e.g. due to editors not displaying and/or automatically removing whitespace)
class MultiLineStringLiteralTest { class MultiLineStringLiteralTest {
private val evaluator = Evaluator.preconfigured() private val evaluator = Evaluator.preconfigured()
@Test @Test
fun `multi-line strings have unix newlines`() { fun `multi-line strings have unix newlines`() {
val module = evaluator.evaluate( val module =
ModuleSource.text( evaluator.evaluate(ModuleSource.text("x = \"\"\"\none\rtwo\nthree\r\nfour\n\"\"\""))
"x = \"\"\"\none\rtwo\nthree\r\nfour\n\"\"\""
)
)
assertThat(module.properties["x"]).isEqualTo("one\ntwo\nthree\nfour") assertThat(module.properties["x"]).isEqualTo("one\ntwo\nthree\nfour")
} }
@Test @Test
fun `raw multi-line strings have unix newlines`() { fun `raw multi-line strings have unix newlines`() {
val module = evaluator.evaluate( val module =
ModuleSource.text("x = #\"\"\"\none\rtwo\nthree\r\nfour\n\"\"\"#") evaluator.evaluate(ModuleSource.text("x = #\"\"\"\none\rtwo\nthree\r\nfour\n\"\"\"#"))
)
assertThat(module.properties["x"]).isEqualTo("one\ntwo\nthree\nfour") assertThat(module.properties["x"]).isEqualTo("one\ntwo\nthree\nfour")
} }
} }

View File

@@ -1,8 +1,23 @@
/**
* Copyright © 2024 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.core.parser package org.pkl.core.parser
import org.pkl.core.Evaluator
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.pkl.core.Evaluator
import org.pkl.core.ModuleSource import org.pkl.core.ModuleSource
class ShebangTest { class ShebangTest {
@@ -10,15 +25,17 @@ class ShebangTest {
@Test @Test
fun `shebang is ignored`() { fun `shebang is ignored`() {
val module = evaluator.evaluate( val module =
ModuleSource.text( evaluator.evaluate(
""" ModuleSource.text(
"""
#!/usr/local/bin/pkl #!/usr/local/bin/pkl
x = 1 x = 1
""".trimIndent() """
.trimIndent()
)
) )
)
assertThat (module.properties["x"]).isEqualTo(1L) assertThat(module.properties["x"]).isEqualTo(1L)
} }
} }

View File

@@ -1,5 +1,22 @@
/**
* Copyright © 2024 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.core.project package org.pkl.core.project
import java.io.ByteArrayOutputStream
import java.nio.charset.StandardCharsets
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@@ -7,23 +24,21 @@ import org.junit.jupiter.api.assertThrows
import org.pkl.commons.test.FileTestUtils import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.test.PackageServer import org.pkl.commons.test.PackageServer
import org.pkl.commons.toPath import org.pkl.commons.toPath
import org.pkl.core.http.HttpClient
import org.pkl.core.PklException import org.pkl.core.PklException
import org.pkl.core.SecurityManagers import org.pkl.core.SecurityManagers
import org.pkl.core.http.HttpClient
import org.pkl.core.packages.PackageResolver import org.pkl.core.packages.PackageResolver
import java.io.ByteArrayOutputStream
import java.nio.charset.StandardCharsets
class ProjectDependenciesResolverTest { class ProjectDependenciesResolverTest {
companion object { companion object {
private val packageServer = PackageServer() private val packageServer = PackageServer()
@JvmStatic @JvmStatic
@AfterAll @AfterAll
fun afterAll() { fun afterAll() {
packageServer.close() packageServer.close()
} }
val httpClient: HttpClient by lazy { val httpClient: HttpClient by lazy {
HttpClient.builder() HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate) .addCertificates(FileTestUtils.selfSignedCertificate)
@@ -36,13 +51,17 @@ class ProjectDependenciesResolverTest {
fun resolveDependencies() { fun resolveDependencies() {
val project2Path = javaClass.getResource("project2/PklProject")!!.toURI().toPath() val project2Path = javaClass.getResource("project2/PklProject")!!.toURI().toPath()
val project = Project.loadFromPath(project2Path) val project = Project.loadFromPath(project2Path)
val packageResolver = PackageResolver.getInstance(SecurityManagers.defaultManager, httpClient, null) val packageResolver =
PackageResolver.getInstance(SecurityManagers.defaultManager, httpClient, null)
val deps = ProjectDependenciesResolver(project, packageResolver, System.out.writer()).resolve() val deps = ProjectDependenciesResolver(project, packageResolver, System.out.writer()).resolve()
val strDeps = ByteArrayOutputStream() val strDeps =
.apply { deps.writeTo(this) } ByteArrayOutputStream()
.toByteArray() .apply { deps.writeTo(this) }
.toString(StandardCharsets.UTF_8) .toByteArray()
assertThat(strDeps).isEqualTo(""" .toString(StandardCharsets.UTF_8)
assertThat(strDeps)
.isEqualTo(
"""
{ {
"schemaVersion": 1, "schemaVersion": 1,
"resolvedDependencies": { "resolvedDependencies": {
@@ -67,22 +86,30 @@ class ProjectDependenciesResolverTest {
} }
} }
} }
""".trimIndent()) """
.trimIndent()
)
} }
@Test @Test
fun `fails if project declares a package with an incorrect checksum`() { fun `fails if project declares a package with an incorrect checksum`() {
val projectPath = javaClass.getResource("badProjectChecksum/PklProject")!!.toURI().toPath() val projectPath = javaClass.getResource("badProjectChecksum/PklProject")!!.toURI().toPath()
val project = Project.loadFromPath(projectPath) val project = Project.loadFromPath(projectPath)
val packageResolver = PackageResolver.getInstance(SecurityManagers.defaultManager, httpClient, null) val packageResolver =
val e = assertThrows<PklException> { PackageResolver.getInstance(SecurityManagers.defaultManager, httpClient, null)
ProjectDependenciesResolver(project, packageResolver, System.err.writer()).resolve() val e =
} assertThrows<PklException> {
assertThat(e).hasMessage(""" ProjectDependenciesResolver(project, packageResolver, System.err.writer()).resolve()
}
assertThat(e)
.hasMessage(
"""
Computed checksum did not match declared checksum for dependency `package://localhost:0/birds@0.5.0`. Computed checksum did not match declared checksum for dependency `package://localhost:0/birds@0.5.0`.
Computed: "${PackageServer.BIRDS_SHA}" Computed: "${PackageServer.BIRDS_SHA}"
Declared: "intentionally bogus value" Declared: "intentionally bogus value"
""".trimIndent()) """
.trimIndent()
)
} }
} }

View File

@@ -1,16 +1,32 @@
/**
* Copyright © 2024 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.core.project package org.pkl.core.project
import java.io.ByteArrayOutputStream
import java.nio.file.Path
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.pkl.core.packages.Checksums import org.pkl.core.packages.Checksums
import org.pkl.core.packages.Dependency import org.pkl.core.packages.Dependency
import org.pkl.core.packages.PackageUri import org.pkl.core.packages.PackageUri
import org.pkl.core.util.EconomicMaps import org.pkl.core.util.EconomicMaps
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.io.ByteArrayOutputStream
import java.nio.file.Path
class ProjectDepsTest { class ProjectDepsTest {
private val projectDepsStr = """ private val projectDepsStr =
"""
{ {
"schemaVersion": 1, "schemaVersion": 1,
"resolvedDependencies": { "resolvedDependencies": {
@@ -28,27 +44,29 @@ class ProjectDepsTest {
} }
} }
} }
""".trimIndent() """
.trimIndent()
private val projectDeps = let { private val projectDeps = let {
val projectDepsMap = EconomicMaps.of<CanonicalPackageUri, Dependency>( val projectDepsMap =
CanonicalPackageUri.of("package://localhost:0/birds@0"), Dependency.RemoteDependency( EconomicMaps.of<CanonicalPackageUri, Dependency>(
PackageUri.create("package://localhost:0/birds@0.5.0"), CanonicalPackageUri.of("package://localhost:0/birds@0"),
Checksums("abc123") Dependency.RemoteDependency(
), PackageUri.create("package://localhost:0/birds@0.5.0"),
CanonicalPackageUri.of("package://localhost:0/fruit@1"), Dependency.LocalDependency( Checksums("abc123")
PackageUri.create("package://localhost:0/fruit@1.1.0"), ),
Path.of("../fruit") CanonicalPackageUri.of("package://localhost:0/fruit@1"),
Dependency.LocalDependency(
PackageUri.create("package://localhost:0/fruit@1.1.0"),
Path.of("../fruit")
)
) )
)
ProjectDeps(projectDepsMap) ProjectDeps(projectDepsMap)
} }
@Test @Test
fun writeTo() { fun writeTo() {
val str = ByteArrayOutputStream() val str = ByteArrayOutputStream().apply { projectDeps.writeTo(this) }.toString(Charsets.UTF_8)
.apply { projectDeps.writeTo(this) }
.toString(Charsets.UTF_8)
assertThat(str).isEqualTo(projectDepsStr) assertThat(str).isEqualTo(projectDepsStr)
} }

View File

@@ -1,5 +1,23 @@
/**
* Copyright © 2024 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.core.project package org.pkl.core.project
import java.net.URI
import java.nio.file.Path
import java.util.regex.Pattern
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@@ -8,54 +26,52 @@ import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.test.PackageServer import org.pkl.commons.test.PackageServer
import org.pkl.commons.writeString import org.pkl.commons.writeString
import org.pkl.core.* import org.pkl.core.*
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings
import org.pkl.core.http.HttpClient import org.pkl.core.http.HttpClient
import org.pkl.core.packages.PackageUri import org.pkl.core.packages.PackageUri
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings
import java.net.URI
import java.nio.file.Path
import java.util.regex.Pattern
class ProjectTest { class ProjectTest {
@Test @Test
fun loadFromPath(@TempDir path: Path) { fun loadFromPath(@TempDir path: Path) {
val projectPath = path.resolve("PklProject") val projectPath = path.resolve("PklProject")
val expectedPackage = Package( val expectedPackage =
"hawk", Package(
PackageUri("package://example.com/hawk@0.5.0"), "hawk",
Version.parse("0.5.0"), PackageUri("package://example.com/hawk@0.5.0"),
URI("https://example.com/hawk/0.5.0/hawk-0.5.0.zip"), Version.parse("0.5.0"),
"Some project about hawks", URI("https://example.com/hawk/0.5.0/hawk-0.5.0.zip"),
listOf("Birdy Bird <birdy@bird.com>"), "Some project about hawks",
URI("https://example.com/my/website"), listOf("Birdy Bird <birdy@bird.com>"),
URI("https://example.com/my/docs"), URI("https://example.com/my/website"),
URI("https://example.com/my/repo"), URI("https://example.com/my/docs"),
"https://example.com/my/repo/0.5.0%{path}", URI("https://example.com/my/repo"),
"MIT", "https://example.com/my/repo/0.5.0%{path}",
""" "MIT",
"""
# Some License text # Some License text
This is my license text This is my license text
""".trimIndent(), """
URI("https://example.com/my/issues"), .trimIndent(),
listOf(Path.of("apiTest1.pkl"), Path.of("apiTest2.pkl")), URI("https://example.com/my/issues"),
listOf("PklProject", "PklProject.deps.json", ".**", "*.exe") listOf(Path.of("apiTest1.pkl"), Path.of("apiTest2.pkl")),
) listOf("PklProject", "PklProject.deps.json", ".**", "*.exe")
val expectedSettings = PklEvaluatorSettings( )
mapOf("two" to "2"), val expectedSettings =
mapOf("one" to "1"), PklEvaluatorSettings(
listOf("foo:", "bar:").map(Pattern::compile), mapOf("two" to "2"),
listOf("baz:", "biz:").map(Pattern::compile), mapOf("one" to "1"),
false, listOf("foo:", "bar:").map(Pattern::compile),
path.resolve("cache/"), listOf("baz:", "biz:").map(Pattern::compile),
listOf( false,
path.resolve("modulepath1/"), path.resolve("cache/"),
path.resolve("modulepath2/") listOf(path.resolve("modulepath1/"), path.resolve("modulepath2/")),
), Duration.ofMinutes(5.0),
Duration.ofMinutes(5.0), path,
path, null
null )
) projectPath.writeString(
projectPath.writeString(""" """
amends "pkl:Project" amends "pkl:Project"
evaluatorSettings { evaluatorSettings {
@@ -114,24 +130,28 @@ class ProjectTest {
"test1.pkl" "test1.pkl"
"test2.pkl" "test2.pkl"
} }
""".trimIndent()) """
.trimIndent()
)
val project = Project.loadFromPath(projectPath) val project = Project.loadFromPath(projectPath)
assertThat(project.`package`).isEqualTo(expectedPackage) assertThat(project.`package`).isEqualTo(expectedPackage)
assertThat(project.evaluatorSettings).isEqualTo(expectedSettings) assertThat(project.evaluatorSettings).isEqualTo(expectedSettings)
assertThat(project.tests).isEqualTo(listOf(path.resolve("test1.pkl"), path.resolve("test2.pkl"))) assertThat(project.tests)
.isEqualTo(listOf(path.resolve("test1.pkl"), path.resolve("test2.pkl")))
} }
@Test @Test
fun `load wrong type`(@TempDir path: Path) { fun `load wrong type`(@TempDir path: Path) {
val projectPath = path.resolve("PklProject") val projectPath = path.resolve("PklProject")
projectPath.writeString(""" projectPath.writeString(
"""
module com.apple.Foo module com.apple.Foo
foo = 1 foo = 1
""".trimIndent()) """
assertThatCode { .trimIndent()
Project.loadFromPath(projectPath, SecurityManagers.defaultManager, null) )
} assertThatCode { Project.loadFromPath(projectPath, SecurityManagers.defaultManager, null) }
.hasMessageContaining("be of type `pkl.Project`, but got type `com.apple.Foo`") .hasMessageContaining("be of type `pkl.Project`, but got type `com.apple.Foo`")
} }
@@ -140,18 +160,20 @@ class ProjectTest {
PackageServer().use { server -> PackageServer().use { server ->
val projectDir = Path.of(javaClass.getResource("badProjectChecksum2/")!!.toURI()) val projectDir = Path.of(javaClass.getResource("badProjectChecksum2/")!!.toURI())
val project = Project.loadFromPath(projectDir.resolve("PklProject")) val project = Project.loadFromPath(projectDir.resolve("PklProject"))
val httpClient = HttpClient.builder() val httpClient =
.addCertificates(FileTestUtils.selfSignedCertificate) HttpClient.builder()
.setTestPort(server.port) .addCertificates(FileTestUtils.selfSignedCertificate)
.build() .setTestPort(server.port)
val evaluator = EvaluatorBuilder.preconfigured() .build()
.applyFromProject(project) val evaluator =
.setModuleCacheDir(null) EvaluatorBuilder.preconfigured()
.setHttpClient(httpClient) .applyFromProject(project)
.build() .setModuleCacheDir(null)
assertThatCode { .setHttpClient(httpClient)
evaluator.evaluate(ModuleSource.path(projectDir.resolve("bug.pkl"))) .build()
}.hasMessageStartingWith(""" assertThatCode { evaluator.evaluate(ModuleSource.path(projectDir.resolve("bug.pkl"))) }
.hasMessageStartingWith(
"""
Pkl Error Pkl Error
Cannot download package `package://localhost:0/fruit@1.0.5` because the computed checksum for package metadata does not match the expected checksum. Cannot download package `package://localhost:0/fruit@1.0.5` because the computed checksum for package metadata does not match the expected checksum.
@@ -161,7 +183,9 @@ class ProjectTest {
1 | import "@fruit/Fruit.pkl" 1 | import "@fruit/Fruit.pkl"
^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
""".trimIndent()) """
.trimIndent()
)
} }
} }
} }

View File

@@ -1,27 +1,43 @@
/**
* Copyright © 2024 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.core.resource package org.pkl.core.resource
import org.pkl.core.Evaluator
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.outputStream
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.api.io.TempDir
import org.pkl.core.Evaluator
import org.pkl.core.EvaluatorBuilder import org.pkl.core.EvaluatorBuilder
import org.pkl.core.ModuleSource import org.pkl.core.ModuleSource
import org.pkl.core.module.ModulePathResolver import org.pkl.core.module.ModulePathResolver
import kotlin.io.path.outputStream
class ResourceReadersEvaluatorTest { class ResourceReadersEvaluatorTest {
@Test @Test
fun `class path`() { fun `class path`() {
val evaluator = Evaluator.preconfigured() val evaluator = Evaluator.preconfigured()
val module = evaluator.evaluate( val module =
ModuleSource.text( evaluator.evaluate(
""" ModuleSource.text(
"""
res1 = read("modulepath:/org/pkl/core/resource/resource.txt").text res1 = read("modulepath:/org/pkl/core/resource/resource.txt").text
""" """
)
) )
)
assertThat(module.getProperty("res1")).isEqualTo("content") assertThat(module.getProperty("res1")).isEqualTo("content")
} }
@@ -38,18 +54,16 @@ class ResourceReadersEvaluatorTest {
ModulePathResolver(listOf(jarFile)).use { resolver -> ModulePathResolver(listOf(jarFile)).use { resolver ->
val reader = ResourceReaders.modulePath(resolver) val reader = ResourceReaders.modulePath(resolver)
val evaluator = EvaluatorBuilder val evaluator = EvaluatorBuilder.preconfigured().addResourceReader(reader).build()
.preconfigured()
.addResourceReader(reader)
.build()
val module = evaluator.evaluate( val module =
ModuleSource.text( evaluator.evaluate(
""" ModuleSource.text(
"""
res1 = read("modulepath:/dir1/resource1.txt").text res1 = read("modulepath:/dir1/resource1.txt").text
""" """
)
) )
)
assertThat(module.getProperty("res1")).isEqualTo("content\n") assertThat(module.getProperty("res1")).isEqualTo("content\n")
} }

View File

@@ -1,14 +1,29 @@
/**
* Copyright © 2024 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.core.resource package org.pkl.core.resource
import java.net.URI
import java.net.URISyntaxException
import java.nio.file.Path
import kotlin.io.path.outputStream
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.api.io.TempDir
import org.pkl.core.module.ModulePathResolver import org.pkl.core.module.ModulePathResolver
import java.net.URI
import java.net.URISyntaxException
import java.nio.file.Path
import kotlin.io.path.outputStream
class ResourceReadersTest { class ResourceReadersTest {
@Test @Test
@@ -82,9 +97,7 @@ class ResourceReadersTest {
fun `module path - missing leading slash`() { fun `module path - missing leading slash`() {
val reader = ResourceReaders.modulePath(ModulePathResolver(listOf())) val reader = ResourceReaders.modulePath(ModulePathResolver(listOf()))
assertThrows<URISyntaxException> { assertThrows<URISyntaxException> { reader.read(URI("modulepath:non/existing")) }
reader.read(URI("modulepath:non/existing"))
}
} }
@Test @Test

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.resource package org.pkl.core.resource
import java.net.URI import java.net.URI

View File

@@ -1,38 +1,58 @@
package org.pkl.core.runtime; /**
* Copyright © 2024 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.core.runtime
// //
//import java.io.IOException; // import java.io.IOException;
//import java.net.URI; // import java.net.URI;
//import java.net.URISyntaxException; // import java.net.URISyntaxException;
//import java.nio.file.Files; // import java.nio.file.Files;
//import java.nio.file.Path; // import java.nio.file.Path;
//import java.util.Arrays; // import java.util.Arrays;
//import java.util.Collections; // import java.util.Collections;
// //
//import org.pkl.core.EvalOptions; // import org.pkl.core.EvalOptions;
//import org.pkl.core.EvalException; // import org.pkl.core.EvalException;
//import org.pkl.core.resolve.ModuleKey; // import org.pkl.core.resolve.ModuleKey;
//import org.pkl.core.resolve.ModuleKeyFactories; // import org.pkl.core.resolve.ModuleKeyFactories;
//import org.pkl.core.resolve.ModuleKeys; // import org.pkl.core.resolve.ModuleKeys;
//import com.oracle.truffle.api.source.SourceSection; // import com.oracle.truffle.api.source.SourceSection;
//import org.junit.Ignore; // import org.junit.Ignore;
//import org.junit.Test; // import org.junit.Test;
// //
//import static org.junit.Assert.assertEquals; // import static org.junit.Assert.assertEquals;
//import static org.junit.Assert.assertTrue; // import static org.junit.Assert.assertTrue;
// //
//public class DefaultModuleResolverTest { // public class DefaultModuleResolverTest {
// private final SourceSection sourceSection = VmUtils.unavailableSourceSection(); // private final SourceSection sourceSection = VmUtils.unavailableSourceSection();
// private final ModuleResolver resolver = // private final ModuleResolver resolver =
// new ModuleResolver(ModuleKeyFactories.namedModuleOnClassPath, EvalOptions.namedModuleOnClassPath.getAllowedModules()); // new ModuleResolver(ModuleKeyFactories.namedModuleOnClassPath,
// EvalOptions.namedModuleOnClassPath.getAllowedModules());
// private final ModuleKey fileUrlModule; // private final ModuleKey fileUrlModule;
// private final ModuleKey httpsUrlModule; // private final ModuleKey httpsUrlModule;
// private final ModuleKey literalUrlModule; // private final ModuleKey literalUrlModule;
// //
// { // {
// try { // try {
// fileUrlModule = ModuleKeys.genericUrl(new URI("file:///path/script.pkl"), ModuleKeys.FULL_TRUST); // fileUrlModule = ModuleKeys.genericUrl(new URI("file:///path/script.pkl"),
// httpsUrlModule = ModuleKeys.genericUrl(new URI("https://some.domain.com/path/script.pkl"), ModuleKeys.FULL_TRUST); // ModuleKeys.FULL_TRUST);
// literalUrlModule = ModuleKeys.synthetic("myLiteralModule", "my literal source code", ModuleKeys.FULL_TRUST); // httpsUrlModule = ModuleKeys.genericUrl(new URI("https://some.domain.com/path/script.pkl"),
// ModuleKeys.FULL_TRUST);
// literalUrlModule = ModuleKeys.synthetic("myLiteralModule", "my literal source code",
// ModuleKeys.FULL_TRUST);
// } catch (URISyntaxException e) { // } catch (URISyntaxException e) {
// throw new RuntimeException(e); // throw new RuntimeException(e);
// } // }
@@ -61,7 +81,8 @@ package org.pkl.core.runtime;
// //
// @Test // @Test
// public void importHttpsUrlFromFileUrl() throws IOException { // public void importHttpsUrlFromFileUrl() throws IOException {
// ModuleKey result = resolver.resolve("https://other.domain.com/path2/script2.pkl", fileUrlModule, sourceSection); // ModuleKey result = resolver.resolve("https://other.domain.com/path2/script2.pkl",
// fileUrlModule, sourceSection);
// assertTrue(result instanceof ModuleKey.Url); // assertTrue(result instanceof ModuleKey.Url);
// assertEquals("https://other.domain.com/path2/script2.pkl", result.toString()); // assertEquals("https://other.domain.com/path2/script2.pkl", result.toString());
// } // }
@@ -94,7 +115,8 @@ package org.pkl.core.runtime;
// //
// @Test // @Test
// public void importHttpsUrlFromHttpsUrl() throws IOException { // public void importHttpsUrlFromHttpsUrl() throws IOException {
// ModuleKey result = resolver.resolve("https://other.domain.com/path2/script2.pkl", httpsUrlModule, sourceSection); // ModuleKey result = resolver.resolve("https://other.domain.com/path2/script2.pkl",
// httpsUrlModule, sourceSection);
// assertTrue(result instanceof ModuleKey.Url); // assertTrue(result instanceof ModuleKey.Url);
// assertEquals("https://other.domain.com/path2/script2.pkl", result.toString()); // assertEquals("https://other.domain.com/path2/script2.pkl", result.toString());
// } // }
@@ -127,14 +149,16 @@ package org.pkl.core.runtime;
// //
// @Test // @Test
// public void importFileUrlFromLiteral() throws IOException { // public void importFileUrlFromLiteral() throws IOException {
// ModuleKey result = resolver.resolve("file:///import/file.pkl", literalUrlModule, sourceSection); // ModuleKey result = resolver.resolve("file:///import/file.pkl", literalUrlModule,
// sourceSection);
// assertTrue(result instanceof ModuleKey.Url); // assertTrue(result instanceof ModuleKey.Url);
// assertEquals("file:///import/file.pkl", result.toString()); // assertEquals("file:///import/file.pkl", result.toString());
// } // }
// //
// @Test // @Test
// public void importHttpsUrlFromLiteral() throws IOException { // public void importHttpsUrlFromLiteral() throws IOException {
// ModuleKey result = resolver.resolve("https://other.domain.com/path2/script2.pkl", literalUrlModule, sourceSection); // ModuleKey result = resolver.resolve("https://other.domain.com/path2/script2.pkl",
// literalUrlModule, sourceSection);
// assertTrue(result instanceof ModuleKey.Url); // assertTrue(result instanceof ModuleKey.Url);
// assertEquals("https://other.domain.com/path2/script2.pkl", result.toString()); // assertEquals("https://other.domain.com/path2/script2.pkl", result.toString());
// } // }
@@ -183,4 +207,4 @@ package org.pkl.core.runtime;
// //
// resolver.resolve("pkl:base", fileUrlModule, sourceSection); // resolver.resolve("pkl:base", fileUrlModule, sourceSection);
// } // }
//} // }

View File

@@ -1,14 +1,28 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import java.net.URI
import java.nio.file.FileSystems
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.net.URI
import java.nio.file.FileSystems
class FileSystemManagerTest { class FileSystemManagerTest {
private val resource = private val resource = javaClass.getResource("/org/pkl/core/resource/resource1.jar")!!.toURI()
javaClass.getResource("/org/pkl/core/resource/resource1.jar")!!.toURI()
private val resourceUri = URI("jar:$resource") private val resourceUri = URI("jar:$resource")
@Test @Test

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@@ -1,22 +1,38 @@
package org.pkl.core.runtime; /**
* Copyright © 2024 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.core.runtime
// //
//import java.io.File; // import java.io.File;
//import java.io.IOException; // import java.io.IOException;
//import java.net.URI; // import java.net.URI;
//import java.net.URISyntaxException; // import java.net.URISyntaxException;
// //
//import org.pkl.core.resolve.ModuleKey; // import org.pkl.core.resolve.ModuleKey;
//import org.pkl.core.resolve.ModuleKeys; // import org.pkl.core.resolve.ModuleKeys;
//import org.junit.Rule; // import org.junit.Rule;
//import org.junit.Test; // import org.junit.Test;
//import org.junit.rules.TemporaryFolder; // import org.junit.rules.TemporaryFolder;
// //
//import static org.junit.Assert.assertEquals; // import static org.junit.Assert.assertEquals;
//import static org.junit.Assert.assertFalse; // import static org.junit.Assert.assertFalse;
//import static org.junit.Assert.assertTrue; // import static org.junit.Assert.assertTrue;
// //
//// some parts of ModuleKey are tested as part of DefaultModuleResolverTest //// some parts of ModuleKey are tested as part of DefaultModuleResolverTest
//public class ModuleKeyTest { // public class ModuleKeyTest {
// @Rule // @Rule
// public TemporaryFolder folder = new TemporaryFolder(); // public TemporaryFolder folder = new TemporaryFolder();
// //
@@ -73,4 +89,4 @@ package org.pkl.core.runtime;
// assertEquals("literal:name", module.getUri()); // assertEquals("literal:name", module.getUri());
// assertFalse(module.isBaseModule()); // assertFalse(module.isBaseModule());
// } // }
//} // }

View File

@@ -1,10 +1,25 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import org.pkl.core.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.pkl.core.*
class StackTraceRendererTest { class StackTraceRendererTest {
companion object { companion object {
@@ -20,39 +35,51 @@ class StackTraceRendererTest {
@Test @Test
fun `stringy self-reference`() { fun `stringy self-reference`() {
val message = assertThrows<PklException> { val message =
evaluator.evaluate( assertThrows<PklException> {
ModuleSource.text( evaluator.evaluate(
""" ModuleSource.text(
"""
self: String = "Strings; if they were lazy, you could tie the knot on \(self.take(7))" self: String = "Strings; if they were lazy, you could tie the knot on \(self.take(7))"
""".trimIndent()) """
) .trimIndent()
}.message!! )
)
}
.message!!
assertThat(message) assertThat(message)
.contains("A stack overflow occurred.") .contains("A stack overflow occurred.")
.containsPattern(""" .containsPattern(
"""
┌─ \d* repetitions of: ┌─ \d* repetitions of:
│ 1 | self: String = "Strings; if they were lazy, you could tie the knot on \\\(self.take\(7\)\)" │ 1 | self: String = "Strings; if they were lazy, you could tie the knot on \\\(self.take\(7\)\)"
│ ^^^^ │ ^^^^
""".trim()) """
.trim()
)
} }
@Test @Test
fun `cyclic property references`() { fun `cyclic property references`() {
val message = assertThrows<PklException> { val message =
evaluator.evaluate( assertThrows<PklException> {
ModuleSource.text( evaluator.evaluate(
""" ModuleSource.text(
"""
foo: String = "FOO:" + bar foo: String = "FOO:" + bar
bar: String = "BAR:" + baz bar: String = "BAR:" + baz
baz: String = "BAZ:" + qux baz: String = "BAZ:" + qux
qux: String = "QUX:" + foo qux: String = "QUX:" + foo
""".trimIndent()) """
.trimIndent()
)
) )
}.message!! }
.message!!
assertThat(message) assertThat(message)
.contains("A stack overflow occurred.") .contains("A stack overflow occurred.")
.containsPattern(""" .containsPattern(
"""
┌─ \d+ repetitions of: ┌─ \d+ repetitions of:
│ 4 | qux: String = "QUX:" + foo │ 4 | qux: String = "QUX:" + foo
│ ^^^ │ ^^^
@@ -70,13 +97,16 @@ class StackTraceRendererTest {
│ ^^^ │ ^^^
│ at text#foo (repl:text) │ at text#foo (repl:text)
└─ └─
""".trim()) """
.trim()
)
} }
@Test @Test
@Suppress("RegExpRepeatedSpace") @Suppress("RegExpRepeatedSpace")
fun `reduce stack overflow from actual Pkl code`() { fun `reduce stack overflow from actual Pkl code`() {
val pklCode = """ val pklCode =
"""
function suffix(n: UInt): UInt = function suffix(n: UInt): UInt =
if (n == 0) if (n == 0)
0 0
@@ -106,15 +136,15 @@ class StackTraceRendererTest {
prefix(n - 1) prefix(n - 1)
result = prefix(13) result = prefix(13)
""".trimIndent() """
val message = assertThrows<PklException> { .trimIndent()
evaluator.evaluate(ModuleSource.text(pklCode)) val message =
}.message!! assertThrows<PklException> { evaluator.evaluate(ModuleSource.text(pklCode)) }.message!!
if (message.contains("5 | suffix")) { if (message.contains("5 | suffix")) {
assertThat(message).containsPattern("repetitions of:\n│ 5 | suffix(n - 1)") assertThat(message).containsPattern("repetitions of:\n│ 5 | suffix(n - 1)")
} }
assertThat(message) assertThat(message)
.contains("A stack overflow occurred.") .contains("A stack overflow occurred.")
.containsPattern("┌─ \\d+ repetitions of:\n\n│ 9 | loop\\(\\)") .containsPattern("┌─ \\d+ repetitions of:\n\n│ 9 | loop\\(\\)")
@@ -159,15 +189,11 @@ class StackTraceRendererTest {
add(createFrame("foo", 3)) add(createFrame("foo", 3))
} }
val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1) val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1)
val frames = listOf( val frames = listOf(createFrame("bar", 1), createFrame("baz", 2), loop)
createFrame("bar", 1), val renderedFrames = buildString { renderer.doRender(frames, null, this, "", true) }
createFrame("baz", 2), assertThat(renderedFrames)
loop .isEqualTo(
) """
val renderedFrames = buildString {
renderer.doRender(frames, null, this, "", true)
}
assertThat(renderedFrames).isEqualTo("""
1 | foo 1 | foo
^ ^
at <unknown> (file:bar) at <unknown> (file:bar)
@@ -188,7 +214,9 @@ class StackTraceRendererTest {
^ ^
at <unknown> (file:foo) at <unknown> (file:foo)
""".trimIndent()) """
.trimIndent()
)
} }
private fun createFrame(name: String, id: Int): StackFrame { private fun createFrame(name: String, id: Int): StackFrame {

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@@ -10,15 +25,12 @@ class VmSafeMathTest {
assertThat(VmSafeMath.negate(0)).isEqualTo(0) assertThat(VmSafeMath.negate(0)).isEqualTo(0)
assertThat(VmSafeMath.negate(1)).isEqualTo(-1) assertThat(VmSafeMath.negate(1)).isEqualTo(-1)
assertThat(VmSafeMath.negate(-1)).isEqualTo(1) assertThat(VmSafeMath.negate(-1)).isEqualTo(1)
assertThat(VmSafeMath.negate(java.lang.Long.MAX_VALUE)) assertThat(VmSafeMath.negate(java.lang.Long.MAX_VALUE)).isEqualTo(-java.lang.Long.MAX_VALUE)
.isEqualTo(-java.lang.Long.MAX_VALUE)
} }
@Test @Test
fun `negate long - overflow`() { fun `negate long - overflow`() {
assertThrows<VmEvalException> { assertThrows<VmEvalException> { VmSafeMath.negate(java.lang.Long.MIN_VALUE) }
VmSafeMath.negate(java.lang.Long.MIN_VALUE)
}
} }
@Test @Test
@@ -27,10 +39,8 @@ class VmSafeMathTest {
assertThat(VmSafeMath.negate(-1.0)).isEqualTo(1.0) assertThat(VmSafeMath.negate(-1.0)).isEqualTo(1.0)
assertThat(VmSafeMath.negate(1.0)).isEqualTo(-1.0) assertThat(VmSafeMath.negate(1.0)).isEqualTo(-1.0)
assertThat(VmSafeMath.negate(123.456)).isEqualTo(-123.456) assertThat(VmSafeMath.negate(123.456)).isEqualTo(-123.456)
assertThat(VmSafeMath.negate(-java.lang.Double.MAX_VALUE)) assertThat(VmSafeMath.negate(-java.lang.Double.MAX_VALUE)).isEqualTo(java.lang.Double.MAX_VALUE)
.isEqualTo(java.lang.Double.MAX_VALUE) assertThat(VmSafeMath.negate(-java.lang.Double.MIN_VALUE)).isEqualTo(java.lang.Double.MIN_VALUE)
assertThat(VmSafeMath.negate(-java.lang.Double.MIN_VALUE))
.isEqualTo(java.lang.Double.MIN_VALUE)
} }
@Test @Test
@@ -38,31 +48,23 @@ class VmSafeMathTest {
assertThat(VmSafeMath.add(0, 0)).isEqualTo(0) assertThat(VmSafeMath.add(0, 0)).isEqualTo(0)
assertThat(VmSafeMath.add(1, 2)).isEqualTo(3) assertThat(VmSafeMath.add(1, 2)).isEqualTo(3)
assertThat(VmSafeMath.add(1, -2)).isEqualTo(-1) assertThat(VmSafeMath.add(1, -2)).isEqualTo(-1)
assertThat(VmSafeMath.add(java.lang.Long.MAX_VALUE - 1, 1)) assertThat(VmSafeMath.add(java.lang.Long.MAX_VALUE - 1, 1)).isEqualTo(java.lang.Long.MAX_VALUE)
.isEqualTo(java.lang.Long.MAX_VALUE)
} }
@Test @Test
fun `add long - overflow #1`() { fun `add long - overflow #1`() {
assertThrows<VmEvalException> { assertThrows<VmEvalException> { VmSafeMath.add(java.lang.Long.MAX_VALUE, 1) }
VmSafeMath.add(java.lang.Long.MAX_VALUE, 1)
}
} }
@Test @Test
fun `add long - overflow #2`() { fun `add long - overflow #2`() {
assertThrows<VmEvalException> { assertThrows<VmEvalException> { VmSafeMath.add(java.lang.Long.MIN_VALUE, -1) }
VmSafeMath.add(java.lang.Long.MIN_VALUE, -1)
}
} }
@Test @Test
fun `add long - overflow #3`() { fun `add long - overflow #3`() {
assertThrows<VmEvalException> { assertThrows<VmEvalException> {
VmSafeMath.add( VmSafeMath.add(java.lang.Long.MAX_VALUE / 2, java.lang.Long.MAX_VALUE / 3 * 2)
java.lang.Long.MAX_VALUE / 2,
java.lang.Long.MAX_VALUE / 3 * 2
)
} }
} }
} }

View File

@@ -1,10 +1,22 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import org.pkl.core.SecurityManagers
import org.pkl.core.module.ModuleKeys
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.net.URI
class VmUtilsTest { class VmUtilsTest {
@Test @Test

View File

@@ -1,3 +1,18 @@
/**
* Copyright © 2024 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.core.runtime package org.pkl.core.runtime
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

Some files were not shown because too many files have changed in this diff Show More