Skip to content

feat: Smoke Tests V2 #841

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

Merged
merged 10 commits into from
Oct 29, 2024
Merged

feat: Smoke Tests V2 #841

merged 10 commits into from
Oct 29, 2024

Conversation

sichanyoo
Copy link
Contributor

@sichanyoo sichanyoo commented Oct 16, 2024

Companion aws-sdk-swift PR: awslabs/aws-sdk-swift#1791

Issue #

1779

Description of changes

  • Adds the smoke test code generator for generic Smithy spec, whose behaviors can be customized via method overrides in a child class.

Scope

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@sichanyoo sichanyoo requested a review from jbelkins October 16, 2024 21:43
Copy link
Contributor Author

@sichanyoo sichanyoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments for reviewers.

Comment on lines +97 to +98
LOGGER.info("[${service.id}] Generating smoke tests for service")
generateSmokeTests(ctx)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the place where smoke test generation gets triggered

Comment on lines +393 to +395
override fun generateSmokeTests(ctx: ProtocolGenerator.GenerationContext) {
return SmokeTestGenerator(ctx).generateSmokeTests()
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This concrete implementation generates smoke tests according to generic Smithy spec.

Comment on lines +131 to +134
/**
* Generate smoke tests defined on the service model
*/
fun generateSmokeTests(ctx: GenerationContext)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets implemented both in smithy-swift and aws-sdk-swift, each generating tests in generic Smithy way vs. AWS way, respectively.

@@ -0,0 +1,267 @@
package software.amazon.smithy.swift.codegen.integration
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generates smoke tests according to generic Smithy spec. A child class in aws-sdk-swift extends this class to customize AWS specific logic by overriding vendor specific methods.

Comment on lines +160 to +177
private fun renderPrintTestResult(
writer: SwiftWriter,
isSuccess: Boolean,
serviceName: String,
operationName: String,
errorExpected: Boolean,
isSkipped: Boolean = false,
printCaughtError: Boolean = false
) {
val result = if (isSuccess) "ok" else "not ok"
val error = if (errorExpected) "error expected from service" else "no error expected from service"
val skipped = if (isSkipped) " # skip" else ""
writer.write("print(\$S)", "$result $serviceName $operationName - $error$skipped")
if (printCaughtError) {
writer.write("print(\"# Caught unexpected error: \\(error)\")")
}
writer.write("return $isSuccess")
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test result line produced here follows the TAP protocol as well as additional restrictions defined in SEPs.

Comment on lines +179 to +241
private fun renderDoCatchBlock(operationShapeId: ShapeId, testCase: SmokeTestCase, serviceName: String, writer: SwiftWriter) {
val operationName = operationShapeId.name
val errorExpected = testCase.expectation.isFailure
val specificErrorExpected = errorExpected && testCase.expectation.failure.get().errorId.isPresent

writer.write("do {")
writer.indent()
// Construct input struct with params from trait.
val inputShape = ctx.model.expectShape(ctx.model.expectShape(operationShapeId).asOperationShape().get().inputShape)
if (testCase.params?.get()?.size() == 0) {
writer.write("let input = ${inputShape.id.name}()")
} else {
writer.writeInline("let input = ")
.call {
ShapeValueGenerator(ctx.model, ctx.symbolProvider).writeShapeValueInline(
writer,
inputShape,
testCase.params.orElse(ObjectNode.builder().build())
)
}
.write("")
}
// Create empty config
val clientName = getClientName()
writer.write("let config = try await $clientName.${clientName}Configuration()")
// Set any vendor-specific values into config.
handleVendorParams(testCase.vendorParams.orElse(null), writer)
// Construct client with the config
writer.write("let client = $clientName(config: config)")
// Call the operation with client and input
writer.write("_ = try await client.${operationName.toLowerCamelCase()}(input: input)")
// Writer after client call:
if (errorExpected) {
// If error was expected, print failure line and return false.
renderPrintTestResult(writer, false, serviceName, operationName, true)
} else {
// If expected success, print success line and return true.
renderPrintTestResult(writer, true, serviceName, operationName, false)
}
writer.dedent()

// Catch specific error only if it is expected
if (specificErrorExpected) {
val expectedErrorName = testCase.expectation.failure.get().errorId.get().name
renderSpecificErrorCatchBlock(expectedErrorName, operationName, serviceName, writer)
}

// Catch generic error
writer.write("} catch {")
writer.indent()
if (specificErrorExpected) {
// Specific error was expected but some other error got caught; print failure & the unexpected error and return false.
renderPrintTestResult(writer, false, serviceName, operationName, true, printCaughtError = true)
} else if (errorExpected) {
// If generic error was expected, print success and return true.
renderPrintTestResult(writer, true, serviceName, operationName, true)
} else {
// If expected success, print failure, the unexpected error, and return false.
renderPrintTestResult(writer, false, serviceName, operationName, false, printCaughtError = true)
}
writer.dedent()
writer.write("}")
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renderDoCatchBlock() contains the logic for test result & error handling codegen.

Comment on lines +251 to +254
open fun handleVendorParams(vendorParams: ObjectNode, writer: SwiftWriter) {
// Pseudo-code example:
// writer.write("config.value1 = ${value1-extracted-from-vendorParams}")
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vendor params are custom client config fields included for a given service client. In our case, all the config fields added to service clients in aws-sdk-swift.

@@ -91,6 +91,7 @@ object ClientRuntimeTypes {
val InterceptorProvider = runtimeSymbol("InterceptorProvider", SwiftDeclaration.PROTOCOL)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and below change are for test files that need to import these.

Copy link
Contributor

@jbelkins jbelkins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this is good.

I'm tempted to include a codegen switch to turn smoke test generation on/off, but as long as the tests are .gitignored and they remain reasonably small, I'm okay with generating them every time for now.

Copy link
Contributor

@jbelkins jbelkins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one minor code cleanup

// exit(0)
// } else {
// exit(1)
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove commented code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment was to show what the method generates

@sichanyoo sichanyoo requested a review from jbelkins October 29, 2024 16:38
@sichanyoo sichanyoo merged commit 2e19981 into main Oct 29, 2024
27 checks passed
@sichanyoo sichanyoo deleted the feat/smoke-tests-v2 branch October 29, 2024 16:43
jbelkins added a commit that referenced this pull request Oct 29, 2024
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244524 -0500

parent 0de1791
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244464 -0500

parent 0de1791
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244451 -0500

parent 0de1791
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244433 -0500

parent 0de1791
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244416 -0500

parent 0de1791
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244397 -0500

parent 0de1791
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244381 -0500

parent 0de1791
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244297 -0500

parent 0de1791
author Josh Elkins <jbelkins@amazon.com> 1725075023 -0500
committer Josh Elkins <jbelkins@amazon.com> 1730244277 -0500

WIP

chore: Organize codegen tests & Kotlin unit tests (#830)

* Organize codegen tests & refactor setup tests for XML codegen tests to reduce duplicate code.

* ktlint

---------

Co-authored-by: Sichan Yoo <chanyoo@amazon.com>

feat: Make models Sendable (#829)

chore: Specify Xcode_16 application name same as on Github runner (#831)

chore: Move HTTP status code test out of SmithyTestUtil (#836)

chore: Protocol tests set their own AWS credentials (#837)

Don't import Darwin on Windows. (#835)

chore: Run macOS 15 with Xcode 16 on CI (#838)

fix: Don't unwrap unneeded value during event marshal (#839)

fix: Don't supply a default value when reading a non-optional value (#840)

feat: Enable sigv4a for SESv2 service (#842)

feat!: Bump CRT version to 0.37.0 & add CRC64NVME checksum algorithm wrapper (#845)

* Bump CRT version to 0.37.0 and add wrapper for new checksum algorithm.

* Add doc comment re: checksum algorithms may expand in future.

---------

Co-authored-by: Sichan Yoo <chanyoo@amazon.com>

chore: Use Smithy 1.52.1 (#846)

feat: Smoke Tests V2 (#841)

* Basic scaffold.

* Add SmokeTestGenerator for generic Smithy spec, with customization points via open functions.

* Refactor both codegen logic and generated code to fix runtime / compile errors in generated code.

* Generate test runner only if there's >=1 test case(s) for the service. Narrow down imports to specific class used.

* Add manual ignore lists for disabling codegen for smoke tests by their test ID or their test tag.

* Add generic codegen for smoke tests

* Rename variables to be more specific.

---------

Co-authored-by: Sichan Yoo <chanyoo@amazon.com>

chore: Updates version to 0.86.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants