import ClawdbotKit import CoreGraphics import ImageIO import Testing import UniformTypeIdentifiers @Suite struct JPEGTranscoderTests { private func makeSolidJPEG(width: Int, height: Int, orientation: Int? = nil) throws -> Data { let cs = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue guard let ctx = CGContext( data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: cs, bitmapInfo: bitmapInfo) else { throw NSError(domain: "JPEGTranscoderTests", code: 1) } ctx.setFillColor(red: 1, green: 0, blue: 0, alpha: 1) ctx.fill(CGRect(x: 0, y: 0, width: width, height: height)) guard let img = ctx.makeImage() else { throw NSError(domain: "JPEGTranscoderTests", code: 5) } let out = NSMutableData() guard let dest = CGImageDestinationCreateWithData(out, UTType.jpeg.identifier as CFString, 1, nil) else { throw NSError(domain: "JPEGTranscoderTests", code: 2) } var props: [CFString: Any] = [ kCGImageDestinationLossyCompressionQuality: 1.0, ] if let orientation { props[kCGImagePropertyOrientation] = orientation } CGImageDestinationAddImage(dest, img, props as CFDictionary) guard CGImageDestinationFinalize(dest) else { throw NSError(domain: "JPEGTranscoderTests", code: 3) } return out as Data } private func makeNoiseJPEG(width: Int, height: Int) throws -> Data { let bytesPerPixel = 4 let byteCount = width * height * bytesPerPixel var data = Data(count: byteCount) let cs = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue let out = try data.withUnsafeMutableBytes { rawBuffer -> Data in guard let base = rawBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { throw NSError(domain: "JPEGTranscoderTests", code: 6) } for idx in 0.. 0) } @Test func doesNotUpscaleWhenSmallerThanMaxWidthPx() throws { let input = try makeSolidJPEG(width: 800, height: 600) let out = try JPEGTranscoder.transcodeToJPEG(imageData: input, maxWidthPx: 1600, quality: 0.9) #expect(out.widthPx == 800) #expect(out.heightPx == 600) } @Test func normalizesOrientationAndUsesOrientedWidthForMaxWidthPx() throws { // Encode a landscape image but mark it rotated 90° (orientation 6). Oriented width becomes 1000. let input = try makeSolidJPEG(width: 2000, height: 1000, orientation: 6) let out = try JPEGTranscoder.transcodeToJPEG(imageData: input, maxWidthPx: 1600, quality: 0.9) #expect(out.widthPx == 1000) #expect(out.heightPx == 2000) } @Test func respectsMaxBytes() throws { let input = try makeNoiseJPEG(width: 1600, height: 1200) let out = try JPEGTranscoder.transcodeToJPEG( imageData: input, maxWidthPx: 1600, quality: 0.95, maxBytes: 180_000) #expect(out.data.count <= 180_000) } }