From 5d201611b974cc54d9ef968aff59c29a45f76e8f Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Tue, 8 Oct 2024 11:39:29 -0700 Subject: [PATCH 01/11] Change var names to be protocol test specific; add basic scaffold. --- .../swift/codegen/AWSHTTPBindingProtocolGenerator.kt | 12 ++++++++---- .../aws/swift/codegen/AWSSmokeTestGenerator.kt | 10 ++++++++++ .../protocols/awsjson/AWSJSON1_0ProtocolGenerator.kt | 2 +- .../protocols/awsjson/AWSJSON1_1ProtocolGenerator.kt | 2 +- .../protocols/awsquery/AWSQueryProtocolGenerator.kt | 2 +- .../protocols/ec2query/EC2QueryProtocolGenerator.kt | 2 +- .../restjson/AWSRestJson1ProtocolGenerator.kt | 2 +- .../protocols/restxml/RestXMLProtocolGenerator.kt | 2 +- 8 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPBindingProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPBindingProtocolGenerator.kt index 32dcbdc56cf..73764d6ca54 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPBindingProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSHTTPBindingProtocolGenerator.kt @@ -36,8 +36,8 @@ abstract class AWSHTTPBindingProtocolGenerator( val requestTestBuilder = HttpProtocolUnitTestRequestGenerator.Builder() val responseTestBuilder = HttpProtocolUnitTestResponseGenerator.Builder() val errorTestBuilder = HttpProtocolUnitTestErrorGenerator.Builder() - open val testsToIgnore: Set = setOf() - open val tagsToIgnore: Set = setOf() + open val protocolTestsToIgnore: Set = setOf() + open val protocolTestTagsToIgnore: Set = setOf() override val shouldRenderEncodableConformance = false override fun generateProtocolUnitTests(ctx: ProtocolGenerator.GenerationContext): Int { @@ -48,11 +48,15 @@ abstract class AWSHTTPBindingProtocolGenerator( errorTestBuilder, customizations, getProtocolHttpBindingResolver(ctx, defaultContentType), - testsToIgnore, - tagsToIgnore, + protocolTestsToIgnore, + protocolTestTagsToIgnore, ).generateProtocolTests() + renderEndpointsTests(ctx) } + override fun generateSmokeTests(ctx: ProtocolGenerator.GenerationContext) { + return AWSSmokeTestGenerator(ctx).generateSmokeTests() + } + fun renderEndpointsTests(ctx: ProtocolGenerator.GenerationContext): Int { val ruleSetNode = ctx.service.getTrait()?.ruleSet val ruleSet = if (ruleSetNode != null) EndpointRuleSet.fromNode(ruleSetNode) else null diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt new file mode 100644 index 00000000000..0fa7f9ee101 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt @@ -0,0 +1,10 @@ +package software.amazon.smithy.aws.swift.codegen + +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.SmokeTestGenerator + +class AWSSmokeTestGenerator( + ctx: ProtocolGenerator.GenerationContext +): SmokeTestGenerator(ctx) { + +} \ No newline at end of file diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsjson/AWSJSON1_0ProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsjson/AWSJSON1_0ProtocolGenerator.kt index e1506fd9dc4..5ff59ae248f 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsjson/AWSJSON1_0ProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsjson/AWSJSON1_0ProtocolGenerator.kt @@ -19,7 +19,7 @@ class AWSJSON1_0ProtocolGenerator : AWSHTTPBindingProtocolGenerator(AWSJSONCusto override val defaultContentType = "application/x-amz-json-1.0" override val protocol: ShapeId = AwsJson1_0Trait.ID override val shouldRenderEncodableConformance: Boolean = true - override val testsToIgnore = setOf( + override val protocolTestsToIgnore = setOf( "SDKAppliedContentEncoding_awsJson1_0", "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_0", ) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsjson/AWSJSON1_1ProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsjson/AWSJSON1_1ProtocolGenerator.kt index fa4cf34548b..0b583f4c998 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsjson/AWSJSON1_1ProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsjson/AWSJSON1_1ProtocolGenerator.kt @@ -19,7 +19,7 @@ class AWSJSON1_1ProtocolGenerator : AWSHTTPBindingProtocolGenerator(AWSJSONCusto override val defaultContentType = "application/x-amz-json-1.1" override val protocol: ShapeId = AwsJson1_1Trait.ID override val shouldRenderEncodableConformance: Boolean = true - override val testsToIgnore = setOf( + override val protocolTestsToIgnore = setOf( "SDKAppliedContentEncoding_awsJson1_1", "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_1", ) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsquery/AWSQueryProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsquery/AWSQueryProtocolGenerator.kt index 6b90e716075..e9a7c58b075 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsquery/AWSQueryProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/awsquery/AWSQueryProtocolGenerator.kt @@ -23,7 +23,7 @@ open class AWSQueryProtocolGenerator : AWSHTTPBindingProtocolGenerator(AWSQueryC HttpBindingResolver = FormURLHttpBindingResolver(ctx, defaultContentType) override val shouldRenderEncodableConformance = true - override val testsToIgnore = setOf( + override val protocolTestsToIgnore = setOf( "SDKAppliedContentEncoding_awsQuery", "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsQuery", ) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/ec2query/EC2QueryProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/ec2query/EC2QueryProtocolGenerator.kt index 449ad111070..53b12cabbc8 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/ec2query/EC2QueryProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/ec2query/EC2QueryProtocolGenerator.kt @@ -23,7 +23,7 @@ class EC2QueryProtocolGenerator : AWSHTTPBindingProtocolGenerator(EC2QueryCustom HttpBindingResolver = FormURLHttpBindingResolver(ctx, contentType) override val shouldRenderEncodableConformance = true - override val testsToIgnore = setOf( + override val protocolTestsToIgnore = setOf( "SDKAppliedContentEncoding_ec2Query", "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_ec2Query" ) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/restjson/AWSRestJson1ProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/restjson/AWSRestJson1ProtocolGenerator.kt index b745bebbc28..4fff69196fd 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/restjson/AWSRestJson1ProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/restjson/AWSRestJson1ProtocolGenerator.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.model.shapes.ShapeId class AWSRestJson1ProtocolGenerator : AWSHTTPBindingProtocolGenerator(RestJSONCustomizations()) { override val defaultContentType = "application/json" override val protocol: ShapeId = RestJson1Trait.ID - override val testsToIgnore = setOf( + override val protocolTestsToIgnore = setOf( "SDKAppliedContentEncoding_restJson1", "SDKAppendedGzipAfterProvidedEncoding_restJson1", ) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/restxml/RestXMLProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/restxml/RestXMLProtocolGenerator.kt index 360f3ff15e4..fd9e9a152f4 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/restxml/RestXMLProtocolGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/protocols/restxml/RestXMLProtocolGenerator.kt @@ -13,7 +13,7 @@ import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator class RestXMLProtocolGenerator : AWSHTTPBindingProtocolGenerator(RestXMLCustomizations()) { override val defaultContentType: String = "application/xml" override val protocol: ShapeId = RestXmlTrait.ID - override val testsToIgnore: Set = setOf( + override val protocolTestsToIgnore: Set = setOf( "S3DefaultAddressing", // can leave disabled, pre-endpoints 2.0 "S3VirtualHostAddressing", // can leave disabled, pre-endpoints 2.0 "S3VirtualHostDualstackAddressing", // can leave disabled, pre-endpoints 2.0 From 833e7a41f762482c37a94ba02ea6e025380f957f Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Thu, 10 Oct 2024 15:19:05 -0700 Subject: [PATCH 02/11] Add AWSSmokeTestGenerator that provides custom behaviors to parent class SmokeTestGenerator in smithy-swift. --- .../swift/codegen/AWSSmokeTestGenerator.kt | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt index 0fa7f9ee101..36d81c94c8e 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt @@ -1,10 +1,65 @@ package software.amazon.smithy.aws.swift.codegen +import software.amazon.smithy.aws.traits.ServiceTrait +import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.swift.codegen.SwiftWriter import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator import software.amazon.smithy.swift.codegen.integration.SmokeTestGenerator +import software.amazon.smithy.swift.codegen.utils.toUpperCamelCase class AWSSmokeTestGenerator( - ctx: ProtocolGenerator.GenerationContext -): SmokeTestGenerator(ctx) { + private val ctx: ProtocolGenerator.GenerationContext +) : SmokeTestGenerator(ctx) { + override fun getServiceName(): String { + return "AWS" + ctx.service.getTrait(ServiceTrait::class.java).get().sdkId.toUpperCamelCase() + } -} \ No newline at end of file + override fun getClientName(): String { + return ctx.service.getTrait(ServiceTrait::class.java).get().sdkId.toUpperCamelCase() + "Client" + } + + override fun renderCustomFilePrivateVariables(writer: SwiftWriter) { + writer.write("fileprivate let regionFromEnv = ProcessInfo.processInfo.environment[\"AWS_SMOKE_TEST_REGION\"]") + writer.write("fileprivate let tagsToSkip = (ProcessInfo.processInfo.environment[\"AWS_SMOKE_TEST_SKIP_TAGS\"] ?? \"\").components(separatedBy: \\\",\\\")\")") + } + + override fun handleVendorParams(vendorParams: ObjectNode, writer: SwiftWriter) { + val nameToValueMappings = getFormattedVendorParams(vendorParams) + nameToValueMappings.forEach { mapping -> + writer.write("config.${mapping.key} = ${mapping.value}") + } + } + + // Converts trait definition vendor param key:value pairs to Swift SDK config field:value pairs. + private fun getFormattedVendorParams(vendorParams: ObjectNode): Map { + val formattedMapping = mutableMapOf() + vendorParams.members.forEach { originalMapping -> + when (originalMapping.key.value) { + /* BaseAwsVendorParams members */ + "region" -> { + // Take region value retrieved from environment variable if present; otherwise, take from trait definition. + val regionValue = "regionFromEnv ?? " + originalMapping.value.expectStringNode().value + formattedMapping.put("region", regionValue) + formattedMapping.put("signingRegion", regionValue) + } + "sigv4aRegionSet" -> { /* no-op; setting multiple signing regions in config is unsupported atm. */ } + "uri" -> { formattedMapping.put("endpoint", originalMapping.value.expectStringNode().value) } + "useFips" -> { formattedMapping.put("useFIPS", originalMapping.value.expectBooleanNode().value.toString()) } + "useDualstack" -> { formattedMapping.put("useDualStack", originalMapping.value.expectBooleanNode().value.toString()) } + "useAccountIdRouting" -> { /* no-op; setting account ID routing in config is unsupported atm. */ } + + /* S3VendorParams members */ + "useAccelerate" -> { formattedMapping.put("accelerate", originalMapping.value.expectBooleanNode().value.toString()) } + "useMultiRegionAccessPoints" -> { + // Name for corresponding config in Swift SDK is: `disableMultiRegionAccessPoints`; value needs to be flipped. + formattedMapping.put("disableMultiRegionAccessPoints", (!(originalMapping.value.expectBooleanNode().value)).toString()) + } + "useGlobalEndpoint", "forcePathStyle", "useArnRegion" -> { + // No change needed for these + formattedMapping.put(originalMapping.key.value, originalMapping.value.expectBooleanNode().value.toString()) + } + } + } + return formattedMapping + } +} From 772a902f06c5978195a37eca7f869ad1834fe397 Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Thu, 10 Oct 2024 15:20:23 -0700 Subject: [PATCH 03/11] Add SmokeTest package manifest generation on top of existing aws-sdk-swift package manifeset generation command. --- AWSSDKSwiftCLI/Package.swift | 1 + .../AWSCLIUtils/FileManager+Utils.swift | 13 +++++++ .../Subcommands/GeneratePackageManifest.swift | 34 ++++++++++++++-- .../Resources/SmokeTestsPackage.Base.txt | 39 +++++++++++++++++++ 4 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt diff --git a/AWSSDKSwiftCLI/Package.swift b/AWSSDKSwiftCLI/Package.swift index 8d9ac136740..584fd7a0454 100644 --- a/AWSSDKSwiftCLI/Package.swift +++ b/AWSSDKSwiftCLI/Package.swift @@ -26,6 +26,7 @@ let package = Package( resources: [ .process("Resources/Package.Prefix.txt"), .process("Resources/Package.Base.txt"), + .process("Resources/SmokeTestsPackage.Base.txt"), .process("Resources/DocIndex.Base.md") ] ), diff --git a/AWSSDKSwiftCLI/Sources/AWSCLIUtils/FileManager+Utils.swift b/AWSSDKSwiftCLI/Sources/AWSCLIUtils/FileManager+Utils.swift index 8dcce837d6a..e689c5250ed 100644 --- a/AWSSDKSwiftCLI/Sources/AWSCLIUtils/FileManager+Utils.swift +++ b/AWSSDKSwiftCLI/Sources/AWSCLIUtils/FileManager+Utils.swift @@ -36,6 +36,19 @@ public extension FileManager { .filter { !$0.hasPrefix(".") } } + /// Returns the list of services that have smoke tests and test runner generated for them. + /// Service names are extracted from service name prefix of directory names under `SmokeTests/`. + /// E.g., extract `AWSS3` from `SmokeTests/AWSS3SmokeTestRunner/`. + /// + /// - Returns: The list of services with generated smoke tests. + func servicesWithSmokeTests() throws -> [String] { + try FileManager.default + .contentsOfDirectory(atPath: "SmokeTests") + .sorted() + .filter { !$0.hasPrefix(".") && $0.hasSuffix("SmokeTestRunner") } + .map { $0.replacingOccurrences(of: "SmokeTestRunner", with: "") } + } + /// Returns the list of Smithy runtime modules within `../smithy-swift/Sources/Core` /// /// - Returns: The list of Smithy runtime modules. diff --git a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/GeneratePackageManifest.swift b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/GeneratePackageManifest.swift index 376b0cf8c36..ab5f16f6929 100644 --- a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/GeneratePackageManifest.swift +++ b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/GeneratePackageManifest.swift @@ -79,12 +79,38 @@ struct GeneratePackageManifest { /// Generates a package manifest file and saves it to `packageFileName` func run() throws { try FileManager.default.changeWorkingDirectory(repoPath) + + // Generate package manifest for aws-sdk-swift and save it as aws-sdk-swift/Package.swift let contents = try generatePackageManifestContents() - try savePackageManifest(contents) + try savePackageManifest(contents, packageFileName) + + // Generate package manifest for smoke tests and save it as aws-sdk-swift/SmokeTests/Package.swift + let smokeTestsContents = try generateSmokeTestsPackageManifestContents() + try savePackageManifest(contents, "SmokeTests/\(packageFileName)") } // MARK: - Helpers + func generateSmokeTestsPackageManifestContents() throws -> String { + return [ + // SmokeTests package manifest uses same prefix as one for aws-sdk-swift. + try PackageManifestBuilder.contentReader(filename: "Package.Prefix")(), + try generateServiceNamesArray(), + try PackageManifestBuilder.contentReader(filename: "SmokeTestsPackage.Base.txt")() + ].joined(separator: .newline) + } + + func generateServiceNamesArray() throws -> String { + let servicesWithSmokeTests = try FileManager.default.servicesWithSmokeTests() + let formatedServiceList = servicesWithSmokeTests.map { "\t\($0)," }.joined(separator: .newline) + return [ + "// All services that have smoke tests generated for them.", + "let serviceNames: [String] = [", + formatedServiceList, + "]" + ].joined(separator: .newline) + } + /// Returns the contents of the generated package manifest. /// This determines the versions of the dependencies and the list of services to include and then genraetes the package manifest with those values. /// @@ -102,10 +128,10 @@ struct GeneratePackageManifest { /// If no file exists, then this will create a new file. Otherwise, this will overwrite the existing file. /// /// - Parameter contents: The contents of the package manifest. - func savePackageManifest(_ contents: String) throws { - log("Saving package manifest to \(packageFileName)...") + func savePackageManifest(_ contents: String, _ packageFilePath: String) throws { + log("Saving package manifest to \(packageFilePath)...") try contents.write( - toFile: packageFileName, + toFile: packageFilePath, atomically: true, encoding: .utf8 ) diff --git a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt new file mode 100644 index 00000000000..0bb7c6e59ad --- /dev/null +++ b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt @@ -0,0 +1,39 @@ +// MARK: - Static Content + +extension Target.Dependency { + // AWS runtime module + static var awsClientRuntime: Self { .product(name: "AWSClientRuntime", package: "aws-sdk-swift") } + // Smithy runtime module + static var clientRuntime: Self { .product(name: "ClientRuntime", package: "smithy-swift") } +} + +let package = Package( + name: "SmokeTests", + platforms: [ + .macOS(.v10_15) + ], + dependencies: [ + .package(path: "../../smithy-swift"), + .package(path: "../../aws-sdk-swift") + ], + products: serviceNames.map(productForRunner(_:)), + targets: serviceNames.map(targetForRunner(_:)) +) + +// MARK: - Helper functions + +private func productForRunner(_ serviceName: String) -> Product { + .executable(name: "\(serviceName)SmokeTestRunner", targets: ["\(serviceName)SmokeTestRunner"]) +} + +private func targetForRunner(_ serviceName: String) -> Target { + .executableTarget( + name: "\(serviceName)SmokeTestRunner", + dependencies: [ + .clientRuntime, + .awsClientRuntime, + .product(name: "\(serviceName)", package: "aws-sdk-swift") + ], + path: "\(serviceName)SmokeTestRunner" + ) +} From 90fe001782d441c7bbd57d80077c36ec3df481a7 Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Fri, 11 Oct 2024 14:55:13 -0700 Subject: [PATCH 04/11] Fix compile errors / warnings --- .../AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt | 2 +- .../smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt index 0bb7c6e59ad..537ffc87374 100644 --- a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt +++ b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Resources/SmokeTestsPackage.Base.txt @@ -12,11 +12,11 @@ let package = Package( platforms: [ .macOS(.v10_15) ], + products: serviceNames.map(productForRunner(_:)), dependencies: [ .package(path: "../../smithy-swift"), .package(path: "../../aws-sdk-swift") ], - products: serviceNames.map(productForRunner(_:)), targets: serviceNames.map(targetForRunner(_:)) ) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt index 36d81c94c8e..e8667f410e8 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt @@ -20,7 +20,7 @@ class AWSSmokeTestGenerator( override fun renderCustomFilePrivateVariables(writer: SwiftWriter) { writer.write("fileprivate let regionFromEnv = ProcessInfo.processInfo.environment[\"AWS_SMOKE_TEST_REGION\"]") - writer.write("fileprivate let tagsToSkip = (ProcessInfo.processInfo.environment[\"AWS_SMOKE_TEST_SKIP_TAGS\"] ?? \"\").components(separatedBy: \\\",\\\")\")") + writer.write("fileprivate let tagsToSkip = (ProcessInfo.processInfo.environment[\"AWS_SMOKE_TEST_SKIP_TAGS\"] ?? \"\").components(separatedBy: \",\")") } override fun handleVendorParams(vendorParams: ObjectNode, writer: SwiftWriter) { @@ -38,12 +38,12 @@ class AWSSmokeTestGenerator( /* BaseAwsVendorParams members */ "region" -> { // Take region value retrieved from environment variable if present; otherwise, take from trait definition. - val regionValue = "regionFromEnv ?? " + originalMapping.value.expectStringNode().value + val regionValue = "regionFromEnv ?? \"${originalMapping.value.expectStringNode().value}\"" formattedMapping.put("region", regionValue) formattedMapping.put("signingRegion", regionValue) } "sigv4aRegionSet" -> { /* no-op; setting multiple signing regions in config is unsupported atm. */ } - "uri" -> { formattedMapping.put("endpoint", originalMapping.value.expectStringNode().value) } + "uri" -> { formattedMapping.put("endpoint", "\"${originalMapping.value.expectStringNode().value}\"") } "useFips" -> { formattedMapping.put("useFIPS", originalMapping.value.expectBooleanNode().value.toString()) } "useDualstack" -> { formattedMapping.put("useDualStack", originalMapping.value.expectBooleanNode().value.toString()) } "useAccountIdRouting" -> { /* no-op; setting account ID routing in config is unsupported atm. */ } From b0b543802a4744261b4ecab024d6e6d02f52d6ce Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Fri, 11 Oct 2024 14:56:00 -0700 Subject: [PATCH 05/11] Fix smoke tests manifest codegen bug. --- .../Subcommands/GeneratePackageManifest.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/GeneratePackageManifest.swift b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/GeneratePackageManifest.swift index ab5f16f6929..5ce63274c87 100644 --- a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/GeneratePackageManifest.swift +++ b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/GeneratePackageManifest.swift @@ -86,7 +86,7 @@ struct GeneratePackageManifest { // Generate package manifest for smoke tests and save it as aws-sdk-swift/SmokeTests/Package.swift let smokeTestsContents = try generateSmokeTestsPackageManifestContents() - try savePackageManifest(contents, "SmokeTests/\(packageFileName)") + try savePackageManifest(smokeTestsContents, "SmokeTests/\(packageFileName)") } // MARK: - Helpers @@ -96,13 +96,13 @@ struct GeneratePackageManifest { // SmokeTests package manifest uses same prefix as one for aws-sdk-swift. try PackageManifestBuilder.contentReader(filename: "Package.Prefix")(), try generateServiceNamesArray(), - try PackageManifestBuilder.contentReader(filename: "SmokeTestsPackage.Base.txt")() + try PackageManifestBuilder.contentReader(filename: "SmokeTestsPackage.Base")() ].joined(separator: .newline) } func generateServiceNamesArray() throws -> String { let servicesWithSmokeTests = try FileManager.default.servicesWithSmokeTests() - let formatedServiceList = servicesWithSmokeTests.map { "\t\($0)," }.joined(separator: .newline) + let formatedServiceList = servicesWithSmokeTests.map { "\t\"\($0)\"," }.joined(separator: .newline) return [ "// All services that have smoke tests generated for them.", "let serviceNames: [String] = [", From ba0b666de1044691f3ff680ad246bc4eca3de400 Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Fri, 11 Oct 2024 14:56:33 -0700 Subject: [PATCH 06/11] Modify staging SDK logic for smoke test directories. --- codegen/sdk-codegen/build.gradle.kts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/codegen/sdk-codegen/build.gradle.kts b/codegen/sdk-codegen/build.gradle.kts index ed342d159bb..34e88492b94 100644 --- a/codegen/sdk-codegen/build.gradle.kts +++ b/codegen/sdk-codegen/build.gradle.kts @@ -203,13 +203,23 @@ val AwsService.modelExtrasDir: String task("stageSdks") { group = "codegen" - description = "relocate generated SDK(s) from build directory to Sources and Tests directories" + description = "relocate generated SDK artifacts from build directory to correct locations" doLast { discoveredServices.forEach { logger.info("copying ${it.outputDir} source to ${it.sourcesDir}") copy { - from("${it.outputDir}") - into("${it.sourcesDir}") + from(it.outputDir) + into(it.sourcesDir) + exclude { details -> + // Exclude `SmokeTests` directory + details.file.name.endsWith("SmokeTests") + } + } + + logger.info("copying ${it.outputDir}/SmokeTests to SmokeTests") + copy { + from("${it.outputDir}/SmokeTests") + into(rootProject.file("SmokeTests").absolutePath) } } } From 83407befbb53bbc8d08126bfe09cf188ddd1c108 Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Fri, 11 Oct 2024 16:16:43 -0700 Subject: [PATCH 07/11] Add override variables for ignoring smoke test codegen by test ID or test tag. Also, add rm -rf for previously generated smoke test artifacts to codegen.sh script. --- .../aws/swift/codegen/AWSSmokeTestGenerator.kt | 12 ++++++++++++ scripts/codegen.sh | 1 + 2 files changed, 13 insertions(+) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt index e8667f410e8..53e4d81c95b 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt @@ -10,6 +10,18 @@ import software.amazon.smithy.swift.codegen.utils.toUpperCamelCase class AWSSmokeTestGenerator( private val ctx: ProtocolGenerator.GenerationContext ) : SmokeTestGenerator(ctx) { + // Filter out tests by name or tag at codegen time. + // Each element must have the prefix ":" before the test name or tag name. + // E.g., "AWSS3:GetObjectTest" or "AWSS3:BucketTests" + override val testIdsToIgnore = setOf( + // Add smoke test name to ignore here: + // E.g., "AWSACM:GetCertificateFailure", + ) + override val testTagsToIgnore = setOf( + // Add smoke test tag to ignore here: + // E.g., "AWSACM:TagToIgnore", + ) + override fun getServiceName(): String { return "AWS" + ctx.service.getTrait(ServiceTrait::class.java).get().sdkId.toUpperCamelCase() } diff --git a/scripts/codegen.sh b/scripts/codegen.sh index e04596ab867..53e38550e87 100755 --- a/scripts/codegen.sh +++ b/scripts/codegen.sh @@ -23,6 +23,7 @@ rm -rf codegen/sdk-codegen/build/smithyprojections/sdk-codegen/* rm -rf ServiceClients/* rm -rf Sources/Services/* rm -rf Tests/Services/* +rm -rf SmokeTests/* # Regenerate code ./gradlew -p codegen/sdk-codegen build From 4ef5af655b87763376fbf1caaa7b630eceb7384a Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Wed, 16 Oct 2024 11:24:17 -0700 Subject: [PATCH 08/11] Add convenience script for running multiple test runners based on AWS_SMOKE_TEST_SERVICE_IDS environment variable. --- scripts/run-smoke-tests.sh | 83 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100755 scripts/run-smoke-tests.sh diff --git a/scripts/run-smoke-tests.sh b/scripts/run-smoke-tests.sh new file mode 100755 index 00000000000..227cce92af1 --- /dev/null +++ b/scripts/run-smoke-tests.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# This script must be run from the directory containing aws-sdk-swift. +SMOKE_TESTS_DIR="aws-sdk-swift/SmokeTests/" +cd "$SMOKE_TESTS_DIR" || { echo "ERROR: Failed to change directory to $SMOKE_TESTS_DIR"; exit 1; } + +print_header() { + local header=$1 + print_empty_line + echo "##### $header #####" + print_empty_line +} + +print_empty_line() { + echo "" +} + +print_header "SETUP SMOKE TESTS" + +# Clean build +swift package clean > /dev/null 2>&1 + +# Expect comma-separated service names from AWS_SMOKE_TEST_SERVICE_IDS environment variable. +# The service names in environment variable must be in the format "AWSName", e.g., "AWSS3". +# The resulting array will be in the format "AWSNameSmokeTestRunner", e.g., "AWSS3SmokeTestRunner". +if [ -z "$AWS_SMOKE_TEST_SERVICE_IDS" ]; then + echo "INFO: The environment variable AWS_SMOKE_TEST_SERVICE_IDS is not set or is empty." + echo "INFO: It must set to a comma-separated string of service names for which you want to run smoke test for." + echo "INFO: Exiting run-smoke-tests.sh with exit code 0." + exit 0 +else + IFS=',' read -r -a testRunnerNames <<< "$AWS_SMOKE_TEST_SERVICE_IDS" + for i in "${!testRunnerNames[@]}"; do + testRunnerNames[$i]="${testRunnerNames[$i]}SmokeTestRunner" + done + echo "INFO: Retrieved the value of AWS_SMOKE_TEST_SERVICE_IDS: $AWS_SMOKE_TEST_SERVICE_IDS." + echo "INFO: Constructed test runner names: ${testRunnerNames[@]}" +fi + +# Array of test failure logs. +testFailureLogs=() + +print_header "RUN SMOKE TESTS" + +for testRunnerName in "${testRunnerNames[@]}"; do + # If test runner was generated under SmokeTests/ + if [ -d "$testRunnerName" ]; then + echo "INFO: Found smoke tests for the service ${testRunnerName%SmokeTestRunner}." + echo "INFO: Building smoke test(s) for the service ${testRunnerName%SmokeTestRunner}..." + # Build the test runner executable (discard output for clean log) + swift build --target "$testRunnerName" > /dev/null 2>&1 + echo "INFO: Running smoke test(s) for the service ${testRunnerName%SmokeTestRunner}..." + # Run executable and save output to `testRunOutput` + testRunOutput=$(swift run --quiet "$testRunnerName") + # Get test runner exit code + testRunExitCode=$? + # If exit code was 1, one or more tests failed. Save its output to array. + if [ "$testRunExitCode" -eq 1 ]; then + testFailureLogs+=("$testRunOutput") + fi + print_empty_line + # If no smoke tests were generated, no-op + else + echo "INFO: No smoke tests found for the service ${testRunnerName%SmokeTestRunner}. Skipping..." + print_empty_line + fi +done + +print_header "SMOKE TEST RESULTS" + +# Log any failure outputs if present and exit with 1 +if [ ${#testFailureLogs[@]} -gt 0 ]; then + echo "# One or more smoke test(s) failed. See the log(s) for failed test runner(s) below:" + print_empty_line + for failureLog in "${testFailureLogs[@]}"; do + echo "${failureLog}" + print_empty_line + done + exit 1 +else + echo "INFO: Every smoke test for every service passed!" + exit 0 +fi From 68224c5c6b2b864235e963ffd4f0a2af41da1f27 Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Wed, 16 Oct 2024 11:37:14 -0700 Subject: [PATCH 09/11] Place holder for adding directory; will be deleted and filled with smoke tests with each new release. --- SmokeTests/placeholder.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 SmokeTests/placeholder.txt diff --git a/SmokeTests/placeholder.txt b/SmokeTests/placeholder.txt new file mode 100644 index 00000000000..e69de29bb2d From cc056bc70fa74e078752173d941f8ac90f09adb6 Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Wed, 16 Oct 2024 14:42:16 -0700 Subject: [PATCH 10/11] temporarily disable reference to main branch for internal build test --- .../Subcommands/PrepareRelease.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/PrepareRelease.swift b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/PrepareRelease.swift index 1d21c45ea1c..b1f58b72234 100644 --- a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/PrepareRelease.swift +++ b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/PrepareRelease.swift @@ -81,16 +81,16 @@ struct PrepareRelease { try FileManager.default.changeWorkingDirectory(repoPath) let previousVersion = try getPreviousVersion() - guard try repoHasChanges(previousVersion) else { - /// If repo has no changes, create an empty release-manifest.json file. - /// Empty manifest file makes GitHubReleasePublisher be no-op. - /// The manifest file is required regardless of whether there should - /// be a release or not. - try createEmptyReleaseManifest() - /// Return without creating new commit or tag in local repos. - /// This makes GitPublisher be no-op. - return - } +// guard try repoHasChanges(previousVersion) else { +// /// If repo has no changes, create an empty release-manifest.json file. +// /// Empty manifest file makes GitHubReleasePublisher be no-op. +// /// The manifest file is required regardless of whether there should +// /// be a release or not. +// try createEmptyReleaseManifest() +// /// Return without creating new commit or tag in local repos. +// /// This makes GitPublisher be no-op. +// return +// } let newVersion = try createNewVersion(previousVersion) try stageFiles() From 19914a1829aacf46238b3e6233e4194416a6fdbb Mon Sep 17 00:00:00 2001 From: Sichan Yoo Date: Wed, 16 Oct 2024 16:25:57 -0700 Subject: [PATCH 11/11] Revert temporarily commented logic in PrepareRelease. Fix client name bug. Add convenience script for running every smoke test. --- .../Subcommands/PrepareRelease.swift | 20 ++++---- .../swift/codegen/AWSSmokeTestGenerator.kt | 6 +-- scripts/run-every-smoke-test.sh | 46 +++++++++++++++++++ 3 files changed, 59 insertions(+), 13 deletions(-) create mode 100755 scripts/run-every-smoke-test.sh diff --git a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/PrepareRelease.swift b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/PrepareRelease.swift index b1f58b72234..1d21c45ea1c 100644 --- a/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/PrepareRelease.swift +++ b/AWSSDKSwiftCLI/Sources/AWSSDKSwiftCLI/Commands/AWSSDKSwiftCLI/Subcommands/PrepareRelease.swift @@ -81,16 +81,16 @@ struct PrepareRelease { try FileManager.default.changeWorkingDirectory(repoPath) let previousVersion = try getPreviousVersion() -// guard try repoHasChanges(previousVersion) else { -// /// If repo has no changes, create an empty release-manifest.json file. -// /// Empty manifest file makes GitHubReleasePublisher be no-op. -// /// The manifest file is required regardless of whether there should -// /// be a release or not. -// try createEmptyReleaseManifest() -// /// Return without creating new commit or tag in local repos. -// /// This makes GitPublisher be no-op. -// return -// } + guard try repoHasChanges(previousVersion) else { + /// If repo has no changes, create an empty release-manifest.json file. + /// Empty manifest file makes GitHubReleasePublisher be no-op. + /// The manifest file is required regardless of whether there should + /// be a release or not. + try createEmptyReleaseManifest() + /// Return without creating new commit or tag in local repos. + /// This makes GitPublisher be no-op. + return + } let newVersion = try createNewVersion(previousVersion) try stageFiles() diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt index 53e4d81c95b..aeb317c8557 100644 --- a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSSmokeTestGenerator.kt @@ -13,11 +13,11 @@ class AWSSmokeTestGenerator( // Filter out tests by name or tag at codegen time. // Each element must have the prefix ":" before the test name or tag name. // E.g., "AWSS3:GetObjectTest" or "AWSS3:BucketTests" - override val testIdsToIgnore = setOf( + override val smokeTestIdsToIgnore = setOf( // Add smoke test name to ignore here: // E.g., "AWSACM:GetCertificateFailure", ) - override val testTagsToIgnore = setOf( + override val smokeTestTagsToIgnore = setOf( // Add smoke test tag to ignore here: // E.g., "AWSACM:TagToIgnore", ) @@ -27,7 +27,7 @@ class AWSSmokeTestGenerator( } override fun getClientName(): String { - return ctx.service.getTrait(ServiceTrait::class.java).get().sdkId.toUpperCamelCase() + "Client" + return ctx.service.getTrait(ServiceTrait::class.java).get().sdkId.toUpperCamelCase().removeSuffix("Service") + "Client" } override fun renderCustomFilePrivateVariables(writer: SwiftWriter) { diff --git a/scripts/run-every-smoke-test.sh b/scripts/run-every-smoke-test.sh new file mode 100755 index 00000000000..3cb3dd77f99 --- /dev/null +++ b/scripts/run-every-smoke-test.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# This is a convenience script for developers for running every smoke test under SmokeTests/. +# The script must be run from aws-sdk-swift/, the directory containing SmokeTests/. + +# cd into test module dir +cd SmokeTests/ || { echo "ERROR: Failed to change directory to SmokeTests."; exit 1; } + +# Build and discard output for clean log +echo "INFO: Building SmokeTests module..." +swift build > /dev/null 2>&1 + +# Header print helpers +print_header() { + print_spacer + local header=$1 + echo "##### $header #####" + print_spacer +} + +print_spacer() { + echo "" +} + +# Build and run each and every test runner; save result to results array +print_header "TEST RUNS" +results=() +for runnerName in ./*; do + if [ -d "$runnerName" ]; then + swift run "${runnerName#./}" + if [ $? -eq 0 ]; then + # Record success + results+=("SUCCESS: ${runnerName#./}") + else + # record failure + results+=("FAILURE: ${runnerName#./}") + fi + print_spacer + fi +done + +# Print result summary +print_header "TEST RESULT SUMMARY" +for result in "${results[@]}"; do + echo "$result" +done