Files
ysicing 9d650f93c7 fix: 启动时更新检查失败后重试,停止测速后不再显示测试中
- “启动时”检查策略在本次运行成功检查前保持待办,开机时网络未就绪导致的失败由每小时定时器重试
- 宽带测速的阶段与进度回调按运行代次丢弃过期回调,停止或完成后不会把界面改回“测试中”
2026-09-26 15:08:07 +08:00

99 lines
3.7 KiB
Swift

// Copyright (C) 2026 ysicing
// SPDX-License-Identifier: AGPL-3.0-or-later
import Foundation
@testable import Metrics
import Testing
@testable import XStatsUI
private actor SpeedProbeGate {
private var started: CheckedContinuation<Void, Never>?
private var blocked: CheckedContinuation<Void, Never>?
private var didStart = false
func wait() async {
didStart = true
started?.resume()
started = nil
await withCheckedContinuation { blocked = $0 }
}
func waitUntilStarted() async {
if didStart { return }
await withCheckedContinuation { started = $0 }
}
func release() {
blocked?.resume()
blocked = nil
}
}
@MainActor
@Suite struct SpeedTestControllerTests {
@Test func cancelledRouteDoesNotPublishOldProbeResult() async {
let suite = "SpeedTestControllerTests.\(UUID())"
let defaults = UserDefaults(suiteName: suite)!
defer { defaults.removePersistentDomain(forName: suite) }
let settings = AppSettings(defaults: defaults)
let controller = SpeedTestController(settings: settings)
let gate = SpeedProbeGate()
var published: [Int] = []
let task = Task {
await controller.measure([1], concurrency: 1) { _ in
await gate.wait()
return LatencyResult(best: 12, attempts: 1)
} done: { node, _ in
published.append(node)
}
}
await gate.waitUntilStarted()
task.cancel()
await gate.release()
await task.value
#expect(published.isEmpty)
}
@Test func stageUpdateQueuedBeforeStopDoesNotRestartBroadband() async {
let suite = "SpeedTestControllerTests.stop.\(UUID())"
let defaults = UserDefaults(suiteName: suite)!
defer { defaults.removePersistentDomain(forName: suite) }
let gate = SpeedProbeGate()
let controller = SpeedTestController(settings: AppSettings(defaults: defaults)) { _, _, stage, progress in
// 用户点停止之后,测速代码才送出阶段与进度回调,模拟排在停止之后的过期回调
await gate.wait()
stage(.download)
progress(50_000_000, 0)
return BroadbandResult()
}
controller.runBroadband()
await gate.waitUntilStarted()
controller.runBroadband() // 第二次点击即停止
#expect(!controller.isTestingBroadband)
await gate.release()
try? await Task.sleep(for: .milliseconds(100))
#expect(controller.broadbandStage == nil, "stale stage callback re-entered the testing state")
#expect(controller.liveBitsPerSecond == 0)
}
@Test func completedBroadbandIgnoresLateStageUpdates() async {
let suite = "SpeedTestControllerTests.done.\(UUID())"
let defaults = UserDefaults(suiteName: suite)!
defer { defaults.removePersistentDomain(forName: suite) }
let controller = SpeedTestController(settings: AppSettings(defaults: defaults)) { _, _, stage, _ in
// 结果写入之后才到达的阶段回调
Task.detached {
try? await Task.sleep(for: .milliseconds(50))
stage(.upload)
}
return BroadbandResult()
}
controller.runBroadband()
// resolvePath 会读取真实代理环境,按时间等待结果写入
for _ in 0..<200 where controller.broadbandDate == nil { try? await Task.sleep(for: .milliseconds(10)) }
try? await Task.sleep(for: .milliseconds(200))
#expect(controller.broadbandDate != nil)
#expect(!controller.isTestingBroadband, "late stage callback revived a finished test")
}
}