diff --git a/Authorization/AuthorizationTests/AuthorizationMock.generated.swift b/Authorization/AuthorizationTests/AuthorizationMock.generated.swift index 00af73a93..548eb1d87 100644 --- a/Authorization/AuthorizationTests/AuthorizationMock.generated.swift +++ b/Authorization/AuthorizationTests/AuthorizationMock.generated.swift @@ -2789,171 +2789,154 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { open func set(userId: Int) { addInvocation(.m_set__userId_userId(Parameter.value(`userId`))) - let perform = methodPerformValue(.m_set__userId_userId(Parameter.value(`userId`))) as? (Int) -> Void - perform?(`userId`) + let perform = methodPerformValue(.m_set__userId_userId(Parameter.value(`userId`))) as? (Int) -> Void + perform?(`userId`) } open func getUserID() -> Int? { addInvocation(.m_getUserID) - let perform = methodPerformValue(.m_getUserID) as? () -> Void - perform?() - var __value: Int? = nil - do { - __value = try methodReturnValue(.m_getUserID).casted() - } catch { - // do nothing - } - return __value + let perform = methodPerformValue(.m_getUserID) as? () -> Void + perform?() + var __value: Int? = nil + do { + __value = try methodReturnValue(.m_getUserID).casted() + } catch { + // do nothing + } + return __value } - open func publisher() -> AnyPublisher { + @MainActor + open func publisher() throws -> AnyPublisher { addInvocation(.m_publisher) - let perform = methodPerformValue(.m_publisher) as? () -> Void - perform?() - var __value: AnyPublisher - do { - __value = try methodReturnValue(.m_publisher).casted() - } catch { - onFatalFailure("Stub return value not specified for publisher(). Use given") - Failure("Stub return value not specified for publisher(). Use given") - } - return __value + let perform = methodPerformValue(.m_publisher) as? () -> Void + perform?() + var __value: AnyPublisher + do { + __value = try methodReturnValue(.m_publisher).casted() + } catch MockError.notStubed { + onFatalFailure("Stub return value not specified for publisher(). Use given") + Failure("Stub return value not specified for publisher(). Use given") + } catch { + throw error + } + return __value } open func addToDownloadQueue(tasks: [DownloadDataTask]) { addInvocation(.m_addToDownloadQueue__tasks_tasks(Parameter<[DownloadDataTask]>.value(`tasks`))) - let perform = methodPerformValue(.m_addToDownloadQueue__tasks_tasks(Parameter<[DownloadDataTask]>.value(`tasks`))) as? ([DownloadDataTask]) -> Void - perform?(`tasks`) + let perform = methodPerformValue(.m_addToDownloadQueue__tasks_tasks(Parameter<[DownloadDataTask]>.value(`tasks`))) as? ([DownloadDataTask]) -> Void + perform?(`tasks`) } open func saveOfflineProgress(progress: OfflineProgress) { addInvocation(.m_saveOfflineProgress__progress_progress(Parameter.value(`progress`))) - let perform = methodPerformValue(.m_saveOfflineProgress__progress_progress(Parameter.value(`progress`))) as? (OfflineProgress) -> Void - perform?(`progress`) + let perform = methodPerformValue(.m_saveOfflineProgress__progress_progress(Parameter.value(`progress`))) as? (OfflineProgress) -> Void + perform?(`progress`) } open func loadProgress(for blockID: String) -> OfflineProgress? { addInvocation(.m_loadProgress__for_blockID(Parameter.value(`blockID`))) - let perform = methodPerformValue(.m_loadProgress__for_blockID(Parameter.value(`blockID`))) as? (String) -> Void - perform?(`blockID`) - var __value: OfflineProgress? = nil - do { - __value = try methodReturnValue(.m_loadProgress__for_blockID(Parameter.value(`blockID`))).casted() - } catch { - // do nothing - } - return __value + let perform = methodPerformValue(.m_loadProgress__for_blockID(Parameter.value(`blockID`))) as? (String) -> Void + perform?(`blockID`) + var __value: OfflineProgress? = nil + do { + __value = try methodReturnValue(.m_loadProgress__for_blockID(Parameter.value(`blockID`))).casted() + } catch { + // do nothing + } + return __value } open func loadAllOfflineProgress() -> [OfflineProgress] { addInvocation(.m_loadAllOfflineProgress) - let perform = methodPerformValue(.m_loadAllOfflineProgress) as? () -> Void - perform?() - var __value: [OfflineProgress] - do { - __value = try methodReturnValue(.m_loadAllOfflineProgress).casted() - } catch { - onFatalFailure("Stub return value not specified for loadAllOfflineProgress(). Use given") - Failure("Stub return value not specified for loadAllOfflineProgress(). Use given") - } - return __value + let perform = methodPerformValue(.m_loadAllOfflineProgress) as? () -> Void + perform?() + var __value: [OfflineProgress] + do { + __value = try methodReturnValue(.m_loadAllOfflineProgress).casted() + } catch { + onFatalFailure("Stub return value not specified for loadAllOfflineProgress(). Use given") + Failure("Stub return value not specified for loadAllOfflineProgress(). Use given") + } + return __value } open func deleteProgress(for blockID: String) { addInvocation(.m_deleteProgress__for_blockID(Parameter.value(`blockID`))) - let perform = methodPerformValue(.m_deleteProgress__for_blockID(Parameter.value(`blockID`))) as? (String) -> Void - perform?(`blockID`) + let perform = methodPerformValue(.m_deleteProgress__for_blockID(Parameter.value(`blockID`))) as? (String) -> Void + perform?(`blockID`) } open func deleteAllProgress() { addInvocation(.m_deleteAllProgress) - let perform = methodPerformValue(.m_deleteAllProgress) as? () -> Void - perform?() + let perform = methodPerformValue(.m_deleteAllProgress) as? () -> Void + perform?() } open func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality) { addInvocation(.m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`downloadQuality`))) - let perform = methodPerformValue(.m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`downloadQuality`))) as? ([CourseBlock], DownloadQuality) -> Void - perform?(`blocks`, `downloadQuality`) - } - - open func nextBlockForDownloading() -> DownloadDataTask? { - addInvocation(.m_nextBlockForDownloading) - let perform = methodPerformValue(.m_nextBlockForDownloading) as? () -> Void - perform?() - var __value: DownloadDataTask? = nil - do { - __value = try methodReturnValue(.m_nextBlockForDownloading).casted() - } catch { - // do nothing - } - return __value + let perform = methodPerformValue(.m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`downloadQuality`))) as? ([CourseBlock], DownloadQuality) -> Void + perform?(`blocks`, `downloadQuality`) } - open func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) { - addInvocation(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) - let perform = methodPerformValue(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) as? (String, DownloadState, Data?) -> Void - perform?(`id`, `state`, `resumeData`) - } - - open func deleteDownloadDataTask(id: String) throws { - addInvocation(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) - let perform = methodPerformValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) as? (String) -> Void - perform?(`id`) - do { - _ = try methodReturnValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } + open func updateTask(task: DownloadDataTask) { + addInvocation(.m_updateTask__task_task(Parameter.value(`task`))) + let perform = methodPerformValue(.m_updateTask__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + perform?(`task`) } open func saveDownloadDataTask(_ task: DownloadDataTask) { addInvocation(.m_saveDownloadDataTask__task(Parameter.value(`task`))) - let perform = methodPerformValue(.m_saveDownloadDataTask__task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void - perform?(`task`) + let perform = methodPerformValue(.m_saveDownloadDataTask__task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + perform?(`task`) } open func downloadDataTask(for blockId: String) -> DownloadDataTask? { addInvocation(.m_downloadDataTask__for_blockId(Parameter.value(`blockId`))) - let perform = methodPerformValue(.m_downloadDataTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void - perform?(`blockId`) - var __value: DownloadDataTask? = nil - do { - __value = try methodReturnValue(.m_downloadDataTask__for_blockId(Parameter.value(`blockId`))).casted() - } catch { - // do nothing - } - return __value + let perform = methodPerformValue(.m_downloadDataTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_downloadDataTask__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value } open func getDownloadDataTasks() -> [DownloadDataTask] { addInvocation(.m_getDownloadDataTasks) - let perform = methodPerformValue(.m_getDownloadDataTasks) as? () -> Void - perform?() - var __value: [DownloadDataTask] - do { - __value = try methodReturnValue(.m_getDownloadDataTasks).casted() - } catch { - onFatalFailure("Stub return value not specified for getDownloadDataTasks(). Use given") - Failure("Stub return value not specified for getDownloadDataTasks(). Use given") - } - return __value + let perform = methodPerformValue(.m_getDownloadDataTasks) as? () -> Void + perform?() + var __value: [DownloadDataTask] + do { + __value = try methodReturnValue(.m_getDownloadDataTasks).casted() + } catch { + onFatalFailure("Stub return value not specified for getDownloadDataTasks(). Use given") + Failure("Stub return value not specified for getDownloadDataTasks(). Use given") + } + return __value } open func getDownloadDataTasksForCourse(_ courseId: String) -> [DownloadDataTask] { addInvocation(.m_getDownloadDataTasksForCourse__courseId(Parameter.value(`courseId`))) - let perform = methodPerformValue(.m_getDownloadDataTasksForCourse__courseId(Parameter.value(`courseId`))) as? (String) -> Void - perform?(`courseId`) - var __value: [DownloadDataTask] - do { - __value = try methodReturnValue(.m_getDownloadDataTasksForCourse__courseId(Parameter.value(`courseId`))).casted() - } catch { - onFatalFailure("Stub return value not specified for getDownloadDataTasksForCourse(_ courseId: String). Use given") - Failure("Stub return value not specified for getDownloadDataTasksForCourse(_ courseId: String). Use given") - } - return __value + let perform = methodPerformValue(.m_getDownloadDataTasksForCourse__courseId(Parameter.value(`courseId`))) as? (String) -> Void + perform?(`courseId`) + var __value: [DownloadDataTask] + do { + __value = try methodReturnValue(.m_getDownloadDataTasksForCourse__courseId(Parameter.value(`courseId`))).casted() + } catch { + onFatalFailure("Stub return value not specified for getDownloadDataTasksForCourse(_ courseId: String). Use given") + Failure("Stub return value not specified for getDownloadDataTasksForCourse(_ courseId: String). Use given") + } + return __value + } + + open func deleteDownloadDataTasks(ids: [String]) { + addInvocation(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) + let perform = methodPerformValue(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) as? ([String]) -> Void + perform?(`ids`) } @@ -2968,85 +2951,80 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case m_deleteProgress__for_blockID(Parameter) case m_deleteAllProgress case m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>, Parameter) - case m_nextBlockForDownloading - case m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter, Parameter, Parameter) - case m_deleteDownloadDataTask__id_id(Parameter) + case m_updateTask__task_task(Parameter) case m_saveDownloadDataTask__task(Parameter) case m_downloadDataTask__for_blockId(Parameter) case m_getDownloadDataTasks case m_getDownloadDataTasksForCourse__courseId(Parameter) + case m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { case (.m_set__userId_userId(let lhsUserid), .m_set__userId_userId(let rhsUserid)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsUserid, rhs: rhsUserid, with: matcher), lhsUserid, rhsUserid, "userId")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsUserid, rhs: rhsUserid, with: matcher), lhsUserid, rhsUserid, "userId")) + return Matcher.ComparisonResult(results) case (.m_getUserID, .m_getUserID): return .match case (.m_publisher, .m_publisher): return .match case (.m_addToDownloadQueue__tasks_tasks(let lhsTasks), .m_addToDownloadQueue__tasks_tasks(let rhsTasks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTasks, rhs: rhsTasks, with: matcher), lhsTasks, rhsTasks, "tasks")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTasks, rhs: rhsTasks, with: matcher), lhsTasks, rhsTasks, "tasks")) + return Matcher.ComparisonResult(results) case (.m_saveOfflineProgress__progress_progress(let lhsProgress), .m_saveOfflineProgress__progress_progress(let rhsProgress)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsProgress, rhs: rhsProgress, with: matcher), lhsProgress, rhsProgress, "progress")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsProgress, rhs: rhsProgress, with: matcher), lhsProgress, rhsProgress, "progress")) + return Matcher.ComparisonResult(results) case (.m_loadProgress__for_blockID(let lhsBlockid), .m_loadProgress__for_blockID(let rhsBlockid)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockID")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockID")) + return Matcher.ComparisonResult(results) case (.m_loadAllOfflineProgress, .m_loadAllOfflineProgress): return .match case (.m_deleteProgress__for_blockID(let lhsBlockid), .m_deleteProgress__for_blockID(let rhsBlockid)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockID")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockID")) + return Matcher.ComparisonResult(results) case (.m_deleteAllProgress, .m_deleteAllProgress): return .match case (.m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(let lhsBlocks, let lhsDownloadquality), .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(let rhsBlocks, let rhsDownloadquality)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsDownloadquality, rhs: rhsDownloadquality, with: matcher), lhsDownloadquality, rhsDownloadquality, "downloadQuality")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsDownloadquality, rhs: rhsDownloadquality, with: matcher), lhsDownloadquality, rhsDownloadquality, "downloadQuality")) + return Matcher.ComparisonResult(results) - case (.m_nextBlockForDownloading, .m_nextBlockForDownloading): return .match - - case (.m_updateDownloadState__id_idstate_stateresumeData_resumeData(let lhsId, let lhsState, let lhsResumedata), .m_updateDownloadState__id_idstate_stateresumeData_resumeData(let rhsId, let rhsState, let rhsResumedata)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsState, rhs: rhsState, with: matcher), lhsState, rhsState, "state")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsResumedata, rhs: rhsResumedata, with: matcher), lhsResumedata, rhsResumedata, "resumeData")) - return Matcher.ComparisonResult(results) - - case (.m_deleteDownloadDataTask__id_id(let lhsId), .m_deleteDownloadDataTask__id_id(let rhsId)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - return Matcher.ComparisonResult(results) + case (.m_updateTask__task_task(let lhsTask), .m_updateTask__task_task(let rhsTask)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) + return Matcher.ComparisonResult(results) case (.m_saveDownloadDataTask__task(let lhsTask), .m_saveDownloadDataTask__task(let rhsTask)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "_ task")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "_ task")) + return Matcher.ComparisonResult(results) case (.m_downloadDataTask__for_blockId(let lhsBlockid), .m_downloadDataTask__for_blockId(let rhsBlockid)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) case (.m_getDownloadDataTasks, .m_getDownloadDataTasks): return .match case (.m_getDownloadDataTasksForCourse__courseId(let lhsCourseid), .m_getDownloadDataTasksForCourse__courseId(let rhsCourseid)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) + return Matcher.ComparisonResult(results) + + case (.m_deleteDownloadDataTasks__ids_ids(let lhsIds), .m_deleteDownloadDataTasks__ids_ids(let rhsIds)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsIds, rhs: rhsIds, with: matcher), lhsIds, rhsIds, "ids")) + return Matcher.ComparisonResult(results) default: return .none } } @@ -3063,13 +3041,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case let .m_deleteProgress__for_blockID(p0): return p0.intValue case .m_deleteAllProgress: return 0 case let .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(p0, p1): return p0.intValue + p1.intValue - case .m_nextBlockForDownloading: return 0 - case let .m_updateDownloadState__id_idstate_stateresumeData_resumeData(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue - case let .m_deleteDownloadDataTask__id_id(p0): return p0.intValue + case let .m_updateTask__task_task(p0): return p0.intValue case let .m_saveDownloadDataTask__task(p0): return p0.intValue case let .m_downloadDataTask__for_blockId(p0): return p0.intValue case .m_getDownloadDataTasks: return 0 case let .m_getDownloadDataTasksForCourse__courseId(p0): return p0.intValue + case let .m_deleteDownloadDataTasks__ids_ids(p0): return p0.intValue } } func assertionName() -> String { @@ -3084,13 +3061,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case .m_deleteProgress__for_blockID: return ".deleteProgress(for:)" case .m_deleteAllProgress: return ".deleteAllProgress()" case .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality: return ".addToDownloadQueue(blocks:downloadQuality:)" - case .m_nextBlockForDownloading: return ".nextBlockForDownloading()" - case .m_updateDownloadState__id_idstate_stateresumeData_resumeData: return ".updateDownloadState(id:state:resumeData:)" - case .m_deleteDownloadDataTask__id_id: return ".deleteDownloadDataTask(id:)" + case .m_updateTask__task_task: return ".updateTask(task:)" case .m_saveDownloadDataTask__task: return ".saveDownloadDataTask(_:)" case .m_downloadDataTask__for_blockId: return ".downloadDataTask(for:)" case .m_getDownloadDataTasks: return ".getDownloadDataTasks()" case .m_getDownloadDataTasksForCourse__courseId: return ".getDownloadDataTasksForCourse(_:)" + case .m_deleteDownloadDataTasks__ids_ids: return ".deleteDownloadDataTasks(ids:)" } } } @@ -3107,6 +3083,7 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getUserID(willReturn: Int?...) -> MethodStub { return Given(method: .m_getUserID, products: willReturn.map({ StubProduct.return($0 as Any) })) } + @MainActor public static func publisher(willReturn: AnyPublisher...) -> MethodStub { return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) } @@ -3116,9 +3093,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func loadAllOfflineProgress(willReturn: [OfflineProgress]...) -> MethodStub { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func nextBlockForDownloading(willReturn: DownloadDataTask?...) -> MethodStub { - return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func downloadDataTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } @@ -3130,69 +3104,57 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { } public static func getUserID(willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [Int?] = [] - let given: Given = { return Given(method: .m_getUserID, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (Int?).self) - willProduce(stubber) - return given - } - public static func publisher(willProduce: (Stubber>) -> Void) -> MethodStub { - let willReturn: [AnyPublisher] = [] - let given: Given = { return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (AnyPublisher).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_getUserID, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (Int?).self) + willProduce(stubber) + return given } public static func loadProgress(for blockID: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [OfflineProgress?] = [] - let given: Given = { return Given(method: .m_loadProgress__for_blockID(`blockID`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (OfflineProgress?).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_loadProgress__for_blockID(`blockID`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (OfflineProgress?).self) + willProduce(stubber) + return given } public static func loadAllOfflineProgress(willProduce: (Stubber<[OfflineProgress]>) -> Void) -> MethodStub { let willReturn: [[OfflineProgress]] = [] - let given: Given = { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([OfflineProgress]).self) - willProduce(stubber) - return given - } - public static func nextBlockForDownloading(willProduce: (Stubber) -> Void) -> MethodStub { - let willReturn: [DownloadDataTask?] = [] - let given: Given = { return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (DownloadDataTask?).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: ([OfflineProgress]).self) + willProduce(stubber) + return given } public static func downloadDataTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [DownloadDataTask?] = [] - let given: Given = { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (DownloadDataTask?).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given } public static func getDownloadDataTasks(willProduce: (Stubber<[DownloadDataTask]>) -> Void) -> MethodStub { let willReturn: [[DownloadDataTask]] = [] - let given: Given = { return Given(method: .m_getDownloadDataTasks, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([DownloadDataTask]).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_getDownloadDataTasks, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: ([DownloadDataTask]).self) + willProduce(stubber) + return given } public static func getDownloadDataTasksForCourse(_ courseId: Parameter, willProduce: (Stubber<[DownloadDataTask]>) -> Void) -> MethodStub { let willReturn: [[DownloadDataTask]] = [] - let given: Given = { return Given(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([DownloadDataTask]).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: ([DownloadDataTask]).self) + willProduce(stubber) + return given } - public static func deleteDownloadDataTask(id: Parameter, willThrow: Error...) -> MethodStub { - return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) + @MainActor + public static func publisher(willThrow: Error...) -> MethodStub { + return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) } - public static func deleteDownloadDataTask(id: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { + @MainActor + public static func publisher(willProduce: (StubberThrows>) -> Void) -> MethodStub { let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) }() + let stubber = given.stubThrows(for: (AnyPublisher).self) + willProduce(stubber) + return given } } @@ -3201,6 +3163,7 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func set(userId: Parameter) -> Verify { return Verify(method: .m_set__userId_userId(`userId`))} public static func getUserID() -> Verify { return Verify(method: .m_getUserID)} + @MainActor public static func publisher() -> Verify { return Verify(method: .m_publisher)} public static func addToDownloadQueue(tasks: Parameter<[DownloadDataTask]>) -> Verify { return Verify(method: .m_addToDownloadQueue__tasks_tasks(`tasks`))} public static func saveOfflineProgress(progress: Parameter) -> Verify { return Verify(method: .m_saveOfflineProgress__progress_progress(`progress`))} @@ -3209,13 +3172,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func deleteProgress(for blockID: Parameter) -> Verify { return Verify(method: .m_deleteProgress__for_blockID(`blockID`))} public static func deleteAllProgress() -> Verify { return Verify(method: .m_deleteAllProgress)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`))} - public static func nextBlockForDownloading() -> Verify { return Verify(method: .m_nextBlockForDownloading)} - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter) -> Verify { return Verify(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`))} - public static func deleteDownloadDataTask(id: Parameter) -> Verify { return Verify(method: .m_deleteDownloadDataTask__id_id(`id`))} + public static func updateTask(task: Parameter) -> Verify { return Verify(method: .m_updateTask__task_task(`task`))} public static func saveDownloadDataTask(_ task: Parameter) -> Verify { return Verify(method: .m_saveDownloadDataTask__task(`task`))} public static func downloadDataTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadDataTask__for_blockId(`blockId`))} public static func getDownloadDataTasks() -> Verify { return Verify(method: .m_getDownloadDataTasks)} public static func getDownloadDataTasksForCourse(_ courseId: Parameter) -> Verify { return Verify(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`))} + public static func deleteDownloadDataTasks(ids: Parameter<[String]>) -> Verify { return Verify(method: .m_deleteDownloadDataTasks__ids_ids(`ids`))} } public struct Perform { @@ -3228,6 +3190,7 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getUserID(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_getUserID, performs: perform) } + @MainActor public static func publisher(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_publisher, performs: perform) } @@ -3252,14 +3215,8 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter, perform: @escaping ([CourseBlock], DownloadQuality) -> Void) -> Perform { return Perform(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`), performs: perform) } - public static func nextBlockForDownloading(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_nextBlockForDownloading, performs: perform) - } - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter, perform: @escaping (String, DownloadState, Data?) -> Void) -> Perform { - return Perform(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`), performs: perform) - } - public static func deleteDownloadDataTask(id: Parameter, perform: @escaping (String) -> Void) -> Perform { - return Perform(method: .m_deleteDownloadDataTask__id_id(`id`), performs: perform) + public static func updateTask(task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { + return Perform(method: .m_updateTask__task_task(`task`), performs: perform) } public static func saveDownloadDataTask(_ task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { return Perform(method: .m_saveDownloadDataTask__task(`task`), performs: perform) @@ -3273,6 +3230,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getDownloadDataTasksForCourse(_ courseId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), performs: perform) } + public static func deleteDownloadDataTasks(ids: Parameter<[String]>, perform: @escaping ([String]) -> Void) -> Perform { + return Perform(method: .m_deleteDownloadDataTasks__ids_ids(`ids`), performs: perform) + } } public func given(_ method: Given) { @@ -3348,6 +3308,7 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { } } + // MARK: - DownloadManagerProtocol open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { @@ -3388,211 +3349,204 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { if scopes.contains(.perform) { methodPerformValues = [] } } - public var currentDownloadTask: DownloadDataTask? { - get { invocations.append(.p_currentDownloadTask_get); return __p_currentDownloadTask ?? optionalGivenGetterValue(.p_currentDownloadTask_get, "DownloadManagerProtocolMock - stub value for currentDownloadTask was not defined") } - } - private var __p_currentDownloadTask: (DownloadDataTask)? - - open func publisher() -> AnyPublisher { - addInvocation(.m_publisher) - let perform = methodPerformValue(.m_publisher) as? () -> Void - perform?() - var __value: AnyPublisher - do { - __value = try methodReturnValue(.m_publisher).casted() - } catch { - onFatalFailure("Stub return value not specified for publisher(). Use given") - Failure("Stub return value not specified for publisher(). Use given") - } - return __value + open func getCurrentDownloadTask() -> DownloadDataTask? { + addInvocation(.m_getCurrentDownloadTask) + let perform = methodPerformValue(.m_getCurrentDownloadTask) as? () -> Void + perform?() + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_getCurrentDownloadTask).casted() + } catch { + // do nothing + } + return __value } open func eventPublisher() -> AnyPublisher { addInvocation(.m_eventPublisher) - let perform = methodPerformValue(.m_eventPublisher) as? () -> Void - perform?() - var __value: AnyPublisher - do { - __value = try methodReturnValue(.m_eventPublisher).casted() - } catch { - onFatalFailure("Stub return value not specified for eventPublisher(). Use given") - Failure("Stub return value not specified for eventPublisher(). Use given") - } - return __value + let perform = methodPerformValue(.m_eventPublisher) as? () -> Void + perform?() + var __value: AnyPublisher + do { + __value = try methodReturnValue(.m_eventPublisher).casted() + } catch { + onFatalFailure("Stub return value not specified for eventPublisher(). Use given") + Failure("Stub return value not specified for eventPublisher(). Use given") + } + return __value } open func addToDownloadQueue(blocks: [CourseBlock]) throws { addInvocation(.m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - do { - _ = try methodReturnValue(.m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } + let perform = methodPerformValue(.m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void + perform?(`blocks`) + do { + _ = try methodReturnValue(.m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))).casted() as Void + } catch MockError.notStubed { + // do nothing + } catch { + throw error + } } open func getDownloadTasks() -> [DownloadDataTask] { addInvocation(.m_getDownloadTasks) - let perform = methodPerformValue(.m_getDownloadTasks) as? () -> Void - perform?() - var __value: [DownloadDataTask] - do { - __value = try methodReturnValue(.m_getDownloadTasks).casted() - } catch { - onFatalFailure("Stub return value not specified for getDownloadTasks(). Use given") - Failure("Stub return value not specified for getDownloadTasks(). Use given") - } - return __value + let perform = methodPerformValue(.m_getDownloadTasks) as? () -> Void + perform?() + var __value: [DownloadDataTask] + do { + __value = try methodReturnValue(.m_getDownloadTasks).casted() + } catch { + onFatalFailure("Stub return value not specified for getDownloadTasks(). Use given") + Failure("Stub return value not specified for getDownloadTasks(). Use given") + } + return __value } open func getDownloadTasksForCourse(_ courseId: String) -> [DownloadDataTask] { addInvocation(.m_getDownloadTasksForCourse__courseId(Parameter.value(`courseId`))) - let perform = methodPerformValue(.m_getDownloadTasksForCourse__courseId(Parameter.value(`courseId`))) as? (String) -> Void - perform?(`courseId`) - var __value: [DownloadDataTask] - do { - __value = try methodReturnValue(.m_getDownloadTasksForCourse__courseId(Parameter.value(`courseId`))).casted() - } catch { - onFatalFailure("Stub return value not specified for getDownloadTasksForCourse(_ courseId: String). Use given") - Failure("Stub return value not specified for getDownloadTasksForCourse(_ courseId: String). Use given") - } - return __value + let perform = methodPerformValue(.m_getDownloadTasksForCourse__courseId(Parameter.value(`courseId`))) as? (String) -> Void + perform?(`courseId`) + var __value: [DownloadDataTask] + do { + __value = try methodReturnValue(.m_getDownloadTasksForCourse__courseId(Parameter.value(`courseId`))).casted() + } catch { + onFatalFailure("Stub return value not specified for getDownloadTasksForCourse(_ courseId: String). Use given") + Failure("Stub return value not specified for getDownloadTasksForCourse(_ courseId: String). Use given") + } + return __value } open func cancelDownloading(courseId: String, blocks: [CourseBlock]) throws { addInvocation(.m_cancelDownloading__courseId_courseIdblocks_blocks(Parameter.value(`courseId`), Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_cancelDownloading__courseId_courseIdblocks_blocks(Parameter.value(`courseId`), Parameter<[CourseBlock]>.value(`blocks`))) as? (String, [CourseBlock]) -> Void - perform?(`courseId`, `blocks`) - do { - _ = try methodReturnValue(.m_cancelDownloading__courseId_courseIdblocks_blocks(Parameter.value(`courseId`), Parameter<[CourseBlock]>.value(`blocks`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } + let perform = methodPerformValue(.m_cancelDownloading__courseId_courseIdblocks_blocks(Parameter.value(`courseId`), Parameter<[CourseBlock]>.value(`blocks`))) as? (String, [CourseBlock]) -> Void + perform?(`courseId`, `blocks`) + do { + _ = try methodReturnValue(.m_cancelDownloading__courseId_courseIdblocks_blocks(Parameter.value(`courseId`), Parameter<[CourseBlock]>.value(`blocks`))).casted() as Void + } catch MockError.notStubed { + // do nothing + } catch { + throw error + } } open func cancelDownloading(task: DownloadDataTask) throws { addInvocation(.m_cancelDownloading__task_task(Parameter.value(`task`))) - let perform = methodPerformValue(.m_cancelDownloading__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void - perform?(`task`) - do { - _ = try methodReturnValue(.m_cancelDownloading__task_task(Parameter.value(`task`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } + let perform = methodPerformValue(.m_cancelDownloading__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + perform?(`task`) + do { + _ = try methodReturnValue(.m_cancelDownloading__task_task(Parameter.value(`task`))).casted() as Void + } catch MockError.notStubed { + // do nothing + } catch { + throw error + } } open func cancelDownloading(courseId: String) throws { addInvocation(.m_cancelDownloading__courseId_courseId(Parameter.value(`courseId`))) - let perform = methodPerformValue(.m_cancelDownloading__courseId_courseId(Parameter.value(`courseId`))) as? (String) -> Void - perform?(`courseId`) - do { - _ = try methodReturnValue(.m_cancelDownloading__courseId_courseId(Parameter.value(`courseId`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } + let perform = methodPerformValue(.m_cancelDownloading__courseId_courseId(Parameter.value(`courseId`))) as? (String) -> Void + perform?(`courseId`) + do { + _ = try methodReturnValue(.m_cancelDownloading__courseId_courseId(Parameter.value(`courseId`))).casted() as Void + } catch MockError.notStubed { + // do nothing + } catch { + throw error + } } open func cancelAllDownloading() throws { addInvocation(.m_cancelAllDownloading) - let perform = methodPerformValue(.m_cancelAllDownloading) as? () -> Void - perform?() - do { - _ = try methodReturnValue(.m_cancelAllDownloading).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } + let perform = methodPerformValue(.m_cancelAllDownloading) as? () -> Void + perform?() + do { + _ = try methodReturnValue(.m_cancelAllDownloading).casted() as Void + } catch MockError.notStubed { + // do nothing + } catch { + throw error + } } - open func deleteFile(blocks: [CourseBlock]) { - addInvocation(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - } - - open func deleteAllFiles() { - addInvocation(.m_deleteAllFiles) - let perform = methodPerformValue(.m_deleteAllFiles) as? () -> Void - perform?() + open func deleteAll() { + addInvocation(.m_deleteAll) + let perform = methodPerformValue(.m_deleteAll) as? () -> Void + perform?() } open func fileUrl(for blockId: String) -> URL? { addInvocation(.m_fileUrl__for_blockId(Parameter.value(`blockId`))) - let perform = methodPerformValue(.m_fileUrl__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void - perform?(`blockId`) - var __value: URL? = nil - do { - __value = try methodReturnValue(.m_fileUrl__for_blockId(Parameter.value(`blockId`))).casted() - } catch { - // do nothing - } - return __value - } - - open func updateUnzippedFileSize(for sequentials: [CourseSequential]) -> [CourseSequential] { - addInvocation(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) - let perform = methodPerformValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) as? ([CourseSequential]) -> Void - perform?(`sequentials`) - var __value: [CourseSequential] - do { - __value = try methodReturnValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))).casted() - } catch { - onFatalFailure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - Failure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - } - return __value + let perform = methodPerformValue(.m_fileUrl__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: URL? = nil + do { + __value = try methodReturnValue(.m_fileUrl__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value } open func resumeDownloading() throws { addInvocation(.m_resumeDownloading) - let perform = methodPerformValue(.m_resumeDownloading) as? () -> Void - perform?() - do { - _ = try methodReturnValue(.m_resumeDownloading).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } + let perform = methodPerformValue(.m_resumeDownloading) as? () -> Void + perform?() + do { + _ = try methodReturnValue(.m_resumeDownloading).casted() as Void + } catch MockError.notStubed { + // do nothing + } catch { + throw error + } } open func isLargeVideosSize(blocks: [CourseBlock]) -> Bool { addInvocation(.m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - var __value: Bool - do { - __value = try methodReturnValue(.m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))).casted() - } catch { - onFatalFailure("Stub return value not specified for isLargeVideosSize(blocks: [CourseBlock]). Use given") - Failure("Stub return value not specified for isLargeVideosSize(blocks: [CourseBlock]). Use given") - } - return __value + let perform = methodPerformValue(.m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void + perform?(`blocks`) + var __value: Bool + do { + __value = try methodReturnValue(.m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))).casted() + } catch { + onFatalFailure("Stub return value not specified for isLargeVideosSize(blocks: [CourseBlock]). Use given") + Failure("Stub return value not specified for isLargeVideosSize(blocks: [CourseBlock]). Use given") + } + return __value } open func removeAppSupportDirectoryUnusedContent() { addInvocation(.m_removeAppSupportDirectoryUnusedContent) - let perform = methodPerformValue(.m_removeAppSupportDirectoryUnusedContent) as? () -> Void - perform?() + let perform = methodPerformValue(.m_removeAppSupportDirectoryUnusedContent) as? () -> Void + perform?() + } + + open func delete(blocks: [CourseBlock], courseId: String) { + addInvocation(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) + let perform = methodPerformValue(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) as? ([CourseBlock], String) -> Void + perform?(`blocks`, `courseId`) + } + + open func downloadTask(for blockId: String) -> DownloadDataTask? { + addInvocation(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) + let perform = methodPerformValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value } fileprivate enum MethodType { - case m_publisher + case m_getCurrentDownloadTask case m_eventPublisher case m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>) case m_getDownloadTasks @@ -3601,84 +3555,83 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case m_cancelDownloading__task_task(Parameter) case m_cancelDownloading__courseId_courseId(Parameter) case m_cancelAllDownloading - case m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>) - case m_deleteAllFiles + case m_deleteAll case m_fileUrl__for_blockId(Parameter) - case m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>) case m_resumeDownloading case m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>) case m_removeAppSupportDirectoryUnusedContent - case p_currentDownloadTask_get + case m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>, Parameter) + case m_downloadTask__for_blockId(Parameter) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { - case (.m_publisher, .m_publisher): return .match + case (.m_getCurrentDownloadTask, .m_getCurrentDownloadTask): return .match case (.m_eventPublisher, .m_eventPublisher): return .match case (.m_addToDownloadQueue__blocks_blocks(let lhsBlocks), .m_addToDownloadQueue__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + return Matcher.ComparisonResult(results) case (.m_getDownloadTasks, .m_getDownloadTasks): return .match case (.m_getDownloadTasksForCourse__courseId(let lhsCourseid), .m_getDownloadTasksForCourse__courseId(let rhsCourseid)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) + return Matcher.ComparisonResult(results) case (.m_cancelDownloading__courseId_courseIdblocks_blocks(let lhsCourseid, let lhsBlocks), .m_cancelDownloading__courseId_courseIdblocks_blocks(let rhsCourseid, let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + return Matcher.ComparisonResult(results) case (.m_cancelDownloading__task_task(let lhsTask), .m_cancelDownloading__task_task(let rhsTask)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) + return Matcher.ComparisonResult(results) case (.m_cancelDownloading__courseId_courseId(let lhsCourseid), .m_cancelDownloading__courseId_courseId(let rhsCourseid)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + return Matcher.ComparisonResult(results) case (.m_cancelAllDownloading, .m_cancelAllDownloading): return .match - case (.m_deleteFile__blocks_blocks(let lhsBlocks), .m_deleteFile__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) - - case (.m_deleteAllFiles, .m_deleteAllFiles): return .match + case (.m_deleteAll, .m_deleteAll): return .match case (.m_fileUrl__for_blockId(let lhsBlockid), .m_fileUrl__for_blockId(let rhsBlockid)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) - return Matcher.ComparisonResult(results) - - case (.m_updateUnzippedFileSize__for_sequentials(let lhsSequentials), .m_updateUnzippedFileSize__for_sequentials(let rhsSequentials)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequentials, rhs: rhsSequentials, with: matcher), lhsSequentials, rhsSequentials, "for sequentials")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) case (.m_resumeDownloading, .m_resumeDownloading): return .match case (.m_isLargeVideosSize__blocks_blocks(let lhsBlocks), .m_isLargeVideosSize__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + return Matcher.ComparisonResult(results) case (.m_removeAppSupportDirectoryUnusedContent, .m_removeAppSupportDirectoryUnusedContent): return .match - case (.p_currentDownloadTask_get,.p_currentDownloadTask_get): return Matcher.ComparisonResult.match + + case (.m_delete__blocks_blockscourseId_courseId(let lhsBlocks, let lhsCourseid), .m_delete__blocks_blockscourseId_courseId(let rhsBlocks, let rhsCourseid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + return Matcher.ComparisonResult(results) + + case (.m_downloadTask__for_blockId(let lhsBlockid), .m_downloadTask__for_blockId(let rhsBlockid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) default: return .none } } func intValue() -> Int { switch self { - case .m_publisher: return 0 + case .m_getCurrentDownloadTask: return 0 case .m_eventPublisher: return 0 case let .m_addToDownloadQueue__blocks_blocks(p0): return p0.intValue case .m_getDownloadTasks: return 0 @@ -3687,19 +3640,18 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case let .m_cancelDownloading__task_task(p0): return p0.intValue case let .m_cancelDownloading__courseId_courseId(p0): return p0.intValue case .m_cancelAllDownloading: return 0 - case let .m_deleteFile__blocks_blocks(p0): return p0.intValue - case .m_deleteAllFiles: return 0 + case .m_deleteAll: return 0 case let .m_fileUrl__for_blockId(p0): return p0.intValue - case let .m_updateUnzippedFileSize__for_sequentials(p0): return p0.intValue case .m_resumeDownloading: return 0 case let .m_isLargeVideosSize__blocks_blocks(p0): return p0.intValue case .m_removeAppSupportDirectoryUnusedContent: return 0 - case .p_currentDownloadTask_get: return 0 + case let .m_delete__blocks_blockscourseId_courseId(p0, p1): return p0.intValue + p1.intValue + case let .m_downloadTask__for_blockId(p0): return p0.intValue } } func assertionName() -> String { switch self { - case .m_publisher: return ".publisher()" + case .m_getCurrentDownloadTask: return ".getCurrentDownloadTask()" case .m_eventPublisher: return ".eventPublisher()" case .m_addToDownloadQueue__blocks_blocks: return ".addToDownloadQueue(blocks:)" case .m_getDownloadTasks: return ".getDownloadTasks()" @@ -3708,14 +3660,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case .m_cancelDownloading__task_task: return ".cancelDownloading(task:)" case .m_cancelDownloading__courseId_courseId: return ".cancelDownloading(courseId:)" case .m_cancelAllDownloading: return ".cancelAllDownloading()" - case .m_deleteFile__blocks_blocks: return ".deleteFile(blocks:)" - case .m_deleteAllFiles: return ".deleteAllFiles()" + case .m_deleteAll: return ".deleteAll()" case .m_fileUrl__for_blockId: return ".fileUrl(for:)" - case .m_updateUnzippedFileSize__for_sequentials: return ".updateUnzippedFileSize(for:)" case .m_resumeDownloading: return ".resumeDownloading()" case .m_isLargeVideosSize__blocks_blocks: return ".isLargeVideosSize(blocks:)" case .m_removeAppSupportDirectoryUnusedContent: return ".removeAppSupportDirectoryUnusedContent()" - case .p_currentDownloadTask_get: return "[get] .currentDownloadTask" + case .m_delete__blocks_blockscourseId_courseId: return ".delete(blocks:courseId:)" + case .m_downloadTask__for_blockId: return ".downloadTask(for:)" } } } @@ -3728,12 +3679,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { super.init(products) } - public static func currentDownloadTask(getter defaultValue: DownloadDataTask?...) -> PropertyStub { - return Given(method: .p_currentDownloadTask_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) - } - public static func publisher(willReturn: AnyPublisher...) -> MethodStub { - return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + public static func getCurrentDownloadTask(willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) } public static func eventPublisher(willReturn: AnyPublisher...) -> MethodStub { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) @@ -3747,127 +3695,127 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func fileUrl(for blockId: Parameter, willReturn: URL?...) -> MethodStub { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willReturn: [CourseSequential]...) -> MethodStub { - return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willReturn: Bool...) -> MethodStub { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func publisher(willProduce: (Stubber>) -> Void) -> MethodStub { - let willReturn: [AnyPublisher] = [] - let given: Given = { return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (AnyPublisher).self) - willProduce(stubber) - return given + public static func downloadTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func getCurrentDownloadTask(willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given } public static func eventPublisher(willProduce: (Stubber>) -> Void) -> MethodStub { let willReturn: [AnyPublisher] = [] - let given: Given = { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (AnyPublisher).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (AnyPublisher).self) + willProduce(stubber) + return given } public static func getDownloadTasks(willProduce: (Stubber<[DownloadDataTask]>) -> Void) -> MethodStub { let willReturn: [[DownloadDataTask]] = [] - let given: Given = { return Given(method: .m_getDownloadTasks, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([DownloadDataTask]).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_getDownloadTasks, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: ([DownloadDataTask]).self) + willProduce(stubber) + return given } public static func getDownloadTasksForCourse(_ courseId: Parameter, willProduce: (Stubber<[DownloadDataTask]>) -> Void) -> MethodStub { let willReturn: [[DownloadDataTask]] = [] - let given: Given = { return Given(method: .m_getDownloadTasksForCourse__courseId(`courseId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([DownloadDataTask]).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_getDownloadTasksForCourse__courseId(`courseId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: ([DownloadDataTask]).self) + willProduce(stubber) + return given } public static func fileUrl(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [URL?] = [] - let given: Given = { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (URL?).self) - willProduce(stubber) - return given - } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willProduce: (Stubber<[CourseSequential]>) -> Void) -> MethodStub { - let willReturn: [[CourseSequential]] = [] - let given: Given = { return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([CourseSequential]).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (URL?).self) + willProduce(stubber) + return given } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [Bool] = [] - let given: Given = { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (Bool).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (Bool).self) + willProduce(stubber) + return given + } + public static func downloadTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given } public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, willThrow: Error...) -> MethodStub { return Given(method: .m_addToDownloadQueue__blocks_blocks(`blocks`), products: willThrow.map({ StubProduct.throw($0) })) } public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, willProduce: (StubberThrows) -> Void) -> MethodStub { let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_addToDownloadQueue__blocks_blocks(`blocks`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_addToDownloadQueue__blocks_blocks(`blocks`), products: willThrow.map({ StubProduct.throw($0) })) }() + let stubber = given.stubThrows(for: (Void).self) + willProduce(stubber) + return given } public static func cancelDownloading(courseId: Parameter, blocks: Parameter<[CourseBlock]>, willThrow: Error...) -> MethodStub { return Given(method: .m_cancelDownloading__courseId_courseIdblocks_blocks(`courseId`, `blocks`), products: willThrow.map({ StubProduct.throw($0) })) } public static func cancelDownloading(courseId: Parameter, blocks: Parameter<[CourseBlock]>, willProduce: (StubberThrows) -> Void) -> MethodStub { let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_cancelDownloading__courseId_courseIdblocks_blocks(`courseId`, `blocks`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_cancelDownloading__courseId_courseIdblocks_blocks(`courseId`, `blocks`), products: willThrow.map({ StubProduct.throw($0) })) }() + let stubber = given.stubThrows(for: (Void).self) + willProduce(stubber) + return given } public static func cancelDownloading(task: Parameter, willThrow: Error...) -> MethodStub { return Given(method: .m_cancelDownloading__task_task(`task`), products: willThrow.map({ StubProduct.throw($0) })) } public static func cancelDownloading(task: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_cancelDownloading__task_task(`task`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_cancelDownloading__task_task(`task`), products: willThrow.map({ StubProduct.throw($0) })) }() + let stubber = given.stubThrows(for: (Void).self) + willProduce(stubber) + return given } public static func cancelDownloading(courseId: Parameter, willThrow: Error...) -> MethodStub { return Given(method: .m_cancelDownloading__courseId_courseId(`courseId`), products: willThrow.map({ StubProduct.throw($0) })) } public static func cancelDownloading(courseId: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_cancelDownloading__courseId_courseId(`courseId`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_cancelDownloading__courseId_courseId(`courseId`), products: willThrow.map({ StubProduct.throw($0) })) }() + let stubber = given.stubThrows(for: (Void).self) + willProduce(stubber) + return given } public static func cancelAllDownloading(willThrow: Error...) -> MethodStub { return Given(method: .m_cancelAllDownloading, products: willThrow.map({ StubProduct.throw($0) })) } public static func cancelAllDownloading(willProduce: (StubberThrows) -> Void) -> MethodStub { let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_cancelAllDownloading, products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_cancelAllDownloading, products: willThrow.map({ StubProduct.throw($0) })) }() + let stubber = given.stubThrows(for: (Void).self) + willProduce(stubber) + return given } public static func resumeDownloading(willThrow: Error...) -> MethodStub { return Given(method: .m_resumeDownloading, products: willThrow.map({ StubProduct.throw($0) })) } public static func resumeDownloading(willProduce: (StubberThrows) -> Void) -> MethodStub { let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_resumeDownloading, products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given + let given: Given = { return Given(method: .m_resumeDownloading, products: willThrow.map({ StubProduct.throw($0) })) }() + let stubber = given.stubThrows(for: (Void).self) + willProduce(stubber) + return given } } public struct Verify { fileprivate var method: MethodType - public static func publisher() -> Verify { return Verify(method: .m_publisher)} + public static func getCurrentDownloadTask() -> Verify { return Verify(method: .m_getCurrentDownloadTask)} public static func eventPublisher() -> Verify { return Verify(method: .m_eventPublisher)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocks(`blocks`))} public static func getDownloadTasks() -> Verify { return Verify(method: .m_getDownloadTasks)} @@ -3876,22 +3824,21 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelDownloading(task: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__task_task(`task`))} public static func cancelDownloading(courseId: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__courseId_courseId(`courseId`))} public static func cancelAllDownloading() -> Verify { return Verify(method: .m_cancelAllDownloading)} - public static func deleteFile(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_deleteFile__blocks_blocks(`blocks`))} - public static func deleteAllFiles() -> Verify { return Verify(method: .m_deleteAllFiles)} + public static func deleteAll() -> Verify { return Verify(method: .m_deleteAll)} public static func fileUrl(for blockId: Parameter) -> Verify { return Verify(method: .m_fileUrl__for_blockId(`blockId`))} - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>) -> Verify { return Verify(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`))} public static func resumeDownloading() -> Verify { return Verify(method: .m_resumeDownloading)} public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_isLargeVideosSize__blocks_blocks(`blocks`))} public static func removeAppSupportDirectoryUnusedContent() -> Verify { return Verify(method: .m_removeAppSupportDirectoryUnusedContent)} - public static var currentDownloadTask: Verify { return Verify(method: .p_currentDownloadTask_get) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter) -> Verify { return Verify(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`))} + public static func downloadTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadTask__for_blockId(`blockId`))} } public struct Perform { fileprivate var method: MethodType var performs: Any - public static func publisher(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_publisher, performs: perform) + public static func getCurrentDownloadTask(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_getCurrentDownloadTask, performs: perform) } public static func eventPublisher(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_eventPublisher, performs: perform) @@ -3917,18 +3864,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelAllDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_cancelAllDownloading, performs: perform) } - public static func deleteFile(blocks: Parameter<[CourseBlock]>, perform: @escaping ([CourseBlock]) -> Void) -> Perform { - return Perform(method: .m_deleteFile__blocks_blocks(`blocks`), performs: perform) - } - public static func deleteAllFiles(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_deleteAllFiles, performs: perform) + public static func deleteAll(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_deleteAll, performs: perform) } public static func fileUrl(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_fileUrl__for_blockId(`blockId`), performs: perform) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, perform: @escaping ([CourseSequential]) -> Void) -> Perform { - return Perform(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), performs: perform) - } public static func resumeDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_resumeDownloading, performs: perform) } @@ -3938,6 +3879,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func removeAppSupportDirectoryUnusedContent(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_removeAppSupportDirectoryUnusedContent, performs: perform) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter, perform: @escaping ([CourseBlock], String) -> Void) -> Perform { + return Perform(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`), performs: perform) + } + public static func downloadTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { + return Perform(method: .m_downloadTask__for_blockId(`blockId`), performs: perform) + } } public func given(_ method: Given) { diff --git a/Core/Core/Analytics/CoreAnalytics.swift b/Core/Core/Analytics/CoreAnalytics.swift index 4a106e756..4f2d6935d 100644 --- a/Core/Core/Analytics/CoreAnalytics.swift +++ b/Core/Core/Analytics/CoreAnalytics.swift @@ -128,7 +128,9 @@ public enum AnalyticsEvent: String { case courseViewCertificateClicked = "Course:View Certificate Clicked" case bulkDownloadVideosToggle = "Video:Bulk Download Toggle" case bulkDownloadVideosSubsection = "Video:Bulk Download Subsection" + case bulkDownloadVideosSection = "Video:Bulk Download Section" case bulkDeleteVideosSubsection = "Videos:Delete Subsection Videos" + case bulkDeleteVideosSection = "Videos:Delete Section Videos" case videoLoaded = "Video:Loaded" case videoSpeedChange = "Video:Change Speed" case videoPlayed = "Video:Played" @@ -204,6 +206,8 @@ public enum EventBIValue: String { case bulkDownloadVideosToggle = "edx.bi.app.videos.download.toggle" case bulkDownloadVideosSubsection = "edx.bi.video.subsection.bulkdownload" case bulkDeleteVideosSubsection = "edx.bi.app.video.delete.subsection" + case bulkDownloadVideosSection = "edx.bi.video.section.bulkdownload" + case bulkDeleteVideosSection = "edx.bi.app.video.delete.section" case videoLoaded = "edx.bi.app.videos.loaded" case videoSpeedChange = "edx.bi.app.videos.speed.changed" case videoPlayed = "edx.bi.app.videos.played" diff --git a/Core/Core/Data/Persistence/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents b/Core/Core/Data/Persistence/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents index acc66b8db..7f6248642 100644 --- a/Core/Core/Data/Persistence/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents +++ b/Core/Core/Data/Persistence/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents @@ -1,11 +1,12 @@ - + + - + diff --git a/Core/Core/Data/Persistence/CorePersistenceProtocol.swift b/Core/Core/Data/Persistence/CorePersistenceProtocol.swift index 9aca9e9e8..44c299dbd 100644 --- a/Core/Core/Data/Persistence/CorePersistenceProtocol.swift +++ b/Core/Core/Data/Persistence/CorePersistenceProtocol.swift @@ -13,44 +13,39 @@ public protocol CorePersistenceProtocol: Sendable { func set(userId: Int) func getUserID() -> Int? @MainActor func publisher() throws -> AnyPublisher - func addToDownloadQueue(tasks: [DownloadDataTask]) async + func addToDownloadQueue(tasks: [DownloadDataTask]) func saveOfflineProgress(progress: OfflineProgress) async func loadProgress(for blockID: String) async -> OfflineProgress? func loadAllOfflineProgress() async -> [OfflineProgress] func deleteProgress(for blockID: String) async func deleteAllProgress() async - func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality) async - func nextBlockForDownloading() async -> DownloadDataTask? - func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) async - func deleteDownloadDataTask(id: String) async throws - func saveDownloadDataTask(_ task: DownloadDataTask) async + func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality) + func updateTask(task: DownloadDataTask) func downloadDataTask(for blockId: String) async -> DownloadDataTask? func getDownloadDataTasks() async -> [DownloadDataTask] func getDownloadDataTasksForCourse(_ courseId: String) async -> [DownloadDataTask] + func deleteDownloadDataTasks(ids: [String]) } #if DEBUG public final class CorePersistenceMock: CorePersistenceProtocol, @unchecked Sendable { - public init() {} public func set(userId: Int) {} public func getUserID() -> Int? {1} public func publisher() -> AnyPublisher { Just(0).eraseToAnyPublisher() } - public func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality) async {} - public func addToDownloadQueue(tasks: [DownloadDataTask]) async {} - public func nextBlockForDownloading() async -> DownloadDataTask? { nil } - public func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) async {} - public func deleteDownloadDataTask(id: String) async throws {} + public func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality) {} + public func addToDownloadQueue(tasks: [DownloadDataTask]) {} + public func updateTask(task: DownloadDataTask) {} public func downloadDataTask(for blockId: String) async -> DownloadDataTask? { nil } public func saveOfflineProgress(progress: OfflineProgress) async {} public func loadProgress(for blockID: String) async -> OfflineProgress? { nil } public func loadAllOfflineProgress() async -> [OfflineProgress] { [] } public func deleteProgress(for blockID: String) async {} public func deleteAllProgress() async {} - public func saveDownloadDataTask(_ task: DownloadDataTask) async {} public func getDownloadDataTasks() async -> [DownloadDataTask] {[]} public func getDownloadDataTasksForCourse(_ courseId: String) async -> [DownloadDataTask] {[]} + public func deleteDownloadDataTasks(ids: [String]) {} } #endif diff --git a/Core/Core/Domain/Model/CourseBlockModel.swift b/Core/Core/Domain/Model/CourseBlockModel.swift index c9e24441b..42bd07114 100644 --- a/Core/Core/Domain/Model/CourseBlockModel.swift +++ b/Core/Core/Domain/Model/CourseBlockModel.swift @@ -269,16 +269,13 @@ public struct CourseBlock: Hashable, Identifiable, Sendable, Equatable { public let encodedVideo: CourseBlockEncodedVideo? public let multiDevice: Bool? public var offlineDownload: OfflineDownload? - public var actualFileSize: Int? public var isDownloadable: Bool { encodedVideo?.isDownloadable ?? false || offlineDownload?.isDownloadable ?? false } public var fileSize: Int? { - if let actualFileSize { - return actualFileSize - } else if let fileSize = encodedVideo?.desktopMP4?.fileSize { + if let fileSize = encodedVideo?.desktopMP4?.fileSize { return fileSize } else if let fileSize = encodedVideo?.fallback?.fileSize { return fileSize diff --git a/Core/Core/Network/DownloadManager.swift b/Core/Core/Network/DownloadManager.swift index 47bef7d17..12471c21e 100644 --- a/Core/Core/Network/DownloadManager.swift +++ b/Core/Core/Network/DownloadManager.swift @@ -46,6 +46,7 @@ public struct DownloadDataTask: Identifiable, Hashable, Sendable { public var state: DownloadState public let type: DownloadType public let fileSize: Int + public var actualSize: Int public var lastModified: String? public var fileSizeInMb: Double { @@ -69,7 +70,8 @@ public struct DownloadDataTask: Identifiable, Hashable, Sendable { state: DownloadState, type: DownloadType, fileSize: Int, - lastModified: String + lastModified: String, + actualSize: Int ) { self.id = id self.courseId = courseId @@ -84,6 +86,7 @@ public struct DownloadDataTask: Identifiable, Hashable, Sendable { self.type = type self.fileSize = fileSize self.lastModified = lastModified + self.actualSize = actualSize } public init(sourse: CDDownloadData) { @@ -100,6 +103,42 @@ public struct DownloadDataTask: Identifiable, Hashable, Sendable { self.type = DownloadType(rawValue: sourse.type ?? "") ?? .video self.fileSize = Int(sourse.fileSize) self.lastModified = sourse.lastModified + self.actualSize = Int(sourse.actualSize) + } + + public init?(block: CourseBlock, userId: Int, downloadQuality: DownloadQuality) { + let url: URL + let fileExtension: String + let fileSize: Int + if let html = block.offlineDownload, let htmlUrl = URL(string: html.fileUrl) { + url = htmlUrl + fileExtension = url.pathExtension + fileSize = html.fileSize + self.lastModified = html.lastModified + self.type = .html + } else if let video = block.encodedVideo?.video(downloadQuality: downloadQuality), + let videoUrlString = video.url, + let videoUrl = URL(string: videoUrlString) { + url = videoUrl + fileExtension = videoUrl.pathExtension + fileSize = video.fileSize ?? 0 + self.type = .video + } else { return nil } + let fileName = "\(block.id).\(fileExtension)" + + let downloadDataId = "\(userId)_\(block.id)" + self.id = downloadDataId + self.blockId = block.id + self.userId = userId + self.courseId = block.courseId + self.url = url.absoluteString + self.fileName = fileName + self.displayName = block.displayName + self.progress = Double.zero + self.resumeData = nil + self.state = .waiting + self.fileSize = fileSize + self.actualSize = 0 } } @@ -108,10 +147,8 @@ public class NoWiFiError: LocalizedError, @unchecked Sendable { } //sourcery: AutoMockable -@MainActor public protocol DownloadManagerProtocol: Sendable { - var currentDownloadTask: DownloadDataTask? { get } - func publisher() throws -> AnyPublisher + func getCurrentDownloadTask() async -> DownloadDataTask? func eventPublisher() -> AnyPublisher func addToDownloadQueue(blocks: [CourseBlock]) async throws @@ -124,52 +161,66 @@ public protocol DownloadManagerProtocol: Sendable { func cancelDownloading(courseId: String) async throws func cancelAllDownloading() async throws - func deleteFile(blocks: [CourseBlock]) async - func deleteAllFiles() async + func deleteAll() async func fileUrl(for blockId: String) async -> URL? - func updateUnzippedFileSize(for sequentials: [CourseSequential]) async -> [CourseSequential] func resumeDownloading() async throws - func isLargeVideosSize(blocks: [CourseBlock]) -> Bool + func isLargeVideosSize(blocks: [CourseBlock]) async -> Bool func removeAppSupportDirectoryUnusedContent() + func delete(blocks: [CourseBlock], courseId: String) async + func downloadTask(for blockId: String) async -> DownloadDataTask? } public enum DownloadManagerEvent: Sendable { case added case started(DownloadDataTask) - case progress(Double, DownloadDataTask) - case paused(DownloadDataTask) - case canceled(DownloadDataTask) + case progress(DownloadDataTask) + case paused([DownloadDataTask]) + case canceled([DownloadDataTask]) case courseCanceled(String) case allCanceled case finished(DownloadDataTask) - case deletedFile(String) + case deletedFile([String]) case clearedAll } -public class DownloadManager: DownloadManagerProtocol { +enum DownloadManagerState { + case idle + case downloading + case paused +} +// swiftlint:disable type_body_length file_length +public actor DownloadManager: DownloadManagerProtocol, @unchecked Sendable { // MARK: - Properties - public nonisolated(unsafe) var currentDownloadTask: DownloadDataTask? + private var currentDownloadTask: DownloadDataTask? private let persistence: CorePersistenceProtocol private let appStorage: CoreStorage private let connectivity: ConnectivityProtocol private var downloadRequest: DownloadRequest? - private var isDownloadingInProgress: Bool = false - private nonisolated(unsafe) var currentDownloadEventPublisher = PassthroughSubject() + nonisolated + private let currentDownloadEventPublisher: PassthroughSubject = .init() private let backgroundTaskProvider = BackgroundTaskProvider() private var cancellables = Set() private nonisolated(unsafe) var failedDownloads: [DownloadDataTask] = [] private let indexPage = "index.html" - private var downloadQuality: DownloadQuality { + public var downloadQuality: DownloadQuality { appStorage.userSettings?.downloadQuality ?? .auto } + private var userId: Int { + appStorage.user?.id ?? 0 + } + + private var queue: [DownloadDataTask] = [] + + private var state: DownloadManagerState = .idle // MARK: - Init + public init( persistence: CorePersistenceProtocol, appStorage: CoreStorage, @@ -178,19 +229,40 @@ public class DownloadManager: DownloadManagerProtocol { self.persistence = persistence self.appStorage = appStorage self.connectivity = connectivity - if let userId = appStorage.user?.id { persistence.set(userId: userId) - self.backgroundTask() Task { - try? await resumeDownloading() + await self.addObsevers() + await self.backgroundTask() } } - + } + + public func getCurrentDownloadTask() async -> DownloadDataTask? { + currentDownloadTask + } + + private func addObsevers() async { + await connectivity.internetReachableSubject + .sink {[weak self] state in + guard let self else { return } + Task { + switch state { + case .notReachable: + await self.waitingAll() + case .reachable: + try? await self.resumeDownloading() + case .none: + return + } + } + } + .store(in: &cancellables) + NotificationCenter.default.publisher(for: .tryDownloadAgain) .compactMap { $0.object as? [DownloadDataTask] } .sink { [weak self] downloads in - Task { + Task {[weak self] in await self?.tryDownloadAgain(downloads: downloads) } } @@ -198,27 +270,40 @@ public class DownloadManager: DownloadManagerProtocol { } private func tryDownloadAgain(downloads: [DownloadDataTask]) async { - await persistence.addToDownloadQueue(tasks: downloads) - Task { - try? await newDownload() + var tasksToInsert: [DownloadDataTask] = [] + + if queue.isEmpty { + _ = await getDownloadTasks() } - } - // MARK: - Publishers + for task in downloads { + if let index = queue.firstIndex(where: { $0.id == task.id }) { + queue[index].state = .waiting + } else { + queue.append(task) + var newTask = task + newTask.state = .waiting + tasksToInsert.append(newTask) + } + } + + if !tasksToInsert.isEmpty { + persistence.addToDownloadQueue(tasks: tasksToInsert) + } - public func publisher() throws -> AnyPublisher { - try persistence.publisher() + try? await newDownload() } + // MARK: - Publishers + nonisolated public func eventPublisher() -> AnyPublisher { currentDownloadEventPublisher - .receive(on: DispatchQueue.main) .eraseToAnyPublisher() } // MARK: - Intents - public func isLargeVideosSize(blocks: [CourseBlock]) -> Bool { + public func isLargeVideosSize(blocks: [CourseBlock]) async -> Bool { let totalSizeInBytes = blocks.reduce(0) { accumulator, block in let videoSize = block.encodedVideo?.video(downloadQuality: downloadQuality)?.fileSize ?? 0 return accumulator + Double(videoSize) @@ -230,21 +315,39 @@ public class DownloadManager: DownloadManagerProtocol { } public func getDownloadTasks() async -> [DownloadDataTask] { - await persistence.getDownloadDataTasks() + if queue.isEmpty { + queue = await persistence.getDownloadDataTasks() + } + return queue } public func getDownloadTasksForCourse(_ courseId: String) async -> [DownloadDataTask] { - await persistence.getDownloadDataTasksForCourse(courseId) + if queue.isEmpty { + await persistence.getDownloadDataTasksForCourse(courseId) + } else { + queue.filter({$0.courseId == courseId}) + } } public func addToDownloadQueue(blocks: [CourseBlock]) async throws { - if userCanDownload() { - await persistence.addToDownloadQueue( + if await userCanDownload() { + let newTasks = blocks.compactMap { + DownloadDataTask( + block: $0, + userId: userId, + downloadQuality: downloadQuality + ) + } + + for task in newTasks where queue.first(where: { $0.id == task.id }) == nil { + queue.append(task) + } + + persistence.addToDownloadQueue( blocks: blocks, downloadQuality: downloadQuality ) currentDownloadEventPublisher.send(.added) - guard !isDownloadingInProgress else { return } try await newDownload() } else { throw NoWiFiError() @@ -252,96 +355,64 @@ public class DownloadManager: DownloadManagerProtocol { } public func resumeDownloading() async throws { + let isInternetAvaliable = await connectivity.isInternetAvaliable + guard state != .downloading && isInternetAvaliable else { return } + state = .idle + if queue.isEmpty { + queue = await persistence.getDownloadDataTasks() + } try await newDownload() } - public func cancelDownloading(courseId: String, blocks: [CourseBlock]) async throws { + private func cancelCurrentTask() { downloadRequest?.cancel() - let downloaded = await getDownloadTasksForCourse(courseId) - let blocksForDelete = blocks.filter { block in - downloaded.first(where: { $0.blockId == block.id }) != nil - } - await deleteFile(blocks: blocksForDelete) - downloaded.forEach { - currentDownloadEventPublisher.send(.canceled($0)) + currentDownloadTask = nil + } + public func cancelDownloading(courseId: String, blocks: [CourseBlock]) async throws { + if blocks.contains(where: { $0.id == currentDownloadTask?.blockId }) { + cancelCurrentTask() } + await delete(blocks: blocks, courseId: courseId) try await newDownload() } - public func cancelDownloading(task: DownloadDataTask) async throws { - downloadRequest?.cancel() - do { - if let fileUrl = await fileUrl(for: task.id) { - try FileManager.default.removeItem(at: fileUrl) - } - try await persistence.deleteDownloadDataTask(id: task.id) - currentDownloadEventPublisher.send(.canceled(task)) - } catch { - NSLog("Error deleting file: \(error.localizedDescription)") + public func cancelDownloading(task: DownloadDataTask) throws { + if task.id == currentDownloadTask?.id { + cancelCurrentTask() + } + + delete(tasks: [task]) + Task { + try await newDownload() } - try await newDownload() } public func cancelDownloading(courseId: String) async throws { + if currentDownloadTask?.courseId == courseId { + cancelCurrentTask() + } + let tasks = await getDownloadTasksForCourse(courseId) - await cancel(tasks: tasks) + delete(tasks: tasks) currentDownloadEventPublisher.send(.courseCanceled(courseId)) - downloadRequest?.cancel() try await newDownload() } public func cancelAllDownloading() async throws { + cancelCurrentTask() + let tasks = await getDownloadTasks().filter { $0.state != .finished } - await cancel(tasks: tasks) + delete(tasks: tasks) currentDownloadEventPublisher.send(.allCanceled) - downloadRequest?.cancel() try await newDownload() } - public func deleteFile(blocks: [CourseBlock]) async { - for block in blocks { - do { - if let fileURL = await fileOrFolderUrl(for: block.id), - FileManager.default.fileExists(atPath: fileURL.path) { - try FileManager.default.removeItem(at: fileURL) - } - try await persistence.deleteDownloadDataTask(id: block.id) - currentDownloadEventPublisher.send(.deletedFile(block.id)) - } catch { - debugLog("Error deleting file: \(error.localizedDescription)") - } - } - } - - public func updateUnzippedFileSize(for sequentials: [CourseSequential]) async -> [CourseSequential] { - var updatedSequentials = sequentials - - for i in 0.. Int { @@ -368,22 +439,33 @@ public class DownloadManager: DownloadManagerProtocol { return Int(totalSize) } - public func deleteAllFiles() async { - let downloadsData = await getDownloadTasks() - for downloadData in downloadsData { - if let fileURL = await fileOrFolderUrl(for: downloadData.id) { - do { - try FileManager.default.removeItem(at: fileURL) - } catch { - debugLog("Error deleting All files: \(error.localizedDescription)") - } + private func getFileSize(at url: URL) -> Int? { + do { + let fileAttributes = try FileManager.default.attributesOfItem(atPath: url.path) + if let fileSize = fileAttributes[.size] as? Int, fileSize > 0 { + return fileSize } + } catch { + debugLog("Error getting file size: \(error.localizedDescription)") } + return nil + } + + public func deleteAll() async { + let downloadsData = await getDownloadTasks() + delete(tasks: downloadsData) currentDownloadEventPublisher.send(.clearedAll) } + + public func downloadTask(for blockId: String) async -> DownloadDataTask? { + if queue.isEmpty { + return await persistence.downloadDataTask(for: blockId) + } + return queue.first(where: {$0.blockId == blockId}) + } public func fileUrl(for blockId: String) async -> URL? { - guard let data = await persistence.downloadDataTask(for: blockId), + guard let data = await downloadTask(for: blockId), data.url.count > 0, data.state == .finished else { return nil } let path = filesFolderUrl @@ -420,13 +502,16 @@ public class DownloadManager: DownloadManagerProtocol { // MARK: - Private Intents - @MainActor private func newDownload() async throws { - guard userCanDownload() else { + guard state != .paused else { return } + guard await userCanDownload() else { throw NoWiFiError() } - guard let downloadTask = await persistence.nextBlockForDownloading() else { - isDownloadingInProgress = false + + guard downloadRequest?.state != .resumed else { return } + guard let downloadTask = queue.first(where: {$0.state != .finished}) else { + downloadRequest = nil + currentDownloadTask = nil if !failedDownloads.isEmpty { DispatchQueue.main.async { NotificationCenter.default.post( @@ -436,26 +521,24 @@ public class DownloadManager: DownloadManagerProtocol { self.failedDownloads = [] } } + print(">>> IS NIL") return } - if !connectivity.isInternetAvaliable { + if await !connectivity.isInternetAvaliable { failedDownloads.append(downloadTask) - try await cancelDownloading(task: downloadTask) + try cancelDownloading(task: downloadTask) return } - - currentDownloadTask = downloadTask if downloadTask.type == .html || downloadTask.type == .problem { try await downloadHTMLWithProgress(downloadTask) } else { try await downloadFileWithProgress(downloadTask) } - currentDownloadEventPublisher.send(.started(downloadTask)) } - private func userCanDownload() -> Bool { + private func userCanDownload() async -> Bool { if appStorage.userSettings?.wifiOnly ?? true { - if !connectivity.isMobileData { + if await !connectivity.isMobileData { return true } else { return false @@ -466,15 +549,23 @@ public class DownloadManager: DownloadManagerProtocol { } private func downloadFileWithProgress(_ download: DownloadDataTask) async throws { - guard let url = URL(string: download.url), let folderURL = self.filesFolderUrl else { return } - - await persistence.updateDownloadState( - id: download.id, - state: .inProgress, - resumeData: download.resumeData - ) - self.isDownloadingInProgress = true + guard state != .paused else { return } + guard let url = URL(string: download.url), let folderURL = self.filesFolderUrl else { + delete(tasks: [download]) + Task { + try await newDownload() + } + return + } + if let index = queue.firstIndex(where: {$0.id == download.id}) { + queue[index].state = .inProgress + persistence.updateTask(task: queue[index]) + } + + currentDownloadTask = download + currentDownloadTask?.state = .inProgress + let destination: DownloadRequest.Destination = { _, _ in let file = folderURL.appendingPathComponent(download.fileName) return (file, [.createIntermediateDirectories, .removePreviousFile]) @@ -489,118 +580,176 @@ public class DownloadManager: DownloadManagerProtocol { downloadRequest?.downloadProgress { @Sendable [weak self] prog in guard let self = self else { return } let fractionCompleted = prog.fractionCompleted - self.currentDownloadTask?.progress = fractionCompleted - self.currentDownloadTask?.state = .inProgress - self.currentDownloadEventPublisher.send(.progress(fractionCompleted, download)) - debugLog(">>>>> Downloading File", download.url, Double(fractionCompleted * 100), "%") + + Task { + var task = download + task.progress = fractionCompleted + task.state = .inProgress + await self.setCurrentTask(with: task) + self.currentDownloadEventPublisher.send(.progress(task)) + } + let completed = Double(fractionCompleted * 100) + debugLog(">>>>> Downloading File", download.url, completed, "%") } - downloadRequest?.responseURL { [weak self] response in - guard let self = self else { return } - if let error = response.error { - if error.asAFError?.isExplicitlyCancelledError == false { - self.failedDownloads.append(download) - Task { - try? await self.newDownload() - } - return - } + downloadRequest?.responseData { [weak self] response in + guard let self else { return } + Task { + await completeFileDownload(with: response, for: download) } - if response.fileURL != nil { + } + state = .downloading + currentDownloadEventPublisher.send(.started(download)) + } + + private func setCurrentTask(with task: DownloadDataTask?) async { + currentDownloadTask = task + } + + private func completeFileDownload(with response: AFDownloadResponse, for download: DownloadDataTask) async { + var state: DownloadState = .finished + if let error = response.error { + if error.isInternetError { + state = .waiting + } else if error.asAFError?.isExplicitlyCancelledError == false { + self.failedDownloads.append(download) Task { - await self.persistence.updateDownloadState( - id: download.id, - state: .finished, - resumeData: nil - ) - self.currentDownloadTask?.state = .finished - self.currentDownloadEventPublisher.send(.finished(download)) try? await self.newDownload() } + return + } + } + + let index = self.queue.firstIndex(where: {$0.id == download.id}) + if let index = index { + self.queue[index].state = state + } + self.currentDownloadTask?.state = state + + if state != .waiting { + if let index = index, let url = response.fileURL { + self.queue[index].actualSize = self.getFileSize(at: url) ?? 0 + self.persistence.updateTask(task: self.queue[index]) + } + self.currentDownloadEventPublisher.send(.finished(download)) + try? await self.newDownload() + } else { + if let index = index { + self.persistence.updateTask(task: self.queue[index]) } + self.currentDownloadEventPublisher.send(.paused([download])) } } - + private func downloadHTMLWithProgress(_ download: DownloadDataTask) async throws { - guard let url = URL(string: download.url), let folderURL = self.filesFolderUrl else { return } - - await persistence.updateDownloadState( - id: download.id, - state: .inProgress, - resumeData: download.resumeData - ) - self.isDownloadingInProgress = true + guard state != .paused else { return } + guard let url = URL(string: download.url), let folderURL = self.filesFolderUrl else { + delete(tasks: [download]) + Task { + try await newDownload() + } + return + } + if let index = queue.firstIndex(where: {$0.id == download.id}) { + queue[index].state = .inProgress + persistence.updateTask(task: queue[index]) + } let destination: DownloadRequest.Destination = { _, _ in let fileName = URL(string: download.url)?.lastPathComponent ?? "file.zip" let file = folderURL.appendingPathComponent(fileName) return (file, [.createIntermediateDirectories, .removePreviousFile]) } - + currentDownloadTask = download + currentDownloadTask?.state = .inProgress if let resumeData = download.resumeData { downloadRequest = AF.download(resumingWith: resumeData, to: destination) } else { downloadRequest = AF.download(url, to: destination) } - downloadRequest?.downloadProgress { [weak self] prog in guard let self else { return } let fractionCompleted = prog.fractionCompleted - self.currentDownloadTask?.progress = fractionCompleted - self.currentDownloadTask?.state = .inProgress - self.currentDownloadEventPublisher.send(.progress(fractionCompleted, download)) - debugLog(">>>>> Downloading HTML", download.url, Double(fractionCompleted * 100), "%") + Task { + var task = download + task.progress = fractionCompleted + await self.setCurrentTask(with: task) + self.currentDownloadEventPublisher.send(.progress(task)) + } + let completed = Double(fractionCompleted * 100) + debugLog(">>>>> Downloading HTML", download.url, completed, "%") } downloadRequest?.responseURL { [weak self] response in - guard let self else { return } - if let error = response.error { - if error.asAFError?.isExplicitlyCancelledError == false { - failedDownloads.append(download) - Task { - try? await self.newDownload() - } - return - } + guard let self else { + return } - if let fileURL = response.fileURL { - Task { - await unzipFile(url: fileURL) - await self.persistence.updateDownloadState( - id: download.id, - state: .finished, - resumeData: nil - ) - self.currentDownloadTask?.state = .finished - self.currentDownloadEventPublisher.send(.finished(download)) - try? await self.newDownload() + Task { + await self.completeHTMLDownload(with: response, for: download) + } + } + state = .downloading + currentDownloadEventPublisher.send(.started(download)) + } + + private func completeHTMLDownload(with response: AFDownloadResponse, for download: DownloadDataTask) async { + if let error = response.error { + if error.asAFError?.isExplicitlyCancelledError == false { + failedDownloads.append(download) + try? await self.newDownload() + return + } + } + if let fileURL = response.fileURL { + if let index = self.queue.firstIndex(where: {$0.id == download.id}) { + if let folderURL = self.unzipFile(url: fileURL) { + self.queue[index].actualSize = (try? self.calculateFolderSize(at: folderURL)) ?? 0 } + self.queue[index].state = .finished + self.persistence.updateTask(task: self.queue[index]) } + self.currentDownloadTask?.state = .finished + self.currentDownloadEventPublisher.send(.finished(download)) + try? await self.newDownload() } } - private func waitingAll() async { - let tasks = await persistence.getDownloadDataTasks() - for task in tasks.filter({ $0.state == .inProgress }) { - await self.persistence.updateDownloadState( - id: task.id, - state: .waiting, - resumeData: nil - ) - self.currentDownloadEventPublisher.send(.added) + private func waitingAll() { + guard state != .paused else { return } + downloadRequest?.suspend() + + for i in 0 ..< queue.count where queue[i].state == .inProgress { + queue[i].state = .waiting + persistence.updateTask(task: queue[i]) } - self.downloadRequest?.cancel() + self.currentDownloadEventPublisher.send(.paused(queue)) + state = .paused } - private func cancel(tasks: [DownloadDataTask]) async { - for task in tasks { - do { - if let fileUrl = await fileUrl(for: task.id) { - try FileManager.default.removeItem(at: fileUrl) + private func delete(tasks: [DownloadDataTask]) { + let ids = tasks.map { $0.id } + let names = tasks.map { $0.fileName } + + deleteTasks(with: ids, and: names) + currentDownloadEventPublisher.send(.deletedFile(tasks.map({$0.blockId}))) + } + + private func deleteTasks(with ids: [String], and names: [String]) { + queue.removeAll(where: {ids.contains($0.id)}) + removeFiles(names: names) + persistence.deleteDownloadDataTasks(ids: ids) + } + + private func removeFiles(names: [String]) { + guard let folderURL = filesFolderUrl else { return } + for name in names { + let fileURL = folderURL.appendingPathComponent(name) + if FileManager.default.fileExists(atPath: fileURL.path) { + do { + try FileManager.default.removeItem(at: fileURL) + } catch { + debugLog("Error deleting file: \(error.localizedDescription)") } - try await persistence.deleteDownloadDataTask(id: task.id) - } catch { - debugLog("Error deleting file: \(error.localizedDescription)") } } } @@ -619,7 +768,7 @@ public class DownloadManager: DownloadManagerProtocol { .store(in: &cancellables) } - var filesFolderUrl: URL? { + private var filesFolderUrl: URL? { let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] guard let folderPathComponent else { return nil } let directoryURL = documentDirectoryURL.appendingPathComponent(folderPathComponent, isDirectory: true) @@ -657,10 +806,10 @@ public class DownloadManager: DownloadManagerProtocol { } } - private func unzipFile(url: URL) { + private func unzipFile(url: URL) -> URL? { let fileName = url.deletingPathExtension().lastPathComponent guard let directoryURL = filesFolderUrl else { - return + return nil } let uniqueDirectory = directoryURL.appendingPathComponent(fileName, isDirectory: true) @@ -682,12 +831,14 @@ public class DownloadManager: DownloadManagerProtocol { } catch { debugLog("Error removing file: \(error.localizedDescription)") } + return uniqueDirectory } - public func removeAppSupportDirectoryUnusedContent() { + nonisolated public func removeAppSupportDirectoryUnusedContent() { deleteMD5HashedFolders() } + nonisolated private func getApplicationSupportDirectory() -> URL? { let fileManager = FileManager.default do { @@ -704,12 +855,14 @@ public class DownloadManager: DownloadManagerProtocol { } } + nonisolated func isMD5Hash(_ folderName: String) -> Bool { let md5Regex = "^[a-fA-F0-9]{32}$" let predicate = NSPredicate(format: "SELF MATCHES %@", md5Regex) return predicate.evaluate(with: folderName) } + nonisolated private func deleteMD5HashedFolders() { guard let appSupportDirectory = getApplicationSupportDirectory() else { return @@ -745,7 +898,7 @@ public final class BackgroundTaskProvider: @unchecked Sendable { private var backgroundTask: UIBackgroundTaskIdentifier = .invalid private var currentEventPublisher: PassthroughSubject = .init() - public enum Events { + public enum Events: Sendable { case didBecomeActive case didEnterBackground } @@ -822,3 +975,4 @@ public final class BackgroundTaskProvider: @unchecked Sendable { } } } +// swiftlint:enable type_body_length file_length diff --git a/Core/Core/Network/DownloadManagerMock.swift b/Core/Core/Network/DownloadManagerMock.swift index f7889df09..1c4a59d86 100644 --- a/Core/Core/Network/DownloadManagerMock.swift +++ b/Core/Core/Network/DownloadManagerMock.swift @@ -10,11 +10,17 @@ import Combine // Mark - For testing and SwiftUI preview #if DEBUG -public class DownloadManagerMock: DownloadManagerProtocol { - +public final class DownloadManagerMock: DownloadManagerProtocol, @unchecked Sendable { + public func getCurrentDownloadTask() async -> DownloadDataTask? { + nil + } + + public func downloadTask(for blockId: String) async -> DownloadDataTask? { + nil + } public init() {} - public func updateUnzippedFileSize(for sequentials: [CourseSequential]) -> [CourseSequential] {[]} + public func delete(blocks: [CourseBlock], courseId: String) async {} public var currentDownloadTask: DownloadDataTask? { return nil @@ -27,6 +33,7 @@ public class DownloadManagerMock: DownloadManagerProtocol { public func eventPublisher() -> AnyPublisher { return Just( .canceled( + [ .init( id: "", blockId: "", @@ -40,8 +47,10 @@ public class DownloadManagerMock: DownloadManagerProtocol { state: .inProgress, type: .video, fileSize: 0, - lastModified: "" + lastModified: "", + actualSize: 0 ) + ] ) ).eraseToAnyPublisher() } @@ -70,9 +79,9 @@ public class DownloadManagerMock: DownloadManagerProtocol { public func deleteFile(blocks: [CourseBlock]) {} - public func deleteAllFiles() {} - - public func fileUrl(for blockId: String) -> URL? { + public func deleteAll() {} + + public func fileUrl(for blockId: String) async -> URL? { return nil } diff --git a/Core/CoreTests/CoreMock.generated.swift b/Core/CoreTests/CoreMock.generated.swift index fb0546e8a..dca70e9a6 100644 --- a/Core/CoreTests/CoreMock.generated.swift +++ b/Core/CoreTests/CoreMock.generated.swift @@ -2529,41 +2529,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { perform?(`blocks`, `downloadQuality`) } - open func nextBlockForDownloading() -> DownloadDataTask? { - addInvocation(.m_nextBlockForDownloading) - let perform = methodPerformValue(.m_nextBlockForDownloading) as? () -> Void - perform?() - var __value: DownloadDataTask? = nil - do { - __value = try methodReturnValue(.m_nextBlockForDownloading).casted() - } catch { - // do nothing - } - return __value - } - - open func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) { - addInvocation(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) - let perform = methodPerformValue(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) as? (String, DownloadState, Data?) -> Void - perform?(`id`, `state`, `resumeData`) - } - - open func deleteDownloadDataTask(id: String) throws { - addInvocation(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) - let perform = methodPerformValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) as? (String) -> Void - perform?(`id`) - do { - _ = try methodReturnValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } - } - - open func saveDownloadDataTask(_ task: DownloadDataTask) { - addInvocation(.m_saveDownloadDataTask__task(Parameter.value(`task`))) - let perform = methodPerformValue(.m_saveDownloadDataTask__task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + open func updateTask(task: DownloadDataTask) { + addInvocation(.m_updateTask__task_task(Parameter.value(`task`))) + let perform = methodPerformValue(.m_updateTask__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void perform?(`task`) } @@ -2608,6 +2576,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { return __value } + open func deleteDownloadDataTasks(ids: [String]) { + addInvocation(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) + let perform = methodPerformValue(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) as? ([String]) -> Void + perform?(`ids`) + } + fileprivate enum MethodType { case m_set__userId_userId(Parameter) @@ -2620,13 +2594,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case m_deleteProgress__for_blockID(Parameter) case m_deleteAllProgress case m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>, Parameter) - case m_nextBlockForDownloading - case m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter, Parameter, Parameter) - case m_deleteDownloadDataTask__id_id(Parameter) - case m_saveDownloadDataTask__task(Parameter) + case m_updateTask__task_task(Parameter) case m_downloadDataTask__for_blockId(Parameter) case m_getDownloadDataTasks case m_getDownloadDataTasksForCourse__courseId(Parameter) + case m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { @@ -2669,23 +2641,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsDownloadquality, rhs: rhsDownloadquality, with: matcher), lhsDownloadquality, rhsDownloadquality, "downloadQuality")) return Matcher.ComparisonResult(results) - case (.m_nextBlockForDownloading, .m_nextBlockForDownloading): return .match - - case (.m_updateDownloadState__id_idstate_stateresumeData_resumeData(let lhsId, let lhsState, let lhsResumedata), .m_updateDownloadState__id_idstate_stateresumeData_resumeData(let rhsId, let rhsState, let rhsResumedata)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsState, rhs: rhsState, with: matcher), lhsState, rhsState, "state")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsResumedata, rhs: rhsResumedata, with: matcher), lhsResumedata, rhsResumedata, "resumeData")) - return Matcher.ComparisonResult(results) - - case (.m_deleteDownloadDataTask__id_id(let lhsId), .m_deleteDownloadDataTask__id_id(let rhsId)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - return Matcher.ComparisonResult(results) - - case (.m_saveDownloadDataTask__task(let lhsTask), .m_saveDownloadDataTask__task(let rhsTask)): + case (.m_updateTask__task_task(let lhsTask), .m_updateTask__task_task(let rhsTask)): var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "_ task")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) return Matcher.ComparisonResult(results) case (.m_downloadDataTask__for_blockId(let lhsBlockid), .m_downloadDataTask__for_blockId(let rhsBlockid)): @@ -2699,6 +2657,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) return Matcher.ComparisonResult(results) + + case (.m_deleteDownloadDataTasks__ids_ids(let lhsIds), .m_deleteDownloadDataTasks__ids_ids(let rhsIds)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsIds, rhs: rhsIds, with: matcher), lhsIds, rhsIds, "ids")) + return Matcher.ComparisonResult(results) default: return .none } } @@ -2715,13 +2678,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case let .m_deleteProgress__for_blockID(p0): return p0.intValue case .m_deleteAllProgress: return 0 case let .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(p0, p1): return p0.intValue + p1.intValue - case .m_nextBlockForDownloading: return 0 - case let .m_updateDownloadState__id_idstate_stateresumeData_resumeData(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue - case let .m_deleteDownloadDataTask__id_id(p0): return p0.intValue - case let .m_saveDownloadDataTask__task(p0): return p0.intValue + case let .m_updateTask__task_task(p0): return p0.intValue case let .m_downloadDataTask__for_blockId(p0): return p0.intValue case .m_getDownloadDataTasks: return 0 case let .m_getDownloadDataTasksForCourse__courseId(p0): return p0.intValue + case let .m_deleteDownloadDataTasks__ids_ids(p0): return p0.intValue } } func assertionName() -> String { @@ -2736,13 +2697,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case .m_deleteProgress__for_blockID: return ".deleteProgress(for:)" case .m_deleteAllProgress: return ".deleteAllProgress()" case .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality: return ".addToDownloadQueue(blocks:downloadQuality:)" - case .m_nextBlockForDownloading: return ".nextBlockForDownloading()" - case .m_updateDownloadState__id_idstate_stateresumeData_resumeData: return ".updateDownloadState(id:state:resumeData:)" - case .m_deleteDownloadDataTask__id_id: return ".deleteDownloadDataTask(id:)" - case .m_saveDownloadDataTask__task: return ".saveDownloadDataTask(_:)" + case .m_updateTask__task_task: return ".updateTask(task:)" case .m_downloadDataTask__for_blockId: return ".downloadDataTask(for:)" case .m_getDownloadDataTasks: return ".getDownloadDataTasks()" case .m_getDownloadDataTasksForCourse__courseId: return ".getDownloadDataTasksForCourse(_:)" + case .m_deleteDownloadDataTasks__ids_ids: return ".deleteDownloadDataTasks(ids:)" } } } @@ -2769,9 +2728,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func loadAllOfflineProgress(willReturn: [OfflineProgress]...) -> MethodStub { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func nextBlockForDownloading(willReturn: DownloadDataTask?...) -> MethodStub { - return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func downloadDataTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } @@ -2802,13 +2758,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func nextBlockForDownloading(willProduce: (Stubber) -> Void) -> MethodStub { - let willReturn: [DownloadDataTask?] = [] - let given: Given = { return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (DownloadDataTask?).self) - willProduce(stubber) - return given - } public static func downloadDataTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [DownloadDataTask?] = [] let given: Given = { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -2842,16 +2791,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func deleteDownloadDataTask(id: Parameter, willThrow: Error...) -> MethodStub { - return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) - } - public static func deleteDownloadDataTask(id: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given - } } public struct Verify { @@ -2868,13 +2807,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func deleteProgress(for blockID: Parameter) -> Verify { return Verify(method: .m_deleteProgress__for_blockID(`blockID`))} public static func deleteAllProgress() -> Verify { return Verify(method: .m_deleteAllProgress)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`))} - public static func nextBlockForDownloading() -> Verify { return Verify(method: .m_nextBlockForDownloading)} - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter) -> Verify { return Verify(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`))} - public static func deleteDownloadDataTask(id: Parameter) -> Verify { return Verify(method: .m_deleteDownloadDataTask__id_id(`id`))} - public static func saveDownloadDataTask(_ task: Parameter) -> Verify { return Verify(method: .m_saveDownloadDataTask__task(`task`))} + public static func updateTask(task: Parameter) -> Verify { return Verify(method: .m_updateTask__task_task(`task`))} public static func downloadDataTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadDataTask__for_blockId(`blockId`))} public static func getDownloadDataTasks() -> Verify { return Verify(method: .m_getDownloadDataTasks)} public static func getDownloadDataTasksForCourse(_ courseId: Parameter) -> Verify { return Verify(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`))} + public static func deleteDownloadDataTasks(ids: Parameter<[String]>) -> Verify { return Verify(method: .m_deleteDownloadDataTasks__ids_ids(`ids`))} } public struct Perform { @@ -2912,17 +2849,8 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter, perform: @escaping ([CourseBlock], DownloadQuality) -> Void) -> Perform { return Perform(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`), performs: perform) } - public static func nextBlockForDownloading(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_nextBlockForDownloading, performs: perform) - } - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter, perform: @escaping (String, DownloadState, Data?) -> Void) -> Perform { - return Perform(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`), performs: perform) - } - public static func deleteDownloadDataTask(id: Parameter, perform: @escaping (String) -> Void) -> Perform { - return Perform(method: .m_deleteDownloadDataTask__id_id(`id`), performs: perform) - } - public static func saveDownloadDataTask(_ task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { - return Perform(method: .m_saveDownloadDataTask__task(`task`), performs: perform) + public static func updateTask(task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { + return Perform(method: .m_updateTask__task_task(`task`), performs: perform) } public static func downloadDataTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_downloadDataTask__for_blockId(`blockId`), performs: perform) @@ -2933,6 +2861,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getDownloadDataTasksForCourse(_ courseId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), performs: perform) } + public static func deleteDownloadDataTasks(ids: Parameter<[String]>, perform: @escaping ([String]) -> Void) -> Perform { + return Perform(method: .m_deleteDownloadDataTasks__ids_ids(`ids`), performs: perform) + } } public func given(_ method: Given) { @@ -3428,7 +3359,7 @@ open class CoreStorageMock: CoreStorage, Mock { } // MARK: - DownloadManagerProtocol -@MainActor + open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public init(sequencing sequencingPolicy: SequencingPolicy = .lastWrittenResolvedFirst, stubbing stubbingPolicy: StubbingPolicy = .wrap, file: StaticString = #file, line: UInt = #line) { SwiftyMockyTestObserver.setup() @@ -3467,27 +3398,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { if scopes.contains(.perform) { methodPerformValues = [] } } - public var currentDownloadTask: DownloadDataTask? { - get { invocations.append(.p_currentDownloadTask_get); return __p_currentDownloadTask ?? optionalGivenGetterValue(.p_currentDownloadTask_get, "DownloadManagerProtocolMock - stub value for currentDownloadTask was not defined") } - } - private var __p_currentDownloadTask: (DownloadDataTask)? - - open func publisher() throws -> AnyPublisher { - addInvocation(.m_publisher) - let perform = methodPerformValue(.m_publisher) as? () -> Void + open func getCurrentDownloadTask() -> DownloadDataTask? { + addInvocation(.m_getCurrentDownloadTask) + let perform = methodPerformValue(.m_getCurrentDownloadTask) as? () -> Void perform?() - var __value: AnyPublisher + var __value: DownloadDataTask? = nil do { - __value = try methodReturnValue(.m_publisher).casted() - } catch MockError.notStubed { - onFatalFailure("Stub return value not specified for publisher(). Use given") - Failure("Stub return value not specified for publisher(). Use given") + __value = try methodReturnValue(.m_getCurrentDownloadTask).casted() } catch { - throw error + // do nothing } return __value } @@ -3599,15 +3522,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { } } - open func deleteFile(blocks: [CourseBlock]) { - addInvocation(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - } - - open func deleteAllFiles() { - addInvocation(.m_deleteAllFiles) - let perform = methodPerformValue(.m_deleteAllFiles) as? () -> Void + open func deleteAll() { + addInvocation(.m_deleteAll) + let perform = methodPerformValue(.m_deleteAll) as? () -> Void perform?() } @@ -3624,20 +3541,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return __value } - open func updateUnzippedFileSize(for sequentials: [CourseSequential]) -> [CourseSequential] { - addInvocation(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) - let perform = methodPerformValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) as? ([CourseSequential]) -> Void - perform?(`sequentials`) - var __value: [CourseSequential] - do { - __value = try methodReturnValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))).casted() - } catch { - onFatalFailure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - Failure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - } - return __value - } - open func resumeDownloading() throws { addInvocation(.m_resumeDownloading) let perform = methodPerformValue(.m_resumeDownloading) as? () -> Void @@ -3671,9 +3574,28 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { perform?() } + open func delete(blocks: [CourseBlock], courseId: String) { + addInvocation(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) + let perform = methodPerformValue(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) as? ([CourseBlock], String) -> Void + perform?(`blocks`, `courseId`) + } + + open func downloadTask(for blockId: String) -> DownloadDataTask? { + addInvocation(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) + let perform = methodPerformValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value + } + fileprivate enum MethodType { - case m_publisher + case m_getCurrentDownloadTask case m_eventPublisher case m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>) case m_getDownloadTasks @@ -3682,18 +3604,17 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case m_cancelDownloading__task_task(Parameter) case m_cancelDownloading__courseId_courseId(Parameter) case m_cancelAllDownloading - case m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>) - case m_deleteAllFiles + case m_deleteAll case m_fileUrl__for_blockId(Parameter) - case m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>) case m_resumeDownloading case m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>) case m_removeAppSupportDirectoryUnusedContent - case p_currentDownloadTask_get + case m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>, Parameter) + case m_downloadTask__for_blockId(Parameter) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { - case (.m_publisher, .m_publisher): return .match + case (.m_getCurrentDownloadTask, .m_getCurrentDownloadTask): return .match case (.m_eventPublisher, .m_eventPublisher): return .match @@ -3727,23 +3648,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case (.m_cancelAllDownloading, .m_cancelAllDownloading): return .match - case (.m_deleteFile__blocks_blocks(let lhsBlocks), .m_deleteFile__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) - - case (.m_deleteAllFiles, .m_deleteAllFiles): return .match + case (.m_deleteAll, .m_deleteAll): return .match case (.m_fileUrl__for_blockId(let lhsBlockid), .m_fileUrl__for_blockId(let rhsBlockid)): var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) return Matcher.ComparisonResult(results) - case (.m_updateUnzippedFileSize__for_sequentials(let lhsSequentials), .m_updateUnzippedFileSize__for_sequentials(let rhsSequentials)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequentials, rhs: rhsSequentials, with: matcher), lhsSequentials, rhsSequentials, "for sequentials")) - return Matcher.ComparisonResult(results) - case (.m_resumeDownloading, .m_resumeDownloading): return .match case (.m_isLargeVideosSize__blocks_blocks(let lhsBlocks), .m_isLargeVideosSize__blocks_blocks(let rhsBlocks)): @@ -3752,14 +3663,24 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return Matcher.ComparisonResult(results) case (.m_removeAppSupportDirectoryUnusedContent, .m_removeAppSupportDirectoryUnusedContent): return .match - case (.p_currentDownloadTask_get,.p_currentDownloadTask_get): return Matcher.ComparisonResult.match + + case (.m_delete__blocks_blockscourseId_courseId(let lhsBlocks, let lhsCourseid), .m_delete__blocks_blockscourseId_courseId(let rhsBlocks, let rhsCourseid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + return Matcher.ComparisonResult(results) + + case (.m_downloadTask__for_blockId(let lhsBlockid), .m_downloadTask__for_blockId(let rhsBlockid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) default: return .none } } func intValue() -> Int { switch self { - case .m_publisher: return 0 + case .m_getCurrentDownloadTask: return 0 case .m_eventPublisher: return 0 case let .m_addToDownloadQueue__blocks_blocks(p0): return p0.intValue case .m_getDownloadTasks: return 0 @@ -3768,19 +3689,18 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case let .m_cancelDownloading__task_task(p0): return p0.intValue case let .m_cancelDownloading__courseId_courseId(p0): return p0.intValue case .m_cancelAllDownloading: return 0 - case let .m_deleteFile__blocks_blocks(p0): return p0.intValue - case .m_deleteAllFiles: return 0 + case .m_deleteAll: return 0 case let .m_fileUrl__for_blockId(p0): return p0.intValue - case let .m_updateUnzippedFileSize__for_sequentials(p0): return p0.intValue case .m_resumeDownloading: return 0 case let .m_isLargeVideosSize__blocks_blocks(p0): return p0.intValue case .m_removeAppSupportDirectoryUnusedContent: return 0 - case .p_currentDownloadTask_get: return 0 + case let .m_delete__blocks_blockscourseId_courseId(p0, p1): return p0.intValue + p1.intValue + case let .m_downloadTask__for_blockId(p0): return p0.intValue } } func assertionName() -> String { switch self { - case .m_publisher: return ".publisher()" + case .m_getCurrentDownloadTask: return ".getCurrentDownloadTask()" case .m_eventPublisher: return ".eventPublisher()" case .m_addToDownloadQueue__blocks_blocks: return ".addToDownloadQueue(blocks:)" case .m_getDownloadTasks: return ".getDownloadTasks()" @@ -3789,14 +3709,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case .m_cancelDownloading__task_task: return ".cancelDownloading(task:)" case .m_cancelDownloading__courseId_courseId: return ".cancelDownloading(courseId:)" case .m_cancelAllDownloading: return ".cancelAllDownloading()" - case .m_deleteFile__blocks_blocks: return ".deleteFile(blocks:)" - case .m_deleteAllFiles: return ".deleteAllFiles()" + case .m_deleteAll: return ".deleteAll()" case .m_fileUrl__for_blockId: return ".fileUrl(for:)" - case .m_updateUnzippedFileSize__for_sequentials: return ".updateUnzippedFileSize(for:)" case .m_resumeDownloading: return ".resumeDownloading()" case .m_isLargeVideosSize__blocks_blocks: return ".isLargeVideosSize(blocks:)" case .m_removeAppSupportDirectoryUnusedContent: return ".removeAppSupportDirectoryUnusedContent()" - case .p_currentDownloadTask_get: return "[get] .currentDownloadTask" + case .m_delete__blocks_blockscourseId_courseId: return ".delete(blocks:courseId:)" + case .m_downloadTask__for_blockId: return ".downloadTask(for:)" } } } @@ -3809,12 +3728,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { super.init(products) } - public static func currentDownloadTask(getter defaultValue: DownloadDataTask?...) -> PropertyStub { - return Given(method: .p_currentDownloadTask_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) - } - public static func publisher(willReturn: AnyPublisher...) -> MethodStub { - return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + public static func getCurrentDownloadTask(willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) } public static func eventPublisher(willReturn: AnyPublisher...) -> MethodStub { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) @@ -3828,12 +3744,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func fileUrl(for blockId: Parameter, willReturn: URL?...) -> MethodStub { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willReturn: [CourseSequential]...) -> MethodStub { - return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willReturn: Bool...) -> MethodStub { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) } + public static func downloadTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func getCurrentDownloadTask(willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given + } public static func eventPublisher(willProduce: (Stubber>) -> Void) -> MethodStub { let willReturn: [AnyPublisher] = [] let given: Given = { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -3862,13 +3785,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willProduce: (Stubber<[CourseSequential]>) -> Void) -> MethodStub { - let willReturn: [[CourseSequential]] = [] - let given: Given = { return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([CourseSequential]).self) - willProduce(stubber) - return given - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [Bool] = [] let given: Given = { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -3876,13 +3792,10 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func publisher(willThrow: Error...) -> MethodStub { - return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) - } - public static func publisher(willProduce: (StubberThrows>) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (AnyPublisher).self) + public static func downloadTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) willProduce(stubber) return given } @@ -3951,7 +3864,7 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public struct Verify { fileprivate var method: MethodType - public static func publisher() -> Verify { return Verify(method: .m_publisher)} + public static func getCurrentDownloadTask() -> Verify { return Verify(method: .m_getCurrentDownloadTask)} public static func eventPublisher() -> Verify { return Verify(method: .m_eventPublisher)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocks(`blocks`))} public static func getDownloadTasks() -> Verify { return Verify(method: .m_getDownloadTasks)} @@ -3960,22 +3873,21 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelDownloading(task: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__task_task(`task`))} public static func cancelDownloading(courseId: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__courseId_courseId(`courseId`))} public static func cancelAllDownloading() -> Verify { return Verify(method: .m_cancelAllDownloading)} - public static func deleteFile(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_deleteFile__blocks_blocks(`blocks`))} - public static func deleteAllFiles() -> Verify { return Verify(method: .m_deleteAllFiles)} + public static func deleteAll() -> Verify { return Verify(method: .m_deleteAll)} public static func fileUrl(for blockId: Parameter) -> Verify { return Verify(method: .m_fileUrl__for_blockId(`blockId`))} - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>) -> Verify { return Verify(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`))} public static func resumeDownloading() -> Verify { return Verify(method: .m_resumeDownloading)} public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_isLargeVideosSize__blocks_blocks(`blocks`))} public static func removeAppSupportDirectoryUnusedContent() -> Verify { return Verify(method: .m_removeAppSupportDirectoryUnusedContent)} - public static var currentDownloadTask: Verify { return Verify(method: .p_currentDownloadTask_get) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter) -> Verify { return Verify(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`))} + public static func downloadTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadTask__for_blockId(`blockId`))} } public struct Perform { fileprivate var method: MethodType var performs: Any - public static func publisher(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_publisher, performs: perform) + public static func getCurrentDownloadTask(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_getCurrentDownloadTask, performs: perform) } public static func eventPublisher(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_eventPublisher, performs: perform) @@ -4001,18 +3913,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelAllDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_cancelAllDownloading, performs: perform) } - public static func deleteFile(blocks: Parameter<[CourseBlock]>, perform: @escaping ([CourseBlock]) -> Void) -> Perform { - return Perform(method: .m_deleteFile__blocks_blocks(`blocks`), performs: perform) - } - public static func deleteAllFiles(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_deleteAllFiles, performs: perform) + public static func deleteAll(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_deleteAll, performs: perform) } public static func fileUrl(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_fileUrl__for_blockId(`blockId`), performs: perform) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, perform: @escaping ([CourseSequential]) -> Void) -> Perform { - return Perform(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), performs: perform) - } public static func resumeDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_resumeDownloading, performs: perform) } @@ -4022,6 +3928,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func removeAppSupportDirectoryUnusedContent(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_removeAppSupportDirectoryUnusedContent, performs: perform) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter, perform: @escaping ([CourseBlock], String) -> Void) -> Perform { + return Perform(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`), performs: perform) + } + public static func downloadTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { + return Perform(method: .m_downloadTask__for_blockId(`blockId`), performs: perform) + } } public func given(_ method: Given) { diff --git a/Core/CoreTests/DownloadManager/DownloadManagerTests.swift b/Core/CoreTests/DownloadManager/DownloadManagerTests.swift index 5c1b2ec53..8b75ab760 100644 --- a/Core/CoreTests/DownloadManager/DownloadManagerTests.swift +++ b/Core/CoreTests/DownloadManager/DownloadManagerTests.swift @@ -19,16 +19,19 @@ final class DownloadManagerTests: XCTestCase { override func setUp() { super.setUp() persistence = CorePersistenceProtocolMock() + let mockTask = createMockDownloadTask() storage = CoreStorageMock() connectivity = ConnectivityProtocolMock() + Given(persistence, .getDownloadDataTasks(willReturn: [mockTask])) + Given(connectivity, .isInternetAvaliable(getter: true)) + Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) + Given(storage, .user(getter: .init(id: 19, username: "username", email: "email", name: "name"))) } // MARK: - Test Add to Queue func testAddToDownloadQueue_WhenWiFiOnlyAndOnWiFi_ShouldAddToQueue() async throws { // Given - Given(connectivity, .isInternetAvaliable(getter: true)) - Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) Given(connectivity, .isMobileData(getter: false)) let downloadManager = DownloadManager( @@ -48,7 +51,6 @@ final class DownloadManagerTests: XCTestCase { // When try await downloadManager.addToDownloadQueue(blocks: blocks) - // Then Verify(persistence, 1, .addToDownloadQueue(blocks: .value(blocks), downloadQuality: .value(.auto))) } @@ -61,7 +63,6 @@ final class DownloadManagerTests: XCTestCase { downloadQuality: .auto, playbackSpeed: 1.0 ))) - Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .isMobileData(getter: true)) let downloadManager = DownloadManager( @@ -90,8 +91,7 @@ final class DownloadManagerTests: XCTestCase { // Given let mockTask = createMockDownloadTask() Given(persistence, .getDownloadDataTasks(willReturn: [mockTask])) - Given(persistence, .nextBlockForDownloading(willReturn: mockTask)) - Given(connectivity, .isInternetAvaliable(getter: true)) +// Given(persistence, .nextBlockForDownloading(willReturn: mockTask)) Given(connectivity, .isMobileData(getter: false)) let downloadManager = DownloadManager( @@ -102,13 +102,9 @@ final class DownloadManagerTests: XCTestCase { // When try await downloadManager.resumeDownloading() - - // Wait a bit for async operations to complete - await Task.yield() - + let currentDownloadTask: DownloadDataTask? = Mirror(reflecting: downloadManager).descendant("currentDownloadTask") as? DownloadDataTask // Then - Verify(persistence, 1, .nextBlockForDownloading()) - XCTAssertEqual(downloadManager.currentDownloadTask?.id, mockTask.id) + XCTAssertEqual(currentDownloadTask?.id, mockTask.id) } // MARK: - Test Cancel Downloads @@ -116,9 +112,7 @@ final class DownloadManagerTests: XCTestCase { func testCancelDownloading_ForSpecificTask_ShouldRemoveFileAndTask() async throws { // Given let task = createMockDownloadTask() - Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .isMobileData(getter: false)) - Given(persistence, .deleteDownloadDataTask(id: .value(task.id), willProduce: { _ in })) let downloadManager = DownloadManager( persistence: persistence, @@ -130,7 +124,7 @@ final class DownloadManagerTests: XCTestCase { try await downloadManager.cancelDownloading(task: task) // Then - Verify(persistence, 1, .deleteDownloadDataTask(id: .value(task.id))) + Verify(persistence, 1, .deleteDownloadDataTasks(ids: .value([task.id]))) } func testCancelDownloading_ForCourse_ShouldCancelAllTasksForCourse() async throws { @@ -138,11 +132,8 @@ final class DownloadManagerTests: XCTestCase { let courseId = "course123" let task = createMockDownloadTask(courseId: courseId) let tasks = [task] - - Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .isMobileData(getter: false)) Given(persistence, .getDownloadDataTasksForCourse(.value(courseId), willReturn: tasks)) - Given(persistence, .deleteDownloadDataTask(id: .value(task.id), willProduce: { _ in })) let downloadManager = DownloadManager( persistence: persistence, @@ -155,7 +146,7 @@ final class DownloadManagerTests: XCTestCase { // Then Verify(persistence, 1, .getDownloadDataTasksForCourse(.value(courseId))) - Verify(persistence, 1, .deleteDownloadDataTask(id: .value(task.id))) + Verify(persistence, 1, .deleteDownloadDataTasks(ids: .value([task.id]))) } // MARK: - Test File Management @@ -163,9 +154,9 @@ final class DownloadManagerTests: XCTestCase { func testDeleteFile_ShouldRemoveFileAndTask() async { // Given let block = createMockCourseBlock() - Given(connectivity, .isInternetAvaliable(getter: true)) + let task = createMockDownloadTask() Given(connectivity, .isMobileData(getter: false)) - Given(persistence, .deleteDownloadDataTask(id: .value(block.id), willProduce: { _ in })) + Given(persistence, .getDownloadDataTasksForCourse(.value(block.courseId), willReturn: [task])) let downloadManager = DownloadManager( persistence: persistence, @@ -174,10 +165,10 @@ final class DownloadManagerTests: XCTestCase { ) // When - await downloadManager.deleteFile(blocks: [block]) + await downloadManager.delete(blocks: [block], courseId: block.courseId) // Then - Verify(persistence, 1, .deleteDownloadDataTask(id: .value(block.id))) + Verify(persistence, 1, .deleteDownloadDataTasks(ids: .value([task.id]))) } func testFileUrl_ForFinishedTask_ShouldReturnCorrectUrl() async { @@ -191,7 +182,6 @@ final class DownloadManagerTests: XCTestCase { ) Given(storage, .user(getter: mockUser)) - Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .isMobileData(getter: false)) Given(persistence, .downloadDataTask(for: .value(task.id), willReturn: task)) @@ -212,10 +202,9 @@ final class DownloadManagerTests: XCTestCase { // MARK: - Test Video Size Calculation - func testIsLargeVideosSize_WhenOver1GB_ShouldReturnTrue() { + func testIsLargeVideosSize_WhenOver1GB_ShouldReturnTrue() async { // Given let blocks = [createMockCourseBlock(videoSize: 1_200_000_000)] // 1.2 GB - Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .isMobileData(getter: false)) let downloadManager = DownloadManager( @@ -225,16 +214,15 @@ final class DownloadManagerTests: XCTestCase { ) // When - let isLarge = downloadManager.isLargeVideosSize(blocks: blocks) + let isLarge = await downloadManager.isLargeVideosSize(blocks: blocks) // Then XCTAssertTrue(isLarge) } - func testIsLargeVideosSize_WhenUnder1GB_ShouldReturnFalse() { + func testIsLargeVideosSize_WhenUnder1GB_ShouldReturnFalse() async { // Given let blocks = [createMockCourseBlock(videoSize: 500_000_000)] // 500 MB - Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .isMobileData(getter: false)) let downloadManager = DownloadManager( @@ -244,7 +232,7 @@ final class DownloadManagerTests: XCTestCase { ) // When - let isLarge = downloadManager.isLargeVideosSize(blocks: blocks) + let isLarge = await downloadManager.isLargeVideosSize(blocks: blocks) // Then XCTAssertFalse(isLarge) @@ -259,7 +247,6 @@ final class DownloadManagerTests: XCTestCase { createMockDownloadTask(id: "2") ] - Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .isMobileData(getter: false)) Given(persistence, .getDownloadDataTasks(willReturn: expectedTasks)) @@ -288,7 +275,7 @@ final class DownloadManagerTests: XCTestCase { ) -> DownloadDataTask { DownloadDataTask( id: id, - blockId: "block123", + blockId: "test123", courseId: courseId, userId: 1, url: "https://test.com/video.mp4", @@ -299,7 +286,8 @@ final class DownloadManagerTests: XCTestCase { state: state, type: .video, fileSize: 1000, - lastModified: "2024-01-01" + lastModified: "2024-01-01", + actualSize: 333 ) } diff --git a/Course/Course.xcodeproj/project.pbxproj b/Course/Course.xcodeproj/project.pbxproj index 28e83f266..b7fc7d5ae 100644 --- a/Course/Course.xcodeproj/project.pbxproj +++ b/Course/Course.xcodeproj/project.pbxproj @@ -65,7 +65,9 @@ 02FF6FAA2C20D56A00E44DD8 /* TotalDownloadedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FF6FA92C20D56A00E44DD8 /* TotalDownloadedProgressView.swift */; }; 02FFAD0D29E4347300140E46 /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FFAD0C29E4347300140E46 /* VideoPlayerViewModel.swift */; }; 060E8BCA2B5FD68C0080C952 /* UnitStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060E8BC92B5FD68C0080C952 /* UnitStack.swift */; }; + 0631FF982D2BE67F007CA011 /* CourseDownloadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0631FF972D2BE674007CA011 /* CourseDownloadHelper.swift */; }; 065275352BB1B39C0093BCCA /* PlayerViewControllerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065275342BB1B39C0093BCCA /* PlayerViewControllerHolder.swift */; }; + 065D575D2D4442D700350351 /* CourseDownloadHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065D575C2D4442D000350351 /* CourseDownloadHelperTests.swift */; }; 067B7B4E2BED339200D1768F /* PlayerTrackerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067B7B472BED339200D1768F /* PlayerTrackerProtocol.swift */; }; 067B7B4F2BED339200D1768F /* PlayerDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067B7B482BED339200D1768F /* PlayerDelegateProtocol.swift */; }; 067B7B502BED339200D1768F /* PlayerControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067B7B492BED339200D1768F /* PlayerControllerProtocol.swift */; }; @@ -198,7 +200,9 @@ 02FF6FA92C20D56A00E44DD8 /* TotalDownloadedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TotalDownloadedProgressView.swift; sourceTree = ""; }; 02FFAD0C29E4347300140E46 /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = ""; }; 060E8BC92B5FD68C0080C952 /* UnitStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitStack.swift; sourceTree = ""; }; + 0631FF972D2BE674007CA011 /* CourseDownloadHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDownloadHelper.swift; sourceTree = ""; }; 065275342BB1B39C0093BCCA /* PlayerViewControllerHolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerViewControllerHolder.swift; sourceTree = ""; }; + 065D575C2D4442D000350351 /* CourseDownloadHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDownloadHelperTests.swift; sourceTree = ""; }; 067B7B472BED339200D1768F /* PlayerTrackerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerTrackerProtocol.swift; sourceTree = ""; }; 067B7B482BED339200D1768F /* PlayerDelegateProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerDelegateProtocol.swift; sourceTree = ""; }; 067B7B492BED339200D1768F /* PlayerControllerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerControllerProtocol.swift; sourceTree = ""; }; @@ -519,6 +523,7 @@ 070019A828F6F33600D5FC78 /* Container */ = { isa = PBXGroup; children = ( + 0631FF972D2BE674007CA011 /* CourseDownloadHelper.swift */, 02F0144E28F46474002E513D /* CourseContainerView.swift */, 02F0145628F4A2FF002E513D /* CourseContainerViewModel.swift */, 073512E129C0E400005CFA41 /* BaseCourseViewModel.swift */, @@ -570,6 +575,7 @@ 0766DFD5299ADA4700EBEF6A /* Container */ = { isa = PBXGroup; children = ( + 065D575C2D4442D000350351 /* CourseDownloadHelperTests.swift */, 02F78AEA29E6BCA20038DE30 /* VideoPlayerViewModelTests.swift */, 022EA8CA297AD63B0014A8F7 /* CourseContainerViewModelTests.swift */, ); @@ -924,6 +930,7 @@ buildActionMask = 2147483647; files = ( 0295B1D9297E6DF8003B0C65 /* CourseUnitViewModelTests.swift in Sources */, + 065D575D2D4442D700350351 /* CourseDownloadHelperTests.swift in Sources */, 02F78AEB29E6BCA20038DE30 /* VideoPlayerViewModelTests.swift in Sources */, 023812F3297AC9ED0087098F /* CourseMock.generated.swift in Sources */, DB205BFB2AE81B1200136EC2 /* CourseDateViewModelTests.swift in Sources */, @@ -953,6 +960,7 @@ 067B7B512BED339200D1768F /* PipManagerProtocol.swift in Sources */, 022C64E029ADEA9B000F532B /* Data_UpdatesResponse.swift in Sources */, 02D4FC2E2BBD7C9C00C47748 /* MessageSectionView.swift in Sources */, + 0631FF982D2BE67F007CA011 /* CourseDownloadHelper.swift in Sources */, 02454CA02A2618E70043052A /* YouTubeView.swift in Sources */, 02454CA22A26190A0043052A /* EncodedVideoView.swift in Sources */, 065275352BB1B39C0093BCCA /* PlayerViewControllerHolder.swift in Sources */, diff --git a/Course/Course/Presentation/Container/CourseContainerView.swift b/Course/Course/Presentation/Container/CourseContainerView.swift index 0043f3fe0..cc2bdc9e2 100644 --- a/Course/Course/Presentation/Container/CourseContainerView.swift +++ b/Course/Course/Presentation/Container/CourseContainerView.swift @@ -375,7 +375,8 @@ struct CourseScreensView_Previews: PreviewProvider { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: CourseDownloadHelper(courseStructure: nil, manager: DownloadManagerMock()) ), courseDatesViewModel: CourseDatesViewModel( interactor: CourseInteractor.mock, diff --git a/Course/Course/Presentation/Container/CourseContainerViewModel.swift b/Course/Course/Presentation/Container/CourseContainerViewModel.swift index 6ae7084e8..7d4c5e456 100644 --- a/Course/Course/Presentation/Container/CourseContainerViewModel.swift +++ b/Course/Course/Presentation/Container/CourseContainerViewModel.swift @@ -114,6 +114,7 @@ public final class CourseContainerViewModel: BaseCourseViewModel { private(set) var storage: CourseStorage private let cellularFileSizeLimit: Int = 100 * 1024 * 1024 + var courseHelper: CourseDownloadHelperProtocol public init( interactor: CourseInteractorProtocol, @@ -131,7 +132,8 @@ public final class CourseContainerViewModel: BaseCourseViewModel { enrollmentEnd: Date?, lastVisitedBlockID: String?, coreAnalytics: CoreAnalytics, - selection: CourseTab = CourseTab.course + selection: CourseTab = CourseTab.course, + courseHelper: CourseDownloadHelperProtocol ) { self.interactor = interactor self.authInteractor = authInteractor @@ -150,7 +152,8 @@ public final class CourseContainerViewModel: BaseCourseViewModel { self.lastVisitedBlockID = lastVisitedBlockID self.coreAnalytics = coreAnalytics self.selection = selection.rawValue - + self.courseHelper = courseHelper + self.courseHelper.videoQuality = storage.userSettings?.downloadQuality ?? .auto super.init(manager: manager) addObservers() } @@ -208,7 +211,9 @@ public final class CourseContainerViewModel: BaseCourseViewModel { isShowRefresh = !withProgress do { let courseStructure = try await getCourseStructure(courseID: courseID) - await setDownloadsStates(courseStructure: courseStructure) + courseHelper.courseStructure = courseStructure + await courseHelper.refreshValue() + update(from: courseHelper.value ?? .empty) self.courseStructure = courseStructure if isInternetAvaliable { @@ -221,7 +226,6 @@ public final class CourseContainerViewModel: BaseCourseViewModel { } } courseVideosStructure = await interactor.getCourseVideoBlocks(fullStructure: courseStructure!) - await getDownloadingProgress() isShowProgress = false isShowRefresh = false @@ -287,6 +291,8 @@ public final class CourseContainerViewModel: BaseCourseViewModel { func update(downloadQuality: DownloadQuality) { storage.userSettings?.downloadQuality = downloadQuality userSettings = storage.userSettings + courseHelper.videoQuality = downloadQuality + courseHelper.refreshValue() } @MainActor @@ -313,21 +319,32 @@ public final class CourseContainerViewModel: BaseCourseViewModel { } } - func verticalsBlocksDownloadable(by courseSequential: CourseSequential) -> [CourseBlock] { - let verticals = downloadableVerticals.filter { verticalState in - courseSequential.childs.contains(where: { item in - return verticalState.vertical.id == item.id - }) + @MainActor + func onDownloadViewTap(chapter: CourseChapter, state: DownloadViewState) async { + let blocks = chapter.childs + .flatMap { $0.childs } + .flatMap { $0.childs } + .filter { $0.isDownloadable } + + if state == .available, await isShowedAllowLargeDownloadAlert(blocks: blocks) { + return } - return verticals.flatMap { $0.vertical.childs.filter { $0.isDownloadable } } - } - - func getTasks(sequential: CourseSequential) -> [DownloadDataTask] { - let blocks = verticalsBlocksDownloadable(by: sequential) - let tasks = blocks.compactMap { block in - courseDownloadTasks.first(where: { $0.id == block.id}) + + if state == .available { + analytics.bulkDownloadVideosSection( + courseID: courseStructure?.id ?? "", + sectionID: chapter.id, + videos: blocks.count + ) + } else if state == .finished { + analytics.bulkDeleteVideosSection( + courseID: courseStructure?.id ?? "", + sectionId: chapter.id, + videos: blocks.count + ) } - return tasks + + await download(state: state, blocks: blocks, sequentials: chapter.childs.filter({ $0.isDownloadable })) } func continueDownload() async { @@ -543,6 +560,7 @@ public final class CourseContainerViewModel: BaseCourseViewModel { view: DownloadActionView( actionType: .confirmDownloadCellular, sequentials: sequentials, + downloadedSize: courseHelper.sizeFor(sequentials: sequentials), action: { [weak self] in guard let self else { return } if !self.isEnoughSpace(for: totalFileSize) { @@ -586,12 +604,13 @@ public final class CourseContainerViewModel: BaseCourseViewModel { sequentials: [CourseSequential], totalFileSize: Int, action: @escaping () -> Void = {} - ) async { - await router.presentView( + ) { + router.presentView( transitionStyle: .coverVertical, view: DownloadActionView( actionType: .confirmDownload, - sequentials: manager.updateUnzippedFileSize(for: sequentials), + sequentials: sequentials, + downloadedSize: courseHelper.sizeFor(sequentials: sequentials), action: { [weak self] in guard let self else { return } if !self.isEnoughSpace(for: totalFileSize) { @@ -615,18 +634,20 @@ public final class CourseContainerViewModel: BaseCourseViewModel { } private func presentRemoveDownloadAlert(blocks: [CourseBlock], sequentials: [CourseSequential]) async { - await router.presentView( + router.presentView( transitionStyle: .coverVertical, view: DownloadActionView( actionType: .remove, - sequentials: manager.updateUnzippedFileSize(for: sequentials), + sequentials: sequentials, + downloadedSize: courseHelper.sizeFor(sequentials: sequentials), action: { [weak self] in guard let self else { return } - Task { - await self.manager.deleteFile(blocks: blocks) - self.router.dismiss(animated: true) + if let courseID = self.courseStructure?.id { + Task { + await manager.delete(blocks: blocks, courseId: courseID) + self.router.dismiss(animated: true) + } } - }, cancel: { [weak self] in guard let self else { return } @@ -650,7 +671,7 @@ public final class CourseContainerViewModel: BaseCourseViewModel { let blocks = sequentials.flatMap { $0.childs.flatMap { $0.childs } } .filter { $0.isDownloadable && (!videoOnly || $0.type == .video) } - if state == .available, isShowedAllowLargeDownloadAlert(blocks: blocks) { + if state == .available, await isShowedAllowLargeDownloadAlert(blocks: blocks) { return [] } @@ -677,9 +698,9 @@ public final class CourseContainerViewModel: BaseCourseViewModel { } @MainActor - func isShowedAllowLargeDownloadAlert(blocks: [CourseBlock]) -> Bool { + func isShowedAllowLargeDownloadAlert(blocks: [CourseBlock]) async -> Bool { waitingDownloads = nil - if storage.allowedDownloadLargeFile == false, manager.isLargeVideosSize(blocks: blocks) { + if storage.allowedDownloadLargeFile == false, await manager.isLargeVideosSize(blocks: blocks) { waitingDownloads = blocks router.presentAlert( alertTitle: CourseLocalization.Download.download, @@ -747,7 +768,7 @@ public final class CourseContainerViewModel: BaseCourseViewModel { } } else { if totalFileSize > 100 * 1024 * 1024 { - await presentConfirmDownloadAlert( + presentConfirmDownloadAlert( blocks: blocksToDownload, sequentials: sequentialsToDownload, totalFileSize: totalFileSize, @@ -764,17 +785,6 @@ public final class CourseContainerViewModel: BaseCourseViewModel { } } - func filterNotDownloadedBlocks(_ blocks: [CourseBlock]) async -> [CourseBlock] { - var result: [CourseBlock] = [] - for block in blocks { - let fileUrl = await manager.fileUrl(for: block.id) - if fileUrl == nil { - result.append(block) - } - } - return result - } - @MainActor func isBlockDownloaded(_ block: CourseBlock) -> Bool { courseDownloadTasks.contains { $0.blockId == block.id && $0.state == .finished } @@ -784,8 +794,7 @@ public final class CourseContainerViewModel: BaseCourseViewModel { func stopAllDownloads() async { do { try await manager.cancelAllDownloading() - await setDownloadsStates(courseStructure: self.courseStructure) - await getDownloadingProgress() + await courseHelper.refreshValue() } catch { errorMessage = CoreLocalization.Error.unknownError } @@ -800,61 +809,6 @@ public final class CourseContainerViewModel: BaseCourseViewModel { return blocks } - @MainActor - func getDownloadingProgress() async { - guard let course = courseStructure else { return } - - var totalFilesSize: Int = 0 - var downloadedFilesSize: Int = 0 - var sequentials: [CourseSequential] = [] - - var updatedBlocks: [CourseBlock] = [] - for chapter in course.childs { - for sequential in chapter.childs { - sequentials.append(sequential) - for vertical in sequential.childs { - for block in vertical.childs { - let updatedBlock = await updateFileSizeIfNeeded(for: block) - updatedBlocks.append(updatedBlock) - } - } - } - } - - for block in updatedBlocks { - if let fileSize = block.fileSize { - totalFilesSize += fileSize - } - } - - if connectivity.isInternetAvaliable { - let updatedSequentials = await manager.updateUnzippedFileSize(for: sequentials) - realDownloadedFilesSize = updatedSequentials.flatMap { - $0.childs.flatMap { $0.childs.compactMap { $0.actualFileSize } } - }.reduce(0, { $0 + $1 }) - } - - for task in courseDownloadTasks where task.state == .finished { - if let fileUrl = await manager.fileUrl(for: task.blockId), - let fileSize = getFileSize(at: fileUrl), - task.type == .video { - if fileSize > 0 { - downloadedFilesSize += fileSize - } - } else { - downloadedFilesSize += task.fileSize - } - } - - withAnimation(.linear(duration: 0.3)) { - self.downloadedFilesSize = downloadedFilesSize - } - withAnimation(.linear(duration: 0.3)) { - self.totalFilesSize = totalFilesSize - } - await fetchLargestDownloadBlocks() - } - private func getFileSize(at url: URL) -> Int? { do { let fileAttributes = try FileManager.default.attributesOfItem(atPath: url.path) @@ -866,72 +820,7 @@ public final class CourseContainerViewModel: BaseCourseViewModel { } return nil } - - @MainActor - func setDownloadsStates(courseStructure: CourseStructure?) async { - guard let course = courseStructure else { return } - courseDownloadTasks = await manager.getDownloadTasksForCourse(course.id) - downloadableVerticals = [] - var sequentialsStates: [String: DownloadViewState] = [:] - for chapter in course.childs { - for sequential in chapter.childs where sequential.isDownloadable { - var sequentialsChilds: [DownloadViewState] = [] - for vertical in sequential.childs where vertical.isDownloadable { - var verticalsChilds: [DownloadViewState] = [] - for block in vertical.childs where block.isDownloadable { - if var download = courseDownloadTasks.first(where: { $0.blockId == block.id }) { - if let newDateOfLastModified = block.offlineDownload?.lastModified, - let oldDateOfLastModified = download.lastModified { - if Date(iso8601: newDateOfLastModified) > Date(iso8601: oldDateOfLastModified) { - guard isEnoughSpace(for: block.fileSize ?? 0) else { return } - download.lastModified = newDateOfLastModified - try? await manager.cancelDownloading(task: download) - sequentialsChilds.append(.available) - verticalsChilds.append(.available) - try? await self.manager.addToDownloadQueue(blocks: [block]) - continue - } - } - switch download.state { - case .waiting, .inProgress: - sequentialsChilds.append(.downloading) - verticalsChilds.append(.downloading) - case .finished: - sequentialsChilds.append(.finished) - verticalsChilds.append(.finished) - } - } else { - sequentialsChilds.append(.available) - verticalsChilds.append(.available) - } - } - if verticalsChilds.first(where: { $0 == .downloading }) != nil { - downloadableVerticals.insert(.init(vertical: vertical, state: .downloading)) - } else if verticalsChilds.allSatisfy({ $0 == .finished }) { - downloadableVerticals.insert(.init(vertical: vertical, state: .finished)) - } else { - downloadableVerticals.insert(.init(vertical: vertical, state: .available)) - } - } - if sequentialsChilds.first(where: { $0 == .downloading }) != nil { - sequentialsStates[sequential.id] = .downloading - } else if sequentialsChilds.allSatisfy({ $0 == .finished }) { - sequentialsStates[sequential.id] = .finished - } else { - sequentialsStates[sequential.id] = .available - } - } - let allStates = sequentialsStates.values - if allStates.contains(.downloading) { - downloadAllButtonState = .cancel - } else { - downloadAllButtonState = .start - } - - self.sequentialsDownloadState = sequentialsStates - } - } - + private func findContinueVertical(blockID: String, courseStructure: CourseStructure) -> ContinueWith? { for chapterIndex in courseStructure.childs.indices { let chapter = courseStructure.childs[chapterIndex] @@ -986,47 +875,6 @@ public final class CourseContainerViewModel: BaseCourseViewModel { } // MARK: Larges Downloads - - @MainActor - func fetchLargestDownloadBlocks() async { - let allBlocks = courseStructure?.childs.flatMap { $0.childs.flatMap { $0.childs.flatMap { $0.childs } } } ?? [] - let downloadedBlocks = allBlocks.filter { block in - if let task = courseDownloadTasks.first(where: { $0.blockId == block.id }) { - return task.state == .finished - } - return false - } - - var updatedDownloadedBlocks: [CourseBlock] = [] - - for block in downloadedBlocks { - let updatedBlock = await updateFileSizeIfNeeded(for: block) - updatedDownloadedBlocks.append(updatedBlock) - } - - let filteredBlocks = Array( - updatedDownloadedBlocks - .filter { $0.fileSize != nil } - .sorted { $0.fileSize! > $1.fileSize! } - .prefix(5) - ) - - withAnimation(.linear(duration: 0.3)) { - largestDownloadBlocks = filteredBlocks - } - } - - @MainActor - func updateFileSizeIfNeeded(for block: CourseBlock) async -> CourseBlock { - var updatedBlock = block - if let fileUrl = await manager.fileUrl(for: block.id), - let fileSize = getFileSize(at: fileUrl), fileSize > 0, - block.type == .video { - updatedBlock.actualFileSize = fileSize - } - return updatedBlock - } - @MainActor func removeBlock(_ block: CourseBlock) async { router.presentView( @@ -1034,13 +882,16 @@ public final class CourseContainerViewModel: BaseCourseViewModel { view: DownloadActionView( actionType: .remove, courseBlocks: [block], + downloadedSize: courseHelper.sizeFor(blocks: [block]), action: { [weak self] in guard let self else { return } withAnimation(.linear(duration: 0.3)) { self.largestDownloadBlocks.removeAll { $0.id == block.id } } Task { - await self.manager.deleteFile(blocks: [block]) + if let courseID = self.courseStructure?.id { + await self.manager.delete(blocks: [block], courseId: courseID) + } } self.router.dismiss(animated: true) }, @@ -1055,6 +906,7 @@ public final class CourseContainerViewModel: BaseCourseViewModel { @MainActor func removeAllBlocks() async { + let totalSize = courseDownloadTasks.reduce(0, { $0 + $1.actualSize }) let allBlocks = courseStructure?.childs.flatMap { $0.childs.flatMap { $0.childs.flatMap { $0.childs } } } ?? [] let blocksToRemove = allBlocks.filter { block in if let task = courseDownloadTasks.first(where: { $0.blockId == block.id }) { @@ -1069,11 +921,14 @@ public final class CourseContainerViewModel: BaseCourseViewModel { actionType: .remove, courseBlocks: blocksToRemove, courseName: courseStructure?.displayName ?? "", + downloadedSize: totalSize, action: { [weak self] in guard let self else { return } Task { await self.stopAllDownloads() - await self.manager.deleteFile(blocks: blocksToRemove) + if let courseID = self.courseStructure?.id { + await self.manager.delete(blocks: blocksToRemove, courseId: courseID) + } } self.router.dismiss(animated: true) }, @@ -1086,26 +941,33 @@ public final class CourseContainerViewModel: BaseCourseViewModel { ) } + private func update(from value: CourseDownloadValue) { + downloadableVerticals = value.downloadableVerticals + downloadAllButtonState = value.state + courseDownloadTasks = value.courseDownloadTasks + sequentialsDownloadState = value.sequentialsStates + withAnimation(.linear(duration: 0.3)) { + downloadedFilesSize = value.downloadedFilesSize + totalFilesSize = value.totalFilesSize + largestDownloadBlocks = value.largestBlocks + } + } + private func addObservers() { - manager.eventPublisher() - .sink { [weak self] state in - guard let self else { return } - if case .progress = state { return } - Task { - debugLog(state, "--- state ---") - await self.setDownloadsStates(courseStructure: self.courseStructure) - await self.getDownloadingProgress() - } + courseHelper + .publisher() + .sink {[weak self] value in + self?.update(from: value) } .store(in: &cancellables) - + connectivity.internetReachableSubject .sink { [weak self] _ in guard let self else { return } self.isInternetAvaliable = self.connectivity.isInternetAvaliable } .store(in: &cancellables) - + NotificationCenter.default.addObserver( self, selector: #selector(handleShiftDueDates), @@ -1113,17 +975,12 @@ public final class CourseContainerViewModel: BaseCourseViewModel { ) completionPublisher + .receive(on: DispatchQueue.main) .sink { [weak self] _ in guard let self = self else { return } updateCourseProgress = true } .store(in: &cancellables) - - $sequentialsDownloadState.sink(receiveValue: { states in - if states.values.allSatisfy({ $0 == .available }) { - self.downloadAllButtonState = .start - } - }).store(in: &cancellables) } deinit { @@ -1155,11 +1012,11 @@ extension CourseContainerViewModel { } } -struct VerticalsDownloadState: Hashable { - let vertical: CourseVertical - let state: DownloadViewState +public struct VerticalsDownloadState: Hashable, Sendable { + public let vertical: CourseVertical + public let state: DownloadViewState - var downloadableBlocks: [CourseBlock] { + public var downloadableBlocks: [CourseBlock] { vertical.childs.filter { $0.isDownloadable && $0.type == .video } } } diff --git a/Course/Course/Presentation/Container/CourseDownloadHelper.swift b/Course/Course/Presentation/Container/CourseDownloadHelper.swift new file mode 100644 index 000000000..359245bc5 --- /dev/null +++ b/Course/Course/Presentation/Container/CourseDownloadHelper.swift @@ -0,0 +1,283 @@ +// +// CourseDownloadHelper.swift +// Course +// +// Created by Vadim Kuznetsov on 6.01.25. +// + +import Combine +import Core +import Foundation + +//sourcery: AutoMockable +public protocol CourseDownloadHelperProtocol: Sendable { + var value: CourseDownloadValue? { get } + var courseStructure: CourseStructure? { get set } + var videoQuality: DownloadQuality { get set } + + func publisher() -> AnyPublisher + func progressPublisher() -> AnyPublisher + func refreshValue() + func refreshValue() async + func sizeFor(block: CourseBlock) -> Int? + func sizeFor(blocks: [CourseBlock]) -> Int + func sizeFor(sequential: CourseSequential) -> Int + func sizeFor(sequentials: [CourseSequential]) -> Int + func cancelDownloading(task: DownloadDataTask) async throws +} + +public struct CourseDownloadValue: Sendable, Equatable { + public static let empty = CourseDownloadValue( + courseDownloadTasks: [], + allDownloadTasks: [], + notFinishedTasks: [], + downloadableVerticals: [], + sequentialsStates: [:], + totalFilesSize: 0, + downloadedFilesSize: 0, + largestBlocks: [], + state: .start + ) + public var currentDownloadTask: DownloadDataTask? + public let courseDownloadTasks: [DownloadDataTask] + public let allDownloadTasks: [DownloadDataTask] + public let notFinishedTasks: [DownloadDataTask] + public let downloadableVerticals: Set + public let sequentialsStates: [String: DownloadViewState] + public let totalFilesSize: Int + public let downloadedFilesSize: Int + public let largestBlocks: [CourseBlock] + let state: OfflineView.DownloadAllState + + mutating func setCurrentDownloadTask(task: DownloadDataTask?) { + self.currentDownloadTask = task + } +} + +/// Helper to obtain download info for course +public final class CourseDownloadHelper: CourseDownloadHelperProtocol, @unchecked Sendable { + + public var value: CourseDownloadValue? + public var courseStructure: CourseStructure? + public var videoQuality: DownloadQuality = .auto + + private let queue: DispatchQueue = .init(label: "course.download.helper.queue") + private let manager: DownloadManagerProtocol + private var sourcePublisher: PassthroughSubject = .init() + private var sourceProgressPublisher: PassthroughSubject = .init() + private var cancellables = Set() + + public func publisher() -> AnyPublisher { + sourcePublisher.share().eraseToAnyPublisher() + } + + public func progressPublisher() -> AnyPublisher { + sourceProgressPublisher + .throttle(for: .seconds(0.1), scheduler: queue, latest: true) + .share() + .receive(on: DispatchQueue.main) + .eraseToAnyPublisher() + } + + public init (courseStructure: CourseStructure?, manager: DownloadManagerProtocol) { + self.manager = manager + self.courseStructure = courseStructure + manager.eventPublisher() + .sink { [weak self] state in + guard let self else { return } + + self.queue.async {[weak self] in + if case let .progress(currentTask) = state { + if let value = self?.value { + var newValue = value + newValue.setCurrentDownloadTask(task: currentTask) + self?.value = newValue + } + self?.sourceProgressPublisher.send(currentTask) + return + } + + self?.refreshValue() + } + } + .store(in: &cancellables) + } + + public func refreshValue() { + Task(priority: .background) { + await refreshValue() + } + } + + public func cancelDownloading(task: DownloadDataTask) async throws { + try await manager.cancelDownloading(task: task) + } + + public func refreshValue() async { + guard let courseStructure else { return } + let downloadTasks = await manager.getDownloadTasks() + await enumerate( + tasks: downloadTasks, + courseStructure: courseStructure, + currentDownloadTask: await manager.getCurrentDownloadTask() + ) + } + + public func sizeFor(block: CourseBlock) -> Int? { + if block.type == .video { + return block.encodedVideo?.video(downloadQuality: videoQuality)?.fileSize + } + return block.fileSize + } + + public func sizeFor(blocks: [CourseBlock]) -> Int { + let filteredBlocks = blocks.filter { $0.isDownloadable } + let sizes = filteredBlocks.compactMap { sizeFor(block: $0) } + return sizes.reduce(0, +) + } + + public func sizeFor(sequential: CourseSequential) -> Int { + let blocks = sequential.childs.flatMap({ $0.childs }) + return sizeFor(blocks: blocks) + } + + public func sizeFor(sequentials: [CourseSequential]) -> Int { + let sizes = sequentials.map { sizeFor(sequential: $0) } + return sizes.reduce(0, +) + } + + // swiftlint:disable function_body_length + private func enumerate( + tasks: [DownloadDataTask], + courseStructure: CourseStructure, + currentDownloadTask: DownloadDataTask? + ) async { + await withCheckedContinuation { continuation in + queue.async {[weak self] in + guard let self else { return } + let notFinishedTasks: [DownloadDataTask] = tasks.filter { $0.state != .finished } + .sorted(by: { $0.state.order < $1.state.order }) + let courseDownloadTasks = tasks.filter { $0.courseId == courseStructure.id } + var downloadableVerticals: Set = [] + var sequentialsStates: [String: DownloadViewState] = [:] + var totalFilesSize: Int = 0 + var downloadedFilesSize: Int = 0 + var largestBlocks: [(block: CourseBlock, task: DownloadDataTask)] = [] + var downloadState: OfflineView.DownloadAllState = .start + for chapter in courseStructure.childs { + for sequential in chapter.childs { + var sequentialsChilds: [DownloadViewState] = [] + for vertical in sequential.childs { + var verticalsChilds: [DownloadViewState] = [] + for block in vertical.childs where block.isDownloadable { + if var download = courseDownloadTasks.first(where: { $0.blockId == block.id }) { + if download.state == .finished, download.actualSize > 0 { + downloadedFilesSize += download.actualSize + totalFilesSize += download.actualSize + largestBlocks.append((block, download)) + largestBlocks = Array( + largestBlocks + .sorted { $0.task.actualSize > $1.task.actualSize } + .prefix(5) + ) + } else { + totalFilesSize += sizeFor(block: block) ?? 0 + } + if let newDateOfLastModified = block.offlineDownload?.lastModified, + let oldDateOfLastModified = download.lastModified { + if Date(iso8601: newDateOfLastModified) > Date(iso8601: oldDateOfLastModified) { + guard isEnoughSpace(for: sizeFor(block: block) ?? 0) else { return } + download.lastModified = newDateOfLastModified + sequentialsChilds.append(.available) + verticalsChilds.append(.available) + Task { + try? await self.manager.cancelDownloading(task: download) + try? await self.manager.addToDownloadQueue(blocks: [block]) + } + continue + } + } + switch download.state { + case .waiting, .inProgress: + sequentialsChilds.append(.downloading) + verticalsChilds.append(.downloading) + case .finished: + sequentialsChilds.append(.finished) + verticalsChilds.append(.finished) + } + } else { + sequentialsChilds.append(.available) + verticalsChilds.append(.available) + totalFilesSize += sizeFor(block: block) ?? 0 + } + } + guard !verticalsChilds.isEmpty else { continue } + if verticalsChilds.first(where: { $0 == .downloading }) != nil { + downloadableVerticals.insert(.init(vertical: vertical, state: .downloading)) + } else if verticalsChilds.allSatisfy({ $0 == .finished }) { + downloadableVerticals.insert(.init(vertical: vertical, state: .finished)) + } else { + downloadableVerticals.insert(.init(vertical: vertical, state: .available)) + } + } + + guard !sequentialsChilds.isEmpty else { continue } + if sequentialsChilds.first(where: { $0 == .downloading }) != nil { + sequentialsStates[sequential.id] = .downloading + } else if sequentialsChilds.allSatisfy({ $0 == .finished }) { + sequentialsStates[sequential.id] = .finished + } else { + sequentialsStates[sequential.id] = .available + } + } + } + if sequentialsStates.values.allSatisfy({ $0 == .available }) { + downloadState = .start + } + + if sequentialsStates.values.contains(.downloading) { + downloadState = .cancel + } + let value = CourseDownloadValue( + currentDownloadTask: currentDownloadTask, + courseDownloadTasks: courseDownloadTasks, + allDownloadTasks: tasks, + notFinishedTasks: notFinishedTasks, + downloadableVerticals: downloadableVerticals, + sequentialsStates: sequentialsStates, + totalFilesSize: totalFilesSize, + downloadedFilesSize: downloadedFilesSize, + largestBlocks: largestBlocks.map { $0.block }, + state: downloadState + ) + + self.value = value + DispatchQueue.main.async { + self.sourcePublisher.send(value) + } + + continuation.resume() + } + } + } + // swiftlint:enable function_body_length + + private func isEnoughSpace(for fileSize: Int) -> Bool { + if let freeSpace = getFreeDiskSpace() { + return freeSpace > Int(Double(fileSize) * 1.2) + } + return false + } + + private func getFreeDiskSpace() -> Int? { + do { + let attributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String) + if let freeSpace = attributes[.systemFreeSize] as? Int64 { + return Int(freeSpace) + } + } catch { + print("Error retrieving free disk space: \(error.localizedDescription)") + } + return nil + } +} diff --git a/Course/Course/Presentation/CourseAnalytics.swift b/Course/Course/Presentation/CourseAnalytics.swift index 5036886c8..098276f86 100644 --- a/Course/Course/Presentation/CourseAnalytics.swift +++ b/Course/Course/Presentation/CourseAnalytics.swift @@ -121,6 +121,17 @@ public protocol CourseAnalytics { videos: Int ) + func bulkDownloadVideosSection( + courseID: String, + sectionID: String, + videos: Int + ) + + func bulkDeleteVideosSection( + courseID: String, + sectionId: String, + videos: Int) + func videoLoaded(courseID: String, blockID: String, videoURL: String) func videoPlayed(courseID: String, blockID: String, videoURL: String) @@ -230,6 +241,17 @@ class CourseAnalyticsMock: CourseAnalytics { videos: Int ) {} + public func bulkDownloadVideosSection( + courseID: String, + sectionID: String, + videos: Int + ) {} + + public func bulkDeleteVideosSection( + courseID: String, + sectionId: String, + videos: Int) {} + public func videoLoaded(courseID: String, blockID: String, videoURL: String) {} public func videoPlayed(courseID: String, blockID: String, videoURL: String) {} diff --git a/Course/Course/Presentation/CourseRouter.swift b/Course/Course/Presentation/CourseRouter.swift index fb40a5ad9..ddd03f8e5 100644 --- a/Course/Course/Presentation/CourseRouter.swift +++ b/Course/Course/Presentation/CourseRouter.swift @@ -56,11 +56,6 @@ public protocol CourseRouter: BaseRouter { courseStructure: CourseStructure, blockLink: String ) - - func showDownloads( - downloads: [DownloadDataTask], - manager: DownloadManagerProtocol - ) func showDatesAndCalendar() @@ -119,11 +114,6 @@ public class CourseRouterMock: BaseRouterMock, CourseRouter { blockLink: String ) {} - public func showDownloads( - downloads: [Core.DownloadDataTask], - manager: Core.DownloadManagerProtocol - ) {} - public func showDatesAndCalendar() {} public func showGatedContentError(url: String) {} diff --git a/Course/Course/Presentation/Downloads/DownloadsView.swift b/Course/Course/Presentation/Downloads/DownloadsView.swift index 2926d82b0..61ae87026 100644 --- a/Course/Course/Presentation/Downloads/DownloadsView.swift +++ b/Course/Course/Presentation/Downloads/DownloadsView.swift @@ -23,17 +23,13 @@ public struct DownloadsView: View { public init( isSheet: Bool = true, router: CourseRouter, - courseId: String? = nil, - downloads: [DownloadDataTask] = [], - manager: DownloadManagerProtocol + courseHelper: CourseDownloadHelperProtocol ) { self.isSheet = isSheet self._viewModel = .init( wrappedValue: .init( router: router, - courseId: courseId, - downloads: downloads, - manager: manager + helper: courseHelper ) ) } diff --git a/Course/Course/Presentation/Downloads/DownloadsViewModel.swift b/Course/Course/Presentation/Downloads/DownloadsViewModel.swift index 766101955..3b902d644 100644 --- a/Course/Course/Presentation/Downloads/DownloadsViewModel.swift +++ b/Course/Course/Presentation/Downloads/DownloadsViewModel.swift @@ -16,23 +16,18 @@ final class DownloadsViewModel: ObservableObject { // MARK: - Properties @Published private(set) var downloads: [DownloadDataTask] = [] - private let courseId: String? let router: CourseRouter - private let manager: DownloadManagerProtocol + private let helper: CourseDownloadHelperProtocol private var cancellables = Set() init( router: CourseRouter, - courseId: String? = nil, - downloads: [DownloadDataTask] = [], - manager: DownloadManagerProtocol + helper: CourseDownloadHelperProtocol ) { self.router = router - self.courseId = courseId - self.manager = manager - self.downloads = downloads + self.helper = helper Task { await configure() } @@ -50,7 +45,7 @@ final class DownloadsViewModel: ObservableObject { @MainActor func cancelDownloading(task: DownloadDataTask) async { do { - try await manager.cancelDownloading(task: task) + try await helper.cancelDownloading(task: task) downloads.removeAll(where: { $0.id == task.id }) } catch { debugLog(error) @@ -59,41 +54,22 @@ final class DownloadsViewModel: ObservableObject { @MainActor private func configure() async { - defer { - filter() - } - if !downloads.isEmpty { - return - } - if let courseId = courseId { - downloads = await manager.getDownloadTasksForCourse(courseId) - return - } - downloads = await manager.getDownloadTasks() + downloads = helper.value?.notFinishedTasks ?? [] } private func observers() { - manager.eventPublisher() - .sink { [weak self] event in - guard let self else { return } - switch event { - case .progress(let progress, let downloadData): - if let firstIndex = downloads.firstIndex(where: { $0.id == downloadData.id }) { - self.downloads[firstIndex].progress = progress - } - case .finished(let downloadData): - downloads.removeAll(where: { $0.id == downloadData.id }) - default: - break + helper.publisher() + .sink {[weak self] value in + self?.downloads = value.notFinishedTasks + } + .store(in: &cancellables) + helper.progressPublisher() + .sink {[weak self] task in + if let firstIndex = self?.downloads.firstIndex(where: { $0.id == task.id }) { + self?.downloads[firstIndex].progress = task.progress } } .store(in: &cancellables) } - - private func filter() { - downloads = downloads - .filter { $0.state == .inProgress || $0.state == .waiting } - .sorted(by: { $0.state.order < $1.state.order }) - } } diff --git a/Course/Course/Presentation/Offline/OfflineView.swift b/Course/Course/Presentation/Offline/OfflineView.swift index 852498383..9b5cee5ed 100644 --- a/Course/Course/Presentation/Offline/OfflineView.swift +++ b/Course/Course/Presentation/Offline/OfflineView.swift @@ -258,10 +258,11 @@ struct OfflineView: View { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: CourseDownloadHelper(courseStructure: nil, manager: DownloadManagerMock()) ) - return OfflineView( + OfflineView( courseID: "123", coordinate: .constant(0), collapsed: .constant(false), diff --git a/Course/Course/Presentation/Offline/Subviews/LargestDownloadsView.swift b/Course/Course/Presentation/Offline/Subviews/LargestDownloadsView.swift index 6099d7e38..7aa0930d7 100644 --- a/Course/Course/Presentation/Offline/Subviews/LargestDownloadsView.swift +++ b/Course/Course/Presentation/Offline/Subviews/LargestDownloadsView.swift @@ -80,11 +80,6 @@ public struct LargestDownloadsView: View { self.isEditing = false } }) - .onAppear { - Task { - await viewModel.fetchLargestDownloadBlocks() - } - } } } @@ -107,7 +102,8 @@ struct LargestDownloadsView_Previews: PreviewProvider { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: CourseDownloadHelper(courseStructure: nil, manager: DownloadManagerMock()) ) LargestDownloadsView(viewModel: vm) diff --git a/Course/Course/Presentation/Outline/CourseOutlineView.swift b/Course/Course/Presentation/Outline/CourseOutlineView.swift index ec6eb502f..2c2c8565e 100644 --- a/Course/Course/Presentation/Outline/CourseOutlineView.swift +++ b/Course/Course/Presentation/Outline/CourseOutlineView.swift @@ -118,7 +118,6 @@ public struct CourseOutlineView: View { // MARK: - Sections CustomDisclosureGroup( - isVideo: isVideo, course: course, proxy: proxy, viewModel: viewModel @@ -201,7 +200,7 @@ public struct CourseOutlineView: View { .ignoresSafeArea() ) .sheet(isPresented: $showingDownloads) { - DownloadsView(router: viewModel.router, manager: viewModel.manager) + DownloadsView(router: viewModel.router, courseHelper: viewModel.courseHelper) } .sheet(isPresented: $showingVideoDownloadQuality) { viewModel.storage.userSettings.map { @@ -344,7 +343,8 @@ struct CourseOutlineView_Previews: PreviewProvider { enrollmentStart: Date(), enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: CourseDownloadHelper(courseStructure: nil, manager: DownloadManagerMock()) ) Task { await withTaskGroup(of: Void.self) { group in diff --git a/Course/Course/Presentation/Outline/CourseVertical/CourseVerticalView.swift b/Course/Course/Presentation/Outline/CourseVertical/CourseVerticalView.swift index cdbf81318..ed7b60af4 100644 --- a/Course/Course/Presentation/Outline/CourseVertical/CourseVerticalView.swift +++ b/Course/Course/Presentation/Outline/CourseVertical/CourseVerticalView.swift @@ -179,7 +179,6 @@ struct CourseVerticalView_Previews: PreviewProvider { chapters: chapters, chapterIndex: 0, sequentialIndex: 0, - manager: DownloadManagerMock(), router: CourseRouterMock(), analytics: CourseAnalyticsMock(), connectivity: Connectivity() diff --git a/Course/Course/Presentation/Outline/CourseVertical/CourseVerticalViewModel.swift b/Course/Course/Presentation/Outline/CourseVertical/CourseVerticalViewModel.swift index eda090f0f..98df0cc27 100644 --- a/Course/Course/Presentation/Outline/CourseVertical/CourseVerticalViewModel.swift +++ b/Course/Course/Presentation/Outline/CourseVertical/CourseVerticalViewModel.swift @@ -7,15 +7,13 @@ import SwiftUI import Core -import Combine import OEXFoundation -public final class CourseVerticalViewModel: BaseCourseViewModel { +public final class CourseVerticalViewModel: ObservableObject, @unchecked Sendable { let router: CourseRouter let analytics: CourseAnalytics let connectivity: ConnectivityProtocol @Published var verticals: [CourseVertical] - @Published var downloadState: [String: DownloadViewState] = [:] @Published var showError: Bool = false let chapters: [CourseChapter] let chapterIndex: Int @@ -33,7 +31,6 @@ public final class CourseVerticalViewModel: BaseCourseViewModel { chapters: [CourseChapter], chapterIndex: Int, sequentialIndex: Int, - manager: DownloadManagerProtocol, router: CourseRouter, analytics: CourseAnalytics, connectivity: ConnectivityProtocol @@ -45,79 +42,8 @@ public final class CourseVerticalViewModel: BaseCourseViewModel { self.analytics = analytics self.connectivity = connectivity self.verticals = chapters[chapterIndex].childs[sequentialIndex].childs - super.init(manager: manager) - - do { - try manager.publisher() - .sink(receiveValue: { [weak self] _ in - guard let self else { return } - Task { - await self.setDownloadsStates() - } - }) - .store(in: &cancellables) - Task { - await setDownloadsStates() - } - } catch { - debugLog(error) - } - } - - @MainActor - func onDownloadViewTap(blockId: String, state: DownloadViewState) async { - if let vertical = verticals.first(where: { $0.id == blockId }) { - let blocks = vertical.childs.filter { $0.isDownloadable } - do { - switch state { - case .available: - try await manager.addToDownloadQueue(blocks: blocks) - downloadState[vertical.id] = .downloading - case .downloading: - try await manager.cancelDownloading(courseId: vertical.courseId, blocks: blocks) - downloadState[vertical.id] = .available - case .finished: - await manager.deleteFile(blocks: blocks) - downloadState[vertical.id] = .available - } - } catch let error { - if error is NoWiFiError { - errorMessage = CoreLocalization.Error.wifi - } - } - } } - @MainActor - private func setDownloadsStates() async { - guard let courseId = verticals.first?.courseId else { return } - let downloadTasks = await manager.getDownloadTasksForCourse(courseId) - var states: [String: DownloadViewState] = [:] - for vertical in verticals where vertical.isDownloadable { - var childs: [DownloadViewState] = [] - for block in vertical.childs where block.isDownloadable { - if let download = downloadTasks.first(where: { $0.id == block.id }) { - switch download.state { - case .waiting, .inProgress: - childs.append(.downloading) - case .finished: - childs.append(.finished) - } - } else { - childs.append(.available) - } - } - if childs.first(where: { $0 == .downloading }) != nil { - states[vertical.id] = .downloading - } else if childs.allSatisfy({ $0 == .finished }) { - states[vertical.id] = .finished - } else { - states[vertical.id] = .available - } - } - downloadState = states - } - func trackVerticalClicked( courseId: String, courseName: String, diff --git a/Course/Course/Presentation/Subviews/ActionViews/DownloadActionView.swift b/Course/Course/Presentation/Subviews/ActionViews/DownloadActionView.swift index 1dbbce63c..9df9db751 100644 --- a/Course/Course/Presentation/Subviews/ActionViews/DownloadActionView.swift +++ b/Course/Course/Presentation/Subviews/ActionViews/DownloadActionView.swift @@ -27,6 +27,7 @@ public struct DownloadActionView: View { private let sequentials: [CourseSequential] private let courseBlocks: [CourseBlock] private let courseName: String? + private let downloadedSize: Int? private let action: () -> Void private let cancel: () -> Void @State private var fadeEffect: Bool = false @@ -36,6 +37,7 @@ public struct DownloadActionView: View { init( actionType: ContentActionType, sequentials: [CourseSequential], + downloadedSize: Int? = nil, action: @escaping () -> Void, cancel: @escaping () -> Void ) { @@ -45,12 +47,14 @@ public struct DownloadActionView: View { self.courseBlocks = [] self.action = action self.cancel = cancel + self.downloadedSize = downloadedSize } init( actionType: ContentActionType, courseBlocks: [CourseBlock], courseName: String? = nil, + downloadedSize: Int? = nil, action: @escaping () -> Void, cancel: @escaping () -> Void ) { @@ -60,6 +64,7 @@ public struct DownloadActionView: View { self.courseName = courseName self.action = action self.cancel = cancel + self.downloadedSize = downloadedSize } public var body: some View { @@ -269,7 +274,9 @@ public struct DownloadActionView: View { } private var totalSize: String { - if sequentials.isEmpty { + if let downloadedSize { + downloadedSize.formattedFileSize() + } else if sequentials.isEmpty { courseBlocks.reduce(0) { $0 + ($1.fileSize ?? 0) }.formattedFileSize() } else { sequentials.reduce(0) { $0 + $1.totalSize }.formattedFileSize() diff --git a/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarView.swift b/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarView.swift index 0f59eacf1..ddc9fc94e 100644 --- a/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarView.swift +++ b/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarView.swift @@ -47,7 +47,7 @@ struct CourseVideoDownloadBarView: View { toggle } .padding(.vertical, 10) - if viewModel.isOn, !viewModel.allVideosDownloaded { + if viewModel.isOn, !viewModel.isAllVideosDownloaded { ProgressView(value: viewModel.progress, total: 1) .tint(Theme.Colors.accentColor) .accessibilityIdentifier("progress_line_view") @@ -57,7 +57,7 @@ struct CourseVideoDownloadBarView: View { .contentShape(Rectangle()) .onTapGesture { Task { - let downloads = await viewModel.allActiveDownloads() + let downloads = viewModel.allActiveDownloads() if !downloads.isEmpty { onTap?() } @@ -70,7 +70,7 @@ struct CourseVideoDownloadBarView: View { private var image: some View { VStack { - if viewModel.isOn, !viewModel.allVideosDownloaded { + if viewModel.isOn, !viewModel.isAllVideosDownloaded { ProgressView() .accessibilityIdentifier("progress_view") } else { diff --git a/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarViewModel.swift b/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarViewModel.swift index 2e259db28..03ffcb755 100644 --- a/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarViewModel.swift +++ b/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarViewModel.swift @@ -40,70 +40,34 @@ final class CourseVideoDownloadBarViewModel: ObservableObject { } } - var progress: Double { - guard let currentDownloadTask = currentDownloadTask else { - return 0.0 - } - guard let index = courseViewModel.courseDownloadTasks.firstIndex( - where: { $0.id == currentDownloadTask.id && $0.type == .video } - ) else { - return 0.0 + /// total progress of downloading video files + var progress: Double = 0 + + var downloadableVerticals: Set = [] { + didSet { + let downloading = downloadableVerticals.filter { $0.state == .downloading } + downloadingVideos = downloading.flatMap { $0.downloadableBlocks }.count + + let finished = downloadableVerticals.filter { $0.state == .finished } + totalFinishedVideos = finished.flatMap { $0.downloadableBlocks }.count + + let inProgress = downloadableVerticals.filter { $0.state != .finished } + remainingVideos = inProgress.flatMap { $0.downloadableBlocks }.count + + let totalFinishedCount = finished.count + isAllVideosDownloaded = totalFinishedCount == downloadableVerticals.count } - courseViewModel.courseDownloadTasks[index].progress = currentDownloadTask.progress - let videoTasks = courseViewModel.courseDownloadTasks.filter { $0.type == .video } - return videoTasks.reduce(0) { $0 + $1.progress } / Double(videoTasks.count) - } - - var downloadableVerticals: Set { - courseViewModel.downloadableVerticals.filter { $0.downloadableBlocks.contains { $0.type == .video } } - } - - var allVideosDownloaded: Bool { - let totalFinishedCount = downloadableVerticals.filter { $0.state == .finished }.count - return totalFinishedCount == downloadableVerticals.count } - var remainingVideos: Int { - let inProgress = downloadableVerticals.filter { $0.state != .finished } - return inProgress.flatMap { $0.downloadableBlocks }.count - } + var isAllVideosDownloaded: Bool = false - var downloadingVideos: Int { - let downloading = downloadableVerticals.filter { $0.state == .downloading } - return downloading.flatMap { $0.downloadableBlocks }.count - } + var remainingVideos: Int = 0 - var totalFinishedVideos: Int { - let finished = downloadableVerticals.filter { $0.state == .finished } - return finished.flatMap { $0.downloadableBlocks }.count - } + var downloadingVideos: Int = 0 - var totalSize: String? { - let downloadQuality = courseViewModel.userSettings?.downloadQuality ?? .auto - let mb = courseStructure.totalVideosSizeInMb( - downloadQuality: downloadQuality - ) + var totalFinishedVideos: Int = 0 - if mb == 0 { return nil } - - if isOn { - let size = mb - calculateSize(value: mb, percentage: progress * 100) - if size == 0 { - return sizeInMbOrGb(size: mb) - } - return sizeInMbOrGb(size: size) - } - - let size = blockToMB( - data: Set(downloadableVerticals - .filter { $0.state != .finished } - .flatMap { $0.downloadableBlocks } - ), - downloadQuality: downloadQuality - ) - - return sizeInMbOrGb(size: size) - } + var totalSize: String? private func sizeInMbOrGb(size: Double) -> String { if size >= 1024.0 { @@ -124,14 +88,14 @@ final class CourseVideoDownloadBarViewModel: ObservableObject { observers() } - func allActiveDownloads() async -> [DownloadDataTask] { - await courseViewModel.manager.getDownloadTasks() + func allActiveDownloads() -> [DownloadDataTask] { + (courseViewModel.courseHelper.value?.allDownloadTasks ?? []) .filter { $0.state == .inProgress || $0.state == .waiting } } @MainActor func onToggle() async { - if allVideosDownloaded { + if isAllVideosDownloaded { courseViewModel.router.presentAlert( alertTitle: CourseLocalization.Alert.warning, alertMessage: "\(CourseLocalization.Alert.deleteAllVideos) \"\(courseStructure.displayName)\"?", @@ -181,7 +145,7 @@ final class CourseVideoDownloadBarViewModel: ObservableObject { private func downloadAll(isOn: Bool) async { let blocks = downloadableVerticals.flatMap { $0.vertical.childs } - if isOn, courseViewModel.isShowedAllowLargeDownloadAlert(blocks: blocks) { + if isOn, await courseViewModel.isShowedAllowLargeDownloadAlert(blocks: blocks) { return } @@ -207,7 +171,10 @@ final class CourseVideoDownloadBarViewModel: ObservableObject { let availableCount = downloadableVerticals.filter { $0.state == .available }.count let finishedCount = downloadableVerticals.filter { $0.state == .finished }.count let downloadingCount = downloadableVerticals.filter { $0.state == .downloading }.count - + self.downloadableVerticals = downloadableVerticals.filter { + $0.downloadableBlocks.contains { $0.type == .video } + } + if downloadingCount == totalCount, totalCount > 0 { self.isOn = true return @@ -230,24 +197,76 @@ final class CourseVideoDownloadBarViewModel: ObservableObject { } private func observers() { - currentDownloadTask = courseViewModel.manager.currentDownloadTask - toggleStateIsOn(downloadableVerticals: courseViewModel.downloadableVerticals) - courseViewModel.$downloadableVerticals - .sink { [weak self] value in - guard let self else { return } - self.currentDownloadTask = self.courseViewModel.manager.currentDownloadTask - self.toggleStateIsOn(downloadableVerticals: value) - } - .store(in: &cancellables) - courseViewModel.manager.eventPublisher() - .sink { [weak self] state in - guard let self else { return } - if case .progress = state { - self.currentDownloadTask = self.courseViewModel.manager.currentDownloadTask - } - self.toggleStateIsOn(downloadableVerticals: self.courseViewModel.downloadableVerticals) + currentDownloadTask = courseViewModel.courseHelper.value?.currentDownloadTask + toggleStateIsOn(downloadableVerticals: courseViewModel.courseHelper.value?.downloadableVerticals ?? []) + calculateProgress() + calculateTotalSize() + courseViewModel.courseHelper.progressPublisher() + .sink {[weak self] task in + self?.currentDownloadTask = task + self?.calculateProgress() + self?.calculateTotalSize() } .store(in: &cancellables) + + courseViewModel.courseHelper.publisher() + .sink {[weak self] value in + self?.currentDownloadTask = value.currentDownloadTask + self?.calculateProgress() + self?.toggleStateIsOn(downloadableVerticals: value.downloadableVerticals) + self?.calculateTotalSize() + } + .store(in: &cancellables) + } + + private func calculateTotalSize() { + let downloadQuality = courseViewModel.userSettings?.downloadQuality ?? .auto + let mb = courseStructure.totalVideosSizeInMb( + downloadQuality: downloadQuality + ) + + if mb == 0 { + totalSize = nil + return + } + + if isOn { + let size = mb - calculateSize(value: mb, percentage: progress * 100) + if size == 0 { + totalSize = sizeInMbOrGb(size: mb) + return + } + totalSize = sizeInMbOrGb(size: size) + return + } + + let size = blockToMB( + data: Set(downloadableVerticals + .filter { $0.state != .finished } + .flatMap { $0.downloadableBlocks } + ), + downloadQuality: downloadQuality + ) + + totalSize = sizeInMbOrGb(size: size) + } + + private func calculateProgress() { + guard let currentDownloadTask = currentDownloadTask else { + progress = 0.0 + return + } + guard let index = courseViewModel.courseDownloadTasks.firstIndex( + where: { $0.id == currentDownloadTask.id && $0.type == .video } + ) else { + progress = 0.0 + return + } + courseViewModel.courseDownloadTasks[index].progress = currentDownloadTask.progress + let videoTasks = courseViewModel.courseDownloadTasks.filter { $0.type == .video } + progress = videoTasks.reduce(0) { + $0 + ($1.state == .finished ? 1 : $1.progress) + } / Double(videoTasks.count) } private func calculateSize(value: Double, percentage: Double) -> Double { diff --git a/Course/Course/Presentation/Subviews/CustomDisclosureGroup.swift b/Course/Course/Presentation/Subviews/CustomDisclosureGroup.swift index 6f555d0dd..34c96d0ca 100644 --- a/Course/Course/Presentation/Subviews/CustomDisclosureGroup.swift +++ b/Course/Course/Presentation/Subviews/CustomDisclosureGroup.swift @@ -12,14 +12,12 @@ import Theme struct CustomDisclosureGroup: View { @State private var expandedSections: [String: Bool] = [:] - private let isVideo: Bool private let proxy: GeometryProxy private let course: CourseStructure private let viewModel: CourseContainerViewModel private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } - init(isVideo: Bool, course: CourseStructure, proxy: GeometryProxy, viewModel: CourseContainerViewModel) { - self.isVideo = isVideo + init(course: CourseStructure, proxy: GeometryProxy, viewModel: CourseContainerViewModel) { self.course = course self.proxy = proxy self.viewModel = viewModel @@ -49,8 +47,8 @@ struct CustomDisclosureGroup: View { .foregroundColor(Theme.Colors.textPrimary) .lineLimit(1) Spacer() - if canDownloadAllSections(in: chapter, videoOnly: isVideo), - let state = downloadAllButtonState(for: chapter, videoOnly: isVideo) { + if canDownloadAllSections(in: chapter), + let state = downloadAllButtonState(for: chapter) { Button( action: { downloadAllSubsections(in: chapter, state: state) @@ -201,19 +199,10 @@ struct CustomDisclosureGroup: View { } } - private func canDownloadAllSections(in chapter: CourseChapter, videoOnly: Bool) -> Bool { - for sequential in chapter.childs { - if videoOnly { - let isDownloadable = sequential.childs.flatMap { - $0.childs.filter { $0.type == .video } - }.contains(where: { $0.isDownloadable }) - guard isDownloadable else { continue } - } - if sequentialDownloadState(sequential, videoOnly: videoOnly) != nil { - return true - } + private func canDownloadAllSections(in chapter: CourseChapter) -> Bool { + chapter.childs.contains { sequential in + sequentialDownloadState(sequential) != nil } - return false } private func assignmentStatusText( @@ -239,8 +228,7 @@ struct CustomDisclosureGroup: View { let blocks = await viewModel.collectBlocks( chapter: chapter, blockId: sequential.id, - state: state, - videoOnly: isVideo + state: state ) if !blocks.isEmpty { allBlocks.append(contentsOf: blocks) @@ -255,11 +243,11 @@ struct CustomDisclosureGroup: View { } } - private func downloadAllButtonState(for chapter: CourseChapter, videoOnly: Bool) -> DownloadViewState? { - if canDownloadAllSections(in: chapter, videoOnly: videoOnly) { + private func downloadAllButtonState(for chapter: CourseChapter) -> DownloadViewState? { + if canDownloadAllSections(in: chapter) { var downloads: [DownloadViewState] = [] for sequential in chapter.childs { - if let state = sequentialDownloadState(sequential, videoOnly: videoOnly) { + if let state = sequentialDownloadState(sequential) { downloads.append(state) } } @@ -274,34 +262,8 @@ struct CustomDisclosureGroup: View { return nil } - private func sequentialDownloadState(_ sequential: CourseSequential, videoOnly: Bool) -> DownloadViewState? { - let blocks: [CourseBlock] - if videoOnly { - blocks = sequential.childs.flatMap { $0.childs }.filter { $0.isDownloadable && $0.type == .video } - } else { - blocks = sequential.childs.flatMap { $0.childs }.filter { $0.isDownloadable } - } - guard !blocks.isEmpty else { return nil } - var blockStates: [DownloadViewState] = [] - for block in blocks { - if let task = viewModel.courseDownloadTasks.first(where: { $0.blockId == block.id }) { - switch task.state { - case .waiting, .inProgress: - blockStates.append(.downloading) - case .finished: - blockStates.append(.finished) - } - } else { - blockStates.append(.available) - } - } - if blockStates.contains(.downloading) { - return .downloading - } else if blockStates.allSatisfy({ $0 == .finished }) { - return .finished - } else { - return .available - } + private func sequentialDownloadState(_ sequential: CourseSequential) -> DownloadViewState? { + return viewModel.sequentialsDownloadState[sequential.id] } } @@ -440,7 +402,8 @@ struct CustomDisclosureGroup_Previews: PreviewProvider { enrollmentStart: Date(), enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: CourseDownloadHelper(courseStructure: nil, manager: DownloadManagerMock()) ) Task { await withTaskGroup(of: Void.self) { group in @@ -456,7 +419,6 @@ struct CustomDisclosureGroup_Previews: PreviewProvider { return GeometryReader { proxy in ScrollView { CustomDisclosureGroup( - isVideo: false, course: CourseStructure( id: "Id", graded: false, diff --git a/Course/CourseTests/CourseMock.generated.swift b/Course/CourseTests/CourseMock.generated.swift index 65fb08602..356a9d0c7 100644 --- a/Course/CourseTests/CourseMock.generated.swift +++ b/Course/CourseTests/CourseMock.generated.swift @@ -2531,41 +2531,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { perform?(`blocks`, `downloadQuality`) } - open func nextBlockForDownloading() -> DownloadDataTask? { - addInvocation(.m_nextBlockForDownloading) - let perform = methodPerformValue(.m_nextBlockForDownloading) as? () -> Void - perform?() - var __value: DownloadDataTask? = nil - do { - __value = try methodReturnValue(.m_nextBlockForDownloading).casted() - } catch { - // do nothing - } - return __value - } - - open func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) { - addInvocation(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) - let perform = methodPerformValue(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) as? (String, DownloadState, Data?) -> Void - perform?(`id`, `state`, `resumeData`) - } - - open func deleteDownloadDataTask(id: String) throws { - addInvocation(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) - let perform = methodPerformValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) as? (String) -> Void - perform?(`id`) - do { - _ = try methodReturnValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } - } - - open func saveDownloadDataTask(_ task: DownloadDataTask) { - addInvocation(.m_saveDownloadDataTask__task(Parameter.value(`task`))) - let perform = methodPerformValue(.m_saveDownloadDataTask__task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + open func updateTask(task: DownloadDataTask) { + addInvocation(.m_updateTask__task_task(Parameter.value(`task`))) + let perform = methodPerformValue(.m_updateTask__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void perform?(`task`) } @@ -2610,6 +2578,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { return __value } + open func deleteDownloadDataTasks(ids: [String]) { + addInvocation(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) + let perform = methodPerformValue(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) as? ([String]) -> Void + perform?(`ids`) + } + fileprivate enum MethodType { case m_set__userId_userId(Parameter) @@ -2622,13 +2596,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case m_deleteProgress__for_blockID(Parameter) case m_deleteAllProgress case m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>, Parameter) - case m_nextBlockForDownloading - case m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter, Parameter, Parameter) - case m_deleteDownloadDataTask__id_id(Parameter) - case m_saveDownloadDataTask__task(Parameter) + case m_updateTask__task_task(Parameter) case m_downloadDataTask__for_blockId(Parameter) case m_getDownloadDataTasks case m_getDownloadDataTasksForCourse__courseId(Parameter) + case m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { @@ -2671,23 +2643,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsDownloadquality, rhs: rhsDownloadquality, with: matcher), lhsDownloadquality, rhsDownloadquality, "downloadQuality")) return Matcher.ComparisonResult(results) - case (.m_nextBlockForDownloading, .m_nextBlockForDownloading): return .match - - case (.m_updateDownloadState__id_idstate_stateresumeData_resumeData(let lhsId, let lhsState, let lhsResumedata), .m_updateDownloadState__id_idstate_stateresumeData_resumeData(let rhsId, let rhsState, let rhsResumedata)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsState, rhs: rhsState, with: matcher), lhsState, rhsState, "state")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsResumedata, rhs: rhsResumedata, with: matcher), lhsResumedata, rhsResumedata, "resumeData")) - return Matcher.ComparisonResult(results) - - case (.m_deleteDownloadDataTask__id_id(let lhsId), .m_deleteDownloadDataTask__id_id(let rhsId)): + case (.m_updateTask__task_task(let lhsTask), .m_updateTask__task_task(let rhsTask)): var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - return Matcher.ComparisonResult(results) - - case (.m_saveDownloadDataTask__task(let lhsTask), .m_saveDownloadDataTask__task(let rhsTask)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "_ task")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) return Matcher.ComparisonResult(results) case (.m_downloadDataTask__for_blockId(let lhsBlockid), .m_downloadDataTask__for_blockId(let rhsBlockid)): @@ -2701,6 +2659,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) return Matcher.ComparisonResult(results) + + case (.m_deleteDownloadDataTasks__ids_ids(let lhsIds), .m_deleteDownloadDataTasks__ids_ids(let rhsIds)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsIds, rhs: rhsIds, with: matcher), lhsIds, rhsIds, "ids")) + return Matcher.ComparisonResult(results) default: return .none } } @@ -2717,13 +2680,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case let .m_deleteProgress__for_blockID(p0): return p0.intValue case .m_deleteAllProgress: return 0 case let .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(p0, p1): return p0.intValue + p1.intValue - case .m_nextBlockForDownloading: return 0 - case let .m_updateDownloadState__id_idstate_stateresumeData_resumeData(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue - case let .m_deleteDownloadDataTask__id_id(p0): return p0.intValue - case let .m_saveDownloadDataTask__task(p0): return p0.intValue + case let .m_updateTask__task_task(p0): return p0.intValue case let .m_downloadDataTask__for_blockId(p0): return p0.intValue case .m_getDownloadDataTasks: return 0 case let .m_getDownloadDataTasksForCourse__courseId(p0): return p0.intValue + case let .m_deleteDownloadDataTasks__ids_ids(p0): return p0.intValue } } func assertionName() -> String { @@ -2738,13 +2699,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case .m_deleteProgress__for_blockID: return ".deleteProgress(for:)" case .m_deleteAllProgress: return ".deleteAllProgress()" case .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality: return ".addToDownloadQueue(blocks:downloadQuality:)" - case .m_nextBlockForDownloading: return ".nextBlockForDownloading()" - case .m_updateDownloadState__id_idstate_stateresumeData_resumeData: return ".updateDownloadState(id:state:resumeData:)" - case .m_deleteDownloadDataTask__id_id: return ".deleteDownloadDataTask(id:)" - case .m_saveDownloadDataTask__task: return ".saveDownloadDataTask(_:)" + case .m_updateTask__task_task: return ".updateTask(task:)" case .m_downloadDataTask__for_blockId: return ".downloadDataTask(for:)" case .m_getDownloadDataTasks: return ".getDownloadDataTasks()" case .m_getDownloadDataTasksForCourse__courseId: return ".getDownloadDataTasksForCourse(_:)" + case .m_deleteDownloadDataTasks__ids_ids: return ".deleteDownloadDataTasks(ids:)" } } } @@ -2771,9 +2730,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func loadAllOfflineProgress(willReturn: [OfflineProgress]...) -> MethodStub { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func nextBlockForDownloading(willReturn: DownloadDataTask?...) -> MethodStub { - return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func downloadDataTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } @@ -2804,13 +2760,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func nextBlockForDownloading(willProduce: (Stubber) -> Void) -> MethodStub { - let willReturn: [DownloadDataTask?] = [] - let given: Given = { return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (DownloadDataTask?).self) - willProduce(stubber) - return given - } public static func downloadDataTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [DownloadDataTask?] = [] let given: Given = { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -2844,16 +2793,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func deleteDownloadDataTask(id: Parameter, willThrow: Error...) -> MethodStub { - return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) - } - public static func deleteDownloadDataTask(id: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given - } } public struct Verify { @@ -2870,13 +2809,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func deleteProgress(for blockID: Parameter) -> Verify { return Verify(method: .m_deleteProgress__for_blockID(`blockID`))} public static func deleteAllProgress() -> Verify { return Verify(method: .m_deleteAllProgress)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`))} - public static func nextBlockForDownloading() -> Verify { return Verify(method: .m_nextBlockForDownloading)} - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter) -> Verify { return Verify(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`))} - public static func deleteDownloadDataTask(id: Parameter) -> Verify { return Verify(method: .m_deleteDownloadDataTask__id_id(`id`))} - public static func saveDownloadDataTask(_ task: Parameter) -> Verify { return Verify(method: .m_saveDownloadDataTask__task(`task`))} + public static func updateTask(task: Parameter) -> Verify { return Verify(method: .m_updateTask__task_task(`task`))} public static func downloadDataTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadDataTask__for_blockId(`blockId`))} public static func getDownloadDataTasks() -> Verify { return Verify(method: .m_getDownloadDataTasks)} public static func getDownloadDataTasksForCourse(_ courseId: Parameter) -> Verify { return Verify(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`))} + public static func deleteDownloadDataTasks(ids: Parameter<[String]>) -> Verify { return Verify(method: .m_deleteDownloadDataTasks__ids_ids(`ids`))} } public struct Perform { @@ -2914,17 +2851,8 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter, perform: @escaping ([CourseBlock], DownloadQuality) -> Void) -> Perform { return Perform(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`), performs: perform) } - public static func nextBlockForDownloading(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_nextBlockForDownloading, performs: perform) - } - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter, perform: @escaping (String, DownloadState, Data?) -> Void) -> Perform { - return Perform(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`), performs: perform) - } - public static func deleteDownloadDataTask(id: Parameter, perform: @escaping (String) -> Void) -> Perform { - return Perform(method: .m_deleteDownloadDataTask__id_id(`id`), performs: perform) - } - public static func saveDownloadDataTask(_ task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { - return Perform(method: .m_saveDownloadDataTask__task(`task`), performs: perform) + public static func updateTask(task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { + return Perform(method: .m_updateTask__task_task(`task`), performs: perform) } public static func downloadDataTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_downloadDataTask__for_blockId(`blockId`), performs: perform) @@ -2935,6 +2863,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getDownloadDataTasksForCourse(_ courseId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), performs: perform) } + public static func deleteDownloadDataTasks(ids: Parameter<[String]>, perform: @escaping ([String]) -> Void) -> Perform { + return Perform(method: .m_deleteDownloadDataTasks__ids_ids(`ids`), performs: perform) + } } public func given(_ method: Given) { @@ -3623,6 +3554,18 @@ open class CourseAnalyticsMock: CourseAnalytics, Mock { perform?(`courseID`, `subSectionID`, `videos`) } + open func bulkDownloadVideosSection(courseID: String, sectionID: String, videos: Int) { + addInvocation(.m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos(Parameter.value(`courseID`), Parameter.value(`sectionID`), Parameter.value(`videos`))) + let perform = methodPerformValue(.m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos(Parameter.value(`courseID`), Parameter.value(`sectionID`), Parameter.value(`videos`))) as? (String, String, Int) -> Void + perform?(`courseID`, `sectionID`, `videos`) + } + + open func bulkDeleteVideosSection(courseID: String, sectionId: String, videos: Int) { + addInvocation(.m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos(Parameter.value(`courseID`), Parameter.value(`sectionId`), Parameter.value(`videos`))) + let perform = methodPerformValue(.m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos(Parameter.value(`courseID`), Parameter.value(`sectionId`), Parameter.value(`videos`))) as? (String, String, Int) -> Void + perform?(`courseID`, `sectionId`, `videos`) + } + open func videoLoaded(courseID: String, blockID: String, videoURL: String) { addInvocation(.m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL(Parameter.value(`courseID`), Parameter.value(`blockID`), Parameter.value(`videoURL`))) let perform = methodPerformValue(.m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL(Parameter.value(`courseID`), Parameter.value(`blockID`), Parameter.value(`videoURL`))) as? (String, String, String) -> Void @@ -3680,6 +3623,8 @@ open class CourseAnalyticsMock: CourseAnalytics, Mock { case m_bulkDownloadVideosToggle__courseID_courseIDaction_action(Parameter, Parameter) case m_bulkDownloadVideosSubsection__courseID_courseIDsectionID_sectionIDsubSectionID_subSectionIDvideos_videos(Parameter, Parameter, Parameter, Parameter) case m_bulkDeleteVideosSubsection__courseID_courseIDsubSectionID_subSectionIDvideos_videos(Parameter, Parameter, Parameter) + case m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos(Parameter, Parameter, Parameter) + case m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos(Parameter, Parameter, Parameter) case m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL(Parameter, Parameter, Parameter) case m_videoPlayed__courseID_courseIDblockID_blockIDvideoURL_videoURL(Parameter, Parameter, Parameter) case m_videoSpeedChange__courseID_courseIDblockID_blockIDvideoURL_videoURLoldSpeed_oldSpeednewSpeed_newSpeedcurrentTime_currentTimeduration_duration(Parameter, Parameter, Parameter, Parameter, Parameter, Parameter, Parameter) @@ -3872,6 +3817,20 @@ open class CourseAnalyticsMock: CourseAnalytics, Mock { results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsVideos, rhs: rhsVideos, with: matcher), lhsVideos, rhsVideos, "videos")) return Matcher.ComparisonResult(results) + case (.m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos(let lhsCourseid, let lhsSectionid, let lhsVideos), .m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos(let rhsCourseid, let rhsSectionid, let rhsVideos)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseID")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSectionid, rhs: rhsSectionid, with: matcher), lhsSectionid, rhsSectionid, "sectionID")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsVideos, rhs: rhsVideos, with: matcher), lhsVideos, rhsVideos, "videos")) + return Matcher.ComparisonResult(results) + + case (.m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos(let lhsCourseid, let lhsSectionid, let lhsVideos), .m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos(let rhsCourseid, let rhsSectionid, let rhsVideos)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseID")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSectionid, rhs: rhsSectionid, with: matcher), lhsSectionid, rhsSectionid, "sectionId")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsVideos, rhs: rhsVideos, with: matcher), lhsVideos, rhsVideos, "videos")) + return Matcher.ComparisonResult(results) + case (.m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL(let lhsCourseid, let lhsBlockid, let lhsVideourl), .m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL(let rhsCourseid, let rhsBlockid, let rhsVideourl)): var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseID")) @@ -3945,6 +3904,8 @@ open class CourseAnalyticsMock: CourseAnalytics, Mock { case let .m_bulkDownloadVideosToggle__courseID_courseIDaction_action(p0, p1): return p0.intValue + p1.intValue case let .m_bulkDownloadVideosSubsection__courseID_courseIDsectionID_sectionIDsubSectionID_subSectionIDvideos_videos(p0, p1, p2, p3): return p0.intValue + p1.intValue + p2.intValue + p3.intValue case let .m_bulkDeleteVideosSubsection__courseID_courseIDsubSectionID_subSectionIDvideos_videos(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue + case let .m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue + case let .m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue case let .m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue case let .m_videoPlayed__courseID_courseIDblockID_blockIDvideoURL_videoURL(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue case let .m_videoSpeedChange__courseID_courseIDblockID_blockIDvideoURL_videoURLoldSpeed_oldSpeednewSpeed_newSpeedcurrentTime_currentTimeduration_duration(p0, p1, p2, p3, p4, p5, p6): return p0.intValue + p1.intValue + p2.intValue + p3.intValue + p4.intValue + p5.intValue + p6.intValue @@ -3979,6 +3940,8 @@ open class CourseAnalyticsMock: CourseAnalytics, Mock { case .m_bulkDownloadVideosToggle__courseID_courseIDaction_action: return ".bulkDownloadVideosToggle(courseID:action:)" case .m_bulkDownloadVideosSubsection__courseID_courseIDsectionID_sectionIDsubSectionID_subSectionIDvideos_videos: return ".bulkDownloadVideosSubsection(courseID:sectionID:subSectionID:videos:)" case .m_bulkDeleteVideosSubsection__courseID_courseIDsubSectionID_subSectionIDvideos_videos: return ".bulkDeleteVideosSubsection(courseID:subSectionID:videos:)" + case .m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos: return ".bulkDownloadVideosSection(courseID:sectionID:videos:)" + case .m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos: return ".bulkDeleteVideosSection(courseID:sectionId:videos:)" case .m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL: return ".videoLoaded(courseID:blockID:videoURL:)" case .m_videoPlayed__courseID_courseIDblockID_blockIDvideoURL_videoURL: return ".videoPlayed(courseID:blockID:videoURL:)" case .m_videoSpeedChange__courseID_courseIDblockID_blockIDvideoURL_videoURLoldSpeed_oldSpeednewSpeed_newSpeedcurrentTime_currentTimeduration_duration: return ".videoSpeedChange(courseID:blockID:videoURL:oldSpeed:newSpeed:currentTime:duration:)" @@ -4027,6 +3990,8 @@ open class CourseAnalyticsMock: CourseAnalytics, Mock { public static func bulkDownloadVideosToggle(courseID: Parameter, action: Parameter) -> Verify { return Verify(method: .m_bulkDownloadVideosToggle__courseID_courseIDaction_action(`courseID`, `action`))} public static func bulkDownloadVideosSubsection(courseID: Parameter, sectionID: Parameter, subSectionID: Parameter, videos: Parameter) -> Verify { return Verify(method: .m_bulkDownloadVideosSubsection__courseID_courseIDsectionID_sectionIDsubSectionID_subSectionIDvideos_videos(`courseID`, `sectionID`, `subSectionID`, `videos`))} public static func bulkDeleteVideosSubsection(courseID: Parameter, subSectionID: Parameter, videos: Parameter) -> Verify { return Verify(method: .m_bulkDeleteVideosSubsection__courseID_courseIDsubSectionID_subSectionIDvideos_videos(`courseID`, `subSectionID`, `videos`))} + public static func bulkDownloadVideosSection(courseID: Parameter, sectionID: Parameter, videos: Parameter) -> Verify { return Verify(method: .m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos(`courseID`, `sectionID`, `videos`))} + public static func bulkDeleteVideosSection(courseID: Parameter, sectionId: Parameter, videos: Parameter) -> Verify { return Verify(method: .m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos(`courseID`, `sectionId`, `videos`))} public static func videoLoaded(courseID: Parameter, blockID: Parameter, videoURL: Parameter) -> Verify { return Verify(method: .m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL(`courseID`, `blockID`, `videoURL`))} public static func videoPlayed(courseID: Parameter, blockID: Parameter, videoURL: Parameter) -> Verify { return Verify(method: .m_videoPlayed__courseID_courseIDblockID_blockIDvideoURL_videoURL(`courseID`, `blockID`, `videoURL`))} public static func videoSpeedChange(courseID: Parameter, blockID: Parameter, videoURL: Parameter, oldSpeed: Parameter, newSpeed: Parameter, currentTime: Parameter, duration: Parameter) -> Verify { return Verify(method: .m_videoSpeedChange__courseID_courseIDblockID_blockIDvideoURL_videoURLoldSpeed_oldSpeednewSpeed_newSpeedcurrentTime_currentTimeduration_duration(`courseID`, `blockID`, `videoURL`, `oldSpeed`, `newSpeed`, `currentTime`, `duration`))} @@ -4113,6 +4078,12 @@ open class CourseAnalyticsMock: CourseAnalytics, Mock { public static func bulkDeleteVideosSubsection(courseID: Parameter, subSectionID: Parameter, videos: Parameter, perform: @escaping (String, String, Int) -> Void) -> Perform { return Perform(method: .m_bulkDeleteVideosSubsection__courseID_courseIDsubSectionID_subSectionIDvideos_videos(`courseID`, `subSectionID`, `videos`), performs: perform) } + public static func bulkDownloadVideosSection(courseID: Parameter, sectionID: Parameter, videos: Parameter, perform: @escaping (String, String, Int) -> Void) -> Perform { + return Perform(method: .m_bulkDownloadVideosSection__courseID_courseIDsectionID_sectionIDvideos_videos(`courseID`, `sectionID`, `videos`), performs: perform) + } + public static func bulkDeleteVideosSection(courseID: Parameter, sectionId: Parameter, videos: Parameter, perform: @escaping (String, String, Int) -> Void) -> Perform { + return Perform(method: .m_bulkDeleteVideosSection__courseID_courseIDsectionId_sectionIdvideos_videos(`courseID`, `sectionId`, `videos`), performs: perform) + } public static func videoLoaded(courseID: Parameter, blockID: Parameter, videoURL: Parameter, perform: @escaping (String, String, String) -> Void) -> Perform { return Perform(method: .m_videoLoaded__courseID_courseIDblockID_blockIDvideoURL_videoURL(`courseID`, `blockID`, `videoURL`), performs: perform) } @@ -4203,6 +4174,473 @@ open class CourseAnalyticsMock: CourseAnalytics, Mock { } } +// MARK: - CourseDownloadHelperProtocol + +open class CourseDownloadHelperProtocolMock: CourseDownloadHelperProtocol, Mock { + public init(sequencing sequencingPolicy: SequencingPolicy = .lastWrittenResolvedFirst, stubbing stubbingPolicy: StubbingPolicy = .wrap, file: StaticString = #file, line: UInt = #line) { + SwiftyMockyTestObserver.setup() + self.sequencingPolicy = sequencingPolicy + self.stubbingPolicy = stubbingPolicy + self.file = file + self.line = line + } + + var matcher: Matcher = Matcher.default + var stubbingPolicy: StubbingPolicy = .wrap + var sequencingPolicy: SequencingPolicy = .lastWrittenResolvedFirst + + private var queue = DispatchQueue(label: "com.swiftymocky.invocations", qos: .userInteractive) + private var invocations: [MethodType] = [] + private var methodReturnValues: [Given] = [] + private var methodPerformValues: [Perform] = [] + private var file: StaticString? + private var line: UInt? + + public typealias PropertyStub = Given + public typealias MethodStub = Given + public typealias SubscriptStub = Given + + /// Convenience method - call setupMock() to extend debug information when failure occurs + public func setupMock(file: StaticString = #file, line: UInt = #line) { + self.file = file + self.line = line + } + + /// Clear mock internals. You can specify what to reset (invocations aka verify, givens or performs) or leave it empty to clear all mock internals + public func resetMock(_ scopes: MockScope...) { + let scopes: [MockScope] = scopes.isEmpty ? [.invocation, .given, .perform] : scopes + if scopes.contains(.invocation) { invocations = [] } + if scopes.contains(.given) { methodReturnValues = [] } + if scopes.contains(.perform) { methodPerformValues = [] } + } + + public var value: CourseDownloadValue? { + get { invocations.append(.p_value_get); return __p_value ?? optionalGivenGetterValue(.p_value_get, "CourseDownloadHelperProtocolMock - stub value for value was not defined") } + } + private var __p_value: (CourseDownloadValue)? + + public var courseStructure: CourseStructure? { + get { invocations.append(.p_courseStructure_get); return __p_courseStructure ?? optionalGivenGetterValue(.p_courseStructure_get, "CourseDownloadHelperProtocolMock - stub value for courseStructure was not defined") } + set { invocations.append(.p_courseStructure_set(.value(newValue))); __p_courseStructure = newValue } + } + private var __p_courseStructure: (CourseStructure)? + + public var videoQuality: DownloadQuality { + get { invocations.append(.p_videoQuality_get); return __p_videoQuality ?? givenGetterValue(.p_videoQuality_get, "CourseDownloadHelperProtocolMock - stub value for videoQuality was not defined") } + set { invocations.append(.p_videoQuality_set(.value(newValue))); __p_videoQuality = newValue } + } + private var __p_videoQuality: (DownloadQuality)? + + + + + + open func publisher() -> AnyPublisher { + addInvocation(.m_publisher) + let perform = methodPerformValue(.m_publisher) as? () -> Void + perform?() + var __value: AnyPublisher + do { + __value = try methodReturnValue(.m_publisher).casted() + } catch { + onFatalFailure("Stub return value not specified for publisher(). Use given") + Failure("Stub return value not specified for publisher(). Use given") + } + return __value + } + + open func progressPublisher() -> AnyPublisher { + addInvocation(.m_progressPublisher) + let perform = methodPerformValue(.m_progressPublisher) as? () -> Void + perform?() + var __value: AnyPublisher + do { + __value = try methodReturnValue(.m_progressPublisher).casted() + } catch { + onFatalFailure("Stub return value not specified for progressPublisher(). Use given") + Failure("Stub return value not specified for progressPublisher(). Use given") + } + return __value + } + + open func refreshValue() { + addInvocation(.m_refreshValue) + let perform = methodPerformValue(.m_refreshValue) as? () -> Void + perform?() + } + + open func sizeFor(block: CourseBlock) -> Int? { + addInvocation(.m_sizeFor__block_block(Parameter.value(`block`))) + let perform = methodPerformValue(.m_sizeFor__block_block(Parameter.value(`block`))) as? (CourseBlock) -> Void + perform?(`block`) + var __value: Int? = nil + do { + __value = try methodReturnValue(.m_sizeFor__block_block(Parameter.value(`block`))).casted() + } catch { + // do nothing + } + return __value + } + + open func sizeFor(blocks: [CourseBlock]) -> Int { + addInvocation(.m_sizeFor__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) + let perform = methodPerformValue(.m_sizeFor__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void + perform?(`blocks`) + var __value: Int + do { + __value = try methodReturnValue(.m_sizeFor__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))).casted() + } catch { + onFatalFailure("Stub return value not specified for sizeFor(blocks: [CourseBlock]). Use given") + Failure("Stub return value not specified for sizeFor(blocks: [CourseBlock]). Use given") + } + return __value + } + + open func sizeFor(sequential: CourseSequential) -> Int { + addInvocation(.m_sizeFor__sequential_sequential(Parameter.value(`sequential`))) + let perform = methodPerformValue(.m_sizeFor__sequential_sequential(Parameter.value(`sequential`))) as? (CourseSequential) -> Void + perform?(`sequential`) + var __value: Int + do { + __value = try methodReturnValue(.m_sizeFor__sequential_sequential(Parameter.value(`sequential`))).casted() + } catch { + onFatalFailure("Stub return value not specified for sizeFor(sequential: CourseSequential). Use given") + Failure("Stub return value not specified for sizeFor(sequential: CourseSequential). Use given") + } + return __value + } + + open func sizeFor(sequentials: [CourseSequential]) -> Int { + addInvocation(.m_sizeFor__sequentials_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) + let perform = methodPerformValue(.m_sizeFor__sequentials_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) as? ([CourseSequential]) -> Void + perform?(`sequentials`) + var __value: Int + do { + __value = try methodReturnValue(.m_sizeFor__sequentials_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))).casted() + } catch { + onFatalFailure("Stub return value not specified for sizeFor(sequentials: [CourseSequential]). Use given") + Failure("Stub return value not specified for sizeFor(sequentials: [CourseSequential]). Use given") + } + return __value + } + + open func cancelDownloading(task: DownloadDataTask) throws { + addInvocation(.m_cancelDownloading__task_task(Parameter.value(`task`))) + let perform = methodPerformValue(.m_cancelDownloading__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + perform?(`task`) + do { + _ = try methodReturnValue(.m_cancelDownloading__task_task(Parameter.value(`task`))).casted() as Void + } catch MockError.notStubed { + // do nothing + } catch { + throw error + } + } + + + fileprivate enum MethodType { + case m_publisher + case m_progressPublisher + case m_refreshValue + case m_sizeFor__block_block(Parameter) + case m_sizeFor__blocks_blocks(Parameter<[CourseBlock]>) + case m_sizeFor__sequential_sequential(Parameter) + case m_sizeFor__sequentials_sequentials(Parameter<[CourseSequential]>) + case m_cancelDownloading__task_task(Parameter) + case p_value_get + case p_courseStructure_get + case p_courseStructure_set(Parameter) + case p_videoQuality_get + case p_videoQuality_set(Parameter) + + static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { + switch (lhs, rhs) { + case (.m_publisher, .m_publisher): return .match + + case (.m_progressPublisher, .m_progressPublisher): return .match + + case (.m_refreshValue, .m_refreshValue): return .match + + case (.m_sizeFor__block_block(let lhsBlock), .m_sizeFor__block_block(let rhsBlock)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlock, rhs: rhsBlock, with: matcher), lhsBlock, rhsBlock, "block")) + return Matcher.ComparisonResult(results) + + case (.m_sizeFor__blocks_blocks(let lhsBlocks), .m_sizeFor__blocks_blocks(let rhsBlocks)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + return Matcher.ComparisonResult(results) + + case (.m_sizeFor__sequential_sequential(let lhsSequential), .m_sizeFor__sequential_sequential(let rhsSequential)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequential, rhs: rhsSequential, with: matcher), lhsSequential, rhsSequential, "sequential")) + return Matcher.ComparisonResult(results) + + case (.m_sizeFor__sequentials_sequentials(let lhsSequentials), .m_sizeFor__sequentials_sequentials(let rhsSequentials)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequentials, rhs: rhsSequentials, with: matcher), lhsSequentials, rhsSequentials, "sequentials")) + return Matcher.ComparisonResult(results) + + case (.m_cancelDownloading__task_task(let lhsTask), .m_cancelDownloading__task_task(let rhsTask)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) + return Matcher.ComparisonResult(results) + case (.p_value_get,.p_value_get): return Matcher.ComparisonResult.match + case (.p_courseStructure_get,.p_courseStructure_get): return Matcher.ComparisonResult.match + case (.p_courseStructure_set(let left),.p_courseStructure_set(let right)): return Matcher.ComparisonResult([Matcher.ParameterComparisonResult(Parameter.compare(lhs: left, rhs: right, with: matcher), left, right, "newValue")]) + case (.p_videoQuality_get,.p_videoQuality_get): return Matcher.ComparisonResult.match + case (.p_videoQuality_set(let left),.p_videoQuality_set(let right)): return Matcher.ComparisonResult([Matcher.ParameterComparisonResult(Parameter.compare(lhs: left, rhs: right, with: matcher), left, right, "newValue")]) + default: return .none + } + } + + func intValue() -> Int { + switch self { + case .m_publisher: return 0 + case .m_progressPublisher: return 0 + case .m_refreshValue: return 0 + case let .m_sizeFor__block_block(p0): return p0.intValue + case let .m_sizeFor__blocks_blocks(p0): return p0.intValue + case let .m_sizeFor__sequential_sequential(p0): return p0.intValue + case let .m_sizeFor__sequentials_sequentials(p0): return p0.intValue + case let .m_cancelDownloading__task_task(p0): return p0.intValue + case .p_value_get: return 0 + case .p_courseStructure_get: return 0 + case .p_courseStructure_set(let newValue): return newValue.intValue + case .p_videoQuality_get: return 0 + case .p_videoQuality_set(let newValue): return newValue.intValue + } + } + func assertionName() -> String { + switch self { + case .m_publisher: return ".publisher()" + case .m_progressPublisher: return ".progressPublisher()" + case .m_refreshValue: return ".refreshValue()" + case .m_sizeFor__block_block: return ".sizeFor(block:)" + case .m_sizeFor__blocks_blocks: return ".sizeFor(blocks:)" + case .m_sizeFor__sequential_sequential: return ".sizeFor(sequential:)" + case .m_sizeFor__sequentials_sequentials: return ".sizeFor(sequentials:)" + case .m_cancelDownloading__task_task: return ".cancelDownloading(task:)" + case .p_value_get: return "[get] .value" + case .p_courseStructure_get: return "[get] .courseStructure" + case .p_courseStructure_set: return "[set] .courseStructure" + case .p_videoQuality_get: return "[get] .videoQuality" + case .p_videoQuality_set: return "[set] .videoQuality" + } + } + } + + open class Given: StubbedMethod { + fileprivate var method: MethodType + + private init(method: MethodType, products: [StubProduct]) { + self.method = method + super.init(products) + } + + public static func value(getter defaultValue: CourseDownloadValue?...) -> PropertyStub { + return Given(method: .p_value_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) + } + public static func courseStructure(getter defaultValue: CourseStructure?...) -> PropertyStub { + return Given(method: .p_courseStructure_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) + } + public static func videoQuality(getter defaultValue: DownloadQuality...) -> PropertyStub { + return Given(method: .p_videoQuality_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) + } + + public static func publisher(willReturn: AnyPublisher...) -> MethodStub { + return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func progressPublisher(willReturn: AnyPublisher...) -> MethodStub { + return Given(method: .m_progressPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func sizeFor(block: Parameter, willReturn: Int?...) -> MethodStub { + return Given(method: .m_sizeFor__block_block(`block`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func sizeFor(blocks: Parameter<[CourseBlock]>, willReturn: Int...) -> MethodStub { + return Given(method: .m_sizeFor__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func sizeFor(sequential: Parameter, willReturn: Int...) -> MethodStub { + return Given(method: .m_sizeFor__sequential_sequential(`sequential`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func sizeFor(sequentials: Parameter<[CourseSequential]>, willReturn: Int...) -> MethodStub { + return Given(method: .m_sizeFor__sequentials_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func publisher(willProduce: (Stubber>) -> Void) -> MethodStub { + let willReturn: [AnyPublisher] = [] + let given: Given = { return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (AnyPublisher).self) + willProduce(stubber) + return given + } + public static func progressPublisher(willProduce: (Stubber>) -> Void) -> MethodStub { + let willReturn: [AnyPublisher] = [] + let given: Given = { return Given(method: .m_progressPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (AnyPublisher).self) + willProduce(stubber) + return given + } + public static func sizeFor(block: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [Int?] = [] + let given: Given = { return Given(method: .m_sizeFor__block_block(`block`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (Int?).self) + willProduce(stubber) + return given + } + public static func sizeFor(blocks: Parameter<[CourseBlock]>, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [Int] = [] + let given: Given = { return Given(method: .m_sizeFor__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (Int).self) + willProduce(stubber) + return given + } + public static func sizeFor(sequential: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [Int] = [] + let given: Given = { return Given(method: .m_sizeFor__sequential_sequential(`sequential`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (Int).self) + willProduce(stubber) + return given + } + public static func sizeFor(sequentials: Parameter<[CourseSequential]>, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [Int] = [] + let given: Given = { return Given(method: .m_sizeFor__sequentials_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (Int).self) + willProduce(stubber) + return given + } + public static func cancelDownloading(task: Parameter, willThrow: Error...) -> MethodStub { + return Given(method: .m_cancelDownloading__task_task(`task`), products: willThrow.map({ StubProduct.throw($0) })) + } + public static func cancelDownloading(task: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { + let willThrow: [Error] = [] + let given: Given = { return Given(method: .m_cancelDownloading__task_task(`task`), products: willThrow.map({ StubProduct.throw($0) })) }() + let stubber = given.stubThrows(for: (Void).self) + willProduce(stubber) + return given + } + } + + public struct Verify { + fileprivate var method: MethodType + + public static func publisher() -> Verify { return Verify(method: .m_publisher)} + public static func progressPublisher() -> Verify { return Verify(method: .m_progressPublisher)} + public static func refreshValue() -> Verify { return Verify(method: .m_refreshValue)} + public static func sizeFor(block: Parameter) -> Verify { return Verify(method: .m_sizeFor__block_block(`block`))} + public static func sizeFor(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_sizeFor__blocks_blocks(`blocks`))} + public static func sizeFor(sequential: Parameter) -> Verify { return Verify(method: .m_sizeFor__sequential_sequential(`sequential`))} + public static func sizeFor(sequentials: Parameter<[CourseSequential]>) -> Verify { return Verify(method: .m_sizeFor__sequentials_sequentials(`sequentials`))} + public static func cancelDownloading(task: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__task_task(`task`))} + public static var value: Verify { return Verify(method: .p_value_get) } + public static var courseStructure: Verify { return Verify(method: .p_courseStructure_get) } + public static func courseStructure(set newValue: Parameter) -> Verify { return Verify(method: .p_courseStructure_set(newValue)) } + public static var videoQuality: Verify { return Verify(method: .p_videoQuality_get) } + public static func videoQuality(set newValue: Parameter) -> Verify { return Verify(method: .p_videoQuality_set(newValue)) } + } + + public struct Perform { + fileprivate var method: MethodType + var performs: Any + + public static func publisher(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_publisher, performs: perform) + } + public static func progressPublisher(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_progressPublisher, performs: perform) + } + public static func refreshValue(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_refreshValue, performs: perform) + } + public static func sizeFor(block: Parameter, perform: @escaping (CourseBlock) -> Void) -> Perform { + return Perform(method: .m_sizeFor__block_block(`block`), performs: perform) + } + public static func sizeFor(blocks: Parameter<[CourseBlock]>, perform: @escaping ([CourseBlock]) -> Void) -> Perform { + return Perform(method: .m_sizeFor__blocks_blocks(`blocks`), performs: perform) + } + public static func sizeFor(sequential: Parameter, perform: @escaping (CourseSequential) -> Void) -> Perform { + return Perform(method: .m_sizeFor__sequential_sequential(`sequential`), performs: perform) + } + public static func sizeFor(sequentials: Parameter<[CourseSequential]>, perform: @escaping ([CourseSequential]) -> Void) -> Perform { + return Perform(method: .m_sizeFor__sequentials_sequentials(`sequentials`), performs: perform) + } + public static func cancelDownloading(task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { + return Perform(method: .m_cancelDownloading__task_task(`task`), performs: perform) + } + } + + public func given(_ method: Given) { + methodReturnValues.append(method) + } + + public func perform(_ method: Perform) { + methodPerformValues.append(method) + methodPerformValues.sort { $0.method.intValue() < $1.method.intValue() } + } + + public func verify(_ method: Verify, count: Count = Count.moreOrEqual(to: 1), file: StaticString = #file, line: UInt = #line) { + let fullMatches = matchingCalls(method, file: file, line: line) + let success = count.matches(fullMatches) + let assertionName = method.method.assertionName() + let feedback: String = { + guard !success else { return "" } + return Utils.closestCallsMessage( + for: self.invocations.map { invocation in + matcher.set(file: file, line: line) + defer { matcher.clearFileAndLine() } + return MethodType.compareParameters(lhs: invocation, rhs: method.method, matcher: matcher) + }, + name: assertionName + ) + }() + MockyAssert(success, "Expected: \(count) invocations of `\(assertionName)`, but was: \(fullMatches).\(feedback)", file: file, line: line) + } + + private func addInvocation(_ call: MethodType) { + self.queue.sync { invocations.append(call) } + } + private func methodReturnValue(_ method: MethodType) throws -> StubProduct { + matcher.set(file: self.file, line: self.line) + defer { matcher.clearFileAndLine() } + let candidates = sequencingPolicy.sorted(methodReturnValues, by: { $0.method.intValue() > $1.method.intValue() }) + let matched = candidates.first(where: { $0.isValid && MethodType.compareParameters(lhs: $0.method, rhs: method, matcher: matcher).isFullMatch }) + guard let product = matched?.getProduct(policy: self.stubbingPolicy) else { throw MockError.notStubed } + return product + } + private func methodPerformValue(_ method: MethodType) -> Any? { + matcher.set(file: self.file, line: self.line) + defer { matcher.clearFileAndLine() } + let matched = methodPerformValues.reversed().first { MethodType.compareParameters(lhs: $0.method, rhs: method, matcher: matcher).isFullMatch } + return matched?.performs + } + private func matchingCalls(_ method: MethodType, file: StaticString?, line: UInt?) -> [MethodType] { + matcher.set(file: file ?? self.file, line: line ?? self.line) + defer { matcher.clearFileAndLine() } + return invocations.filter { MethodType.compareParameters(lhs: $0, rhs: method, matcher: matcher).isFullMatch } + } + private func matchingCalls(_ method: Verify, file: StaticString?, line: UInt?) -> Int { + return matchingCalls(method.method, file: file, line: line).count + } + private func givenGetterValue(_ method: MethodType, _ message: String) -> T { + do { + return try methodReturnValue(method).casted() + } catch { + onFatalFailure(message) + Failure(message) + } + } + private func optionalGivenGetterValue(_ method: MethodType, _ message: String) -> T? { + do { + return try methodReturnValue(method).casted() + } catch { + return nil + } + } + private func onFatalFailure(_ message: String) { + guard let file = self.file, let line = self.line else { return } // Let if fail if cannot handle gratefully + SwiftyMockyTestObserver.handleFatalError(message: message, file: file, line: line) + } +} + // MARK: - CourseInteractorProtocol open class CourseInteractorProtocolMock: CourseInteractorProtocol, Mock { @@ -4838,7 +5276,7 @@ open class CourseInteractorProtocolMock: CourseInteractorProtocol, Mock { } // MARK: - DownloadManagerProtocol -@MainActor + open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public init(sequencing sequencingPolicy: SequencingPolicy = .lastWrittenResolvedFirst, stubbing stubbingPolicy: StubbingPolicy = .wrap, file: StaticString = #file, line: UInt = #line) { SwiftyMockyTestObserver.setup() @@ -4877,27 +5315,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { if scopes.contains(.perform) { methodPerformValues = [] } } - public var currentDownloadTask: DownloadDataTask? { - get { invocations.append(.p_currentDownloadTask_get); return __p_currentDownloadTask ?? optionalGivenGetterValue(.p_currentDownloadTask_get, "DownloadManagerProtocolMock - stub value for currentDownloadTask was not defined") } - } - private var __p_currentDownloadTask: (DownloadDataTask)? - - open func publisher() throws -> AnyPublisher { - addInvocation(.m_publisher) - let perform = methodPerformValue(.m_publisher) as? () -> Void + open func getCurrentDownloadTask() -> DownloadDataTask? { + addInvocation(.m_getCurrentDownloadTask) + let perform = methodPerformValue(.m_getCurrentDownloadTask) as? () -> Void perform?() - var __value: AnyPublisher + var __value: DownloadDataTask? = nil do { - __value = try methodReturnValue(.m_publisher).casted() - } catch MockError.notStubed { - onFatalFailure("Stub return value not specified for publisher(). Use given") - Failure("Stub return value not specified for publisher(). Use given") + __value = try methodReturnValue(.m_getCurrentDownloadTask).casted() } catch { - throw error + // do nothing } return __value } @@ -5009,15 +5439,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { } } - open func deleteFile(blocks: [CourseBlock]) { - addInvocation(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - } - - open func deleteAllFiles() { - addInvocation(.m_deleteAllFiles) - let perform = methodPerformValue(.m_deleteAllFiles) as? () -> Void + open func deleteAll() { + addInvocation(.m_deleteAll) + let perform = methodPerformValue(.m_deleteAll) as? () -> Void perform?() } @@ -5034,20 +5458,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return __value } - open func updateUnzippedFileSize(for sequentials: [CourseSequential]) -> [CourseSequential] { - addInvocation(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) - let perform = methodPerformValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) as? ([CourseSequential]) -> Void - perform?(`sequentials`) - var __value: [CourseSequential] - do { - __value = try methodReturnValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))).casted() - } catch { - onFatalFailure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - Failure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - } - return __value - } - open func resumeDownloading() throws { addInvocation(.m_resumeDownloading) let perform = methodPerformValue(.m_resumeDownloading) as? () -> Void @@ -5081,9 +5491,28 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { perform?() } + open func delete(blocks: [CourseBlock], courseId: String) { + addInvocation(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) + let perform = methodPerformValue(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) as? ([CourseBlock], String) -> Void + perform?(`blocks`, `courseId`) + } + + open func downloadTask(for blockId: String) -> DownloadDataTask? { + addInvocation(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) + let perform = methodPerformValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value + } + fileprivate enum MethodType { - case m_publisher + case m_getCurrentDownloadTask case m_eventPublisher case m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>) case m_getDownloadTasks @@ -5092,18 +5521,17 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case m_cancelDownloading__task_task(Parameter) case m_cancelDownloading__courseId_courseId(Parameter) case m_cancelAllDownloading - case m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>) - case m_deleteAllFiles + case m_deleteAll case m_fileUrl__for_blockId(Parameter) - case m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>) case m_resumeDownloading case m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>) case m_removeAppSupportDirectoryUnusedContent - case p_currentDownloadTask_get + case m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>, Parameter) + case m_downloadTask__for_blockId(Parameter) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { - case (.m_publisher, .m_publisher): return .match + case (.m_getCurrentDownloadTask, .m_getCurrentDownloadTask): return .match case (.m_eventPublisher, .m_eventPublisher): return .match @@ -5137,23 +5565,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case (.m_cancelAllDownloading, .m_cancelAllDownloading): return .match - case (.m_deleteFile__blocks_blocks(let lhsBlocks), .m_deleteFile__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) - - case (.m_deleteAllFiles, .m_deleteAllFiles): return .match + case (.m_deleteAll, .m_deleteAll): return .match case (.m_fileUrl__for_blockId(let lhsBlockid), .m_fileUrl__for_blockId(let rhsBlockid)): var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) return Matcher.ComparisonResult(results) - case (.m_updateUnzippedFileSize__for_sequentials(let lhsSequentials), .m_updateUnzippedFileSize__for_sequentials(let rhsSequentials)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequentials, rhs: rhsSequentials, with: matcher), lhsSequentials, rhsSequentials, "for sequentials")) - return Matcher.ComparisonResult(results) - case (.m_resumeDownloading, .m_resumeDownloading): return .match case (.m_isLargeVideosSize__blocks_blocks(let lhsBlocks), .m_isLargeVideosSize__blocks_blocks(let rhsBlocks)): @@ -5162,14 +5580,24 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return Matcher.ComparisonResult(results) case (.m_removeAppSupportDirectoryUnusedContent, .m_removeAppSupportDirectoryUnusedContent): return .match - case (.p_currentDownloadTask_get,.p_currentDownloadTask_get): return Matcher.ComparisonResult.match + + case (.m_delete__blocks_blockscourseId_courseId(let lhsBlocks, let lhsCourseid), .m_delete__blocks_blockscourseId_courseId(let rhsBlocks, let rhsCourseid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + return Matcher.ComparisonResult(results) + + case (.m_downloadTask__for_blockId(let lhsBlockid), .m_downloadTask__for_blockId(let rhsBlockid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) default: return .none } } func intValue() -> Int { switch self { - case .m_publisher: return 0 + case .m_getCurrentDownloadTask: return 0 case .m_eventPublisher: return 0 case let .m_addToDownloadQueue__blocks_blocks(p0): return p0.intValue case .m_getDownloadTasks: return 0 @@ -5178,19 +5606,18 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case let .m_cancelDownloading__task_task(p0): return p0.intValue case let .m_cancelDownloading__courseId_courseId(p0): return p0.intValue case .m_cancelAllDownloading: return 0 - case let .m_deleteFile__blocks_blocks(p0): return p0.intValue - case .m_deleteAllFiles: return 0 + case .m_deleteAll: return 0 case let .m_fileUrl__for_blockId(p0): return p0.intValue - case let .m_updateUnzippedFileSize__for_sequentials(p0): return p0.intValue case .m_resumeDownloading: return 0 case let .m_isLargeVideosSize__blocks_blocks(p0): return p0.intValue case .m_removeAppSupportDirectoryUnusedContent: return 0 - case .p_currentDownloadTask_get: return 0 + case let .m_delete__blocks_blockscourseId_courseId(p0, p1): return p0.intValue + p1.intValue + case let .m_downloadTask__for_blockId(p0): return p0.intValue } } func assertionName() -> String { switch self { - case .m_publisher: return ".publisher()" + case .m_getCurrentDownloadTask: return ".getCurrentDownloadTask()" case .m_eventPublisher: return ".eventPublisher()" case .m_addToDownloadQueue__blocks_blocks: return ".addToDownloadQueue(blocks:)" case .m_getDownloadTasks: return ".getDownloadTasks()" @@ -5199,14 +5626,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case .m_cancelDownloading__task_task: return ".cancelDownloading(task:)" case .m_cancelDownloading__courseId_courseId: return ".cancelDownloading(courseId:)" case .m_cancelAllDownloading: return ".cancelAllDownloading()" - case .m_deleteFile__blocks_blocks: return ".deleteFile(blocks:)" - case .m_deleteAllFiles: return ".deleteAllFiles()" + case .m_deleteAll: return ".deleteAll()" case .m_fileUrl__for_blockId: return ".fileUrl(for:)" - case .m_updateUnzippedFileSize__for_sequentials: return ".updateUnzippedFileSize(for:)" case .m_resumeDownloading: return ".resumeDownloading()" case .m_isLargeVideosSize__blocks_blocks: return ".isLargeVideosSize(blocks:)" case .m_removeAppSupportDirectoryUnusedContent: return ".removeAppSupportDirectoryUnusedContent()" - case .p_currentDownloadTask_get: return "[get] .currentDownloadTask" + case .m_delete__blocks_blockscourseId_courseId: return ".delete(blocks:courseId:)" + case .m_downloadTask__for_blockId: return ".downloadTask(for:)" } } } @@ -5219,12 +5645,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { super.init(products) } - public static func currentDownloadTask(getter defaultValue: DownloadDataTask?...) -> PropertyStub { - return Given(method: .p_currentDownloadTask_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) - } - public static func publisher(willReturn: AnyPublisher...) -> MethodStub { - return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + public static func getCurrentDownloadTask(willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) } public static func eventPublisher(willReturn: AnyPublisher...) -> MethodStub { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) @@ -5238,12 +5661,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func fileUrl(for blockId: Parameter, willReturn: URL?...) -> MethodStub { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willReturn: [CourseSequential]...) -> MethodStub { - return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willReturn: Bool...) -> MethodStub { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) } + public static func downloadTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func getCurrentDownloadTask(willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given + } public static func eventPublisher(willProduce: (Stubber>) -> Void) -> MethodStub { let willReturn: [AnyPublisher] = [] let given: Given = { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -5272,13 +5702,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willProduce: (Stubber<[CourseSequential]>) -> Void) -> MethodStub { - let willReturn: [[CourseSequential]] = [] - let given: Given = { return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([CourseSequential]).self) - willProduce(stubber) - return given - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [Bool] = [] let given: Given = { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -5286,13 +5709,10 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func publisher(willThrow: Error...) -> MethodStub { - return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) - } - public static func publisher(willProduce: (StubberThrows>) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (AnyPublisher).self) + public static func downloadTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) willProduce(stubber) return given } @@ -5361,7 +5781,7 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public struct Verify { fileprivate var method: MethodType - public static func publisher() -> Verify { return Verify(method: .m_publisher)} + public static func getCurrentDownloadTask() -> Verify { return Verify(method: .m_getCurrentDownloadTask)} public static func eventPublisher() -> Verify { return Verify(method: .m_eventPublisher)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocks(`blocks`))} public static func getDownloadTasks() -> Verify { return Verify(method: .m_getDownloadTasks)} @@ -5370,22 +5790,21 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelDownloading(task: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__task_task(`task`))} public static func cancelDownloading(courseId: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__courseId_courseId(`courseId`))} public static func cancelAllDownloading() -> Verify { return Verify(method: .m_cancelAllDownloading)} - public static func deleteFile(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_deleteFile__blocks_blocks(`blocks`))} - public static func deleteAllFiles() -> Verify { return Verify(method: .m_deleteAllFiles)} + public static func deleteAll() -> Verify { return Verify(method: .m_deleteAll)} public static func fileUrl(for blockId: Parameter) -> Verify { return Verify(method: .m_fileUrl__for_blockId(`blockId`))} - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>) -> Verify { return Verify(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`))} public static func resumeDownloading() -> Verify { return Verify(method: .m_resumeDownloading)} public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_isLargeVideosSize__blocks_blocks(`blocks`))} public static func removeAppSupportDirectoryUnusedContent() -> Verify { return Verify(method: .m_removeAppSupportDirectoryUnusedContent)} - public static var currentDownloadTask: Verify { return Verify(method: .p_currentDownloadTask_get) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter) -> Verify { return Verify(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`))} + public static func downloadTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadTask__for_blockId(`blockId`))} } public struct Perform { fileprivate var method: MethodType var performs: Any - public static func publisher(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_publisher, performs: perform) + public static func getCurrentDownloadTask(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_getCurrentDownloadTask, performs: perform) } public static func eventPublisher(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_eventPublisher, performs: perform) @@ -5411,18 +5830,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelAllDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_cancelAllDownloading, performs: perform) } - public static func deleteFile(blocks: Parameter<[CourseBlock]>, perform: @escaping ([CourseBlock]) -> Void) -> Perform { - return Perform(method: .m_deleteFile__blocks_blocks(`blocks`), performs: perform) - } - public static func deleteAllFiles(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_deleteAllFiles, performs: perform) + public static func deleteAll(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_deleteAll, performs: perform) } public static func fileUrl(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_fileUrl__for_blockId(`blockId`), performs: perform) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, perform: @escaping ([CourseSequential]) -> Void) -> Perform { - return Perform(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), performs: perform) - } public static func resumeDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_resumeDownloading, performs: perform) } @@ -5432,6 +5845,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func removeAppSupportDirectoryUnusedContent(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_removeAppSupportDirectoryUnusedContent, performs: perform) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter, perform: @escaping ([CourseBlock], String) -> Void) -> Perform { + return Perform(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`), performs: perform) + } + public static func downloadTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { + return Perform(method: .m_downloadTask__for_blockId(`blockId`), performs: perform) + } } public func given(_ method: Given) { diff --git a/Course/CourseTests/Presentation/Container/CourseContainerViewModelTests.swift b/Course/CourseTests/Presentation/Container/CourseContainerViewModelTests.swift index 278faf66d..b4d9172d8 100644 --- a/Course/CourseTests/Presentation/Container/CourseContainerViewModelTests.swift +++ b/Course/CourseTests/Presentation/Container/CourseContainerViewModelTests.swift @@ -7,7 +7,7 @@ import SwiftyMocky import XCTest -@testable import Core +import Core @testable import Course import Alamofire import SwiftUI @@ -15,6 +15,13 @@ import Combine @MainActor final class CourseContainerViewModelTests: XCTestCase { + var courseHelperMock: CourseDownloadHelperProtocolMock! + + override func setUpWithError() throws { + try super.setUpWithError() + courseHelperMock = CourseDownloadHelperProtocolMock() + Given(courseHelperMock, .publisher(willReturn: Just(.empty).eraseToAnyPublisher())) + } func testGetCourseBlocksSuccess() async throws { let interactor = CourseInteractorProtocolMock() @@ -42,7 +49,8 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) let block = CourseBlock( @@ -133,6 +141,7 @@ final class CourseContainerViewModelTests: XCTestCase { XCTAssertFalse(viewModel.showError) XCTAssertNil(viewModel.errorMessage) XCTAssertEqual(viewModel.courseStructure, courseStructure) + XCTAssertEqual(viewModel.courseHelper.courseStructure, courseStructure) } func testGetCourseBlocksOfflineSuccess() async throws { @@ -161,7 +170,8 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) let courseStructure = CourseStructure( @@ -198,6 +208,7 @@ final class CourseContainerViewModelTests: XCTestCase { XCTAssertFalse(viewModel.showError) XCTAssertNil(viewModel.errorMessage) XCTAssertEqual(viewModel.courseStructure, courseStructure) + XCTAssertEqual(viewModel.courseHelper.courseStructure, courseStructure) } func testGetCourseBlocksNoInternetError() async throws { @@ -226,7 +237,8 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) let noInternetError = AFError.sessionInvalidated(error: URLError(.notConnectedToInternet)) @@ -241,6 +253,7 @@ final class CourseContainerViewModelTests: XCTestCase { XCTAssertFalse(viewModel.isShowProgress) XCTAssertNil(viewModel.courseStructure) XCTAssertNil(viewModel.courseVideosStructure) + XCTAssertNil(viewModel.courseHelper.courseStructure) } func testGetCourseBlocksNoCacheError() async throws { @@ -269,7 +282,8 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) Given(interactor, .getCourseBlocks(courseID: "123", @@ -281,6 +295,7 @@ final class CourseContainerViewModelTests: XCTestCase { XCTAssertFalse(viewModel.isShowProgress) XCTAssertNil(viewModel.courseStructure) XCTAssertNil(viewModel.courseVideosStructure) + XCTAssertNil(viewModel.courseHelper.courseStructure) } func testGetCourseBlocksUnknownError() async throws { @@ -309,7 +324,8 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) Given(interactor, .getCourseBlocks(courseID: "123", @@ -321,6 +337,7 @@ final class CourseContainerViewModelTests: XCTestCase { XCTAssertFalse(viewModel.isShowProgress) XCTAssertNil(viewModel.courseStructure) XCTAssertNil(viewModel.courseVideosStructure) + XCTAssertNil(viewModel.courseHelper.courseStructure) } func testTabSelectedAnalytics() { @@ -349,7 +366,8 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) viewModel.trackSelectedTab(selection: .course, courseId: "1", courseName: "name") @@ -431,7 +449,7 @@ final class CourseContainerViewModelTests: XCTestCase { ) let courseStructure = CourseStructure( - id: "123", + id: "course123", graded: true, completion: 0, viewYouTubeUrl: "", @@ -465,17 +483,17 @@ final class CourseContainerViewModelTests: XCTestCase { state: .inProgress, type: .video, fileSize: 1000, - lastModified: "" + lastModified: "", + actualSize: 333 ) Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) Given(connectivity, .isMobileData(getter: false)) - Given(downloadManager, .publisher(willReturn: Empty().eraseToAnyPublisher())) Given(downloadManager, .eventPublisher(willReturn: Just(.added).eraseToAnyPublisher())) - Given(downloadManager, .getDownloadTasksForCourse(.any, willReturn: [downloadData])) - Given(downloadManager, .updateUnzippedFileSize(for: .any, willReturn: [sequential])) + Given(downloadManager, .isLargeVideosSize(blocks: .any, willReturn: false)) + Given(downloadManager, .getCurrentDownloadTask(willReturn: downloadData)) let viewModel = CourseContainerViewModel( interactor: interactor, @@ -492,20 +510,28 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) viewModel.courseStructure = courseStructure - await viewModel.setDownloadsStates(courseStructure: courseStructure) - - await viewModel.download( - state: .available, - blocks: [block], - sequentials: [sequential] - ) + await viewModel.onDownloadViewTap( + chapter: chapter, + state: .available + ) await Task.yield() - XCTAssertEqual(viewModel.sequentialsDownloadState[blockId], .downloading) + Verify( + analytics, + 1, + .bulkDownloadVideosSection( + courseID: .value(courseStructure.id), + sectionID: .value(chapter.id), + videos: .value(1) + ) + ) + Verify(analytics, 0, .bulkDeleteVideosSection(courseID: .any, sectionId: .any, videos: .any)) + Verify(downloadManager, 1, .addToDownloadQueue(blocks: .value([block]))) } @@ -600,10 +626,7 @@ final class CourseContainerViewModelTests: XCTestCase { Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) Given(connectivity, .isMobileData(getter: false)) - Given(downloadManager, .publisher(willReturn: Empty().eraseToAnyPublisher())) Given(downloadManager, .eventPublisher(willReturn: Just(.added).eraseToAnyPublisher())) - Given(downloadManager, .getDownloadTasksForCourse(.any, willReturn: [])) - Given(downloadManager, .updateUnzippedFileSize(for: .any, willReturn: [])) let viewModel = CourseContainerViewModel( interactor: interactor, @@ -620,20 +643,29 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) viewModel.courseStructure = courseStructure - await viewModel.setDownloadsStates(courseStructure: courseStructure) - await viewModel.download( - state: .available, - blocks: [block], - sequentials: [sequential] - ) + await viewModel.onDownloadViewTap( + chapter: chapter, + state: .downloading + ) await Task.yield() - XCTAssertEqual(viewModel.sequentialsDownloadState[blockId], .available) + Verify( + analytics, + 0, + .bulkDownloadVideosSection( + courseID: .any, + sectionID: .any, + videos: .any + ) + ) + Verify(analytics, 0, .bulkDeleteVideosSection(courseID: .any, sectionId: .any, videos: .any)) + Verify(downloadManager, 1, .cancelDownloading(courseId: .value(courseStructure.id), blocks: .value([block]))) } func testOnDownloadViewFinishedTap() async throws { @@ -727,10 +759,8 @@ final class CourseContainerViewModelTests: XCTestCase { Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) Given(connectivity, .isMobileData(getter: false)) - Given(downloadManager, .publisher(willReturn: Empty().eraseToAnyPublisher())) Given(downloadManager, .eventPublisher(willReturn: Just(.added).eraseToAnyPublisher())) - Given(downloadManager, .getDownloadTasksForCourse(.any, willReturn: [])) - Given(downloadManager, .updateUnzippedFileSize(for: .any, willReturn: [])) + Given(courseHelperMock, .sizeFor(sequentials: .any, willReturn: 1000)) let viewModel = CourseContainerViewModel( interactor: interactor, @@ -747,21 +777,36 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelperMock ) viewModel.courseStructure = courseStructure - await viewModel.setDownloadsStates(courseStructure: courseStructure) - await viewModel.download( - state: .available, - blocks: [block], - sequentials: [sequential] - ) + await viewModel.onDownloadViewTap( + chapter: chapter, + state: .finished + ) await Task.yield() - XCTAssertEqual(viewModel.sequentialsDownloadState[blockId], .available) - + Verify( + analytics, + 0, + .bulkDownloadVideosSection( + courseID: .any, + sectionID: .any, + videos: .any + ) + ) + Verify( + analytics, + 1, + .bulkDeleteVideosSection( + courseID: .value(courseStructure.id), + sectionId: .value(chapter.id), + videos: .value(1) + ) + ) } func testSetDownloadsStatesAvailable() async throws { @@ -855,10 +900,9 @@ final class CourseContainerViewModelTests: XCTestCase { Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) Given(connectivity, .isMobileData(getter: false)) - Given(downloadManager, .publisher(willReturn: Empty().eraseToAnyPublisher())) Given(downloadManager, .eventPublisher(willReturn: Just(.added).eraseToAnyPublisher())) - Given(downloadManager, .getDownloadTasksForCourse(.any, willReturn: [])) - Given(downloadManager, .updateUnzippedFileSize(for: .any, willReturn: [])) + Given(downloadManager, .getDownloadTasks(willReturn: [])) + let courseHelper = CourseDownloadHelper(courseStructure: courseStructure, manager: downloadManager) let viewModel = CourseContainerViewModel( interactor: interactor, @@ -875,10 +919,13 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelper ) viewModel.courseStructure = courseStructure - await viewModel.setDownloadsStates(courseStructure: courseStructure) + viewModel.courseStructure = courseStructure + viewModel.courseHelper.courseStructure = courseStructure + await viewModel.courseHelper.refreshValue() await Task.yield() @@ -971,7 +1018,7 @@ final class CourseContainerViewModelTests: XCTestCase { let downloadData = DownloadDataTask( id: "1", blockId: "1", - courseId: "course123", + courseId: "123", userId: 1, url: "https://example.com/file.mp4", fileName: "file.mp4", @@ -981,18 +1028,17 @@ final class CourseContainerViewModelTests: XCTestCase { state: .inProgress, type: .video, fileSize: 1000, - lastModified: "" + lastModified: "", + actualSize: 333 ) Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) Given(connectivity, .isMobileData(getter: false)) - Given(downloadManager, .publisher(willReturn: Empty().eraseToAnyPublisher())) Given(downloadManager, .eventPublisher(willReturn: Just(.added).eraseToAnyPublisher())) - Given(downloadManager, .getDownloadTasksForCourse(.any, willReturn: [downloadData])) - Given(downloadManager, .updateUnzippedFileSize(for: .any, willReturn: [sequential])) - + Given(downloadManager, .getDownloadTasks(willReturn: [downloadData])) + let courseHelper = CourseDownloadHelper(courseStructure: courseStructure, manager: downloadManager) let viewModel = CourseContainerViewModel( interactor: interactor, authInteractor: authInteractor, @@ -1008,10 +1054,12 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelper ) viewModel.courseStructure = courseStructure - await viewModel.setDownloadsStates(courseStructure: courseStructure) + viewModel.courseHelper.courseStructure = courseStructure + await viewModel.courseHelper.refreshValue() await Task.yield() @@ -1104,7 +1152,7 @@ final class CourseContainerViewModelTests: XCTestCase { let downloadData = DownloadDataTask( id: "1", blockId: "1", - courseId: "course123", + courseId: "123", userId: 1, url: "https://example.com/file.mp4", fileName: "file.mp4", @@ -1114,18 +1162,17 @@ final class CourseContainerViewModelTests: XCTestCase { state: .finished, type: .video, fileSize: 1000, - lastModified: "" + lastModified: "", + actualSize: 333 ) Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) Given(connectivity, .isMobileData(getter: false)) - Given(downloadManager, .publisher(willReturn: Empty().eraseToAnyPublisher())) Given(downloadManager, .eventPublisher(willReturn: Just(.added).eraseToAnyPublisher())) - Given(downloadManager, .getDownloadTasksForCourse(.any, willReturn: [downloadData])) - Given(downloadManager, .updateUnzippedFileSize(for: .any, willReturn: [sequential])) - + Given(downloadManager, .getDownloadTasks(willReturn: [downloadData])) + let courseHelper = CourseDownloadHelper(courseStructure: courseStructure, manager: downloadManager) let viewModel = CourseContainerViewModel( interactor: interactor, authInteractor: authInteractor, @@ -1141,10 +1188,12 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelper ) viewModel.courseStructure = courseStructure - await viewModel.setDownloadsStates(courseStructure: courseStructure) + viewModel.courseHelper.courseStructure = courseStructure + await viewModel.courseHelper.refreshValue() await Task.yield() @@ -1264,7 +1313,7 @@ final class CourseContainerViewModelTests: XCTestCase { let downloadData = DownloadDataTask( id: "1", blockId: "1", - courseId: "course123", + courseId: "123", userId: 1, url: "https://example.com/file.mp4", fileName: "file.mp4", @@ -1274,18 +1323,17 @@ final class CourseContainerViewModelTests: XCTestCase { state: .finished, type: .video, fileSize: 1000, - lastModified: "" + lastModified: "", + actualSize: 333 ) Given(connectivity, .isInternetAvaliable(getter: true)) Given(connectivity, .internetReachableSubject(getter: .init(.reachable))) Given(connectivity, .isMobileData(getter: false)) - Given(downloadManager, .publisher(willReturn: Empty().eraseToAnyPublisher())) Given(downloadManager, .eventPublisher(willReturn: Just(.added).eraseToAnyPublisher())) - Given(downloadManager, .getDownloadTasksForCourse(.any, willReturn: [downloadData])) - Given(downloadManager, .updateUnzippedFileSize(for: .any, willReturn: [sequential])) - + Given(downloadManager, .getDownloadTasks(willReturn: [downloadData])) + let courseHelper = CourseDownloadHelper(courseStructure: courseStructure, manager: downloadManager) let viewModel = CourseContainerViewModel( interactor: interactor, authInteractor: authInteractor, @@ -1301,11 +1349,12 @@ final class CourseContainerViewModelTests: XCTestCase { enrollmentStart: nil, enrollmentEnd: nil, lastVisitedBlockID: nil, - coreAnalytics: CoreAnalyticsMock() + coreAnalytics: CoreAnalyticsMock(), + courseHelper: courseHelper ) viewModel.courseStructure = courseStructure - await viewModel.setDownloadsStates(courseStructure: courseStructure) - + viewModel.courseHelper.courseStructure = courseStructure + await viewModel.courseHelper.refreshValue() await Task.yield() XCTAssertEqual(viewModel.sequentialsDownloadState[sequential.id], .available) diff --git a/Course/CourseTests/Presentation/Container/CourseDownloadHelperTests.swift b/Course/CourseTests/Presentation/Container/CourseDownloadHelperTests.swift new file mode 100644 index 000000000..fa0503c4b --- /dev/null +++ b/Course/CourseTests/Presentation/Container/CourseDownloadHelperTests.swift @@ -0,0 +1,259 @@ +// +// CourseDownloadHelperTests.swift +// Course +// +// Created by Vadim Kuznetsov on 25.01.25. +// + +import Core +import Combine +import SwiftyMocky +import XCTest + +@testable import Course + +@MainActor +final class CourseDownloadHelperTests: XCTestCase { + var downloadManagerMock: DownloadManagerProtocolMock! + var helper: CourseDownloadHelper! + var cancellables: [AnyCancellable] = [] + var downloadPublisher: PassthroughSubject = .init() + var block: CourseBlock! + var sequential: CourseSequential! + var task: DownloadDataTask! + var value: CourseDownloadValue! + var timeout: TimeInterval = 5 + + override func setUp() { + super.setUp() + downloadManagerMock = DownloadManagerProtocolMock() + block = CourseBlock( + blockId: "", + id: "1", + courseId: "123", + topicId: "", + graded: false, + due: Date(), + completion: 0, + type: .video, + displayName: "", + studentUrl: "", + webUrl: "", + encodedVideo: .init( + fallback: nil, + youtube: nil, + desktopMP4: .init(url: "http://test/test.mp4", fileSize: 1000, streamPriority: 1), + mobileHigh: nil, + mobileLow: nil, + hls: nil + ), + multiDevice: true, + offlineDownload: nil + ) + + let vertical = CourseVertical( + blockId: "", + id: "", + courseId: "123", + displayName: "", + type: .vertical, + completion: 0, + childs: [block], + webUrl: "" + ) + sequential = CourseSequential( + blockId: "", + id: "", + displayName: "", + type: .chapter, + completion: 0, + childs: [vertical], + sequentialProgress: nil, + due: Date() + ) + let chapter = CourseChapter( + blockId: "", + id: "", + displayName: "", + type: .chapter, + childs: [sequential] + ) + + let childs = [chapter] + + let courseStructure = CourseStructure( + id: "123", + graded: true, + completion: 0, + viewYouTubeUrl: "", + encodedVideo: "", + displayName: "", + topicID: nil, + childs: childs, + media: CourseMedia( + image: CourseImage( + raw: "", + small: "", + large: "" + ) + ), + certificate: nil, + org: "", + isSelfPaced: true, + courseProgress: nil + ) + task = DownloadDataTask(block: block, userId: 0, downloadQuality: .auto) + value = .init( + currentDownloadTask: task, + courseDownloadTasks: [task], + allDownloadTasks: [task], + notFinishedTasks: [task], + downloadableVerticals: [.init(vertical: vertical, state: .downloading)], + sequentialsStates: [sequential.id: .downloading], + totalFilesSize: block.fileSize!, + downloadedFilesSize: 0, + largestBlocks: [], + state: .cancel + ) + Given(downloadManagerMock, .getDownloadTasks(willReturn: [task!])) + Given(downloadManagerMock, .getCurrentDownloadTask(willReturn: task)) + Given(downloadManagerMock, .eventPublisher(willReturn: downloadPublisher.eraseToAnyPublisher())) + helper = CourseDownloadHelper(courseStructure: courseStructure, manager: downloadManagerMock) + } + + override func tearDown() { + super.tearDown() + cancellables.removeAll() + cancellables = [] + } + + func testPublisher_whenRefresh_ShouldSendValue() { + // given + var valueReceived: CourseDownloadValue? + let expectation = expectation(description: "wait for publisher") + helper.publisher() + .sink { value in + valueReceived = value + expectation.fulfill() + } + .store(in: &cancellables) + + // when + helper.refreshValue() + + // then + wait(for: [expectation], timeout: timeout) + Verify(downloadManagerMock, .once, .getDownloadTasks()) + Verify(downloadManagerMock, .once, .getCurrentDownloadTask()) + XCTAssertEqual(valueReceived, value) + } + + func testPublisher_whenAsyncRefresh_ShouldSendValue() async { + // given + var valueReceived: CourseDownloadValue? + let expectation = expectation(description: "wait for publisher") + helper.publisher() + .sink { value in + valueReceived = value + expectation.fulfill() + } + .store(in: &cancellables) + + // when + await helper.refreshValue() + + // then + await fulfillment(of: [expectation], timeout: timeout) + Verify(downloadManagerMock, .once, .getDownloadTasks()) + Verify(downloadManagerMock, .once, .getCurrentDownloadTask()) + XCTAssertEqual(valueReceived, value) + } + + func testPublisher_whenReceivedNotProgressEvent_ShouldSendValue() async { + // given + var valueReceived: CourseDownloadValue? + var receivedCount = 0 + let addedExpectation = expectation(description: "wait for added event") + + let expectations: [XCTestExpectation] = [ + addedExpectation + ] + + helper.publisher() + .sink { value in + expectations[receivedCount].fulfill() + receivedCount += 1 + valueReceived = value + } + .store(in: &cancellables) + // when + downloadPublisher.send(.added) //1 + // then + await fulfillment(of: expectations, timeout: timeout) + Verify(downloadManagerMock, .once, .getDownloadTasks()) + Verify(downloadManagerMock, .once, .getCurrentDownloadTask()) + XCTAssertEqual(receivedCount, 1) + XCTAssertEqual(valueReceived, value) + } + + func testEventPublisher_whenReceivedProgressEvent_ShouldSendEvent() async { + // given + var valueReceived: DownloadDataTask? + task.progress = 0.5 + let expectation = expectation(description: "wait for progress event") + var countOfEvents: Int = 0 + helper.progressPublisher() + .sink { value in + expectation.fulfill() + countOfEvents += 1 + valueReceived = value + } + .store(in: &cancellables) + helper.value = value + // when + downloadPublisher.send(.progress(task)) + // then + await fulfillment(of: [expectation], timeout: timeout) + value.currentDownloadTask = task + XCTAssertEqual(helper.value, value) + XCTAssertEqual(valueReceived, task) + XCTAssertEqual(countOfEvents, 1) + Verify(downloadManagerMock, .never, .getDownloadTasks()) + Verify(downloadManagerMock, .never, .getCurrentDownloadTask()) + } + + func testSizeForBlock_whenCalled_ShouldReturnSize() { + // when + let size = helper.sizeFor(block: block) + // then + XCTAssertEqual(size, block.fileSize) + } + + func testSizeForBlocks_whenCalled_ShouldReturnSize() { + // when + let size = helper.sizeFor(blocks: [block]) + // then + XCTAssertEqual(size, block.fileSize) + } + + func testSizeForSequential_whenCalled_ShouldReturnSize() { + // when + let size = helper.sizeFor(sequential: sequential) + // then + XCTAssertEqual(size, sequential.totalSize) + } + + func testSizeForSequentials_whenCalled_ShouldReturnSize() { + // when + let size = helper.sizeFor(sequentials: [sequential]) + // then + XCTAssertEqual(size, sequential.totalSize) + } + + func testCancelDownloading_whenCalled_ShouldCallManagerMethod() async throws { + // when + try await helper.cancelDownloading(task: task) + // then + Verify(downloadManagerMock, .cancelDownloading(task: .value(task))) + } +} diff --git a/Dashboard/DashboardTests/DashboardMock.generated.swift b/Dashboard/DashboardTests/DashboardMock.generated.swift index 888d63f26..71d96e33a 100644 --- a/Dashboard/DashboardTests/DashboardMock.generated.swift +++ b/Dashboard/DashboardTests/DashboardMock.generated.swift @@ -2531,41 +2531,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { perform?(`blocks`, `downloadQuality`) } - open func nextBlockForDownloading() -> DownloadDataTask? { - addInvocation(.m_nextBlockForDownloading) - let perform = methodPerformValue(.m_nextBlockForDownloading) as? () -> Void - perform?() - var __value: DownloadDataTask? = nil - do { - __value = try methodReturnValue(.m_nextBlockForDownloading).casted() - } catch { - // do nothing - } - return __value - } - - open func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) { - addInvocation(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) - let perform = methodPerformValue(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) as? (String, DownloadState, Data?) -> Void - perform?(`id`, `state`, `resumeData`) - } - - open func deleteDownloadDataTask(id: String) throws { - addInvocation(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) - let perform = methodPerformValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) as? (String) -> Void - perform?(`id`) - do { - _ = try methodReturnValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } - } - - open func saveDownloadDataTask(_ task: DownloadDataTask) { - addInvocation(.m_saveDownloadDataTask__task(Parameter.value(`task`))) - let perform = methodPerformValue(.m_saveDownloadDataTask__task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + open func updateTask(task: DownloadDataTask) { + addInvocation(.m_updateTask__task_task(Parameter.value(`task`))) + let perform = methodPerformValue(.m_updateTask__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void perform?(`task`) } @@ -2610,6 +2578,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { return __value } + open func deleteDownloadDataTasks(ids: [String]) { + addInvocation(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) + let perform = methodPerformValue(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) as? ([String]) -> Void + perform?(`ids`) + } + fileprivate enum MethodType { case m_set__userId_userId(Parameter) @@ -2622,13 +2596,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case m_deleteProgress__for_blockID(Parameter) case m_deleteAllProgress case m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>, Parameter) - case m_nextBlockForDownloading - case m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter, Parameter, Parameter) - case m_deleteDownloadDataTask__id_id(Parameter) - case m_saveDownloadDataTask__task(Parameter) + case m_updateTask__task_task(Parameter) case m_downloadDataTask__for_blockId(Parameter) case m_getDownloadDataTasks case m_getDownloadDataTasksForCourse__courseId(Parameter) + case m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { @@ -2671,23 +2643,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsDownloadquality, rhs: rhsDownloadquality, with: matcher), lhsDownloadquality, rhsDownloadquality, "downloadQuality")) return Matcher.ComparisonResult(results) - case (.m_nextBlockForDownloading, .m_nextBlockForDownloading): return .match - - case (.m_updateDownloadState__id_idstate_stateresumeData_resumeData(let lhsId, let lhsState, let lhsResumedata), .m_updateDownloadState__id_idstate_stateresumeData_resumeData(let rhsId, let rhsState, let rhsResumedata)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsState, rhs: rhsState, with: matcher), lhsState, rhsState, "state")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsResumedata, rhs: rhsResumedata, with: matcher), lhsResumedata, rhsResumedata, "resumeData")) - return Matcher.ComparisonResult(results) - - case (.m_deleteDownloadDataTask__id_id(let lhsId), .m_deleteDownloadDataTask__id_id(let rhsId)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - return Matcher.ComparisonResult(results) - - case (.m_saveDownloadDataTask__task(let lhsTask), .m_saveDownloadDataTask__task(let rhsTask)): + case (.m_updateTask__task_task(let lhsTask), .m_updateTask__task_task(let rhsTask)): var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "_ task")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) return Matcher.ComparisonResult(results) case (.m_downloadDataTask__for_blockId(let lhsBlockid), .m_downloadDataTask__for_blockId(let rhsBlockid)): @@ -2701,6 +2659,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) return Matcher.ComparisonResult(results) + + case (.m_deleteDownloadDataTasks__ids_ids(let lhsIds), .m_deleteDownloadDataTasks__ids_ids(let rhsIds)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsIds, rhs: rhsIds, with: matcher), lhsIds, rhsIds, "ids")) + return Matcher.ComparisonResult(results) default: return .none } } @@ -2717,13 +2680,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case let .m_deleteProgress__for_blockID(p0): return p0.intValue case .m_deleteAllProgress: return 0 case let .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(p0, p1): return p0.intValue + p1.intValue - case .m_nextBlockForDownloading: return 0 - case let .m_updateDownloadState__id_idstate_stateresumeData_resumeData(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue - case let .m_deleteDownloadDataTask__id_id(p0): return p0.intValue - case let .m_saveDownloadDataTask__task(p0): return p0.intValue + case let .m_updateTask__task_task(p0): return p0.intValue case let .m_downloadDataTask__for_blockId(p0): return p0.intValue case .m_getDownloadDataTasks: return 0 case let .m_getDownloadDataTasksForCourse__courseId(p0): return p0.intValue + case let .m_deleteDownloadDataTasks__ids_ids(p0): return p0.intValue } } func assertionName() -> String { @@ -2738,13 +2699,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case .m_deleteProgress__for_blockID: return ".deleteProgress(for:)" case .m_deleteAllProgress: return ".deleteAllProgress()" case .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality: return ".addToDownloadQueue(blocks:downloadQuality:)" - case .m_nextBlockForDownloading: return ".nextBlockForDownloading()" - case .m_updateDownloadState__id_idstate_stateresumeData_resumeData: return ".updateDownloadState(id:state:resumeData:)" - case .m_deleteDownloadDataTask__id_id: return ".deleteDownloadDataTask(id:)" - case .m_saveDownloadDataTask__task: return ".saveDownloadDataTask(_:)" + case .m_updateTask__task_task: return ".updateTask(task:)" case .m_downloadDataTask__for_blockId: return ".downloadDataTask(for:)" case .m_getDownloadDataTasks: return ".getDownloadDataTasks()" case .m_getDownloadDataTasksForCourse__courseId: return ".getDownloadDataTasksForCourse(_:)" + case .m_deleteDownloadDataTasks__ids_ids: return ".deleteDownloadDataTasks(ids:)" } } } @@ -2771,9 +2730,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func loadAllOfflineProgress(willReturn: [OfflineProgress]...) -> MethodStub { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func nextBlockForDownloading(willReturn: DownloadDataTask?...) -> MethodStub { - return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func downloadDataTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } @@ -2804,13 +2760,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func nextBlockForDownloading(willProduce: (Stubber) -> Void) -> MethodStub { - let willReturn: [DownloadDataTask?] = [] - let given: Given = { return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (DownloadDataTask?).self) - willProduce(stubber) - return given - } public static func downloadDataTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [DownloadDataTask?] = [] let given: Given = { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -2844,16 +2793,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func deleteDownloadDataTask(id: Parameter, willThrow: Error...) -> MethodStub { - return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) - } - public static func deleteDownloadDataTask(id: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given - } } public struct Verify { @@ -2870,13 +2809,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func deleteProgress(for blockID: Parameter) -> Verify { return Verify(method: .m_deleteProgress__for_blockID(`blockID`))} public static func deleteAllProgress() -> Verify { return Verify(method: .m_deleteAllProgress)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`))} - public static func nextBlockForDownloading() -> Verify { return Verify(method: .m_nextBlockForDownloading)} - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter) -> Verify { return Verify(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`))} - public static func deleteDownloadDataTask(id: Parameter) -> Verify { return Verify(method: .m_deleteDownloadDataTask__id_id(`id`))} - public static func saveDownloadDataTask(_ task: Parameter) -> Verify { return Verify(method: .m_saveDownloadDataTask__task(`task`))} + public static func updateTask(task: Parameter) -> Verify { return Verify(method: .m_updateTask__task_task(`task`))} public static func downloadDataTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadDataTask__for_blockId(`blockId`))} public static func getDownloadDataTasks() -> Verify { return Verify(method: .m_getDownloadDataTasks)} public static func getDownloadDataTasksForCourse(_ courseId: Parameter) -> Verify { return Verify(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`))} + public static func deleteDownloadDataTasks(ids: Parameter<[String]>) -> Verify { return Verify(method: .m_deleteDownloadDataTasks__ids_ids(`ids`))} } public struct Perform { @@ -2914,17 +2851,8 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter, perform: @escaping ([CourseBlock], DownloadQuality) -> Void) -> Perform { return Perform(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`), performs: perform) } - public static func nextBlockForDownloading(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_nextBlockForDownloading, performs: perform) - } - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter, perform: @escaping (String, DownloadState, Data?) -> Void) -> Perform { - return Perform(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`), performs: perform) - } - public static func deleteDownloadDataTask(id: Parameter, perform: @escaping (String) -> Void) -> Perform { - return Perform(method: .m_deleteDownloadDataTask__id_id(`id`), performs: perform) - } - public static func saveDownloadDataTask(_ task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { - return Perform(method: .m_saveDownloadDataTask__task(`task`), performs: perform) + public static func updateTask(task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { + return Perform(method: .m_updateTask__task_task(`task`), performs: perform) } public static func downloadDataTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_downloadDataTask__for_blockId(`blockId`), performs: perform) @@ -2935,6 +2863,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getDownloadDataTasksForCourse(_ courseId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), performs: perform) } + public static func deleteDownloadDataTasks(ids: Parameter<[String]>, perform: @escaping ([String]) -> Void) -> Perform { + return Perform(method: .m_deleteDownloadDataTasks__ids_ids(`ids`), performs: perform) + } } public func given(_ method: Given) { @@ -3993,7 +3924,7 @@ open class DashboardInteractorProtocolMock: DashboardInteractorProtocol, Mock { } // MARK: - DownloadManagerProtocol -@MainActor + open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public init(sequencing sequencingPolicy: SequencingPolicy = .lastWrittenResolvedFirst, stubbing stubbingPolicy: StubbingPolicy = .wrap, file: StaticString = #file, line: UInt = #line) { SwiftyMockyTestObserver.setup() @@ -4032,27 +3963,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { if scopes.contains(.perform) { methodPerformValues = [] } } - public var currentDownloadTask: DownloadDataTask? { - get { invocations.append(.p_currentDownloadTask_get); return __p_currentDownloadTask ?? optionalGivenGetterValue(.p_currentDownloadTask_get, "DownloadManagerProtocolMock - stub value for currentDownloadTask was not defined") } - } - private var __p_currentDownloadTask: (DownloadDataTask)? - - open func publisher() throws -> AnyPublisher { - addInvocation(.m_publisher) - let perform = methodPerformValue(.m_publisher) as? () -> Void + open func getCurrentDownloadTask() -> DownloadDataTask? { + addInvocation(.m_getCurrentDownloadTask) + let perform = methodPerformValue(.m_getCurrentDownloadTask) as? () -> Void perform?() - var __value: AnyPublisher + var __value: DownloadDataTask? = nil do { - __value = try methodReturnValue(.m_publisher).casted() - } catch MockError.notStubed { - onFatalFailure("Stub return value not specified for publisher(). Use given") - Failure("Stub return value not specified for publisher(). Use given") + __value = try methodReturnValue(.m_getCurrentDownloadTask).casted() } catch { - throw error + // do nothing } return __value } @@ -4164,15 +4087,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { } } - open func deleteFile(blocks: [CourseBlock]) { - addInvocation(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - } - - open func deleteAllFiles() { - addInvocation(.m_deleteAllFiles) - let perform = methodPerformValue(.m_deleteAllFiles) as? () -> Void + open func deleteAll() { + addInvocation(.m_deleteAll) + let perform = methodPerformValue(.m_deleteAll) as? () -> Void perform?() } @@ -4189,20 +4106,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return __value } - open func updateUnzippedFileSize(for sequentials: [CourseSequential]) -> [CourseSequential] { - addInvocation(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) - let perform = methodPerformValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) as? ([CourseSequential]) -> Void - perform?(`sequentials`) - var __value: [CourseSequential] - do { - __value = try methodReturnValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))).casted() - } catch { - onFatalFailure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - Failure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - } - return __value - } - open func resumeDownloading() throws { addInvocation(.m_resumeDownloading) let perform = methodPerformValue(.m_resumeDownloading) as? () -> Void @@ -4236,9 +4139,28 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { perform?() } + open func delete(blocks: [CourseBlock], courseId: String) { + addInvocation(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) + let perform = methodPerformValue(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) as? ([CourseBlock], String) -> Void + perform?(`blocks`, `courseId`) + } + + open func downloadTask(for blockId: String) -> DownloadDataTask? { + addInvocation(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) + let perform = methodPerformValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value + } + fileprivate enum MethodType { - case m_publisher + case m_getCurrentDownloadTask case m_eventPublisher case m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>) case m_getDownloadTasks @@ -4247,18 +4169,17 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case m_cancelDownloading__task_task(Parameter) case m_cancelDownloading__courseId_courseId(Parameter) case m_cancelAllDownloading - case m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>) - case m_deleteAllFiles + case m_deleteAll case m_fileUrl__for_blockId(Parameter) - case m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>) case m_resumeDownloading case m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>) case m_removeAppSupportDirectoryUnusedContent - case p_currentDownloadTask_get + case m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>, Parameter) + case m_downloadTask__for_blockId(Parameter) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { - case (.m_publisher, .m_publisher): return .match + case (.m_getCurrentDownloadTask, .m_getCurrentDownloadTask): return .match case (.m_eventPublisher, .m_eventPublisher): return .match @@ -4292,23 +4213,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case (.m_cancelAllDownloading, .m_cancelAllDownloading): return .match - case (.m_deleteFile__blocks_blocks(let lhsBlocks), .m_deleteFile__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) - - case (.m_deleteAllFiles, .m_deleteAllFiles): return .match + case (.m_deleteAll, .m_deleteAll): return .match case (.m_fileUrl__for_blockId(let lhsBlockid), .m_fileUrl__for_blockId(let rhsBlockid)): var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) return Matcher.ComparisonResult(results) - case (.m_updateUnzippedFileSize__for_sequentials(let lhsSequentials), .m_updateUnzippedFileSize__for_sequentials(let rhsSequentials)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequentials, rhs: rhsSequentials, with: matcher), lhsSequentials, rhsSequentials, "for sequentials")) - return Matcher.ComparisonResult(results) - case (.m_resumeDownloading, .m_resumeDownloading): return .match case (.m_isLargeVideosSize__blocks_blocks(let lhsBlocks), .m_isLargeVideosSize__blocks_blocks(let rhsBlocks)): @@ -4317,14 +4228,24 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return Matcher.ComparisonResult(results) case (.m_removeAppSupportDirectoryUnusedContent, .m_removeAppSupportDirectoryUnusedContent): return .match - case (.p_currentDownloadTask_get,.p_currentDownloadTask_get): return Matcher.ComparisonResult.match + + case (.m_delete__blocks_blockscourseId_courseId(let lhsBlocks, let lhsCourseid), .m_delete__blocks_blockscourseId_courseId(let rhsBlocks, let rhsCourseid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + return Matcher.ComparisonResult(results) + + case (.m_downloadTask__for_blockId(let lhsBlockid), .m_downloadTask__for_blockId(let rhsBlockid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) default: return .none } } func intValue() -> Int { switch self { - case .m_publisher: return 0 + case .m_getCurrentDownloadTask: return 0 case .m_eventPublisher: return 0 case let .m_addToDownloadQueue__blocks_blocks(p0): return p0.intValue case .m_getDownloadTasks: return 0 @@ -4333,19 +4254,18 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case let .m_cancelDownloading__task_task(p0): return p0.intValue case let .m_cancelDownloading__courseId_courseId(p0): return p0.intValue case .m_cancelAllDownloading: return 0 - case let .m_deleteFile__blocks_blocks(p0): return p0.intValue - case .m_deleteAllFiles: return 0 + case .m_deleteAll: return 0 case let .m_fileUrl__for_blockId(p0): return p0.intValue - case let .m_updateUnzippedFileSize__for_sequentials(p0): return p0.intValue case .m_resumeDownloading: return 0 case let .m_isLargeVideosSize__blocks_blocks(p0): return p0.intValue case .m_removeAppSupportDirectoryUnusedContent: return 0 - case .p_currentDownloadTask_get: return 0 + case let .m_delete__blocks_blockscourseId_courseId(p0, p1): return p0.intValue + p1.intValue + case let .m_downloadTask__for_blockId(p0): return p0.intValue } } func assertionName() -> String { switch self { - case .m_publisher: return ".publisher()" + case .m_getCurrentDownloadTask: return ".getCurrentDownloadTask()" case .m_eventPublisher: return ".eventPublisher()" case .m_addToDownloadQueue__blocks_blocks: return ".addToDownloadQueue(blocks:)" case .m_getDownloadTasks: return ".getDownloadTasks()" @@ -4354,14 +4274,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case .m_cancelDownloading__task_task: return ".cancelDownloading(task:)" case .m_cancelDownloading__courseId_courseId: return ".cancelDownloading(courseId:)" case .m_cancelAllDownloading: return ".cancelAllDownloading()" - case .m_deleteFile__blocks_blocks: return ".deleteFile(blocks:)" - case .m_deleteAllFiles: return ".deleteAllFiles()" + case .m_deleteAll: return ".deleteAll()" case .m_fileUrl__for_blockId: return ".fileUrl(for:)" - case .m_updateUnzippedFileSize__for_sequentials: return ".updateUnzippedFileSize(for:)" case .m_resumeDownloading: return ".resumeDownloading()" case .m_isLargeVideosSize__blocks_blocks: return ".isLargeVideosSize(blocks:)" case .m_removeAppSupportDirectoryUnusedContent: return ".removeAppSupportDirectoryUnusedContent()" - case .p_currentDownloadTask_get: return "[get] .currentDownloadTask" + case .m_delete__blocks_blockscourseId_courseId: return ".delete(blocks:courseId:)" + case .m_downloadTask__for_blockId: return ".downloadTask(for:)" } } } @@ -4374,12 +4293,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { super.init(products) } - public static func currentDownloadTask(getter defaultValue: DownloadDataTask?...) -> PropertyStub { - return Given(method: .p_currentDownloadTask_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) - } - public static func publisher(willReturn: AnyPublisher...) -> MethodStub { - return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + public static func getCurrentDownloadTask(willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) } public static func eventPublisher(willReturn: AnyPublisher...) -> MethodStub { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) @@ -4393,12 +4309,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func fileUrl(for blockId: Parameter, willReturn: URL?...) -> MethodStub { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willReturn: [CourseSequential]...) -> MethodStub { - return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willReturn: Bool...) -> MethodStub { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) } + public static func downloadTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func getCurrentDownloadTask(willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given + } public static func eventPublisher(willProduce: (Stubber>) -> Void) -> MethodStub { let willReturn: [AnyPublisher] = [] let given: Given = { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -4427,13 +4350,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willProduce: (Stubber<[CourseSequential]>) -> Void) -> MethodStub { - let willReturn: [[CourseSequential]] = [] - let given: Given = { return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([CourseSequential]).self) - willProduce(stubber) - return given - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [Bool] = [] let given: Given = { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -4441,13 +4357,10 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func publisher(willThrow: Error...) -> MethodStub { - return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) - } - public static func publisher(willProduce: (StubberThrows>) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (AnyPublisher).self) + public static func downloadTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) willProduce(stubber) return given } @@ -4516,7 +4429,7 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public struct Verify { fileprivate var method: MethodType - public static func publisher() -> Verify { return Verify(method: .m_publisher)} + public static func getCurrentDownloadTask() -> Verify { return Verify(method: .m_getCurrentDownloadTask)} public static func eventPublisher() -> Verify { return Verify(method: .m_eventPublisher)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocks(`blocks`))} public static func getDownloadTasks() -> Verify { return Verify(method: .m_getDownloadTasks)} @@ -4525,22 +4438,21 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelDownloading(task: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__task_task(`task`))} public static func cancelDownloading(courseId: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__courseId_courseId(`courseId`))} public static func cancelAllDownloading() -> Verify { return Verify(method: .m_cancelAllDownloading)} - public static func deleteFile(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_deleteFile__blocks_blocks(`blocks`))} - public static func deleteAllFiles() -> Verify { return Verify(method: .m_deleteAllFiles)} + public static func deleteAll() -> Verify { return Verify(method: .m_deleteAll)} public static func fileUrl(for blockId: Parameter) -> Verify { return Verify(method: .m_fileUrl__for_blockId(`blockId`))} - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>) -> Verify { return Verify(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`))} public static func resumeDownloading() -> Verify { return Verify(method: .m_resumeDownloading)} public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_isLargeVideosSize__blocks_blocks(`blocks`))} public static func removeAppSupportDirectoryUnusedContent() -> Verify { return Verify(method: .m_removeAppSupportDirectoryUnusedContent)} - public static var currentDownloadTask: Verify { return Verify(method: .p_currentDownloadTask_get) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter) -> Verify { return Verify(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`))} + public static func downloadTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadTask__for_blockId(`blockId`))} } public struct Perform { fileprivate var method: MethodType var performs: Any - public static func publisher(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_publisher, performs: perform) + public static func getCurrentDownloadTask(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_getCurrentDownloadTask, performs: perform) } public static func eventPublisher(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_eventPublisher, performs: perform) @@ -4566,18 +4478,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelAllDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_cancelAllDownloading, performs: perform) } - public static func deleteFile(blocks: Parameter<[CourseBlock]>, perform: @escaping ([CourseBlock]) -> Void) -> Perform { - return Perform(method: .m_deleteFile__blocks_blocks(`blocks`), performs: perform) - } - public static func deleteAllFiles(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_deleteAllFiles, performs: perform) + public static func deleteAll(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_deleteAll, performs: perform) } public static func fileUrl(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_fileUrl__for_blockId(`blockId`), performs: perform) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, perform: @escaping ([CourseSequential]) -> Void) -> Perform { - return Perform(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), performs: perform) - } public static func resumeDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_resumeDownloading, performs: perform) } @@ -4587,6 +4493,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func removeAppSupportDirectoryUnusedContent(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_removeAppSupportDirectoryUnusedContent, performs: perform) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter, perform: @escaping ([CourseBlock], String) -> Void) -> Perform { + return Perform(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`), performs: perform) + } + public static func downloadTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { + return Perform(method: .m_downloadTask__for_blockId(`blockId`), performs: perform) + } } public func given(_ method: Given) { diff --git a/Discovery/DiscoveryTests/DiscoveryMock.generated.swift b/Discovery/DiscoveryTests/DiscoveryMock.generated.swift index 9938d2ecb..ea41a496a 100644 --- a/Discovery/DiscoveryTests/DiscoveryMock.generated.swift +++ b/Discovery/DiscoveryTests/DiscoveryMock.generated.swift @@ -2531,41 +2531,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { perform?(`blocks`, `downloadQuality`) } - open func nextBlockForDownloading() -> DownloadDataTask? { - addInvocation(.m_nextBlockForDownloading) - let perform = methodPerformValue(.m_nextBlockForDownloading) as? () -> Void - perform?() - var __value: DownloadDataTask? = nil - do { - __value = try methodReturnValue(.m_nextBlockForDownloading).casted() - } catch { - // do nothing - } - return __value - } - - open func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) { - addInvocation(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) - let perform = methodPerformValue(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) as? (String, DownloadState, Data?) -> Void - perform?(`id`, `state`, `resumeData`) - } - - open func deleteDownloadDataTask(id: String) throws { - addInvocation(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) - let perform = methodPerformValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) as? (String) -> Void - perform?(`id`) - do { - _ = try methodReturnValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } - } - - open func saveDownloadDataTask(_ task: DownloadDataTask) { - addInvocation(.m_saveDownloadDataTask__task(Parameter.value(`task`))) - let perform = methodPerformValue(.m_saveDownloadDataTask__task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + open func updateTask(task: DownloadDataTask) { + addInvocation(.m_updateTask__task_task(Parameter.value(`task`))) + let perform = methodPerformValue(.m_updateTask__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void perform?(`task`) } @@ -2610,6 +2578,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { return __value } + open func deleteDownloadDataTasks(ids: [String]) { + addInvocation(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) + let perform = methodPerformValue(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) as? ([String]) -> Void + perform?(`ids`) + } + fileprivate enum MethodType { case m_set__userId_userId(Parameter) @@ -2622,13 +2596,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case m_deleteProgress__for_blockID(Parameter) case m_deleteAllProgress case m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>, Parameter) - case m_nextBlockForDownloading - case m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter, Parameter, Parameter) - case m_deleteDownloadDataTask__id_id(Parameter) - case m_saveDownloadDataTask__task(Parameter) + case m_updateTask__task_task(Parameter) case m_downloadDataTask__for_blockId(Parameter) case m_getDownloadDataTasks case m_getDownloadDataTasksForCourse__courseId(Parameter) + case m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { @@ -2671,23 +2643,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsDownloadquality, rhs: rhsDownloadquality, with: matcher), lhsDownloadquality, rhsDownloadquality, "downloadQuality")) return Matcher.ComparisonResult(results) - case (.m_nextBlockForDownloading, .m_nextBlockForDownloading): return .match - - case (.m_updateDownloadState__id_idstate_stateresumeData_resumeData(let lhsId, let lhsState, let lhsResumedata), .m_updateDownloadState__id_idstate_stateresumeData_resumeData(let rhsId, let rhsState, let rhsResumedata)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsState, rhs: rhsState, with: matcher), lhsState, rhsState, "state")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsResumedata, rhs: rhsResumedata, with: matcher), lhsResumedata, rhsResumedata, "resumeData")) - return Matcher.ComparisonResult(results) - - case (.m_deleteDownloadDataTask__id_id(let lhsId), .m_deleteDownloadDataTask__id_id(let rhsId)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - return Matcher.ComparisonResult(results) - - case (.m_saveDownloadDataTask__task(let lhsTask), .m_saveDownloadDataTask__task(let rhsTask)): + case (.m_updateTask__task_task(let lhsTask), .m_updateTask__task_task(let rhsTask)): var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "_ task")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) return Matcher.ComparisonResult(results) case (.m_downloadDataTask__for_blockId(let lhsBlockid), .m_downloadDataTask__for_blockId(let rhsBlockid)): @@ -2701,6 +2659,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) return Matcher.ComparisonResult(results) + + case (.m_deleteDownloadDataTasks__ids_ids(let lhsIds), .m_deleteDownloadDataTasks__ids_ids(let rhsIds)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsIds, rhs: rhsIds, with: matcher), lhsIds, rhsIds, "ids")) + return Matcher.ComparisonResult(results) default: return .none } } @@ -2717,13 +2680,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case let .m_deleteProgress__for_blockID(p0): return p0.intValue case .m_deleteAllProgress: return 0 case let .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(p0, p1): return p0.intValue + p1.intValue - case .m_nextBlockForDownloading: return 0 - case let .m_updateDownloadState__id_idstate_stateresumeData_resumeData(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue - case let .m_deleteDownloadDataTask__id_id(p0): return p0.intValue - case let .m_saveDownloadDataTask__task(p0): return p0.intValue + case let .m_updateTask__task_task(p0): return p0.intValue case let .m_downloadDataTask__for_blockId(p0): return p0.intValue case .m_getDownloadDataTasks: return 0 case let .m_getDownloadDataTasksForCourse__courseId(p0): return p0.intValue + case let .m_deleteDownloadDataTasks__ids_ids(p0): return p0.intValue } } func assertionName() -> String { @@ -2738,13 +2699,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case .m_deleteProgress__for_blockID: return ".deleteProgress(for:)" case .m_deleteAllProgress: return ".deleteAllProgress()" case .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality: return ".addToDownloadQueue(blocks:downloadQuality:)" - case .m_nextBlockForDownloading: return ".nextBlockForDownloading()" - case .m_updateDownloadState__id_idstate_stateresumeData_resumeData: return ".updateDownloadState(id:state:resumeData:)" - case .m_deleteDownloadDataTask__id_id: return ".deleteDownloadDataTask(id:)" - case .m_saveDownloadDataTask__task: return ".saveDownloadDataTask(_:)" + case .m_updateTask__task_task: return ".updateTask(task:)" case .m_downloadDataTask__for_blockId: return ".downloadDataTask(for:)" case .m_getDownloadDataTasks: return ".getDownloadDataTasks()" case .m_getDownloadDataTasksForCourse__courseId: return ".getDownloadDataTasksForCourse(_:)" + case .m_deleteDownloadDataTasks__ids_ids: return ".deleteDownloadDataTasks(ids:)" } } } @@ -2771,9 +2730,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func loadAllOfflineProgress(willReturn: [OfflineProgress]...) -> MethodStub { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func nextBlockForDownloading(willReturn: DownloadDataTask?...) -> MethodStub { - return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func downloadDataTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } @@ -2804,13 +2760,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func nextBlockForDownloading(willProduce: (Stubber) -> Void) -> MethodStub { - let willReturn: [DownloadDataTask?] = [] - let given: Given = { return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (DownloadDataTask?).self) - willProduce(stubber) - return given - } public static func downloadDataTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [DownloadDataTask?] = [] let given: Given = { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -2844,16 +2793,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func deleteDownloadDataTask(id: Parameter, willThrow: Error...) -> MethodStub { - return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) - } - public static func deleteDownloadDataTask(id: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given - } } public struct Verify { @@ -2870,13 +2809,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func deleteProgress(for blockID: Parameter) -> Verify { return Verify(method: .m_deleteProgress__for_blockID(`blockID`))} public static func deleteAllProgress() -> Verify { return Verify(method: .m_deleteAllProgress)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`))} - public static func nextBlockForDownloading() -> Verify { return Verify(method: .m_nextBlockForDownloading)} - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter) -> Verify { return Verify(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`))} - public static func deleteDownloadDataTask(id: Parameter) -> Verify { return Verify(method: .m_deleteDownloadDataTask__id_id(`id`))} - public static func saveDownloadDataTask(_ task: Parameter) -> Verify { return Verify(method: .m_saveDownloadDataTask__task(`task`))} + public static func updateTask(task: Parameter) -> Verify { return Verify(method: .m_updateTask__task_task(`task`))} public static func downloadDataTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadDataTask__for_blockId(`blockId`))} public static func getDownloadDataTasks() -> Verify { return Verify(method: .m_getDownloadDataTasks)} public static func getDownloadDataTasksForCourse(_ courseId: Parameter) -> Verify { return Verify(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`))} + public static func deleteDownloadDataTasks(ids: Parameter<[String]>) -> Verify { return Verify(method: .m_deleteDownloadDataTasks__ids_ids(`ids`))} } public struct Perform { @@ -2914,17 +2851,8 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter, perform: @escaping ([CourseBlock], DownloadQuality) -> Void) -> Perform { return Perform(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`), performs: perform) } - public static func nextBlockForDownloading(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_nextBlockForDownloading, performs: perform) - } - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter, perform: @escaping (String, DownloadState, Data?) -> Void) -> Perform { - return Perform(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`), performs: perform) - } - public static func deleteDownloadDataTask(id: Parameter, perform: @escaping (String) -> Void) -> Perform { - return Perform(method: .m_deleteDownloadDataTask__id_id(`id`), performs: perform) - } - public static func saveDownloadDataTask(_ task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { - return Perform(method: .m_saveDownloadDataTask__task(`task`), performs: perform) + public static func updateTask(task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { + return Perform(method: .m_updateTask__task_task(`task`), performs: perform) } public static func downloadDataTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_downloadDataTask__for_blockId(`blockId`), performs: perform) @@ -2935,6 +2863,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getDownloadDataTasksForCourse(_ courseId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), performs: perform) } + public static func deleteDownloadDataTasks(ids: Parameter<[String]>, perform: @escaping ([String]) -> Void) -> Perform { + return Perform(method: .m_deleteDownloadDataTasks__ids_ids(`ids`), performs: perform) + } } public func given(_ method: Given) { @@ -4156,7 +4087,7 @@ open class DiscoveryInteractorProtocolMock: DiscoveryInteractorProtocol, Mock { } // MARK: - DownloadManagerProtocol -@MainActor + open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public init(sequencing sequencingPolicy: SequencingPolicy = .lastWrittenResolvedFirst, stubbing stubbingPolicy: StubbingPolicy = .wrap, file: StaticString = #file, line: UInt = #line) { SwiftyMockyTestObserver.setup() @@ -4195,27 +4126,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { if scopes.contains(.perform) { methodPerformValues = [] } } - public var currentDownloadTask: DownloadDataTask? { - get { invocations.append(.p_currentDownloadTask_get); return __p_currentDownloadTask ?? optionalGivenGetterValue(.p_currentDownloadTask_get, "DownloadManagerProtocolMock - stub value for currentDownloadTask was not defined") } - } - private var __p_currentDownloadTask: (DownloadDataTask)? - - open func publisher() throws -> AnyPublisher { - addInvocation(.m_publisher) - let perform = methodPerformValue(.m_publisher) as? () -> Void + open func getCurrentDownloadTask() -> DownloadDataTask? { + addInvocation(.m_getCurrentDownloadTask) + let perform = methodPerformValue(.m_getCurrentDownloadTask) as? () -> Void perform?() - var __value: AnyPublisher + var __value: DownloadDataTask? = nil do { - __value = try methodReturnValue(.m_publisher).casted() - } catch MockError.notStubed { - onFatalFailure("Stub return value not specified for publisher(). Use given") - Failure("Stub return value not specified for publisher(). Use given") + __value = try methodReturnValue(.m_getCurrentDownloadTask).casted() } catch { - throw error + // do nothing } return __value } @@ -4327,15 +4250,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { } } - open func deleteFile(blocks: [CourseBlock]) { - addInvocation(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - } - - open func deleteAllFiles() { - addInvocation(.m_deleteAllFiles) - let perform = methodPerformValue(.m_deleteAllFiles) as? () -> Void + open func deleteAll() { + addInvocation(.m_deleteAll) + let perform = methodPerformValue(.m_deleteAll) as? () -> Void perform?() } @@ -4352,20 +4269,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return __value } - open func updateUnzippedFileSize(for sequentials: [CourseSequential]) -> [CourseSequential] { - addInvocation(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) - let perform = methodPerformValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) as? ([CourseSequential]) -> Void - perform?(`sequentials`) - var __value: [CourseSequential] - do { - __value = try methodReturnValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))).casted() - } catch { - onFatalFailure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - Failure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - } - return __value - } - open func resumeDownloading() throws { addInvocation(.m_resumeDownloading) let perform = methodPerformValue(.m_resumeDownloading) as? () -> Void @@ -4399,9 +4302,28 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { perform?() } + open func delete(blocks: [CourseBlock], courseId: String) { + addInvocation(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) + let perform = methodPerformValue(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) as? ([CourseBlock], String) -> Void + perform?(`blocks`, `courseId`) + } + + open func downloadTask(for blockId: String) -> DownloadDataTask? { + addInvocation(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) + let perform = methodPerformValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value + } + fileprivate enum MethodType { - case m_publisher + case m_getCurrentDownloadTask case m_eventPublisher case m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>) case m_getDownloadTasks @@ -4410,18 +4332,17 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case m_cancelDownloading__task_task(Parameter) case m_cancelDownloading__courseId_courseId(Parameter) case m_cancelAllDownloading - case m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>) - case m_deleteAllFiles + case m_deleteAll case m_fileUrl__for_blockId(Parameter) - case m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>) case m_resumeDownloading case m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>) case m_removeAppSupportDirectoryUnusedContent - case p_currentDownloadTask_get + case m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>, Parameter) + case m_downloadTask__for_blockId(Parameter) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { - case (.m_publisher, .m_publisher): return .match + case (.m_getCurrentDownloadTask, .m_getCurrentDownloadTask): return .match case (.m_eventPublisher, .m_eventPublisher): return .match @@ -4455,23 +4376,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case (.m_cancelAllDownloading, .m_cancelAllDownloading): return .match - case (.m_deleteFile__blocks_blocks(let lhsBlocks), .m_deleteFile__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) - - case (.m_deleteAllFiles, .m_deleteAllFiles): return .match + case (.m_deleteAll, .m_deleteAll): return .match case (.m_fileUrl__for_blockId(let lhsBlockid), .m_fileUrl__for_blockId(let rhsBlockid)): var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) return Matcher.ComparisonResult(results) - case (.m_updateUnzippedFileSize__for_sequentials(let lhsSequentials), .m_updateUnzippedFileSize__for_sequentials(let rhsSequentials)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequentials, rhs: rhsSequentials, with: matcher), lhsSequentials, rhsSequentials, "for sequentials")) - return Matcher.ComparisonResult(results) - case (.m_resumeDownloading, .m_resumeDownloading): return .match case (.m_isLargeVideosSize__blocks_blocks(let lhsBlocks), .m_isLargeVideosSize__blocks_blocks(let rhsBlocks)): @@ -4480,14 +4391,24 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return Matcher.ComparisonResult(results) case (.m_removeAppSupportDirectoryUnusedContent, .m_removeAppSupportDirectoryUnusedContent): return .match - case (.p_currentDownloadTask_get,.p_currentDownloadTask_get): return Matcher.ComparisonResult.match + + case (.m_delete__blocks_blockscourseId_courseId(let lhsBlocks, let lhsCourseid), .m_delete__blocks_blockscourseId_courseId(let rhsBlocks, let rhsCourseid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + return Matcher.ComparisonResult(results) + + case (.m_downloadTask__for_blockId(let lhsBlockid), .m_downloadTask__for_blockId(let rhsBlockid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) default: return .none } } func intValue() -> Int { switch self { - case .m_publisher: return 0 + case .m_getCurrentDownloadTask: return 0 case .m_eventPublisher: return 0 case let .m_addToDownloadQueue__blocks_blocks(p0): return p0.intValue case .m_getDownloadTasks: return 0 @@ -4496,19 +4417,18 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case let .m_cancelDownloading__task_task(p0): return p0.intValue case let .m_cancelDownloading__courseId_courseId(p0): return p0.intValue case .m_cancelAllDownloading: return 0 - case let .m_deleteFile__blocks_blocks(p0): return p0.intValue - case .m_deleteAllFiles: return 0 + case .m_deleteAll: return 0 case let .m_fileUrl__for_blockId(p0): return p0.intValue - case let .m_updateUnzippedFileSize__for_sequentials(p0): return p0.intValue case .m_resumeDownloading: return 0 case let .m_isLargeVideosSize__blocks_blocks(p0): return p0.intValue case .m_removeAppSupportDirectoryUnusedContent: return 0 - case .p_currentDownloadTask_get: return 0 + case let .m_delete__blocks_blockscourseId_courseId(p0, p1): return p0.intValue + p1.intValue + case let .m_downloadTask__for_blockId(p0): return p0.intValue } } func assertionName() -> String { switch self { - case .m_publisher: return ".publisher()" + case .m_getCurrentDownloadTask: return ".getCurrentDownloadTask()" case .m_eventPublisher: return ".eventPublisher()" case .m_addToDownloadQueue__blocks_blocks: return ".addToDownloadQueue(blocks:)" case .m_getDownloadTasks: return ".getDownloadTasks()" @@ -4517,14 +4437,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case .m_cancelDownloading__task_task: return ".cancelDownloading(task:)" case .m_cancelDownloading__courseId_courseId: return ".cancelDownloading(courseId:)" case .m_cancelAllDownloading: return ".cancelAllDownloading()" - case .m_deleteFile__blocks_blocks: return ".deleteFile(blocks:)" - case .m_deleteAllFiles: return ".deleteAllFiles()" + case .m_deleteAll: return ".deleteAll()" case .m_fileUrl__for_blockId: return ".fileUrl(for:)" - case .m_updateUnzippedFileSize__for_sequentials: return ".updateUnzippedFileSize(for:)" case .m_resumeDownloading: return ".resumeDownloading()" case .m_isLargeVideosSize__blocks_blocks: return ".isLargeVideosSize(blocks:)" case .m_removeAppSupportDirectoryUnusedContent: return ".removeAppSupportDirectoryUnusedContent()" - case .p_currentDownloadTask_get: return "[get] .currentDownloadTask" + case .m_delete__blocks_blockscourseId_courseId: return ".delete(blocks:courseId:)" + case .m_downloadTask__for_blockId: return ".downloadTask(for:)" } } } @@ -4537,12 +4456,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { super.init(products) } - public static func currentDownloadTask(getter defaultValue: DownloadDataTask?...) -> PropertyStub { - return Given(method: .p_currentDownloadTask_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) - } - public static func publisher(willReturn: AnyPublisher...) -> MethodStub { - return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + public static func getCurrentDownloadTask(willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) } public static func eventPublisher(willReturn: AnyPublisher...) -> MethodStub { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) @@ -4556,12 +4472,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func fileUrl(for blockId: Parameter, willReturn: URL?...) -> MethodStub { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willReturn: [CourseSequential]...) -> MethodStub { - return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willReturn: Bool...) -> MethodStub { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) } + public static func downloadTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func getCurrentDownloadTask(willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given + } public static func eventPublisher(willProduce: (Stubber>) -> Void) -> MethodStub { let willReturn: [AnyPublisher] = [] let given: Given = { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -4590,13 +4513,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willProduce: (Stubber<[CourseSequential]>) -> Void) -> MethodStub { - let willReturn: [[CourseSequential]] = [] - let given: Given = { return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([CourseSequential]).self) - willProduce(stubber) - return given - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [Bool] = [] let given: Given = { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -4604,13 +4520,10 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func publisher(willThrow: Error...) -> MethodStub { - return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) - } - public static func publisher(willProduce: (StubberThrows>) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (AnyPublisher).self) + public static func downloadTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) willProduce(stubber) return given } @@ -4679,7 +4592,7 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public struct Verify { fileprivate var method: MethodType - public static func publisher() -> Verify { return Verify(method: .m_publisher)} + public static func getCurrentDownloadTask() -> Verify { return Verify(method: .m_getCurrentDownloadTask)} public static func eventPublisher() -> Verify { return Verify(method: .m_eventPublisher)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocks(`blocks`))} public static func getDownloadTasks() -> Verify { return Verify(method: .m_getDownloadTasks)} @@ -4688,22 +4601,21 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelDownloading(task: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__task_task(`task`))} public static func cancelDownloading(courseId: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__courseId_courseId(`courseId`))} public static func cancelAllDownloading() -> Verify { return Verify(method: .m_cancelAllDownloading)} - public static func deleteFile(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_deleteFile__blocks_blocks(`blocks`))} - public static func deleteAllFiles() -> Verify { return Verify(method: .m_deleteAllFiles)} + public static func deleteAll() -> Verify { return Verify(method: .m_deleteAll)} public static func fileUrl(for blockId: Parameter) -> Verify { return Verify(method: .m_fileUrl__for_blockId(`blockId`))} - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>) -> Verify { return Verify(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`))} public static func resumeDownloading() -> Verify { return Verify(method: .m_resumeDownloading)} public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_isLargeVideosSize__blocks_blocks(`blocks`))} public static func removeAppSupportDirectoryUnusedContent() -> Verify { return Verify(method: .m_removeAppSupportDirectoryUnusedContent)} - public static var currentDownloadTask: Verify { return Verify(method: .p_currentDownloadTask_get) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter) -> Verify { return Verify(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`))} + public static func downloadTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadTask__for_blockId(`blockId`))} } public struct Perform { fileprivate var method: MethodType var performs: Any - public static func publisher(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_publisher, performs: perform) + public static func getCurrentDownloadTask(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_getCurrentDownloadTask, performs: perform) } public static func eventPublisher(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_eventPublisher, performs: perform) @@ -4729,18 +4641,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelAllDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_cancelAllDownloading, performs: perform) } - public static func deleteFile(blocks: Parameter<[CourseBlock]>, perform: @escaping ([CourseBlock]) -> Void) -> Perform { - return Perform(method: .m_deleteFile__blocks_blocks(`blocks`), performs: perform) - } - public static func deleteAllFiles(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_deleteAllFiles, performs: perform) + public static func deleteAll(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_deleteAll, performs: perform) } public static func fileUrl(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_fileUrl__for_blockId(`blockId`), performs: perform) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, perform: @escaping ([CourseSequential]) -> Void) -> Perform { - return Perform(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), performs: perform) - } public static func resumeDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_resumeDownloading, performs: perform) } @@ -4750,6 +4656,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func removeAppSupportDirectoryUnusedContent(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_removeAppSupportDirectoryUnusedContent, performs: perform) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter, perform: @escaping ([CourseBlock], String) -> Void) -> Perform { + return Perform(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`), performs: perform) + } + public static func downloadTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { + return Perform(method: .m_downloadTask__for_blockId(`blockId`), performs: perform) + } } public func given(_ method: Given) { diff --git a/Discussion/DiscussionTests/DiscussionMock.generated.swift b/Discussion/DiscussionTests/DiscussionMock.generated.swift index 352630d17..c18c3cb41 100644 --- a/Discussion/DiscussionTests/DiscussionMock.generated.swift +++ b/Discussion/DiscussionTests/DiscussionMock.generated.swift @@ -2531,41 +2531,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { perform?(`blocks`, `downloadQuality`) } - open func nextBlockForDownloading() -> DownloadDataTask? { - addInvocation(.m_nextBlockForDownloading) - let perform = methodPerformValue(.m_nextBlockForDownloading) as? () -> Void - perform?() - var __value: DownloadDataTask? = nil - do { - __value = try methodReturnValue(.m_nextBlockForDownloading).casted() - } catch { - // do nothing - } - return __value - } - - open func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) { - addInvocation(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) - let perform = methodPerformValue(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) as? (String, DownloadState, Data?) -> Void - perform?(`id`, `state`, `resumeData`) - } - - open func deleteDownloadDataTask(id: String) throws { - addInvocation(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) - let perform = methodPerformValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) as? (String) -> Void - perform?(`id`) - do { - _ = try methodReturnValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } - } - - open func saveDownloadDataTask(_ task: DownloadDataTask) { - addInvocation(.m_saveDownloadDataTask__task(Parameter.value(`task`))) - let perform = methodPerformValue(.m_saveDownloadDataTask__task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + open func updateTask(task: DownloadDataTask) { + addInvocation(.m_updateTask__task_task(Parameter.value(`task`))) + let perform = methodPerformValue(.m_updateTask__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void perform?(`task`) } @@ -2610,6 +2578,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { return __value } + open func deleteDownloadDataTasks(ids: [String]) { + addInvocation(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) + let perform = methodPerformValue(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) as? ([String]) -> Void + perform?(`ids`) + } + fileprivate enum MethodType { case m_set__userId_userId(Parameter) @@ -2622,13 +2596,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case m_deleteProgress__for_blockID(Parameter) case m_deleteAllProgress case m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>, Parameter) - case m_nextBlockForDownloading - case m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter, Parameter, Parameter) - case m_deleteDownloadDataTask__id_id(Parameter) - case m_saveDownloadDataTask__task(Parameter) + case m_updateTask__task_task(Parameter) case m_downloadDataTask__for_blockId(Parameter) case m_getDownloadDataTasks case m_getDownloadDataTasksForCourse__courseId(Parameter) + case m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { @@ -2671,23 +2643,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsDownloadquality, rhs: rhsDownloadquality, with: matcher), lhsDownloadquality, rhsDownloadquality, "downloadQuality")) return Matcher.ComparisonResult(results) - case (.m_nextBlockForDownloading, .m_nextBlockForDownloading): return .match - - case (.m_updateDownloadState__id_idstate_stateresumeData_resumeData(let lhsId, let lhsState, let lhsResumedata), .m_updateDownloadState__id_idstate_stateresumeData_resumeData(let rhsId, let rhsState, let rhsResumedata)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsState, rhs: rhsState, with: matcher), lhsState, rhsState, "state")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsResumedata, rhs: rhsResumedata, with: matcher), lhsResumedata, rhsResumedata, "resumeData")) - return Matcher.ComparisonResult(results) - - case (.m_deleteDownloadDataTask__id_id(let lhsId), .m_deleteDownloadDataTask__id_id(let rhsId)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - return Matcher.ComparisonResult(results) - - case (.m_saveDownloadDataTask__task(let lhsTask), .m_saveDownloadDataTask__task(let rhsTask)): + case (.m_updateTask__task_task(let lhsTask), .m_updateTask__task_task(let rhsTask)): var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "_ task")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) return Matcher.ComparisonResult(results) case (.m_downloadDataTask__for_blockId(let lhsBlockid), .m_downloadDataTask__for_blockId(let rhsBlockid)): @@ -2701,6 +2659,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) return Matcher.ComparisonResult(results) + + case (.m_deleteDownloadDataTasks__ids_ids(let lhsIds), .m_deleteDownloadDataTasks__ids_ids(let rhsIds)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsIds, rhs: rhsIds, with: matcher), lhsIds, rhsIds, "ids")) + return Matcher.ComparisonResult(results) default: return .none } } @@ -2717,13 +2680,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case let .m_deleteProgress__for_blockID(p0): return p0.intValue case .m_deleteAllProgress: return 0 case let .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(p0, p1): return p0.intValue + p1.intValue - case .m_nextBlockForDownloading: return 0 - case let .m_updateDownloadState__id_idstate_stateresumeData_resumeData(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue - case let .m_deleteDownloadDataTask__id_id(p0): return p0.intValue - case let .m_saveDownloadDataTask__task(p0): return p0.intValue + case let .m_updateTask__task_task(p0): return p0.intValue case let .m_downloadDataTask__for_blockId(p0): return p0.intValue case .m_getDownloadDataTasks: return 0 case let .m_getDownloadDataTasksForCourse__courseId(p0): return p0.intValue + case let .m_deleteDownloadDataTasks__ids_ids(p0): return p0.intValue } } func assertionName() -> String { @@ -2738,13 +2699,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case .m_deleteProgress__for_blockID: return ".deleteProgress(for:)" case .m_deleteAllProgress: return ".deleteAllProgress()" case .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality: return ".addToDownloadQueue(blocks:downloadQuality:)" - case .m_nextBlockForDownloading: return ".nextBlockForDownloading()" - case .m_updateDownloadState__id_idstate_stateresumeData_resumeData: return ".updateDownloadState(id:state:resumeData:)" - case .m_deleteDownloadDataTask__id_id: return ".deleteDownloadDataTask(id:)" - case .m_saveDownloadDataTask__task: return ".saveDownloadDataTask(_:)" + case .m_updateTask__task_task: return ".updateTask(task:)" case .m_downloadDataTask__for_blockId: return ".downloadDataTask(for:)" case .m_getDownloadDataTasks: return ".getDownloadDataTasks()" case .m_getDownloadDataTasksForCourse__courseId: return ".getDownloadDataTasksForCourse(_:)" + case .m_deleteDownloadDataTasks__ids_ids: return ".deleteDownloadDataTasks(ids:)" } } } @@ -2771,9 +2730,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func loadAllOfflineProgress(willReturn: [OfflineProgress]...) -> MethodStub { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func nextBlockForDownloading(willReturn: DownloadDataTask?...) -> MethodStub { - return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func downloadDataTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } @@ -2804,13 +2760,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func nextBlockForDownloading(willProduce: (Stubber) -> Void) -> MethodStub { - let willReturn: [DownloadDataTask?] = [] - let given: Given = { return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (DownloadDataTask?).self) - willProduce(stubber) - return given - } public static func downloadDataTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [DownloadDataTask?] = [] let given: Given = { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -2844,16 +2793,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func deleteDownloadDataTask(id: Parameter, willThrow: Error...) -> MethodStub { - return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) - } - public static func deleteDownloadDataTask(id: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given - } } public struct Verify { @@ -2870,13 +2809,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func deleteProgress(for blockID: Parameter) -> Verify { return Verify(method: .m_deleteProgress__for_blockID(`blockID`))} public static func deleteAllProgress() -> Verify { return Verify(method: .m_deleteAllProgress)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`))} - public static func nextBlockForDownloading() -> Verify { return Verify(method: .m_nextBlockForDownloading)} - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter) -> Verify { return Verify(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`))} - public static func deleteDownloadDataTask(id: Parameter) -> Verify { return Verify(method: .m_deleteDownloadDataTask__id_id(`id`))} - public static func saveDownloadDataTask(_ task: Parameter) -> Verify { return Verify(method: .m_saveDownloadDataTask__task(`task`))} + public static func updateTask(task: Parameter) -> Verify { return Verify(method: .m_updateTask__task_task(`task`))} public static func downloadDataTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadDataTask__for_blockId(`blockId`))} public static func getDownloadDataTasks() -> Verify { return Verify(method: .m_getDownloadDataTasks)} public static func getDownloadDataTasksForCourse(_ courseId: Parameter) -> Verify { return Verify(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`))} + public static func deleteDownloadDataTasks(ids: Parameter<[String]>) -> Verify { return Verify(method: .m_deleteDownloadDataTasks__ids_ids(`ids`))} } public struct Perform { @@ -2914,17 +2851,8 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter, perform: @escaping ([CourseBlock], DownloadQuality) -> Void) -> Perform { return Perform(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`), performs: perform) } - public static func nextBlockForDownloading(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_nextBlockForDownloading, performs: perform) - } - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter, perform: @escaping (String, DownloadState, Data?) -> Void) -> Perform { - return Perform(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`), performs: perform) - } - public static func deleteDownloadDataTask(id: Parameter, perform: @escaping (String) -> Void) -> Perform { - return Perform(method: .m_deleteDownloadDataTask__id_id(`id`), performs: perform) - } - public static func saveDownloadDataTask(_ task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { - return Perform(method: .m_saveDownloadDataTask__task(`task`), performs: perform) + public static func updateTask(task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { + return Perform(method: .m_updateTask__task_task(`task`), performs: perform) } public static func downloadDataTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_downloadDataTask__for_blockId(`blockId`), performs: perform) @@ -2935,6 +2863,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getDownloadDataTasksForCourse(_ courseId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), performs: perform) } + public static func deleteDownloadDataTasks(ids: Parameter<[String]>, perform: @escaping ([String]) -> Void) -> Perform { + return Perform(method: .m_deleteDownloadDataTasks__ids_ids(`ids`), performs: perform) + } } public func given(_ method: Given) { @@ -5247,7 +5178,7 @@ open class DiscussionRouterMock: DiscussionRouter, Mock { } // MARK: - DownloadManagerProtocol -@MainActor + open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public init(sequencing sequencingPolicy: SequencingPolicy = .lastWrittenResolvedFirst, stubbing stubbingPolicy: StubbingPolicy = .wrap, file: StaticString = #file, line: UInt = #line) { SwiftyMockyTestObserver.setup() @@ -5286,27 +5217,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { if scopes.contains(.perform) { methodPerformValues = [] } } - public var currentDownloadTask: DownloadDataTask? { - get { invocations.append(.p_currentDownloadTask_get); return __p_currentDownloadTask ?? optionalGivenGetterValue(.p_currentDownloadTask_get, "DownloadManagerProtocolMock - stub value for currentDownloadTask was not defined") } - } - private var __p_currentDownloadTask: (DownloadDataTask)? - - open func publisher() throws -> AnyPublisher { - addInvocation(.m_publisher) - let perform = methodPerformValue(.m_publisher) as? () -> Void + open func getCurrentDownloadTask() -> DownloadDataTask? { + addInvocation(.m_getCurrentDownloadTask) + let perform = methodPerformValue(.m_getCurrentDownloadTask) as? () -> Void perform?() - var __value: AnyPublisher + var __value: DownloadDataTask? = nil do { - __value = try methodReturnValue(.m_publisher).casted() - } catch MockError.notStubed { - onFatalFailure("Stub return value not specified for publisher(). Use given") - Failure("Stub return value not specified for publisher(). Use given") + __value = try methodReturnValue(.m_getCurrentDownloadTask).casted() } catch { - throw error + // do nothing } return __value } @@ -5418,15 +5341,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { } } - open func deleteFile(blocks: [CourseBlock]) { - addInvocation(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - } - - open func deleteAllFiles() { - addInvocation(.m_deleteAllFiles) - let perform = methodPerformValue(.m_deleteAllFiles) as? () -> Void + open func deleteAll() { + addInvocation(.m_deleteAll) + let perform = methodPerformValue(.m_deleteAll) as? () -> Void perform?() } @@ -5443,20 +5360,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return __value } - open func updateUnzippedFileSize(for sequentials: [CourseSequential]) -> [CourseSequential] { - addInvocation(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) - let perform = methodPerformValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) as? ([CourseSequential]) -> Void - perform?(`sequentials`) - var __value: [CourseSequential] - do { - __value = try methodReturnValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))).casted() - } catch { - onFatalFailure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - Failure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - } - return __value - } - open func resumeDownloading() throws { addInvocation(.m_resumeDownloading) let perform = methodPerformValue(.m_resumeDownloading) as? () -> Void @@ -5490,9 +5393,28 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { perform?() } + open func delete(blocks: [CourseBlock], courseId: String) { + addInvocation(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) + let perform = methodPerformValue(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) as? ([CourseBlock], String) -> Void + perform?(`blocks`, `courseId`) + } + + open func downloadTask(for blockId: String) -> DownloadDataTask? { + addInvocation(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) + let perform = methodPerformValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value + } + fileprivate enum MethodType { - case m_publisher + case m_getCurrentDownloadTask case m_eventPublisher case m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>) case m_getDownloadTasks @@ -5501,18 +5423,17 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case m_cancelDownloading__task_task(Parameter) case m_cancelDownloading__courseId_courseId(Parameter) case m_cancelAllDownloading - case m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>) - case m_deleteAllFiles + case m_deleteAll case m_fileUrl__for_blockId(Parameter) - case m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>) case m_resumeDownloading case m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>) case m_removeAppSupportDirectoryUnusedContent - case p_currentDownloadTask_get + case m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>, Parameter) + case m_downloadTask__for_blockId(Parameter) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { - case (.m_publisher, .m_publisher): return .match + case (.m_getCurrentDownloadTask, .m_getCurrentDownloadTask): return .match case (.m_eventPublisher, .m_eventPublisher): return .match @@ -5546,23 +5467,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case (.m_cancelAllDownloading, .m_cancelAllDownloading): return .match - case (.m_deleteFile__blocks_blocks(let lhsBlocks), .m_deleteFile__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) - - case (.m_deleteAllFiles, .m_deleteAllFiles): return .match + case (.m_deleteAll, .m_deleteAll): return .match case (.m_fileUrl__for_blockId(let lhsBlockid), .m_fileUrl__for_blockId(let rhsBlockid)): var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) return Matcher.ComparisonResult(results) - case (.m_updateUnzippedFileSize__for_sequentials(let lhsSequentials), .m_updateUnzippedFileSize__for_sequentials(let rhsSequentials)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequentials, rhs: rhsSequentials, with: matcher), lhsSequentials, rhsSequentials, "for sequentials")) - return Matcher.ComparisonResult(results) - case (.m_resumeDownloading, .m_resumeDownloading): return .match case (.m_isLargeVideosSize__blocks_blocks(let lhsBlocks), .m_isLargeVideosSize__blocks_blocks(let rhsBlocks)): @@ -5571,14 +5482,24 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return Matcher.ComparisonResult(results) case (.m_removeAppSupportDirectoryUnusedContent, .m_removeAppSupportDirectoryUnusedContent): return .match - case (.p_currentDownloadTask_get,.p_currentDownloadTask_get): return Matcher.ComparisonResult.match + + case (.m_delete__blocks_blockscourseId_courseId(let lhsBlocks, let lhsCourseid), .m_delete__blocks_blockscourseId_courseId(let rhsBlocks, let rhsCourseid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + return Matcher.ComparisonResult(results) + + case (.m_downloadTask__for_blockId(let lhsBlockid), .m_downloadTask__for_blockId(let rhsBlockid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) default: return .none } } func intValue() -> Int { switch self { - case .m_publisher: return 0 + case .m_getCurrentDownloadTask: return 0 case .m_eventPublisher: return 0 case let .m_addToDownloadQueue__blocks_blocks(p0): return p0.intValue case .m_getDownloadTasks: return 0 @@ -5587,19 +5508,18 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case let .m_cancelDownloading__task_task(p0): return p0.intValue case let .m_cancelDownloading__courseId_courseId(p0): return p0.intValue case .m_cancelAllDownloading: return 0 - case let .m_deleteFile__blocks_blocks(p0): return p0.intValue - case .m_deleteAllFiles: return 0 + case .m_deleteAll: return 0 case let .m_fileUrl__for_blockId(p0): return p0.intValue - case let .m_updateUnzippedFileSize__for_sequentials(p0): return p0.intValue case .m_resumeDownloading: return 0 case let .m_isLargeVideosSize__blocks_blocks(p0): return p0.intValue case .m_removeAppSupportDirectoryUnusedContent: return 0 - case .p_currentDownloadTask_get: return 0 + case let .m_delete__blocks_blockscourseId_courseId(p0, p1): return p0.intValue + p1.intValue + case let .m_downloadTask__for_blockId(p0): return p0.intValue } } func assertionName() -> String { switch self { - case .m_publisher: return ".publisher()" + case .m_getCurrentDownloadTask: return ".getCurrentDownloadTask()" case .m_eventPublisher: return ".eventPublisher()" case .m_addToDownloadQueue__blocks_blocks: return ".addToDownloadQueue(blocks:)" case .m_getDownloadTasks: return ".getDownloadTasks()" @@ -5608,14 +5528,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case .m_cancelDownloading__task_task: return ".cancelDownloading(task:)" case .m_cancelDownloading__courseId_courseId: return ".cancelDownloading(courseId:)" case .m_cancelAllDownloading: return ".cancelAllDownloading()" - case .m_deleteFile__blocks_blocks: return ".deleteFile(blocks:)" - case .m_deleteAllFiles: return ".deleteAllFiles()" + case .m_deleteAll: return ".deleteAll()" case .m_fileUrl__for_blockId: return ".fileUrl(for:)" - case .m_updateUnzippedFileSize__for_sequentials: return ".updateUnzippedFileSize(for:)" case .m_resumeDownloading: return ".resumeDownloading()" case .m_isLargeVideosSize__blocks_blocks: return ".isLargeVideosSize(blocks:)" case .m_removeAppSupportDirectoryUnusedContent: return ".removeAppSupportDirectoryUnusedContent()" - case .p_currentDownloadTask_get: return "[get] .currentDownloadTask" + case .m_delete__blocks_blockscourseId_courseId: return ".delete(blocks:courseId:)" + case .m_downloadTask__for_blockId: return ".downloadTask(for:)" } } } @@ -5628,12 +5547,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { super.init(products) } - public static func currentDownloadTask(getter defaultValue: DownloadDataTask?...) -> PropertyStub { - return Given(method: .p_currentDownloadTask_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) - } - public static func publisher(willReturn: AnyPublisher...) -> MethodStub { - return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + public static func getCurrentDownloadTask(willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) } public static func eventPublisher(willReturn: AnyPublisher...) -> MethodStub { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) @@ -5647,12 +5563,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func fileUrl(for blockId: Parameter, willReturn: URL?...) -> MethodStub { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willReturn: [CourseSequential]...) -> MethodStub { - return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willReturn: Bool...) -> MethodStub { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) } + public static func downloadTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func getCurrentDownloadTask(willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given + } public static func eventPublisher(willProduce: (Stubber>) -> Void) -> MethodStub { let willReturn: [AnyPublisher] = [] let given: Given = { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -5681,13 +5604,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willProduce: (Stubber<[CourseSequential]>) -> Void) -> MethodStub { - let willReturn: [[CourseSequential]] = [] - let given: Given = { return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([CourseSequential]).self) - willProduce(stubber) - return given - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [Bool] = [] let given: Given = { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -5695,13 +5611,10 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func publisher(willThrow: Error...) -> MethodStub { - return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) - } - public static func publisher(willProduce: (StubberThrows>) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (AnyPublisher).self) + public static func downloadTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) willProduce(stubber) return given } @@ -5770,7 +5683,7 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public struct Verify { fileprivate var method: MethodType - public static func publisher() -> Verify { return Verify(method: .m_publisher)} + public static func getCurrentDownloadTask() -> Verify { return Verify(method: .m_getCurrentDownloadTask)} public static func eventPublisher() -> Verify { return Verify(method: .m_eventPublisher)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocks(`blocks`))} public static func getDownloadTasks() -> Verify { return Verify(method: .m_getDownloadTasks)} @@ -5779,22 +5692,21 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelDownloading(task: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__task_task(`task`))} public static func cancelDownloading(courseId: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__courseId_courseId(`courseId`))} public static func cancelAllDownloading() -> Verify { return Verify(method: .m_cancelAllDownloading)} - public static func deleteFile(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_deleteFile__blocks_blocks(`blocks`))} - public static func deleteAllFiles() -> Verify { return Verify(method: .m_deleteAllFiles)} + public static func deleteAll() -> Verify { return Verify(method: .m_deleteAll)} public static func fileUrl(for blockId: Parameter) -> Verify { return Verify(method: .m_fileUrl__for_blockId(`blockId`))} - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>) -> Verify { return Verify(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`))} public static func resumeDownloading() -> Verify { return Verify(method: .m_resumeDownloading)} public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_isLargeVideosSize__blocks_blocks(`blocks`))} public static func removeAppSupportDirectoryUnusedContent() -> Verify { return Verify(method: .m_removeAppSupportDirectoryUnusedContent)} - public static var currentDownloadTask: Verify { return Verify(method: .p_currentDownloadTask_get) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter) -> Verify { return Verify(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`))} + public static func downloadTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadTask__for_blockId(`blockId`))} } public struct Perform { fileprivate var method: MethodType var performs: Any - public static func publisher(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_publisher, performs: perform) + public static func getCurrentDownloadTask(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_getCurrentDownloadTask, performs: perform) } public static func eventPublisher(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_eventPublisher, performs: perform) @@ -5820,18 +5732,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelAllDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_cancelAllDownloading, performs: perform) } - public static func deleteFile(blocks: Parameter<[CourseBlock]>, perform: @escaping ([CourseBlock]) -> Void) -> Perform { - return Perform(method: .m_deleteFile__blocks_blocks(`blocks`), performs: perform) - } - public static func deleteAllFiles(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_deleteAllFiles, performs: perform) + public static func deleteAll(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_deleteAll, performs: perform) } public static func fileUrl(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_fileUrl__for_blockId(`blockId`), performs: perform) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, perform: @escaping ([CourseSequential]) -> Void) -> Perform { - return Perform(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), performs: perform) - } public static func resumeDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_resumeDownloading, performs: perform) } @@ -5841,6 +5747,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func removeAppSupportDirectoryUnusedContent(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_removeAppSupportDirectoryUnusedContent, performs: perform) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter, perform: @escaping ([CourseBlock], String) -> Void) -> Perform { + return Perform(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`), performs: perform) + } + public static func downloadTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { + return Perform(method: .m_downloadTask__for_blockId(`blockId`), performs: perform) + } } public func given(_ method: Given) { diff --git a/OpenEdX/AppDelegate.swift b/OpenEdX/AppDelegate.swift index 16cb8e7d1..96d1c6105 100644 --- a/OpenEdX/AppDelegate.swift +++ b/OpenEdX/AppDelegate.swift @@ -176,7 +176,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Task { await Container.shared.resolve(CorePersistenceProtocol.self)?.deleteAllProgress() - await Container.shared.resolve(DownloadManagerProtocol.self)?.deleteAllFiles() + await Container.shared.resolve(DownloadManagerProtocol.self)?.deleteAll() await Container.shared.resolve(CoreDataHandlerProtocol.self)?.clear() } window?.rootViewController = RouteController() diff --git a/OpenEdX/DI/ScreenAssembly.swift b/OpenEdX/DI/ScreenAssembly.swift index 1e642facb..a760627b7 100644 --- a/OpenEdX/DI/ScreenAssembly.swift +++ b/OpenEdX/DI/ScreenAssembly.swift @@ -351,16 +351,20 @@ class ScreenAssembly: Assembly { enrollmentEnd: enrollEnd, lastVisitedBlockID: lastVisitedBlockID, coreAnalytics: r.resolve(CoreAnalytics.self)!, - selection: selection + selection: selection, + courseHelper: r.resolve(CourseDownloadHelperProtocol.self)! ) } - + container.register( + CourseDownloadHelperProtocol.self + ) { @MainActor r in + CourseDownloadHelper(courseStructure: nil, manager: r.resolve(DownloadManagerProtocol.self)!) + } container.register(CourseVerticalViewModel.self) { @MainActor r, chapters, chapterIndex, sequentialIndex in CourseVerticalViewModel( chapters: chapters, chapterIndex: chapterIndex, sequentialIndex: sequentialIndex, - manager: r.resolve(DownloadManagerProtocol.self)!, router: r.resolve(CourseRouter.self)!, analytics: r.resolve(CourseAnalytics.self)!, connectivity: r.resolve(ConnectivityProtocol.self)! diff --git a/OpenEdX/Data/CorePersistence.swift b/OpenEdX/Data/CorePersistence.swift index d77ab2a7e..9d89525fd 100644 --- a/OpenEdX/Data/CorePersistence.swift +++ b/OpenEdX/Data/CorePersistence.swift @@ -61,9 +61,11 @@ public final class CorePersistence: CorePersistenceProtocol { private nonisolated(unsafe) var userId: Int? private let container: NSPersistentContainer + private let context: NSManagedObjectContext public init(container: NSPersistentContainer) { self.container = container + self.context = container.newBackgroundContext() } public func set(userId: Int) { @@ -79,101 +81,108 @@ public final class CorePersistence: CorePersistenceProtocol { public func addToDownloadQueue( blocks: [CourseBlock], downloadQuality: DownloadQuality - ) async { + ) { let userId = getUserId32() ?? 0 - await container.performBackgroundTask { context in - for block in blocks { - let downloadDataId = self.downloadDataId(from: block.id) - let data = try? CorePersistenceHelper.fetchCDDownloadData( - predicate: CDPredicate.id(downloadDataId), - context: context, - userId: userId - ) - guard data?.first == nil else { return } - - var fileExtension: String? - var url: String? - var fileSize: Int32? - var fileName: String? - - if let html = block.offlineDownload { - let fileUrl = html.fileUrl - url = fileUrl - fileSize = Int32(html.fileSize) - fileExtension = URL(string: fileUrl)?.pathExtension - if let folderName = URL(string: fileUrl)?.lastPathComponent, - let folderUrl = URL(string: folderName)?.deletingPathExtension() { - fileName = folderUrl.absoluteString - } - saveDownloadData(context) - } else if let encodedVideo = block.encodedVideo, - let video = encodedVideo.video(downloadQuality: downloadQuality), - let videoUrl = video.url { - url = videoUrl - if let videoFileSize = video.fileSize { - fileSize = Int32(videoFileSize) - } - fileExtension = URL(string: videoUrl)?.pathExtension - fileName = "\(block.id).\(fileExtension ?? "")" - saveDownloadData(context) - } else { return } - - func saveDownloadData(_ context: NSManagedObjectContext) { - let newDownloadData = CDDownloadData(context: context) - context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump - newDownloadData.id = downloadDataId - newDownloadData.blockId = block.id - newDownloadData.userId = userId - newDownloadData.courseId = block.courseId - newDownloadData.url = url - newDownloadData.fileName = fileName - newDownloadData.displayName = block.displayName - if let lastModified = block.offlineDownload?.lastModified { - newDownloadData.lastModified = lastModified - } - newDownloadData.progress = .zero - newDownloadData.resumeData = nil - newDownloadData.state = DownloadState.waiting.rawValue - newDownloadData.type = block.offlineDownload != nil - ? DownloadType.html.rawValue - : DownloadType.video.rawValue - newDownloadData.fileSize = Int32(fileSize ?? 0) + let objects: [[String: Any]] = blocks.compactMap { block -> [String: Any]? in + let downloadDataId = downloadDataId(from: block.id) + var fileExtension: String? + let url: String + var fileSize: Int32? + var fileName: String? + + if let html = block.offlineDownload { + let fileUrl = html.fileUrl + url = fileUrl + fileSize = Int32(html.fileSize) + fileExtension = URL(string: fileUrl)?.pathExtension + if let folderName = URL(string: fileUrl)?.lastPathComponent, + let folderUrl = URL(string: folderName)?.deletingPathExtension() { + fileName = folderUrl.absoluteString } - } + } else if let encodedVideo = block.encodedVideo, + let video = encodedVideo.video(downloadQuality: downloadQuality), + let videoUrl = video.url { + url = videoUrl + if let videoFileSize = video.fileSize { + fileSize = Int32(videoFileSize) + } + fileExtension = URL(string: videoUrl)?.pathExtension + fileName = "\(block.id).\(fileExtension ?? "")" + } else { return nil } + var dictionary = [ + "id": downloadDataId, + "blockId": block.id, + "userId": userId, + "courseId": block.courseId, + "url": url, + "fileName": fileName ?? "", + "displayName": block.displayName, + "progress": Double.zero, + "state": DownloadState.waiting.rawValue, + "type": block.offlineDownload != nil ? DownloadType.html.rawValue : DownloadType.video.rawValue, + "fileSize": fileSize ?? 0, + "actualSize": 0 + ] + if let lastModified = block.offlineDownload?.lastModified { + dictionary["lastModified"] = lastModified + } + return dictionary + } + + insertDownloadData(objects: objects) + } + + public func addToDownloadQueue(tasks: [DownloadDataTask]) { + let objects: [[String: Any]] = tasks.map { task in + [ + "id": downloadDataId(from: task.id), + "blockId": task.blockId, + "userId": task.userId, + "courseId": task.courseId, + "url": task.url, + "fileName": task.fileName, + "displayName": task.displayName, + "progress": task.progress, + "state": task.state, + "type": task.type, + "fileSize": task.fileSize, + "actualSize": task.actualSize + ] + } + insertDownloadData(objects: objects) + } + + func insertDownloadData(objects: [[String: Any]]) { + let batchInsertRequest = NSBatchInsertRequest(entityName: "CDDownloadData", objects: objects) + batchInsertRequest.resultType = .objectIDs + context.perform { [context] in do { - try context.save() + let batchInsertResult = try context.execute(batchInsertRequest) as? NSBatchInsertResult + if let objectIDs = batchInsertResult?.result as? [NSManagedObjectID] { + NSManagedObjectContext.mergeChanges( + fromRemoteContextSave: [NSInsertedObjectsKey: objectIDs], + into: [context] + ) + } } catch { debugLog("⛔️⛔️⛔️⛔️⛔️", error) } } } - - public func addToDownloadQueue(tasks: [DownloadDataTask]) async { - await container.performBackgroundTask { context in - for task in tasks { - let newDownloadData = CDDownloadData(context: context) - context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump - newDownloadData.id = task.id - newDownloadData.blockId = task.blockId - newDownloadData.userId = Int32(task.userId) - newDownloadData.courseId = task.courseId - newDownloadData.url = task.url - newDownloadData.fileName = task.fileName - newDownloadData.displayName = task.displayName - newDownloadData.lastModified = task.lastModified - newDownloadData.progress = .zero - newDownloadData.resumeData = nil - newDownloadData.state = DownloadState.waiting.rawValue - newDownloadData.type = task.type.rawValue - newDownloadData.fileSize = Int32(task.fileSize) + + private func perform(block: @escaping () -> T) async -> T { + await withCheckedContinuation { continuation in + context.perform { + let result = block() + continuation.resume(returning: result) } } } public func getDownloadDataTasks() async -> [DownloadDataTask] { let userId = getUserId32() ?? 0 - return await container.performBackgroundTask { context in + return await perform {[context] in guard let data = try? CorePersistenceHelper.fetchCDDownloadData( context: context, userId: userId @@ -192,7 +201,7 @@ public final class CorePersistence: CorePersistenceProtocol { ) async -> [DownloadDataTask] { let uID = userId let int32Id = getUserId32() - return await container.performBackgroundTask { context in + return await perform { [context] in guard let data = try? CorePersistenceHelper.fetchCDDownloadData( predicate: .courseId(courseId), context: context, @@ -216,7 +225,7 @@ public final class CorePersistence: CorePersistenceProtocol { public func downloadDataTask(for blockId: String) async -> DownloadDataTask? { let dataId = downloadDataId(from: blockId) let userId = getUserId32() - return await container.performBackgroundTask { context in + return await perform { [context] in let data = try? CorePersistenceHelper.fetchCDDownloadData( predicate: .id(dataId), context: context, @@ -231,32 +240,11 @@ public final class CorePersistence: CorePersistenceProtocol { } } - public func nextBlockForDownloading() async -> DownloadDataTask? { + public func updateTask(task: DownloadDataTask) { + let dataId = downloadDataId(from: task.id) let userId = getUserId32() - return await container.performBackgroundTask { context in - let data = try? CorePersistenceHelper.fetchCDDownloadData( - predicate: .state(DownloadState.finished.rawValue), - fetchLimit: 1, - context: context, - userId: userId - ) - - guard let downloadData = data?.first else { - return nil - } - - return DownloadDataTask(sourse: downloadData) - } - } - public func updateDownloadState( - id: String, - state: DownloadState, - resumeData: Data? - ) async { - let dataId = downloadDataId(from: id) - let userId = getUserId32() - return await container.performBackgroundTask { context in + context.perform { [context] in guard let data = try? CorePersistenceHelper.fetchCDDownloadData( predicate: .id(dataId), context: context, @@ -265,11 +253,18 @@ public final class CorePersistence: CorePersistenceProtocol { return } - guard let task = data.first else { return } + guard let dataTask = data.first else { return } - task.state = state.rawValue - if state == .finished { task.progress = 1 } - task.resumeData = resumeData + dataTask.state = task.state.rawValue + dataTask.resumeData = task.resumeData + dataTask.url = task.url + dataTask.fileName = task.fileName + dataTask.progress = task.progress + dataTask.type = task.type.rawValue + dataTask.fileSize = Int64(task.fileSize) + dataTask.actualSize = Int64(task.actualSize) + + if task.state == .finished { dataTask.progress = 1 } do { try context.save() @@ -278,51 +273,25 @@ public final class CorePersistence: CorePersistenceProtocol { } } } - - public func deleteDownloadDataTask(id: String) async throws { - let dataId = downloadDataId(from: id) - let userId = getUserId32() - await container.performBackgroundTask { context in + public func deleteDownloadDataTasks(ids: [String]) { + context.perform { [context] in + let request: NSFetchRequest = CDDownloadData.fetchRequest() + request.predicate = NSPredicate(format: "id IN %@", ids) + let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: request) + batchDeleteRequest.resultType = .resultTypeObjectIDs + do { - let records = try CorePersistenceHelper.fetchCDDownloadData( - predicate: .id(dataId), - context: context, - userId: userId - ) - - for record in records { - context.delete(record) - try context.save() - debugLog("File erased successfully") + let deleteResult = try context.execute(batchDeleteRequest) as? NSBatchDeleteResult + + if let objectIDs = deleteResult?.result as? [NSManagedObjectID] { + NSManagedObjectContext.mergeChanges( + fromRemoteContextSave: [NSDeletedObjectsKey: objectIDs], + into: [context] + ) } - - } catch { - debugLog("Error fetching records: \(error.localizedDescription)") - } - } - } - - public func saveDownloadDataTask(_ task: DownloadDataTask) async { - await container.performBackgroundTask { context in - let newDownloadData = CDDownloadData(context: context) - context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump - newDownloadData.id = task.id - newDownloadData.blockId = task.blockId - newDownloadData.userId = Int32(task.userId) - newDownloadData.courseId = task.courseId - newDownloadData.url = task.url - newDownloadData.progress = task.progress - newDownloadData.fileName = task.fileName - newDownloadData.displayName = task.displayName - newDownloadData.resumeData = task.resumeData - newDownloadData.state = task.state.rawValue - newDownloadData.type = task.type.rawValue - newDownloadData.fileSize = Int32(task.fileSize) - - do { - try context.save() + debugLog("Tasks erased successfully") } catch { - debugLog("⛔️⛔️⛔️⛔️⛔️", error) + debugLog("Error deleting tasks: \(error.localizedDescription)") } } } @@ -352,7 +321,7 @@ public final class CorePersistence: CorePersistenceProtocol { // MARK: - Offline Progress public func saveOfflineProgress(progress: OfflineProgress) async { - await container.performBackgroundTask { context in + await perform { [context] in let progressForSaving = CDOfflineProgress(context: context) context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump progressForSaving.blockID = progress.blockID @@ -367,7 +336,7 @@ public final class CorePersistence: CorePersistenceProtocol { } public func loadProgress(for blockID: String) async -> OfflineProgress? { - return await container.performBackgroundTask { context in + await perform { [context] in let request = CDOfflineProgress.fetchRequest() request.predicate = NSPredicate(format: "blockID = %@", blockID) guard let progress = try? context.fetch(request).first, @@ -382,7 +351,7 @@ public final class CorePersistence: CorePersistenceProtocol { } public func loadAllOfflineProgress() async -> [OfflineProgress] { - return await container.performBackgroundTask { context in + await perform { [context] in let result = try? context.fetch(CDOfflineProgress.fetchRequest()) .map { OfflineProgress( @@ -397,7 +366,7 @@ public final class CorePersistence: CorePersistenceProtocol { } public func deleteProgress(for blockID: String) async { - return await container.performBackgroundTask { context in + await perform { [context] in let request = CDOfflineProgress.fetchRequest() request.predicate = NSPredicate(format: "blockID = %@", blockID) guard let progress = try? context.fetch(request).first else { return } @@ -413,7 +382,7 @@ public final class CorePersistence: CorePersistenceProtocol { } public func deleteAllProgress() async { - return await container.performBackgroundTask { context in + await perform { [context] in let request = CDOfflineProgress.fetchRequest() guard let allProgress = try? context.fetch(request) else { return } @@ -442,7 +411,7 @@ public final class CorePersistence: CorePersistenceProtocol { guard let userId else { return id } - if id.contains(String(userId)) { + if id.contains(String("\(userId)_")) { return id } return "\(userId)_\(id)" diff --git a/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift b/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift index 05f1ddc5f..2921fe8f8 100644 --- a/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift +++ b/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift @@ -736,6 +736,22 @@ class AnalyticsManager: AuthorizationAnalytics, logEvent(.bulkDownloadVideosSubsection, parameters: parameters) } + public func bulkDownloadVideosSection( + courseID: String, + sectionID: String, + videos: Int + ) { + let parameters: [String: Any] = [ + EventParamKey.courseID: courseID, + EventParamKey.courseSection: sectionID, + EventParamKey.noOfVideos: videos, + EventParamKey.category: EventCategory.video, + EventParamKey.name: EventBIValue.bulkDownloadVideosSection.rawValue + ] + + logEvent(.bulkDownloadVideosSection, parameters: parameters) + } + public func bulkDeleteVideosSubsection( courseID: String, subSectionID: String, @@ -751,7 +767,21 @@ class AnalyticsManager: AuthorizationAnalytics, logEvent(.bulkDeleteVideosSubsection, parameters: parameters) } - + public func bulkDeleteVideosSection( + courseID: String, + sectionId: String, + videos: Int + ) { + let parameters: [String: Any] = [ + EventParamKey.courseID: courseID, + EventParamKey.courseSection: sectionId, + EventParamKey.noOfVideos: videos, + EventParamKey.category: EventCategory.video, + EventParamKey.name: EventBIValue.bulkDeleteVideosSection.rawValue + ] + + logEvent(.bulkDeleteVideosSection, parameters: parameters) + } public func videoLoaded(courseID: String, blockID: String, videoURL: String) { let parameters: [String: Any] = [ EventParamKey.courseID: courseID, diff --git a/OpenEdX/Router.swift b/OpenEdX/Router.swift index fb23efbba..0f9617aa9 100644 --- a/OpenEdX/Router.swift +++ b/OpenEdX/Router.swift @@ -587,20 +587,6 @@ public class Router: AuthorizationRouter, ) } - public func showDownloads( - downloads: [DownloadDataTask], - manager: DownloadManagerProtocol - ) { - let downloadsView = DownloadsView( - isSheet: false, - router: Container.shared.resolve(CourseRouter.self)!, - downloads: downloads, - manager: manager - ) - let controller = UIHostingController(rootView: downloadsView) - navigationController.pushViewController(controller, animated: true) - } - public func replaceCourseUnit( courseName: String, blockId: String, diff --git a/Profile/ProfileTests/ProfileMock.generated.swift b/Profile/ProfileTests/ProfileMock.generated.swift index a5da4e707..536769038 100644 --- a/Profile/ProfileTests/ProfileMock.generated.swift +++ b/Profile/ProfileTests/ProfileMock.generated.swift @@ -2531,41 +2531,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { perform?(`blocks`, `downloadQuality`) } - open func nextBlockForDownloading() -> DownloadDataTask? { - addInvocation(.m_nextBlockForDownloading) - let perform = methodPerformValue(.m_nextBlockForDownloading) as? () -> Void - perform?() - var __value: DownloadDataTask? = nil - do { - __value = try methodReturnValue(.m_nextBlockForDownloading).casted() - } catch { - // do nothing - } - return __value - } - - open func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) { - addInvocation(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) - let perform = methodPerformValue(.m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter.value(`id`), Parameter.value(`state`), Parameter.value(`resumeData`))) as? (String, DownloadState, Data?) -> Void - perform?(`id`, `state`, `resumeData`) - } - - open func deleteDownloadDataTask(id: String) throws { - addInvocation(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) - let perform = methodPerformValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))) as? (String) -> Void - perform?(`id`) - do { - _ = try methodReturnValue(.m_deleteDownloadDataTask__id_id(Parameter.value(`id`))).casted() as Void - } catch MockError.notStubed { - // do nothing - } catch { - throw error - } - } - - open func saveDownloadDataTask(_ task: DownloadDataTask) { - addInvocation(.m_saveDownloadDataTask__task(Parameter.value(`task`))) - let perform = methodPerformValue(.m_saveDownloadDataTask__task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void + open func updateTask(task: DownloadDataTask) { + addInvocation(.m_updateTask__task_task(Parameter.value(`task`))) + let perform = methodPerformValue(.m_updateTask__task_task(Parameter.value(`task`))) as? (DownloadDataTask) -> Void perform?(`task`) } @@ -2610,6 +2578,12 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { return __value } + open func deleteDownloadDataTasks(ids: [String]) { + addInvocation(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) + let perform = methodPerformValue(.m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>.value(`ids`))) as? ([String]) -> Void + perform?(`ids`) + } + fileprivate enum MethodType { case m_set__userId_userId(Parameter) @@ -2622,13 +2596,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case m_deleteProgress__for_blockID(Parameter) case m_deleteAllProgress case m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(Parameter<[CourseBlock]>, Parameter) - case m_nextBlockForDownloading - case m_updateDownloadState__id_idstate_stateresumeData_resumeData(Parameter, Parameter, Parameter) - case m_deleteDownloadDataTask__id_id(Parameter) - case m_saveDownloadDataTask__task(Parameter) + case m_updateTask__task_task(Parameter) case m_downloadDataTask__for_blockId(Parameter) case m_getDownloadDataTasks case m_getDownloadDataTasksForCourse__courseId(Parameter) + case m_deleteDownloadDataTasks__ids_ids(Parameter<[String]>) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { @@ -2671,23 +2643,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsDownloadquality, rhs: rhsDownloadquality, with: matcher), lhsDownloadquality, rhsDownloadquality, "downloadQuality")) return Matcher.ComparisonResult(results) - case (.m_nextBlockForDownloading, .m_nextBlockForDownloading): return .match - - case (.m_updateDownloadState__id_idstate_stateresumeData_resumeData(let lhsId, let lhsState, let lhsResumedata), .m_updateDownloadState__id_idstate_stateresumeData_resumeData(let rhsId, let rhsState, let rhsResumedata)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsState, rhs: rhsState, with: matcher), lhsState, rhsState, "state")) - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsResumedata, rhs: rhsResumedata, with: matcher), lhsResumedata, rhsResumedata, "resumeData")) - return Matcher.ComparisonResult(results) - - case (.m_deleteDownloadDataTask__id_id(let lhsId), .m_deleteDownloadDataTask__id_id(let rhsId)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsId, rhs: rhsId, with: matcher), lhsId, rhsId, "id")) - return Matcher.ComparisonResult(results) - - case (.m_saveDownloadDataTask__task(let lhsTask), .m_saveDownloadDataTask__task(let rhsTask)): + case (.m_updateTask__task_task(let lhsTask), .m_updateTask__task_task(let rhsTask)): var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "_ task")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsTask, rhs: rhsTask, with: matcher), lhsTask, rhsTask, "task")) return Matcher.ComparisonResult(results) case (.m_downloadDataTask__for_blockId(let lhsBlockid), .m_downloadDataTask__for_blockId(let rhsBlockid)): @@ -2701,6 +2659,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "_ courseId")) return Matcher.ComparisonResult(results) + + case (.m_deleteDownloadDataTasks__ids_ids(let lhsIds), .m_deleteDownloadDataTasks__ids_ids(let rhsIds)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsIds, rhs: rhsIds, with: matcher), lhsIds, rhsIds, "ids")) + return Matcher.ComparisonResult(results) default: return .none } } @@ -2717,13 +2680,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case let .m_deleteProgress__for_blockID(p0): return p0.intValue case .m_deleteAllProgress: return 0 case let .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(p0, p1): return p0.intValue + p1.intValue - case .m_nextBlockForDownloading: return 0 - case let .m_updateDownloadState__id_idstate_stateresumeData_resumeData(p0, p1, p2): return p0.intValue + p1.intValue + p2.intValue - case let .m_deleteDownloadDataTask__id_id(p0): return p0.intValue - case let .m_saveDownloadDataTask__task(p0): return p0.intValue + case let .m_updateTask__task_task(p0): return p0.intValue case let .m_downloadDataTask__for_blockId(p0): return p0.intValue case .m_getDownloadDataTasks: return 0 case let .m_getDownloadDataTasksForCourse__courseId(p0): return p0.intValue + case let .m_deleteDownloadDataTasks__ids_ids(p0): return p0.intValue } } func assertionName() -> String { @@ -2738,13 +2699,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { case .m_deleteProgress__for_blockID: return ".deleteProgress(for:)" case .m_deleteAllProgress: return ".deleteAllProgress()" case .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality: return ".addToDownloadQueue(blocks:downloadQuality:)" - case .m_nextBlockForDownloading: return ".nextBlockForDownloading()" - case .m_updateDownloadState__id_idstate_stateresumeData_resumeData: return ".updateDownloadState(id:state:resumeData:)" - case .m_deleteDownloadDataTask__id_id: return ".deleteDownloadDataTask(id:)" - case .m_saveDownloadDataTask__task: return ".saveDownloadDataTask(_:)" + case .m_updateTask__task_task: return ".updateTask(task:)" case .m_downloadDataTask__for_blockId: return ".downloadDataTask(for:)" case .m_getDownloadDataTasks: return ".getDownloadDataTasks()" case .m_getDownloadDataTasksForCourse__courseId: return ".getDownloadDataTasksForCourse(_:)" + case .m_deleteDownloadDataTasks__ids_ids: return ".deleteDownloadDataTasks(ids:)" } } } @@ -2771,9 +2730,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func loadAllOfflineProgress(willReturn: [OfflineProgress]...) -> MethodStub { return Given(method: .m_loadAllOfflineProgress, products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func nextBlockForDownloading(willReturn: DownloadDataTask?...) -> MethodStub { - return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func downloadDataTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } @@ -2804,13 +2760,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func nextBlockForDownloading(willProduce: (Stubber) -> Void) -> MethodStub { - let willReturn: [DownloadDataTask?] = [] - let given: Given = { return Given(method: .m_nextBlockForDownloading, products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: (DownloadDataTask?).self) - willProduce(stubber) - return given - } public static func downloadDataTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [DownloadDataTask?] = [] let given: Given = { return Given(method: .m_downloadDataTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -2844,16 +2793,6 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { willProduce(stubber) return given } - public static func deleteDownloadDataTask(id: Parameter, willThrow: Error...) -> MethodStub { - return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) - } - public static func deleteDownloadDataTask(id: Parameter, willProduce: (StubberThrows) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_deleteDownloadDataTask__id_id(`id`), products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (Void).self) - willProduce(stubber) - return given - } } public struct Verify { @@ -2870,13 +2809,11 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func deleteProgress(for blockID: Parameter) -> Verify { return Verify(method: .m_deleteProgress__for_blockID(`blockID`))} public static func deleteAllProgress() -> Verify { return Verify(method: .m_deleteAllProgress)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`))} - public static func nextBlockForDownloading() -> Verify { return Verify(method: .m_nextBlockForDownloading)} - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter) -> Verify { return Verify(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`))} - public static func deleteDownloadDataTask(id: Parameter) -> Verify { return Verify(method: .m_deleteDownloadDataTask__id_id(`id`))} - public static func saveDownloadDataTask(_ task: Parameter) -> Verify { return Verify(method: .m_saveDownloadDataTask__task(`task`))} + public static func updateTask(task: Parameter) -> Verify { return Verify(method: .m_updateTask__task_task(`task`))} public static func downloadDataTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadDataTask__for_blockId(`blockId`))} public static func getDownloadDataTasks() -> Verify { return Verify(method: .m_getDownloadDataTasks)} public static func getDownloadDataTasksForCourse(_ courseId: Parameter) -> Verify { return Verify(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`))} + public static func deleteDownloadDataTasks(ids: Parameter<[String]>) -> Verify { return Verify(method: .m_deleteDownloadDataTasks__ids_ids(`ids`))} } public struct Perform { @@ -2914,17 +2851,8 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>, downloadQuality: Parameter, perform: @escaping ([CourseBlock], DownloadQuality) -> Void) -> Perform { return Perform(method: .m_addToDownloadQueue__blocks_blocksdownloadQuality_downloadQuality(`blocks`, `downloadQuality`), performs: perform) } - public static func nextBlockForDownloading(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_nextBlockForDownloading, performs: perform) - } - public static func updateDownloadState(id: Parameter, state: Parameter, resumeData: Parameter, perform: @escaping (String, DownloadState, Data?) -> Void) -> Perform { - return Perform(method: .m_updateDownloadState__id_idstate_stateresumeData_resumeData(`id`, `state`, `resumeData`), performs: perform) - } - public static func deleteDownloadDataTask(id: Parameter, perform: @escaping (String) -> Void) -> Perform { - return Perform(method: .m_deleteDownloadDataTask__id_id(`id`), performs: perform) - } - public static func saveDownloadDataTask(_ task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { - return Perform(method: .m_saveDownloadDataTask__task(`task`), performs: perform) + public static func updateTask(task: Parameter, perform: @escaping (DownloadDataTask) -> Void) -> Perform { + return Perform(method: .m_updateTask__task_task(`task`), performs: perform) } public static func downloadDataTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_downloadDataTask__for_blockId(`blockId`), performs: perform) @@ -2935,6 +2863,9 @@ open class CorePersistenceProtocolMock: CorePersistenceProtocol, Mock { public static func getDownloadDataTasksForCourse(_ courseId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_getDownloadDataTasksForCourse__courseId(`courseId`), performs: perform) } + public static func deleteDownloadDataTasks(ids: Parameter<[String]>, perform: @escaping ([String]) -> Void) -> Perform { + return Perform(method: .m_deleteDownloadDataTasks__ids_ids(`ids`), performs: perform) + } } public func given(_ method: Given) { @@ -3430,7 +3361,7 @@ open class CoreStorageMock: CoreStorage, Mock { } // MARK: - DownloadManagerProtocol -@MainActor + open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public init(sequencing sequencingPolicy: SequencingPolicy = .lastWrittenResolvedFirst, stubbing stubbingPolicy: StubbingPolicy = .wrap, file: StaticString = #file, line: UInt = #line) { SwiftyMockyTestObserver.setup() @@ -3469,27 +3400,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { if scopes.contains(.perform) { methodPerformValues = [] } } - public var currentDownloadTask: DownloadDataTask? { - get { invocations.append(.p_currentDownloadTask_get); return __p_currentDownloadTask ?? optionalGivenGetterValue(.p_currentDownloadTask_get, "DownloadManagerProtocolMock - stub value for currentDownloadTask was not defined") } - } - private var __p_currentDownloadTask: (DownloadDataTask)? - - open func publisher() throws -> AnyPublisher { - addInvocation(.m_publisher) - let perform = methodPerformValue(.m_publisher) as? () -> Void + open func getCurrentDownloadTask() -> DownloadDataTask? { + addInvocation(.m_getCurrentDownloadTask) + let perform = methodPerformValue(.m_getCurrentDownloadTask) as? () -> Void perform?() - var __value: AnyPublisher + var __value: DownloadDataTask? = nil do { - __value = try methodReturnValue(.m_publisher).casted() - } catch MockError.notStubed { - onFatalFailure("Stub return value not specified for publisher(). Use given") - Failure("Stub return value not specified for publisher(). Use given") + __value = try methodReturnValue(.m_getCurrentDownloadTask).casted() } catch { - throw error + // do nothing } return __value } @@ -3601,15 +3524,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { } } - open func deleteFile(blocks: [CourseBlock]) { - addInvocation(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) - let perform = methodPerformValue(.m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>.value(`blocks`))) as? ([CourseBlock]) -> Void - perform?(`blocks`) - } - - open func deleteAllFiles() { - addInvocation(.m_deleteAllFiles) - let perform = methodPerformValue(.m_deleteAllFiles) as? () -> Void + open func deleteAll() { + addInvocation(.m_deleteAll) + let perform = methodPerformValue(.m_deleteAll) as? () -> Void perform?() } @@ -3626,20 +3543,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return __value } - open func updateUnzippedFileSize(for sequentials: [CourseSequential]) -> [CourseSequential] { - addInvocation(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) - let perform = methodPerformValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))) as? ([CourseSequential]) -> Void - perform?(`sequentials`) - var __value: [CourseSequential] - do { - __value = try methodReturnValue(.m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>.value(`sequentials`))).casted() - } catch { - onFatalFailure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - Failure("Stub return value not specified for updateUnzippedFileSize(for sequentials: [CourseSequential]). Use given") - } - return __value - } - open func resumeDownloading() throws { addInvocation(.m_resumeDownloading) let perform = methodPerformValue(.m_resumeDownloading) as? () -> Void @@ -3673,9 +3576,28 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { perform?() } + open func delete(blocks: [CourseBlock], courseId: String) { + addInvocation(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) + let perform = methodPerformValue(.m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>.value(`blocks`), Parameter.value(`courseId`))) as? ([CourseBlock], String) -> Void + perform?(`blocks`, `courseId`) + } + + open func downloadTask(for blockId: String) -> DownloadDataTask? { + addInvocation(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) + let perform = methodPerformValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))) as? (String) -> Void + perform?(`blockId`) + var __value: DownloadDataTask? = nil + do { + __value = try methodReturnValue(.m_downloadTask__for_blockId(Parameter.value(`blockId`))).casted() + } catch { + // do nothing + } + return __value + } + fileprivate enum MethodType { - case m_publisher + case m_getCurrentDownloadTask case m_eventPublisher case m_addToDownloadQueue__blocks_blocks(Parameter<[CourseBlock]>) case m_getDownloadTasks @@ -3684,18 +3606,17 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case m_cancelDownloading__task_task(Parameter) case m_cancelDownloading__courseId_courseId(Parameter) case m_cancelAllDownloading - case m_deleteFile__blocks_blocks(Parameter<[CourseBlock]>) - case m_deleteAllFiles + case m_deleteAll case m_fileUrl__for_blockId(Parameter) - case m_updateUnzippedFileSize__for_sequentials(Parameter<[CourseSequential]>) case m_resumeDownloading case m_isLargeVideosSize__blocks_blocks(Parameter<[CourseBlock]>) case m_removeAppSupportDirectoryUnusedContent - case p_currentDownloadTask_get + case m_delete__blocks_blockscourseId_courseId(Parameter<[CourseBlock]>, Parameter) + case m_downloadTask__for_blockId(Parameter) static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { - case (.m_publisher, .m_publisher): return .match + case (.m_getCurrentDownloadTask, .m_getCurrentDownloadTask): return .match case (.m_eventPublisher, .m_eventPublisher): return .match @@ -3729,23 +3650,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case (.m_cancelAllDownloading, .m_cancelAllDownloading): return .match - case (.m_deleteFile__blocks_blocks(let lhsBlocks), .m_deleteFile__blocks_blocks(let rhsBlocks)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) - return Matcher.ComparisonResult(results) - - case (.m_deleteAllFiles, .m_deleteAllFiles): return .match + case (.m_deleteAll, .m_deleteAll): return .match case (.m_fileUrl__for_blockId(let lhsBlockid), .m_fileUrl__for_blockId(let rhsBlockid)): var results: [Matcher.ParameterComparisonResult] = [] results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) return Matcher.ComparisonResult(results) - case (.m_updateUnzippedFileSize__for_sequentials(let lhsSequentials), .m_updateUnzippedFileSize__for_sequentials(let rhsSequentials)): - var results: [Matcher.ParameterComparisonResult] = [] - results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsSequentials, rhs: rhsSequentials, with: matcher), lhsSequentials, rhsSequentials, "for sequentials")) - return Matcher.ComparisonResult(results) - case (.m_resumeDownloading, .m_resumeDownloading): return .match case (.m_isLargeVideosSize__blocks_blocks(let lhsBlocks), .m_isLargeVideosSize__blocks_blocks(let rhsBlocks)): @@ -3754,14 +3665,24 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { return Matcher.ComparisonResult(results) case (.m_removeAppSupportDirectoryUnusedContent, .m_removeAppSupportDirectoryUnusedContent): return .match - case (.p_currentDownloadTask_get,.p_currentDownloadTask_get): return Matcher.ComparisonResult.match + + case (.m_delete__blocks_blockscourseId_courseId(let lhsBlocks, let lhsCourseid), .m_delete__blocks_blockscourseId_courseId(let rhsBlocks, let rhsCourseid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlocks, rhs: rhsBlocks, with: matcher), lhsBlocks, rhsBlocks, "blocks")) + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseId")) + return Matcher.ComparisonResult(results) + + case (.m_downloadTask__for_blockId(let lhsBlockid), .m_downloadTask__for_blockId(let rhsBlockid)): + var results: [Matcher.ParameterComparisonResult] = [] + results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsBlockid, rhs: rhsBlockid, with: matcher), lhsBlockid, rhsBlockid, "for blockId")) + return Matcher.ComparisonResult(results) default: return .none } } func intValue() -> Int { switch self { - case .m_publisher: return 0 + case .m_getCurrentDownloadTask: return 0 case .m_eventPublisher: return 0 case let .m_addToDownloadQueue__blocks_blocks(p0): return p0.intValue case .m_getDownloadTasks: return 0 @@ -3770,19 +3691,18 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case let .m_cancelDownloading__task_task(p0): return p0.intValue case let .m_cancelDownloading__courseId_courseId(p0): return p0.intValue case .m_cancelAllDownloading: return 0 - case let .m_deleteFile__blocks_blocks(p0): return p0.intValue - case .m_deleteAllFiles: return 0 + case .m_deleteAll: return 0 case let .m_fileUrl__for_blockId(p0): return p0.intValue - case let .m_updateUnzippedFileSize__for_sequentials(p0): return p0.intValue case .m_resumeDownloading: return 0 case let .m_isLargeVideosSize__blocks_blocks(p0): return p0.intValue case .m_removeAppSupportDirectoryUnusedContent: return 0 - case .p_currentDownloadTask_get: return 0 + case let .m_delete__blocks_blockscourseId_courseId(p0, p1): return p0.intValue + p1.intValue + case let .m_downloadTask__for_blockId(p0): return p0.intValue } } func assertionName() -> String { switch self { - case .m_publisher: return ".publisher()" + case .m_getCurrentDownloadTask: return ".getCurrentDownloadTask()" case .m_eventPublisher: return ".eventPublisher()" case .m_addToDownloadQueue__blocks_blocks: return ".addToDownloadQueue(blocks:)" case .m_getDownloadTasks: return ".getDownloadTasks()" @@ -3791,14 +3711,13 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { case .m_cancelDownloading__task_task: return ".cancelDownloading(task:)" case .m_cancelDownloading__courseId_courseId: return ".cancelDownloading(courseId:)" case .m_cancelAllDownloading: return ".cancelAllDownloading()" - case .m_deleteFile__blocks_blocks: return ".deleteFile(blocks:)" - case .m_deleteAllFiles: return ".deleteAllFiles()" + case .m_deleteAll: return ".deleteAll()" case .m_fileUrl__for_blockId: return ".fileUrl(for:)" - case .m_updateUnzippedFileSize__for_sequentials: return ".updateUnzippedFileSize(for:)" case .m_resumeDownloading: return ".resumeDownloading()" case .m_isLargeVideosSize__blocks_blocks: return ".isLargeVideosSize(blocks:)" case .m_removeAppSupportDirectoryUnusedContent: return ".removeAppSupportDirectoryUnusedContent()" - case .p_currentDownloadTask_get: return "[get] .currentDownloadTask" + case .m_delete__blocks_blockscourseId_courseId: return ".delete(blocks:courseId:)" + case .m_downloadTask__for_blockId: return ".downloadTask(for:)" } } } @@ -3811,12 +3730,9 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { super.init(products) } - public static func currentDownloadTask(getter defaultValue: DownloadDataTask?...) -> PropertyStub { - return Given(method: .p_currentDownloadTask_get, products: defaultValue.map({ StubProduct.return($0 as Any) })) - } - public static func publisher(willReturn: AnyPublisher...) -> MethodStub { - return Given(method: .m_publisher, products: willReturn.map({ StubProduct.return($0 as Any) })) + public static func getCurrentDownloadTask(willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) } public static func eventPublisher(willReturn: AnyPublisher...) -> MethodStub { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) @@ -3830,12 +3746,19 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func fileUrl(for blockId: Parameter, willReturn: URL?...) -> MethodStub { return Given(method: .m_fileUrl__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willReturn: [CourseSequential]...) -> MethodStub { - return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willReturn: Bool...) -> MethodStub { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) } + public static func downloadTask(for blockId: Parameter, willReturn: DownloadDataTask?...) -> MethodStub { + return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) + } + public static func getCurrentDownloadTask(willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_getCurrentDownloadTask, products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) + willProduce(stubber) + return given + } public static func eventPublisher(willProduce: (Stubber>) -> Void) -> MethodStub { let willReturn: [AnyPublisher] = [] let given: Given = { return Given(method: .m_eventPublisher, products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -3864,13 +3787,6 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, willProduce: (Stubber<[CourseSequential]>) -> Void) -> MethodStub { - let willReturn: [[CourseSequential]] = [] - let given: Given = { return Given(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() - let stubber = given.stub(for: ([CourseSequential]).self) - willProduce(stubber) - return given - } public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>, willProduce: (Stubber) -> Void) -> MethodStub { let willReturn: [Bool] = [] let given: Given = { return Given(method: .m_isLargeVideosSize__blocks_blocks(`blocks`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() @@ -3878,13 +3794,10 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { willProduce(stubber) return given } - public static func publisher(willThrow: Error...) -> MethodStub { - return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) - } - public static func publisher(willProduce: (StubberThrows>) -> Void) -> MethodStub { - let willThrow: [Error] = [] - let given: Given = { return Given(method: .m_publisher, products: willThrow.map({ StubProduct.throw($0) })) }() - let stubber = given.stubThrows(for: (AnyPublisher).self) + public static func downloadTask(for blockId: Parameter, willProduce: (Stubber) -> Void) -> MethodStub { + let willReturn: [DownloadDataTask?] = [] + let given: Given = { return Given(method: .m_downloadTask__for_blockId(`blockId`), products: willReturn.map({ StubProduct.return($0 as Any) })) }() + let stubber = given.stub(for: (DownloadDataTask?).self) willProduce(stubber) return given } @@ -3953,7 +3866,7 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public struct Verify { fileprivate var method: MethodType - public static func publisher() -> Verify { return Verify(method: .m_publisher)} + public static func getCurrentDownloadTask() -> Verify { return Verify(method: .m_getCurrentDownloadTask)} public static func eventPublisher() -> Verify { return Verify(method: .m_eventPublisher)} public static func addToDownloadQueue(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_addToDownloadQueue__blocks_blocks(`blocks`))} public static func getDownloadTasks() -> Verify { return Verify(method: .m_getDownloadTasks)} @@ -3962,22 +3875,21 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelDownloading(task: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__task_task(`task`))} public static func cancelDownloading(courseId: Parameter) -> Verify { return Verify(method: .m_cancelDownloading__courseId_courseId(`courseId`))} public static func cancelAllDownloading() -> Verify { return Verify(method: .m_cancelAllDownloading)} - public static func deleteFile(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_deleteFile__blocks_blocks(`blocks`))} - public static func deleteAllFiles() -> Verify { return Verify(method: .m_deleteAllFiles)} + public static func deleteAll() -> Verify { return Verify(method: .m_deleteAll)} public static func fileUrl(for blockId: Parameter) -> Verify { return Verify(method: .m_fileUrl__for_blockId(`blockId`))} - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>) -> Verify { return Verify(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`))} public static func resumeDownloading() -> Verify { return Verify(method: .m_resumeDownloading)} public static func isLargeVideosSize(blocks: Parameter<[CourseBlock]>) -> Verify { return Verify(method: .m_isLargeVideosSize__blocks_blocks(`blocks`))} public static func removeAppSupportDirectoryUnusedContent() -> Verify { return Verify(method: .m_removeAppSupportDirectoryUnusedContent)} - public static var currentDownloadTask: Verify { return Verify(method: .p_currentDownloadTask_get) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter) -> Verify { return Verify(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`))} + public static func downloadTask(for blockId: Parameter) -> Verify { return Verify(method: .m_downloadTask__for_blockId(`blockId`))} } public struct Perform { fileprivate var method: MethodType var performs: Any - public static func publisher(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_publisher, performs: perform) + public static func getCurrentDownloadTask(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_getCurrentDownloadTask, performs: perform) } public static func eventPublisher(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_eventPublisher, performs: perform) @@ -4003,18 +3915,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func cancelAllDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_cancelAllDownloading, performs: perform) } - public static func deleteFile(blocks: Parameter<[CourseBlock]>, perform: @escaping ([CourseBlock]) -> Void) -> Perform { - return Perform(method: .m_deleteFile__blocks_blocks(`blocks`), performs: perform) - } - public static func deleteAllFiles(perform: @escaping () -> Void) -> Perform { - return Perform(method: .m_deleteAllFiles, performs: perform) + public static func deleteAll(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_deleteAll, performs: perform) } public static func fileUrl(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { return Perform(method: .m_fileUrl__for_blockId(`blockId`), performs: perform) } - public static func updateUnzippedFileSize(for sequentials: Parameter<[CourseSequential]>, perform: @escaping ([CourseSequential]) -> Void) -> Perform { - return Perform(method: .m_updateUnzippedFileSize__for_sequentials(`sequentials`), performs: perform) - } public static func resumeDownloading(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_resumeDownloading, performs: perform) } @@ -4024,6 +3930,12 @@ open class DownloadManagerProtocolMock: DownloadManagerProtocol, Mock { public static func removeAppSupportDirectoryUnusedContent(perform: @escaping () -> Void) -> Perform { return Perform(method: .m_removeAppSupportDirectoryUnusedContent, performs: perform) } + public static func delete(blocks: Parameter<[CourseBlock]>, courseId: Parameter, perform: @escaping ([CourseBlock], String) -> Void) -> Perform { + return Perform(method: .m_delete__blocks_blockscourseId_courseId(`blocks`, `courseId`), performs: perform) + } + public static func downloadTask(for blockId: Parameter, perform: @escaping (String) -> Void) -> Perform { + return Perform(method: .m_downloadTask__for_blockId(`blockId`), performs: perform) + } } public func given(_ method: Given) {