import Foundation import RastrilloNative import XCTest private func summary(_ lanes: [String: (pending: Int, held: Int, inFlight: Int)], hold: Hold.Reason? = nil) -> OutboxSummary { var summary = OutboxSummary() for (name, counts) in lanes { var lane = OutboxSummary.Lane() lane.pending = counts.pending; lane.held = counts.held; lane.inFlight = counts.inFlight if counts.held > 0 { lane.firstHold = hold ?? .accessLost } summary.lanes[name] = lane } return summary } final class SyncStatusTests: XCTestCase { func testPriorityOrder() { let pending = summary(["doc:a": (2, 0, 0), "doc:b": (1, 0, 0)]) let held = summary(["doc:a": (1, 1, 0)], hold: .tooLarge) let flying = summary(["doc:a": (2, 0, 1)]) // 1. A hold beats everything, even an ended session. XCTAssertEqual(SyncStatus(summary: held, sending: true, sessionEnded: true, offline: true, backingOff: true), .failed(pending: 1, reason: .held(.tooLarge))) // A local failure is failed too. XCTAssertEqual(SyncStatus(summary: pending, sending: false, sessionEnded: false, offline: false, backingOff: false, localFailure: .cannotSaveOnDevice), .failed(pending: 2, reason: .cannotSaveOnDevice)) // 2. Session ended. XCTAssertEqual(SyncStatus(summary: pending, sending: false, sessionEnded: true, offline: true, backingOff: true), .needsSignIn(pending: 2)) // 3. Nothing to send. XCTAssertEqual(SyncStatus(summary: OutboxSummary(), sending: false, sessionEnded: false, offline: false, backingOff: false), .saved) // 4. In flight. XCTAssertEqual(SyncStatus(summary: flying, sending: true, sessionEnded: false, offline: true, backingOff: false), .saving) // 5. Offline. XCTAssertEqual(SyncStatus(summary: pending, sending: false, sessionEnded: false, offline: true, backingOff: true), .offline(pending: 2)) // 6. Backing off. XCTAssertEqual(SyncStatus(summary: pending, sending: false, sessionEnded: false, offline: false, backingOff: true), .retrying(pending: 2)) // Sendable, online, not yet started: about to save, never "saved". XCTAssertEqual(SyncStatus(summary: pending, sending: false, sessionEnded: false, offline: false, backingOff: false), .saving) } func testOfflineWithNothingPendingIsSilentlySaved() { let status = SyncStatus(summary: OutboxSummary(), sending: false, sessionEnded: false, offline: true, backingOff: false) XCTAssertEqual(status, .saved, "\"Offline ยท 0 pending\" must never appear") XCTAssertEqual(SyncStatus(summary: OutboxSummary(), sending: false, sessionEnded: true, offline: true, backingOff: false), .needsSignIn(pending: 0), "Session-ended has priority over an empty outbox (spec 3.4)") } func testNCountsLanesByDefaultAndWhatTheAppChooses() { let lanes = summary(["doc:a": (5, 0, 0), "doc:b": (1, 0, 0)]) XCTAssertEqual(SyncStatus(summary: lanes, sending: false, sessionEnded: false, offline: true, backingOff: false), .offline(pending: 2)) XCTAssertEqual(SyncStatus(summary: lanes, sending: false, sessionEnded: false, offline: true, backingOff: false, count: { $0.pending }), .offline(pending: 6)) } func testSaveStateMappingStickinessAndAnnouncements() { XCTAssertEqual(SyncStatus.saved.saveState, .saved) XCTAssertEqual(SyncStatus.saving.saveState, .saving) XCTAssertEqual(SyncStatus.offline(pending: 1).saveState, .retrying, "The Slides precedent: offline is retrying") XCTAssertEqual(SyncStatus.retrying(pending: 1).saveState, .retrying) XCTAssertEqual(SyncStatus.needsSignIn(pending: 1).saveState, .failed) XCTAssertEqual(SyncStatus.failed(pending: 1, reason: .tooManyUnsent).saveState, .failed) XCTAssertTrue(SyncStatus.needsSignIn(pending: 1).isSticky) XCTAssertTrue(SyncStatus.failed(pending: 1, reason: .cannotSaveOnDevice).isSticky) XCTAssertFalse(SyncStatus.offline(pending: 1).isSticky) XCTAssertFalse(SyncStatus.saving.isAnnounced, "A saving flicker is never announced") XCTAssertTrue(SyncStatus.offline(pending: 1).isAnnounced) XCTAssertTrue(SyncStatus.needsSignIn(pending: 1).isAnnounced) XCTAssertTrue(SyncStatus.failed(pending: 1, reason: .tooManyUnsent).isAnnounced) } }