-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding state machine tests. More conventional naming. Todo list added.
- Loading branch information
Showing
10 changed files
with
379 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
## List of todo tasks | ||
|
||
- Add event emitter abstraction instead of interfaces + Java style |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import { describe, expect, it, jest } from "@jest/globals"; | ||
|
||
import { GenericAction } from "#/fixtures/mocks"; | ||
|
||
import { AbstractAction } from "@/AbstractAction"; | ||
import { IFiniteStateMachinePlanEventListener } from "@/event/IFiniteStateMachinePlanEventListener"; | ||
import { AbstractPlanner } from "@/planner/AbstractPlanner"; | ||
import { FiniteStateMachine } from "@/state_machine/FiniteStateMachine"; | ||
import { IdleState } from "@/state_machine/IdleState"; | ||
import { MoveToState } from "@/state_machine/MoveToState"; | ||
import { RunActionState } from "@/state_machine/RunActionState"; | ||
import { Queue } from "@/types"; | ||
import { IUnit } from "@/unit/IUnit"; | ||
|
||
describe("FiniteStateMachine class", () => { | ||
it("should correctly initialize", () => { | ||
const fsm: FiniteStateMachine = new FiniteStateMachine(); | ||
|
||
expect(fsm.getStack()).toEqual([]); | ||
expect(fsm.hasAny()).toBe(false); | ||
}); | ||
|
||
it("should correctly initialize handle pop/push/get", () => { | ||
const fsm: FiniteStateMachine = new FiniteStateMachine(); | ||
const moveToState: MoveToState = new MoveToState(new GenericAction(1)); | ||
const actState: RunActionState = new RunActionState(fsm, [new GenericAction(2), new GenericAction(3)]); | ||
const idleState: IdleState = new IdleState({} as AbstractPlanner); | ||
|
||
expect(fsm.getStack()).toEqual([]); | ||
expect(fsm.pop()).toBeUndefined(); | ||
expect(fsm.hasAny()).toBe(false); | ||
|
||
fsm.push(idleState); | ||
fsm.push(actState); | ||
fsm.push(moveToState); | ||
|
||
expect(fsm.hasAny()).toBe(true); | ||
expect(fsm.getStack()).toEqual([idleState, actState, moveToState]); | ||
expect(fsm.pop()).toBe(moveToState); | ||
expect(fsm.getStack()).toEqual([idleState, actState]); | ||
expect(fsm.pop()).toBe(actState); | ||
expect(fsm.getStack()).toEqual([idleState]); | ||
expect(fsm.pop()).toBe(idleState); | ||
expect(fsm.getStack()).toEqual([]); | ||
expect(fsm.pop()).toBeUndefined(); | ||
expect(fsm.getStack()).toEqual([]); | ||
|
||
fsm.push(idleState); | ||
fsm.push(actState); | ||
|
||
expect(fsm.hasAny()).toBe(true); | ||
expect(fsm.getStack()).toEqual([idleState, actState]); | ||
|
||
fsm.clear(); | ||
|
||
expect(fsm.hasAny()).toBe(false); | ||
expect(fsm.getStack()).toEqual([]); | ||
}); | ||
|
||
it("should correctly update when empty", () => { | ||
const fsm: FiniteStateMachine = new FiniteStateMachine(); | ||
const eventListener: IFiniteStateMachinePlanEventListener = { onPlanFailed: jest.fn(), onPlanFinished: jest.fn() }; | ||
const unit: IUnit = {} as IUnit; | ||
|
||
fsm.addEventListener(eventListener); | ||
|
||
fsm.update(unit); | ||
fsm.update(unit); | ||
fsm.update(unit); | ||
|
||
expect(eventListener.onPlanFinished).not.toHaveBeenCalled(); | ||
expect(eventListener.onPlanFailed).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("should correctly update and handle perform action emit event", () => { | ||
const fsm: FiniteStateMachine = new FiniteStateMachine(); | ||
const eventListener: IFiniteStateMachinePlanEventListener = { onPlanFailed: jest.fn(), onPlanFinished: jest.fn() }; | ||
const actState: RunActionState = new RunActionState(fsm, [new GenericAction(2), new GenericAction(3)]); | ||
const unit: IUnit = {} as IUnit; | ||
|
||
fsm.addEventListener(eventListener); | ||
fsm.push(actState); | ||
|
||
jest.spyOn(actState, "execute").mockImplementation(() => true); | ||
|
||
fsm.update(unit); | ||
fsm.update(unit); | ||
|
||
expect(eventListener.onPlanFinished).not.toHaveBeenCalled(); | ||
expect(eventListener.onPlanFailed).not.toHaveBeenCalled(); | ||
expect(fsm.getStack()).toEqual([actState]); | ||
|
||
jest.spyOn(actState, "execute").mockImplementation(() => false); | ||
|
||
fsm.update(unit); | ||
|
||
expect(eventListener.onPlanFinished).toHaveBeenCalledTimes(1); | ||
expect(eventListener.onPlanFinished).toHaveBeenCalled(); | ||
expect(eventListener.onPlanFailed).not.toHaveBeenCalled(); | ||
expect(fsm.getStack()).toEqual([]); | ||
}); | ||
|
||
it("should correctly update and handle errors", () => { | ||
const fsm: FiniteStateMachine = new FiniteStateMachine(); | ||
const listener: IFiniteStateMachinePlanEventListener = { onPlanFailed: jest.fn(), onPlanFinished: jest.fn() }; | ||
const unit: IUnit = {} as IUnit; | ||
|
||
const somePlan: Queue<AbstractAction> = [new GenericAction(5), new GenericAction(32)]; | ||
const anotherActState: RunActionState = new RunActionState(fsm, somePlan); | ||
const actState: RunActionState = new RunActionState(fsm, [new GenericAction(2), new GenericAction(3)]); | ||
const idleState: IdleState = new IdleState({} as AbstractPlanner); | ||
|
||
fsm.addEventListener(listener); | ||
fsm.push(idleState); | ||
fsm.push(actState); | ||
fsm.push(anotherActState); | ||
|
||
jest.spyOn(anotherActState, "execute").mockImplementation(() => { | ||
throw new Error("test-error"); | ||
}); | ||
|
||
expect(fsm.getStack()).toEqual([idleState, actState, anotherActState]); | ||
|
||
fsm.update(unit); | ||
|
||
expect(listener.onPlanFinished).not.toHaveBeenCalled(); | ||
expect(listener.onPlanFailed).toHaveBeenCalledWith(somePlan); | ||
expect(fsm.getStack()).toEqual([idleState, actState]); | ||
|
||
fsm.push(anotherActState); | ||
fsm.update(unit); | ||
|
||
expect(listener.onPlanFailed).toHaveBeenCalledTimes(2); | ||
|
||
fsm.removeEventListener(listener); | ||
|
||
fsm.push(anotherActState); | ||
fsm.update(unit); | ||
|
||
expect(listener.onPlanFailed).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
it("should correctly update and emit events", () => { | ||
const fsm: FiniteStateMachine = new FiniteStateMachine(); | ||
const moveToState: MoveToState = new MoveToState(new GenericAction(1)); | ||
const actState: RunActionState = new RunActionState(fsm, [new GenericAction(2), new GenericAction(3)]); | ||
const idleState: IdleState = new IdleState({} as AbstractPlanner); | ||
|
||
fsm.push(idleState); | ||
fsm.push(actState); | ||
fsm.push(moveToState); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { describe, expect, it, jest } from "@jest/globals"; | ||
|
||
import { GenericAction } from "#/fixtures/mocks"; | ||
|
||
import { AbstractAction } from "@/AbstractAction"; | ||
import { IPlanCreatedEventListener } from "@/event/IPlanCreatedEventListener"; | ||
import { IPlanner } from "@/planner/IPlanner"; | ||
import { IdleState } from "@/state_machine/IdleState"; | ||
import { Queue } from "@/types"; | ||
import { IUnit } from "@/unit/IUnit"; | ||
|
||
describe("IdleState class", () => { | ||
it("should correctly initialize", () => { | ||
const planner: IPlanner = { plan: jest.fn(() => null) } as IPlanner; | ||
const state: IdleState = new IdleState(planner); | ||
const unit: IUnit = {} as IUnit; | ||
|
||
expect(state.execute(unit)).toBe(true); | ||
expect(planner.plan).toHaveBeenCalledTimes(1); | ||
expect(planner.plan).toHaveBeenCalledWith(unit); | ||
}); | ||
|
||
it("should correctly emit events when plan is created", () => { | ||
const plan: Queue<AbstractAction> = [new GenericAction(1), new GenericAction(2)]; | ||
const planner: IPlanner = { plan: jest.fn(() => null) } as IPlanner; | ||
const listener: IPlanCreatedEventListener = { onPlanCreated: jest.fn() }; | ||
const state: IdleState = new IdleState(planner); | ||
const unit: IUnit = {} as IUnit; | ||
|
||
state.addListener(listener); | ||
|
||
expect(state.execute(unit)).toBe(true); | ||
expect(listener.onPlanCreated).not.toHaveBeenCalled(); | ||
|
||
jest.spyOn(planner, "plan").mockImplementation(() => plan); | ||
|
||
expect(state.execute(unit)).toBe(true); | ||
expect(listener.onPlanCreated).toHaveBeenCalledTimes(1); | ||
expect(listener.onPlanCreated).toHaveBeenCalledWith(plan); | ||
|
||
expect(state.execute(unit)).toBe(true); | ||
expect(listener.onPlanCreated).toHaveBeenCalledTimes(2); | ||
expect(listener.onPlanCreated).toHaveBeenNthCalledWith(2, plan); | ||
|
||
state.removeListener(listener); | ||
|
||
expect(state.execute(unit)).toBe(true); | ||
expect(listener.onPlanCreated).toHaveBeenCalledTimes(2); | ||
}); | ||
}); |
Oops, something went wrong.