import XCTest @testable import suixinkan /// 回归验证相机配置期间启动、快速关闭和重复启动的顺序。 final class QRCodeCaptureSessionControllerTests: XCTestCase { func testStartWaitsForConfigurationCommit() { let session = RecordingCaptureSession() let queue = DispatchQueue(label: "qr-capture-test") let controller = QRCodeCaptureSessionController(session: session, queue: queue) let entered = DispatchSemaphore(value: 0) let release = DispatchSemaphore(value: 0) let completed = expectation(description: "configuration committed") controller.configure({ entered.signal() _ = release.wait(timeout: .now() + 5) return true }, completion: { succeeded in XCTAssertTrue(Thread.isMainThread) XCTAssertTrue(succeeded) completed.fulfill() }) XCTAssertEqual(entered.wait(timeout: .now() + 5), .success) controller.setRunning(true) release.signal() queue.sync { XCTAssertEqual(session.events, ["begin", "commit", "start"]) XCTAssertFalse(session.startedDuringConfiguration) } wait(for: [completed], timeout: 5) } func testImmediateCloseStopsPendingStart() { let session = RecordingCaptureSession() let queue = DispatchQueue(label: "qr-capture-test") let controller = QRCodeCaptureSessionController(session: session, queue: queue) controller.configure({ true }, completion: { _ in }) controller.setRunning(true) controller.setRunning(false) queue.sync { XCTAssertEqual(session.events, ["begin", "commit", "start", "stop"]) XCTAssertFalse(session.isRunning) } } func testRepeatedStartAndStopAreIdempotent() { let session = RecordingCaptureSession() let queue = DispatchQueue(label: "qr-capture-test") let controller = QRCodeCaptureSessionController(session: session, queue: queue) controller.configure({ true }, completion: { _ in }) controller.setRunning(true) controller.setRunning(true) controller.setRunning(false) controller.setRunning(false) queue.sync { XCTAssertEqual(session.events, ["begin", "commit", "start", "stop"]) } } func testFailedConfigurationCommitsButDoesNotStart() { let session = RecordingCaptureSession() let queue = DispatchQueue(label: "qr-capture-test") let controller = QRCodeCaptureSessionController(session: session, queue: queue) let completed = expectation(description: "configuration failed") controller.configure({ false }, completion: { succeeded in XCTAssertFalse(succeeded) completed.fulfill() }) controller.setRunning(true) queue.sync { XCTAssertEqual(session.events, ["begin", "commit"]) XCTAssertFalse(session.isRunning) } wait(for: [completed], timeout: 5) } } /// 记录调用顺序并检测配置事务内启动的会话替身,仅在测试串行队列读写。 private final class RecordingCaptureSession: QRCodeCaptureSession { var isRunning = false var events: [String] = [] var startedDuringConfiguration = false private var configuring = false func beginConfiguration() { configuring = true events.append("begin") } func commitConfiguration() { configuring = false events.append("commit") } func startRunning() { startedDuringConfiguration = configuring isRunning = true events.append("start") } func stopRunning() { isRunning = false events.append("stop") } }