Skip to content

Commit a9ad475

Browse files
committed
Complete Test Coverage
1 parent 6932007 commit a9ad475

File tree

4 files changed

+60
-24
lines changed

4 files changed

+60
-24
lines changed

CodeEdit/Features/Welcome/Model/RecentProjectsStore.swift

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import AppKit
99
import CoreSpotlight
10+
import OSLog
1011

1112
/// Helper methods for managing the recent projects list and donating list items to CoreSpotlight.
1213
///
@@ -15,6 +16,8 @@ import CoreSpotlight
1516
/// If a UI element needs to listen to changes in this list, listen for the
1617
/// ``RecentProjectsStore/didUpdateNotification`` notification.
1718
class RecentProjectsStore {
19+
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "", category: "RecentProjectsStore")
20+
1821
/// The default projects store, uses the `UserDefaults.standard` storage location.
1922
static let shared = RecentProjectsStore()
2023

@@ -56,19 +59,6 @@ class RecentProjectsStore {
5659
/// Save a new paths array to defaults. Automatically limits the list to the most recent `100` items, donates
5760
/// search items to Spotlight, and notifies observers.
5861
private func setPaths(_ paths: [String]) {
59-
var paths = paths
60-
61-
// Remove duplicates
62-
var foundPaths = Set<String>()
63-
for (idx, path) in paths.enumerated().reversed() {
64-
if foundPaths.contains(path) {
65-
paths.remove(at: idx)
66-
} else {
67-
foundPaths.insert(path)
68-
}
69-
}
70-
71-
// Limit list to to 100 items after de-duplication
7262
defaults.setValue(Array(paths.prefix(100)), forKey: Self.projectsdDefaultsKey)
7363
setDocumentControllerRecents()
7464
donateSearchableItems()
@@ -95,10 +85,11 @@ class RecentProjectsStore {
9585
/// - Parameter paths: The paths to remove.
9686
/// - Returns: The remaining urls in the recent projects list.
9787
func removeRecentProjects(_ paths: Set<URL>) -> [URL] {
98-
var recentProjectPaths = recentProjectURLs()
88+
let paths = Set(paths.map { $0.path(percentEncoded: false) })
89+
var recentProjectPaths = recentPaths()
9990
recentProjectPaths.removeAll(where: { paths.contains($0) })
100-
setPaths(recentProjectPaths.map { $0.path(percentEncoded: false) })
101-
return recentProjectURLs()
91+
setPaths(recentProjectPaths)
92+
return recentURLs()
10293
}
10394

10495
func clearList() {
@@ -109,14 +100,14 @@ class RecentProjectsStore {
109100
/// Syncs AppKit's recent documents list with ours, keeping the dock menu and other lists up-to-date.
110101
private func setDocumentControllerRecents() {
111102
CodeEditDocumentController.shared.clearRecentDocuments(nil)
112-
for path in recentProjectURLs().prefix(10) {
103+
for path in recentURLs().prefix(10) {
113104
CodeEditDocumentController.shared.noteNewRecentDocumentURL(path)
114105
}
115106
}
116107

117108
/// Donates all recent URLs to Core Search, making them searchable in Spotlight
118109
private func donateSearchableItems() {
119-
let searchableItems = recentProjectURLs().map { entity in
110+
let searchableItems = recentURLs().map { entity in
120111
let attributeSet = CSSearchableItemAttributeSet(contentType: .content)
121112
attributeSet.title = entity.lastPathComponent
122113
attributeSet.relatedUniqueIdentifier = entity.path()
@@ -126,9 +117,9 @@ class RecentProjectsStore {
126117
attributeSet: attributeSet
127118
)
128119
}
129-
CSSearchableIndex.default().indexSearchableItems(searchableItems) { error in
120+
CSSearchableIndex.default().indexSearchableItems(searchableItems) { [weak self] error in
130121
if let error = error {
131-
print(error)
122+
self?.logger.debug("Failed to donate recent projects, error: \(error, privacy: .auto)")
132123
}
133124
}
134125
}

CodeEdit/Features/WindowCommands/Utils/RecentProjectsMenu.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import AppKit
1010
class RecentProjectsMenu: NSObject {
1111
let projectsStore: RecentProjectsStore
1212

13-
init(projectsStore: RecentProjectsStore = .default) {
13+
init(projectsStore: RecentProjectsStore = .shared) {
1414
self.projectsStore = projectsStore
1515
}
1616

CodeEditTestPlan.xctestplan

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
}
1010
],
1111
"defaultOptions" : {
12-
"codeCoverage" : false,
1312
"targetForVariableExpansion" : {
1413
"containerPath" : "container:CodeEdit.xcodeproj",
1514
"identifier" : "B658FB2B27DA9E0F00EA4DBD",

CodeEditTests/Features/Welcome/RecentProjectsTests.swift

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ import Testing
99
import Foundation
1010
@testable import CodeEdit
1111

12+
// This suite needs to be serial due to the use of `UserDefaults` and sharing one testing storage location.
13+
@Suite(.serialized)
1214
class RecentProjectsTests {
1315
let store: RecentProjectsStore
1416

1517
init() {
1618
let defaults = UserDefaults(suiteName: #file)!
19+
defaults.removeSuite(named: #file)
1720
store = RecentProjectsStore(defaults: defaults)
1821
}
1922

@@ -31,9 +34,52 @@ class RecentProjectsTests {
3134
store.documentOpened(at: URL(filePath: "Directory/", directoryHint: .isDirectory))
3235
store.documentOpened(at: URL(filePath: "Directory/file.txt", directoryHint: .notDirectory))
3336

37+
let recentURLs = store.recentURLs()
38+
#expect(recentURLs.count == 2)
39+
#expect(recentURLs[0].path(percentEncoded: false) == "Directory/file.txt")
40+
#expect(recentURLs[1].path(percentEncoded: false) == "Directory/")
41+
}
42+
43+
@Test
44+
func clearURLs() {
45+
store.documentOpened(at: URL(filePath: "Directory/", directoryHint: .isDirectory))
46+
store.documentOpened(at: URL(filePath: "Directory/file.txt", directoryHint: .notDirectory))
47+
3448
#expect(store.recentURLs().count == 2)
35-
#expect(store.recentURLs()[0].path(percentEncoded: false) == "Directory/")
36-
#expect(store.recentURLs()[1].path(percentEncoded: false) == "Directory/file.txt")
49+
50+
store.clearList()
51+
52+
#expect(store.recentURLs().isEmpty)
53+
}
54+
55+
@Test
56+
func duplicatesAreMovedToFront() {
57+
store.documentOpened(at: URL(filePath: "Directory/", directoryHint: .isDirectory))
58+
store.documentOpened(at: URL(filePath: "Directory/file.txt", directoryHint: .notDirectory))
59+
// Move to front
60+
store.documentOpened(at: URL(filePath: "Directory/", directoryHint: .isDirectory))
61+
// Remove duplicate
62+
store.documentOpened(at: URL(filePath: "Directory/", directoryHint: .isDirectory))
63+
64+
let recentURLs = store.recentURLs()
65+
#expect(recentURLs.count == 2)
66+
67+
// Should be moved to the front of the list because it was 'opened' again.
68+
#expect(recentURLs[0].path(percentEncoded: false) == "Directory/")
69+
#expect(recentURLs[1].path(percentEncoded: false) == "Directory/file.txt")
70+
}
71+
72+
@Test
73+
func removeSubset() {
74+
store.documentOpened(at: URL(filePath: "Directory/", directoryHint: .isDirectory))
75+
store.documentOpened(at: URL(filePath: "Directory/file.txt", directoryHint: .notDirectory))
76+
77+
let remaining = store.removeRecentProjects(Set([URL(filePath: "Directory/", directoryHint: .isDirectory)]))
78+
79+
#expect(remaining == [URL(filePath: "Directory/file.txt")])
80+
let recentURLs = store.recentURLs()
81+
#expect(recentURLs.count == 1)
82+
#expect(recentURLs[0].path(percentEncoded: false) == "Directory/file.txt")
3783
}
3884

3985
@Test

0 commit comments

Comments
 (0)