Viewing is open to everyone. Downloading and starring need a GitHub sign-in.
Sign in with GitHub
CKeyTests.swift
v0.1.0-build.85
Latest
Commit ede38f5
Sign in with GitHub to download. Each download is recorded with your GitHub username.
| 1 | import CKey |
| 2 | import CoreGraphics |
| 3 | import CoreVideo |
| 4 | import XCTest |
| 5 | |
| 6 | final class CKeyRoundTripTests: XCTestCase { |
| 7 | func testEncodeRenderDecodeText() throws { |
| 8 | let symbol = try CKeySymbol.encode("Hello, CKey") |
| 9 | XCTAssertGreaterThanOrEqual(symbol.version, 1) |
| 10 | XCTAssertEqual(symbol.level, .m) |
| 11 | XCTAssertEqual(symbol.layout, .standard) |
| 12 | XCTAssertEqual(symbol.payloadLength, 11) |
| 13 | |
| 14 | let image = try symbol.cgImage(size: 512) |
| 15 | XCTAssertEqual(image.width, 512) |
| 16 | XCTAssertEqual(image.height, 512) |
| 17 | let result = try XCTUnwrap(CKeyDecoder.decode(cgImage: image)) |
| 18 | XCTAssertEqual(result.bytes, Data("Hello, CKey".utf8)) |
| 19 | XCTAssertEqual(result.text, "Hello, CKey") |
| 20 | XCTAssertEqual(result.profile, .scalable) |
| 21 | XCTAssertEqual(result.version, symbol.version) |
| 22 | XCTAssertEqual(result.layout, .standard) |
| 23 | XCTAssertEqual(result.telemetryProfileCode, 0) |
| 24 | } |
| 25 | |
| 26 | func testTransparentRenderingDecodesOverWhite() throws { |
| 27 | let symbol = try CKeySymbol.encode("https://openckey.org") |
| 28 | let image = try symbol.cgImage(size: 400, transparent: true) |
| 29 | XCTAssertEqual(image.alphaInfo, .last) |
| 30 | let result = try XCTUnwrap(CKeyDecoder.decode(cgImage: image)) |
| 31 | XCTAssertEqual(result.text, "https://openckey.org") |
| 32 | } |
| 33 | |
| 34 | func testEveryPolicyAndPrintLayoutRoundTrip() throws { |
| 35 | for layout in CKeyLayout.allCases { |
| 36 | for policy in CKeyRasterPolicy.allCases { |
| 37 | let symbol = try CKeySymbol.encode("CKey \(layout) \(policy)", level: .q, layout: layout) |
| 38 | let image = try symbol.cgImage(size: 600, policy: policy) |
| 39 | let result = try XCTUnwrap(CKeyDecoder.decode(cgImage: image), "\(layout) \(policy)") |
| 40 | XCTAssertEqual(result.text, "CKey \(layout) \(policy)") |
| 41 | XCTAssertEqual(result.layout, layout) |
| 42 | } |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | func testBinaryPayloadRoundTripThroughPixelBuffer() throws { |
| 47 | let payload = Data((0..<37).map { UInt8(($0 * 37 + 11) & 0xff) } + [0, 1, 2]) |
| 48 | let symbol = try CKeySymbol.encode(payload, version: 6, level: .h) |
| 49 | XCTAssertEqual(symbol.version, 6) |
| 50 | let pixels = try symbol.pixels(size: 640) |
| 51 | |
| 52 | var buffer: CVPixelBuffer? |
| 53 | XCTAssertEqual(CVPixelBufferCreate(nil, pixels.width, pixels.height, kCVPixelFormatType_32BGRA, nil, &buffer), |
| 54 | kCVReturnSuccess) |
| 55 | let pixelBuffer = try XCTUnwrap(buffer) |
| 56 | CVPixelBufferLockBaseAddress(pixelBuffer, []) |
| 57 | let base = try XCTUnwrap(CVPixelBufferGetBaseAddress(pixelBuffer)).assumingMemoryBound(to: UInt8.self) |
| 58 | let rowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer) |
| 59 | pixels.rgba.withUnsafeBytes { raw in |
| 60 | let rgba = raw.bindMemory(to: UInt8.self) |
| 61 | for y in 0..<pixels.height { |
| 62 | for x in 0..<pixels.width { |
| 63 | let s = (y * pixels.width + x) * 4 |
| 64 | let d = base + y * rowBytes + x * 4 |
| 65 | d[0] = rgba[s + 2]; d[1] = rgba[s + 1]; d[2] = rgba[s]; d[3] = rgba[s + 3] |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | CVPixelBufferUnlockBaseAddress(pixelBuffer, []) |
| 70 | |
| 71 | let result = try XCTUnwrap(CKeyDecoder.decode(pixelBuffer: pixelBuffer)) |
| 72 | XCTAssertEqual(result.bytes, payload) |
| 73 | XCTAssertNil(result.text, "binary payloads have no text") |
| 74 | XCTAssertEqual(result.version, 6) |
| 75 | } |
| 76 | |
| 77 | func testBlankImageDoesNotDecode() throws { |
| 78 | let symbol = try CKeySymbol.encode("x") |
| 79 | // A plain white image has no border candidates. |
| 80 | let space = CGColorSpaceCreateDeviceGray() |
| 81 | let context = try XCTUnwrap(CGContext(data: nil, width: 300, height: 300, bitsPerComponent: 8, bytesPerRow: 300, |
| 82 | space: space, bitmapInfo: CGImageAlphaInfo.none.rawValue)) |
| 83 | context.setFillColor(gray: 1, alpha: 1) |
| 84 | context.fill(CGRect(x: 0, y: 0, width: 300, height: 300)) |
| 85 | XCTAssertNil(CKeyDecoder.decode(cgImage: try XCTUnwrap(context.makeImage()))) |
| 86 | XCTAssertNotNil(CKeyDecoder.decode(cgImage: try symbol.cgImage(size: 300))) |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | final class CKeyExportTests: XCTestCase { |
| 91 | func testPNGHeader() throws { |
| 92 | let png = try CKeySymbol.encode("PNG").pngData(size: 256, transparent: true, dpi: 300) |
| 93 | XCTAssertGreaterThan(png.count, 64) |
| 94 | XCTAssertEqual(Array(png.prefix(8)), [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) |
| 95 | XCTAssertEqual(Array(png[12..<16]), Array("IHDR".utf8)) |
| 96 | } |
| 97 | |
| 98 | func testPDFHeaderAndTrailer() throws { |
| 99 | let pdf = try CKeySymbol.encode("PDF").pdfData(widthPoints: 144) |
| 100 | XCTAssertEqual(Array(pdf.prefix(5)), Array("%PDF-".utf8)) |
| 101 | let tail = String(decoding: pdf.suffix(32), as: UTF8.self) |
| 102 | XCTAssertTrue(tail.contains("%%EOF"), tail) |
| 103 | } |
| 104 | |
| 105 | func testSVG() throws { |
| 106 | let svg = try CKeySymbol.encode("SVG").svg(scale: 8, style: .concept) |
| 107 | XCTAssertTrue(svg.contains("<svg")) |
| 108 | XCTAssertTrue(svg.hasSuffix(">") || svg.hasSuffix(">\n")) |
| 109 | XCTAssertFalse(svg.contains("\u{0}")) |
| 110 | } |
| 111 | |
| 112 | func testTransparentAlpha() throws { |
| 113 | let symbol = try CKeySymbol.encode("alpha") |
| 114 | let transparent = try symbol.pixels(size: 128, transparent: true) |
| 115 | XCTAssertEqual(transparent.rgba.count, 128 * 128 * 4) |
| 116 | XCTAssertTrue(transparent.hasAlpha) |
| 117 | XCTAssertEqual(transparent.rgba[3], 0, "the quiet-margin corner is unpainted") |
| 118 | XCTAssertTrue(stride(from: 3, to: transparent.rgba.count, by: 4).contains { transparent.rgba[$0] == 255 }, |
| 119 | "ink is opaque") |
| 120 | |
| 121 | let opaque = try symbol.pixels(size: 128, transparent: false) |
| 122 | XCTAssertFalse(opaque.hasAlpha) |
| 123 | XCTAssertTrue(stride(from: 3, to: opaque.rgba.count, by: 4).allSatisfy { opaque.rgba[$0] == 255 }) |
| 124 | |
| 125 | let image = try symbol.cgImage(size: 128, transparent: true) |
| 126 | let data = try XCTUnwrap(image.dataProvider?.data) as Data |
| 127 | XCTAssertEqual(data[3], 0) |
| 128 | } |
| 129 | |
| 130 | func testStylesAndCapacity() throws { |
| 131 | XCTAssertEqual(CKeyStyle(), CKeyStyle.plain) |
| 132 | XCTAssertEqual(CKeyStyle.plain.ink, .black) |
| 133 | XCTAssertEqual(CKeyStyle.plain.policy, .strict) |
| 134 | XCTAssertNotEqual(CKeyStyle.concept, CKeyStyle.plain) |
| 135 | |
| 136 | let symbol = try CKeySymbol.encode("style") |
| 137 | XCTAssertNoThrow(try symbol.check(style: .plain)) |
| 138 | XCTAssertNoThrow(try symbol.cgImage(size: 256, style: .concept)) |
| 139 | |
| 140 | var unreadable = CKeyStyle.plain |
| 141 | unreadable.ink = CKeyColor(red: 250, green: 250, blue: 250) |
| 142 | unreadable.inkShade = unreadable.ink |
| 143 | XCTAssertThrowsError(try symbol.check(style: unreadable)) |
| 144 | XCTAssertThrowsError(try symbol.cgImage(size: 256, style: unreadable), "strict policy refuses") |
| 145 | unreadable.policy = .override |
| 146 | XCTAssertNoThrow(try symbol.cgImage(size: 256, style: unreadable), "override draws as given") |
| 147 | |
| 148 | XCTAssertGreaterThan(CKeySymbol.capacity(version: 1), 0) |
| 149 | XCTAssertEqual(CKeySymbol.capacity(version: 0), 0) |
| 150 | XCTAssertThrowsError(try CKeySymbol.encode("x", version: 32)) |
| 151 | let tooLong = String(repeating: "A", count: CKeySymbol.capacity(version: 1) + 1) |
| 152 | XCTAssertThrowsError(try CKeySymbol.encode(tooLong, version: 1)) { error in |
| 153 | XCTAssertTrue(error is CKeyError) |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | func testCertification() throws { |
| 158 | let symbol = try CKeySymbol.encode("certify") |
| 159 | let plain = try symbol.certify() |
| 160 | XCTAssertTrue(plain.accepted, plain.message) |
| 161 | XCTAssertTrue(plain.certified, plain.message) |
| 162 | |
| 163 | let unknownBackground = try symbol.certify(transparent: true) |
| 164 | XCTAssertFalse(unknownBackground.certified, "a transparent symbol over an unknown background is never certified") |
| 165 | |
| 166 | let onWhite = try symbol.certify(transparent: true, backdrops: [.white]) |
| 167 | XCTAssertTrue(onWhite.certified, onWhite.message) |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | final class CKeyTelemetryTests: XCTestCase { |
| 172 | override func tearDown() { |
| 173 | CKey.telemetry = .on |
| 174 | super.tearDown() |
| 175 | } |
| 176 | |
| 177 | func testDefaultAndValidation() throws { |
| 178 | XCTAssertEqual(CKeyTelemetryMode.on.rawValue, 1) |
| 179 | CKey.telemetry = .local |
| 180 | for invalid in [-1, 3, 42, Int.max] { |
| 181 | XCTAssertThrowsError(try CKey.setTelemetry(rawValue: invalid)) { error in |
| 182 | XCTAssertEqual((error as? CKeyError)?.code, .invalidConfiguration) |
| 183 | } |
| 184 | XCTAssertEqual(CKey.telemetry, .local, "an invalid value leaves the mode unchanged") |
| 185 | } |
| 186 | try CKey.setTelemetry(rawValue: 0) |
| 187 | XCTAssertEqual(CKey.telemetry, .off) |
| 188 | try CKey.setTelemetry(rawValue: 1) |
| 189 | XCTAssertEqual(CKey.telemetry, .on) |
| 190 | CKey.telemetry = 2 |
| 191 | XCTAssertEqual(CKey.telemetry, .local) |
| 192 | CKey.telemetry = 0 |
| 193 | XCTAssertEqual(CKey.telemetry, .off) |
| 194 | XCTAssertNil(CKeyTelemetryMode(rawValue: 5)) |
| 195 | } |
| 196 | |
| 197 | func testLocalModeCountsSessionsAndOffPurges() throws { |
| 198 | CKey.telemetry = .off // purge |
| 199 | CKey.telemetry = .local // count from zero, never send |
| 200 | let session = CKeyTelemetrySession.shared |
| 201 | let symbol = try CKeySymbol.encode("telemetry") |
| 202 | let result = try XCTUnwrap(CKeyDecoder.decode(cgImage: try symbol.cgImage(size: 400))) |
| 203 | |
| 204 | session.scanSessionDidBegin() |
| 205 | session.scanFrameProcessed(milliseconds: 12) |
| 206 | session.scanSessionDidDecode(result) |
| 207 | session.scanSessionDidEnd() |
| 208 | session.scanSessionDidBegin() |
| 209 | session.scanSessionDidEnd() // no decode: fails |
| 210 | |
| 211 | let counters = CKey.telemetryCounters |
| 212 | XCTAssertEqual(counters.sessionsStarted, 2) |
| 213 | XCTAssertEqual(counters.sessionsSucceeded, 1) |
| 214 | XCTAssertEqual(counters.sessionsFailed, 1) |
| 215 | XCTAssertEqual(counters.frames, 1) |
| 216 | XCTAssertEqual(counters.decoded["p0"], 1) |
| 217 | XCTAssertEqual(counters.timeToDecodeMilliseconds.count, 1) |
| 218 | XCTAssertEqual(counters.frameMilliseconds.buckets.count, 10) |
| 219 | XCTAssertEqual(counters.frameMilliseconds.buckets.reduce(0, +), counters.frameMilliseconds.count) |
| 220 | |
| 221 | CKey.telemetry = .off |
| 222 | let purged = CKey.telemetryCounters |
| 223 | XCTAssertEqual(purged.sessionsStarted, 0) |
| 224 | XCTAssertEqual(purged.frames, 0) |
| 225 | session.scanSessionDidBegin() // mode 0: a no-op |
| 226 | XCTAssertEqual(CKey.telemetryCounters.sessionsStarted, 0) |
| 227 | } |
| 228 | |
| 229 | func testVersion() { |
| 230 | XCTAssertTrue(CKey.version.hasPrefix("0."), CKey.version) |
| 231 | XCTAssertEqual(CKeyCoreInfo.softwareVersion, CKey.version) |
| 232 | } |
| 233 | } |