From 1f485bd6392b2ce4422d643f4a6be8b2db093d37 Mon Sep 17 00:00:00 2001 From: PathGao <42336971+PathGao@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:08:05 +0800 Subject: [PATCH] Fix 0.1.2 release checks and use small speed test fixtures --- .../Services/Metrics/SpeedTest.swift | 6 +++-- Tests/MetricsTests.swift | 4 +-- Tests/ProductSettingsTests.swift | 8 +++--- Tests/SpeedTestTests.swift | 27 ++++++++++++++++++- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Sources/Vorssaint/Services/Metrics/SpeedTest.swift b/Sources/Vorssaint/Services/Metrics/SpeedTest.swift index aeae040..35266b0 100644 --- a/Sources/Vorssaint/Services/Metrics/SpeedTest.swift +++ b/Sources/Vorssaint/Services/Metrics/SpeedTest.swift @@ -36,7 +36,7 @@ final class SpeedTest: NSObject, ObservableObject { // so request under that and loop chunks back-to-back until the time box — that // keeps a fast link's pipe full for a full measurement window. private let downloadBytes = 90_000_000 - private let uploadBytes = 100_000_000 + private let uploadBytes: Int private let queue = OperationQueue() private var session: URLSession! @@ -48,8 +48,10 @@ final class SpeedTest: NSObject, ObservableObject { private var generation = 0 private var stopWork: DispatchWorkItem? - init(configuration: URLSessionConfiguration = .ephemeral, sampleSeconds: TimeInterval = 5) { + init(configuration: URLSessionConfiguration = .ephemeral, sampleSeconds: TimeInterval = 5, + uploadBytes: Int = 100_000_000) { self.sampleSeconds = sampleSeconds + self.uploadBytes = uploadBytes super.init() queue.maxConcurrentOperationCount = 1 configuration.requestCachePolicy = .reloadIgnoringLocalCacheData diff --git a/Tests/MetricsTests.swift b/Tests/MetricsTests.swift index fe45f09..0cd6d95 100644 --- a/Tests/MetricsTests.swift +++ b/Tests/MetricsTests.swift @@ -3585,10 +3585,10 @@ UninstallerSelectionTests.run { expect($0, $1) } // decision above is made consciously, never by omission. let releasePlist = NSDictionary(contentsOfFile: "Resources/Info.plist") let plistVersion = (releasePlist?["CFBundleShortVersionString"] as? String) ?? "" - expect(plistVersion == "0.1.1", + expect(plistVersion == "0.1.2", "bumping the app version requires re-deciding the support prompt pin above") let plistBuild = (releasePlist?["CFBundleVersion"] as? String) ?? "" - expect(plistBuild == "2", + expect(plistBuild == "3", "every app version needs its own incremented bundle build") expect(!SupportUpdateIntroInfo.shouldShow(appVersion: plistVersion, lastSeenVersion: nil) && !UpdateHighlightsInfo.shouldShow(appVersion: plistVersion, lastSeenVersion: nil), diff --git a/Tests/ProductSettingsTests.swift b/Tests/ProductSettingsTests.swift index 218dbd4..6b28a56 100644 --- a/Tests/ProductSettingsTests.swift +++ b/Tests/ProductSettingsTests.swift @@ -5,7 +5,7 @@ enum ProductSettingsTests { static func run(_ expect: (Bool, String) -> Void) { let changelog = """ ## [Unreleased] - ## [0.1.1] - 2026-09-15 + ## [0.1.2] - 2026-09-16 ### Added - Input tuning and graph controls. ## [0.1.0] - 2026-09-11 @@ -17,7 +17,7 @@ enum ProductSettingsTests { """ let current = ReleaseNotes.current(changelog: changelog) expect(current.version == ProductIdentity.currentReleaseNotesVersion, "current notes use product identity") - expect(current.version == "0.1.1", "current kururu build selects the new release") + expect(current.version == "0.1.2", "current kururu build selects the new release") expect(current.sections.first?.bulletItems == ["Input tuning and graph controls."], "real parser selects the new product body") let localized = ["en", "zh-Hans", "de", "fr", "es", "ja"].map { ReleaseNotes.current(languageCode: $0, changelog: changelog) @@ -28,10 +28,10 @@ enum ProductSettingsTests { expect(localized.allSatisfy { !$0.sections.flatMap(\.bulletItems).joined().contains("Upstream historical scope") }, "current localized notes never show upstream history") let historical = ReleaseNotes.notes(for: "3.3.5", changelog: changelog) expect(historical.date == "2026-09-06" && historical.sections.first?.paragraphItems == ["Upstream historical scope."], "numbered history still parses") - expect(ReleaseNotes.allVersions(changelog: changelog) == ["0.1.1", "0.1.0", "3.3.5"], "product release precedes upstream history; unreleased is omitted") + expect(ReleaseNotes.allVersions(changelog: changelog) == ["0.1.2", "0.1.0", "3.3.5"], "product release precedes upstream history; unreleased is omitted") expect(ReleaseNotes.current(changelog: nil).sections.isEmpty, "missing product notes do not fall back to upstream") let labels = ["en", "zh-Hans", "de", "fr", "es", "ja"].map { current.versionLabel(languageCode: $0) } - expect(labels.allSatisfy { $0 == "v0.1.1 · 2026-09-15" }, "all locales show the product release version and date") + expect(labels.allSatisfy { $0 == "v0.1.2 · 2026-09-16" }, "all locales show the product release version and date") expect(historical.versionLabel(languageCode: "en") == "v3.3.5 · 2026-09-06", "published label retained") expect(!BuildCapabilityPolicy.allowsUpdates(configured: false, development: false), "unconfigured release cannot update") expect(!BuildCapabilityPolicy.allowsUpdates(configured: true, development: true), "development cannot update") diff --git a/Tests/SpeedTestTests.swift b/Tests/SpeedTestTests.swift index d0e050d..7b8bc33 100644 --- a/Tests/SpeedTestTests.swift +++ b/Tests/SpeedTestTests.swift @@ -17,7 +17,8 @@ enum SpeedTestTests { let configuration = URLSessionConfiguration.ephemeral configuration.protocolClasses = [SpeedTestProtocol.self] configuration.httpAdditionalHeaders = ["X-Test-Scenario": testCase.name] - let test = SpeedTest(configuration: configuration, sampleSeconds: 0.1) + // These cases test HTTP status handling, not a 100 MB upload benchmark. + let test = SpeedTest(configuration: configuration, sampleSeconds: 0.1, uploadBytes: 1_024) test.start() let deadline = Date().addingTimeInterval(3) var completed = false @@ -62,6 +63,10 @@ enum SpeedTestTests { expect(test.phase == phase, "speed test \(testCase.name) stays terminal after its time box") expect(SpeedTestProtocol.requests(for: testCase.name) == testCase.requests, "speed test \(testCase.name) stops requesting data at the failed phase") + if testCase.requests.contains("upload") { + expect(SpeedTestProtocol.uploadSize(for: testCase.name) == 1_024, + "speed test \(testCase.name) uses the small fixture upload body") + } test.cancel() } } @@ -70,6 +75,13 @@ enum SpeedTestTests { private final class SpeedTestProtocol: URLProtocol { private static let lock = NSLock() private static var recordedRequests: [String: [String]] = [:] + private static var uploadSizes: [String: Int] = [:] + + static func uploadSize(for scenario: String) -> Int? { + lock.lock() + defer { lock.unlock() } + return uploadSizes[scenario] + } static func requests(for scenario: String) -> [String] { lock.lock() @@ -84,7 +96,20 @@ private final class SpeedTestProtocol: URLProtocol { let scenario = request.value(forHTTPHeaderField: "X-Test-Scenario") ?? "" let url = request.url! let phase = url.path == "/__up" ? "upload" : url.query == "bytes=0" ? "latency" : "download" + var uploadSize = request.httpBody?.count ?? 0 + if phase == "upload", let stream = request.httpBodyStream { + stream.open() + defer { stream.close() } + // One byte beyond the fixture size detects an accidental real upload. + var buffer = [UInt8](repeating: 0, count: 1_025) + while uploadSize < buffer.count { + let count = stream.read(&buffer, maxLength: buffer.count - uploadSize) + guard count > 0 else { break } + uploadSize += count + } + } Self.lock.lock() + if phase == "upload" { Self.uploadSizes[scenario] = uploadSize } Self.recordedRequests[scenario, default: []].append(phase) let downloadCount = Self.recordedRequests[scenario, default: []].filter { $0 == "download" }.count Self.lock.unlock()