Compare commits
9 Commits
main
...
jp-reenabl
Author | SHA1 | Date |
---|---|---|
![]() |
da7ef33c38 | |
![]() |
41509fe7f0 | |
![]() |
07861f2caa | |
![]() |
b4854f08f3 | |
![]() |
286c8403f8 | |
![]() |
b50e0ce4b9 | |
![]() |
63c6b602e4 | |
![]() |
48834fb887 | |
![]() |
ab3b57bb0d |
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -11,12 +11,31 @@
|
|||
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||
[#1387](https://github.com/realm/SwiftLint/issues/1453)
|
||||
|
||||
* Add `modificationDate(forFileAtPath:)` function requirement to
|
||||
`LintableFileManager` protocol.
|
||||
[Victor Pimentel](https://github.com/victorpimentel)
|
||||
|
||||
* Replace `configurationHash` parameters in all `LinterCache` initializers
|
||||
with `configurationDescription` parameters.
|
||||
[Victor Pimentel](https://github.com/victorpimentel)
|
||||
|
||||
* Remove `fileHash` parameter from `LinterCache.cache(violations:forFile:)`
|
||||
and `LinterCache.violations(forFile:)` functions.
|
||||
[Victor Pimentel](https://github.com/victorpimentel)
|
||||
|
||||
* Remove `hash` attribute from `Configuration` struct.
|
||||
[Victor Pimentel](https://github.com/victorpimentel)
|
||||
|
||||
* Rename `ConditionalReturnsOnNewline` struct to
|
||||
`ConditionalReturnsOnNewlineRule` to match rule naming conventions.
|
||||
[JP Simard](https://github.com/jpsim)
|
||||
|
||||
##### Enhancements
|
||||
|
||||
* Cache linter results for files unmodified since the previous linter run.
|
||||
[Victor Pimentel](https://github.com/victorpimentel)
|
||||
[#1184](https://github.com/realm/SwiftLint/issues/1184)
|
||||
|
||||
* Add opt-in configurations to `generic_type_name`, `identifier_name` and
|
||||
`type_name` rules to allow excluding non-alphanumeric characters and names
|
||||
that start with uppercase.
|
||||
|
|
|
@ -10,6 +10,7 @@ import Foundation
|
|||
|
||||
public protocol LintableFileManager {
|
||||
func filesToLint(inPath: String, rootDirectory: String?) -> [String]
|
||||
func modificationDate(forFileAtPath: String) -> Date?
|
||||
}
|
||||
|
||||
extension FileManager: LintableFileManager {
|
||||
|
@ -31,4 +32,8 @@ extension FileManager: LintableFileManager {
|
|||
return nil
|
||||
} ?? []
|
||||
}
|
||||
|
||||
public func modificationDate(forFileAtPath path: String) -> Date? {
|
||||
return (try? attributesOfItem(atPath: path))?[.modificationDate] as? Date
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
import Foundation
|
||||
import SourceKittenFramework
|
||||
|
||||
private let fileManager = FileManager.default
|
||||
|
||||
private enum ConfigurationKey: String {
|
||||
case cachePath = "cache_path"
|
||||
case disabledRules = "disabled_rules"
|
||||
|
@ -34,9 +32,25 @@ public struct Configuration: Equatable {
|
|||
public let rules: [Rule]
|
||||
public var rootPath: String? // the root path to search for nested configurations
|
||||
public var configurationPath: String? // if successfully loaded from a path
|
||||
public var hash: Int?
|
||||
public let cachePath: String?
|
||||
|
||||
public var cacheDescription: String {
|
||||
let cacheRulesDescriptions: [String: Any] = rules.reduce([:]) { accu, element in
|
||||
var accu = accu
|
||||
accu[type(of: element).description.identifier] = element.cacheDescription
|
||||
return accu
|
||||
}
|
||||
let dict: [String: Any] = [
|
||||
"root": rootPath ?? FileManager.default.currentDirectoryPath,
|
||||
"rules": cacheRulesDescriptions
|
||||
]
|
||||
if let jsonData = try? JSONSerialization.data(withJSONObject: dict),
|
||||
let jsonString = String(data: jsonData, encoding: .utf8) {
|
||||
return jsonString
|
||||
}
|
||||
fatalError("Could not serialize configuration for cache")
|
||||
}
|
||||
|
||||
public init?(disabledRules: [String] = [],
|
||||
optInRules: [String] = [],
|
||||
enableAllRules: Bool = false,
|
||||
|
@ -176,7 +190,6 @@ public struct Configuration: Equatable {
|
|||
}
|
||||
self.init(dict: dict, enableAllRules: enableAllRules)!
|
||||
configurationPath = fullPath
|
||||
hash = dict.description.hashValue
|
||||
self.rootPath = rootPath
|
||||
return
|
||||
} catch YamlParserError.yamlParsing(let message) {
|
||||
|
@ -192,7 +205,7 @@ public struct Configuration: Equatable {
|
|||
optional: !CommandLine.arguments.contains("--config"), quiet: quiet, enableAllRules: enableAllRules)
|
||||
}
|
||||
|
||||
public func lintablePaths(inPath path: String, fileManager: LintableFileManager = fileManager) -> [String] {
|
||||
public func lintablePaths(inPath path: String, fileManager: LintableFileManager = FileManager.default) -> [String] {
|
||||
// If path is a Swift file, skip filtering with excluded/included paths
|
||||
if path.bridge().isSwiftFile() && path.isFile {
|
||||
return [path]
|
||||
|
|
|
@ -85,8 +85,7 @@ public struct Linter {
|
|||
}
|
||||
|
||||
if let cache = cache, let path = file.path {
|
||||
let hash = file.contents.hash
|
||||
cache.cache(violations: violations, forFile: path, fileHash: hash)
|
||||
cache.cache(violations: violations, forFile: path)
|
||||
}
|
||||
|
||||
for (deprecatedIdentifier, identifier) in deprecatedToValidIdentifier {
|
||||
|
@ -99,11 +98,8 @@ public struct Linter {
|
|||
|
||||
private func cachedStyleViolations(benchmark: Bool = false) -> ([StyleViolation], [(id: String, time: Double)])? {
|
||||
let start: Date! = benchmark ? Date() : nil
|
||||
guard let cache = cache,
|
||||
let file = file.path,
|
||||
case let hash = self.file.contents.hash,
|
||||
let cachedViolations = cache.violations(forFile: file, hash: hash) else {
|
||||
return nil
|
||||
guard let cache = cache, let file = file.path, let cachedViolations = cache.violations(forFile: file) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ruleTimes = [(id: String, time: Double)]()
|
||||
|
|
|
@ -18,25 +18,26 @@ public enum LinterCacheError: Error {
|
|||
public final class LinterCache {
|
||||
private var cache: [String: Any]
|
||||
private let lock = NSLock()
|
||||
internal lazy var fileManager: LintableFileManager = FileManager.default
|
||||
|
||||
public init(currentVersion: Version = .current, configurationHash: Int? = nil) {
|
||||
public init(currentVersion: Version = .current, configurationDescription: String? = nil) {
|
||||
cache = [
|
||||
"version": currentVersion.value,
|
||||
"files": [:]
|
||||
Key.version.rawValue: currentVersion.value,
|
||||
Key.files.rawValue: [:]
|
||||
]
|
||||
cache["configuration_hash"] = configurationHash
|
||||
cache[Key.configuration.rawValue] = configurationDescription
|
||||
}
|
||||
|
||||
public init(cache: Any, currentVersion: Version = .current, configurationHash: Int? = nil) throws {
|
||||
public init(cache: Any, currentVersion: Version = .current, configurationDescription: String? = nil) throws {
|
||||
guard let dictionary = cache as? [String: Any] else {
|
||||
throw LinterCacheError.invalidFormat
|
||||
}
|
||||
|
||||
guard let version = dictionary["version"] as? String, version == currentVersion.value else {
|
||||
guard dictionary[Key.version.rawValue] as? String == currentVersion.value else {
|
||||
throw LinterCacheError.differentVersion
|
||||
}
|
||||
|
||||
if dictionary["configuration_hash"] as? Int != configurationHash {
|
||||
guard dictionary[Key.configuration.rawValue] as? String == configurationDescription else {
|
||||
throw LinterCacheError.differentConfiguration
|
||||
}
|
||||
|
||||
|
@ -44,32 +45,40 @@ public final class LinterCache {
|
|||
}
|
||||
|
||||
public convenience init(contentsOf url: URL, currentVersion: Version = .current,
|
||||
configurationHash: Int? = nil) throws {
|
||||
configurationDescription: String? = nil) throws {
|
||||
let data = try Data(contentsOf: url)
|
||||
let json = try JSONSerialization.jsonObject(with: data, options: [])
|
||||
try self.init(cache: json, currentVersion: currentVersion,
|
||||
configurationHash: configurationHash)
|
||||
configurationDescription: configurationDescription)
|
||||
}
|
||||
|
||||
public func cache(violations: [StyleViolation], forFile file: String, fileHash: Int) {
|
||||
public func cache(violations: [StyleViolation], forFile file: String) {
|
||||
guard let lastModification = fileManager.modificationDate(forFileAtPath: file) else {
|
||||
return
|
||||
}
|
||||
|
||||
lock.lock()
|
||||
var filesCache = (cache["files"] as? [String: Any]) ?? [:]
|
||||
var filesCache = (cache[Key.files.rawValue] as? [String: Any]) ?? [:]
|
||||
filesCache[file] = [
|
||||
"violations": violations.map(dictionary(for:)),
|
||||
"hash": fileHash
|
||||
Key.violations.rawValue: violations.map(dictionary(for:)),
|
||||
Key.lastModification.rawValue: lastModification.timeIntervalSinceReferenceDate
|
||||
]
|
||||
cache["files"] = filesCache
|
||||
cache[Key.files.rawValue] = filesCache
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
public func violations(forFile file: String, hash: Int) -> [StyleViolation]? {
|
||||
public func violations(forFile file: String) -> [StyleViolation]? {
|
||||
guard let lastModification = fileManager.modificationDate(forFileAtPath: file) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
lock.lock()
|
||||
|
||||
guard let filesCache = cache["files"] as? [String: Any],
|
||||
guard let filesCache = cache[Key.files.rawValue] as? [String: Any],
|
||||
let entry = filesCache[file] as? [String: Any],
|
||||
let cacheHash = entry["hash"] as? Int,
|
||||
cacheHash == hash,
|
||||
let violations = entry["violations"] as? [[String: Any]] else {
|
||||
let cacheLastModification = entry[Key.lastModification.rawValue] as? TimeInterval,
|
||||
cacheLastModification == lastModification.timeIntervalSinceReferenceDate,
|
||||
let violations = entry[Key.violations.rawValue] as? [[String: Any]] else {
|
||||
lock.unlock()
|
||||
return nil
|
||||
}
|
||||
|
@ -87,29 +96,46 @@ public final class LinterCache {
|
|||
|
||||
private func dictionary(for violation: StyleViolation) -> [String: Any] {
|
||||
return [
|
||||
"line": violation.location.line ?? NSNull() as Any,
|
||||
"character": violation.location.character ?? NSNull() as Any,
|
||||
"severity": violation.severity.rawValue,
|
||||
"type": violation.ruleDescription.name,
|
||||
"rule_id": violation.ruleDescription.identifier,
|
||||
"reason": violation.reason
|
||||
Key.line.rawValue: violation.location.line ?? NSNull() as Any,
|
||||
Key.character.rawValue: violation.location.character ?? NSNull() as Any,
|
||||
Key.severity.rawValue: violation.severity.rawValue,
|
||||
Key.type.rawValue: violation.ruleDescription.name,
|
||||
Key.ruleID.rawValue: violation.ruleDescription.identifier,
|
||||
Key.reason.rawValue: violation.reason
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension LinterCache {
|
||||
fileprivate enum Key: String {
|
||||
case character
|
||||
case configuration
|
||||
case files
|
||||
case lastModification = "last_modification"
|
||||
case line
|
||||
case reason
|
||||
case ruleID = "rule_id"
|
||||
case severity
|
||||
case type
|
||||
case version
|
||||
case violations
|
||||
}
|
||||
}
|
||||
|
||||
extension StyleViolation {
|
||||
fileprivate static func from(cache: [String: Any], file: String) -> StyleViolation? {
|
||||
guard let severity = (cache["severity"] as? String).flatMap({ ViolationSeverity(rawValue: $0) }),
|
||||
let name = cache["type"] as? String,
|
||||
let ruleId = cache["rule_id"] as? String,
|
||||
let reason = cache["reason"] as? String else {
|
||||
guard let severityString = (cache[LinterCache.Key.severity.rawValue] as? String),
|
||||
let severity = ViolationSeverity(rawValue: severityString),
|
||||
let name = cache[LinterCache.Key.type.rawValue] as? String,
|
||||
let ruleID = cache[LinterCache.Key.ruleID.rawValue] as? String,
|
||||
let reason = cache[LinterCache.Key.reason.rawValue] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let line = cache["line"] as? Int
|
||||
let character = cache["character"] as? Int
|
||||
let line = cache[LinterCache.Key.line.rawValue] as? Int
|
||||
let character = cache[LinterCache.Key.character.rawValue] as? Int
|
||||
|
||||
let ruleDescription = RuleDescription(identifier: ruleId, name: name, description: reason)
|
||||
let ruleDescription = RuleDescription(identifier: ruleID, name: name, description: reason)
|
||||
let location = Location(file: file, line: line, character: character)
|
||||
let violation = StyleViolation(ruleDescription: ruleDescription, severity: severity,
|
||||
location: location, reason: reason)
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// CacheDescriptionProvider.swift
|
||||
// SwiftLint
|
||||
//
|
||||
// Created by JP Simard on 5/16/17.
|
||||
// Copyright © 2017 Realm. All rights reserved.
|
||||
//
|
||||
|
||||
internal protocol CacheDescriptionProvider {
|
||||
var cacheDescription: String { get }
|
||||
}
|
|
@ -21,6 +21,10 @@ extension Rule {
|
|||
public func isEqualTo(_ rule: Rule) -> Bool {
|
||||
return type(of: self).description == type(of: rule).description
|
||||
}
|
||||
|
||||
internal var cacheDescription: String {
|
||||
return (self as? CacheDescriptionProvider)?.cacheDescription ?? configurationDescription
|
||||
}
|
||||
}
|
||||
|
||||
public protocol OptInRule: Rule {}
|
||||
|
|
|
@ -6,14 +6,18 @@
|
|||
// Copyright © 2016 Realm. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol RuleConfiguration {
|
||||
mutating func apply(configuration: Any) throws
|
||||
func isEqualTo(_ ruleConfiguration: RuleConfiguration) -> Bool
|
||||
var consoleDescription: String { get }
|
||||
}
|
||||
|
||||
extension RuleConfiguration {
|
||||
internal var cacheDescription: String {
|
||||
return (self as? CacheDescriptionProvider)?.cacheDescription ?? consoleDescription
|
||||
}
|
||||
}
|
||||
|
||||
extension RuleConfiguration where Self: Equatable {
|
||||
public func isEqualTo(_ ruleConfiguration: RuleConfiguration) -> Bool {
|
||||
return self == ruleConfiguration as? Self
|
||||
|
|
|
@ -17,8 +17,11 @@ private extension Region {
|
|||
|
||||
// MARK: - CustomRulesConfiguration
|
||||
|
||||
public struct CustomRulesConfiguration: RuleConfiguration, Equatable {
|
||||
public struct CustomRulesConfiguration: RuleConfiguration, Equatable, CacheDescriptionProvider {
|
||||
public var consoleDescription: String { return "user-defined" }
|
||||
internal var cacheDescription: String {
|
||||
return customRuleConfigurations.map({ $0.cacheDescription }).joined(separator: "\n")
|
||||
}
|
||||
public var customRuleConfigurations = [RegexConfiguration]()
|
||||
|
||||
public init() {}
|
||||
|
@ -42,7 +45,11 @@ public func == (lhs: CustomRulesConfiguration, rhs: CustomRulesConfiguration) ->
|
|||
|
||||
// MARK: - CustomRules
|
||||
|
||||
public struct CustomRules: Rule, ConfigurationProviderRule {
|
||||
public struct CustomRules: Rule, ConfigurationProviderRule, CacheDescriptionProvider {
|
||||
|
||||
internal var cacheDescription: String {
|
||||
return configuration.cacheDescription
|
||||
}
|
||||
|
||||
public static let description = RuleDescription(
|
||||
identifier: "custom_rules",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import Foundation
|
||||
import SourceKittenFramework
|
||||
|
||||
public struct RegexConfiguration: RuleConfiguration, Equatable {
|
||||
public struct RegexConfiguration: RuleConfiguration, Equatable, CacheDescriptionProvider {
|
||||
public let identifier: String
|
||||
public var name: String?
|
||||
public var message = "Regex matched."
|
||||
|
@ -27,6 +27,23 @@ public struct RegexConfiguration: RuleConfiguration, Equatable {
|
|||
return "\(severity.rawValue): \(regex.pattern)"
|
||||
}
|
||||
|
||||
internal var cacheDescription: String {
|
||||
var dict = [String: Any]()
|
||||
dict["identifier"] = identifier
|
||||
dict["name"] = name
|
||||
dict["message"] = message
|
||||
dict["regex"] = regex.pattern
|
||||
dict["included"] = included?.pattern
|
||||
dict["excluded"] = excluded?.pattern
|
||||
dict["match_kinds"] = matchKinds.map { $0.rawValue }
|
||||
dict["severity"] = severityConfiguration.consoleDescription
|
||||
if let jsonData = try? JSONSerialization.data(withJSONObject: dict),
|
||||
let jsonString = String(data: jsonData, encoding: .utf8) {
|
||||
return jsonString
|
||||
}
|
||||
fatalError("Could not serialize regex configuration for cache")
|
||||
}
|
||||
|
||||
public var description: RuleDescription {
|
||||
return RuleDescription(identifier: identifier, name: name ?? identifier, description: "")
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ struct LintOptions: OptionsProtocol {
|
|||
// swiftlint:disable line_length
|
||||
static func create(_ path: String) -> (_ useSTDIN: Bool) -> (_ configurationFile: String) -> (_ strict: Bool) -> (_ lenient: Bool) -> (_ useScriptInputFiles: Bool) -> (_ benchmark: Bool) -> (_ reporter: String) -> (_ quiet: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ enableAllRules: Bool) -> LintOptions {
|
||||
return { useSTDIN in { configurationFile in { strict in { lenient in { useScriptInputFiles in { benchmark in { reporter in { quiet in { cachePath in { ignoreCache in { enableAllRules in
|
||||
self.init(path: path, useSTDIN: useSTDIN, configurationFile: configurationFile, strict: strict, lenient: lenient, useScriptInputFiles: useScriptInputFiles, benchmark: benchmark, reporter: reporter, quiet: quiet, cachePath: cachePath, ignoreCache: true, enableAllRules: enableAllRules)
|
||||
self.init(path: path, useSTDIN: useSTDIN, configurationFile: configurationFile, strict: strict, lenient: lenient, useScriptInputFiles: useScriptInputFiles, benchmark: benchmark, reporter: reporter, quiet: quiet, cachePath: cachePath, ignoreCache: ignoreCache, enableAllRules: enableAllRules)
|
||||
}}}}}}}}}}}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,12 @@ extension LinterCache {
|
|||
return nil
|
||||
}
|
||||
|
||||
let configurationHash = configuration.hash
|
||||
let configurationDescription = configuration.cacheDescription
|
||||
let cache: LinterCache
|
||||
do {
|
||||
cache = try LinterCache(contentsOf: url, configurationHash: configurationHash)
|
||||
cache = try LinterCache(contentsOf: url, configurationDescription: configurationDescription)
|
||||
} catch {
|
||||
cache = LinterCache(configurationHash: configurationHash)
|
||||
cache = LinterCache(configurationDescription: configurationDescription)
|
||||
}
|
||||
|
||||
return cache
|
||||
|
@ -50,7 +50,7 @@ private func defaultCacheURL(options: LintOptions) -> URL {
|
|||
#if os(Linux)
|
||||
let baseURL = URL(fileURLWithPath: "/var/tmp/")
|
||||
#else
|
||||
let baseURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
||||
let baseURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
||||
#endif
|
||||
|
||||
let fileName = String(rootPath.hash) + ".json"
|
||||
|
|
|
@ -120,8 +120,8 @@
|
|||
D44254201DB87CA200492EA4 /* ValidIBInspectableRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D442541E1DB87C3D00492EA4 /* ValidIBInspectableRule.swift */; };
|
||||
D44254271DB9C15C00492EA4 /* SyntacticSugarRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44254251DB9C12300492EA4 /* SyntacticSugarRule.swift */; };
|
||||
D4470D571EB69225008A1B2E /* ImplicitReturnRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D561EB69225008A1B2E /* ImplicitReturnRule.swift */; };
|
||||
D4470D5B1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */; };
|
||||
D4470D591EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D581EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift */; };
|
||||
D4470D5B1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */; };
|
||||
D44AD2761C0AA5350048F7B0 /* LegacyConstructorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */; };
|
||||
D462021F1E15F52D0027AAD1 /* NumberSeparatorRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D462021E1E15F52D0027AAD1 /* NumberSeparatorRuleExamples.swift */; };
|
||||
D46252541DF63FB200BE2CA1 /* NumberSeparatorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46252531DF63FB200BE2CA1 /* NumberSeparatorRule.swift */; };
|
||||
|
@ -170,6 +170,7 @@
|
|||
E315B83C1DFA4BC500621B44 /* DynamicInlineRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E315B83B1DFA4BC500621B44 /* DynamicInlineRule.swift */; };
|
||||
E57B23C11B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E57B23C01B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift */; };
|
||||
E802ED001C56A56000A35AE1 /* Benchmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = E802ECFF1C56A56000A35AE1 /* Benchmark.swift */; };
|
||||
E80746F61ECB722F00548D31 /* CacheDescriptionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80746F51ECB722F00548D31 /* CacheDescriptionProvider.swift */; };
|
||||
E809EDA11B8A71DF00399043 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E809EDA01B8A71DF00399043 /* Configuration.swift */; };
|
||||
E809EDA31B8A73FB00399043 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E809EDA21B8A73FB00399043 /* ConfigurationTests.swift */; };
|
||||
E80E018D1B92C0F60078EB70 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E80E018C1B92C0F60078EB70 /* Command.swift */; };
|
||||
|
@ -409,8 +410,8 @@
|
|||
D442541E1DB87C3D00492EA4 /* ValidIBInspectableRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidIBInspectableRule.swift; sourceTree = "<group>"; };
|
||||
D44254251DB9C12300492EA4 /* SyntacticSugarRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyntacticSugarRule.swift; sourceTree = "<group>"; };
|
||||
D4470D561EB69225008A1B2E /* ImplicitReturnRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImplicitReturnRule.swift; sourceTree = "<group>"; };
|
||||
D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnusedOptionalBindingRuleTests.swift; sourceTree = "<group>"; };
|
||||
D4470D581EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyEnumArgumentsRule.swift; sourceTree = "<group>"; };
|
||||
D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnusedOptionalBindingRuleTests.swift; sourceTree = "<group>"; };
|
||||
D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyConstructorRule.swift; sourceTree = "<group>"; };
|
||||
D462021E1E15F52D0027AAD1 /* NumberSeparatorRuleExamples.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSeparatorRuleExamples.swift; sourceTree = "<group>"; };
|
||||
D46252531DF63FB200BE2CA1 /* NumberSeparatorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSeparatorRule.swift; sourceTree = "<group>"; };
|
||||
|
@ -460,6 +461,7 @@
|
|||
E57B23C01B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReturnArrowWhitespaceRule.swift; sourceTree = "<group>"; };
|
||||
E5A167C81B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperatorFunctionWhitespaceRule.swift; sourceTree = "<group>"; };
|
||||
E802ECFF1C56A56000A35AE1 /* Benchmark.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Benchmark.swift; sourceTree = "<group>"; };
|
||||
E80746F51ECB722F00548D31 /* CacheDescriptionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheDescriptionProvider.swift; sourceTree = "<group>"; };
|
||||
E809EDA01B8A71DF00399043 /* Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
|
||||
E809EDA21B8A73FB00399043 /* ConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = "<group>"; };
|
||||
E80E018C1B92C0F60078EB70 /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
|
||||
|
@ -935,6 +937,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
E88DEA8B1B0999A000A66CB0 /* ASTRule.swift */,
|
||||
E80746F51ECB722F00548D31 /* CacheDescriptionProvider.swift */,
|
||||
E86396C11BADAAE5002C9E88 /* Reporter.swift */,
|
||||
E88DEA761B098D0C00A66CB0 /* Rule.swift */,
|
||||
3B828E521C546468000D180E /* RuleConfiguration.swift */,
|
||||
|
@ -1330,6 +1333,7 @@
|
|||
F22314B01D4FA4D7009AD165 /* LegacyNSGeometryFunctionsRule.swift in Sources */,
|
||||
E88DEA8C1B0999A000A66CB0 /* ASTRule.swift in Sources */,
|
||||
1E82D5591D7775C7009553D7 /* ClosureSpacingRule.swift in Sources */,
|
||||
E80746F61ECB722F00548D31 /* CacheDescriptionProvider.swift in Sources */,
|
||||
094385041D5D4F7C009168CF /* PrivateOutletRule.swift in Sources */,
|
||||
E88DEA6B1B0983FE00A66CB0 /* StyleViolation.swift in Sources */,
|
||||
3BB47D831C514E8100AE6A10 /* RegexConfiguration.swift in Sources */,
|
||||
|
|
|
@ -131,6 +131,10 @@ class ConfigurationTests: XCTestCase {
|
|||
XCTFail("Should not be called with path \(path)")
|
||||
return []
|
||||
}
|
||||
|
||||
public func modificationDate(forFileAtPath path: String) -> Date? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testExcludedPaths() {
|
||||
|
|
|
@ -12,6 +12,20 @@ import XCTest
|
|||
|
||||
class LinterCacheTests: XCTestCase {
|
||||
|
||||
private class TestFileManager: LintableFileManager {
|
||||
func filesToLint(inPath: String, rootDirectory: String? = nil) -> [String] {
|
||||
return []
|
||||
}
|
||||
|
||||
internal var stubbedModificationDateByPath: [String: Date] = [:]
|
||||
|
||||
public func modificationDate(forFileAtPath path: String) -> Date? {
|
||||
return stubbedModificationDateByPath[path]
|
||||
}
|
||||
}
|
||||
|
||||
private let fileManager = TestFileManager()
|
||||
|
||||
func testInitThrowsWhenUsingDifferentVersion() {
|
||||
let cache = ["version": "0.1.0"]
|
||||
checkError(LinterCacheError.differentVersion) {
|
||||
|
@ -27,10 +41,10 @@ class LinterCacheTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testInitThrowsWhenUsingDifferentConfiguration() {
|
||||
let cache = ["version": "0.1.0", "configuration_hash": 1] as [String : Any]
|
||||
let cache = ["version": "0.1.0", "configuration": "Configuration 1"] as [String : Any]
|
||||
checkError(LinterCacheError.differentConfiguration) {
|
||||
_ = try LinterCache(cache: cache, currentVersion: Version(value: "0.1.0"),
|
||||
configurationHash: 2)
|
||||
configurationDescription: "Configuration 2")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,15 +54,16 @@ class LinterCacheTests: XCTestCase {
|
|||
XCTAssertNotNil(linterCache)
|
||||
}
|
||||
|
||||
func testInitSucceedsWithConfigurationHash() {
|
||||
let cache = ["version": "0.2.0", "configuration_hash": 1] as [String : Any]
|
||||
func testInitSucceedsWithConfiguration() {
|
||||
let cache = ["version": "0.2.0", "configuration": "Configuration 1"] as [String : Any]
|
||||
let linterCache = try? LinterCache(cache: cache, currentVersion: Version(value: "0.2.0"),
|
||||
configurationHash: 1)
|
||||
configurationDescription: "Configuration 1")
|
||||
XCTAssertNotNil(linterCache)
|
||||
}
|
||||
|
||||
func testParsesViolations() {
|
||||
let cache = LinterCache(currentVersion: Version(value: "0.2.0"))
|
||||
cache.fileManager = fileManager
|
||||
let file = "foo.swift"
|
||||
let ruleDescription = RuleDescription(identifier: "rule", name: "Some rule",
|
||||
description: "Validates stuff")
|
||||
|
@ -62,27 +77,72 @@ class LinterCacheTests: XCTestCase {
|
|||
location: Location(file: file, line: 5, character: nil),
|
||||
reason: "Something is wrong.")
|
||||
]
|
||||
fileManager.stubbedModificationDateByPath[file] = Date()
|
||||
|
||||
cache.cache(violations: violations, forFile: file, fileHash: 1)
|
||||
let cachedViolations = cache.violations(forFile: file, hash: 1)
|
||||
cache.cache(violations: violations, forFile: file)
|
||||
let cachedViolations = cache.violations(forFile: file)
|
||||
|
||||
XCTAssertNotNil(cachedViolations)
|
||||
XCTAssertEqual(cachedViolations!, violations)
|
||||
}
|
||||
|
||||
func testParsesViolationsWithModifiedHash() {
|
||||
func testParsesViolationsWithEmptyViolations() {
|
||||
let cache = LinterCache(currentVersion: Version(value: "0.2.0"))
|
||||
cache.fileManager = fileManager
|
||||
let file = "foo.swift"
|
||||
cache.cache(violations: [], forFile: file, fileHash: 1)
|
||||
let cachedViolations = cache.violations(forFile: file, hash: 2)
|
||||
fileManager.stubbedModificationDateByPath[file] = Date()
|
||||
|
||||
let cachedViolations = cache.violations(forFile: file)
|
||||
|
||||
XCTAssertNil(cachedViolations)
|
||||
}
|
||||
|
||||
func testParsesViolationsWithEmptyViolations() {
|
||||
func testParsesViolationsWithNoDate() {
|
||||
let cache = LinterCache(currentVersion: Version(value: "0.2.0"))
|
||||
cache.fileManager = fileManager
|
||||
let file = "foo.swift"
|
||||
let cachedViolations = cache.violations(forFile: file, hash: 2)
|
||||
let ruleDescription = RuleDescription(identifier: "rule", name: "Some rule",
|
||||
description: "Validates stuff")
|
||||
let violations = [
|
||||
StyleViolation(ruleDescription: ruleDescription,
|
||||
severity: .warning,
|
||||
location: Location(file: file, line: 10, character: 2),
|
||||
reason: "Something is not right."),
|
||||
StyleViolation(ruleDescription: ruleDescription,
|
||||
severity: .error,
|
||||
location: Location(file: file, line: 5, character: nil),
|
||||
reason: "Something is wrong.")
|
||||
]
|
||||
fileManager.stubbedModificationDateByPath[file] = Date()
|
||||
|
||||
cache.cache(violations: violations, forFile: file)
|
||||
fileManager.stubbedModificationDateByPath[file] = nil
|
||||
let cachedViolations = cache.violations(forFile: file)
|
||||
|
||||
XCTAssertNil(cachedViolations)
|
||||
}
|
||||
|
||||
func testParsesViolationsWithDifferentDate() {
|
||||
let cache = LinterCache(currentVersion: Version(value: "0.2.0"))
|
||||
cache.fileManager = fileManager
|
||||
let file = "foo.swift"
|
||||
let ruleDescription = RuleDescription(identifier: "rule", name: "Some rule",
|
||||
description: "Validates stuff")
|
||||
let violations = [
|
||||
StyleViolation(ruleDescription: ruleDescription,
|
||||
severity: .warning,
|
||||
location: Location(file: file, line: 10, character: 2),
|
||||
reason: "Something is not right."),
|
||||
StyleViolation(ruleDescription: ruleDescription,
|
||||
severity: .error,
|
||||
location: Location(file: file, line: 5, character: nil),
|
||||
reason: "Something is wrong.")
|
||||
]
|
||||
fileManager.stubbedModificationDateByPath[file] = Date()
|
||||
|
||||
cache.cache(violations: violations, forFile: file)
|
||||
fileManager.stubbedModificationDateByPath[file] = Date()
|
||||
let cachedViolations = cache.violations(forFile: file)
|
||||
|
||||
XCTAssertNil(cachedViolations)
|
||||
}
|
||||
|
@ -95,10 +155,11 @@ extension LinterCacheTests {
|
|||
("testInitThrowsWhenUsingInvalidCacheFormat", testInitThrowsWhenUsingInvalidCacheFormat),
|
||||
("testInitThrowsWhenUsingDifferentConfiguration", testInitThrowsWhenUsingDifferentConfiguration),
|
||||
("testInitSucceeds", testInitSucceeds),
|
||||
("testInitSucceedsWithConfigurationHash", testInitSucceedsWithConfigurationHash),
|
||||
("testInitSucceedsWithConfiguration", testInitSucceedsWithConfiguration),
|
||||
("testParsesViolations", testParsesViolations),
|
||||
("testParsesViolationsWithModifiedHash", testParsesViolationsWithModifiedHash),
|
||||
("testParsesViolationsWithEmptyViolations", testParsesViolationsWithEmptyViolations)
|
||||
("testParsesViolationsWithEmptyViolations", testParsesViolationsWithEmptyViolations),
|
||||
("testParsesViolationsWithNoDate", testParsesViolationsWithNoDate),
|
||||
("testParsesViolationsWithDifferentDate", testParsesViolationsWithDifferentDate)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue