mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
656 lines
24 KiB
Swift
656 lines
24 KiB
Swift
import Foundation
|
|
import SourceKittenFramework
|
|
import TestHelpers
|
|
import Testing
|
|
|
|
@testable import SwiftLintFramework
|
|
|
|
// swiftlint:disable file_length
|
|
|
|
private let optInRules = RuleRegistry.shared.list.list.filter({ $0.1.init() is any OptInRule }).map(\.0)
|
|
|
|
@Suite(.rulesRegistered)
|
|
struct ConfigurationTests { // swiftlint:disable:this type_body_length
|
|
init() {
|
|
Configuration.resetCache()
|
|
}
|
|
|
|
@Test
|
|
func basicInit() throws {
|
|
_ = try Configuration(dict: [:])
|
|
_ = try Configuration(dict: ["a": 1, "b": 2])
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.emptyFolder))
|
|
func useDefaultIfNoConfiguration() {
|
|
#expect(Configuration(configurationFiles: []) == Configuration.default)
|
|
}
|
|
|
|
@Test
|
|
func emptyConfiguration() {
|
|
guard let config = try? Configuration(dict: [:]) else {
|
|
Issue.record("empty YAML string should yield non-nil Configuration")
|
|
return
|
|
}
|
|
#expect(config.rulesWrapper.disabledRuleIdentifiers.isEmpty)
|
|
#expect(config.includedPaths.isEmpty)
|
|
#expect(config.excludedPaths.isEmpty)
|
|
#expect(config.indentation == .spaces(count: 4))
|
|
#expect(config.reporter == "xcode")
|
|
#expect(reporterFrom(identifier: config.reporter).identifier == "xcode")
|
|
#expect(!config.allowZeroLintableFiles)
|
|
#expect(!config.strict)
|
|
#expect(!config.lenient)
|
|
#expect(config.baseline == nil)
|
|
#expect(config.writeBaseline == nil)
|
|
#expect(!config.checkForUpdates)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func initWithRelativePathAndRootPath() {
|
|
let expectedConfig = Constants.Config._0
|
|
|
|
let config = Configuration(configurationFiles: [".swiftlint.yml".url()])
|
|
|
|
#expect(config.rulesWrapper.disabledRuleIdentifiers == expectedConfig.rulesWrapper.disabledRuleIdentifiers)
|
|
#expect(config.includedPaths == expectedConfig.includedPaths)
|
|
#expect(config.excludedPaths == expectedConfig.excludedPaths)
|
|
#expect(config.indentation == expectedConfig.indentation)
|
|
#expect(config.reporter == expectedConfig.reporter)
|
|
#expect(config.allowZeroLintableFiles)
|
|
#expect(config.strict)
|
|
#expect(config.baseline != nil)
|
|
#expect(config.writeBaseline != nil)
|
|
}
|
|
|
|
@Test
|
|
func enableAllRulesConfiguration() throws {
|
|
let configuration = try Configuration(
|
|
dict: [:],
|
|
enableAllRules: true,
|
|
cachePath: nil
|
|
)
|
|
|
|
#expect(configuration.rules.count == RuleRegistry.shared.list.list.count)
|
|
}
|
|
|
|
@Test
|
|
func onlyRule() throws {
|
|
let configuration = try Configuration(
|
|
dict: [:],
|
|
onlyRule: ["nesting"],
|
|
cachePath: nil
|
|
)
|
|
|
|
#expect(configuration.rules.count == 1)
|
|
}
|
|
|
|
@Test
|
|
func onlyRuleMultiple() throws {
|
|
let onlyRuleIdentifiers = ["nesting", "todo"].sorted()
|
|
let configuration = try Configuration(
|
|
dict: ["only_rules": "line_length"],
|
|
onlyRule: onlyRuleIdentifiers,
|
|
cachePath: nil
|
|
)
|
|
#expect(onlyRuleIdentifiers == configuration.enabledRuleIdentifiers)
|
|
|
|
let childConfiguration = try Configuration(dict: ["disabled_rules": onlyRuleIdentifiers.last ?? ""])
|
|
let mergedConfiguration = configuration.merged(withChild: childConfiguration)
|
|
#expect(onlyRuleIdentifiers.dropLast() == mergedConfiguration.enabledRuleIdentifiers)
|
|
}
|
|
|
|
@Test
|
|
func onlyRules() throws {
|
|
let only = ["nesting", "todo"]
|
|
|
|
let config = try Configuration(dict: ["only_rules": only])
|
|
let configuredIdentifiers = config.rules.map {
|
|
type(of: $0).identifier
|
|
}.sorted()
|
|
#expect(only == configuredIdentifiers)
|
|
}
|
|
|
|
@Test
|
|
func onlyRulesWithCustomRules() 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 {
|
|
Issue.record("Custom rules are expected to be present")
|
|
return
|
|
}
|
|
#expect(
|
|
resultingCustomRules.configuration.customRuleConfigurations.contains {
|
|
$0.identifier == customRuleIdentifier
|
|
}
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func onlyRulesWithSpecificCustomRules() 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
|
|
#expect(resultingCustomRules != nil)
|
|
|
|
let enabledCustomRuleIdentifiers =
|
|
resultingCustomRules?.configuration.customRuleConfigurations.map(\.identifier)
|
|
#expect(enabledCustomRuleIdentifiers == [customRuleIdentifier])
|
|
}
|
|
|
|
@Test
|
|
func warningThreshold_value() throws {
|
|
let config = try Configuration(dict: ["warning_threshold": 5])
|
|
#expect(config.warningThreshold == 5)
|
|
}
|
|
|
|
@Test
|
|
func warningThreshold_nil() throws {
|
|
let config = try Configuration(dict: [:])
|
|
#expect(config.warningThreshold == nil)
|
|
}
|
|
|
|
@Test
|
|
func otherRuleConfigurationsAlongsideOnlyRules() {
|
|
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)
|
|
#expect(configuration == nil)
|
|
configuration = try? Configuration(dict: disabledRulesConfigDict)
|
|
#expect(configuration == nil)
|
|
configuration = try? Configuration(dict: combinedRulesConfigDict)
|
|
#expect(configuration == nil)
|
|
}
|
|
|
|
@Test
|
|
func disabledRules() throws {
|
|
let disabledConfig = try Configuration(dict: ["disabled_rules": ["nesting", "todo"]])
|
|
#expect(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
|
|
})
|
|
#expect(expectedIdentifiers == configuredIdentifiers)
|
|
}
|
|
|
|
@Test
|
|
func disabledRulesWithUnknownRule() throws {
|
|
let validRule = "nesting"
|
|
let bogusRule = "no_sprites_with_elf_shoes"
|
|
|
|
let configuration = try Configuration(dict: ["disabled_rules": [validRule, bogusRule]])
|
|
|
|
#expect(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) }))
|
|
#expect(expectedIdentifiers == Set(configuration.enabledRuleIdentifiers))
|
|
}
|
|
|
|
@Test
|
|
func duplicatedRules() {
|
|
let duplicateConfig1 = try? Configuration(dict: ["only_rules": ["todo", "todo"]])
|
|
#expect(
|
|
duplicateConfig1?.rules.count == 1, "duplicate rules should be removed when initializing Configuration"
|
|
)
|
|
|
|
let duplicateConfig2 = try? Configuration(dict: ["opt_in_rules": [optInRules.first!, optInRules.first!]])
|
|
#expect(
|
|
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"]])
|
|
#expect(
|
|
duplicateConfig3?.rulesWrapper.disabledRuleIdentifiers.count == 1,
|
|
"duplicate rules should be removed when initializing Configuration"
|
|
)
|
|
}
|
|
|
|
@Test(.disabled(if: isRunningWithBazel), .workingDirectory(Constants.Dir.level1))
|
|
func includedExcludedRelativeLocationLevel1() {
|
|
// 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 = "File1.swift".url(relativeTo: Constants.Dir.level0)
|
|
let actualExcludedPath = configuration.excludedPaths.first!
|
|
let desiredExcludedPath = "File2.swift".url(relativeTo: Constants.Dir.level0)
|
|
|
|
#expect(actualIncludedPath == desiredIncludedPath)
|
|
#expect(actualExcludedPath == desiredExcludedPath)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func includedExcludedRelativeLocationLevel0() {
|
|
// Same as testIncludedPathRelatedToConfigurationFileLocationLevel1(),
|
|
// but run from the directory the config file resides in
|
|
let configuration = Configuration(configurationFiles: ["custom_included_excluded.yml".url()])
|
|
let actualIncludedPath = configuration.includedPaths.first!
|
|
let desiredIncludedPath = "File1.swift".url(relativeTo: Constants.Dir.level0)
|
|
let actualExcludedPath = configuration.excludedPaths.first!
|
|
let desiredExcludedPath = "File2.swift".url(relativeTo: Constants.Dir.level0)
|
|
|
|
#expect(actualIncludedPath == desiredIncludedPath)
|
|
#expect(actualExcludedPath == desiredExcludedPath)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.exclusionTests))
|
|
func excludedPaths() {
|
|
let configuration = Configuration(
|
|
includedPaths: ["directory".url()],
|
|
excludedPaths: ["directory/excluded".url(), "directory/ExcludedFile.swift".url()]
|
|
)
|
|
|
|
let paths = configuration.lintablePaths(
|
|
inPath: URL.cwd,
|
|
forceExclude: false,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
assertEqual(["directory/File1.swift", "directory/File2.swift"], paths)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.exclusionTests))
|
|
func forceExcludesFile() {
|
|
let configuration = Configuration(excludedPaths: ["directory/ExcludedFile.swift".url()])
|
|
|
|
let paths = configuration.lintablePaths(
|
|
inPath: "directory/ExcludedFile.swift".url(),
|
|
forceExclude: true,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
#expect(paths.isEmpty)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.exclusionTests))
|
|
func forceExcludesFileNotPresentInExcluded() {
|
|
let configuration = Configuration(
|
|
includedPaths: ["directory".url()],
|
|
excludedPaths: ["directory/ExcludedFile.swift".url(), "directory/excluded".url()]
|
|
)
|
|
|
|
let paths = configuration.lintablePaths(
|
|
inPath: URL.cwd,
|
|
forceExclude: true,
|
|
excludeByPrefix: false
|
|
)
|
|
|
|
assertEqual(["directory/File1.swift", "directory/File2.swift"], paths)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.exclusionTests))
|
|
func forceExcludesDirectory() {
|
|
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)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.exclusionTests))
|
|
func forceExcludesDirectoryThatIsNotInExcludedButHasChildrenThatAre() {
|
|
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)
|
|
}
|
|
|
|
@Test
|
|
func lintablePaths() {
|
|
let paths = Configuration.default.lintablePaths(inPath: Constants.Dir.level0,
|
|
forceExclude: false,
|
|
excludeByPrefix: false)
|
|
let filenames = paths.map(\.lastPathComponent).sorted()
|
|
let expectedFilenames = [
|
|
"DirectoryLevel1.swift",
|
|
"Level0.swift", "Level1.swift", "Level2.swift", "Level3.swift",
|
|
"Main.swift", "Sub.swift",
|
|
]
|
|
|
|
#expect(Set(expectedFilenames) == Set(filenames))
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func globIncludePaths() {
|
|
let configuration = Configuration(includedPaths: ["**/Level2".url()])
|
|
let paths = configuration.lintablePaths(inPath: Constants.Dir.level0,
|
|
forceExclude: true,
|
|
excludeByPrefix: false)
|
|
let filenames = paths.map(\.lastPathComponent).sorted()
|
|
let expectedFilenames = ["Level2.swift", "Level3.swift"]
|
|
|
|
#expect(Set(expectedFilenames) == Set(filenames))
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func duplicatedGlobIncludePaths() {
|
|
let configuration = Configuration(includedPaths: ["**/Level2".url(), "**/Level2".url()])
|
|
let paths = configuration.lintablePaths(inPath: Constants.Dir.level0,
|
|
forceExclude: true,
|
|
excludeByPrefix: false)
|
|
let filenames = paths.map(\.lastPathComponent).sorted()
|
|
let expectedFilenames = ["Level2.swift", "Level3.swift"]
|
|
|
|
#expect(expectedFilenames == filenames)
|
|
}
|
|
|
|
@Test
|
|
func globExcludePaths() {
|
|
let configuration = Configuration(
|
|
includedPaths: [Constants.Dir.level3],
|
|
excludedPaths: [Constants.Dir.level3.appending(path: "*.swift", directoryHint: .notDirectory)]
|
|
)
|
|
|
|
let lintablePaths = configuration.lintablePaths(inPath: URL.cwd,
|
|
forceExclude: false,
|
|
excludeByPrefix: false)
|
|
#expect(lintablePaths.isEmpty)
|
|
}
|
|
|
|
// MARK: - Testing Configuration Equality
|
|
|
|
@Test
|
|
func isEqualTo() {
|
|
#expect(Constants.Config._0 == Constants.Config._0) // swiftlint:disable:this identical_operands
|
|
}
|
|
|
|
@Test
|
|
func isNotEqualTo() {
|
|
#expect(Constants.Config._0 != Constants.Config._2)
|
|
}
|
|
|
|
// MARK: - Testing Custom Configuration File
|
|
|
|
@Test
|
|
func customConfiguration() {
|
|
let file = SwiftLintFile(path: Constants.Swift._0)!
|
|
#expect(Constants.Config._0.configuration(for: file) != Constants.Config._0Custom.configuration(for: file))
|
|
}
|
|
|
|
@Test
|
|
func configurationWithSwiftFileAsRoot() {
|
|
let configuration = Configuration(configurationFiles: [Constants.Yml._0])
|
|
|
|
let file = SwiftLintFile(path: Constants.Swift._0)!
|
|
#expect(configuration.configuration(for: file) == configuration)
|
|
}
|
|
|
|
@Test
|
|
func configurationWithSwiftFileAsRootAndCustomConfiguration() {
|
|
let configuration = Constants.Config._0Custom
|
|
|
|
let file = SwiftLintFile(path: Constants.Swift._0)!
|
|
#expect(configuration.configuration(for: file) == configuration)
|
|
}
|
|
|
|
// MARK: - Testing custom indentation
|
|
|
|
@Test
|
|
func indentationTabs() throws {
|
|
let configuration = try Configuration(dict: ["indentation": "tabs"])
|
|
#expect(configuration.indentation == .tabs)
|
|
}
|
|
|
|
@Test
|
|
func indentationSpaces() throws {
|
|
let configuration = try Configuration(dict: ["indentation": 2])
|
|
#expect(configuration.indentation == .spaces(count: 2))
|
|
}
|
|
|
|
@Test
|
|
func indentationFallback() throws {
|
|
let configuration = try Configuration(dict: ["indentation": "invalid"])
|
|
#expect(configuration.indentation == .spaces(count: 4))
|
|
}
|
|
|
|
// MARK: - Testing Rules from config dictionary
|
|
|
|
nonisolated(unsafe) private static let testRuleList = RuleList(rules: RuleWithLevelsMock.self)
|
|
|
|
@Test
|
|
func configuresCorrectlyFromDict() throws {
|
|
let ruleConfiguration = [1, 2]
|
|
let config = [RuleWithLevelsMock.identifier: ruleConfiguration]
|
|
let rules = try Self.testRuleList.allRulesWrapped(configurationDict: config).map(\.rule)
|
|
#expect(rules == [try RuleWithLevelsMock(configuration: ruleConfiguration)])
|
|
}
|
|
|
|
@Test
|
|
func configureFallsBackCorrectly() throws {
|
|
let config = [RuleWithLevelsMock.identifier: ["a", "b"]]
|
|
let rules = try Self.testRuleList.allRulesWrapped(configurationDict: config).map(\.rule)
|
|
#expect(rules == [RuleWithLevelsMock()])
|
|
}
|
|
|
|
@Test
|
|
func allowZeroLintableFiles() throws {
|
|
let configuration = try Configuration(dict: ["allow_zero_lintable_files": true])
|
|
#expect(configuration.allowZeroLintableFiles)
|
|
}
|
|
|
|
@Test
|
|
func strict() throws {
|
|
let configuration = try Configuration(dict: ["strict": true])
|
|
#expect(configuration.strict)
|
|
}
|
|
|
|
@Test
|
|
func lenient() throws {
|
|
let configuration = try Configuration(dict: ["lenient": true])
|
|
#expect(configuration.lenient)
|
|
}
|
|
|
|
@Test
|
|
func baseline() throws {
|
|
let baselinePath = "Baseline.json"
|
|
let configuration = try Configuration(dict: ["baseline": baselinePath])
|
|
#expect(configuration.baseline == baselinePath.url())
|
|
}
|
|
|
|
@Test
|
|
func writeBaseline() throws {
|
|
let baselinePath = "Baseline.json"
|
|
let configuration = try Configuration(dict: ["write_baseline": baselinePath])
|
|
#expect(configuration.writeBaseline == baselinePath.url())
|
|
}
|
|
|
|
@Test
|
|
func checkForUpdates() throws {
|
|
let configuration = try Configuration(dict: ["check_for_updates": true])
|
|
#expect(configuration.checkForUpdates)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func excludeByPrefixExcludedPaths() {
|
|
let configuration = Configuration(
|
|
includedPaths: ["Level1".url()],
|
|
excludedPaths: ["Level1/Level1.swift".url(), "Level1/Level2/Level3".url()]
|
|
)
|
|
let paths = configuration.lintablePaths(
|
|
inPath: Constants.Dir.level0,
|
|
forceExclude: false,
|
|
excludeByPrefix: true
|
|
)
|
|
let filenames = paths.map(\.lastPathComponent)
|
|
#expect(filenames == ["Level2.swift"])
|
|
}
|
|
|
|
@Test
|
|
func excludeByPrefixForceExcludesFile() {
|
|
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)
|
|
#expect(paths.isEmpty)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func excludeByPrefixForceExcludesFileNotPresentInExcluded() {
|
|
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(\.lastPathComponent).sorted()
|
|
#expect(["Level2.swift", "Level3.swift"] == filenames)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func excludeByPrefixForceExcludesDirectory() {
|
|
let configuration = Configuration(
|
|
excludedPaths: [
|
|
"Level1/Level2", "Directory.swift", "ChildConfig", "ParentConfig", "NestedConfig"
|
|
].map { $0.url() }
|
|
)
|
|
let paths = configuration.lintablePaths(
|
|
inPath: URL.cwd,
|
|
forceExclude: true,
|
|
excludeByPrefix: true
|
|
)
|
|
let filenames = paths.map(\.lastPathComponent).sorted()
|
|
#expect(["Level0.swift", "Level1.swift"] == filenames)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func excludeByPrefixForceExcludesDirectoryThatIsNotInExcludedButHasChildrenThatAre() {
|
|
let configuration = Configuration(
|
|
excludedPaths: [
|
|
"Level1", "Directory.swift/DirectoryLevel1.swift", "ChildConfig", "ParentConfig", "NestedConfig"
|
|
].map { $0.url() }
|
|
)
|
|
let paths = configuration.lintablePaths(
|
|
inPath: URL.cwd,
|
|
forceExclude: true,
|
|
excludeByPrefix: true
|
|
)
|
|
let filenames = paths.map(\.lastPathComponent)
|
|
#expect(["Level0.swift"] == filenames)
|
|
}
|
|
|
|
@Test(.workingDirectory(Constants.Dir.level0))
|
|
func excludeByPrefixGlobExcludePaths() {
|
|
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(\.lastPathComponent).sorted()
|
|
#expect(filenames == ["Level1.swift"])
|
|
}
|
|
|
|
@Test
|
|
func dictInitWithCachePath() throws {
|
|
let configuration = try Configuration(
|
|
dict: ["cache_path": "cache/path/1"]
|
|
)
|
|
|
|
#expect(configuration.cachePath == "cache/path/1")
|
|
}
|
|
|
|
@Test
|
|
func dictInitWithCachePathFromCommandLine() throws {
|
|
let configuration = try Configuration(
|
|
dict: ["cache_path": "cache/path/1"],
|
|
cachePath: "cache/path/2"
|
|
)
|
|
|
|
#expect(configuration.cachePath == "cache/path/2")
|
|
}
|
|
|
|
@Test
|
|
func mainInitWithCachePath() {
|
|
let configuration = Configuration(
|
|
configurationFiles: [],
|
|
cachePath: "cache/path/1"
|
|
)
|
|
|
|
#expect(configuration.cachePath == "cache/path/1")
|
|
}
|
|
|
|
@Test(.temporaryDirectory)
|
|
func mainInitWithCachePathAndCachedConfig() {
|
|
let configuration1 = Configuration(
|
|
configurationFiles: [],
|
|
cachePath: "cache/path/1"
|
|
)
|
|
|
|
let configuration2 = Configuration(
|
|
configurationFiles: [],
|
|
cachePath: "cache/path/2"
|
|
)
|
|
|
|
#expect(configuration1.cachePath == "cache/path/1")
|
|
#expect(configuration2.cachePath == "cache/path/2")
|
|
}
|
|
|
|
private func assertEqual(_ relativeExpectedPaths: [String],
|
|
_ actualPaths: [URL],
|
|
sourceLocation: Testing.SourceLocation = #_sourceLocation) {
|
|
#expect(
|
|
relativeExpectedPaths.absolutePathsStandardized().sorted()
|
|
== actualPaths.map(\.path).absolutePathsStandardized().sorted(),
|
|
sourceLocation: sourceLocation
|
|
)
|
|
}
|
|
}
|
|
|
|
private extension Sequence where Element == String {
|
|
func absolutePathsStandardized() -> [String] {
|
|
// In Bazel builds, absolute paths might be prefixed with `/private`.
|
|
map { $0.url().standardizedFileURL.resolvingSymlinksInPath().relativeDisplayPath }
|
|
}
|
|
}
|
|
|
|
private extension Configuration {
|
|
var enabledRuleIdentifiers: [String] {
|
|
rules.map { type(of: $0).identifier }.sorted()
|
|
}
|
|
}
|