mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-08 11:14:51 +00:00
176 lines
6.0 KiB
Swift
176 lines
6.0 KiB
Swift
import Foundation
|
|
import Testing
|
|
|
|
@testable import ShadowsocksX_NG2
|
|
|
|
/// 瞬态门控裁决表:稳定值立即可见、瞬态值延迟上屏、短命瞬态被吞、
|
|
/// 已上屏瞬态保持最短时长。时间经注入的 now/sleep 虚拟化,不真实等待。
|
|
@MainActor
|
|
struct TransientDelayGateTests {
|
|
@Test func firstValueDisplaysImmediatelyWithoutDelay() {
|
|
var displayed: [String] = []
|
|
let gate = TransientDelayGate<String>(
|
|
interval: 0.4,
|
|
now: { 0 },
|
|
sleep: { _ in })
|
|
gate.display = { displayed.append($0) }
|
|
gate.update("applied", isTransient: false)
|
|
#expect(displayed == ["applied"])
|
|
}
|
|
|
|
@Test func transientValueDisplaysOnlyAfterDelayInterval() async {
|
|
var now: TimeInterval = 0
|
|
var displayed: [String] = []
|
|
var delays: [TimeInterval] = []
|
|
let sleepEntered = AsyncGate()
|
|
let sleepRelease = AsyncGate()
|
|
let gate = TransientDelayGate<String>(
|
|
interval: 0.4,
|
|
now: { now },
|
|
sleep: { delay in
|
|
delays.append(delay)
|
|
sleepEntered.release()
|
|
await sleepRelease.wait()
|
|
})
|
|
gate.display = { displayed.append($0) }
|
|
gate.update("applied", isTransient: false)
|
|
gate.update("applying", isTransient: true)
|
|
let task = gate.scheduledDisplayTask
|
|
await sleepEntered.wait()
|
|
#expect(delays == [0.4])
|
|
#expect(displayed == ["applied"])
|
|
now = 0.4
|
|
sleepRelease.release()
|
|
await task?.value
|
|
#expect(displayed == ["applied", "applying"])
|
|
}
|
|
|
|
@Test func shortLivedTransientIsSupersededAndNeverDisplayed() async {
|
|
let clock = SleepGateRecorder()
|
|
var now: TimeInterval = 0
|
|
var displayed: [String] = []
|
|
let gate = TransientDelayGate<String>(
|
|
interval: 0.4,
|
|
now: { now },
|
|
sleep: { delay in await clock.sleep(delay) })
|
|
gate.display = { displayed.append($0) }
|
|
gate.update("applied", isTransient: false)
|
|
let transientSleep = clock.armNextCall()
|
|
gate.update("applying", isTransient: true)
|
|
let transientTask = gate.scheduledDisplayTask
|
|
await transientSleep.entered.wait()
|
|
now = 0.2
|
|
gate.update("failed", isTransient: false)
|
|
#expect(displayed == ["applied", "failed"])
|
|
transientSleep.release.release()
|
|
await transientTask?.value
|
|
#expect(displayed == ["applied", "failed"])
|
|
}
|
|
|
|
@Test func displayedTransientHoldsForMinimumInterval() async {
|
|
let clock = SleepGateRecorder()
|
|
var now: TimeInterval = 0
|
|
var displayed: [String] = []
|
|
let gate = TransientDelayGate<String>(
|
|
interval: 0.4,
|
|
now: { now },
|
|
sleep: { delay in await clock.sleep(delay) })
|
|
gate.display = { displayed.append($0) }
|
|
gate.update("applied", isTransient: false)
|
|
let transientSleep = clock.armNextCall()
|
|
gate.update("applying", isTransient: true)
|
|
let transientTask = gate.scheduledDisplayTask
|
|
await transientSleep.entered.wait()
|
|
now = 0.4
|
|
transientSleep.release.release()
|
|
await transientTask?.value
|
|
#expect(displayed == ["applied", "applying"])
|
|
let heldSleep = clock.armNextCall()
|
|
gate.update("failed", isTransient: false)
|
|
let heldTask = gate.scheduledDisplayTask
|
|
#expect(displayed == ["applied", "applying"])
|
|
await heldSleep.entered.wait()
|
|
#expect(clock.delays == [0.4, 0.4])
|
|
now = 0.8
|
|
heldSleep.release.release()
|
|
await heldTask?.value
|
|
#expect(displayed == ["applied", "applying", "failed"])
|
|
}
|
|
|
|
@Test func transientAfterDisplayedTransientWaitsForDelayAndHold() async {
|
|
let clock = SleepGateRecorder()
|
|
var now: TimeInterval = 0
|
|
var displayed: [String] = []
|
|
let gate = TransientDelayGate<String>(
|
|
interval: 0.4,
|
|
now: { now },
|
|
sleep: { delay in await clock.sleep(delay) })
|
|
gate.display = { displayed.append($0) }
|
|
gate.update("applied", isTransient: false)
|
|
let applyingSleep = clock.armNextCall()
|
|
gate.update("applying", isTransient: true)
|
|
let applyingTask = gate.scheduledDisplayTask
|
|
await applyingSleep.entered.wait()
|
|
now = 0.4
|
|
applyingSleep.release.release()
|
|
await applyingTask?.value
|
|
#expect(displayed == ["applied", "applying"])
|
|
let repairingSleep = clock.armNextCall()
|
|
now = 0.5
|
|
gate.update("repairing", isTransient: true)
|
|
let repairingTask = gate.scheduledDisplayTask
|
|
#expect(displayed == ["applied", "applying"])
|
|
await repairingSleep.entered.wait()
|
|
#expect(clock.delays == [0.4, 0.4])
|
|
now = 0.9
|
|
repairingSleep.release.release()
|
|
await repairingTask?.value
|
|
#expect(displayed == ["applied", "applying", "repairing"])
|
|
}
|
|
|
|
@Test func duplicatePendingPayloadKeepsOriginalSchedule() async {
|
|
let clock = SleepGateRecorder()
|
|
var now: TimeInterval = 0
|
|
var displayed: [String] = []
|
|
let gate = TransientDelayGate<String>(
|
|
interval: 0.4,
|
|
now: { now },
|
|
sleep: { delay in await clock.sleep(delay) })
|
|
gate.display = { displayed.append($0) }
|
|
gate.update("applied", isTransient: false)
|
|
let applyingSleep = clock.armNextCall()
|
|
gate.update("applying", isTransient: true)
|
|
let applyingTask = gate.scheduledDisplayTask
|
|
await applyingSleep.entered.wait()
|
|
now = 0.1
|
|
gate.update("applying", isTransient: true)
|
|
now = 0.4
|
|
applyingSleep.release.release()
|
|
await applyingTask?.value
|
|
#expect(displayed == ["applied", "applying"])
|
|
#expect(clock.delays == [0.4])
|
|
}
|
|
|
|
@Test func valueEqualToDisplayedCancelsPendingDisplay() async {
|
|
let clock = SleepGateRecorder()
|
|
let now: TimeInterval = 0
|
|
var displayed: [String] = []
|
|
let gate = TransientDelayGate<String>(
|
|
interval: 0.4,
|
|
now: { now },
|
|
sleep: { delay in await clock.sleep(delay) })
|
|
gate.display = { displayed.append($0) }
|
|
gate.update("applied", isTransient: false)
|
|
let applyingSleep = clock.armNextCall()
|
|
gate.update("applying", isTransient: true)
|
|
let applyingTask = gate.scheduledDisplayTask
|
|
await applyingSleep.entered.wait()
|
|
gate.update("applied", isTransient: false)
|
|
#expect(displayed == ["applied"])
|
|
applyingSleep.release.release()
|
|
await applyingTask?.value
|
|
#expect(displayed == ["applied"])
|
|
#expect(clock.delays == [0.4])
|
|
}
|
|
}
|