151 lines
5 KiB
Swift
151 lines
5 KiB
Swift
import XCTest
|
|
import Foundation
|
|
import ForgeCamera
|
|
import ForgeGrade
|
|
import ForgeColor
|
|
import ForgeSim
|
|
@testable import ForgeCameraRED
|
|
|
|
final class RedDriverTests: XCTestCase {
|
|
|
|
var sim: RedSimulator!
|
|
|
|
override func setUp() async throws {
|
|
sim = RedSimulator()
|
|
try await sim.start(port: 0)
|
|
}
|
|
|
|
override func tearDown() async throws {
|
|
try? await sim.stop()
|
|
sim = nil
|
|
}
|
|
|
|
func makeDriver(pollInterval: Duration = .milliseconds(20)) async -> RedDriver {
|
|
let port = await sim.boundPort!
|
|
return RedDriver(host: "127.0.0.1", port: port, pollInterval: pollInterval)
|
|
}
|
|
|
|
// Capabilities: RED = native CDL (IPP2), 17/33 LUTs.
|
|
func testCapabilities() async {
|
|
let driver = await makeDriver()
|
|
XCTAssertEqual(driver.capabilities.vendor, .red)
|
|
XCTAssertTrue(driver.capabilities.supportsNativeCDL)
|
|
XCTAssertTrue(driver.capabilities.lut3dSizes.contains(33))
|
|
}
|
|
|
|
// Connect performs RCP-style handshake, emits connected.
|
|
func testConnectHandshake() async throws {
|
|
let driver = await makeDriver()
|
|
var iterator = driver.state.makeAsyncIterator()
|
|
try await driver.connect()
|
|
var connected = false
|
|
for _ in 0..<5 {
|
|
if let s = await iterator.next(), s.connection == .connected {
|
|
connected = true
|
|
break
|
|
}
|
|
}
|
|
XCTAssertTrue(connected)
|
|
let handshakes = await sim.handshakeCount
|
|
XCTAssertEqual(handshakes, 1)
|
|
await driver.disconnect()
|
|
}
|
|
|
|
func testConnectFailsOnDeadPort() async {
|
|
let driver = RedDriver(host: "127.0.0.1", port: 1, pollInterval: .milliseconds(20))
|
|
do {
|
|
try await driver.connect()
|
|
XCTFail("expected throw")
|
|
} catch {}
|
|
}
|
|
|
|
// Rec state + clip name via param polling.
|
|
func testRecStateEvents() async throws {
|
|
let driver = await makeDriver()
|
|
var iterator = driver.state.makeAsyncIterator()
|
|
try await driver.connect()
|
|
|
|
await sim.setParam("RECORD_STATE", value: "1")
|
|
await sim.setParam("CLIP_NAME", value: "B001_C001_0710AB")
|
|
var sawRec = false
|
|
for _ in 0..<30 {
|
|
if let s = await iterator.next(), s.isRecording {
|
|
XCTAssertEqual(s.clipName, "B001_C001_0710AB")
|
|
sawRec = true
|
|
break
|
|
}
|
|
}
|
|
XCTAssertTrue(sawRec)
|
|
|
|
await sim.setParam("RECORD_STATE", value: "0")
|
|
var sawStop = false
|
|
for _ in 0..<30 {
|
|
if let s = await iterator.next(), !s.isRecording {
|
|
sawStop = true
|
|
break
|
|
}
|
|
}
|
|
XCTAssertTrue(sawStop)
|
|
await driver.disconnect()
|
|
}
|
|
|
|
// Metadata params surface.
|
|
func testMetadata() async throws {
|
|
await sim.setParam("ISO", value: "1600")
|
|
await sim.setParam("COLOR_TEMP", value: "4500")
|
|
await sim.setParam("TINT", value: "3")
|
|
await sim.setParam("SENSOR_FPS", value: "23.98")
|
|
await sim.setParam("TIMECODE", value: "15:30:00:00")
|
|
|
|
let driver = await makeDriver()
|
|
var iterator = driver.state.makeAsyncIterator()
|
|
try await driver.connect()
|
|
|
|
var got = false
|
|
for _ in 0..<30 {
|
|
if let s = await iterator.next(), let md = s.metadata, md.exposureIndex == 1600 {
|
|
XCTAssertEqual(md.whiteBalance, 4500)
|
|
XCTAssertEqual(md.tint, 3)
|
|
XCTAssertEqual(md.timecode, "15:30:00:00")
|
|
got = true
|
|
break
|
|
}
|
|
}
|
|
XCTAssertTrue(got)
|
|
await driver.disconnect()
|
|
}
|
|
|
|
// Push: CDL to IPP2 slots + LUT upload.
|
|
func testPushLookWithNativeCDL() async throws {
|
|
let driver = await makeDriver()
|
|
try await driver.connect()
|
|
|
|
let cdl = CDL(slope: SIMD3(1.1, 1.0, 0.9), offset: SIMD3(0.02, 0, 0), power: .one, saturation: 1.3)
|
|
let look = FlattenedLook(cdl: cdl, lut: Lut3D.identity(size: 33), latticeSize: 33)
|
|
try await driver.push(look: look)
|
|
|
|
let cdlValue = await sim.params["IPP2_CDL"]
|
|
XCTAssertNotNil(cdlValue)
|
|
let obj = try JSONSerialization.jsonObject(with: Data(cdlValue!.utf8)) as? [String: Any]
|
|
let slope = obj?["slope"] as? [Double]
|
|
XCTAssertEqual(slope?[0] ?? 0, 1.1, accuracy: 1e-5)
|
|
|
|
let luts = await sim.uploadedLuts
|
|
XCTAssertEqual(luts.count, 1)
|
|
XCTAssertEqual(luts[0].size, 33)
|
|
await driver.disconnect()
|
|
}
|
|
|
|
// Unsupported LUT size rejected.
|
|
func testUnsupportedLutRejected() async throws {
|
|
let driver = await makeDriver()
|
|
try await driver.connect()
|
|
do {
|
|
try await driver.push(look: FlattenedLook(cdl: nil, lut: Lut3D.identity(size: 65), latticeSize: 65))
|
|
XCTFail("expected unsupportedLook")
|
|
} catch let e as CameraError {
|
|
guard case .unsupportedLook = e else { return XCTFail("wrong error") }
|
|
}
|
|
await driver.disconnect()
|
|
}
|
|
}
|