Allow for specified Cargo commands to be run on a subset of the integration tests (#1165)

This commit allows for the `modules` and `cargoCommands` properties to
be specified when running the `codegen-test` and `codegen-server-test`
Gradle modules. The `modules` property allows one to only generate a
subset of the integration test services, while the `cargoCommands`
property allows one to specify the Cargo commands to be run on the
generated Rust crates.

This functionality can be useful to reduce development iteration cycles. For
instance, to only run `cargo test` on the integration test
`simple.smithy`:

```sh
./gradlew codegen-test:build -P cargoCommands='test' -P modules='simple'
```

This commit also refactors the buildscripts of the `codegen-test` and
the `codegen-server-test` modules, extracting shared functionality to
`buildSrc/src/main/kotlin/CodegenTestCommon.kt`.
This commit is contained in:
david-perez 2022-02-18 14:14:36 +01:00 committed by GitHub
parent 329c036e6d
commit c3ef017d41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 193 additions and 122 deletions

View File

@ -0,0 +1,104 @@
import java.lang.IllegalArgumentException
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
/**
* This file contains common functionality shared across the buildscripts for the `codegen-test` and `codegen-server-test`
* modules.
*/
data class CodegenTest(val service: String, val module: String, val extraConfig: String? = null)
fun generateSmithyBuild(projectDir: String, pluginName: String, tests: List<CodegenTest>): String {
val projections = tests.joinToString(",\n") {
"""
"${it.module}": {
"plugins": {
"$pluginName": {
"runtimeConfig": {
"relativePath": "$projectDir/rust-runtime"
},
"service": "${it.service}",
"module": "${it.module}",
"moduleVersion": "0.0.1",
"moduleDescription": "test",
"moduleAuthors": ["protocoltest@example.com"]
${it.extraConfig ?: ""}
}
}
}
""".trimIndent()
}
return """
{
"version": "1.0",
"projections": {
$projections
}
}
""".trimIndent()
}
enum class Cargo(val toString: String) {
CHECK("cargoCheck"),
TEST("cargoTest"),
DOCS("cargoDocs"),
CLIPPY("cargoClippy");
}
fun generateCargoWorkspace(pluginName: String, tests: List<CodegenTest>) =
"""
[workspace]
members = [
${tests.joinToString(",") { "\"${it.module}/$pluginName\"" }}
]
""".trimIndent()
/**
* Filter the service integration tests for which to generate Rust crates in [allTests] using the given [properties].
*/
fun codegenTests(properties: PropertyRetriever, allTests: List<CodegenTest>): List<CodegenTest> {
val modulesOverride = properties.get("modules")?.split(",")?.map { it.trim() }
val ret = if (modulesOverride != null) {
println("modulesOverride: $modulesOverride")
allTests.filter { modulesOverride.contains(it.module) }
} else {
allTests
}
require(ret.isNotEmpty()) {
"None of the provided module overrides (`$modulesOverride`) are valid test services (`${allTests.map { it.module }}`)"
}
return ret
}
val AllCargoCommands = listOf(Cargo.CHECK, Cargo.TEST, Cargo.CLIPPY, Cargo.DOCS)
/**
* Filter the Cargo commands to be run on the generated Rust crates using the given [properties].
* The list of Cargo commands that is run by default is defined in [AllCargoCommands].
*/
fun cargoCommands(properties: PropertyRetriever): List<Cargo> {
val cargoCommandsOverride = properties.get("cargoCommands")?.split(",")?.map { it.trim() }?.map {
when(it) {
"check" -> Cargo.CHECK
"test" -> Cargo.TEST
"docs" -> Cargo.DOCS
"clippy" -> Cargo.CLIPPY
else -> throw IllegalArgumentException("Unexpected Cargo command `$it` (valid commands are `check`, `test`, `docs`, `clippy`)")
}
}
val ret = if (cargoCommandsOverride != null) {
println("cargoCommandsOverride: $cargoCommandsOverride")
AllCargoCommands.filter { cargoCommandsOverride.contains(it) }
} else {
AllCargoCommands
}
require(ret.isNotEmpty()) {
"None of the provided cargo commands (`$cargoCommandsOverride`) are valid cargo commands (`${AllCargoCommands.map { it.toString }}`)"
}
return ret
}

View File

@ -1,8 +1,6 @@
# Codegen Integration Test
This module defines an integration test of the code generation machinery. `.build.gradle.kts` will generate a `smithy-build.json` file as part of the build. The Smithy build plugin then invokes our codegen machinery and generates Rust crates.
# Server Codegen Integration Tests
The `test` task will run `cargo check` and `cargo clippy` to validate that the generated Rust compiles and is idiomatic.
## Usage
```
../gradlew test
```
Refer to `../codegen-test/README.md` for documentation on how to use this
module. This module is analogous to the `codegen-test` one, but it's named
`codegen-server-test` and it runs the `rust-server-codegen` Smithy build
plugin instead.

View File

@ -4,7 +4,6 @@
*/
extra["displayName"] = "Smithy :: Rust :: Codegen :: Server :: Test"
extra["moduleName"] = "software.amazon.smithy.rust.kotlin.codegen.server.test"
tasks["jar"].enabled = false
@ -14,6 +13,10 @@ plugins { id("software.amazon.smithy").version("0.5.3") }
val smithyVersion: String by project
val defaultRustFlags: String by project
val defaultRustDocFlags: String by project
val properties = PropertyRetriever(rootProject, project)
val pluginName = "rust-server-codegen"
val workingDirUnderBuildDir = "smithyprojections/codegen-server-test/"
buildscript {
val smithyVersion: String by project
@ -29,104 +32,64 @@ dependencies {
implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion")
}
data class CodegenTest(val service: String, val module: String, val extraConfig: String? = null)
val CodegenTests = listOf(
val allCodegenTests = listOf(
CodegenTest("com.amazonaws.simple#SimpleService", "simple"),
CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"),
CodegenTest("com.amazonaws.ebs#Ebs", "ebs"),
CodegenTest("com.amazonaws.s3#AmazonS3", "s3")
)
/**
* `includeFluentClient` must be set to `false` as we are not generating all the supporting
* code for it.
* TODO: Review how can we make this a default in the server so that customers don't
* have to specify it.
*/
fun generateSmithyBuild(tests: List<CodegenTest>): String {
val projections =
tests.joinToString(",\n") {
"""
"${it.module}": {
"plugins": {
"rust-server-codegen": {
"runtimeConfig": {
"relativePath": "${rootProject.projectDir.absolutePath}/rust-runtime"
},
"service": "${it.service}",
"module": "${it.module}",
"moduleVersion": "0.0.1",
"moduleDescription": "test",
"moduleAuthors": ["protocoltest@example.com"]
${it.extraConfig ?: ""}
}
}
}
""".trimIndent()
}
return """
{
"version": "1.0",
"projections": { $projections }
}
"""
}
task("generateSmithyBuild") {
description = "generate smithy-build.json"
doFirst { projectDir.resolve("smithy-build.json").writeText(generateSmithyBuild(CodegenTests)) }
}
fun generateCargoWorkspace(tests: List<CodegenTest>): String {
return """
[workspace]
members = [
${tests.joinToString(",") { "\"${it.module}/rust-server-codegen\"" }}
]
""".trimIndent()
doFirst {
projectDir.resolve("smithy-build.json")
.writeText(generateSmithyBuild(
rootProject.projectDir.absolutePath,
pluginName,
codegenTests(properties, allCodegenTests))
)
}
}
task("generateCargoWorkspace") {
description = "generate Cargo.toml workspace file"
doFirst {
buildDir.resolve("smithyprojections/codegen-server-test/Cargo.toml")
.writeText(generateCargoWorkspace(CodegenTests))
buildDir.resolve("$workingDirUnderBuildDir/Cargo.toml")
.writeText(generateCargoWorkspace(pluginName, codegenTests(properties, allCodegenTests)))
}
}
tasks["smithyBuildJar"].dependsOn("generateSmithyBuild")
tasks["assemble"].dependsOn("smithyBuildJar")
tasks["assemble"].finalizedBy("generateCargoWorkspace")
tasks.register<Exec>("cargoCheck") {
workingDir("build/smithyprojections/codegen-server-test/")
tasks.register<Exec>(Cargo.CHECK.toString) {
workingDir("$buildDir/$workingDirUnderBuildDir")
environment("RUSTFLAGS", defaultRustFlags)
commandLine("cargo", "check")
dependsOn("assemble")
}
tasks.register<Exec>("cargoTest") {
workingDir("build/smithyprojections/codegen-server-test/")
tasks.register<Exec>(Cargo.TEST.toString) {
workingDir("$buildDir/$workingDirUnderBuildDir")
environment("RUSTFLAGS", defaultRustFlags)
commandLine("cargo", "test")
dependsOn("assemble")
}
tasks.register<Exec>("cargoDocs") {
workingDir("build/smithyprojections/codegen-server-test/")
tasks.register<Exec>(Cargo.DOCS.toString) {
workingDir("$buildDir/$workingDirUnderBuildDir")
environment("RUSTDOCFLAGS", defaultRustDocFlags)
commandLine("cargo", "doc", "--no-deps")
dependsOn("assemble")
}
tasks.register<Exec>("cargoClippy") {
workingDir("build/smithyprojections/codegen-server-test/")
tasks.register<Exec>(Cargo.CLIPPY.toString) {
workingDir("$buildDir/$workingDirUnderBuildDir")
environment("RUSTFLAGS", defaultRustFlags)
commandLine("cargo", "clippy")
dependsOn("assemble")
}
tasks["test"].finalizedBy("cargoCheck", "cargoClippy", "cargoTest", "cargoDocs")
tasks["test"].finalizedBy(cargoCommands(properties).map { it.toString })
tasks["clean"].doFirst { delete("smithy-build.json") }

View File

@ -1,8 +1,42 @@
# Codegen Integration Test
This module defines an integration test of the code generation machinery. `.build.gradle.kts` will generate a `smithy-build.json` file as part of the build. The Smithy build plugin then invokes our codegen machinery and generates Rust crates.
# Codegen Integration Tests
This module defines integration tests of the code generation machinery.
`./build.gradle.kts` will generate a `smithy-build.json` file as part of the
build. The `rust-codegen` Smithy build plugin then invokes our codegen
machinery and generates Rust crates, one for each of the integration test
services defined under `model/`.
The `test` task will run `cargo check` and `cargo clippy` to validate that the generated Rust compiles and is idiomatic.
## Usage
These commands are all meant to be run from the repository root.
To run all protocol tests of all the integration test services:
```sh
./gradlew codegen-test:build
```
../gradlew test
To run only a _subset_ of the integration test services (refer to
`./build.gradle.kts` for a full list):
```sh
./gradlew codegen-test:build -P modules='simple,rest_json'
```
The Gradle task will run `cargo check`, `cargo test`, `cargo docs` and `cargo
clippy` by default on all the generated Rust crates. You can also specify a
subset of these commands. For instance, if you're working on documentation and
want to check that the crates also compile, you can run:
```sh
./gradlew codegen-test:build -P cargoCommands='check,docs'
```
For fast development iteration cycles on protocol tests, we recommend you write
a codegen _unit_ test with a minimal service definition and only run that unit
test. Alternatively, you can write a minimal integration test service
definition in `model/simple.smithy` and run:
```sh
./gradlew codegen-test:build -P cargoCommands='test' -P modules='simple'
```

View File

@ -15,6 +15,10 @@ plugins {
val smithyVersion: String by project
val defaultRustFlags: String by project
val defaultRustDocFlags: String by project
val properties = PropertyRetriever(rootProject, project)
val pluginName = "rust-codegen"
val workingDirUnderBuildDir = "smithyprojections/codegen-test/"
buildscript {
val smithyVersion: String by project
@ -30,9 +34,7 @@ dependencies {
implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion")
}
data class CodegenTest(val service: String, val module: String, val extraConfig: String? = null)
val CodegenTests = listOf(
val allCodegenTests = listOf(
CodegenTest("com.amazonaws.simple#SimpleService", "simple"),
CodegenTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"),
CodegenTest("com.amazonaws.ebs#Ebs", "ebs"),
@ -84,88 +86,58 @@ val CodegenTests = listOf(
)
)
fun generateSmithyBuild(tests: List<CodegenTest>): String {
val projections = tests.joinToString(",\n") {
"""
"${it.module}": {
"plugins": {
"rust-codegen": {
"runtimeConfig": {
"relativePath": "${rootProject.projectDir.absolutePath}/rust-runtime"
},
"service": "${it.service}",
"module": "${it.module}",
"moduleVersion": "0.0.1",
"moduleDescription": "test",
"moduleAuthors": ["protocoltest@example.com"]
${it.extraConfig ?: ""}
}
}
}
""".trimIndent()
}
return """
{
"version": "1.0",
"projections": { $projections }
}
"""
}
task("generateSmithyBuild") {
description = "generate smithy-build.json"
doFirst {
projectDir.resolve("smithy-build.json").writeText(generateSmithyBuild(CodegenTests))
projectDir.resolve("smithy-build.json")
.writeText(generateSmithyBuild(
rootProject.projectDir.absolutePath,
pluginName,
codegenTests(properties, allCodegenTests))
)
}
}
fun generateCargoWorkspace(tests: List<CodegenTest>): String {
return """
[workspace]
members = [
${tests.joinToString(",") { "\"${it.module}/rust-codegen\"" }}
]
""".trimIndent()
}
task("generateCargoWorkspace") {
description = "generate Cargo.toml workspace file"
doFirst {
buildDir.resolve("smithyprojections/codegen-test/Cargo.toml").writeText(generateCargoWorkspace(CodegenTests))
buildDir.resolve("$workingDirUnderBuildDir/Cargo.toml")
.writeText(generateCargoWorkspace(pluginName, codegenTests(properties, allCodegenTests)))
}
}
tasks["smithyBuildJar"].dependsOn("generateSmithyBuild")
tasks["assemble"].finalizedBy("generateCargoWorkspace")
tasks.register<Exec>("cargoCheck") {
workingDir("build/smithyprojections/codegen-test/")
tasks.register<Exec>(Cargo.CHECK.toString) {
workingDir("$buildDir/$workingDirUnderBuildDir")
environment("RUSTFLAGS", defaultRustFlags)
commandLine("cargo", "check")
dependsOn("assemble")
}
tasks.register<Exec>("cargoTest") {
workingDir("build/smithyprojections/codegen-test/")
tasks.register<Exec>(Cargo.TEST.toString) {
workingDir("$buildDir/$workingDirUnderBuildDir")
environment("RUSTFLAGS", defaultRustFlags)
commandLine("cargo", "test")
dependsOn("assemble")
}
tasks.register<Exec>("cargoDocs") {
workingDir("build/smithyprojections/codegen-test/")
tasks.register<Exec>(Cargo.DOCS.toString) {
workingDir("$buildDir/$workingDirUnderBuildDir")
environment("RUSTDOCFLAGS", defaultRustDocFlags)
commandLine("cargo", "doc", "--no-deps")
dependsOn("assemble")
}
tasks.register<Exec>("cargoClippy") {
workingDir("build/smithyprojections/codegen-test/")
tasks.register<Exec>(Cargo.CLIPPY.toString) {
workingDir("$buildDir/$workingDirUnderBuildDir")
environment("RUSTFLAGS", defaultRustFlags)
commandLine("cargo", "clippy")
dependsOn("assemble")
}
tasks["test"].finalizedBy("cargoCheck", "cargoClippy", "cargoTest", "cargoDocs")
tasks["test"].finalizedBy(cargoCommands(properties).map { it.toString })
tasks["clean"].doFirst {
delete("smithy-build.json")