field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

TeraRuntimeResourceLifecycleTests.swift (7033B)


      1 @testable import TeraApp
      2 import XCTest
      3 
      4 final class TeraRuntimeResourceLifecycleTests: XCTestCase {
      5   func testStartupTimeoutClosesLateBackendWithoutResurrection() async throws {
      6     let configuration = TeraRuntimeClientTests().makeConfiguration(generation: "30")
      7     let backend = ResourceTestBackend(publicKeyHex: configuration.publicKeyHex)
      8     let pause = ResourceTestPause()
      9     let deadlines = TeraRuntimeDeadlinePolicy(startupNanoseconds: 1_000_000)
     10     let client = TeraRuntimeClient(
     11       factory: { _ in await pause.wait(); return await backend.start() }, deadlines: deadlines
     12     )
     13     let start = Task { try await client.start(configuration: configuration) }
     14     await pause.entered.wait()
     15     do {
     16       _ = try await start.value
     17       XCTFail("Startup deadline must end the wait")
     18     } catch let TeraRuntimeClientError.startup(failure) {
     19       XCTAssertEqual(failure.code, "ios.runtime.deadline_exceeded")
     20     }
     21     await pause.resume.open()
     22     await assertClosed(backend)
     23     guard case .failed = await client.lifecycle() else { return XCTFail("Late success cannot revive the runtime") }
     24     _ = try await client.stop()
     25   }
     26 
     27   func testLastCancelledStartupWaiterClosesItsLateBackend() async throws {
     28     let configuration = TeraRuntimeClientTests().makeConfiguration(generation: "31")
     29     let backend = ResourceTestBackend(publicKeyHex: configuration.publicKeyHex)
     30     let pause = ResourceTestPause()
     31     let client = TeraRuntimeClient(factory: { _ in await pause.wait(); return await backend.start() })
     32     let start = Task { try await client.start(configuration: configuration) }
     33     await pause.entered.wait()
     34     start.cancel()
     35     do {
     36       _ = try await start.value
     37       XCTFail("A cancelled sole waiter must not adopt the runtime")
     38     } catch let TeraRuntimeClientError.startup(failure) {
     39       XCTAssertEqual(failure.code, "ios.runtime.cancelled")
     40     }
     41     let lifecycle = await client.lifecycle()
     42     XCTAssertEqual(lifecycle, .stopped)
     43     await pause.resume.open()
     44     await assertClosed(backend)
     45     _ = try await client.stop()
     46     let count = await backend.shutdownCount
     47     XCTAssertEqual(count, 1)
     48   }
     49 
     50   func testLateSupersededStartupCannotCloseNewRuntime() async throws {
     51     let firstConfig = TeraRuntimeClientTests().makeConfiguration(generation: "32")
     52     let nextConfig = TeraRuntimeClientTests().makeConfiguration(generation: "33")
     53     let old = ResourceTestBackend(publicKeyHex: firstConfig.publicKeyHex)
     54     let current = ResourceTestBackend(publicKeyHex: nextConfig.publicKeyHex)
     55     let pause = ResourceTestPause()
     56     let client = TeraRuntimeClient(factory: { configuration in
     57       if configuration == firstConfig {
     58         await pause.wait(); return await old.start()
     59       }
     60       return await current.start()
     61     })
     62     let first = Task { try await client.start(configuration: firstConfig) }
     63     await pause.entered.wait()
     64     let replacement = Task { try await client.start(configuration: nextConfig) }
     65     do {
     66       _ = try await first.value
     67       XCTFail("Old startup must be superseded")
     68     } catch {
     69       XCTAssertEqual(error as? TeraRuntimeClientError, .superseded)
     70     }
     71     await pause.resume.open()
     72     await assertClosed(old)
     73     _ = try await replacement.value
     74     let snapshot = try await client.snapshot()
     75     let activeCloses = await current.shutdownCount
     76     XCTAssertEqual(snapshot.identity.publicKeyHex, nextConfig.publicKeyHex)
     77     XCTAssertEqual(activeCloses, 0)
     78     _ = try await client.stop()
     79   }
     80 
     81   func testLateSubscriptionAfterCancellationDetachesOnce() async throws {
     82     try await assertLateSubscription(termination: .cancel)
     83   }
     84 
     85   func testLateSubscriptionAfterTimeoutDetachesOnce() async throws {
     86     try await assertLateSubscription(termination: .timeout)
     87   }
     88 
     89   func testLateSubscriptionAfterStopDetachesOnce() async throws {
     90     try await assertLateSubscription(termination: .stop)
     91   }
     92 
     93   func testAdoptedSubscriptionFinishesAndDetachesOnceOnRepeatedStop() async throws {
     94     let configuration = TeraRuntimeClientTests().makeConfiguration(generation: "34")
     95     let backend = ResourceTestBackend(publicKeyHex: configuration.publicKeyHex)
     96     let client = TeraRuntimeClient(factory: { _ in await backend.start() })
     97     _ = try await client.start(configuration: configuration)
     98     let stream = try await client.changes()
     99     await backend.emit(1)
    100     var iterator = stream.makeAsyncIterator()
    101     let first = await iterator.next()
    102     XCTAssertEqual(first?.revision.rawValue, 1)
    103     _ = try await client.stop()
    104     _ = try await client.stop()
    105     await backend.emit(2)
    106     let final = await iterator.next()
    107     let cancels = await backend.token.cancelCount
    108     let closes = await backend.shutdownCount
    109     XCTAssertNil(final)
    110     XCTAssertEqual(cancels, 1)
    111     XCTAssertEqual(closes, 1)
    112   }
    113 
    114   private enum Termination { case cancel, timeout, stop }
    115 
    116   private func assertLateSubscription(termination: Termination) async throws {
    117     let configuration = TeraRuntimeClientTests().makeConfiguration(generation: "35")
    118     let backend = ResourceTestBackend(publicKeyHex: configuration.publicKeyHex)
    119     let pause = ResourceTestPause()
    120     await backend.pauseSubscription(pause)
    121     let deadlines = TeraRuntimeDeadlinePolicy(
    122       subscriptionNanoseconds: termination == .timeout ? 1_000_000 : 10_000_000_000
    123     )
    124     let client = TeraRuntimeClient(factory: { _ in await backend.start() }, deadlines: deadlines)
    125     _ = try await client.start(configuration: configuration)
    126     let creation = Task { try await client.changes() }
    127     await pause.entered.wait()
    128     var stop: Task<TeraRuntimeShutdownReceipt, Error>?
    129     switch termination {
    130     case .cancel: creation.cancel()
    131     case .stop: stop = Task { try await client.stop() }
    132     case .timeout: break
    133     }
    134     do {
    135       _ = try await creation.value
    136       XCTFail("Abandoned creation must not return an observer")
    137     } catch {
    138       if termination == .stop {
    139         XCTAssertEqual(error as? TeraRuntimeClientError, .superseded)
    140       } else {
    141         guard case let TeraRuntimeClientError.subscription(failure) = error else { throw error }
    142         XCTAssertEqual(failure.code, termination == .timeout ? "ios.runtime.deadline_exceeded" : "ios.runtime.cancelled")
    143       }
    144     }
    145     await pause.resume.open()
    146     let detached = expectation(description: "Late token detached")
    147     let wait = Task { await backend.token.cancelled.wait(); detached.fulfill() }
    148     await fulfillment(of: [detached], timeout: 2)
    149     await backend.token.cancelled.open()
    150     await wait.value
    151     _ = try await stop?.value
    152     _ = try await client.stop()
    153     let count = await backend.token.cancelCount
    154     XCTAssertEqual(count, 1)
    155   }
    156 
    157   private func assertClosed(_ backend: ResourceTestBackend) async {
    158     let closed = expectation(description: "Late backend closed")
    159     let wait = Task { await backend.closed.wait(); closed.fulfill() }
    160     await fulfillment(of: [closed], timeout: 2)
    161     await backend.closed.open()
    162     await wait.value
    163     let count = await backend.shutdownCount
    164     XCTAssertEqual(count, 1)
    165   }
    166 }