Files
iccery-v2-mac/Tests/ICCeryCoreTests/SpotReadClassifierTests.swift
gronodandDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> d40932f4cf feat(spotread): spot-read console for live Lab (#148)
Refs #148

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-13 01:03:57 +01:00

92 lines
3.6 KiB
Swift

import XCTest
@testable import ICCeryCore
/// `SpotReadClassifier` / `SpotReadParser` against real `spotread`
/// phrasing (issue #148). The calibration-tile line classifies through
/// `ChartreadClassifier`; the spot prompt and its continuation lines
/// need the spot-specific matchers.
final class SpotReadClassifierTests: XCTestCase {
// Real `spotread` stdout (calibration then spot prompt).
private let calibrateLines = [
"Spot read needs a calibration before continuing",
"Place instrument on spot reading white calibration tile,",
" and then hit any key to continue,",
"or hit Esc or Q to abort:",
]
private let spotPromptLines = [
"Place instrument on a spot to be measured,",
" and hit a key to take a reading,",
"or hit Esc or Q to abort:",
]
func testCalibrationPrompt() {
var state = ChartreadState.idle
for line in calibrateLines {
state = SpotReadClassifier.classify(line: line, previousState: state).state
}
XCTAssertEqual(state, .calibrating)
}
func testSpotPromptIsAwaitingTrigger() {
var state = ChartreadState.calibrating
for line in spotPromptLines {
state = SpotReadClassifier.classify(line: line, previousState: state).state
}
XCTAssertEqual(state, .awaitingStrip)
}
func testAbortLineDoesNotBecomeWarning() {
// "or hit Esc or Q to abort:" contains no '?' but does contain
// "abort" — it must stay on the current prompt, never flip to
// a warning.
let r = SpotReadClassifier.classify(
line: "or hit Esc or Q to abort:", previousState: .awaitingStrip)
XCTAssertEqual(r.state, .awaitingStrip)
}
func testParseResultLine() throws {
let parsed = SpotReadParser.parse(
line: "Result is XYZ: 18.51 20.05 15.71, D50 Lab: 51.9 -8.3 12.2")
let lab = try XCTUnwrap(parsed?.lab)
XCTAssertEqual(lab.l, 51.9, accuracy: 0.001)
XCTAssertEqual(lab.a, -8.3, accuracy: 0.001)
XCTAssertEqual(lab.b, 12.2, accuracy: 0.001)
let xyz = try XCTUnwrap(parsed?.xyz)
XCTAssertEqual(xyz.x, 18.51, accuracy: 0.001)
XCTAssertEqual(xyz.y, 20.05, accuracy: 0.001)
XCTAssertEqual(xyz.z, 15.71, accuracy: 0.001)
}
func testParseLabOnlyLine() throws {
let parsed = SpotReadParser.parse(line: "Result is Lab: 40.0 1.2 -3.4")
let lab = try XCTUnwrap(parsed?.lab)
XCTAssertEqual(lab.l, 40.0, accuracy: 0.001)
XCTAssertNil(parsed?.xyz)
}
func testNonSampleLineParsesNil() {
XCTAssertNil(SpotReadParser.parse(line: "Place instrument on a spot to be measured,"))
XCTAssertNil(SpotReadParser.parse(line: "Calibration successful."))
XCTAssertNil(SpotReadParser.parse(line: ""))
}
func testDeltaEBetweenFixtures() {
let a = SpotReadParser.parse(
line: "Result is XYZ: 18.51 20.05 15.71, D50 Lab: 51.9 -8.3 12.2")!.lab
let b = SpotReadParser.parse(
line: "Result is XYZ: 19.00 20.50 16.00, D50 Lab: 52.3 -8.0 12.6")!.lab
XCTAssertEqual(ColorDifference.deltaE00(a, a), 0, accuracy: 0.0001)
XCTAssertGreaterThan(ColorDifference.deltaE00(a, b), 0)
XCTAssertEqual(
ColorDifference.classify(deltaE: 1.0, goodMax: 2.0, warningMax: 5.0),
.good)
XCTAssertEqual(
ColorDifference.classify(deltaE: 3.0, goodMax: 2.0, warningMax: 5.0),
.warning)
XCTAssertEqual(
ColorDifference.classify(deltaE: 6.0, goodMax: 2.0, warningMax: 5.0),
.bad)
}
}