-
Notifications
You must be signed in to change notification settings - Fork 30
feat: add protocol tests runner #1293
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"id": "e9fa30cd-1aa7-4c5d-b01a-384078b8863a", | ||
"type": "feature", | ||
"description": "Added a protocol tests runner, a new mechanism to run the tests and report the results in JSON format detached from the test phase of a regular build." | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,12 @@ class CodegenVisitor(context: PluginContext) : ShapeVisitor.Default<Unit>() { | |
} | ||
|
||
fun execute() { | ||
generateShapes() | ||
generateNonShapes() | ||
writers.flushWriters() | ||
} | ||
|
||
fun generateShapes() { | ||
logger.info("Generating Kotlin client for service ${settings.service}") | ||
|
||
logger.info("Walking shapes from ${settings.service} to find shapes to generate") | ||
|
@@ -122,15 +128,7 @@ class CodegenVisitor(context: PluginContext) : ShapeVisitor.Default<Unit>() { | |
serviceShapes.forEach { it.accept(this) } | ||
|
||
protocolGenerator?.apply { | ||
val ctx = ProtocolGenerator.GenerationContext( | ||
settings, | ||
model, | ||
service, | ||
symbolProvider, | ||
integrations, | ||
protocol, | ||
writers, | ||
) | ||
val ctx = generationContext() | ||
|
||
logger.info("[${service.id}] Generating unit tests for protocol $protocol") | ||
generateProtocolUnitTests(ctx) | ||
|
@@ -144,7 +142,9 @@ class CodegenVisitor(context: PluginContext) : ShapeVisitor.Default<Unit>() { | |
logger.info("[${service.id}] Generating auth scheme provider for protocol $protocol") | ||
generateAuthSchemeProvider(ctx) | ||
} | ||
} | ||
|
||
fun generateNonShapes() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style: The name of this method is not very clear because it's not obvious what "non-shapes" are. Can we come up with a more descriptive name that will make more sense in calling code? (e.g., |
||
writers.finalize() | ||
|
||
if (settings.build.generateDefaultBuildFiles) { | ||
|
@@ -156,10 +156,18 @@ class CodegenVisitor(context: PluginContext) : ShapeVisitor.Default<Unit>() { | |
|
||
// write files defined by integrations | ||
integrations.forEach { it.writeAdditionalFiles(baseGenerationContext, writers) } | ||
|
||
writers.flushWriters() | ||
} | ||
|
||
fun generationContext(): ProtocolGenerator.GenerationContext = ProtocolGenerator.GenerationContext( | ||
settings, | ||
model, | ||
service, | ||
symbolProvider, | ||
integrations, | ||
protocolGenerator!!.protocol, | ||
writers, | ||
) | ||
|
||
override fun getDefault(shape: Shape?) { } | ||
|
||
override fun structureShape(shape: StructureShape) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ private fun getDefaultRuntimeVersion(): String { | |
const val RUNTIME_GROUP: String = "aws.smithy.kotlin" | ||
val RUNTIME_VERSION: String = System.getProperty("smithy.kotlin.codegen.clientRuntimeVersion", getDefaultRuntimeVersion()) | ||
val KOTLIN_COMPILER_VERSION: String = System.getProperty("smithy.kotlin.codegen.kotlinCompilerVersion", "2.1.0") | ||
val SHADOW_JAR_VERSION: String = System.getProperty("com.github.johnrengelman.shadowVersion", "8.1.1") | ||
Comment on lines
40
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: We need to stop embedding hardcoded versions into our codegen files and should be auto-discovering them instead, similar to how we do with |
||
|
||
enum class SourceSet { | ||
CommonMain, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ fun writeGradleBuild( | |
settings: KotlinSettings, | ||
manifest: FileManifest, | ||
dependencies: List<KotlinDependency>, | ||
enableApplication: Boolean = false, | ||
) { | ||
val writer = GradleWriter() | ||
|
||
|
@@ -53,6 +54,12 @@ fun writeGradleBuild( | |
} | ||
}, | ||
) | ||
if (enableApplication) { | ||
indent() | ||
write("application") | ||
write("id(\"com.github.johnrengelman.shadow\") version #S", SHADOW_JAR_VERSION) | ||
dedent() | ||
} | ||
Comment on lines
+57
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style: Prefer Smithy placeholders or triple-quotes to avoid escaping strings which may contain quotes. // either
write("id(#S) version #S", "com.github.johnrengelman.shadow", SHADOW_JAR_VERSION)
// or
write("""id("com.github.johnrengelman.shadow") version #S""", SHADOW_JAR_VERSION) Applies in other spots as well. |
||
} | ||
|
||
when { | ||
|
@@ -74,6 +81,13 @@ fun writeGradleBuild( | |
) | ||
} | ||
|
||
if (enableApplication) { | ||
writer.write("") | ||
writer.openBlock("application {") | ||
writer.write("mainClass.set(\"${settings.pkg.name}.pt.RunnerKt\")") | ||
writer.closeBlock("}") | ||
} | ||
Comment on lines
+84
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: Why do the generated projects need the |
||
|
||
val contents = writer.toString() | ||
manifest.writeFile("build.gradle.kts", contents) | ||
if (settings.build.generateFullProject) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
import aws.sdk.kotlin.gradle.dsl.configurePublishing | ||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget | ||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | ||
|
||
plugins { | ||
alias(libs.plugins.kotlin.jvm) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: Does this need to be JVM only? Or can these tests be run in Kotlin/Native as well? |
||
jacoco | ||
`maven-publish` | ||
} | ||
|
||
val codegenVersion: String by project | ||
description = "Smithy Kotlin Codegen for Protocol tests" | ||
group = "software.amazon.smithy.kotlin" | ||
version = codegenVersion | ||
|
||
dependencies { | ||
api(project(":codegen:smithy-kotlin-codegen")) | ||
|
||
api(libs.smithy.aws.traits) | ||
api(libs.smithy.protocol.test.traits) | ||
api(libs.smithy.protocol.traits) | ||
|
||
testImplementation(libs.junit.jupiter) | ||
testImplementation(libs.junit.jupiter.params) | ||
testImplementation(libs.kotest.assertions.core.jvm) | ||
testImplementation(libs.kotlin.test.junit5) | ||
testImplementation(project(":codegen:smithy-kotlin-codegen-testutils")) | ||
|
||
testImplementation(libs.slf4j.api) | ||
testImplementation(libs.slf4j.simple) | ||
testImplementation(libs.kotlinx.serialization.json) | ||
} | ||
|
||
tasks.withType<KotlinCompile> { | ||
compilerOptions { | ||
jvmTarget.set(JvmTarget.JVM_17) | ||
} | ||
} | ||
|
||
tasks.withType<JavaCompile> { | ||
sourceCompatibility = JavaVersion.VERSION_17.toString() | ||
targetCompatibility = JavaVersion.VERSION_17.toString() | ||
} | ||
|
||
// Reusable license copySpec | ||
val licenseSpec = copySpec { | ||
from("${project.rootDir}/LICENSE") | ||
from("${project.rootDir}/NOTICE") | ||
} | ||
|
||
// Configure jars to include license related info | ||
tasks.jar { | ||
metaInf.with(licenseSpec) | ||
inputs.property("moduleName", project.name) | ||
manifest { | ||
attributes["Automatic-Module-Name"] = project.name | ||
} | ||
} | ||
|
||
tasks.test { | ||
useJUnitPlatform() | ||
testLogging { | ||
events("passed", "skipped", "failed") | ||
showStandardStreams = true | ||
showStackTraces = true | ||
showExceptions = true | ||
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL | ||
} | ||
} | ||
|
||
// Configure jacoco (code coverage) to generate an HTML report | ||
tasks.jacocoTestReport { | ||
reports { | ||
xml.required.set(false) | ||
csv.required.set(false) | ||
html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco")) | ||
} | ||
} | ||
|
||
// Always run the jacoco test report after testing. | ||
tasks["test"].finalizedBy(tasks["jacocoTestReport"]) | ||
|
||
val sourcesJar by tasks.creating(Jar::class) { | ||
group = "publishing" | ||
description = "Assembles Kotlin sources jar" | ||
archiveClassifier.set("sources") | ||
from(sourceSets.getByName("main").allSource) | ||
} | ||
|
||
publishing { | ||
publications { | ||
create<MavenPublication>("codegen") { | ||
from(components["java"]) | ||
artifact(sourcesJar) | ||
} | ||
} | ||
} | ||
|
||
configurePublishing("smithy-kotlin", "smithy-lang") | ||
Comment on lines
+94
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: Why do we need to publish this project? Will we need to fetch/run it from Maven Central in some cases? Applies in other places too. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
package software.amazon.smithy.kotlin.codegen.pt | ||
|
||
Comment on lines
+5
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style: We should use a more descriptive namespace than |
||
import software.amazon.smithy.build.PluginContext | ||
import software.amazon.smithy.build.SmithyBuildPlugin | ||
import software.amazon.smithy.kotlin.codegen.CodegenVisitor | ||
import software.amazon.smithy.kotlin.codegen.core.GradleConfiguration | ||
import software.amazon.smithy.kotlin.codegen.core.KOTLIN_COMPILER_VERSION | ||
import software.amazon.smithy.kotlin.codegen.core.KotlinDependency | ||
import software.amazon.smithy.kotlin.codegen.rendering.protocol.TestMemberDelta | ||
import software.amazon.smithy.kotlin.codegen.rendering.protocol.pt.ProtocolTestGenerator | ||
import software.amazon.smithy.kotlin.codegen.rendering.writeGradleBuild | ||
|
||
// We redefine the kotlin-test and smithy-tests dependencies since for this use case | ||
// we need them in the implementation scope instead of just in the test scope. | ||
val KOTLIN_TEST_RT = KotlinDependency( | ||
GradleConfiguration.Implementation, | ||
"kotlin.test", | ||
"org.jetbrains.kotlin", | ||
"kotlin-test", | ||
KOTLIN_COMPILER_VERSION, | ||
) | ||
val SMITHY_TEST_RT = KotlinDependency( | ||
GradleConfiguration.Implementation, | ||
KotlinDependency.SMITHY_TEST.namespace, | ||
KotlinDependency.SMITHY_TEST.group, | ||
KotlinDependency.SMITHY_TEST.artifact, | ||
KotlinDependency.SMITHY_TEST.version, | ||
) | ||
Comment on lines
+17
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Could be
Comment on lines
+17
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: What does "RT" mean in these names?
Comment on lines
+17
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Prefer the generated val KOTLIN_TEST_RT = KOTLIN_TEST.copy(config = GradleConfiguration.Implementation)
val SMITHY_TEST_RT = SMITHY_TEST.copy(config = GradleConfiguration.Implementation) This saves us from duplicating the other values in code and potentially diverging. |
||
|
||
/** | ||
* Plugin to trigger Kotlin protocol tests code generation. This plugin also generates the client and the | ||
* request/response shapes. | ||
*/ | ||
public class KotlinProtocolTestCodegenPlugin : SmithyBuildPlugin { | ||
|
||
override fun getName(): String = "kotlin-protocol-tests-codegen" | ||
|
||
override fun execute(context: PluginContext?) { | ||
// Run the regular codegen | ||
var codegen = CodegenVisitor(context ?: error("context was null")) | ||
codegen.generateShapes() | ||
|
||
// Generate the protocol tests | ||
val ctx = codegen.generationContext() | ||
val requestTestBuilder = ProtocolTestRequestGenerator.Builder() | ||
val responseTestBuilder = ProtocolTestResponseGenerator.Builder() | ||
val errorTestBuilder = ProtocolTestErrorGenerator.Builder() | ||
val ignoredTests = TestMemberDelta( | ||
setOf(), | ||
) | ||
ProtocolTestGenerator( | ||
ctx, | ||
requestTestBuilder, | ||
responseTestBuilder, | ||
errorTestBuilder, | ||
ignoredTests, | ||
).generateProtocolTests() | ||
Comment on lines
+47
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: Doesn't |
||
|
||
val writers = ctx.delegator | ||
writers.finalize() | ||
val settings = ctx.settings | ||
|
||
if (settings.build.generateDefaultBuildFiles) { | ||
val dependencies = writers.dependencies | ||
.mapNotNull { it.properties["dependency"] as? KotlinDependency } | ||
.distinct() | ||
val newDependencies = ArrayList<KotlinDependency>() | ||
newDependencies.addAll(dependencies) | ||
newDependencies.add(KOTLIN_TEST_RT) | ||
newDependencies.add(SMITHY_TEST_RT) | ||
writeGradleBuild(settings, writers.fileManifest, newDependencies, enableApplication = true) | ||
} | ||
writers.flushWriters() | ||
Comment on lines
+47
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correctness: All of this seems like the responsibility of the I suggest making |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This update isn't user-facing so wouldn't typically need a changelog message. You can delete this and add the no-changelog label to this PR.