Files
SwiftFormat/Tests/Rules/RedundantAsyncTests.swift

297 lines
7.6 KiB
Swift

//
// RedundantAsyncTests.swift
// SwiftFormatTests
//
// Created by Cal Stephens on 2025-09-18.
// Copyright © 2024 Nick Lockwood. All rights reserved.
//
import XCTest
@testable import SwiftFormat
final class RedundantAsyncTests: XCTestCase {
func testRemovesAsyncFromXCTestFunction() {
let input = """
import XCTest
class TestCase: XCTestCase {
func test_something() async {
XCTAssertEqual(1, 1)
}
}
"""
let output = """
import XCTest
class TestCase: XCTestCase {
func test_something() {
XCTAssertEqual(1, 1)
}
}
"""
testFormatting(for: input, output, rule: .redundantAsync)
}
func testRemovesAsyncFromSwiftTestingFunction() {
let input = """
import Testing
@Test func something() async {
#expect(1 == 1)
}
"""
let output = """
import Testing
@Test func something() {
#expect(1 == 1)
}
"""
testFormatting(for: input, output, rule: .redundantAsync)
}
func testIgnoresNonTestFunctionsInTestsOnlyMode() {
let input = """
import XCTest
class TestCase: XCTestCase {
func helper() async {
// This is not a test function, should not be modified
}
func testHelper() async -> Bool {
// This is not a test function, should not be modified
false
}
func test_something() async {
XCTAssertEqual(1, 1)
}
}
"""
let output = """
import XCTest
class TestCase: XCTestCase {
func helper() async {
// This is not a test function, should not be modified
}
func testHelper() async -> Bool {
// This is not a test function, should not be modified
false
}
func test_something() {
XCTAssertEqual(1, 1)
}
}
"""
testFormatting(for: input, output, rule: .redundantAsync, exclude: [.testSuiteAccessControl])
}
func testRemovesAsyncFromAnyFunctionInAlwaysMode() {
let input = """
func foo() async -> Int {
return 0
}
init() async -> Int {
return 0
}
subscript(_: String) async -> Int {
return 0
}
"""
let output = """
func foo() -> Int {
return 0
}
init() -> Int {
return 0
}
subscript(_: String) -> Int {
return 0
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, output, rule: .redundantAsync, options: options)
}
func testDoesNotModifyOverrideFunctions() {
let input = """
class TestCase {
override func setUpWithError() async {
// Setup code that doesn't actually await
}
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, rule: .redundantAsync, options: options)
}
func testPreservesAsyncWhenFunctionContainsAwait() {
let input = """
func baz() async -> Int {
await somethingAsync()
return 0
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, rule: .redundantAsync, options: options)
}
func testPreservesAsyncWhenFunctionContainsAwaitInControlFlow() {
let input = """
func foo() async -> Int {
if someCondition {
await somethingAsync()
}
return 0
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, rule: .redundantAsync, options: options)
}
func testRemovesAsyncWhenAwaitInNestedClosure() {
let input = """
func foo() async -> Int {
let closure = {
await somethingAsync()
}
return 0
}
"""
let output = """
func foo() -> Int {
let closure = {
await somethingAsync()
}
return 0
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, output, rule: .redundantAsync, options: options)
}
func testPreservesAsyncWithMultipleAwaitCalls() {
let input = """
func foo() async -> Int {
await firstCall()
await secondCall()
return 0
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, rule: .redundantAsync, options: options)
}
func testRemovesAsyncFromAsyncThrowsFunction() {
let input = """
func foo() async throws -> Int {
throw MyError.someError
}
"""
let output = """
func foo() throws -> Int {
throw MyError.someError
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, output, rule: .redundantAsync, options: options)
}
func testPreservesAsyncInAsyncThrowsWithAwait() {
let input = """
func foo() async throws -> Int {
await someAsyncCall()
throw MyError.someError
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, rule: .redundantAsync, options: options)
}
func testRemovesAsyncFromInitializer() {
let input = """
struct MyStruct {
init() async {
// No await calls
}
}
"""
let output = """
struct MyStruct {
init() {
// No await calls
}
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, output, rule: .redundantAsync, options: options)
}
func testRemovesAsyncFromSubscript() {
let input = """
struct MyStruct {
subscript(key: String) async -> String {
key
}
}
"""
let output = """
struct MyStruct {
subscript(key: String) -> String {
key
}
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, output, rule: .redundantAsync, options: options)
}
func testPreservesAsyncInNestedFunction() {
let input = """
func outerFunction() async {
func innerFunction() async {
await someAsyncCall()
}
// No await in outer function body
}
"""
let output = """
func outerFunction() {
func innerFunction() async {
await someAsyncCall()
}
// No await in outer function body
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, output, rule: .redundantAsync, options: options)
}
func testIssue2217_asyncNotRemovedForAwaitInStringInterpolation() {
let input = """
var x: String {
get async {
"y"
}
}
func y() async {
"\\(await x)"
}
"""
let options = FormatOptions(redundantAsync: .always)
testFormatting(for: input, rule: .redundantAsync, options: options)
}
}