Files
openclaw/apps/shared/OpenClawKit/Tests/OpenClawKitTests/JPEGTranscoderTests.swift
lin-hongkuan 522e253c53 fix(ios): resize oversized shared images (#103860)
* fix(ios): resize oversized shared images

* fix(ios): satisfy SwiftFormat await rules

* fix(ios): integrate share image processing

* fix(ios): type share attachment failures

---------

Co-authored-by: lin-hongkuan <lin-hongkuan@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-11 15:16:45 -07:00

148 lines
5.4 KiB
Swift

import OpenClawKit
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..<byteCount {
base[idx] = UInt8.random(in: 0...255)
}
guard
let ctx = CGContext(
data: base,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: width * bytesPerPixel,
space: cs,
bitmapInfo: bitmapInfo)
else {
throw NSError(domain: "JPEGTranscoderTests", code: 7)
}
guard let img = ctx.makeImage() else {
throw NSError(domain: "JPEGTranscoderTests", code: 8)
}
let encoded = NSMutableData()
guard let dest = CGImageDestinationCreateWithData(encoded, UTType.jpeg.identifier as CFString, 1, nil)
else {
throw NSError(domain: "JPEGTranscoderTests", code: 9)
}
CGImageDestinationAddImage(dest, img, nil)
guard CGImageDestinationFinalize(dest) else {
throw NSError(domain: "JPEGTranscoderTests", code: 10)
}
return encoded as Data
}
return out
}
@Test func downscalesToMaxWidthPx() throws {
let input = try makeSolidJPEG(width: 2000, height: 1000)
let out = try JPEGTranscoder.transcodeToJPEG(imageData: input, maxWidthPx: 1600, quality: 0.9)
#expect(out.widthPx == 1600)
#expect(abs(out.heightPx - 800) <= 1)
#expect(out.data.count > 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)
}
@Test func explicitlyFailsWhenSizeLimitCannotBeMet() throws {
let input = try makeSolidJPEG(width: 800, height: 600)
do {
_ = try JPEGTranscoder.transcodeToJPEG(
imageData: input,
maxWidthPx: 800,
quality: 0.9,
maxBytes: 1)
Issue.record("Expected a size-limit error")
} catch JPEGTranscodeError.sizeLimitExceeded(let maxBytes, let actualBytes) {
#expect(maxBytes == 1)
#expect(actualBytes > maxBytes)
} catch {
Issue.record("Unexpected error: \(error)")
}
}
}