11import AppKit
2+ import ImageIO
23import XCTest
34@testable import OpenComputerUseKit
45
@@ -136,6 +137,29 @@ final class OpenComputerUseKitTests: XCTestCase {
136137 XCTAssertEqual ( resolvedOpenComputerUseVersion ( bundle: Bundle ( for: Self . self) ) , openComputerUseVersion)
137138 }
138139
140+ func testBoundedScreenshotPNGDataShrinksLargeScreenshots( ) throws {
141+ let image = try makeNoisyTestImage ( width: 800 , height: 600 )
142+ let data = try XCTUnwrap ( boundedScreenshotPNGData (
143+ for: image,
144+ maxBytes: 50_000 ,
145+ maxDimension: 320 ,
146+ minScale: 0.05
147+ ) )
148+ let size = try imageSize ( in: data)
149+
150+ XCTAssertLessThanOrEqual ( data. count, 50_000 )
151+ XCTAssertLessThanOrEqual ( max ( size. width, size. height) , 320 )
152+ }
153+
154+ func testBoundedScreenshotPNGDataKeepsSmallScreenshotsAtOriginalSize( ) throws {
155+ let image = try makeSolidTestImage ( width: 32 , height: 24 )
156+ let data = try XCTUnwrap ( boundedScreenshotPNGData ( for: image, maxBytes: 1_000_000 , maxDimension: 320 ) )
157+ let size = try imageSize ( in: data)
158+
159+ XCTAssertEqual ( size. width, 32 )
160+ XCTAssertEqual ( size. height, 24 )
161+ }
162+
139163 func testToolDefinitionCount( ) {
140164 XCTAssertEqual ( ToolDefinitions . all. count, 9 )
141165 }
@@ -436,7 +460,7 @@ final class OpenComputerUseKitTests: XCTestCase {
436460
437461 func testInitializeResponseContainsToolsCapability( ) throws {
438462 let server = StdioMCPServer ( service: ComputerUseService ( ) )
439- let response = server. handle ( line: #"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"test","version":"0.1.47 "},"capabilities":{}}}"# )
463+ let response = server. handle ( line: #"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"test","version":"0.1.48 "},"capabilities":{}}}"# )
440464 XCTAssertNotNil ( response)
441465 XCTAssertTrue ( response!. contains ( #""name":"open-computer-use""# ) )
442466 XCTAssertTrue ( response!. contains ( #""tools":{"listChanged":false}"# ) )
@@ -445,7 +469,7 @@ final class OpenComputerUseKitTests: XCTestCase {
445469 func testInitializeResponseContainsComputerUseInstructions( ) throws {
446470 let server = StdioMCPServer ( service: ComputerUseService ( ) )
447471 let response = try XCTUnwrap (
448- server. handle ( line: #"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"test","version":"0.1.47 "},"capabilities":{}}}"# )
472+ server. handle ( line: #"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"test","version":"0.1.48 "},"capabilities":{}}}"# )
449473 )
450474 let data = try XCTUnwrap ( response. data ( using: . utf8) )
451475 let json = try XCTUnwrap ( JSONSerialization . jsonObject ( with: data) as? [ String : Any ] )
@@ -1420,4 +1444,59 @@ final class OpenComputerUseKitTests: XCTestCase {
14201444 let length = max ( hypot ( dx, dy) , 0.001 )
14211445 return CGVector ( dx: dx / length, dy: dy / length)
14221446 }
1447+
1448+ private func makeNoisyTestImage( width: Int , height: Int ) throws -> CGImage {
1449+ var pixels = [ UInt8] ( repeating: 0 , count: width * height * 4 )
1450+ for y in 0 ..< height {
1451+ for x in 0 ..< width {
1452+ let offset = ( y * width + x) * 4
1453+ pixels [ offset] = UInt8 ( ( x * 37 + y * 17 ) & 0xFF )
1454+ pixels [ offset + 1 ] = UInt8 ( ( x * 11 + y * 43 ) & 0xFF )
1455+ pixels [ offset + 2 ] = UInt8 ( ( x * 71 + y * 5 ) & 0xFF )
1456+ pixels [ offset + 3 ] = 255
1457+ }
1458+ }
1459+
1460+ return try makeTestImage ( width: width, height: height, pixels: pixels)
1461+ }
1462+
1463+ private func makeSolidTestImage( width: Int , height: Int ) throws -> CGImage {
1464+ var pixels = [ UInt8] ( repeating: 0 , count: width * height * 4 )
1465+ for index in stride ( from: 0 , to: pixels. count, by: 4 ) {
1466+ pixels [ index] = 80
1467+ pixels [ index + 1 ] = 140
1468+ pixels [ index + 2 ] = 220
1469+ pixels [ index + 3 ] = 255
1470+ }
1471+
1472+ return try makeTestImage ( width: width, height: height, pixels: pixels)
1473+ }
1474+
1475+ private func makeTestImage( width: Int , height: Int , pixels: [ UInt8 ] ) throws -> CGImage {
1476+ let data = Data ( pixels)
1477+ let provider = try XCTUnwrap ( CGDataProvider ( data: data as CFData ) )
1478+ let image = CGImage (
1479+ width: width,
1480+ height: height,
1481+ bitsPerComponent: 8 ,
1482+ bitsPerPixel: 32 ,
1483+ bytesPerRow: width * 4 ,
1484+ space: CGColorSpaceCreateDeviceRGB ( ) ,
1485+ bitmapInfo: CGBitmapInfo ( rawValue: CGImageAlphaInfo . premultipliedLast. rawValue) ,
1486+ provider: provider,
1487+ decode: nil ,
1488+ shouldInterpolate: false ,
1489+ intent: . defaultIntent
1490+ )
1491+
1492+ return try XCTUnwrap ( image)
1493+ }
1494+
1495+ private func imageSize( in data: Data ) throws -> ( width: Int , height: Int ) {
1496+ let source = try XCTUnwrap ( CGImageSourceCreateWithData ( data as CFData , nil ) )
1497+ let properties = try XCTUnwrap ( CGImageSourceCopyPropertiesAtIndex ( source, 0 , nil ) as? [ CFString : Any ] )
1498+ let width = try XCTUnwrap ( properties [ kCGImagePropertyPixelWidth] as? Int )
1499+ let height = try XCTUnwrap ( properties [ kCGImagePropertyPixelHeight] as? Int )
1500+ return ( width, height)
1501+ }
14231502}
0 commit comments