TeraMediaBudgetTests.swift (9397B)
1 import Foundation 2 @testable import TeraApp 3 import UIKit 4 import XCTest 5 6 final class TeraMediaBudgetTests: XCTestCase { 7 func testStartupLimitsRejectZeroAndEveryMaximumPlusOne() throws { 8 let maximum = [8 * 1024 * 1024, 16_000_000, 1024, 32 * 1024 * 1024, 64, 2, 16, 4096] 9 func limits(_ values: [Int]) -> TeraMediaPresentationLimits? { 10 TeraMediaPresentationLimits(encodedBytes: values[0], sourcePixels: values[1], 11 thumbnailDimension: values[2], cacheBytes: values[3], cacheEntries: values[4], 12 workers: values[5], queuedRequests: values[6], visibilityEntries: values[7]) 13 } 14 XCTAssertEqual(limits(maximum), .standard) 15 for index in maximum.indices { 16 var invalid = maximum 17 invalid[index] += 1 18 XCTAssertNil(limits(invalid)) 19 invalid[index] = 0 20 XCTAssertNil(limits(invalid)) 21 } 22 let policy = try XCTUnwrap(limits(maximum)) 23 XCTAssertTrue(policy.accepts(byteCount: maximum[0], width: 4000, height: 4000)) 24 XCTAssertFalse(policy.accepts(byteCount: maximum[0] + 1, width: 4000, height: 4000)) 25 XCTAssertFalse(policy.accepts(byteCount: maximum[0], width: 16_000_001, height: 1)) 26 XCTAssertFalse(policy.accepts(byteCount: 1, width: Int.max, height: Int.max)) 27 } 28 29 @MainActor 30 func testDecodeUsesBoundedRasterOffUIActorAndRejectsCorruption() async throws { 31 let renderer = UIGraphicsImageRenderer(size: CGSize(width: 2048, height: 1024), format: { 32 let format = UIGraphicsImageRendererFormat() 33 format.scale = 1 34 return format 35 }()) 36 let bytes = renderer.pngData { context in 37 UIColor.green.setFill() 38 context.fill(CGRect(x: 0, y: 0, width: 2048, height: 1024)) 39 } 40 let artifact = try XCTUnwrap(TeraVerifiedMediaArtifact(artifactID: String(repeating: "a", count: 64), 41 bytes: bytes, byteSize: UInt64(bytes.count), mediaType: "image/png", width: 2048, height: 1024)) 42 let image = try await TeraMediaThumbnail.prepare(artifact, limits: .standard) 43 XCTAssertEqual(image.width, 1024) 44 XCTAssertEqual(image.height, 512) 45 do { 46 _ = try await TeraMediaThumbnail.prepare(TeraScopeFixtures.artifact("a", corrupt: true), limits: .standard) 47 XCTFail("Corrupt raster must fail") 48 } catch TeraMediaThumbnailFailure.corrupt {} 49 } 50 51 func testActualDecoderRejectsEncodedAndDeclaredPixelMaximumPlusOne() async throws { 52 let original = try TeraScopeFixtures.artifact("a") 53 for (bytes, width, height) in [(Data(repeating: 0, count: 8 * 1024 * 1024 + 1), UInt32(1), UInt32(1)), 54 (original.bytes, UInt32(16_000_001), UInt32(1))] 55 { 56 let artifact = try XCTUnwrap(TeraVerifiedMediaArtifact(artifactID: original.artifactID, 57 bytes: bytes, byteSize: UInt64(bytes.count), mediaType: "image/png", width: width, height: height)) 58 do { 59 _ = try await TeraMediaThumbnail.prepare(artifact, limits: .standard) 60 XCTFail("Resource admission must precede raster decoding") 61 } catch TeraMediaThumbnailFailure.resourceLimit {} 62 } 63 } 64 65 @MainActor 66 func testQueueMaxPlusOneAndCancellationKeepPhysicalWorkersBounded() async { 67 let queue = TeraMediaWorkQueue(limits: .standard) 68 let firstPause = ResourceTestPause() 69 let secondPause = ResourceTestPause() 70 let first = UUID() 71 var started = 0 72 XCTAssertTrue(queue.submit(id: first) { started += 1; await firstPause.wait() }) 73 XCTAssertTrue(queue.submit(id: UUID()) { started += 1; await secondPause.wait() }) 74 for _ in 0 ..< 16 { 75 XCTAssertTrue(queue.submit(id: UUID()) { started += 1 }) 76 } 77 await firstPause.entered.wait() 78 await secondPause.entered.wait() 79 XCTAssertEqual(started, 2) 80 XCTAssertEqual(queue.activeCount, 2) 81 XCTAssertEqual(queue.queuedCount, 16) 82 XCTAssertFalse(queue.submit(id: UUID()) {}) 83 queue.cancel(id: first) 84 XCTAssertEqual(queue.activeCount, 2) 85 XCTAssertFalse(queue.submit(id: UUID()) {}) 86 queue.cancelAll() 87 XCTAssertEqual(queue.activeCount, 2) 88 XCTAssertEqual(queue.queuedCount, 0) 89 await firstPause.resume.open() 90 await secondPause.resume.open() 91 await TeraScopeFixtures.eventually { queue.activeCount == 0 } 92 XCTAssertEqual(started, 2) 93 } 94 95 @MainActor 96 func testPresentationCacheEvictsOnlyTransientBytesAtCapacity() async throws { 97 let backend = try TeraScopeBackend() 98 let client = try await TeraScopeFixtures.client(backend) 99 let policy = try XCTUnwrap(TeraMediaPresentationLimits(encodedBytes: 1024, sourcePixels: 16_000_000, 100 thumbnailDimension: 1024, cacheBytes: 200, cacheEntries: 3, workers: 1, queuedRequests: 1, 101 visibilityEntries: 4096)) 102 let store = TeraMediaStore(runtimeClient: client, limits: policy) 103 let context = TeraLocalNetwork.defaultContext(snapshot: TeraScopeFixtures.snapshot()) 104 for index in 0 ..< 4 { 105 var reference = TeraScopeFixtures.reference() 106 reference = TeraMediaReference(referenceFingerprint: String(repeating: String(index), count: 64), 107 url: reference.url, sha256: reference.sha256, mediaType: reference.mediaType, 108 width: reference.width, height: reference.height, byteSize: reference.byteSize, 109 alt: reference.alt, verification: reference.verification) 110 store.load(media: reference, context: context) 111 await TeraScopeFixtures.eventually { store.state(for: reference, context: context) != .loading } 112 XCTAssertLessThanOrEqual(store.stateCount, policy.cacheEntries) 113 XCTAssertLessThanOrEqual(store.cachedByteCount, policy.cacheBytes) 114 XCTAssertGreaterThan(store.cachedByteCount, 0) 115 } 116 let invalidations = await backend.counts[.invalidate] 117 XCTAssertNil(invalidations) 118 store.reset() 119 XCTAssertEqual(store.cachedByteCount, 0) 120 _ = try await client.stop() 121 } 122 123 @MainActor 124 func testVisibilityCapacityFailsClosedAndExplicitCurrentReferencesCanReturn() async throws { 125 let backend = try TeraScopeBackend() 126 let client = try await TeraScopeFixtures.client(backend) 127 let limits = try XCTUnwrap(TeraMediaPresentationLimits(encodedBytes: 1024, 128 sourcePixels: 16_000_000, thumbnailDimension: 1024, cacheBytes: 1024, 129 cacheEntries: 64, workers: 2, queuedRequests: 16, visibilityEntries: 2)) 130 let store = TeraMediaStore(runtimeClient: client, limits: limits) 131 let context = TeraLocalNetwork.defaultContext(snapshot: TeraScopeFixtures.snapshot()) 132 let references = (0 ..< 3).map { reference($0, verification: .unavailable) } 133 store.reconcileVisibility(previous: Array(references.prefix(2)), current: [], context: context) 134 store.reconcileVisibility(previous: [references[0]], current: [], context: context) 135 let artifact = try TeraScopeFixtures.artifact("a") 136 store.load(media: references[2], context: context) 137 await TeraScopeFixtures.eventually { store.state(for: references[2], context: context) == .ready(artifact) } 138 store.reconcileVisibility(previous: [references[2]], current: [], context: context) 139 for value in references { 140 store.load(media: value, context: context) 141 store.retry(media: value, context: context) 142 XCTAssertEqual(store.state(for: value, context: context), .unavailable) 143 } 144 let calls = await backend.counts[.media] 145 XCTAssertEqual(calls, 1) 146 store.reconcileVisibility(previous: [], current: [references[2]], context: context) 147 store.load(media: references[2], context: context) 148 await TeraScopeFixtures.eventually { store.state(for: references[2], context: context) == .ready(artifact) } 149 store.retry(media: references[0], context: context) 150 let finalCalls = await backend.counts[.media] 151 XCTAssertEqual(finalCalls, 2) 152 store.reset() 153 _ = try await client.stop() 154 } 155 156 @MainActor 157 func testPendingStateEntryMaximumPlusOneIsBounded() async throws { 158 let backend = try TeraScopeBackend() 159 let client = try await TeraScopeFixtures.client(backend) 160 let store = TeraMediaStore(runtimeClient: client) 161 let context = TeraLocalNetwork.defaultContext(snapshot: TeraScopeFixtures.snapshot()) 162 for index in 0 ..< 64 { 163 store.load(media: reference(index, verification: .pending), context: context) 164 } 165 XCTAssertEqual(store.stateCount, 64) 166 store.load(media: reference(64, verification: .pending), context: context) 167 XCTAssertEqual(store.stateCount, 64) 168 let calls = await backend.counts[.media] 169 XCTAssertNil(calls) 170 store.reset() 171 _ = try await client.stop() 172 } 173 174 private func reference(_ index: Int, verification: TeraMediaVerificationState) -> TeraMediaReference { 175 let original = TeraScopeFixtures.reference() 176 return TeraMediaReference(referenceFingerprint: String(format: "%064x", index), 177 url: original.url, sha256: original.sha256, mediaType: original.mediaType, 178 width: original.width, height: original.height, byteSize: original.byteSize, 179 alt: original.alt, verification: verification) 180 } 181 }