import XCTest @testable import CallsKit /// The rules about when someone waiting is worth interrupting for. All of /// them live in KnockWatch precisely so they can be held here, without a /// notification centre or an app bundle. final class KnockWatchTests: XCTestCase { private func peer(_ id: String, _ name: String) -> RosterPeer { let json = #"{"peer":"\#(id)","name":"\#(name)","kind":"guest","gen":null}"# return try! JSONDecoder().decode(RosterPeer.self, from: Data(json.utf8)) } @MainActor func testSomeoneArrivingWhileAwayIsAnnouncedExactlyOnce() { let watch = KnockWatch() let bea = peer("p1", "Bea") XCTAssertEqual(watch.arrivals(waiting: [bea], visible: false).map(\.name), ["Bea"]) // The same roster arriving again is the same person still waiting, // not a second knock. State events repeat constantly. XCTAssertTrue(watch.arrivals(waiting: [bea], visible: false).isEmpty) XCTAssertTrue(watch.arrivals(waiting: [bea], visible: false).isEmpty) } @MainActor func testNobodyIsAnnouncedWhileTheQueueIsOnScreen() { let watch = KnockWatch() XCTAssertTrue(watch.arrivals(waiting: [peer("p1", "Bea")], visible: true).isEmpty) } @MainActor func testPeopleWhoArrivedWhileLookingAreNotAnnouncedOnTabbingAway() { // The rule that makes the feature bearable. Someone watching the call // sees the queue; tabbing away afterwards must not then produce a // pile of notifications about people who have been there for a while. // Only an arrival *during* the absence is news. let watch = KnockWatch() let bea = peer("p1", "Bea") _ = watch.arrivals(waiting: [bea], visible: true) XCTAssertTrue( watch.arrivals(waiting: [bea], visible: false).isEmpty, "someone seen while the app was in front must not be announced later") let cass = peer("p2", "Cass") XCTAssertEqual( watch.arrivals(waiting: [bea, cass], visible: false).map(\.name), ["Cass"], "the person who arrived while away is the only news") } @MainActor func testLeavingTheQueueAndKnockingAgainIsHeardAgain() { // Someone let in, or who gave up and came back, is a new knock. A set // that only ever grew would swallow the second one in silence. let watch = KnockWatch() let bea = peer("p1", "Bea") XCTAssertEqual(watch.arrivals(waiting: [bea], visible: false).count, 1) XCTAssertTrue(watch.arrivals(waiting: [], visible: false).isEmpty) XCTAssertEqual( watch.arrivals(waiting: [bea], visible: false).map(\.name), ["Bea"], "a second knock from the same person must be heard") } @MainActor func testOnePersonSwappedForAnotherIsStillOneAnnouncement() { // A count would read this as "still one waiting" and say nothing. The // set is what makes the swap visible. let watch = KnockWatch() _ = watch.arrivals(waiting: [peer("p1", "Bea")], visible: false) XCTAssertEqual( watch.arrivals(waiting: [peer("p2", "Cass")], visible: false).map(\.name), ["Cass"]) } @MainActor func testSeveralArrivingAtOnceAreAllAnnounced() { let watch = KnockWatch() let names = watch.arrivals( waiting: [peer("p1", "Bea"), peer("p2", "Cass")], visible: false ).map(\.name) XCTAssertEqual(names, ["Bea", "Cass"]) } @MainActor func testEndingACallForgetsItsQueue() { let watch = KnockWatch() let bea = peer("p1", "Bea") _ = watch.arrivals(waiting: [bea], visible: false) watch.reset() XCTAssertEqual( watch.arrivals(waiting: [bea], visible: false).map(\.name), ["Bea"], "a queue from a finished meeting says nothing about the next one") } }