mirror of
https://github.com/samsonjs/NotificationSmuggler.git
synced 2026-03-25 08:25:48 +00:00
199 lines
7.1 KiB
Swift
199 lines
7.1 KiB
Swift
import Combine
|
|
import Foundation
|
|
@testable import NotificationSmuggler
|
|
import Testing
|
|
|
|
struct SmugglingTests {
|
|
|
|
// MARK: Notification extensions
|
|
|
|
@Test func buildNotification() {
|
|
let contraband = HitchhikersNotification(answer: 42)
|
|
let notification = Notification.smuggle(contraband)
|
|
#expect(notification.name == HitchhikersNotification.notificationName)
|
|
#expect(notification.userInfo?.count == 1)
|
|
let key = HitchhikersNotification.userInfoKey
|
|
let userInfoValue = notification.userInfo?[key] as? HitchhikersNotification
|
|
#expect(userInfoValue == contraband)
|
|
}
|
|
|
|
@Test func extractContraband() {
|
|
let contraband = HitchhikersNotification(answer: 42)
|
|
let notification = Notification.smuggle(contraband)
|
|
let extracted: HitchhikersNotification? = notification.smuggled()
|
|
#expect(extracted == contraband)
|
|
}
|
|
|
|
@Test func extractContrabandFailsOnWrongType() {
|
|
let imposter = Notification(
|
|
name: HitchhikersNotification.notificationName,
|
|
object: nil,
|
|
userInfo: [HitchhikersNotification.userInfoKey: "imposter"]
|
|
)
|
|
let extracted: HitchhikersNotification? = imposter.smuggled()
|
|
#expect(extracted == nil)
|
|
}
|
|
|
|
@Test func extractContrabandFailsOnMissingPayload() {
|
|
let incompleteNotification = Notification(name: HitchhikersNotification.notificationName)
|
|
let extracted: HitchhikersNotification? = incompleteNotification.smuggled()
|
|
#expect(extracted == nil)
|
|
}
|
|
|
|
// MARK: NotificationCenter extensions
|
|
|
|
@Test(.timeLimit(.minutes(1)))
|
|
@MainActor func notificationCenterSmuggle() async {
|
|
let center = NotificationCenter()
|
|
let task = Task {
|
|
for await contraband in center.notifications(for: HitchhikersNotification.self) {
|
|
#expect(contraband.answer == 42)
|
|
return
|
|
}
|
|
}
|
|
await Task.yield()
|
|
|
|
let contraband = HitchhikersNotification(answer: 42)
|
|
center.smuggle(contraband)
|
|
await task.value
|
|
}
|
|
|
|
// It's important that the tests and the notification-observing tasks are not on the same actor,
|
|
// so we make the tests @MainActor and observe notifications on another actor. Otherwise it's
|
|
// a deadlock.
|
|
|
|
@Test(.timeLimit(.minutes(1)))
|
|
@MainActor func notificationCenterAsyncSequence() async throws {
|
|
let center = NotificationCenter()
|
|
nonisolated(unsafe) var received = false
|
|
Task {
|
|
for await contraband in center.notifications(for: HitchhikersNotification.self) {
|
|
#expect(contraband.answer == 42)
|
|
received = true
|
|
break
|
|
}
|
|
}
|
|
await Task.yield()
|
|
|
|
let contraband = HitchhikersNotification(answer: 42)
|
|
center.smuggle(contraband)
|
|
while !received { await Task.yield() }
|
|
}
|
|
|
|
@Test(.timeLimit(.minutes(1)))
|
|
@MainActor func notificationCenterAsyncSequenceIgnoresInvalidPayloads() async throws {
|
|
let center = NotificationCenter()
|
|
nonisolated(unsafe) var received = false
|
|
let task = Task {
|
|
for await _ in center.notifications(for: HitchhikersNotification.self) {
|
|
received = true
|
|
}
|
|
}
|
|
defer { task.cancel() }
|
|
await Task.yield()
|
|
|
|
// Post an invalid one that should be ignored.
|
|
let imposter = Notification(
|
|
name: HitchhikersNotification.notificationName,
|
|
object: nil,
|
|
userInfo: [HitchhikersNotification.userInfoKey: "imposter"]
|
|
)
|
|
center.post(imposter)
|
|
|
|
try await Task.sleep(for: .milliseconds(10))
|
|
#expect(!received)
|
|
}
|
|
|
|
@Test(.timeLimit(.minutes(1)))
|
|
@MainActor func notificationCenterPublisher() async {
|
|
var cancellables = Set<AnyCancellable>()
|
|
let center = NotificationCenter()
|
|
nonisolated(unsafe) var received = false
|
|
center.publisher(for: HitchhikersNotification.self)
|
|
.sink { hitchhikers in
|
|
#expect(hitchhikers.answer == 42)
|
|
received = true
|
|
}.store(in: &cancellables)
|
|
await Task.yield()
|
|
|
|
let contraband = HitchhikersNotification(answer: 42)
|
|
center.smuggle(contraband)
|
|
while !received { await Task.yield() }
|
|
}
|
|
|
|
@Test(.timeLimit(.minutes(1)))
|
|
@MainActor func notificationCenterPublisherIgnoresInvalidPayloads() async throws {
|
|
var cancellables = Set<AnyCancellable>()
|
|
let center = NotificationCenter()
|
|
nonisolated(unsafe) var received = false
|
|
center.publisher(for: HitchhikersNotification.self)
|
|
.sink { contraband in
|
|
#expect(contraband.answer == 42)
|
|
received = true
|
|
}.store(in: &cancellables)
|
|
await Task.yield()
|
|
|
|
// Post an invalid one that should be ignored.
|
|
let imposter = Notification(
|
|
name: HitchhikersNotification.notificationName,
|
|
object: nil,
|
|
userInfo: [HitchhikersNotification.userInfoKey: "imposter"]
|
|
)
|
|
center.post(imposter)
|
|
|
|
try await Task.sleep(for: .milliseconds(10))
|
|
#expect(!received)
|
|
}
|
|
|
|
// MARK: Object-specific notifications
|
|
|
|
final class SenderObject: NSObject, Sendable {}
|
|
|
|
@Test(.timeLimit(.minutes(1)))
|
|
@MainActor func notificationCenterAsyncSequenceWithObject() async throws {
|
|
let center = NotificationCenter()
|
|
let senderObject = SenderObject()
|
|
let decoyObject = NSObject()
|
|
|
|
let task = Task {
|
|
for await contraband in center.notifications(for: HitchhikersNotification.self, object: senderObject) {
|
|
#expect(contraband.answer == 42)
|
|
return
|
|
}
|
|
}
|
|
await Task.yield()
|
|
|
|
// Post from decay object, should be ignored
|
|
center.post(.smuggle(HitchhikersNotification(answer: 99), object: decoyObject))
|
|
try await Task.sleep(for: .milliseconds(10))
|
|
|
|
// Post from sender object, should be received
|
|
center.post(.smuggle(HitchhikersNotification(answer: 42), object: senderObject))
|
|
await task.value
|
|
}
|
|
|
|
@Test(.timeLimit(.minutes(1)))
|
|
@MainActor func notificationCenterPublisherWithObject() async throws {
|
|
var cancellables = Set<AnyCancellable>()
|
|
let center = NotificationCenter()
|
|
let senderObject = SenderObject()
|
|
let decoyObject = NSObject()
|
|
nonisolated(unsafe) var received = false
|
|
|
|
center.publisher(for: HitchhikersNotification.self, object: senderObject)
|
|
.sink { contraband in
|
|
#expect(contraband.answer == 42)
|
|
received = true
|
|
}.store(in: &cancellables)
|
|
await Task.yield()
|
|
|
|
// Post from decoy object, should be ignored
|
|
center.post(.smuggle(HitchhikersNotification(answer: 99), object: decoyObject))
|
|
try await Task.sleep(for: .milliseconds(10))
|
|
#expect(!received)
|
|
|
|
// Post from sender object, should be received
|
|
center.post(.smuggle(HitchhikersNotification(answer: 42), object: senderObject))
|
|
while !received { await Task.yield() }
|
|
}
|
|
}
|