Skip to content

Commit 75de97b

Browse files
authored
[Collections] listCollections always returns all configured collections (swiftlang#3565)
Motivation: `listCollections` has optional `identifiers` that if specified, only those collections are supposed to be fetched. However, the current implementation always fetches all configured collections because the logic for determining "missing" collections is flawed. Modification: Fix logic such that `listCollections` returns all or a subset of collections depending on `identifiers` param.
1 parent 1a6a370 commit 75de97b

File tree

2 files changed

+61
-12
lines changed

2 files changed

+61
-12
lines changed

Sources/PackageCollections/PackageCollections.swift

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,34 +81,38 @@ public struct PackageCollections: PackageCollectionsProtocol {
8181
case .failure(let error):
8282
callback(.failure(error))
8383
case .success(let sources):
84-
let identifiers = sources.map { .init(from: $0) }.filter { identifiers?.contains($0) ?? true }
85-
if identifiers.isEmpty {
86-
return callback(.success([]))
84+
let identiferSource = sources.reduce(into: [PackageCollectionsModel.CollectionIdentifier: PackageCollectionsModel.CollectionSource]()) { result, source in
85+
result[.init(from: source)] = source
8786
}
88-
let collectionOrder = identifiers.enumerated().reduce([Model.CollectionIdentifier: Int]()) { partial, element in
89-
var dictionary = partial
90-
dictionary[element.element] = element.offset
91-
return dictionary
87+
let identifiersToFetch = identiferSource.keys.filter { identifiers?.contains($0) ?? true }
88+
89+
if identifiersToFetch.isEmpty {
90+
return callback(.success([]))
9291
}
93-
self.storage.collections.list(identifiers: identifiers) { result in
92+
93+
self.storage.collections.list(identifiers: identifiersToFetch) { result in
9494
switch result {
9595
case .failure(let error):
9696
callback(.failure(error))
9797
case .success(var collections):
98+
let sourceOrder = sources.enumerated().reduce(into: [Model.CollectionIdentifier: Int]()) { result, item in
99+
result[.init(from: item.element)] = item.offset
100+
}
101+
98102
// re-order by profile order which reflects the user's election
99103
let sort = { (lhs: PackageCollectionsModel.Collection, rhs: PackageCollectionsModel.Collection) -> Bool in
100-
collectionOrder[lhs.identifier] ?? 0 < collectionOrder[rhs.identifier] ?? 0
104+
sourceOrder[lhs.identifier] ?? 0 < sourceOrder[rhs.identifier] ?? 0
101105
}
102106

103-
// We've fetched all the configured collections and we're done
104-
if collections.count == sources.count {
107+
// We've fetched all the wanted collections and we're done
108+
if collections.count == identifiersToFetch.count {
105109
collections.sort(by: sort)
106110
return callback(.success(collections))
107111
}
108112

109113
// Some of the results are missing. This happens when deserialization of stored collections fail,
110114
// so we will try refreshing the missing collections to update data in storage.
111-
let missingSources = Set(sources).subtracting(Set(collections.map { $0.source }))
115+
let missingSources = Set(identifiersToFetch.compactMap { identiferSource[$0] }).subtracting(Set(collections.map { $0.source }))
112116
let refreshResults = ThreadSafeArrayStore<Result<Model.Collection, Error>>()
113117
missingSources.forEach { source in
114118
self.refreshCollectionFromSource(source: source, trustConfirmationProvider: nil) { refreshResult in

Tests/PackageCollectionsTests/PackageCollectionsTests.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,51 @@ final class PackageCollectionsTests: XCTestCase {
567567
}
568568
}
569569

570+
func testList() throws {
571+
try skipIfUnsupportedPlatform()
572+
573+
let configuration = PackageCollections.Configuration()
574+
let storage = makeMockStorage()
575+
defer { XCTAssertNoThrow(try storage.close()) }
576+
577+
let mockCollections = makeMockCollections(count: 10)
578+
let mockPackage = mockCollections.last!.packages.last!
579+
let mockMetadata = makeMockPackageBasicMetadata()
580+
let collectionProviders = [PackageCollectionsModel.CollectionSourceType.json: MockCollectionsProvider(mockCollections)]
581+
let metadataProvider = MockMetadataProvider([mockPackage.reference: mockMetadata])
582+
let packageCollections = PackageCollections(configuration: configuration, storage: storage, collectionProviders: collectionProviders, metadataProvider: metadataProvider)
583+
584+
try mockCollections.forEach { collection in
585+
_ = try tsc_await { callback in packageCollections.addCollection(collection.source, trustConfirmationProvider: { _, cb in cb(true) }, callback: callback) }
586+
}
587+
588+
let list = try tsc_await { callback in packageCollections.listCollections(callback: callback) }
589+
XCTAssertEqual(list.count, mockCollections.count, "list count should match")
590+
}
591+
592+
func testListSubset() throws {
593+
try skipIfUnsupportedPlatform()
594+
595+
let configuration = PackageCollections.Configuration()
596+
let storage = makeMockStorage()
597+
defer { XCTAssertNoThrow(try storage.close()) }
598+
599+
let mockCollections = makeMockCollections(count: 10)
600+
let mockPackage = mockCollections.last!.packages.last!
601+
let mockMetadata = makeMockPackageBasicMetadata()
602+
let collectionProviders = [PackageCollectionsModel.CollectionSourceType.json: MockCollectionsProvider(mockCollections)]
603+
let metadataProvider = MockMetadataProvider([mockPackage.reference: mockMetadata])
604+
let packageCollections = PackageCollections(configuration: configuration, storage: storage, collectionProviders: collectionProviders, metadataProvider: metadataProvider)
605+
606+
try mockCollections.forEach { collection in
607+
_ = try tsc_await { callback in packageCollections.addCollection(collection.source, trustConfirmationProvider: { _, cb in cb(true) }, callback: callback) }
608+
}
609+
610+
let expectedCollections = Set([mockCollections.first!.identifier, mockCollections.last!.identifier])
611+
let list = try tsc_await { callback in packageCollections.listCollections(identifiers: expectedCollections, callback: callback) }
612+
XCTAssertEqual(list.count, expectedCollections.count, "list count should match")
613+
}
614+
570615
func testListPerformance() throws {
571616
#if ENABLE_COLLECTION_PERF_TESTS
572617
#else

0 commit comments

Comments
 (0)