Add --suite-name-format option to swiftTestingTestCaseNames rule (#2384)

Co-authored-by: calda <1811727+calda@users.noreply.github.com>
This commit is contained in:
Copilot
2026-02-18 15:17:29 -08:00
committed by GitHub
co-authored by calda
parent 97057ce10e
commit dbb52495a9
6 changed files with 596 additions and 57 deletions
+3 -2
View File
@@ -3669,11 +3669,12 @@ set to 4.2 or above.
## swiftTestingTestCaseNames
In Swift Testing, don't prefix @Test methods with 'test', and use raw identifier test function names.
Format Swift Testing @Test and @Suite names.
Option | Description
--- | ---
`--test-case-name-format` | Swift Testing test case name format: "preserve" or "raw-identifiers" (default)
`--test-case-name-format` | Swift Testing test case name format: "preserve", "raw-identifiers" (default) or "standard-identifiers"
`--suite-name-format` | Swift Testing suite name format: "preserve", "raw-identifiers" or "standard-identifiers" (default)
<details>
<summary>Examples</summary>
+23 -13
View File
@@ -1886,7 +1886,7 @@ extension Formatter {
newMethodName = String(newMethodName.dropFirst())
}
updateFunctionName(forFunctionAt: funcKeywordIndex, to: newMethodName)
updateDeclarationName(forDeclarationAt: funcKeywordIndex, to: newMethodName)
}
// Handle names like ``func `test feature`()``, ``func `Test Feature`()``
@@ -1895,29 +1895,39 @@ extension Formatter {
newMethodName = String(newMethodName.first!) + newMethodName.dropFirst()
newMethodName = "`" + newMethodName
updateFunctionName(forFunctionAt: funcKeywordIndex, to: newMethodName)
updateDeclarationName(forDeclarationAt: funcKeywordIndex, to: newMethodName)
}
}
/// Updates the name of the given method / function, unless that change could cause a build failure.
func updateFunctionName(forFunctionAt funcKeywordIndex: Int, to newMethodName: String) {
// The name of a function always immediately follows the `func` keyword
guard let methodNameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: funcKeywordIndex),
tokens[methodNameIndex].isIdentifier
/// Updates the name of the given declaration (function or type), unless that change could cause a build failure.
func updateDeclarationName(forDeclarationAt keywordIndex: Int, to newName: String) {
// The name of a declaration always immediately follows the keyword (e.g. `func`, `struct`, etc.)
guard let nameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: keywordIndex),
tokens[nameIndex].isIdentifier
else { return }
// For type declarations (struct, class, actor, enum), don't rename if the current
// name is referenced elsewhere in the file, since renaming would break those references.
if case let .keyword(keyword) = tokens[keywordIndex],
Token.swiftTypeKeywords.contains(keyword)
{
let currentIdentifier = tokens[nameIndex]
let referenceCount = tokens.filter { $0 == currentIdentifier }.count
guard referenceCount <= 1 else { return }
}
// Ensure that the new identifier is valid (e.g. starts with a letter, not a number),
// and is unique / doesn't already exist somewhere in the file.
let unescapedName = newMethodName.hasPrefix("`") && newMethodName.hasSuffix("`")
? String(newMethodName.dropFirst().dropLast()) : newMethodName
guard !newMethodName.isEmpty,
newMethodName.first?.isLetter == true || newMethodName.first == "`",
!tokens.contains(.identifier(newMethodName)),
let unescapedName = newName.hasPrefix("`") && newName.hasSuffix("`")
? String(newName.dropFirst().dropLast()) : newName
guard !newName.isEmpty,
newName.first?.isLetter == true || newName.first == "`",
!tokens.contains(.identifier(newName)),
!tokens.contains(.identifier(unescapedName)),
!swiftKeywords.union(["Any", "Self", "self", "super", "nil", "true", "false"]).contains(unescapedName)
else { return }
replaceToken(at: methodNameIndex, with: .identifier(newMethodName))
replaceToken(at: nameIndex, with: .identifier(newName))
}
}
+7
View File
@@ -1466,6 +1466,13 @@ struct _Descriptors {
keyPath: \.testCaseNameFormat
)
let suiteNameFormat = OptionDescriptor(
argumentName: "suite-name-format",
displayName: "Suite Name Format",
help: "Swift Testing suite name format:",
keyPath: \.suiteNameFormat
)
// MARK: - Internal
let fragment = OptionDescriptor(
+8 -4
View File
@@ -187,10 +187,11 @@ public enum ClosureVoidReturn: String, CaseIterable {
case preserve
}
/// Format for Swift Testing test case names
public enum TestCaseNameFormat: String, CaseIterable {
/// Format for Swift Testing test case / suite names
public enum SwiftTestingNameFormat: String, CaseIterable {
case preserve
case rawIdentifiers = "raw-identifiers"
case standardIdentifiers = "standard-identifiers"
}
public enum TrailingCommas: String, CaseIterable {
@@ -884,7 +885,8 @@ public struct FormatOptions: CustomStringConvertible {
public var redundantAsync: RedundantEffectMode
public var allowPartialWrapping: Bool
public var preferSynthesizedInitForInternalStructs: PreferSynthesizedInitMode
public var testCaseNameFormat: TestCaseNameFormat
public var testCaseNameFormat: SwiftTestingNameFormat
public var suiteNameFormat: SwiftTestingNameFormat
/// Deprecated
public var indentComments: Bool
@@ -1030,7 +1032,8 @@ public struct FormatOptions: CustomStringConvertible {
redundantAsync: RedundantEffectMode = .testsOnly,
allowPartialWrapping: Bool = true,
preferSynthesizedInitForInternalStructs: PreferSynthesizedInitMode = .never,
testCaseNameFormat: TestCaseNameFormat = .rawIdentifiers,
testCaseNameFormat: SwiftTestingNameFormat = .rawIdentifiers,
suiteNameFormat: SwiftTestingNameFormat = .standardIdentifiers,
// Doesn't really belong here, but hard to put elsewhere
fragment: Bool = false,
ignoreConflictMarkers: Bool = false,
@@ -1166,6 +1169,7 @@ public struct FormatOptions: CustomStringConvertible {
self.allowPartialWrapping = allowPartialWrapping
self.preferSynthesizedInitForInternalStructs = preferSynthesizedInitForInternalStructs
self.testCaseNameFormat = testCaseNameFormat
self.suiteNameFormat = suiteNameFormat
self.indentComments = indentComments
self.fragment = fragment
self.ignoreConflictMarkers = ignoreConflictMarkers
+99 -38
View File
@@ -5,8 +5,8 @@ import Foundation
public extension FormatRule {
static let swiftTestingTestCaseNames = FormatRule(
help: "In Swift Testing, don't prefix @Test methods with 'test', and use raw identifier test function names.",
options: ["test-case-name-format"]
help: "Format Swift Testing @Test and @Suite names.",
options: ["test-case-name-format", "suite-name-format"]
) { formatter in
guard formatter.hasImport("Testing") else { return }
@@ -18,12 +18,34 @@ public extension FormatRule {
switch formatter.options.testCaseNameFormat {
case .rawIdentifiers:
guard formatter.options.swiftVersion >= "6.2" else { return }
formatter.convertToRawIdentifier(forTestFunctionAt: funcKeywordIndex)
formatter.convertToRawIdentifier(forDeclarationAt: funcKeywordIndex, macroName: "@Test", upperCamelCase: false)
case .standardIdentifiers:
formatter.convertToStandardIdentifier(forDeclarationAt: funcKeywordIndex, macroName: "@Test", upperCamelCase: false)
case .preserve:
break
}
}
let typeKeywords: [String] = ["struct", "class", "actor", "enum"]
for typeKeyword in typeKeywords {
formatter.forEach(.keyword(typeKeyword)) { keywordIndex, _ in
guard formatter.modifiersForDeclaration(at: keywordIndex, contains: "@Suite") else { return }
switch formatter.options.suiteNameFormat {
case .rawIdentifiers:
guard formatter.options.swiftVersion >= "6.2" else { return }
formatter.convertToRawIdentifier(forDeclarationAt: keywordIndex, macroName: "@Suite", upperCamelCase: true)
case .standardIdentifiers:
formatter.convertToStandardIdentifier(forDeclarationAt: keywordIndex, macroName: "@Suite", upperCamelCase: true)
case .preserve:
break
}
}
}
} examples: {
"""
```diff
@@ -52,49 +74,70 @@ public extension FormatRule {
}
extension Formatter {
/// Converts a `@Test` function name to use a raw identifier (backtick-quoted name with spaces).
/// If the `@Test` attribute has a display name, uses that as the function name and removes it from the attribute.
/// Otherwise, converts the camelCase/underscore function name to a space-separated raw identifier.
func convertToRawIdentifier(forTestFunctionAt funcKeywordIndex: Int) {
guard let methodNameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: funcKeywordIndex),
tokens[methodNameIndex].isIdentifier
/// Converts a declaration name to use a raw identifier (backtick-quoted name with spaces).
/// If the macro attribute has a display name, uses that as the name and removes it from the attribute.
/// Otherwise, converts the camelCase/underscore name to a space-separated raw identifier.
func convertToRawIdentifier(forDeclarationAt keywordIndex: Int, macroName: String, upperCamelCase: Bool) {
guard let nameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: keywordIndex),
tokens[nameIndex].isIdentifier
else { return }
let methodName = tokens[methodNameIndex].string
let name = tokens[nameIndex].string
// Check if the @Test attribute has a display name argument
if var displayName = testDisplayName(forDeclarationAt: funcKeywordIndex) {
// Remove any existing backticks from the test name, since raw identifiers cant contain backticks
// Check if the macro attribute has a display name argument
if var displayName = macroDisplayName(forDeclarationAt: keywordIndex, macroName: macroName) {
// Remove any existing backticks from the name, since raw identifiers can't contain backticks
displayName = displayName.replacingOccurrences(of: "`", with: "")
let newMethodName = "`\(displayName)`"
updateFunctionName(forFunctionAt: funcKeywordIndex, to: newMethodName)
removeTestDisplayNameString(forDeclarationAt: funcKeywordIndex)
let newName = "`\(displayName)`"
updateDeclarationName(forDeclarationAt: keywordIndex, to: newName)
removeMacroDisplayNameString(forDeclarationAt: keywordIndex, macroName: macroName)
} else {
// Convert the method name to a raw identifier
let baseName = methodName.camelCaseToWords()
guard !baseName.isEmpty, baseName != methodName else { return }
// Convert the name to a raw identifier
let baseName = name.camelCaseToWords()
guard !baseName.isEmpty, baseName != name else { return }
let newMethodName = "`\(baseName)`"
guard tokens[methodNameIndex] != .identifier(newMethodName) else { return }
let newName = "`\(baseName)`"
guard tokens[nameIndex] != .identifier(newName) else { return }
updateFunctionName(forFunctionAt: funcKeywordIndex, to: newMethodName)
updateDeclarationName(forDeclarationAt: keywordIndex, to: newName)
}
}
/// Extracts the display name string from a `@Test("display name")` attribute, if present.
func testDisplayName(forDeclarationAt funcKeywordIndex: Int) -> String? {
// Walk backwards from `func` to find `@Test`
var testAttrIndex: Int?
_ = modifiersForDeclaration(at: funcKeywordIndex, contains: { index, modifier in
if modifier.hasPrefix("@Test(") || modifier.hasPrefix("@Test (") {
testAttrIndex = index
/// Converts a raw identifier declaration name to a standard identifier, and removes
/// any display name string from the macro attribute.
func convertToStandardIdentifier(forDeclarationAt keywordIndex: Int, macroName: String, upperCamelCase: Bool) {
// Remove display name string from the macro attribute if present
removeMacroDisplayNameString(forDeclarationAt: keywordIndex, macroName: macroName)
guard let nameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: keywordIndex),
tokens[nameIndex].isIdentifier
else { return }
let name = tokens[nameIndex].string
// Only convert raw identifiers (backtick-quoted names)
guard name.hasPrefix("`"), name.hasSuffix("`") else { return }
let rawName = String(name.dropFirst().dropLast())
let newName = rawName.wordsToIdentifier(upperCamelCase: upperCamelCase)
guard !newName.isEmpty, newName != name else { return }
updateDeclarationName(forDeclarationAt: keywordIndex, to: newName)
}
/// Extracts the display name string from a macro attribute like `@Test("display name")` or `@Suite("display name")`.
func macroDisplayName(forDeclarationAt keywordIndex: Int, macroName: String) -> String? {
var macroAttrIndex: Int?
_ = modifiersForDeclaration(at: keywordIndex, contains: { index, modifier in
if modifier.hasPrefix("\(macroName)(") || modifier.hasPrefix("\(macroName) (") {
macroAttrIndex = index
return true
}
return false
})
guard let attrIndex = testAttrIndex,
guard let attrIndex = macroAttrIndex,
let parenStart = index(of: .startOfScope("("), after: attrIndex),
let firstToken = index(of: .nonSpaceOrCommentOrLinebreak, after: parenStart),
tokens[firstToken] == .startOfScope("\"")
@@ -112,18 +155,18 @@ extension Formatter {
return displayName.isEmpty ? nil : displayName
}
/// Removes the display name from a `@Test("display name")` or `@Test("display name", ...)` attribute.
func removeTestDisplayNameString(forDeclarationAt funcKeywordIndex: Int) {
var testAttrIndex: Int?
_ = modifiersForDeclaration(at: funcKeywordIndex, contains: { index, modifier in
if modifier.hasPrefix("@Test(") || modifier.hasPrefix("@Test (") {
testAttrIndex = index
/// Removes the display name from a macro attribute like `@Test("display name")` or `@Suite("display name", ...)`.
func removeMacroDisplayNameString(forDeclarationAt keywordIndex: Int, macroName: String) {
var macroAttrIndex: Int?
_ = modifiersForDeclaration(at: keywordIndex, contains: { index, modifier in
if modifier.hasPrefix("\(macroName)(") || modifier.hasPrefix("\(macroName) (") {
macroAttrIndex = index
return true
}
return false
})
guard let attrIndex = testAttrIndex,
guard let attrIndex = macroAttrIndex,
let parenStart = index(of: .startOfScope("("), after: attrIndex),
let firstToken = index(of: .nonSpaceOrCommentOrLinebreak, after: parenStart),
tokens[firstToken] == .startOfScope("\""),
@@ -141,7 +184,7 @@ extension Formatter {
removeTokens(in: firstToken ..< removeEnd)
} else {
// This is the only argument — remove the entire parentheses
// Also remove any space between @Test and (
// Also remove any space between the macro and (
let removeStart = (index(of: .nonSpaceOrComment, after: attrIndex) == parenStart)
? (attrIndex + 1) : parenStart
removeTokens(in: removeStart ... parenEnd)
@@ -191,4 +234,22 @@ extension String {
return words
}
/// Converts a space-separated string to a standard identifier (camelCase or UpperCamelCase).
/// For example: "my test case" -> "myTestCase" (lowerCamelCase) or "MyTestCase" (upperCamelCase).
func wordsToIdentifier(upperCamelCase: Bool) -> String {
let words = split(separator: " ").map(String.init)
guard !words.isEmpty else { return "" }
var result = ""
for (index, word) in words.enumerated() {
guard !word.isEmpty else { continue }
if index == 0, !upperCamelCase {
result += word.prefix(1).lowercased() + word.dropFirst()
} else {
result += word.prefix(1).uppercased() + word.dropFirst()
}
}
return result
}
}
@@ -527,4 +527,460 @@ final class SwiftTestingTestCaseNamesTests: XCTestCase {
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(swiftVersion: "6.2"))
}
// MARK: - standard-identifiers for @Test
func testStandardIdentifiersConvertsRawIdentifierToLowerCamelCase() {
let input = """
import Testing
struct MyFeatureTests {
@Test
func `my feature has no bugs`() {
#expect(true)
}
}
"""
let output = """
import Testing
struct MyFeatureTests {
@Test
func myFeatureHasNoBugs() {
#expect(true)
}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
}
func testStandardIdentifiersRemovesDisplayNameFromTest() {
let input = """
import Testing
struct MyFeatureTests {
@Test("My feature works")
func myFeatureWorks() {
#expect(true)
}
}
"""
let output = """
import Testing
struct MyFeatureTests {
@Test
func myFeatureWorks() {
#expect(true)
}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
}
func testStandardIdentifiersRemovesDisplayNameAndConvertsRawIdentifier() {
let input = """
import Testing
struct MyFeatureTests {
@Test("My feature works")
func `my feature works`() {
#expect(true)
}
}
"""
let output = """
import Testing
struct MyFeatureTests {
@Test
func myFeatureWorks() {
#expect(true)
}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
}
func testStandardIdentifiersRemovesDisplayNameWithArguments() {
let input = """
import Testing
struct MyFeatureTests {
@Test("Features work as expected", arguments: [
.foo,
.bar,
])
func `features work as expected`(_ feature: Feature) {
#expect(true)
}
}
"""
let output = """
import Testing
struct MyFeatureTests {
@Test(arguments: [
.foo,
.bar,
])
func featuresWorkAsExpected(_ feature: Feature) {
#expect(true)
}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"),
exclude: [.unusedArguments])
}
func testStandardIdentifiersPreservesStandardIdentifierFunction() {
let input = """
import Testing
struct MyFeatureTests {
@Test
func myFeatureWorks() {
#expect(true)
}
}
"""
testFormatting(for: input, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
}
func testStandardIdentifiersStillRemovesTestPrefix() {
let input = """
import Testing
struct MyFeatureTests {
@Test
func testMyFeatureWorks() {
#expect(true)
}
}
"""
let output = """
import Testing
struct MyFeatureTests {
@Test
func myFeatureWorks() {
#expect(true)
}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
}
// MARK: - @Suite with standard-identifiers (default)
func testSuiteStandardIdentifiersRemovesDisplayName() {
let input = """
import Testing
@Suite("My Feature Tests")
struct MyFeatureTests {
@Test func myTest() {}
}
"""
let output = """
import Testing
@Suite
struct MyFeatureTests {
@Test func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
func testSuiteStandardIdentifiersConvertsRawIdentifier() {
let input = """
import Testing
@Suite
struct `My test suite` {
@Test func myTest() {}
}
"""
let output = """
import Testing
@Suite
struct MyTestSuite {
@Test func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
func testSuiteStandardIdentifiersRemovesDisplayNameAndConvertsRawIdentifier() {
let input = """
import Testing
@Suite("My test suite")
struct `My test suite` {
@Test func myTest() {}
}
"""
let output = """
import Testing
@Suite
struct MyTestSuite {
@Test func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
func testSuiteStandardIdentifiersPreservesStandardName() {
let input = """
import Testing
@Suite
struct MyFeatureTests {
@Test func myTest() {}
}
"""
testFormatting(for: input, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
func testSuiteStandardIdentifiersRemovesDisplayNameWithOtherArgs() {
let input = """
import Testing
@Suite("My Feature Tests", .serialized)
struct MyFeatureTests {
@Test func myTest() {}
}
"""
let output = """
import Testing
@Suite(.serialized)
struct MyFeatureTests {
@Test func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
// MARK: - @Suite with raw-identifiers
func testSuiteRawIdentifiersConvertsCamelCase() {
let input = """
import Testing
@Suite
struct MyFeatureTests {
@Test func myTest() {}
}
"""
let output = """
import Testing
@Suite
struct `my feature tests` {
@Test func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve,
suiteNameFormat: .rawIdentifiers,
swiftVersion: "6.2"))
}
func testSuiteRawIdentifiersUsesDisplayName() {
let input = """
import Testing
@Suite("My Feature Tests")
struct MyFeatureTests {
@Test func myTest() {}
}
"""
let output = """
import Testing
@Suite
struct `My Feature Tests` {
@Test func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve,
suiteNameFormat: .rawIdentifiers,
swiftVersion: "6.2"))
}
func testSuiteRawIdentifiersFallsBackToPreserveBelowSwift6_2() {
let input = """
import Testing
@Suite
struct MyFeatureTests {
@Test func myTest() {}
}
"""
testFormatting(for: input, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve,
suiteNameFormat: .rawIdentifiers,
swiftVersion: "6.1"))
}
// MARK: - @Suite with preserve
func testSuitePreserveKeepsEverything() {
let input = """
import Testing
@Suite("My Feature Tests")
struct MyFeatureTests {
@Test func myTest() {}
}
"""
testFormatting(for: input, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve,
suiteNameFormat: .preserve,
swiftVersion: "6.2"))
}
// MARK: - @Suite on class/actor/enum
func testSuiteWorksWithClass() {
let input = """
import Testing
@Suite("My Feature Tests")
class MyFeatureTests {
@Test func myTest() {}
}
"""
let output = """
import Testing
@Suite
class MyFeatureTests {
@Test func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
func testSuiteWorksWithActor() {
let input = """
import Testing
@Suite("My Feature Tests")
actor MyFeatureTests {
@Test func myTest() {}
}
"""
let output = """
import Testing
@Suite
actor MyFeatureTests {
@Test func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
func testSuiteWorksWithEnum() {
let input = """
import Testing
@Suite("My Feature Tests")
enum MyFeatureTests {
@Test static func myTest() {}
}
"""
let output = """
import Testing
@Suite
enum MyFeatureTests {
@Test static func myTest() {}
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
func testSuiteNameNotUpdatedWhenReferencedAsStaticMember() {
let input = """
import Testing
@Suite("My Feature Tests")
struct `My Feature Tests` {
@Test func myTest() {}
}
func runTests() {
`My Feature Tests`.runAll()
}
"""
let output = """
import Testing
@Suite
struct `My Feature Tests` {
@Test func myTest() {}
}
func runTests() {
`My Feature Tests`.runAll()
}
"""
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
}
}