引入进程级 CameraTetheringSession 与共享 USB Browser;离开传图页仅取消 UI 回调,App 终止时再完整 shutdown 释放 PTP 与 Browser 资源。 Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
4.1 KiB
Swift
86 lines
4.1 KiB
Swift
//
|
|
// CanonPTPCommandsTests.swift
|
|
// suixinkanTests
|
|
//
|
|
|
|
import XCTest
|
|
@testable import suixinkan
|
|
|
|
/// Canon PTP 命令与事件解析测试。
|
|
final class CanonPTPCommandsTests: XCTestCase {
|
|
/// 测试 SetRemoteMode 命令包 opcode。
|
|
func testBuildSetRemoteModeCommand() {
|
|
let data = PTPHelper.buildCommand(
|
|
code: CanonPTPCommand.eosSetRemoteMode,
|
|
transactionID: 1,
|
|
parameters: [1]
|
|
)
|
|
|
|
XCTAssertEqual(data.count, 16)
|
|
XCTAssertEqual(data[6], 0x14)
|
|
XCTAssertEqual(data[7], 0x91)
|
|
}
|
|
|
|
/// 测试 ObjectAddedEx (0xC181) 事件解析。
|
|
func testParseObjectAddedExEvent() {
|
|
var blob = Data()
|
|
let recordSize: UInt32 = 8 + 40
|
|
blob.append(contentsOf: withUnsafeBytes(of: recordSize.littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: CanonPTPCommand.eosObjectAddedEx.littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0x1234).littleEndian) { Data($0) }) // handle
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0x00010001).littleEndian) { Data($0) }) // storage
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0x3801).littleEndian) { Data($0) }) // ofc
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0).littleEndian) { Data($0) }) // flags
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0).littleEndian) { Data($0) }) // parent
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0).littleEndian) { Data($0) }) // reserved
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(1024).littleEndian) { Data($0) }) // size
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0).littleEndian) { Data($0) }) // reserved
|
|
let filename = "IMG_0001.JPG"
|
|
blob.append(filename.data(using: .ascii)!)
|
|
blob.append(0)
|
|
|
|
let events = CanonEosEventParser.parse(blob)
|
|
XCTAssertEqual(events.count, 1)
|
|
guard case .objectAdded(let added) = events[0] else {
|
|
return XCTFail("expected objectAdded")
|
|
}
|
|
XCTAssertEqual(added.objectHandle, 0x1234)
|
|
XCTAssertEqual(added.filename, "IMG_0001.JPG")
|
|
XCTAssertEqual(added.size, 1024)
|
|
}
|
|
|
|
/// 测试 ObjectAddedEx2 (0xC1A7) 大文件 size 解析。
|
|
func testParseObjectAddedEx2Event() {
|
|
var blob = Data()
|
|
let recordSize: UInt32 = 8 + 48
|
|
blob.append(contentsOf: withUnsafeBytes(of: recordSize.littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: CanonPTPCommand.eosObjectAddedEx2.littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0x5678).littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0x00010001).littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0x3801).littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0).littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0).littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(512).littleEndian) { Data($0) }) // sizeLow
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(1).littleEndian) { Data($0) }) // sizeHigh
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0).littleEndian) { Data($0) })
|
|
blob.append(contentsOf: withUnsafeBytes(of: UInt32(0).littleEndian) { Data($0) })
|
|
blob.append("LARGE.JPG".data(using: .ascii)!)
|
|
blob.append(0)
|
|
|
|
let events = CanonEosEventParser.parse(blob)
|
|
XCTAssertEqual(events.count, 1)
|
|
guard case .objectAdded(let added) = events[0] else {
|
|
return XCTFail("expected objectAdded")
|
|
}
|
|
XCTAssertEqual(added.objectHandle, 0x5678)
|
|
XCTAssertEqual(added.size, 512 | (1 << 32))
|
|
}
|
|
|
|
/// 测试支持的文件名扩展名过滤。
|
|
func testSupportedFilenameExtensions() {
|
|
XCTAssertTrue(CanonPTPHelper.isSupportedFilename("photo.JPG"))
|
|
XCTAssertTrue(CanonPTPHelper.isSupportedFilename("photo.cr2"))
|
|
XCTAssertFalse(CanonPTPHelper.isSupportedFilename("photo.tif"))
|
|
}
|
|
}
|