local n=require(script.Parent.Packages.testez)return function()local n=require(script.Parent.Packages.Arch)describe("\097 \115\105\110\103\108\101-\108\097\121\101\114 \109\097\099\104\105\110\101",function()local W it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \098\117\105\108\100 \097\110\100 \115\116\097\114\116",function()(expect(function()W=n.createMachine({id="\098\097\115\105\099 \102\115\109",initial="\083\116\097\116\101\065";states={StateA={id="\083\116\097\116\101\065",janitor=true;events={Switch="\083\116\097\116\101\066";Switch2={{target="\083\116\097\116\101\066",guards={function()return false end}}},Switch3={target="\083\116\097\116\101\066",actions={function() end}}}};StateB={id="\083\116\097\116\101\066";janitor=true;events={Switch1={target="\083\116\097\116\101\065";guards={"\065\108\119\097\121\115\084\114\117\101\071\117\097\114\100"};actions={function() end;"\082\097\110\100\111\109\083\116\114\105\110\103\065\099\116\105\111\110"}};ToC="\083\116\097\116\101\067"}},StateC={id="\083\116\097\116\101\067",OnEntry=function() end,OnExit=function() end,events={refresh="\083\116\097\116\101\067",targetless={actions={function() end}};ToB={target="\083\116\097\116\101\066",actions={function(n)n.janitor:Add(Instance.new("\080\097\114\116"),"\068\101\115\116\114\111\121")end}}}}}},{guards={AlwaysTrueGuard=function()return true end},actions={RandomStringAction=function() end}})W:Start()end)).never.to.throw()end)it("\115\104\111\117\108\100 \115\117\098\115\099\114\105\098\101 \116\111 \111\110\084\114\097\110\115\105\116\105\111\110 \101\118\101\110\116",function()(expect(function()W.onTransition:Connect(function(n) end)end)).never.to.throw()end)it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \116\114\097\110\115\105\116\105\111\110",function()(expect(function()assert(W.configuration[1].id=="\083\116\097\116\101\065")W:Send("\083\119\105\116\099\104")assert(W.configuration[1].id=="\083\116\097\116\101\066")W:Send("\083\119\105\116\099\104\049")assert(W.configuration[1].id=="\083\116\097\116\101\065")W:Send("\083\119\105\116\099\104\050")assert(W.configuration[1].id=="\083\116\097\116\101\065")W:Send("\083\119\105\116\099\104\051")end)).never.to.throw()end)it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \115\101\108\102-\116\114\097\110\115\105\116\105\111\110",function()(expect(function()W:Send("\084\111\067")assert(W.configuration[1].id=="\083\116\097\116\101\067")end)).never.to.throw();(expect(function()W:Send("\114\101\102\114\101\115\104")assert(W.configuration[1].id=="\083\116\097\116\101\067")end)).never.to.throw()end)it("\115\104\111\117\108\100 \097\108\108\111\119 \116\097\114\103\101\116\108\101\115\115 \116\114\097\110\115\105\116\105\111\110\115",function()(expect(function()W:Send("\116\097\114\103\101\116\108\101\115\115")assert(W.configuration[1].id=="\083\116\097\116\101\067")end)).never.to.throw()end)it("\115\104\111\117\108\100 \103\105\118\101 \106\097\110\105\116\111\114 \116\111 \116\097\114\103\101\116\101\100 \116\114\097\110\115\105\116\105\111\110 \097\099\116\105\111\110\115",function()(expect(function()W:Send("\084\111\066")assert(W.configuration[1].id=="\083\116\097\116\101\066")end)).never.to.throw()end)it("\115\104\111\117\108\100 \097\108\108\111\119 \114\111\111\116-\108\101\118\101\108 \112\097\114\097\108\108\101\108\108\105\115\109",function()(expect(function()local W=n.createMachine({id="\112\097\114\097\108\108\101\108\077\097\099\104\105\110\101";parallel=true,states={StateA={},StateB={}}})W:Start()assert(W.configuration[1].id=="\083\116\097\116\101\065"or W.configuration[1].id=="\083\116\097\116\101\066")assert(W.configuration[2].id=="\083\116\097\116\101\065"or W.configuration[2].id=="\083\116\097\116\101\066")end)).never.to.throw()end)end)describe("\097 \109\117\108\116\105-\108\097\121\101\114 \109\097\099\104\105\110\101",function()local W it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \098\117\105\108\100 \097\110\100 \115\116\097\114\116",function()(expect(function()W=n.createMachine({id="\109\117\108\116\105-\108\097\121\101\114 \109\097\099\104\105\110\101",initial="\067\104\105\108\100\065",states={ChildA={initial="\071\114\097\110\100\099\104\105\108\100\065";OnExit=function() end;OnDestroy="\100\101\115\116\114\111\121\065",states={GrandchildA={},GrandhcildB={}};events={["\067\065-\067\066"]={target="\067\104\105\108\100\066"};["\067\065-\067\067"]="\067\104\105\108\100\067",["\067\065-\071\071\068"]="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\068";["\067\065-\071\068"]="\071\114\097\110\100\099\104\105\108\100\068"}};ChildB={initial="\071\114\097\110\100\099\104\105\108\100\067";history="\100\101\101\112";OnDestroy=function(n) end,events={["\067\066-\067\065"]="\067\104\105\108\100\065"};states={GrandchildC={initial="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\065",states={["\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\065"]={events={["\071\071\065-\071\071\066"]="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\066"}},["\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\066"]={}}};GrandchildD={initial="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\067";history="\115\104\097\108\108\111\119";events={["\071\068-\071\067"]="\071\114\097\110\100\099\104\105\108\100\067"},states={["\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\067"]={};["\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\068"]={}}}}},ChildC={parallel=true;states={GrandchildE={};GrandchildF={}}}}},{context={};actions={destroyA=function() end}})W:Start()end)).never.to.throw()end)it("\115\104\111\117\108\100 \100\111 \098\097\115\105\099 \116\114\097\110\115\105\116\105\111\110\115",function()(expect(function()W:Send("\067\065-\067\066")assert(W.configuration[1].id=="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\065")W:Send("\071\071\065-\071\071\066")assert(W.configuration[1].id=="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\066")W:Send("\067\066-\067\065")assert(W.configuration[1].id=="\071\114\097\110\100\099\104\105\108\100\065")end)).never.to.throw()end)it("\115\104\111\117\108\100 \097\108\108\111\119 \104\105\115\116\111\114\121 \115\116\097\116\101\115",function()(expect(function()W:Send("\067\065-\067\066")assert(W.configuration[1].id=="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\066")W:Send("\067\066-\067\065")end)).never.to.throw()expect(function()W:Send("\067\065-\071\071\068")assert(W.configuration[1].id=="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\068")W:Send("\071\068-\071\067")assert(W.configuration[1].id=="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\065")W:Send("\067\066-\067\065")assert(W.configuration[1].id=="\071\114\097\110\100\099\104\105\108\100\065")W:Send("\067\065-\071\068")assert(W.configuration[1].id=="\071\114\101\097\116-\071\114\097\110\100\099\104\105\108\100\068")W:Send("\067\066-\067\065")end)end)it("\115\104\111\117\108\100 \097\108\108\111\119 \112\097\114\097\108\108\101\108 \115\116\097\116\101\115",function()(expect(function()W:Send("\067\065-\067\067")assert(W.configuration[1].id=="\071\114\097\110\100\099\104\105\108\100\069"or W.configuration[1].id=="\071\114\097\110\100\099\104\105\108\100\070")assert(W.configuration[2].id=="\071\114\097\110\100\099\104\105\108\100\069"or W.configuration[2].id=="\071\114\097\110\100\099\104\105\108\100\070")end)).never.to.throw();(expect(function()local W={id="\084\101\115\116";parallel=true,states={PlayerActions={initial="\073\100\108\101";events={Attack="\065\116\116\097\099\107\105\110\103"},states={Idle={};Attacking={OnEntry=function()print("\101\110\116\101\114\101\100")end},Blocking={},BlockBroken={}}};CombatState={initial="\079\117\116\115\105\100\101\067\111\109\098\097\116";states={OutsideCombat={events={Combat="\073\110\067\111\109\098\097\116"}},InCombat={events={Safe="\079\117\116\115\105\100\101\067\111\109\098\097\116"}}}}}}local l=n.createMachine(W)l:Start()l:Send("\065\116\116\097\099\107")end)).never.to.throw()end)it("\115\104\111\117\108\100 \101\120\105\116 \097\110\100 \100\101\115\116\114\111\121",function()(expect(function()W:Stop()end)).never.to.throw();(expect(function()error(W._running)end)).to.throw(false)end)end)end
local TestEZ = require(script.Parent.Packages.testez)

return function()
	local arch = require(script.Parent.Packages.Arch)

	describe("a single-layer machine", function()
		local machine
		it("should be able to build and start", function()
			expect(function()
				machine = arch.createMachine({
					id = "basic fsm",
					initial = "StateA",

					states = {
						StateA = {
							id = "StateA",
							janitor = true,

							events = {
								Switch = "StateB",
								Switch2 = {
									{
										target = "StateB",
										guards = {
											function()
												return false
											end,
										},
									},
								},
								Switch3 = {
									target = "StateB",
									actions = {
										function() end,
									},
								},
							},
						},
						StateB = {
							id = "StateB",
							janitor = true,

							events = {
								Switch1 = {
									target = "StateA",
									guards = {
										"AlwaysTrueGuard",
									},
									actions = {
										function() end,
										"RandomStringAction",
									},
								},
								ToC = "StateC",
							},
						},
						StateC = {
							id = "StateC",

							OnEntry = function() end,
							OnExit = function() end,

							events = {
								refresh = "StateC",
								targetless = {
									actions = {
										function() end,
									},
								},
								ToB = {
									target = "StateB",
									actions = {
										function(context)
											context.janitor:Add(Instance.new("Part"), "Destroy")
										end,
									},
								},
							},
						},
					},
				}, {
					guards = {
						AlwaysTrueGuard = function()
							return true
						end,
					},
					actions = {
						RandomStringAction = function() end,
					},
				})
				machine:Start()
			end).never.to.throw()
		end)

		it("should subscribe to onTransition event", function()
			expect(function()
				machine.onTransition:Connect(function(stateOfTheMachine)
					--print(stateOfTheMachine, machine.configuration)
				end)
			end).never.to.throw()
		end)

		it("should be able to transition", function()
			expect(function()
				assert(machine.configuration[1].id == "StateA")
				machine:Send("Switch")
				assert(machine.configuration[1].id == "StateB")
				machine:Send("Switch1")
				assert(machine.configuration[1].id == "StateA")
				machine:Send("Switch2")
				assert(machine.configuration[1].id == "StateA")
				machine:Send("Switch3")
			end).never.to.throw()
		end)

		it("should be able to self-transition", function()
			expect(function()
				machine:Send("ToC")
				assert(machine.configuration[1].id == "StateC")
			end).never.to.throw()
			expect(function()
				machine:Send("refresh")
				assert(machine.configuration[1].id == "StateC")
			end).never.to.throw()
		end)

		it("should allow targetless transitions", function()
			expect(function()
				machine:Send("targetless")
				assert(machine.configuration[1].id == "StateC")
			end).never.to.throw()
		end)

		it("should give janitor to targeted transition actions", function()
			expect(function()
				machine:Send("ToB")
				assert(machine.configuration[1].id == "StateB")
			end).never.to.throw()
		end)

		it("should allow root-level parallellism", function()
			expect(function()
				local pMachine = arch.createMachine({
					id = "parallelMachine",
					parallel = true,

					states = {
						StateA = {},
						StateB = {},
					},
				})

				pMachine:Start()

				assert(pMachine.configuration[1].id == "StateA" or pMachine.configuration[1].id == "StateB")
				assert(pMachine.configuration[2].id == "StateA" or pMachine.configuration[2].id == "StateB")
			end).never.to.throw()
		end)
	end)
	describe("a multi-layer machine", function()
		local machine
		it("should be able to build and start", function()
			expect(function()
				machine = arch.createMachine({
					id = "multi-layer machine",
					initial = "ChildA",

					states = {
						ChildA = {
							initial = "GrandchildA",
							OnExit = function() end,
							OnDestroy = "destroyA",

							states = {
								GrandchildA = {},
								GrandhcildB = {},
							},

							events = {
								["CA-CB"] = {
									target = "ChildB",
								},
								["CA-CC"] = "ChildC",
								["CA-GGD"] = "Great-GrandchildD",
								["CA-GD"] = "GrandchildD",
							},
						},
						ChildB = {
							initial = "GrandchildC",
							history = "deep",

							OnDestroy = function(context) end,

							events = {
								["CB-CA"] = "ChildA",
							},

							states = {
								GrandchildC = {
									initial = "Great-GrandchildA",

									states = {
										["Great-GrandchildA"] = {
											events = {
												["GGA-GGB"] = "Great-GrandchildB",
											},
										},
										["Great-GrandchildB"] = {},
									},
								},
								GrandchildD = {
									initial = "Great-GrandchildC",
									history = "shallow",

									events = {
										["GD-GC"] = "GrandchildC",
									},

									states = {
										["Great-GrandchildC"] = {},
										["Great-GrandchildD"] = {},
									},
								},
							},
						},
						ChildC = {
							parallel = true,

							states = {
								GrandchildE = {},
								GrandchildF = {},
							},
						},
					},
				}, {
					context = {},
					actions = {
						destroyA = function() end,
					},
				})
				machine:Start()
			end).never.to.throw()
		end)

		it("should do basic transitions", function()
			expect(function()
				machine:Send("CA-CB")
				assert(machine.configuration[1].id == "Great-GrandchildA")
				machine:Send("GGA-GGB")
				assert(machine.configuration[1].id == "Great-GrandchildB")
				machine:Send("CB-CA")
				assert(machine.configuration[1].id == "GrandchildA")
			end).never.to.throw()
		end)

		it("should allow history states", function()
			expect(function()
				machine:Send("CA-CB")
				assert(machine.configuration[1].id == "Great-GrandchildB")
				machine:Send("CB-CA")
			end).never.to.throw()

			expect(function()
				machine:Send("CA-GGD")
				assert(machine.configuration[1].id == "Great-GrandchildD")
				machine:Send("GD-GC")
				assert(machine.configuration[1].id == "Great-GrandchildA")
				machine:Send("CB-CA")
				assert(machine.configuration[1].id == "GrandchildA")
				machine:Send("CA-GD")
				assert(machine.configuration[1].id == "Great-GrandchildD")
				machine:Send("CB-CA")
			end)
		end)

		it("should allow parallel states", function()
			expect(function()
				machine:Send("CA-CC")
				assert(machine.configuration[1].id == "GrandchildE" or machine.configuration[1].id == "GrandchildF")
				assert(machine.configuration[2].id == "GrandchildE" or machine.configuration[2].id == "GrandchildF")
			end).never.to.throw()
			expect(function()
				local definition = {

					id = "Test",
					parallel = true,

					states = {
						PlayerActions = {

							initial = "Idle",

							events = {
								Attack = "Attacking",
							},

							states = {
								Idle = {},
								Attacking = {
									OnEntry = function()
										print("entered")
									end,
								},
								Blocking = {},
								BlockBroken = {},
							},
						},
						CombatState = {
							initial = "OutsideCombat",
							states = {
								OutsideCombat = {
									events = {
										Combat = "InCombat",
									},
								},
								InCombat = {
									events = {
										Safe = "OutsideCombat",
									},
								},
							},
						},
					},
				}

				local machine = arch.createMachine(definition)
				machine:Start()
				machine:Send("Attack")
			end).never.to.throw()
		end)

		it("should exit and destroy", function()
			expect(function()
				machine:Stop()
			end).never.to.throw()
			expect(function()
				error(machine._running)
			end).to.throw(false)
		end)
	end)
end
