Skip to content

Commit

Permalink
Merge pull request #9 from giginet/snake-case-conversion
Browse files Browse the repository at this point in the history
Implement snake_case Conversion and fix inifinite loops
  • Loading branch information
giginet authored Jul 18, 2024
2 parents 4a12e21 + 8890699 commit d3416ff
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 23 deletions.
40 changes: 40 additions & 0 deletions Sources/RevolutionKit/Rewriter/TestMethodNameConverter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation

struct TestMethodNameConverter {
let shouldStripPrefix: Bool
private let testPrefix = "test"
private let snakeCasePrefix = "test_"

/// Strip first `test` prefix from test case names.
/// `testCamelCase` -> `camelCase`
/// `test_snake_case` -> `snake_case`
/// `test` -> `test`
func convert(_ testMethodName: String) -> String {
guard shouldStripPrefix else { return testMethodName }

if testMethodName.hasPrefix(snakeCasePrefix) {
return testMethodName.strippedFirst(snakeCasePrefix.count)
} else if testMethodName == testPrefix {
return testMethodName
} else if testMethodName.hasPrefix(testPrefix) {
return testMethodName
.strippedFirst(testPrefix.count)
.lowercasedFirstLetter()
}
assertionFailure("Unexpected call")
return testMethodName
}
}

extension String {
fileprivate func strippedFirst(_ k: Int) -> String {
var mutating = self
mutating.removeFirst(k)
return mutating
}

fileprivate func lowercasedFirstLetter() -> String {
guard let firstLetter = first else { return self }
return firstLetter.lowercased() + self[index(after: startIndex)..<endIndex]
}
}
44 changes: 21 additions & 23 deletions Sources/RevolutionKit/Rewriter/XCTestRewriter+MethodVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import SwiftSyntax
/// Visitor to rewrite XCTest's test methods to swift-testing.
extension XCTestRewriter {
func visitForTestFunctionDecl(_ node: FunctionDeclSyntax) -> DeclSyntax {
guard !node.hasTestMacroAttribute else { return super.visit(node) }
guard let methodKind = detectMethodKind(of: node) else {
return super.visit(node)
}
Expand All @@ -18,15 +19,17 @@ extension XCTestRewriter {
}
}

private var testMethodNameConverter: TestMethodNameConverter {
TestMethodNameConverter(
shouldStripPrefix: globalOptions.enableStrippingTestPrefix
)
}

/// Rewrite XCTest test case methods to swift-testing
/// func testExample() -> @Test func example()
private func rewriteTestCase(node: FunctionDeclSyntax) -> DeclSyntax {
let testCaseName = node.name.text
let newTestCaseName = if globalOptions.enableStrippingTestPrefix {
strippingTestPrefix(of: testCaseName)
} else {
testCaseName
}
let newTestCaseName = testMethodNameConverter.convert(testCaseName)

let testMacroAttribute = AttributeSyntax(
attributeName: IdentifierTypeSyntax(
Expand Down Expand Up @@ -91,24 +94,6 @@ extension XCTestRewriter {
return DeclSyntax(deinitializerDecl)
}

/// Strip first `test` prefix from test case names.
/// `testCamelCase` -> `camelCase`
/// `test` -> `test`
private func strippingTestPrefix(of testCaseName: String) -> String {
precondition(testCaseName.hasPrefix("test"))

return {
var convertedName = testCaseName
convertedName.removeFirst(4)

guard let firstCharacter = convertedName.first else {
return testCaseName
}

return firstCharacter.lowercased() + convertedName.dropFirst()
}()
}

/// Returns a kind of the method
private func detectMethodKind(of node: FunctionDeclSyntax) -> MethodKind? {
guard !isStaticMethod(node: node) else { return nil }
Expand Down Expand Up @@ -152,3 +137,16 @@ extension XCTestRewriter {
case tearDown
}
}

extension FunctionDeclSyntax {
fileprivate var hasTestMacroAttribute: Bool {
attributes.contains { attribute in
switch attribute {
case .attribute(let attributeNode):
let attributeName = attributeNode.attributeName.as(IdentifierTypeSyntax.self)?.name
return attributeName?.tokenKind == .identifier("Test")
case .ifConfigDecl: return false
}
}
}
}
21 changes: 21 additions & 0 deletions Tests/RevolutionKitTests/TestMethodNameConverterTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Testing
@testable import RevolutionKit

struct TestMethodNameConverterTests {
private static let fixtures = [
(true, "testDoSomething", "doSomething"),
(false, "testDoSomething", "testDoSomething"),
(true, "test_do_something", "do_something"),
(false, "test_do_something", "test_do_something"),
(true, "test", "test"),
(false, "test", "test"),
]

@Test("TestMethodNameConverter can convert method names", arguments: fixtures)
func testConversion(enableStripping: Bool, input: String, expected: String) {
let converter = TestMethodNameConverter(shouldStripPrefix: enableStripping)

let actual = converter.convert(input)
#expect(actual == expected)
}
}
20 changes: 20 additions & 0 deletions Tests/RevolutionKitTests/TestMethodsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ private let testCaseConversionFixtures: [ConversionTestFixture] = [
}
"""
),
.init(
"""
func test_do_something() {
}
""",
"""
@Test func do_something() {
}
"""
),
.init(
"""
func test() {
}
""",
"""
@Test func test() {
}
"""
),
.init(
"""
@MainActor func testExample() {
Expand Down

0 comments on commit d3416ff

Please sign in to comment.