return function()local f=require(script.Parent.validateMotor)local e=require(script.Parent.createSpy)local t=require(script.Parent.createGroupMotor)local function I(f)local e={stepCount=0}e.step=function(t,I,S)e.stepCount=e.stepCount+1 if e.stepCount>=f then return{value=I.value;velocity=I.velocity,complete=true}end return I end setmetatable(e,{__index=function(f,e)error(("\037\113 \105\115 \110\111\116 \097 \118\097\108\105\100 \109\101\109\098\101\114 \111\102 \115\116\101\112\112\101\114"):format(e))end})return e end it("\115\104\111\117\108\100 \098\101 \097 \118\097\108\105\100 \109\111\116\111\114",function()local e=t({})f(e)e:destroy()end)describe("\111\110\083\116\101\112",function()it("\115\104\111\117\108\100 \110\111\116 \098\101 \099\097\108\108\101\100 \105\110\105\116\105\097\108\108\121",function()local f=t({x=0})local S=e()f:onStep(S.value)f:setGoal({x=I(5)});(expect(S.callCount)).to.equal(0)end)end)describe("\111\110\067\111\109\112\108\101\116\101 \115\104\111\117\108\100 \098\101 \099\097\108\108\101\100 \119\104\101\110",function()it("\104\097\115 \099\111\109\112\108\101\116\101\100 \105\116\115 \109\111\116\105\111\110",function()local f=t({x=0})f:setGoal({x=I(5)})local S=e()f:onComplete(S.value)for e=1,5,1 do f:step(1)end;(expect(S.callCount)).to.equal(1)f:destroy()end)it("\104\097\115 \109\117\108\116\105\112\108\101 \097\116\114\105\098\117\116\101\115 \105\110 \109\111\116\105\111\110",function()local f=t({x=0,y=10})f:setGoal({x=I(2),y=I(5)})local S=e()f:onComplete(S.value)for e=1,2,1 do f:step(1)end;(expect(S.callCount)).to.equal(0)for e=1,3,1 do f:step(1)end;(expect(S.callCount)).to.equal(1)f:destroy()end)it("\104\097\115 \114\101\115\116\097\114\116\101\100 \105\116\115 \109\111\116\105\111\110",function()local f=t({x=0})f:setGoal({x=I(3)})local S=e()f:onComplete(S.value)for e=1,3,1 do f:step(1)end;(expect(S.callCount)).to.equal(1)f:setGoal({x=I(3)})for e=1,3,1 do f:step(1)end;(expect(S.callCount)).to.equal(2)f:destroy()end)end)describe("\111\110\067\111\109\112\108\101\116\101 \115\104\111\117\108\100 \110\111\116 \098\101 \099\097\108\108\101\100 \119\104\101\110",function()it("\104\097\115 \110\111 \103\111\097\108\115 \115\101\116",function()local f=t({x=2})local I=e()f:onComplete(I.value)for e=1,3,1 do f:step(1)end;(expect(I.callCount)).to.equal(0)f:destroy()end)it("\104\097\115 \110\111\116 \099\111\109\112\108\101\116\101\100 \109\111\116\105\111\110",function()local f=t({x=0})f:setGoal({x=I(2)})local S=e()f:onComplete(S.value)f:step(1);(expect(S.callCount)).to.equal(0)f:destroy()end)it("\104\097\115 \111\110\101 \110\111\110-\099\111\109\112\108\101\116\101\100 \109\111\116\105\111\110",function()local f=t({x=0,y=0})f:setGoal({x=I(0),y=I(2)})local S=e()f:onComplete(S.value)f:step(1);(expect(S.callCount)).to.equal(0)f:destroy()end)it("\100\111\101\115 \110\111\116 \099\097\108\108 \115\116\101\112",function()local f=t({x=0})f:setGoal({x=I(0)})local S=e()f:onComplete(S.value);(expect(S.callCount)).to.equal(0)f:destroy()end)end)end
return function()
	local validateMotor = require(script.Parent.validateMotor)
	local createSpy = require(script.Parent.createSpy)

	local createGroupMotor = require(script.Parent.createGroupMotor)

	-- test motion object that completes after step has been called numSteps times
	local function createStepper(numSteps)
		local self = {
			stepCount = 0,
		}

		self.step = function(_, state, dt)
			self.stepCount = self.stepCount + 1

			if self.stepCount >= numSteps then
				return {
					value = state.value,
					velocity = state.velocity,
					complete = true,
				}
			end

			return state
		end

		setmetatable(self, {
			__index = function(_, key)
				error(("%q is not a valid member of stepper"):format(key))
			end,
		})

		return self
	end

	it("should be a valid motor", function()
		local motor = createGroupMotor({})
		validateMotor(motor)
		motor:destroy()
	end)

	describe("onStep", function()
		it("should not be called initially", function()
			local motor = createGroupMotor({
				x = 0,
			})

			local spy = createSpy()
			motor:onStep(spy.value)

			motor:setGoal({
				x = createStepper(5),
			})

			expect(spy.callCount).to.equal(0)
		end)
	end)

	describe("onComplete should be called when", function()
		it("has completed its motion", function()
			local motor = createGroupMotor({
				x = 0,
			})

			motor:setGoal({
				x = createStepper(5),
			})

			local spy = createSpy()

			motor:onComplete(spy.value)

			for _ = 1, 5 do
				motor:step(1)
			end

			expect(spy.callCount).to.equal(1)

			motor:destroy()
		end)

		it("has multiple atributes in motion", function()
			local motor = createGroupMotor({
				x = 0,
				y = 10,
			})

			motor:setGoal({
				x = createStepper(2),
				y = createStepper(5),
			})

			local spy = createSpy()

			motor:onComplete(spy.value)

			for _ = 1, 2 do
				motor:step(1)
			end

			expect(spy.callCount).to.equal(0)

			for _ = 1, 3 do
				motor:step(1)
			end

			expect(spy.callCount).to.equal(1)

			motor:destroy()
		end)

		it("has restarted its motion", function()
			local motor = createGroupMotor({
				x = 0,
			})

			motor:setGoal({
				x = createStepper(3),
			})

			local spy = createSpy()

			motor:onComplete(spy.value)

			for _ = 1, 3 do
				motor:step(1)
			end

			expect(spy.callCount).to.equal(1)

			motor:setGoal({
				x = createStepper(3),
			})

			for _ = 1, 3 do
				motor:step(1)
			end

			expect(spy.callCount).to.equal(2)

			motor:destroy()
		end)
	end)

	describe("onComplete should not be called when", function()
		it("has no goals set", function()
			local motor = createGroupMotor({
				x = 2,
			})

			local spy = createSpy()
			motor:onComplete(spy.value)

			for _ = 1, 3 do
				motor:step(1)
			end

			expect(spy.callCount).to.equal(0)

			motor:destroy()
		end)

		it("has not completed motion", function()
			local motor = createGroupMotor({
				x = 0,
			})

			motor:setGoal({
				x = createStepper(2),
			})

			local spy = createSpy()
			motor:onComplete(spy.value)

			motor:step(1)

			expect(spy.callCount).to.equal(0)

			motor:destroy()
		end)

		it("has one non-completed motion", function()
			local motor = createGroupMotor({
				x = 0,
				y = 0,
			})

			motor:setGoal({
				x = createStepper(0),
				y = createStepper(2),
			})

			local spy = createSpy()
			motor:onComplete(spy.value)

			motor:step(1)

			expect(spy.callCount).to.equal(0)

			motor:destroy()
		end)

		it("does not call step", function()
			local motor = createGroupMotor({
				x = 0,
			})

			motor:setGoal({
				x = createStepper(0),
			})

			local spy = createSpy()
			motor:onComplete(spy.value)

			expect(spy.callCount).to.equal(0)

			motor:destroy()
		end)
	end)
end