mirror of
https://github.com/apple/pkl.git
synced 2026-07-08 05:55:13 +02:00
Initial commit
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package org.pkl.gradle
|
||||
|
||||
import org.pkl.commons.createParentDirectories
|
||||
import org.pkl.commons.readString
|
||||
import org.pkl.commons.writeString
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gradle.testkit.runner.BuildResult
|
||||
import org.gradle.testkit.runner.GradleRunner
|
||||
import org.gradle.testkit.runner.UnexpectedBuildFailure
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
|
||||
abstract class AbstractTest {
|
||||
private val gradleVersion: String? = System.getProperty("testGradleVersion")
|
||||
|
||||
private val gradleDistributionUrl: String? = System.getProperty("testGradleDistributionUrl")
|
||||
|
||||
@TempDir
|
||||
protected lateinit var testProjectDir: Path
|
||||
|
||||
protected fun runTask(
|
||||
taskName: String,
|
||||
expectFailure: Boolean = false
|
||||
): BuildResult {
|
||||
|
||||
val runner = GradleRunner.create()
|
||||
.withProjectDir(testProjectDir.toFile())
|
||||
.withArguments("--stacktrace", "--no-build-cache", taskName)
|
||||
.withPluginClasspath()
|
||||
.withDebug(true)
|
||||
|
||||
if (gradleVersion != null) {
|
||||
runner.withGradleVersion(gradleVersion)
|
||||
}
|
||||
if (gradleDistributionUrl != null) {
|
||||
runner.withGradleDistribution(URI(gradleDistributionUrl))
|
||||
}
|
||||
|
||||
return try {
|
||||
if (expectFailure) runner.buildAndFail() else runner.build()
|
||||
} catch (e: UnexpectedBuildFailure) {
|
||||
throw AssertionError(e.buildResult.output)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun writeFile(fileName: String, contents: String): Path {
|
||||
return testProjectDir.resolve(fileName)
|
||||
.apply { createParentDirectories() }
|
||||
.writeString(contents.trimIndent())
|
||||
}
|
||||
|
||||
protected fun checkFileContents(file: Path, contents: String) {
|
||||
assertThat(file).exists()
|
||||
assertThat(file.readString().trim())
|
||||
.isEqualTo(contents.trim())
|
||||
}
|
||||
|
||||
protected fun checkTextContains(text: String, vararg contents: String) {
|
||||
for (content in contents) {
|
||||
try {
|
||||
assertThat(text).contains(content.trimMargin())
|
||||
} catch (e: AssertionError) {
|
||||
// to get diff output in IDE
|
||||
assertThat(text).isEqualTo(content.trimMargin())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
package org.pkl.gradle
|
||||
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.pkl.commons.readString
|
||||
import org.pkl.commons.test.PackageServer
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.nio.file.Path
|
||||
|
||||
class EvaluatorsTest : AbstractTest() {
|
||||
@Test
|
||||
fun `render Pcf`() {
|
||||
writeBuildFile("pcf")
|
||||
|
||||
writePklFile()
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.pcf")
|
||||
checkFileContents(
|
||||
outputFile, """
|
||||
person {
|
||||
name = "Pigeon"
|
||||
age = 30
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `render YAML`() {
|
||||
writeBuildFile("yaml")
|
||||
|
||||
writePklFile()
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.yaml")
|
||||
checkFileContents(
|
||||
outputFile, """
|
||||
person:
|
||||
name: Pigeon
|
||||
age: 30
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `render JSON`() {
|
||||
writeBuildFile("json")
|
||||
|
||||
writePklFile()
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.json")
|
||||
checkFileContents(
|
||||
outputFile, """
|
||||
{
|
||||
"person": {
|
||||
"name": "Pigeon",
|
||||
"age": 30
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `render plist`() {
|
||||
writeBuildFile("plist")
|
||||
|
||||
writePklFile()
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.plist")
|
||||
checkFileContents(
|
||||
outputFile, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>person</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Pigeon</string>
|
||||
<key>age</key>
|
||||
<integer>30</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `set external properties`() {
|
||||
writeBuildFile(
|
||||
"pcf", """
|
||||
externalProperties = [prop1: "value1", prop2: "value2"]
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
writePklFile(
|
||||
"""
|
||||
prop1 = read("prop:prop1")
|
||||
prop2 = read("prop:prop2")
|
||||
other = read?("prop:other")
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.pcf")
|
||||
checkFileContents(
|
||||
outputFile, """
|
||||
prop1 = "value1"
|
||||
prop2 = "value2"
|
||||
other = null
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `defaults to empty environment variables`() {
|
||||
writeBuildFile("pcf")
|
||||
|
||||
writePklFile(
|
||||
"""
|
||||
prop1 = read?("env:USER")
|
||||
prop2 = read?("env:PATH")
|
||||
prop3 = read?("env:JAVA_HOME")
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.pcf")
|
||||
checkFileContents(
|
||||
outputFile, """
|
||||
prop1 = null
|
||||
prop2 = null
|
||||
prop3 = null
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `set environment variables`() {
|
||||
writeBuildFile(
|
||||
"pcf", """
|
||||
environmentVariables = [VAR1: "value1", VAR2: "value2"]
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
writePklFile(
|
||||
"""
|
||||
prop1 = read("env:VAR1")
|
||||
prop2 = read("env:VAR2")
|
||||
other = read?("env:OTHER")
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.pcf")
|
||||
checkFileContents(
|
||||
outputFile, """
|
||||
prop1 = "value1"
|
||||
prop2 = "value2"
|
||||
other = null
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no source modules`() {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
evaluators {
|
||||
evalTest {
|
||||
outputFormat = "pcf"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val result = runTask("evalTest", true)
|
||||
assertThat(result.output).contains("No source modules specified.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `source module URIs`() {
|
||||
val pklFile = writeFile(
|
||||
"test.pkl", """
|
||||
person {
|
||||
name = "Pigeon"
|
||||
age = 20 + 10
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
evaluators {
|
||||
evalTest {
|
||||
sourceModules = [uri("modulepath:/test.pkl")]
|
||||
modulePath.from "${pklFile.parent}"
|
||||
outputFile = layout.projectDirectory.file("test.pcf")
|
||||
settingsModule = "pkl:settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.pcf")
|
||||
checkFileContents(
|
||||
outputFile, """
|
||||
person {
|
||||
name = "Pigeon"
|
||||
age = 30
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cannot evaluate module located outside evalRootDir`() {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
evaluators {
|
||||
evalTest {
|
||||
evalRootDir = file("/non/existing")
|
||||
sourceModules = ["test.pkl"]
|
||||
settingsModule = "pkl:settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val result = runTask("evalTest", expectFailure = true)
|
||||
assertThat(result.output).contains("Refusing to load module")
|
||||
assertThat(result.output).contains("because it does not match any entry in the module allowlist (`--allowed-modules`).")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `evaluation timeout`() {
|
||||
// Gradle 4.10 doesn't automatically import Duration
|
||||
writeBuildFile(
|
||||
"pcf", """
|
||||
evalTimeout = java.time.Duration.ofMillis(100)
|
||||
"""
|
||||
)
|
||||
|
||||
writePklFile(
|
||||
"""
|
||||
function fib(n) = if (n < 2) 0 else fib(n - 1) + fib(n - 2)
|
||||
x = fib(100)
|
||||
"""
|
||||
)
|
||||
|
||||
val result = runTask("evalTest", expectFailure = true)
|
||||
assertThat(result.output).contains("timed out")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `module output separator`() {
|
||||
val outputFile = testProjectDir.resolve("test.pcf")
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
evaluators {
|
||||
evalTask {
|
||||
moduleOutputSeparator = "// hello"
|
||||
sourceModules = ["test1.pkl", "test2.pkl"]
|
||||
settingsModule = "pkl:settings"
|
||||
outputFile = layout.projectDirectory.file("test.pcf")
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
writeFile(
|
||||
"test1.pkl",
|
||||
"foo = 1"
|
||||
)
|
||||
writeFile(
|
||||
"test2.pkl",
|
||||
"bar = 2"
|
||||
)
|
||||
runTask("evalTask")
|
||||
|
||||
checkFileContents(outputFile, """
|
||||
foo = 1
|
||||
// hello
|
||||
bar = 2
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `compliant file URIs`() {
|
||||
writeBuildFile("pcf")
|
||||
writeFile("test.pkl", """
|
||||
import "pkl:reflect"
|
||||
output {
|
||||
text = reflect.Module(module).uri
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
runTask("evalTest")
|
||||
|
||||
val outputFile = testProjectDir.resolve("test.pcf")
|
||||
assertThat(outputFile).exists()
|
||||
assertThat(outputFile.readString().trim()).startsWith("file:///")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple file output`() {
|
||||
writeBuildFile("pcf", """
|
||||
multipleFileOutputDir = layout.projectDirectory.dir("my-output")
|
||||
""".trimIndent())
|
||||
writeFile(
|
||||
"test.pkl",
|
||||
"""
|
||||
output {
|
||||
files {
|
||||
["output-1.txt"] {
|
||||
text = "My output 1"
|
||||
}
|
||||
["output-2.txt"] {
|
||||
text = "My output 2"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
runTask("evalTest")
|
||||
checkFileContents(testProjectDir.resolve("my-output/output-1.txt"), "My output 1")
|
||||
checkFileContents(testProjectDir.resolve("my-output/output-2.txt"), "My output 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun expression() {
|
||||
writeBuildFile("yaml", """
|
||||
expression = "metadata.name"
|
||||
outputFile = layout.projectDirectory.file("output.txt")
|
||||
""".trimIndent())
|
||||
writeFile(
|
||||
"test.pkl",
|
||||
"""
|
||||
metadata {
|
||||
name = "Uni"
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
runTask("evalTest")
|
||||
checkFileContents(testProjectDir.resolve("output.txt"), "Uni")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `explicitly set cache dir`(@TempDir tempDir: Path) {
|
||||
writeBuildFile("pcf", """
|
||||
moduleCacheDir = file("$tempDir")
|
||||
""".trimIndent())
|
||||
writeFile(
|
||||
"test.pkl",
|
||||
"""
|
||||
import "package://localhost:12110/birds@0.5.0#/Bird.pkl"
|
||||
|
||||
res = new Bird { name = "Wally"; favoriteFruit { name = "bananas" } }
|
||||
""".trimIndent()
|
||||
)
|
||||
PackageServer.populateCacheDir(tempDir)
|
||||
runTask("evalTest")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `explicitly set project dir`() {
|
||||
writeBuildFile("pcf", "projectDir = file(\"proj1\")", listOf("proj1/foo.pkl"))
|
||||
|
||||
writeFile("proj1/PklProject", """
|
||||
amends "pkl:Project"
|
||||
|
||||
dependencies {
|
||||
["proj2"] = import("../proj2/PklProject")
|
||||
}
|
||||
|
||||
package {
|
||||
name = "proj1"
|
||||
baseUri = "package://localhost:12110/\(name)"
|
||||
version = "1.0.0"
|
||||
packageZipUrl = "https://localhost:12110/\(name)@\(version).zip"
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
writeFile("proj2/PklProject", """
|
||||
amends "pkl:Project"
|
||||
|
||||
package {
|
||||
name = "proj2"
|
||||
baseUri = "package://localhost:12110/\(name)"
|
||||
version = "1.0.0"
|
||||
packageZipUrl = "https://localhost:12110/\(name)@\(version).zip"
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
writeFile("proj1/PklProject.deps.json", """
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"resolvedDependencies": {
|
||||
"package://localhost:12110/proj2@1": {
|
||||
"type": "local",
|
||||
"uri": "projectpackage://localhost:12110/proj2@1.0.0",
|
||||
"path": "../proj2"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
writeFile("proj2/PklProject.deps.json", """
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"resolvedDependencies": {}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
writeFile("proj1/foo.pkl", """
|
||||
module proj1.foo
|
||||
|
||||
bar: String = import("@proj2/baz.pkl").qux
|
||||
""".trimIndent())
|
||||
|
||||
writeFile("proj2/baz.pkl", """
|
||||
qux: String = "Contents of @proj2/qux"
|
||||
""".trimIndent())
|
||||
|
||||
runTask("evalTest")
|
||||
assertThat(testProjectDir.resolve("proj1/foo.pcf")).exists()
|
||||
}
|
||||
|
||||
private fun writeBuildFile(
|
||||
// don't use `org.pkl.core.OutputFormat`
|
||||
// because test compile class path doesn't contain pkl-core
|
||||
outputFormat: String,
|
||||
additionalContents: String = "",
|
||||
sourceModules: List<String> = listOf("test.pkl")
|
||||
) {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
evaluators {
|
||||
evalTest {
|
||||
sourceModules = [${sourceModules.joinToString(separator = ", ") { "\"$it\"" }}]
|
||||
outputFormat = "$outputFormat"
|
||||
settingsModule = "pkl:settings"
|
||||
$additionalContents
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
private fun writePklFile(
|
||||
contents: String = """
|
||||
person {
|
||||
name = "Pigeon"
|
||||
age = 20 + 10
|
||||
}
|
||||
"""
|
||||
) {
|
||||
writeFile("test.pkl", contents)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package org.pkl.gradle
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.io.path.listDirectoryEntries
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class JavaCodeGeneratorsTest : AbstractTest() {
|
||||
@Test
|
||||
fun `generate code`() {
|
||||
writeBuildFile()
|
||||
writePklFile()
|
||||
|
||||
runTask("configClasses")
|
||||
|
||||
val baseDir = testProjectDir.resolve("build/generated/java/org")
|
||||
val moduleFile = baseDir.resolve("Mod.java")
|
||||
|
||||
assertThat(baseDir.listDirectoryEntries().count()).isEqualTo(1)
|
||||
assertThat(moduleFile).exists()
|
||||
|
||||
val text = moduleFile.readText()
|
||||
|
||||
// shading must not affect generated code
|
||||
assertThat(text).doesNotContain("org.pkl.thirdparty")
|
||||
|
||||
checkTextContains(
|
||||
text, """
|
||||
|public final class Mod {
|
||||
| public final @Nonnull Object other;
|
||||
"""
|
||||
)
|
||||
|
||||
checkTextContains(
|
||||
text, """
|
||||
| public static final class Person {
|
||||
| public final @Nonnull String name;
|
||||
|
|
||||
| public final @Nonnull List<@Nonnull Address> addresses;
|
||||
"""
|
||||
)
|
||||
|
||||
checkTextContains(
|
||||
text, """
|
||||
| public static final class Address {
|
||||
| public final @Nonnull String street;
|
||||
|
|
||||
| public final long zip;
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `compile generated code`() {
|
||||
writeBuildFile()
|
||||
writeFile("mod.pkl", """
|
||||
module org.mod
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
addresses: List<Address?>
|
||||
}
|
||||
|
||||
class Address {
|
||||
street: String
|
||||
zip: Int
|
||||
}
|
||||
|
||||
other: Any = 42
|
||||
""".trimIndent())
|
||||
|
||||
runTask("compileJava")
|
||||
|
||||
val classesDir = testProjectDir.resolve("build/classes/java/main")
|
||||
val moduleClassFile = classesDir.resolve("org/Mod.class")
|
||||
val personClassFile = classesDir.resolve("org/Mod\$Person.class")
|
||||
val addressClassFile = classesDir.resolve("org/Mod\$Address.class")
|
||||
assertThat(moduleClassFile).exists()
|
||||
assertThat(personClassFile).exists()
|
||||
assertThat(addressClassFile).exists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no source modules`() {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
evaluators {
|
||||
evalTest {
|
||||
outputFormat = "pcf"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val result = runTask("evalTest", true)
|
||||
assertThat(result.output).contains("No source modules specified.")
|
||||
}
|
||||
|
||||
private fun writeBuildFile() {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "java"
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "javax.inject:javax.inject:1"
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
}
|
||||
|
||||
pkl {
|
||||
javaCodeGenerators {
|
||||
configClasses {
|
||||
sourceModules = ["mod.pkl"]
|
||||
outputDir = file("build/generated")
|
||||
paramsAnnotation = "javax.inject.Named"
|
||||
nonNullAnnotation = "javax.annotation.Nonnull"
|
||||
settingsModule = "pkl:settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
private fun writeGradlePropertiesFile() {
|
||||
writeFile("gradle.properties", """
|
||||
systemProp.http.proxyHost=proxy.config.pcp.local
|
||||
systemProp.http.proxyPort=3128
|
||||
systemProp.http.nonProxyHosts=localhost|*.apple.com
|
||||
|
||||
systemProp.https.proxyHost=proxy.config.pcp.local
|
||||
systemProp.https.proxyPort=3128
|
||||
systemProp.https.nonProxyHosts=localhost|*.apple.com
|
||||
""")
|
||||
}
|
||||
|
||||
private fun writePklFile() {
|
||||
writeFile(
|
||||
"mod.pkl", """
|
||||
module org.mod
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
addresses: List<Address>
|
||||
}
|
||||
|
||||
class Address {
|
||||
street: String
|
||||
zip: Int
|
||||
}
|
||||
|
||||
other = 42
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package org.pkl.gradle
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.io.path.listDirectoryEntries
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class KotlinCodeGeneratorsTest : AbstractTest() {
|
||||
@Test
|
||||
fun `generate code`() {
|
||||
writeBuildFile()
|
||||
writePklFile()
|
||||
|
||||
runTask("configClasses")
|
||||
|
||||
val baseDir = testProjectDir.resolve("build/generated/kotlin/org")
|
||||
val kotlinFile = baseDir.resolve("Mod.kt")
|
||||
|
||||
assertThat(baseDir.listDirectoryEntries().count()).isEqualTo(1)
|
||||
assertThat(kotlinFile).exists()
|
||||
|
||||
val text = kotlinFile.readText()
|
||||
|
||||
// shading must not affect generated code
|
||||
assertThat(text).doesNotContain("org.pkl.thirdparty")
|
||||
|
||||
checkTextContains(
|
||||
text, """
|
||||
|data class Mod(
|
||||
| val other: Any?
|
||||
|)
|
||||
"""
|
||||
)
|
||||
|
||||
checkTextContains(
|
||||
text, """
|
||||
| data class Person(
|
||||
| val name: String,
|
||||
| val addresses: List<Address>
|
||||
| )
|
||||
"""
|
||||
)
|
||||
|
||||
checkTextContains(
|
||||
text, """
|
||||
| open class Address(
|
||||
| open val street: String,
|
||||
| open val zip: Long
|
||||
| )
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `compile generated code`() {
|
||||
writeBuildFile()
|
||||
writePklFile()
|
||||
runTask("compileKotlin")
|
||||
|
||||
val classesDir = testProjectDir.resolve("build/classes/kotlin/main")
|
||||
val moduleClassFile = classesDir.resolve("org/Mod.class")
|
||||
val personClassFile = classesDir.resolve("org/Mod\$Person.class")
|
||||
val addressClassFile = classesDir.resolve("org/Mod\$Address.class")
|
||||
assertThat(moduleClassFile).exists()
|
||||
assertThat(personClassFile).exists()
|
||||
assertThat(addressClassFile).exists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no source modules`() {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
evaluators {
|
||||
evalTest {
|
||||
outputFormat = "pcf"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val result = runTask("evalTest", true)
|
||||
assertThat(result.output).contains("No source modules specified.")
|
||||
}
|
||||
|
||||
private fun writeBuildFile() {
|
||||
val kotlinVersion = "1.6.0"
|
||||
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") {
|
||||
exclude module: "kotlin-android-extensions"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
apply plugin: "kotlin"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||
}
|
||||
|
||||
pkl {
|
||||
kotlinCodeGenerators {
|
||||
configClasses {
|
||||
sourceModules = ["mod.pkl"]
|
||||
outputDir = file("build/generated")
|
||||
settingsModule = "pkl:settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
private fun writePklFile() {
|
||||
writeFile(
|
||||
"mod.pkl", """
|
||||
module org.mod
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
addresses: List<Address>
|
||||
}
|
||||
|
||||
// "open" to test generating regular class
|
||||
open class Address {
|
||||
street: String
|
||||
zip: Int
|
||||
}
|
||||
|
||||
other = 42
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.pkl.gradle
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class PkldocGeneratorsTest : AbstractTest() {
|
||||
@Test
|
||||
fun `generate docs`() {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
pkldocGenerators {
|
||||
pkldoc {
|
||||
sourceModules = ["person.pkl", "doc-package-info.pkl"]
|
||||
outputDir = file("build/pkldoc")
|
||||
settingsModule = "pkl:settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
writeFile(
|
||||
"doc-package-info.pkl", """
|
||||
/// A test package.
|
||||
amends "pkl:DocPackageInfo"
|
||||
name = "test"
|
||||
version = "1.0.0"
|
||||
importUri = "https://pkl-lang.org/"
|
||||
authors { "publisher@apple.com" }
|
||||
sourceCode = "sources.apple.com/"
|
||||
issueTracker = "issues.apple.com"
|
||||
""".trimIndent()
|
||||
)
|
||||
writeFile(
|
||||
"person.pkl", """
|
||||
module test.person
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
addresses: List<Address>
|
||||
}
|
||||
|
||||
class Address {
|
||||
street: String
|
||||
zip: Int
|
||||
}
|
||||
|
||||
other = 42
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
runTask("pkldoc")
|
||||
|
||||
val baseDir = testProjectDir.resolve("build/pkldoc")
|
||||
val mainFile = baseDir.resolve("index.html")
|
||||
val packageFile = baseDir.resolve("test/1.0.0/index.html")
|
||||
val moduleFile = baseDir.resolve("test/1.0.0/person/index.html")
|
||||
val personFile = baseDir.resolve("test/1.0.0/person/Person.html")
|
||||
val addressFile = baseDir.resolve("test/1.0.0/person/Address.html")
|
||||
|
||||
assertThat(mainFile).exists()
|
||||
assertThat(packageFile).exists()
|
||||
assertThat(moduleFile).exists()
|
||||
assertThat(personFile).exists()
|
||||
assertThat(addressFile).exists()
|
||||
|
||||
checkTextContains(mainFile.readText(), "<html>", "test")
|
||||
checkTextContains(packageFile.readText(), "<html>", "test.person")
|
||||
checkTextContains(moduleFile.readText(), "<html>", "Person", "Address", "other")
|
||||
checkTextContains(personFile.readText(), "<html>", "name", "addresses")
|
||||
checkTextContains(addressFile.readText(), "<html>", "street", "zip")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no source modules`() {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
pkldocGenerators {
|
||||
pkldoc {
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val result = runTask("pkldoc", true)
|
||||
assertThat(result.output).contains("No source modules specified.")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.pkl.gradle
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ProjectPackageTest : AbstractTest() {
|
||||
@Test
|
||||
fun basic() {
|
||||
writeBuildFile("skipPublishCheck.set(true)")
|
||||
writeProjectContent()
|
||||
runTask("createMyPackages")
|
||||
assertThat(testProjectDir.resolve("build/generated/pkl/packages/proj1@1.0.0.zip")).exists()
|
||||
assertThat(testProjectDir.resolve("build/generated/pkl/packages/proj1@1.0.0")).exists()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `custom output dir`() {
|
||||
writeBuildFile( """
|
||||
outputPath.set(file("thepackages"))
|
||||
skipPublishCheck.set(true)
|
||||
""")
|
||||
writeProjectContent()
|
||||
runTask("createMyPackages")
|
||||
assertThat(testProjectDir.resolve("thepackages/proj1@1.0.0.zip")).exists()
|
||||
assertThat(testProjectDir.resolve("thepackages/proj1@1.0.0")).exists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `junit dir`() {
|
||||
writeBuildFile("""
|
||||
junitReportsDir.set(file("test-reports"))
|
||||
skipPublishCheck.set(true)
|
||||
""".trimIndent())
|
||||
writeProjectContent()
|
||||
runTask("createMyPackages")
|
||||
assertThat(testProjectDir.resolve("test-reports")).isNotEmptyDirectory()
|
||||
}
|
||||
|
||||
private fun writeBuildFile(additionalContents: String = "") {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
project {
|
||||
packagers {
|
||||
createMyPackages {
|
||||
projectDirectories.from(file("proj1"))
|
||||
settingsModule = "pkl:settings"
|
||||
$additionalContents
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
private fun writeProjectContent() {
|
||||
writeFile("proj1/PklProject", """
|
||||
amends "pkl:Project"
|
||||
|
||||
package {
|
||||
name = "proj1"
|
||||
baseUri = "package://localhost:12110/proj1"
|
||||
version = "1.0.0"
|
||||
packageZipUrl = "https://localhost:12110/proj1@\(version).zip"
|
||||
apiTests {
|
||||
"tests.pkl"
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
writeFile("proj1/PklProject.deps.json", """
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"dependencies": {}
|
||||
}
|
||||
""".trimIndent())
|
||||
writeFile("proj1/foo.pkl", """
|
||||
module proj1.foo
|
||||
|
||||
bar: String
|
||||
""".trimIndent())
|
||||
writeFile("proj1/tests.pkl", """
|
||||
amends "pkl:test"
|
||||
|
||||
facts {
|
||||
["it works"] {
|
||||
1 == 1
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
writeFile("foo.txt", "The contents of foo.txt")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.pkl.gradle
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ProjectResolveTest : AbstractTest() {
|
||||
@Test
|
||||
fun basic() {
|
||||
writeBuildFile()
|
||||
writeProjectContent()
|
||||
runTask("resolveMyProj")
|
||||
assertThat(testProjectDir.resolve("proj1/PklProject.deps.json")).hasContent("""
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"resolvedDependencies": {}
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
private fun writeBuildFile(additionalContents: String = "") {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
project {
|
||||
resolvers {
|
||||
resolveMyProj {
|
||||
projectDirectories.from(file("proj1"))
|
||||
settingsModule = "pkl:settings"
|
||||
$additionalContents
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
private fun writeProjectContent() {
|
||||
writeFile("proj1/PklProject", """
|
||||
amends "pkl:Project"
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package org.pkl.gradle
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class TestsTest : AbstractTest() {
|
||||
|
||||
@Test
|
||||
fun `facts pass`() {
|
||||
writeBuildFile()
|
||||
|
||||
writePklFile()
|
||||
|
||||
val res = runTask("evalTest")
|
||||
assertThat(res.output).contains("should pass ✅")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `facts fail`() {
|
||||
writeBuildFile()
|
||||
|
||||
writePklFile(additionalFacts = """
|
||||
["should fail"] {
|
||||
1 == 3
|
||||
"foo" == "bar"
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
val res = runTask("evalTest", expectFailure = true)
|
||||
assertThat(res.output).contains("should fail ❌")
|
||||
assertThat(res.output).contains("1 == 3 ❌")
|
||||
assertThat(res.output).contains(""""foo" == "bar" ❌""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun error() {
|
||||
writeBuildFile()
|
||||
|
||||
writePklFile(additionalFacts = """
|
||||
["error"] {
|
||||
throw("exception")
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
val output = runTask("evalTest", expectFailure = true).output.stripFilesAndLines()
|
||||
|
||||
assertThat(output.trimStart()).startsWith("""
|
||||
> Task :evalTest FAILED
|
||||
module test (file:///file, line x)
|
||||
test ❌
|
||||
Error:
|
||||
–– Pkl Error ––
|
||||
exception
|
||||
|
||||
9 | throw("exception")
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
at test#facts["error"][#1] (file:///file, line x)
|
||||
|
||||
3 | facts {
|
||||
^^^^^^^
|
||||
at test#facts (file:///file, line x)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `full example`() {
|
||||
writePklFile(contents = bigTest)
|
||||
writeFile("test.pkl-expected.pcf", bigTestExpected)
|
||||
|
||||
writeBuildFile()
|
||||
|
||||
val output = runTask("evalTest", expectFailure = true).output.stripFilesAndLines()
|
||||
|
||||
assertThat(output.trimStart()).contains("""
|
||||
module test (file:///file, line x)
|
||||
sum numbers ✅
|
||||
divide numbers ✅
|
||||
fail ❌
|
||||
4 == 9 ❌ (file:///file, line x)
|
||||
"foo" == "bar" ❌ (file:///file, line x)
|
||||
user 0 ✅
|
||||
user 1 ❌
|
||||
(file:///file, line x)
|
||||
Expected: (file:///file, line x)
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 40
|
||||
}
|
||||
Actual: (file:///file, line x)
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 41
|
||||
}
|
||||
user 1 ❌
|
||||
(file:///file, line x)
|
||||
Expected: (file:///file, line x)
|
||||
new {
|
||||
name = "Parrot"
|
||||
age = 35
|
||||
}
|
||||
Actual: (file:///file, line x)
|
||||
new {
|
||||
name = "Welma"
|
||||
age = 35
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `overwrite expected examples`() {
|
||||
writePklFile(additionalExamples = """
|
||||
["user 0"] {
|
||||
new {
|
||||
name = "Cool"
|
||||
age = 11
|
||||
}
|
||||
}
|
||||
["user 1"] {
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 41
|
||||
}
|
||||
new {
|
||||
name = "Welma"
|
||||
age = 35
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
writeFile("test.pkl-expected.pcf", bigTestExpected)
|
||||
|
||||
writeBuildFile("overwrite = true")
|
||||
|
||||
val output = runTask("evalTest").output
|
||||
|
||||
assertThat(output).contains("user 0 ✍️")
|
||||
assertThat(output).contains("user 1 ✍️")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `JUnit reports`() {
|
||||
val pklFile = writePklFile(contents = bigTest)
|
||||
writeFile("test.pkl-expected.pcf", bigTestExpected)
|
||||
|
||||
writeBuildFile("junitReportsDir = file('${pklFile.parent}/build')")
|
||||
|
||||
runTask("evalTest", expectFailure = true)
|
||||
|
||||
val outputFile = testProjectDir.resolve("build/test.xml")
|
||||
val report = outputFile.readText().stripFilesAndLines()
|
||||
|
||||
assertThat(report).isEqualTo("""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuite name="test" tests="6" failures="4">
|
||||
<testcase classname="test" name="sum numbers"></testcase>
|
||||
<testcase classname="test" name="divide numbers"></testcase>
|
||||
<testcase classname="test" name="fail">
|
||||
<failure message="Fact Failure">4 == 9 ❌ (file:///file, line x)</failure>
|
||||
<failure message="Fact Failure">"foo" == "bar" ❌ (file:///file, line x)</failure>
|
||||
</testcase>
|
||||
<testcase classname="test" name="user 0"></testcase>
|
||||
<testcase classname="test" name="user 1">
|
||||
<failure message="Example Failure">(file:///file, line x)
|
||||
Expected: (file:///file, line x)
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 40
|
||||
}
|
||||
Actual: (file:///file, line x)
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 41
|
||||
}</failure>
|
||||
</testcase>
|
||||
<testcase classname="test" name="user 1">
|
||||
<failure message="Example Failure">(file:///file, line x)
|
||||
Expected: (file:///file, line x)
|
||||
new {
|
||||
name = "Parrot"
|
||||
age = 35
|
||||
}
|
||||
Actual: (file:///file, line x)
|
||||
new {
|
||||
name = "Welma"
|
||||
age = 35
|
||||
}</failure>
|
||||
</testcase>
|
||||
<system-err><![CDATA[8 = 8
|
||||
]]></system-err>
|
||||
</testsuite>
|
||||
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
private val bigTest = """
|
||||
amends "pkl:test"
|
||||
|
||||
local function sum(a, b) = a + b
|
||||
|
||||
facts {
|
||||
["sum numbers"] {
|
||||
sum(3, 5) == trace(8)
|
||||
sum(3, 0) == 3
|
||||
}
|
||||
["divide numbers"] {
|
||||
(8 / 4) == 2
|
||||
(12 / 2) == 6
|
||||
}
|
||||
["fail"] {
|
||||
4 == 9
|
||||
"foo" == "bar"
|
||||
}
|
||||
}
|
||||
|
||||
examples {
|
||||
["user 0"] {
|
||||
new {
|
||||
name = "Cool"
|
||||
age = 11
|
||||
}
|
||||
}
|
||||
["user 1"] {
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 41
|
||||
}
|
||||
new {
|
||||
name = "Welma"
|
||||
age = 35
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
private val bigTestExpected = """
|
||||
examples {
|
||||
["user 0"] {
|
||||
new {
|
||||
name = "Cool"
|
||||
age = 11
|
||||
}
|
||||
}
|
||||
["user 1"] {
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 40
|
||||
}
|
||||
new {
|
||||
name = "Parrot"
|
||||
age = 35
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
private fun writeBuildFile(additionalContents: String = "") {
|
||||
writeFile(
|
||||
"build.gradle", """
|
||||
plugins {
|
||||
id "org.pkl-lang"
|
||||
}
|
||||
|
||||
pkl {
|
||||
tests {
|
||||
evalTest {
|
||||
sourceModules = ["test.pkl"]
|
||||
settingsModule = "pkl:settings"
|
||||
$additionalContents
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
private fun writePklFile(
|
||||
additionalFacts: String = "",
|
||||
additionalExamples: String = "",
|
||||
contents: String = """
|
||||
amends "pkl:test"
|
||||
|
||||
facts {
|
||||
["should pass"] {
|
||||
1 == 1
|
||||
10 == 10
|
||||
}
|
||||
$additionalFacts
|
||||
}
|
||||
|
||||
examples {
|
||||
$additionalExamples
|
||||
}
|
||||
"""
|
||||
): Path {
|
||||
return writeFile("test.pkl", contents)
|
||||
}
|
||||
|
||||
private fun String.stripFilesAndLines(): String =
|
||||
replace(Regex("""\(file:///.*, line \d+\)"""), "(file:///file, line x)")
|
||||
}
|
||||
Reference in New Issue
Block a user