mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
* Disable tests on macOS 14/Xcode 16.2 * Remove `--parallel` which is the default * Set reasonable test timeouts
232 lines
8.1 KiB
Swift
232 lines
8.1 KiB
Swift
import Foundation
|
|
import SourceKittenFramework
|
|
import SwiftLintFramework
|
|
import TestHelpers
|
|
import Testing
|
|
|
|
@testable import SwiftLintCore
|
|
|
|
@Suite(.rulesRegistered)
|
|
struct ConfigPathResolutionTests {
|
|
private func fixturePath(_ scenario: String) -> URL {
|
|
#filePath.url(directoryHint: .isDirectory)
|
|
.deletingLastPathComponent()
|
|
.appending(path: "Resources", directoryHint: .isDirectory)
|
|
.appending(path: scenario, directoryHint: .isDirectory)
|
|
}
|
|
|
|
/// Returns the paths of lintable files relative to the fixture directory.
|
|
private func lintableFilePaths(in scenario: String, configFile: String? = nil, inPath: String? = nil) -> [String] {
|
|
let scenarioPath = fixturePath(scenario)
|
|
return CurrentWorkingDirectory.$url.withValue(scenarioPath) {
|
|
let config = Configuration(configurationFiles: configFile.map { [$0.url()] } ?? [])
|
|
let files = config.lintableFiles(
|
|
inPath: inPath.map { $0.url() } ?? URL.cwd,
|
|
forceExclude: false,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
// swiftlint:disable:next force_try
|
|
return files.map { $0.path!.path.replacing(try! Regex(".+/\(scenario)/"), with: "") }.sorted()
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func parentChildSameDirectory() {
|
|
#expect(
|
|
lintableFilePaths(in: "_1_parent_child_same_dir", configFile: "parent.yml") == ["Sources/CoreFile.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func parentChildDifferentDirectories() {
|
|
#expect(
|
|
lintableFilePaths(
|
|
in: "_2_parent_child_different_dirs",
|
|
configFile: "project/.swiftlint.yml",
|
|
inPath: "project"
|
|
) == ["project/Sources/Core/Service.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func childOverridesParentExclusion() {
|
|
#expect(
|
|
lintableFilePaths(
|
|
in: "_3_child_overrides_parent_exclusion",
|
|
configFile: "project/.swiftlint.yml",
|
|
inPath: "project"
|
|
) == ["project/Vendor/Critical/Important.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func parentIncludesChildExcludes() {
|
|
#expect(
|
|
lintableFilePaths(in: "_1_parent_child_same_dir", configFile: "parent.yml") == ["Sources/CoreFile.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func nestedConfigurationBasic() {
|
|
#expect(
|
|
lintableFilePaths(in: "_4_nested_basic", configFile: ".swiftlint.yml")
|
|
== ["ModuleA/File.swift", "ModuleA/Generated/File.swift", "ModuleB/File.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func wildcardPatternCount() {
|
|
#expect(
|
|
lintableFilePaths(
|
|
in: "_5_wildcard_patterns",
|
|
configFile: "project/.swiftlint.yml",
|
|
inPath: "project"
|
|
) == ["project/Sources/Models/User.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func lintChildFolder() {
|
|
#expect(
|
|
lintableFilePaths(
|
|
in: "_2_parent_child_different_dirs",
|
|
configFile: "project/.swiftlint.yml",
|
|
inPath: "project"
|
|
) == ["project/Sources/Core/Service.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func emptyIncludedDefaultsToAll() {
|
|
#expect(
|
|
lintableFilePaths(
|
|
in: "_6_wildcards_from_nested_folder",
|
|
configFile: ".swiftlint-exclude-thirdparty.yml"
|
|
) == [
|
|
"Generated/API.swift",
|
|
"MyProject/Package.swift",
|
|
"MyProject/Sources/App.swift",
|
|
"MyProject/SubModule/Package.swift",
|
|
]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func multipleLevelsOfExclusion() {
|
|
#expect(
|
|
lintableFilePaths(in: "_1_parent_child_same_dir", configFile: "parent.yml") == ["Sources/CoreFile.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func configFromParentFolder() {
|
|
#expect(
|
|
lintableFilePaths(in: "_6_wildcards_from_nested_folder", configFile: ".swiftlint.yml")
|
|
== ["MyProject/Sources/App.swift"]
|
|
)
|
|
|
|
#expect(
|
|
lintableFilePaths(in: "_6_wildcards_from_nested_folder/MyProject", configFile: "../.swiftlint.yml")
|
|
== ["Sources/App.swift"]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func nestedConfigurationAppliesOnlyToSubdirectory() throws {
|
|
let scenarioPath = fixturePath("_4_nested_basic")
|
|
try CurrentWorkingDirectory.$url.withValue(scenarioPath) {
|
|
let config = Configuration(configurationFiles: [])
|
|
|
|
let moduleAFile = try #require(
|
|
SwiftLintFile(path: scenarioPath.appending(path: "ModuleA/File.swift"))
|
|
)
|
|
let moduleBFile = try #require(
|
|
SwiftLintFile(path: scenarioPath.appending(path: "ModuleB/File.swift"))
|
|
)
|
|
|
|
#expect(
|
|
config.configuration(for: moduleAFile).rules
|
|
.map { type(of: $0).identifier }
|
|
.contains("explicit_type_interface")
|
|
)
|
|
|
|
#expect(
|
|
!config.configuration(for: moduleBFile).rules
|
|
.map { type(of: $0).identifier }
|
|
.contains("explicit_type_interface")
|
|
)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func nestedConfigurationDisabledByConfigFlag() {
|
|
let scenarioPath = fixturePath("_4_nested_basic")
|
|
|
|
let moduleAFile = SwiftLintFile(
|
|
path: scenarioPath.appending(path: "ModuleB/File.swift")
|
|
)!
|
|
|
|
#expect(
|
|
!Configuration(configurationFiles: [scenarioPath.appending(path: "root.yml")])
|
|
.configuration(for: moduleAFile)
|
|
.rules
|
|
.map { type(of: $0).identifier }
|
|
.contains("explicit_type_interface")
|
|
)
|
|
}
|
|
|
|
#if !os(Windows)
|
|
@Test(.enabled(
|
|
if: ProcessInfo.processInfo.environment["SWIFTLINT_BAZEL_TEST"] == nil,
|
|
"Bazel's sandboxed environment uses symlinks heavily breaking the fixture setup"
|
|
))
|
|
func symlinkedFileAndFolderAreFollowed() throws {
|
|
let expectedPaths = ["Real/Folder/Nested.swift", "Real/Target.swift"]
|
|
|
|
// With symlinks
|
|
#expect(lintableFilePaths(in: "_9_symlinked_paths", configFile: ".swiftlint.yml") == expectedPaths)
|
|
|
|
let fixture = fixturePath("_9_symlinked_paths")
|
|
let fileLink = fixture.appending(path: "LinkToFile.swift", directoryHint: .notDirectory)
|
|
var folderLink = fixture.appending(path: "LinkToFolder", directoryHint: .isDirectory)
|
|
let targetFile = fixture.appending(path: "Real/Target.swift", directoryHint: .notDirectory)
|
|
let targetFolder = fixture.appending(path: "Real/Folder", directoryHint: .isDirectory)
|
|
|
|
let fileManager = FileManager.default
|
|
#expect(fileManager.fileExists(atPath: fileLink.filepath))
|
|
#expect(fileManager.fileExists(atPath: folderLink.filepath))
|
|
#expect(try fileLink.resourceValues(forKeys: [.isSymbolicLinkKey]).isSymbolicLink == true)
|
|
#expect(try folderLink.resourceValues(forKeys: [.isSymbolicLinkKey]).isSymbolicLink == true)
|
|
#expect(fileLink.resolvingSymlinksInPath() == targetFile)
|
|
|
|
#expect(folderLink != targetFile)
|
|
folderLink.resolveSymlinksInPath()
|
|
#expect(try folderLink.resourceValues(forKeys: [.isSymbolicLinkKey]).isSymbolicLink == false)
|
|
#expect(folderLink == targetFolder)
|
|
|
|
// Without symlinks
|
|
#expect(lintableFilePaths(in: "_9_symlinked_paths", configFile: ".swiftlint.yml") == expectedPaths)
|
|
}
|
|
#endif
|
|
|
|
#if !os(Windows)
|
|
@Test
|
|
func unicodePrivateUseAreaCharacterInPath() throws {
|
|
let fixture = fixturePath("_8_unicode_private_use_area")
|
|
|
|
let process = Process()
|
|
process.executableURL = URL(filePath: "/usr/bin/env", directoryHint: .notDirectory)
|
|
process.arguments = ["unzip", "-o", fixture.appending(path: "app.zip").filepath, "-d", fixture.filepath]
|
|
try process.run()
|
|
process.waitUntilExit()
|
|
defer { try? FileManager.default.removeItem(at: fixture.appending(path: "App")) }
|
|
|
|
#expect(
|
|
lintableFilePaths(in: "_8_unicode_private_use_area/App")
|
|
== ["Resources/Settings.bundle/androidx.core:core-bundle.swift"]
|
|
)
|
|
}
|
|
#endif
|
|
}
|