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

Introduces unit testing for code generators #1

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
name: Build
on:
- push
- pull_request
push:
branches:
- main
pull_request:

jobs:
build:
Expand All @@ -17,3 +19,6 @@ jobs:
uses: actions/checkout@v3
- name: Build
run: swift build -c ${{ matrix.build-config }}
- name: Test
if: ${{ matrix.build-config == 'debug' }}
run: swift test
23 changes: 23 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,28 @@ let package = Package(
dependencies: [
.byName(name: "TecoCodeGeneratorCommons"),
]),
.target(
name: "TecoCodeGeneratorTestHelpers",
dependencies: [
.byName(name: "TecoCodeGeneratorCommons"),
]),
.testTarget(
name: "TecoDateWrapperGeneratorTests",
dependencies: [
.byName(name: "TecoCodeGeneratorTestHelpers"),
.byName(name: "TecoDateWrapperGenerator"),
]),
.testTarget(
name: "TecoCommonErrorGeneratorTests",
dependencies: [
.byName(name: "TecoCodeGeneratorTestHelpers"),
.byName(name: "TecoCommonErrorGenerator"),
]),
.testTarget(
name: "TecoPackageGeneratorTests",
dependencies: [
.byName(name: "TecoCodeGeneratorTestHelpers"),
.byName(name: "TecoPackageGenerator"),
]),
]
)
38 changes: 38 additions & 0 deletions Sources/TecoCodeGeneratorTestHelpers/AssertBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import SwiftSyntax
@_implementationOnly import TecoCodeGeneratorCommons
import XCTest

private func buildSyntax(_ buildable: some SyntaxProtocol) -> [String] {
// Format the code.
let source = buildable.formatted(using: CodeGenerationFormat())

// Work around styling issues regarding blank lines.
let code = source.description.trimmingCharacters(in: .whitespacesAndNewlines)

// Work around styling issues regarding trailing whitespaces.
return code.split(omittingEmptySubsequences: false, whereSeparator: \.isNewline).map {
var line = $0
while line.last?.isWhitespace == true {
line.removeLast()
}
return String(line)
}
}

public func AssertBuilder(_ buildable: some SyntaxProtocol, _ result: String) {
// Format the code.
let code = buildSyntax(buildable).joined(separator: "\n")

// Assert build result
XCTAssertEqual(code, result)
}

public func AssertBuilder(_ buildable: some SyntaxProtocol, contains lines: [String]) {
// Format the code.
let code = buildSyntax(buildable)

// Assert build result
for line in lines {
XCTAssertTrue(code.contains(line))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import TecoCodeGeneratorTestHelpers
import XCTest

#if Xcode // Works around FB11980900
@testable import teco_common_error_generator
#else
@testable import TecoCommonErrorGenerator
#endif

final class TecoCommonErrorGeneratorTests: XCTestCase {
func testCommonErrorStructDeclBuilder() {
let errors = [
CommonError(code: "ActionOffline", description: "This API has been deprecated.\n接口已下线。", solution: nil),
CommonError(
code: "AuthFailure.SecretIdNotFound",
description: """
Key does not exist. Check if the key has been deleted or disabled in the console, and if not, check if the key is correctly entered. Note that whitespaces should not exist before or after the key.
密钥不存在。请在[控制台](https://console.cloud.tencent.com/cam/capi)检查密钥是否已被删除或者禁用,如状态正常,请检查密钥是否填写正确,注意前后不得有空格。
""",
solution: """
- The SecretId is not found, please ensure that your SecretId is correct.
SecretId不存在,请输入正确的密钥。

当您接口返回这些错误时,说明您调接口时用的密钥信息不存在,请在控制台检查密钥是否已被删除或者禁用,如状态正常,请检查密钥是否填写正确,注意前后不得有空格。
"""
),
]
AssertBuilder(buildCommonErrorStructDecl(from: errors), """
/// Common error type returned by Tencent Cloud.
public struct TCCommonError: TCServiceErrorType {
enum Code: String {
case actionOffline = "ActionOffline"
case authFailure_SecretIdNotFound = "AuthFailure.SecretIdNotFound"
}

private let error: Code

public let context: TCErrorContext?

public var errorCode: String {
self.error.rawValue
}

public init?(errorCode: String, context: TCErrorContext) {
guard let error = Code(rawValue: errorCode) else {
return nil
}
self.error = error
self.context = context
}

public func asCommonError() -> TCCommonError? {
return self
}

internal init(_ error: Code, context: TCErrorContext? = nil) {
self.error = error
self.context = context
}

/// This API has been deprecated.
/// 接口已下线。
public static var actionOffline: TCCommonError {
TCCommonError(.actionOffline)
}

/// Key does not exist. Check if the key has been deleted or disabled in the console, and if not, check if the key is correctly entered. Note that whitespaces should not exist before or after the key.
/// 密钥不存在。请在[控制台](https://console.cloud.tencent.com/cam/capi)检查密钥是否已被删除或者禁用,如状态正常,请检查密钥是否填写正确,注意前后不得有空格。
///
/// - The SecretId is not found, please ensure that your SecretId is correct.
/// SecretId不存在,请输入正确的密钥。
///
/// 当您接口返回这些错误时,说明您调接口时用的密钥信息不存在,请在控制台检查密钥是否已被删除或者禁用,如状态正常,请检查密钥是否填写正确,注意前后不得有空格。
public static var authFailure_SecretIdNotFound: TCCommonError {
TCCommonError(.authFailure_SecretIdNotFound)
}
}
""")
}

func testFormatErrorSolution() {
let solution = """
The provided credentials could not be validated. Please check your signature is correct.
请求签名验证失败,请检查您的签名计算是否正确。
The provided credentials could not be validated because of exceeding request size limit, please use new signature method `TC3-HMAC-SHA256`.
由于请求包大小超过限制,请求签名验证失败,请使用新的签名方法 `TC3-HMAC-SHA256`。

当您看到此类错误信息,说明此次请求签名计算错误,强烈建议使用官网提供的 SDK 调用,自己计算签名比较容易出错,SDK 屏蔽了计算签名的细节,调用者只需关注接口参数。
如果仍然想自己计算签名,参照官网签名文档,可以在API Explorer【签名串生成】处进行签名验证。
此外,SecretKey输入错误也可能会导致签名计算错误。
"""
XCTAssertEqual(formatErrorSolution(solution), """
- The provided credentials could not be validated. Please check your signature is correct.
请求签名验证失败,请检查您的签名计算是否正确。
- The provided credentials could not be validated because of exceeding request size limit, please use new signature method `TC3-HMAC-SHA256`.
由于请求包大小超过限制,请求签名验证失败,请使用新的签名方法 `TC3-HMAC-SHA256`。

当您看到此类错误信息,说明此次请求签名计算错误,强烈建议使用官网提供的 SDK 调用,自己计算签名比较容易出错,SDK 屏蔽了计算签名的细节,调用者只需关注接口参数。
如果仍然想自己计算签名,参照官网签名文档,可以在API Explorer【签名串生成】处进行签名验证。
此外,SecretKey输入错误也可能会导致签名计算错误。
""")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import TecoCodeGeneratorTestHelpers
import XCTest

#if Xcode // Works around FB11980900
@testable import teco_date_wrapper_generator
#else
@testable import TecoDateWrapperGenerator
#endif

final class TecoDateWrapperGeneratorTests: XCTestCase {
func testImportDeclsBuilder() {
AssertBuilder(buildImportDecls(for: .date), contains: [
"import struct Foundation.Date",
"import class Foundation.DateFormatter"
])
AssertBuilder(buildImportDecls(for: .timestamp), contains: [
"import struct Foundation.Date",
"import class Foundation.DateFormatter"
])
AssertBuilder(buildImportDecls(for: .timestamp_iso8601), contains: [
"import struct Foundation.Date",
"import class Foundation.ISO8601DateFormatter"
])
}

func testDateWrapperNames() {
let expected: Set = ["TCDateEncoding", "TCTimestampEncoding", "TCTimestampISO8601Encoding"]
XCTAssertEqual(Set(DateEncoding.all.map(\.rawValue)), expected)
}
}
48 changes: 48 additions & 0 deletions Tests/TecoPackageGeneratorTests/TecoPackageGeneratorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import TecoCodeGeneratorTestHelpers
import XCTest

#if Xcode // Works around FB11980900
@testable import teco_package_generator
#else
@testable import TecoPackageGenerator
#endif

final class TecoPackageGeneratorTests: XCTestCase {
private let services: [(service: String, version: String)] = [
("Aa", "V20200224"), ("Ams", "V20200608"), ("Ams", "V20201229"),
]

func testProductExprBuilder() {
AssertBuilder(buildProductExpr(name: "TecoDemo"), """
.library(name: "TecoDemo", targets: ["TecoDemo"])
""")
AssertBuilder(buildProductExpr(name: "TecoDemo", trailingComma: true), """
.library(name: "TecoDemo", targets: ["TecoDemo"]),
""")
}

func testProductExprListBuilder() {
AssertBuilder(buildProductListExpr(for: services), contains: [
#" .library(name: "TecoAaV20200224", targets: ["TecoAaV20200224"]),"#,
#" .library(name: "TecoAmsV20200608", targets: ["TecoAmsV20200608"]),"#,
#" .library(name: "TecoAmsV20201229", targets: ["TecoAmsV20201229"]),"#,
])
}

func testTargetExprBuilder() {
AssertBuilder(buildTargetExpr(name: "TecoDemo", path: "./Demo"), """
.target(name: "TecoDemo", dependencies: [.product(name: "TecoCore", package: "teco-core")], path: "./Demo")
""")
AssertBuilder(buildTargetExpr(name: "TecoDemo", path: "./Demo", trailingComma: true), """
.target(name: "TecoDemo", dependencies: [.product(name: "TecoCore", package: "teco-core")], path: "./Demo"),
""")
}

func testTargetExprListBuilder() {
AssertBuilder(buildTargetListExpr(for: services), contains: [
#" .target(name: "TecoAaV20200224", dependencies: [.product(name: "TecoCore", package: "teco-core")], path: "./Sources/Teco/Aa/V20200224"),"#,
#" .target(name: "TecoAmsV20201229", dependencies: [.product(name: "TecoCore", package: "teco-core")], path: "./Sources/Teco/Ams/V20201229"),"#,
#" .target(name: "TecoAmsV20200608", dependencies: [.product(name: "TecoCore", package: "teco-core")], path: "./Sources/Teco/Ams/V20200608"),"#,
])
}
}