Skip to content

Commit

Permalink
Merge pull request #1551 from realm/write-cache
Browse files Browse the repository at this point in the history
Split cache into read cache and write cache
  • Loading branch information
marcelofabri authored May 24, 2017
2 parents eef3959 + 9a5e30e commit bdcfe1b
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 26 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
* Cache linter results for files unmodified since the previous linter run.
[Victor Pimentel](https://github.com/victorpimentel)
[JP Simard](https://github.com/jpsim)
[Marcelo Fabri](https://github.com/marcelofabri)
[#1184](https://github.com/realm/SwiftLint/issues/1184)
[#1550](https://github.com/realm/SwiftLint/issues/1550)

* Add opt-in configurations to `generic_type_name`, `identifier_name` and
`type_name` rules to allow excluding non-alphanumeric characters and names
Expand Down
73 changes: 54 additions & 19 deletions Source/SwiftLintFramework/Models/LinterCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,47 @@ internal enum LinterCacheError: Error {
}

public final class LinterCache {
private var cache = [String: Any]()
private typealias Cache = [String: [String: [String: Any]]]

private let readCache: Cache
private var writeCache = Cache()
private let lock = NSLock()
internal lazy var fileManager: LintableFileManager = FileManager.default
internal let fileManager: LintableFileManager
private let location: URL?

internal init() {
internal init(fileManager: LintableFileManager = FileManager.default) {
location = nil
self.fileManager = fileManager
self.readCache = [:]
}

internal init(cache: Any) throws {
guard let dictionary = cache as? [String: Any] else {
internal init(cache: Any, fileManager: LintableFileManager = FileManager.default) throws {
guard let dictionary = cache as? Cache else {
throw LinterCacheError.invalidFormat
}

self.cache = dictionary
self.readCache = dictionary
location = nil
self.fileManager = fileManager
}

public init(configuration: Configuration) {
public init(configuration: Configuration,
fileManager: LintableFileManager = FileManager.default) {
location = configuration.cacheURL
if let data = try? Data(contentsOf: location!),
let json = try? JSONSerialization.jsonObject(with: data) {
cache = (json as? [String: Any]) ?? [:]
let json = try? JSONSerialization.jsonObject(with: data),
let cache = json as? Cache {
readCache = cache
} else {
readCache = [:]
}
self.fileManager = fileManager
}

private init(cache: Cache, location: URL?, fileManager: LintableFileManager) {
self.readCache = cache
self.location = location
self.fileManager = fileManager
}

internal func cache(violations: [StyleViolation], forFile file: String, configuration: Configuration) {
Expand All @@ -49,12 +66,12 @@ public final class LinterCache {
let configurationDescription = configuration.cacheDescription

lock.lock()
var filesCache = (cache[configurationDescription] as? [String: Any]) ?? [:]
var filesCache = writeCache[configurationDescription] ?? [:]
filesCache[file] = [
Key.violations.rawValue: violations.map(dictionary(for:)),
Key.lastModification.rawValue: lastModification.timeIntervalSinceReferenceDate
]
cache[configurationDescription] = filesCache
writeCache[configurationDescription] = filesCache
lock.unlock()
}

Expand All @@ -65,31 +82,49 @@ public final class LinterCache {

let configurationDescription = configuration.cacheDescription

lock.lock()

guard let filesCache = cache[configurationDescription] as? [String: Any],
let entry = filesCache[file] as? [String: Any],
guard let filesCache = readCache[configurationDescription],
let entry = filesCache[file],
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
}

lock.unlock()
return violations.flatMap { StyleViolation.from(cache: $0, file: file) }
}

public func save() throws {
guard let url = location else {
throw LinterCacheError.noLocation
}
lock.lock()
guard !writeCache.isEmpty else {
return
}

let cache = mergeCaches()
let json = toJSON(cache)
lock.unlock()
try json.write(to: url, atomically: true, encoding: .utf8)
}

internal func flushed() -> LinterCache {
return LinterCache(cache: mergeCaches(), location: location, fileManager: fileManager)
}

private func mergeCaches() -> Cache {
var cache = readCache
lock.lock()
for (key, value) in writeCache {
var filesCache = cache[key] ?? [:]
for (file, fileCache) in value {
filesCache[file] = fileCache
}
cache[key] = filesCache
}
lock.unlock()

return cache
}

private func dictionary(for violation: StyleViolation) -> [String: Any] {
return [
Key.line.rawValue: violation.location.line ?? NSNull() as Any,
Expand Down
11 changes: 4 additions & 7 deletions Tests/SwiftLintFrameworkTests/LinterCacheTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,7 @@ class LinterCacheTests: XCTestCase {

// MARK: Test Helpers

private var cache: LinterCache = {
let cache = LinterCache()
cache.fileManager = TestFileManager()
return cache
}()
private var cache = LinterCache(fileManager: TestFileManager())

private func makeCacheTestHelper(dict: [String: Any]) -> CacheTestHelper {
return CacheTestHelper(dict: dict, cache: cache)
Expand All @@ -89,8 +85,9 @@ class LinterCacheTests: XCTestCase {
private func cacheAndValidate(violations: [StyleViolation], forFile: String, configuration: Configuration,
file: StaticString = #file, line: UInt = #line) {
cache.cache(violations: violations, forFile: forFile, configuration: configuration)
XCTAssertEqual(cache.violations(forFile: forFile, configuration: configuration)!, violations,
file: file, line: line)
cache = cache.flushed()
XCTAssertEqual(cache.violations(forFile: forFile, configuration: configuration)!,
violations, file: file, line: line)
}

private func cacheAndValidateNoViolationsTwoFiles(configuration: Configuration,
Expand Down

0 comments on commit bdcfe1b

Please sign in to comment.