local function Z(Z,X)local v=os.clock()X=X or 10 while true do if Z()then return true end if os.clock()-v>X then return false end task.wait()end end return function()local X=require(script.Parent)local v local function o(Z)Z=Z or v return#Z:GetConnections()end beforeEach(function()v=X.new()end)afterEach(function()v:Destroy()end)describe("\067\111\110\115\116\114\117\099\116\111\114",function()it("\115\104\111\117\108\100 \099\114\101\097\116\101 \097 \110\101\119 \115\105\103\110\097\108 \097\110\100 \102\105\114\101 \105\116",function()(expect(X.Is(v))).to.equal(true)task.defer(function()v:Fire(10,20)end)local Z,o=v:Wait();(expect(Z)).to.equal(10);(expect(o)).to.equal(20)end)it("\115\104\111\117\108\100 \099\114\101\097\116\101 \097 \112\114\111\120\121 \115\105\103\110\097\108 \097\110\100 \099\111\110\110\101\099\116 \116\111 \105\116",function()local v=X.Wrap((game:GetService("\082\117\110\083\101\114\118\105\099\101")).Heartbeat);(expect(X.Is(v))).to.equal(true)local o=false v:Connect(function()o=true end);(expect(Z(function()return o end,2))).to.equal(true)v:Destroy()end)end)describe("\070\105\114\101\068\101\102\101\114\114\101\100",function()it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \102\105\114\101 \112\114\105\109\105\116\105\118\101 \097\114\103\117\109\101\110\116",function()local X=10 local o v:Connect(function(Z)o=Z end)v:FireDeferred(X);(expect(Z(function()return X==o end,1))).to.equal(true)end)it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \102\105\114\101 \097 \114\101\102\101\114\101\110\099\101 \098\097\115\101\100 \097\114\103\117\109\101\110\116",function()local X={10;20}local o v:Connect(function(Z)o=Z end)v:FireDeferred(X);(expect(Z(function()return X==o end,1))).to.equal(true)end)end)describe("\070\105\114\101",function()it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \102\105\114\101 \112\114\105\109\105\116\105\118\101 \097\114\103\117\109\101\110\116",function()local Z=10 local X v:Connect(function(Z)X=Z end)v:Fire(Z);(expect(X)).to.equal(Z)end)it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \102\105\114\101 \097 \114\101\102\101\114\101\110\099\101 \098\097\115\101\100 \097\114\103\117\109\101\110\116",function()local Z={10,20}local X v:Connect(function(Z)X=Z end)v:Fire(Z);(expect(X)).to.equal(Z)end)end)describe("\067\111\110\110\101\099\116\079\110\099\101",function()it("\115\104\111\117\108\100 \111\110\108\121 \099\097\112\116\117\114\101 \102\105\114\115\116 \102\105\114\101",function()local Z local X=v:ConnectOnce(function(X)Z=X end);(expect(X.Connected)).to.equal(true)v:Fire(10);(expect(X.Connected)).to.equal(false)v:Fire(20);(expect(Z)).to.equal(10)end)end)describe("\087\097\105\116",function()it("\115\104\111\117\108\100 \098\101 \097\098\108\101 \116\111 \119\097\105\116 \102\111\114 \097 \115\105\103\110\097\108 \116\111 \102\105\114\101",function()task.defer(function()v:Fire(10,20,30)end)local Z,X,o=v:Wait();(expect(Z)).to.equal(10);(expect(X)).to.equal(20);(expect(o)).to.equal(30)end)end)describe("\068\105\115\099\111\110\110\101\099\116\065\108\108",function()it("\115\104\111\117\108\100 \100\105\115\099\111\110\110\101\099\116 \097\108\108 \099\111\110\110\101\099\116\105\111\110\115",function()v:Connect(function() end)v:Connect(function() end);(expect(o())).to.equal(2)v:DisconnectAll();(expect(o())).to.equal(0)end)end)describe("\068\105\115\099\111\110\110\101\099\116",function()it("\115\104\111\117\108\100 \100\105\115\099\111\110\110\101\099\116 \099\111\110\110\101\099\116\105\111\110",function()local Z=v:Connect(function() end);(expect(o())).to.equal(1)Z:Disconnect();(expect(o())).to.equal(0)end)it("\115\104\111\117\108\100 \115\116\105\108\108 \119\111\114\107 \105\102 \099\111\110\110\101\099\116\105\111\110\115 \100\105\115\099\111\110\110\101\099\116\101\100 \119\104\105\108\101 \102\105\114\105\110\103",function()local Z=0 local X v:Connect(function()Z+=1 end)X=v:Connect(function()X:Disconnect()Z+=1 end)v:Connect(function()Z+=1 end)v:Fire();(expect(Z)).to.equal(3)end)it("\115\104\111\117\108\100 \115\116\105\108\108 \119\111\114\107 \105\102 \099\111\110\110\101\099\116\105\111\110\115 \100\105\115\099\111\110\110\101\099\116\101\100 \119\104\105\108\101 \102\105\114\105\110\103 \100\101\102\101\114\114\101\100",function()local X=0 local o v:Connect(function()X+=1 end)o=v:Connect(function()o:Disconnect()X+=1 end)v:Connect(function()X+=1 end)v:FireDeferred();(expect(Z(function()return X==3 end))).to.equal(true)end)end)end
local function AwaitCondition(predicate, timeout)
	local start = os.clock()
	timeout = (timeout or 10)
	while true do
		if predicate() then
			return true
		end
		if (os.clock() - start) > timeout then
			return false
		end
		task.wait()
	end
end

return function()
	local Signal = require(script.Parent)

	local signal

	local function NumConns(sig)
		sig = sig or signal
		return #sig:GetConnections()
	end

	beforeEach(function()
		signal = Signal.new()
	end)

	afterEach(function()
		signal:Destroy()
	end)

	describe("Constructor", function()
		it("should create a new signal and fire it", function()
			expect(Signal.Is(signal)).to.equal(true)
			task.defer(function()
				signal:Fire(10, 20)
			end)
			local n1, n2 = signal:Wait()
			expect(n1).to.equal(10)
			expect(n2).to.equal(20)
		end)

		it("should create a proxy signal and connect to it", function()
			local signalWrap = Signal.Wrap(game:GetService("RunService").Heartbeat)
			expect(Signal.Is(signalWrap)).to.equal(true)
			local fired = false
			signalWrap:Connect(function()
				fired = true
			end)
			expect(AwaitCondition(function()
				return fired
			end, 2)).to.equal(true)
			signalWrap:Destroy()
		end)
	end)

	describe("FireDeferred", function()
		it("should be able to fire primitive argument", function()
			local send = 10
			local value
			signal:Connect(function(v)
				value = v
			end)
			signal:FireDeferred(send)
			expect(AwaitCondition(function()
				return (send == value)
			end, 1)).to.equal(true)
		end)

		it("should be able to fire a reference based argument", function()
			local send = { 10, 20 }
			local value
			signal:Connect(function(v)
				value = v
			end)
			signal:FireDeferred(send)
			expect(AwaitCondition(function()
				return (send == value)
			end, 1)).to.equal(true)
		end)
	end)

	describe("Fire", function()
		it("should be able to fire primitive argument", function()
			local send = 10
			local value
			signal:Connect(function(v)
				value = v
			end)
			signal:Fire(send)
			expect(value).to.equal(send)
		end)

		it("should be able to fire a reference based argument", function()
			local send = { 10, 20 }
			local value
			signal:Connect(function(v)
				value = v
			end)
			signal:Fire(send)
			expect(value).to.equal(send)
		end)
	end)

	describe("ConnectOnce", function()
		it("should only capture first fire", function()
			local value
			local c = signal:ConnectOnce(function(v)
				value = v
			end)
			expect(c.Connected).to.equal(true)
			signal:Fire(10)
			expect(c.Connected).to.equal(false)
			signal:Fire(20)
			expect(value).to.equal(10)
		end)
	end)

	describe("Wait", function()
		it("should be able to wait for a signal to fire", function()
			task.defer(function()
				signal:Fire(10, 20, 30)
			end)
			local n1, n2, n3 = signal:Wait()
			expect(n1).to.equal(10)
			expect(n2).to.equal(20)
			expect(n3).to.equal(30)
		end)
	end)

	describe("DisconnectAll", function()
		it("should disconnect all connections", function()
			signal:Connect(function() end)
			signal:Connect(function() end)
			expect(NumConns()).to.equal(2)
			signal:DisconnectAll()
			expect(NumConns()).to.equal(0)
		end)
	end)

	describe("Disconnect", function()
		it("should disconnect connection", function()
			local con = signal:Connect(function() end)
			expect(NumConns()).to.equal(1)
			con:Disconnect()
			expect(NumConns()).to.equal(0)
		end)

		it("should still work if connections disconnected while firing", function()
			local a = 0
			local c
			signal:Connect(function()
				a += 1
			end)
			c = signal:Connect(function()
				c:Disconnect()
				a += 1
			end)
			signal:Connect(function()
				a += 1
			end)
			signal:Fire()
			expect(a).to.equal(3)
		end)

		it("should still work if connections disconnected while firing deferred", function()
			local a = 0
			local c
			signal:Connect(function()
				a += 1
			end)
			c = signal:Connect(function()
				c:Disconnect()
				a += 1
			end)
			signal:Connect(function()
				a += 1
			end)
			signal:FireDeferred()
			expect(AwaitCondition(function()
				return a == 3
			end)).to.equal(true)
		end)
	end)
end
