Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client integration test #1956

Merged
merged 7 commits into from
Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ object AwsRuntimeType {
CargoDependency.SmithyHttpTower(this),
CargoDependency.SmithyClient(this),
CargoDependency.Tower,
sigAuth(),
awsHttp(),
awsEndpoint(),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomiza
import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection
import java.nio.file.Files
import java.nio.file.Paths
import kotlin.io.path.absolute

class IntegrationTestDecorator : RustCodegenDecorator<ClientProtocolGenerator, ClientCodegenContext> {
override val name: String = "IntegrationTest"
Expand All @@ -32,7 +33,7 @@ class IntegrationTestDecorator : RustCodegenDecorator<ClientProtocolGenerator, C
): List<LibRsCustomization> {
val integrationTestPath = Paths.get(SdkSettings.from(codegenContext.settings).integrationTestPath)
check(Files.exists(integrationTestPath)) {
"Failed to find the AWS SDK integration tests. Make sure the integration test path is configured " +
"Failed to find the AWS SDK integration tests (${integrationTestPath.absolute()}). Make sure the integration test path is configured " +
"correctly in the smithy-build.json."
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,16 @@
package software.amazon.smithy.rustsdk

import org.junit.jupiter.api.Test
import software.amazon.smithy.model.node.Node
import software.amazon.smithy.model.node.ObjectNode
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.CodegenVisitor
import software.amazon.smithy.rust.codegen.client.smithy.customizations.AllowLintsGenerator
import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations
import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator
import software.amazon.smithy.rust.codegen.client.testutil.stubConfigCustomization
import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest
import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.core.rustlang.asType
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
import software.amazon.smithy.rust.codegen.core.util.runCommand
import java.io.File

internal class EndpointConfigCustomizationTest {
private val placeholderEndpointParams = AwsTestRuntimeConfig.awsEndpoint().asType().member("Params")
Expand All @@ -52,13 +42,15 @@ internal class EndpointConfigCustomizationTest {
}

@aws.api#service(sdkId: "Test", endpointPrefix: "iam")
@title("test")
@restJson1
service NoRegions {
version: "123",
operations: [Nop]
}

@aws.api#service(sdkId: "Test")
@title("test")
@restJson1
service NoEndpointPrefix {
version: "123",
Expand Down Expand Up @@ -127,38 +119,31 @@ internal class EndpointConfigCustomizationTest {
""".let { ObjectNode.parse(it).expectObjectNode() }

private fun validateEndpointCustomizationForService(service: String, test: ((RustCrate) -> Unit)? = null) {
val (context, testDir) = generatePluginContext(model, service = service, runtimeConfig = AwsTestRuntimeConfig)
val codegenDecorator = object : RustCodegenDecorator<ClientProtocolGenerator, ClientCodegenContext> {
override val name: String = "tests and config"
override val order: Byte = 0
override fun configCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<ConfigCustomization>,
): List<ConfigCustomization> =
baseCustomizations + stubConfigCustomization("a") + EndpointConfigCustomization(
codegenContext,
endpointConfig,
) + stubConfigCustomization("b")

override fun libRsCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<LibRsCustomization>,
): List<LibRsCustomization> =
baseCustomizations + PubUseEndpoint(AwsTestRuntimeConfig) + AllowLintsGenerator(listOf("dead_code"), listOf(), listOf())

override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) {
if (test != null) {
test(rustCrate)
}
val endpointsFile = File.createTempFile("endpoints", ".json")
endpointsFile.writeText(Node.printJson(endpointConfig))
clientIntegrationTest(
model,
listOf(),
service = service,
runtimeConfig = AwsTestRuntimeConfig,
additionalSettings = ObjectNode.builder()
.withMember(
"customizationConfig",
ObjectNode.builder()
.withMember(
"awsSdk",
ObjectNode.builder()
.withMember("integrationTestPath", "../sdk/integration-tests")
.withMember("endpointsConfigPath", endpointsFile.absolutePath)
.build(),
).build(),
)
.withMember("codegen", ObjectNode.builder().withMember("includeFluentClient", false).build()).build(),
) { _, rustCrate ->
if (test != null) {
test(rustCrate)
}

override fun supportsCodegenContext(clazz: Class<out CodegenContext>): Boolean =
clazz.isAssignableFrom(ClientCodegenContext::class.java)
}
val customization = CombinedCodegenDecorator(listOf(RequiredCustomizations(), codegenDecorator))
CodegenVisitor(context, customization).execute()
println("file:///$testDir")
"cargo test".runCommand(testDir)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
package software.amazon.smithy.rust.codegen.client.smithy

import software.amazon.smithy.build.PluginContext
import software.amazon.smithy.build.SmithyBuildPlugin
import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations
import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations
import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator
import software.amazon.smithy.rust.codegen.client.testutil.DecoratableBuildPlugin
import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.NonExhaustive
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider
import software.amazon.smithy.rust.codegen.core.smithy.BaseSymbolMetadataProvider
Expand All @@ -31,10 +33,12 @@ import java.util.logging.Logger
* `resources/META-INF.services/software.amazon.smithy.build.SmithyBuildPlugin` refers to this class by name which
* enables the smithy-build plugin to invoke `execute` with all of the Smithy plugin context + models.
*/
class RustCodegenPlugin : SmithyBuildPlugin {
class RustCodegenPlugin : DecoratableBuildPlugin<ClientProtocolGenerator, ClientCodegenContext>() {
override fun getName(): String = "rust-codegen"

override fun execute(context: PluginContext) {
override fun executeWithDecorator(
context: PluginContext,
vararg decorator: RustCodegenDecorator<ClientProtocolGenerator, ClientCodegenContext>,
) {
// Suppress extremely noisy logs about reserved words
Logger.getLogger(ReservedWordSymbolProvider::class.java.name).level = Level.OFF
// Discover `RustCodegenDecorators` on the classpath. `RustCodegenDecorator` returns different types of
Expand All @@ -49,6 +53,7 @@ class RustCodegenPlugin : SmithyBuildPlugin {
RequiredCustomizations(),
FluentClientDecorator(),
NoOpEventStreamSigningDecorator(),
*decorator,
)

// CodegenVisitor is the main driver of code generation that traverses the model and generates code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ open class CombinedCodegenDecorator<T, C : CodegenContext>(decorators: List<Rust
override val order: Byte
get() = 0

fun withDecorator(decorator: RustCodegenDecorator<T, C>) = CombinedCodegenDecorator(orderedDecorators + decorator)
fun withDecorator(vararg decorator: RustCodegenDecorator<T, C>) =
CombinedCodegenDecorator(orderedDecorators + decorator)

override fun configCustomizations(
codegenContext: C,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rust.codegen.client.testutil

import software.amazon.smithy.build.PluginContext
import software.amazon.smithy.build.SmithyBuildPlugin
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.node.ObjectNode
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.RustCodegenPlugin
import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext
import software.amazon.smithy.rust.codegen.core.testutil.printGeneratedFiles
import software.amazon.smithy.rust.codegen.core.util.runCommand
import java.io.File
import java.nio.file.Path

/**
* Run cargo test on a true, end-to-end, codegen product of a given model.
*
* For test purposes, additional codegen decorators can also be composed.
*/
fun clientIntegrationTest(
model: Model,
addtionalDecorators: List<RustCodegenDecorator<ClientProtocolGenerator, ClientCodegenContext>> = listOf(),
addModuleToEventStreamAllowList: Boolean = false,
service: String? = null,
runtimeConfig: RuntimeConfig? = null,
additionalSettings: ObjectNode = ObjectNode.builder().build(),
test: (ClientCodegenContext, RustCrate) -> Unit,
): Path {
return codegenIntegrationTest(
model,
RustCodegenPlugin(),
addtionalDecorators,
addModuleToEventStreamAllowList = addModuleToEventStreamAllowList,
service = service,
runtimeConfig = runtimeConfig,
additionalSettings = additionalSettings,
test = test,
)
}

/**
* A Smithy BuildPlugin that accepts an additional decorator
*
* This exists to allow tests to easily customize the _real_ build without needing to list out customizations
* or attempt to manually discover them from the path
*/
abstract class DecoratableBuildPlugin<T, C : CodegenContext> : SmithyBuildPlugin {
abstract fun executeWithDecorator(
context: PluginContext,
vararg decorator: RustCodegenDecorator<T, C>,
)

override fun execute(context: PluginContext) {
executeWithDecorator(context)
}
}

// TODO(https://github.com/awslabs/smithy-rs/issues/1864): move to core once CodegenDecorator is in core
private fun <T, C : CodegenContext> codegenIntegrationTest(
model: Model,
buildPlugin: DecoratableBuildPlugin<T, C>,
additionalDecorators: List<RustCodegenDecorator<T, C>>,
additionalSettings: ObjectNode = ObjectNode.builder().build(),
addModuleToEventStreamAllowList: Boolean = false,
service: String? = null,
runtimeConfig: RuntimeConfig? = null,
overrideTestDir: File? = null, test: (C, RustCrate) -> Unit,
): Path {
val (ctx, testDir) = generatePluginContext(
model,
additionalSettings,
addModuleToEventStreamAllowList,
service,
runtimeConfig,
overrideTestDir,
)

val codegenDecorator = object : RustCodegenDecorator<T, C> {
override val name: String = "Add tests"
override val order: Byte = 0
override fun supportsCodegenContext(clazz: Class<out CodegenContext>): Boolean {
// never discoverable on the classpath
return false
}

override fun extras(codegenContext: C, rustCrate: RustCrate) {
test(codegenContext, rustCrate)
}
}
buildPlugin.executeWithDecorator(ctx, codegenDecorator, *additionalDecorators.toTypedArray())
ctx.fileManifest.printGeneratedFiles()
"cargo test".runCommand(testDir, environment = mapOf("RUSTFLAGS" to "-D warnings"))
return testDir
}