refactor: simplify undo and redo logic in GameEngine and removed Hacky Test
This commit is contained in:
@@ -144,37 +144,31 @@ class GameEngine extends Observable:
|
|||||||
|
|
||||||
private def performUndo(): Unit =
|
private def performUndo(): Unit =
|
||||||
if invoker.canUndo then
|
if invoker.canUndo then
|
||||||
val history = invoker.history
|
val cmd = invoker.history(invoker.getCurrentIndex)
|
||||||
val currentIdx = invoker.getCurrentIndex
|
(cmd: @unchecked) match
|
||||||
if currentIdx >= 0 && currentIdx < history.size then
|
case moveCmd: MoveCommand =>
|
||||||
val cmd = history(currentIdx)
|
if moveCmd.undo() then
|
||||||
(cmd: @unchecked) match
|
moveCmd.previousBoard.foreach(currentBoard = _)
|
||||||
case moveCmd: MoveCommand =>
|
moveCmd.previousHistory.foreach(currentHistory = _)
|
||||||
if moveCmd.undo() then
|
moveCmd.previousTurn.foreach(currentTurn = _)
|
||||||
moveCmd.previousBoard.foreach(currentBoard = _)
|
invoker.undo()
|
||||||
moveCmd.previousHistory.foreach(currentHistory = _)
|
notifyObservers(BoardResetEvent(currentBoard, currentHistory, currentTurn))
|
||||||
moveCmd.previousTurn.foreach(currentTurn = _)
|
|
||||||
invoker.undo()
|
|
||||||
notifyObservers(BoardResetEvent(currentBoard, currentHistory, currentTurn))
|
|
||||||
else
|
else
|
||||||
notifyObservers(InvalidMoveEvent(currentBoard, currentHistory, currentTurn, "Nothing to undo."))
|
notifyObservers(InvalidMoveEvent(currentBoard, currentHistory, currentTurn, "Nothing to undo."))
|
||||||
|
|
||||||
private def performRedo(): Unit =
|
private def performRedo(): Unit =
|
||||||
if invoker.canRedo then
|
if invoker.canRedo then
|
||||||
val history = invoker.history
|
val cmd = invoker.history(invoker.getCurrentIndex + 1)
|
||||||
val nextIdx = invoker.getCurrentIndex + 1
|
(cmd: @unchecked) match
|
||||||
if nextIdx >= 0 && nextIdx < history.size then
|
case moveCmd: MoveCommand =>
|
||||||
val cmd = history(nextIdx)
|
if moveCmd.execute() then
|
||||||
(cmd: @unchecked) match
|
moveCmd.moveResult.foreach {
|
||||||
case moveCmd: MoveCommand =>
|
case de.nowchess.chess.command.MoveResult.Successful(newBoard, newHistory, newTurn, captured) =>
|
||||||
if moveCmd.execute() then
|
updateGameState(newBoard, newHistory, newTurn)
|
||||||
moveCmd.moveResult.foreach {
|
invoker.redo()
|
||||||
case de.nowchess.chess.command.MoveResult.Successful(newBoard, newHistory, newTurn, captured) =>
|
emitMoveEvent(moveCmd.from.toString, moveCmd.to.toString, captured, newTurn)
|
||||||
updateGameState(newBoard, newHistory, newTurn)
|
case _ => ()
|
||||||
invoker.redo()
|
}
|
||||||
emitMoveEvent(moveCmd.from.toString, moveCmd.to.toString, captured, newTurn)
|
|
||||||
case _ => ()
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
notifyObservers(InvalidMoveEvent(currentBoard, currentHistory, currentTurn, "Nothing to redo."))
|
notifyObservers(InvalidMoveEvent(currentBoard, currentHistory, currentTurn, "Nothing to redo."))
|
||||||
|
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
package de.nowchess.chess.engine
|
|
||||||
|
|
||||||
import de.nowchess.api.board.{Board, Color}
|
|
||||||
import de.nowchess.chess.logic.GameHistory
|
|
||||||
import de.nowchess.chess.command.{CommandInvoker, Command}
|
|
||||||
import org.scalatest.funsuite.AnyFunSuite
|
|
||||||
import org.scalatest.matchers.should.Matchers
|
|
||||||
import java.lang.reflect.Field
|
|
||||||
|
|
||||||
class GameEngineCoverageHackTest extends AnyFunSuite with Matchers {
|
|
||||||
|
|
||||||
test("Hack: trigger impossible conditions in performUndo and performRedo using reflection") {
|
|
||||||
val engine = new GameEngine()
|
|
||||||
|
|
||||||
// We need to inject a mock CommandInvoker
|
|
||||||
class MockInvoker extends CommandInvoker {
|
|
||||||
var forceCanUndo = false
|
|
||||||
var forceCanRedo = false
|
|
||||||
var returnedIndex = -1
|
|
||||||
|
|
||||||
override def canUndo: Boolean = forceCanUndo
|
|
||||||
override def canRedo: Boolean = forceCanRedo
|
|
||||||
override def getCurrentIndex: Int = returnedIndex
|
|
||||||
override def history: List[Command] = List.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
val mockInvoker = new MockInvoker()
|
|
||||||
|
|
||||||
// Use reflection to set the private invoker field
|
|
||||||
val field: Field = classOf[GameEngine].getDeclaredField("invoker")
|
|
||||||
field.setAccessible(true)
|
|
||||||
field.set(engine, mockInvoker)
|
|
||||||
|
|
||||||
// Trigger performUndo where canUndo is true but currentIdx < 0
|
|
||||||
mockInvoker.forceCanUndo = true
|
|
||||||
mockInvoker.returnedIndex = -1 // fails currentIdx >= 0
|
|
||||||
engine.undo() // Hits the unreachable false branch!
|
|
||||||
|
|
||||||
// Trigger performUndo where currentIdx >= history.size
|
|
||||||
mockInvoker.forceCanUndo = true
|
|
||||||
mockInvoker.returnedIndex = 5 // fails currentIdx < history.size
|
|
||||||
engine.undo() // Hits the unreachable false branch!
|
|
||||||
|
|
||||||
// Trigger performRedo where nextIdx < 0
|
|
||||||
mockInvoker.forceCanRedo = true
|
|
||||||
mockInvoker.returnedIndex = -5 // nextIdx = -4, fails nextIdx >= 0
|
|
||||||
engine.redo() // Hits unreachable branch!
|
|
||||||
|
|
||||||
// Trigger performRedo where nextIdx >= history.size
|
|
||||||
mockInvoker.forceCanRedo = true
|
|
||||||
mockInvoker.returnedIndex = 5 // nextIdx = 6, fails nextIdx < history.size
|
|
||||||
engine.redo() // Hits unreachable branch!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user