import XCTest
@testable import Physarum
import PhysarumEngine
final class UndoRedoTests: XCTestCase {
private func moveSpeed0() -> ParamDescriptor {
ParamRegistry.all.first { $0.id == .moveSpeed(0) }!
}
func testPushUndoThenUndoRestoresPreviousValue() {
let state = AppState()
let d = moveSpeed0()
let original = d.get(state.edited)
state.pushUndo() // snapshot pre-edit
d.set(&state.edited, original + 0.5)
XCTAssertEqual(d.get(state.edited), original + 0.5, accuracy: 1e-4)
XCTAssertTrue(state.canUndo)
state.undoEdit()
XCTAssertEqual(d.get(state.edited), original, accuracy: 1e-4)
}
func testRedoReappliesUndoneEdit() {
let state = AppState()
let d = moveSpeed0()
let original = d.get(state.edited)
state.pushUndo()
d.set(&state.edited, original + 0.5)
state.undoEdit()
XCTAssertTrue(state.canRedo)
state.redoEdit()
XCTAssertEqual(d.get(state.edited), original + 0.5, accuracy: 1e-4)
}
func testNewEditAfterUndoInvalidatesRedo() {
let state = AppState()
let d = moveSpeed0()
let base = d.get(state.edited)
state.pushUndo(); d.set(&state.edited, base + 0.5)
state.undoEdit()
XCTAssertTrue(state.canRedo)
state.pushUndo() // a new committed edit
d.set(&state.edited, base + 0.9)
XCTAssertFalse(state.canRedo, "redo must be cleared after a new edit")
}
func testUndoStackIsCapped() {
let state = AppState()
for _ in 0..<80 { state.pushUndo() }
XCTAssertLessThanOrEqual(state.undoStack.count, 50)
}
func testSelectingPresetClearsHistory() {
// Two known slots so there is a second preset to switch to (independent of the bundle).
func p(_ n: String) -> Preset { var x = PresetCatalog.fallbackPreset; x.name = n; x.builtIn = false; return x }
let factory = PresetSlots.Factory(presets: [p("A"), p("B")], version: 1)
let state = AppState(slotStore: SlotStore(backend: InMemorySlotBackend(), factory: factory))
state.pushUndo()
XCTAssertTrue(state.canUndo)
// select a different preset than the current one
let other = state.presets.first { $0.id != state.selectedID }!
state.select(id: other.id)
XCTAssertFalse(state.canUndo)
XCTAssertFalse(state.canRedo)
}
}