mirror of
https://github.com/realm/SwiftLint.git
synced 2026-05-07 20:12:49 +00:00
658 lines
27 KiB
Swift
658 lines
27 KiB
Swift
import Foundation
|
|
import SourceKittenFramework
|
|
import TestHelpers
|
|
import XCTest
|
|
|
|
@testable import SwiftLintFramework
|
|
|
|
// swiftlint:disable file_length
|
|
|
|
private let optInRules = RuleRegistry.shared.list.list.filter({ $0.1.init() is any OptInRule }).map(\.0)
|
|
|
|
final class ConfigurationTests: SwiftLintTestCase {
|
|
// MARK: Setup & Teardown
|
|
private var previousWorkingDir: String! // swiftlint:disable:this implicitly_unwrapped_optional
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
Configuration.resetCache()
|
|
previousWorkingDir = FileManager.default.currentDirectoryPath
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
}
|
|
|
|
override func tearDown() {
|
|
super.tearDown()
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(previousWorkingDir))
|
|
}
|
|
|
|
// MARK: Tests
|
|
func testInit() {
|
|
XCTAssertNotNil(
|
|
try? Configuration(dict: [:]),
|
|
"initializing Configuration with empty Dictionary should succeed"
|
|
)
|
|
XCTAssertNotNil(
|
|
try? Configuration(dict: ["a": 1, "b": 2]),
|
|
"initializing Configuration with valid Dictionary should succeed"
|
|
)
|
|
}
|
|
|
|
func testNoConfiguration() {
|
|
// Change to a folder where there is no `.swiftlint.yml`
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.emptyFolder.filepath))
|
|
|
|
// Test whether the default configuration is used if there is no `.swiftlint.yml` or other config file
|
|
XCTAssertEqual(Configuration(configurationFiles: []), Configuration.default)
|
|
}
|
|
|
|
func testEmptyConfiguration() {
|
|
guard let config = try? Configuration(dict: [:]) else {
|
|
XCTFail("empty YAML string should yield non-nil Configuration")
|
|
return
|
|
}
|
|
XCTAssertEqual(config.rulesWrapper.disabledRuleIdentifiers, [])
|
|
XCTAssertEqual(config.includedPaths, [])
|
|
XCTAssertEqual(config.excludedPaths, [])
|
|
XCTAssertEqual(config.indentation, .spaces(count: 4))
|
|
XCTAssertEqual(config.reporter, "xcode")
|
|
XCTAssertEqual(reporterFrom(identifier: config.reporter).identifier, "xcode")
|
|
XCTAssertFalse(config.allowZeroLintableFiles)
|
|
XCTAssertFalse(config.strict)
|
|
XCTAssertFalse(config.lenient)
|
|
XCTAssertNil(config.baseline)
|
|
XCTAssertNil(config.writeBaseline)
|
|
XCTAssertFalse(config.checkForUpdates)
|
|
}
|
|
|
|
func testInitWithRelativePathAndRootPath() {
|
|
let expectedConfig = Mock.Config._0
|
|
|
|
let config = Configuration(configurationFiles: [".swiftlint.yml".url])
|
|
|
|
XCTAssertEqual(config.rulesWrapper.disabledRuleIdentifiers, expectedConfig.rulesWrapper.disabledRuleIdentifiers)
|
|
XCTAssertEqual(config.includedPaths, expectedConfig.includedPaths)
|
|
XCTAssertEqual(config.excludedPaths, expectedConfig.excludedPaths)
|
|
XCTAssertEqual(config.indentation, expectedConfig.indentation)
|
|
XCTAssertEqual(config.reporter, expectedConfig.reporter)
|
|
XCTAssertTrue(config.allowZeroLintableFiles)
|
|
XCTAssertTrue(config.strict)
|
|
XCTAssertEqual(config.baseline, expectedConfig.baseline)
|
|
XCTAssertEqual(config.writeBaseline, expectedConfig.writeBaseline)
|
|
}
|
|
|
|
func testEnableAllRulesConfiguration() throws {
|
|
let configuration = try Configuration(
|
|
dict: [:],
|
|
enableAllRules: true,
|
|
cachePath: nil
|
|
)
|
|
|
|
XCTAssertEqual(configuration.rules.count, RuleRegistry.shared.list.list.count)
|
|
}
|
|
|
|
func testOnlyRule() throws {
|
|
let configuration = try Configuration(
|
|
dict: [:],
|
|
onlyRule: ["nesting"],
|
|
cachePath: nil
|
|
)
|
|
|
|
XCTAssertEqual(configuration.rules.count, 1)
|
|
}
|
|
|
|
func testOnlyRuleMultiple() throws {
|
|
let onlyRuleIdentifiers = ["nesting", "todo"].sorted()
|
|
let configuration = try Configuration(
|
|
dict: ["only_rules": "line_length"],
|
|
onlyRule: onlyRuleIdentifiers,
|
|
cachePath: nil
|
|
)
|
|
XCTAssertEqual(onlyRuleIdentifiers, configuration.enabledRuleIdentifiers)
|
|
|
|
let childConfiguration = try Configuration(dict: ["disabled_rules": onlyRuleIdentifiers.last ?? ""])
|
|
let mergedConfiguration = configuration.merged(withChild: childConfiguration)
|
|
XCTAssertEqual(onlyRuleIdentifiers.dropLast(), mergedConfiguration.enabledRuleIdentifiers)
|
|
}
|
|
|
|
func testOnlyRules() throws {
|
|
let only = ["nesting", "todo"]
|
|
|
|
let config = try Configuration(dict: ["only_rules": only])
|
|
let configuredIdentifiers = config.rules.map {
|
|
type(of: $0).identifier
|
|
}.sorted()
|
|
XCTAssertEqual(only, configuredIdentifiers)
|
|
}
|
|
|
|
func testOnlyRulesWithCustomRules() throws {
|
|
// All custom rules from a config file should be active if the `custom_rules` is included in the `only_rules`
|
|
// As the behavior is different for custom rules from parent configs, this test is helpful
|
|
let only = ["custom_rules"]
|
|
let customRuleIdentifier = "my_custom_rule"
|
|
let customRules = [customRuleIdentifier: ["name": "A name for this custom rule", "regex": "this is illegal"]]
|
|
|
|
let config = try Configuration(dict: ["only_rules": only, "custom_rules": customRules])
|
|
guard let resultingCustomRules = config.rules.customRules else {
|
|
XCTFail("Custom rules are expected to be present")
|
|
return
|
|
}
|
|
XCTAssertTrue(
|
|
resultingCustomRules.configuration.customRuleConfigurations.contains {
|
|
$0.identifier == customRuleIdentifier
|
|
}
|
|
)
|
|
}
|
|
|
|
func testOnlyRulesWithSpecificCustomRules() throws {
|
|
// Individual custom rules can be specified on the command line without specifying `custom_rules` as well.
|
|
let customRuleIdentifier = "my_custom_rule"
|
|
let customRuleIdentifier2 = "my_custom_rule2"
|
|
let only = ["custom_rules"]
|
|
let customRules = [
|
|
customRuleIdentifier: ["name": "A custom rule", "regex": "this is illegal"],
|
|
customRuleIdentifier2: ["name": "Another custom rule", "regex": "this is also illegal"],
|
|
]
|
|
|
|
let configuration = try Configuration(
|
|
dict: [
|
|
"only_rules": only,
|
|
"custom_rules": customRules,
|
|
],
|
|
onlyRule: [customRuleIdentifier]
|
|
)
|
|
let resultingCustomRules = configuration.rules.customRules
|
|
XCTAssertNotNil(resultingCustomRules)
|
|
|
|
let enabledCustomRuleIdentifiers =
|
|
resultingCustomRules?.configuration.customRuleConfigurations.map(\.identifier)
|
|
XCTAssertEqual(enabledCustomRuleIdentifiers, [customRuleIdentifier])
|
|
}
|
|
|
|
func testWarningThreshold_value() throws {
|
|
let config = try Configuration(dict: ["warning_threshold": 5])
|
|
XCTAssertEqual(config.warningThreshold, 5)
|
|
}
|
|
|
|
func testWarningThreshold_nil() throws {
|
|
let config = try Configuration(dict: [:])
|
|
XCTAssertNil(config.warningThreshold)
|
|
}
|
|
|
|
func testOtherRuleConfigurationsAlongsideOnlyRules() {
|
|
let only = ["nesting", "todo"]
|
|
let enabledRulesConfigDict = [
|
|
"opt_in_rules": ["line_length"],
|
|
"only_rules": only,
|
|
]
|
|
let disabledRulesConfigDict = [
|
|
"disabled_rules": ["identifier_name"],
|
|
"only_rules": only,
|
|
]
|
|
let combinedRulesConfigDict = enabledRulesConfigDict.reduce(into: disabledRulesConfigDict) { $0[$1.0] = $1.1 }
|
|
var configuration = try? Configuration(dict: enabledRulesConfigDict)
|
|
XCTAssertNil(configuration)
|
|
configuration = try? Configuration(dict: disabledRulesConfigDict)
|
|
XCTAssertNil(configuration)
|
|
configuration = try? Configuration(dict: combinedRulesConfigDict)
|
|
XCTAssertNil(configuration)
|
|
}
|
|
|
|
func testDisabledRules() throws {
|
|
let disabledConfig = try Configuration(dict: ["disabled_rules": ["nesting", "todo"]])
|
|
XCTAssertEqual(disabledConfig.rulesWrapper.disabledRuleIdentifiers,
|
|
["nesting", "todo"],
|
|
"initializing Configuration with valid rules in Dictionary should succeed")
|
|
let expectedIdentifiers = Set(RuleRegistry.shared.list.list.keys
|
|
.filter({ !(["nesting", "todo"] + optInRules).contains($0) }))
|
|
let configuredIdentifiers = Set(disabledConfig.rules.map {
|
|
type(of: $0).identifier
|
|
})
|
|
XCTAssertEqual(expectedIdentifiers, configuredIdentifiers)
|
|
}
|
|
|
|
func testDisabledRulesWithUnknownRule() throws {
|
|
let validRule = "nesting"
|
|
let bogusRule = "no_sprites_with_elf_shoes"
|
|
|
|
let configuration = try Configuration(dict: ["disabled_rules": [validRule, bogusRule]])
|
|
|
|
XCTAssertEqual(configuration.rulesWrapper.disabledRuleIdentifiers,
|
|
[validRule],
|
|
"initializing Configuration with valid rules in YAML string should succeed")
|
|
let expectedIdentifiers = Set(RuleRegistry.shared.list.list.keys
|
|
.filter({ !([validRule] + optInRules).contains($0) }))
|
|
XCTAssertEqual(expectedIdentifiers, Set(configuration.enabledRuleIdentifiers))
|
|
}
|
|
|
|
func testDuplicatedRules() {
|
|
let duplicateConfig1 = try? Configuration(dict: ["only_rules": ["todo", "todo"]])
|
|
XCTAssertEqual(
|
|
duplicateConfig1?.rules.count, 1, "duplicate rules should be removed when initializing Configuration"
|
|
)
|
|
|
|
let duplicateConfig2 = try? Configuration(dict: ["opt_in_rules": [optInRules.first!, optInRules.first!]])
|
|
XCTAssertEqual(
|
|
duplicateConfig2?.rules.filter { type(of: $0).identifier == optInRules.first! }.count, 1,
|
|
"duplicate rules should be removed when initializing Configuration"
|
|
)
|
|
|
|
let duplicateConfig3 = try? Configuration(dict: ["disabled_rules": ["todo", "todo"]])
|
|
XCTAssertEqual(
|
|
duplicateConfig3?.rulesWrapper.disabledRuleIdentifiers.count, 1,
|
|
"duplicate rules should be removed when initializing Configuration"
|
|
)
|
|
}
|
|
|
|
func testIncludedExcludedRelativeLocationLevel1() {
|
|
guard !isRunningWithBazel else {
|
|
return
|
|
}
|
|
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level1.filepath))
|
|
|
|
// The included path "File.swift" should be put relative to the configuration file
|
|
// (~> Resources/ProjectMock/File.swift) and not relative to the path where
|
|
// SwiftLint is run from (~> Resources/ProjectMock/Level1/File.swift)
|
|
let configuration = Configuration(configurationFiles: ["../custom_included_excluded.yml".url])
|
|
let actualIncludedPath = configuration.includedPaths.first!
|
|
let desiredIncludedPath = URL(filePath: "File1.swift", relativeTo: Mock.Dir.level0)
|
|
let actualExcludedPath = configuration.excludedPaths.first!
|
|
let desiredExcludedPath = URL(filePath: "File2.swift", relativeTo: Mock.Dir.level0)
|
|
|
|
XCTAssertEqual(actualIncludedPath, desiredIncludedPath)
|
|
XCTAssertEqual(actualExcludedPath, desiredExcludedPath)
|
|
}
|
|
|
|
func testIncludedExcludedRelativeLocationLevel0() {
|
|
// Same as testIncludedPathRelatedToConfigurationFileLocationLevel1(),
|
|
// but run from the directory the config file resides in
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(configurationFiles: ["custom_included_excluded.yml".url])
|
|
let actualIncludedPath = configuration.includedPaths.first!
|
|
let desiredIncludedPath = URL(filePath: "File1.swift", relativeTo: Mock.Dir.level0)
|
|
let actualExcludedPath = configuration.excludedPaths.first!
|
|
let desiredExcludedPath = URL(filePath: "File2.swift", relativeTo: Mock.Dir.level0)
|
|
|
|
XCTAssertEqual(actualIncludedPath.path, desiredIncludedPath.path)
|
|
XCTAssertEqual(actualExcludedPath.path, desiredExcludedPath.path)
|
|
}
|
|
|
|
func testExcludedPaths() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.exclusionTests.filepath))
|
|
let configuration = Configuration(
|
|
includedPaths: ["directory".url],
|
|
excludedPaths: ["directory/excluded".url, "directory/ExcludedFile.swift".url]
|
|
)
|
|
|
|
let paths = configuration.lintablePaths(
|
|
inPath: URL.currentDirectory(),
|
|
forceExclude: false,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
assertEqual(["directory/File1.swift", "directory/File2.swift"], paths)
|
|
}
|
|
|
|
func testForceExcludesFile() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.exclusionTests.filepath))
|
|
let configuration = Configuration(excludedPaths: ["directory/ExcludedFile.swift".url])
|
|
|
|
let paths = configuration.lintablePaths(
|
|
inPath: "directory/ExcludedFile.swift".url,
|
|
forceExclude: true,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
XCTAssert(paths.isEmpty)
|
|
}
|
|
|
|
func testForceExcludesFileNotPresentInExcluded() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.exclusionTests.filepath))
|
|
let configuration = Configuration(
|
|
includedPaths: ["directory".url],
|
|
excludedPaths: ["directory/ExcludedFile.swift".url, "directory/excluded".url]
|
|
)
|
|
|
|
let paths = configuration.lintablePaths(
|
|
inPath: URL.currentDirectory(),
|
|
forceExclude: true,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
assertEqual(["directory/File1.swift", "directory/File2.swift"], paths)
|
|
}
|
|
|
|
func testForceExcludesDirectory() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.exclusionTests.filepath))
|
|
let configuration = Configuration(excludedPaths: ["directory/excluded".url])
|
|
|
|
let paths = configuration.lintablePaths(
|
|
inPath: "directory".url,
|
|
forceExclude: true,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
assertEqual(["directory/File1.swift", "directory/File2.swift", "directory/ExcludedFile.swift"], paths)
|
|
}
|
|
|
|
func testForceExcludesDirectoryThatIsNotInExcludedButHasChildrenThatAre() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.exclusionTests.filepath))
|
|
let configuration = Configuration(excludedPaths: ["directory/ExcludedFile.swift".url])
|
|
|
|
let paths = configuration.lintablePaths(
|
|
inPath: "directory".url,
|
|
forceExclude: true,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
assertEqual(["directory/File1.swift", "directory/File2.swift", "directory/excluded/Excluded.swift"], paths)
|
|
}
|
|
|
|
func testLintablePaths() {
|
|
let paths = Configuration.default.lintablePaths(inPath: Mock.Dir.level0,
|
|
forceExclude: false,
|
|
excludeByPrefix: false)
|
|
let filenames = paths.map { $0.lastPathComponent }.sorted()
|
|
let expectedFilenames = [
|
|
"DirectoryLevel1.swift",
|
|
"Level0.swift", "Level1.swift", "Level2.swift", "Level3.swift",
|
|
"Main.swift", "Sub.swift",
|
|
]
|
|
|
|
XCTAssertEqual(Set(expectedFilenames), Set(filenames))
|
|
}
|
|
|
|
func testGlobIncludePaths() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(includedPaths: ["**/Level2".url])
|
|
let paths = configuration.lintablePaths(inPath: Mock.Dir.level0,
|
|
forceExclude: true,
|
|
excludeByPrefix: false)
|
|
let filenames = paths.map { $0.lastPathComponent }.sorted()
|
|
let expectedFilenames = ["Level2.swift", "Level3.swift"]
|
|
|
|
XCTAssertEqual(Set(expectedFilenames), Set(filenames))
|
|
}
|
|
|
|
func testDuplicatedGlobIncludePaths() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(includedPaths: ["**/Level2".url, "**/Level2".url])
|
|
let paths = configuration.lintablePaths(inPath: Mock.Dir.level0,
|
|
forceExclude: true,
|
|
excludeByPrefix: false)
|
|
let filenames = paths.map { $0.lastPathComponent }.sorted()
|
|
let expectedFilenames = ["Level2.swift", "Level3.swift"]
|
|
|
|
XCTAssertEqual(expectedFilenames, filenames)
|
|
}
|
|
|
|
func testGlobExcludePaths() {
|
|
let configuration = Configuration(
|
|
includedPaths: [Mock.Dir.level3],
|
|
excludedPaths: [Mock.Dir.level3.appending(path: "*.swift")]
|
|
)
|
|
|
|
let lintablePaths = configuration.lintablePaths(inPath: URL.currentDirectory(),
|
|
forceExclude: false,
|
|
excludeByPrefix: false)
|
|
XCTAssertEqual(lintablePaths, [])
|
|
}
|
|
|
|
// MARK: - Testing Configuration Equality
|
|
|
|
func testIsEqualTo() {
|
|
XCTAssertEqual(Mock.Config._0, Mock.Config._0)
|
|
}
|
|
|
|
func testIsNotEqualTo() {
|
|
XCTAssertNotEqual(Mock.Config._0, Mock.Config._2)
|
|
}
|
|
|
|
// MARK: - Testing Custom Configuration File
|
|
|
|
func testCustomConfiguration() {
|
|
let file = SwiftLintFile(path: Mock.Swift._0)!
|
|
XCTAssertNotEqual(Mock.Config._0.configuration(for: file),
|
|
Mock.Config._0Custom.configuration(for: file))
|
|
}
|
|
|
|
func testConfigurationWithSwiftFileAsRoot() {
|
|
let configuration = Configuration(configurationFiles: [Mock.Yml._0])
|
|
|
|
let file = SwiftLintFile(path: Mock.Swift._0)!
|
|
XCTAssertEqual(configuration.configuration(for: file), configuration)
|
|
}
|
|
|
|
func testConfigurationWithSwiftFileAsRootAndCustomConfiguration() {
|
|
let configuration = Mock.Config._0Custom
|
|
|
|
let file = SwiftLintFile(path: Mock.Swift._0)!
|
|
XCTAssertEqual(configuration.configuration(for: file), configuration)
|
|
}
|
|
|
|
// MARK: - Testing custom indentation
|
|
|
|
func testIndentationTabs() throws {
|
|
let configuration = try Configuration(dict: ["indentation": "tabs"])
|
|
XCTAssertEqual(configuration.indentation, .tabs)
|
|
}
|
|
|
|
func testIndentationSpaces() throws {
|
|
let configuration = try Configuration(dict: ["indentation": 2])
|
|
XCTAssertEqual(configuration.indentation, .spaces(count: 2))
|
|
}
|
|
|
|
func testIndentationFallback() throws {
|
|
let configuration = try Configuration(dict: ["indentation": "invalid"])
|
|
XCTAssertEqual(configuration.indentation, .spaces(count: 4))
|
|
}
|
|
|
|
// MARK: - Testing Rules from config dictionary
|
|
|
|
private let testRuleList = RuleList(rules: RuleWithLevelsMock.self)
|
|
|
|
func testConfiguresCorrectlyFromDict() throws {
|
|
let ruleConfiguration = [1, 2]
|
|
let config = [RuleWithLevelsMock.identifier: ruleConfiguration]
|
|
let rules = try testRuleList.allRulesWrapped(configurationDict: config).map(\.rule)
|
|
// swiftlint:disable:next xct_specific_matcher
|
|
XCTAssertTrue(rules == [try RuleWithLevelsMock(configuration: ruleConfiguration)])
|
|
}
|
|
|
|
func testConfigureFallsBackCorrectly() throws {
|
|
let config = [RuleWithLevelsMock.identifier: ["a", "b"]]
|
|
let rules = try testRuleList.allRulesWrapped(configurationDict: config).map(\.rule)
|
|
// swiftlint:disable:next xct_specific_matcher
|
|
XCTAssertTrue(rules == [RuleWithLevelsMock()])
|
|
}
|
|
|
|
func testAllowZeroLintableFiles() throws {
|
|
let configuration = try Configuration(dict: ["allow_zero_lintable_files": true])
|
|
XCTAssertTrue(configuration.allowZeroLintableFiles)
|
|
}
|
|
|
|
func testStrict() throws {
|
|
let configuration = try Configuration(dict: ["strict": true])
|
|
XCTAssertTrue(configuration.strict)
|
|
}
|
|
|
|
func testLenient() throws {
|
|
let configuration = try Configuration(dict: ["lenient": true])
|
|
XCTAssertTrue(configuration.lenient)
|
|
}
|
|
|
|
func testBaseline() throws {
|
|
let baselinePath = "Baseline.json"
|
|
let configuration = try Configuration(dict: ["baseline": baselinePath])
|
|
XCTAssertEqual(configuration.baseline, baselinePath.url)
|
|
}
|
|
|
|
func testWriteBaseline() throws {
|
|
let baselinePath = "Baseline.json"
|
|
let configuration = try Configuration(dict: ["write_baseline": baselinePath])
|
|
XCTAssertEqual(configuration.writeBaseline, baselinePath.url)
|
|
}
|
|
|
|
func testCheckForUpdates() throws {
|
|
let configuration = try Configuration(dict: ["check_for_updates": true])
|
|
XCTAssertTrue(configuration.checkForUpdates)
|
|
}
|
|
|
|
// MARK: - ExcludeByPrefix option tests
|
|
|
|
func testExcludeByPrefixExcludedPaths() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(
|
|
includedPaths: ["Level1".url],
|
|
excludedPaths: ["Level1/Level1.swift".url, "Level1/Level2/Level3".url]
|
|
)
|
|
let paths = configuration.lintablePaths(inPath: Mock.Dir.level0,
|
|
forceExclude: false,
|
|
excludeByPrefix: true)
|
|
let filenames = paths.map { $0.lastPathComponent }
|
|
XCTAssertEqual(filenames, ["Level2.swift"])
|
|
}
|
|
|
|
func testExcludeByPrefixForceExcludesFile() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(excludedPaths: ["Level1/Level2/Level3/Level3.swift".url])
|
|
let paths = configuration.lintablePaths(inPath: "Level1/Level2/Level3/Level3.swift".url,
|
|
forceExclude: true,
|
|
excludeByPrefix: true)
|
|
XCTAssertEqual([], paths)
|
|
}
|
|
|
|
func testExcludeByPrefixForceExcludesFileNotPresentInExcluded() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(includedPaths: ["Level1".url],
|
|
excludedPaths: ["Level1/Level1.swift".url])
|
|
let paths = configuration.lintablePaths(inPath: "Level1".url,
|
|
forceExclude: true,
|
|
excludeByPrefix: true)
|
|
let filenames = paths.map { $0.lastPathComponent }.sorted()
|
|
XCTAssertEqual(["Level2.swift", "Level3.swift"], filenames)
|
|
}
|
|
|
|
func testExcludeByPrefixForceExcludesDirectory() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(
|
|
excludedPaths: [
|
|
"Level1/Level2".url,
|
|
"Directory.swift".url,
|
|
"ChildConfig".url,
|
|
"ParentConfig".url,
|
|
"NestedConfig".url,
|
|
]
|
|
)
|
|
let paths = configuration.lintablePaths(inPath: URL.currentDirectory(),
|
|
forceExclude: true,
|
|
excludeByPrefix: true)
|
|
let filenames = paths.map { $0.lastPathComponent }.sorted()
|
|
XCTAssertEqual(["Level0.swift", "Level1.swift"], filenames)
|
|
}
|
|
|
|
func testExcludeByPrefixForceExcludesDirectoryThatIsNotInExcludedButHasChildrenThatAre() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(
|
|
excludedPaths: [
|
|
"Level1".url,
|
|
"Directory.swift/DirectoryLevel1.swift".url,
|
|
"ChildConfig".url,
|
|
"ParentConfig".url,
|
|
"NestedConfig".url,
|
|
]
|
|
)
|
|
let paths = configuration.lintablePaths(inPath: URL.currentDirectory(),
|
|
forceExclude: true,
|
|
excludeByPrefix: true)
|
|
let filenames = paths.map { $0.lastPathComponent }
|
|
XCTAssertEqual(["Level0.swift"], filenames)
|
|
}
|
|
|
|
func testExcludeByPrefixGlobExcludePaths() {
|
|
XCTAssert(FileManager.default.changeCurrentDirectoryPath(Mock.Dir.level0.filepath))
|
|
let configuration = Configuration(
|
|
includedPaths: ["Level1".url],
|
|
excludedPaths: ["Level1/*/*.swift".url, "Level1/*/*/*.swift".url]
|
|
)
|
|
let paths = configuration.lintablePaths(inPath: "Level1".url,
|
|
forceExclude: false,
|
|
excludeByPrefix: true)
|
|
let filenames = paths.map { $0.lastPathComponent }.sorted()
|
|
XCTAssertEqual(filenames, ["Level1.swift"])
|
|
}
|
|
|
|
func testDictInitWithCachePath() throws {
|
|
let configuration = try Configuration(
|
|
dict: ["cache_path": "cache/path/1"]
|
|
)
|
|
|
|
XCTAssertEqual(configuration.cachePath, "cache/path/1")
|
|
}
|
|
|
|
func testDictInitWithCachePathFromCommandLine() throws {
|
|
let configuration = try Configuration(
|
|
dict: ["cache_path": "cache/path/1"],
|
|
cachePath: "cache/path/2"
|
|
)
|
|
|
|
XCTAssertEqual(configuration.cachePath, "cache/path/2")
|
|
}
|
|
|
|
func testMainInitWithCachePath() {
|
|
let configuration = Configuration(
|
|
configurationFiles: [],
|
|
cachePath: "cache/path/1"
|
|
)
|
|
|
|
XCTAssertEqual(configuration.cachePath, "cache/path/1")
|
|
}
|
|
|
|
// This test demonstrates an existing bug: when the Configuration is obtained from the in-memory cache, the
|
|
// cachePath is not taken into account
|
|
//
|
|
// This issue may not be reproducible under normal execution: the cache is in memory, so when a user changes
|
|
// the cachePath from command line and re-runs swiftlint, cache is not reused leading to the correct behavior
|
|
func testMainInitWithCachePathAndCachedConfig() {
|
|
let configuration1 = Configuration(
|
|
configurationFiles: [],
|
|
cachePath: "cache/path/1"
|
|
)
|
|
|
|
let configuration2 = Configuration(
|
|
configurationFiles: [],
|
|
cachePath: "cache/path/2"
|
|
)
|
|
|
|
XCTAssertEqual(configuration1.cachePath, "cache/path/1")
|
|
XCTAssertEqual(configuration2.cachePath, "cache/path/1")
|
|
}
|
|
|
|
private func assertEqual(_ relativeExpectedPaths: [String],
|
|
_ actualPaths: [URL],
|
|
file: StaticString = #filePath,
|
|
line: UInt = #line) {
|
|
XCTAssertEqual(
|
|
relativeExpectedPaths.absolutePathsStandardized().sorted(),
|
|
actualPaths.map(\.path).absolutePathsStandardized().sorted(),
|
|
file: file,
|
|
line: line
|
|
)
|
|
}
|
|
}
|
|
|
|
private extension Sequence where Element == String {
|
|
func absolutePathsStandardized() -> [String] {
|
|
// In Bazel builds, absolute paths might be prefixed with `/private`.
|
|
map { String($0.absolutePathStandardized().trimmingPrefix("/private")) }
|
|
}
|
|
}
|
|
|
|
private extension Configuration {
|
|
var enabledRuleIdentifiers: [String] {
|
|
rules.map {
|
|
type(of: $0).identifier
|
|
}.sorted()
|
|
}
|
|
}
|