Files
SwiftFormat/Tests/Rules/SortImportsTests.swift

646 lines
16 KiB
Swift

//
// SortImportsTests.swift
// SwiftFormatTests
//
// Created by Nick Lockwood on 8/13/23.
// Copyright © 2024 Nick Lockwood. All rights reserved.
//
import XCTest
@testable import SwiftFormat
final class SortImportsTests: XCTestCase {
func testSortImportsSimpleCase() {
let input = """
import Foo
import Bar
"""
let output = """
import Bar
import Foo
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testDefaultGroupingBehaviorIsAccessControlThenAlpha() {
let input = """
@testable import Foo
import Zed
public import Alpha
import Bar
public import Beta
@testable import Ace
"""
let output = """
public import Alpha
public import Beta
@testable import Ace
import Bar
@testable import Foo
import Zed
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testSortImportsKeepsPreviousCommentWithImport() {
let input = """
import Foo
// important comment
// (very important)
import Bar
"""
let output = """
// important comment
// (very important)
import Bar
import Foo
"""
testFormatting(for: input, output, rule: .sortImports,
exclude: [.blankLineAfterImports])
}
func testSortImportsKeepsPreviousCommentWithImport2() {
let input = """
// important comment
// (very important)
import Foo
import Bar
"""
let output = """
import Bar
// important comment
// (very important)
import Foo
"""
testFormatting(for: input, output, rule: .sortImports,
exclude: [.blankLineAfterImports])
}
func testSortImportsDoesntMoveHeaderComment() {
let input = """
// header comment
import Foo
import Bar
"""
let output = """
// header comment
import Bar
import Foo
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testSortImportsDoesntMoveHeaderCommentFollowedByImportComment() {
let input = """
// header comment
// important comment
import Foo
import Bar
"""
let output = """
// header comment
import Bar
// important comment
import Foo
"""
testFormatting(for: input, output, rule: .sortImports,
exclude: [.blankLineAfterImports])
}
func testSortImportsOnSameLine() {
let input = """
import Foo; import Bar
import Baz
"""
let output = """
import Baz
import Foo; import Bar
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testSortImportsWithSemicolonAndCommentOnSameLine() {
let input = """
import Foo; // foobar
import Bar
import Baz
"""
let output = """
import Bar
import Baz
import Foo; // foobar
"""
testFormatting(for: input, output, rule: .sortImports, exclude: [.semicolons])
}
func testSortImportEnum() {
let input = """
import enum Foo.baz
import Foo.bar
"""
let output = """
import Foo.bar
import enum Foo.baz
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testSortImportFunc() {
let input = """
import func Foo.baz
import Foo.bar
"""
let output = """
import Foo.bar
import func Foo.baz
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testAlreadySortImportsDoesNothing() {
let input = """
import Bar
import Foo
"""
testFormatting(for: input, rule: .sortImports)
}
func testPreprocessorSortImports() {
let input = """
#if os(iOS)
import Foo2
import Bar2
#else
import Foo1
import Bar1
#endif
import Foo3
import Bar3
"""
let output = """
#if os(iOS)
import Bar2
import Foo2
#else
import Bar1
import Foo1
#endif
import Bar3
import Foo3
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testTestableSortImports() {
let input = """
@testable import Foo3
import Bar3
"""
let output = """
import Bar3
@testable import Foo3
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testLengthSortImports() {
let input = """
import Foo
import Module
import Bar3
"""
let output = """
import Foo
import Bar3
import Module
"""
let options = FormatOptions(importGrouping: [.length])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testTestableImportsWithTestableOnPreviousLine() {
let input = """
@testable
import Foo3
import Bar3
"""
let output = """
import Bar3
@testable
import Foo3
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testTestableImportsWithGroupingTestableBottom() {
let input = """
@testable import Bar
import Foo
@testable import UIKit
"""
let output = """
import Foo
@testable import Bar
@testable import UIKit
"""
let options = FormatOptions(importGrouping: [.alpha, .testableLast])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testTestableImportsWithGroupingTestableTop() {
let input = """
@testable import Bar
import Foo
@testable import UIKit
"""
let output = """
@testable import Bar
@testable import UIKit
import Foo
"""
let options = FormatOptions(importGrouping: [.alpha, .testableFirst])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testCaseInsensitiveSortImports() {
let input = """
import Zlib
import lib
"""
let output = """
import lib
import Zlib
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testCaseInsensitiveCaseDifferingSortImports() {
let input = """
import c
import B
import A.a
import A.A
"""
let output = """
import A.A
import A.a
import B
import c
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testNoDeleteCodeBetweenImports() {
let input = """
import Foo
func bar() {}
import Bar
"""
testFormatting(for: input, rule: .sortImports,
exclude: [.blankLineAfterImports])
}
func testNoDeleteCodeBetweenImports2() {
let input = """
import Foo
import Bar
foo = bar
import Bar
"""
let output = """
import Bar
import Foo
foo = bar
import Bar
"""
testFormatting(for: input, output, rule: .sortImports,
exclude: [.blankLineAfterImports])
}
func testNoDeleteCodeBetweenImports3() {
let input = """
import Z
// one
#if FLAG
print("hi")
#endif
import A
"""
testFormatting(for: input, rule: .sortImports)
}
func testSortContiguousImports() {
let input = """
import Foo
import Bar
func bar() {}
import Quux
import Baz
"""
let output = """
import Bar
import Foo
func bar() {}
import Baz
import Quux
"""
testFormatting(for: input, output, rule: .sortImports,
exclude: [.blankLineAfterImports])
}
func testNoMangleImportsPrecededByComment() {
let input = """
// evil comment
#if canImport(Foundation)
import Foundation
#if canImport(UIKit) && canImport(AVFoundation)
import UIKit
import AVFoundation
#endif
#endif
"""
let output = """
// evil comment
#if canImport(Foundation)
import Foundation
#if canImport(UIKit) && canImport(AVFoundation)
import AVFoundation
import UIKit
#endif
#endif
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testNoMangleFileHeaderNotFollowedByLinebreak() {
let input = """
//
// Code.swift
// Module
//
// Created by Someone on 4/30/20.
//
import AModuleUI
import AModule
import AModuleHelper
import SomeOtherModule
"""
let output = """
//
// Code.swift
// Module
//
// Created by Someone on 4/30/20.
//
import AModule
import AModuleHelper
import AModuleUI
import SomeOtherModule
"""
testFormatting(for: input, output, rule: .sortImports)
}
func testNoMangleConditionalImportsFollowedByPrivateDeclaration() {
let input = """
#if canImport(UIKit)
import UIKit
private struct Foo {}
#elseif canImport(AppKit)
import AppKit
private struct Foo {}
#endif
"""
let options = FormatOptions(swiftVersion: "6.1")
testFormatting(for: input, rule: .sortImports, options: options, exclude: [.blankLineAfterImports, .indent])
}
func testNoMoveSwiftToolsVersionLine() {
let input = """
// swift-tools-version: 6.2
import PackageDescription
import CompilerPluginSupport
"""
let output = """
// swift-tools-version: 6.2
import CompilerPluginSupport
import PackageDescription
"""
testFormatting(for: input, output, rule: .sortImports)
}
// MARK: - Access control sorting
func testAccessControlSortImports() {
let input = """
import Foo
private import Bar
public import Baz
"""
let output = """
public import Baz
private import Bar
import Foo
"""
let options = FormatOptions(importGrouping: [.alpha, .accessControl])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testAccessControlSortAlphaWithinLevel() {
let input = """
public import Zebra
public import Alpha
public import Middle
"""
let output = """
public import Alpha
public import Middle
public import Zebra
"""
let options = FormatOptions(importGrouping: [.alpha, .accessControl])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testAccessControlSortLengthWithinLevel() {
let input = """
public import Zebra
public import Al
public import Middle
"""
let output = """
public import Al
public import Zebra
public import Middle
"""
let options = FormatOptions(importGrouping: [.length, .accessControl])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testAccessControlSortLengthWithMultipleACLs() {
let input = """
private import LongPrivate
public import Baz
private import Al
public import LongPublic
import Foo
"""
let output = """
public import Baz
public import LongPublic
private import Al
private import LongPrivate
import Foo
"""
let options = FormatOptions(importGrouping: [.length, .accessControl])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testAccessControlWithTestableFirst() {
let input = """
import Foo
@testable import Bar
public import Baz
"""
let output = """
@testable import Bar
public import Baz
import Foo
"""
let options = FormatOptions(importGrouping: [.alpha, .accessControl, .testableFirst])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testAccessControlWithTestableLast() {
let input = """
public import Baz
@testable import Bar
import Foo
"""
let output = """
public import Baz
import Foo
@testable import Bar
"""
let options = FormatOptions(importGrouping: [.alpha, .accessControl, .testableLast])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testUnlabeledImportsSortLast() {
let input = """
import Foo
public import Bar
internal import Baz
import Qux
"""
let output = """
public import Bar
internal import Baz
import Foo
import Qux
"""
let options = FormatOptions(importGrouping: [.alpha, .accessControl])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testTestableImportsSortedByACLAndAlpha() {
let input = """
@testable import DModule
@testable public import CModule
@testable import AModule
@testable public import BModule
import ZModule
public import UModule
import YModule
public import TModule
"""
let output = """
public import TModule
public import UModule
import YModule
import ZModule
@testable public import BModule
@testable public import CModule
@testable import AModule
@testable import DModule
"""
let options = FormatOptions(importGrouping: [.alpha, .accessControl, .testableLast])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
// MARK: - Length + testable combinations
func testLengthSortWithTestableTop() {
let input = """
import Foo
@testable import LongModule
import Ba
@testable import Az
"""
let output = """
@testable import Az
@testable import LongModule
import Ba
import Foo
"""
let options = FormatOptions(importGrouping: [.length, .testableFirst])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testLengthSortWithTestableBottom() {
let input = """
@testable import LongModule
import Foo
import Ba
@testable import Az
"""
let output = """
import Ba
import Foo
@testable import Az
@testable import LongModule
"""
let options = FormatOptions(importGrouping: [.length, .testableLast])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testLengthThenAlphaSortImports() {
let input = """
import Module
import Foo
import Bar
import Ab
"""
let output = """
import Ab
import Bar
import Foo
import Module
"""
let options = FormatOptions(importGrouping: [.length, .alpha])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
func testLengthThenAlphaSortImportsWithSameLength() {
let input = """
import Zed
import Foo
import Bar
"""
let output = """
import Bar
import Foo
import Zed
"""
let options = FormatOptions(importGrouping: [.length, .alpha])
testFormatting(for: input, output, rule: .sortImports, options: options)
}
}