local v=game:GetService("\082\117\110\083\101\114\118\105\099\101")local j=require(script.Parent.Parent.Promise)local d=require(script.Parent)local z=require(script.Parent.Connection)return function()describe("\084\114\117\101\083\105\103\110\097\108\046\110\101\119",function()it("\115\104\111\117\108\100 \099\114\101\097\116\101 \097 \110\101\119 \115\105\103\110\097\108 \111\098\106\101\099\116",function()local v=d.new();(expect(v)).to.be.a("\116\097\098\108\101");(expect(getmetatable(v))).to.equal(d)v:destroy()end)end)describe("\084\114\117\101\083\105\103\110\097\108\058\102\105\114\101",function()it("\115\104\111\117\108\100 \102\105\114\101 \099\111\110\110\101\099\116\105\111\110\115 \119\105\116\104 \109\117\108\116\105\112\108\101 \097\114\103\115",function()local v=d.new()local j={}local z=v:connect(function(...)table.insert(j,table.pack(...))end)v:fire(1,2,3)v:fire("\072\101\108\108\111 ","\102\114\105\101\110\100");(expect(j[1].n)).to.equal(3);(expect(j[1][1])).to.equal(1);(expect(j[1][2])).to.equal(2);(expect(j[1][3])).to.equal(3);(expect(j[2].n)).to.equal(2);(expect(j[2][1])).to.equal("\072\101\108\108\111 ");(expect(j[2][2])).to.equal("\102\114\105\101\110\100")z:destroy()v:destroy()end)it("\115\104\111\117\108\100 \102\105\114\101 \099\111\110\110\101\099\116\101\100 \099\111\110\110\101\099\116\105\111\110\115 \105\109\109\101\100\105\097\116\101\108\121 \105\102 \100\101\102\101\114\114\101\100\061\102\097\108\115\101",function()local v=d.new()local j,z={},{}local T=v:connect(function(v)j[v]=true end)v:fire(1)local Z=v:connect(function(v)z[v]=true end)v:fire(2);(expect(j[1])).to.be.ok();(expect(j[2])).to.be.ok();(expect(z[1])).to.never.be.ok();(expect(z[2])).to.be.ok()T:destroy()Z:destroy()v:destroy()end)it("\115\104\111\117\108\100 \102\105\114\101 \099\111\110\110\101\099\116\101\100 \099\111\110\110\101\099\116\105\111\110\115 \101\110\100-\111\102-\102\114\097\109\101 \105\102 \100\101\102\101\114\114\101\100\061\116\114\117\101",function()local v=d.new(true)local j,z={},{}local T=v:connect(function(v)j[v]=true end)v:fire(1)local Z=v:connect(function(v)z[v]=true end)v:fire(2);(expect(j[1])).to.never.be.ok();(expect(j[2])).to.never.be.ok();(expect(z[1])).to.never.be.ok();(expect(z[2])).to.never.be.ok()task.defer(task.spawn,coroutine.running())coroutine.yield();(expect(j[1])).to.be.ok();(expect(j[2])).to.be.ok();(expect(z[1])).to.never.be.ok();(expect(z[2])).to.be.ok()T:destroy()Z:destroy()v:destroy()end)end)describe("\084\114\117\101\083\105\103\110\097\108\058\099\111\110\110\101\099\116",function()it("\115\104\111\117\108\100 \114\101\116\117\114\110 \097 \099\111\110\110\101\099\116\105\111\110 \116\104\097\116 \105\115 \099\111\110\110\101\099\116\101\100 \105\110\105\116\105\097\108\108\121",function()local v=d.new()local j=v:connect(function() end);(expect(j)).to.be.a("\116\097\098\108\101");(expect(getmetatable(j))).to.equal(z);(expect(j.connected)).to.equal(true)v:destroy()end)it("\115\104\111\117\108\100 \116\104\114\111\119 \099\111\110\110\101\099\116\105\111\110 \104\097\110\100\108\101\114 \105\102 \099\111\110\110\101\099\116\105\111\110 \105\115 \100\105\115\099\111\110\110\101\099\116\101\100 \102\114\111\109 \105\110\115\105\100\101 \104\097\110\100\108\101\114 \105\102 \100\101\102\101\114\114\101\100\061\102\097\108\115\101 \097\110\100 \113\117\101\117\101\105\110\103\061\116\114\117\101",function()local v=d.new(false,true)local j,z=0,0 local T v:fire()v:fire()T=v:connect(function()local v=pcall(function()T:disconnect()end)if v then j+=1 else z+=1 end end);(expect(j)).to.equal(0);(expect(z)).to.equal(2)T:destroy()v:destroy()end)it("\115\104\111\117\108\100 \102\105\114\101 \116\104\101 \099\111\110\110\101\099\116\105\111\110 \105\109\109\101\100\105\097\116\101\108\121 \119\105\116\104 \113\117\101\117\101\100 \097\114\103\115 \105\102 \100\101\102\101\114\114\101\100\061\102\097\108\115\101 \097\110\100 \113\117\101\117\101\105\110\103\061\116\114\117\101",function()local v=d.new(false,true)local j={}v:fire(1)v:fire(2)v:connect(function(v)j[v]=true end);(expect(j[1])).to.be.ok();(expect(j[2])).to.be.ok()v:destroy()end)it("\115\104\111\117\108\100 \102\105\114\101 \116\104\101 \099\111\110\110\101\099\116\105\111\110 \101\110\100-\111\102-\102\114\097\109\101 \119\105\116\104 \113\117\101\117\101\100 \097\114\103\115 \105\102 \100\101\102\101\114\114\101\100\061\116\114\117\101 \097\110\100 \113\117\101\117\101\105\110\103\061\116\114\117\101",function()local v=d.new(true,true)local j={}v:fire(1)v:fire(2)v:connect(function(v)j[v]=true end);(expect(j[1])).to.never.be.ok();(expect(j[2])).to.never.be.ok()task.defer(task.spawn,coroutine.running())coroutine.yield();(expect(j[1])).to.be.ok();(expect(j[2])).to.be.ok()v:destroy()end)end)describe("\084\114\117\101\083\105\103\110\097\108\058\102\108\117\115\104",function()it("\115\104\111\117\108\100 \114\101\109\111\118\101 \097\110\121 \113\117\101\117\101\100 \097\114\103\115 \097\110\100 \119\111\114\107 \112\114\111\112\101\114\108\121 \105\102 \100\101\102\101\114\114\101\100\061\102\097\108\115\101",function()local v=d.new(false,true)v:fire(1)v:fire(2)v:flush()v:fire(3);(expect(v:wait())).to.equal(3)v:destroy()end)it("\115\104\111\117\108\100 \114\101\109\111\118\101 \097\110\121 \113\117\101\117\101\100 \097\114\103\115 \097\110\100 \119\111\114\107 \112\114\111\112\101\114\108\121 \105\102 \100\101\102\101\114\114\101\100\061\116\114\117\101",function()local v=d.new(true,true)v:fire(1)v:fire(2)v:flush()v:fire(3);(expect(v:wait())).to.equal(3)v:destroy()end)end)describe("\084\114\117\101\083\105\103\110\097\108\058\119\097\105\116",function()it("\115\104\111\117\108\100 \121\105\101\108\100 \117\110\116\105\108 \115\105\103\110\097\108 \105\115 \102\105\114\101\100 \097\110\100 \114\101\116\117\114\110 \112\097\115\115\101\100 \097\114\103\115 \102\114\111\109 \116\104\101 \102\105\114\101 \099\097\108\108 \105\102 \100\101\102\101\114\114\101\100\061\102\097\108\115\101 \097\110\100 \113\117\101\117\101\105\110\103\061\102\097\108\115\101",function()local v=d.new()local j,z task.spawn(function()j,z=v:wait()end);(expect(j)).to.never.be.ok();(expect(z)).to.never.be.ok()v:fire("\116\119\111","\109\101\115\115\097\103\101\115");(expect(j)).to.equal("\116\119\111");(expect(z)).to.equal("\109\101\115\115\097\103\101\115")v:destroy()end)it("\115\104\111\117\108\100 \121\105\101\108\100 \117\110\116\105\108 \101\110\100 \111\102 \102\114\097\109\101 \119\104\101\110 \115\105\103\110\097\108 \105\115 \102\105\114\101\100 \097\110\100 \114\101\116\117\114\110 \112\097\115\115\101\100 \097\114\103\115 \102\114\111\109 \116\104\101 \102\105\114\101 \099\097\108\108 \105\102 \100\101\102\101\114\114\101\100\061\116\114\117\101 \097\110\100 \113\117\101\117\101\105\110\103\061\102\097\108\115\101",function()local v=d.new(true)local j,z task.spawn(function()j,z=v:wait()end);(expect(j)).to.never.be.ok();(expect(z)).to.never.be.ok()v:fire("\116\119\111","\109\101\115\115\097\103\101\115");(expect(j)).to.never.be.ok();(expect(z)).to.never.be.ok()task.defer(task.spawn,coroutine.running())coroutine.yield();(expect(j)).to.equal("\116\119\111");(expect(z)).to.equal("\109\101\115\115\097\103\101\115")v:destroy()end)it("\105\116 \115\104\111\117\108\100 \114\101\116\117\114\110 \097\114\103\115 \112\111\112\112\101\100 \102\114\111\109 \116\104\101 \113\117\101\117\101 \105\109\109\101\100\105\097\116\101\108\121 \100\101\102\101\114\114\101\100\061\102\097\108\115\101 \097\110\100 \113\117\101\117\101\105\110\103\061\116\114\117\101",function()local v=d.new(false,true)v:fire(1)v:fire(2)local j=v:wait()local z=v:wait();(expect(j)).to.equal(1);(expect(z)).to.equal(2)v:destroy()end)it("\105\116 \115\104\111\117\108\100 \114\101\116\117\114\110 \097\114\103\115 \112\111\112\112\101\100 \102\114\111\109 \116\104\101 \113\117\101\117\101 \097\116 \116\104\101 \101\110\100 \111\102 \116\104\101 \102\114\097\109\101 \105\102 \100\101\102\101\114\114\101\100\061\116\114\117\101 \097\110\100 \113\117\101\117\101\105\110\103\061\116\114\117\101",function()local v=d.new(true,true)local j,z v:fire(1)v:fire(2)task.spawn(function()j=v:wait()end)task.spawn(function()z=v:wait()end);(expect(j)).to.never.be.ok();(expect(z)).to.never.be.ok()task.defer(task.spawn,coroutine.running())coroutine.yield();(expect(j)).to.equal(1);(expect(z)).to.equal(2)v:destroy()end)end)describe("\084\114\117\101\083\105\103\110\097\108\058\112\114\111\109\105\115\101",function()it("\115\104\111\117\108\100 \114\101\116\117\114\110 \097 \112\114\111\109\105\115\101 \116\104\097\116 \114\101\115\111\108\118\101\115 \105\109\109\101\100\105\097\116\101\108\121 \119\105\116\104 \116\104\101 \097\114\103\115 \112\097\115\115\101\100 \105\110 \116\104\101 \110\101\120\116 \102\105\114\101 \099\097\108\108 \105\102 \100\101\102\101\114\114\101\100\061\102\097\108\115\101 \097\110\100 \113\117\101\117\101\105\110\103\061\102\097\108\115\101",function()local v=d.new()local z=v:promise();(expect(z:getStatus())).to.equal(j.Status.Started)v:fire("\109\101\115\115\097\103\101");(expect(z:getStatus())).to.equal(j.Status.Resolved);(expect(z:expect())).to.equal("\109\101\115\115\097\103\101")v:destroy()end)it("\115\104\111\117\108\100 \114\101\116\117\114\110 \097 \112\114\111\109\105\115\101 \116\104\097\116 \114\101\115\111\108\118\101\115 \097\116 \116\104\101 \101\110\100 \111\102 \116\104\101 \102\114\097\109\101 \119\105\116\104 \116\104\101 \097\114\103\115 \112\097\115\115\101\100 \105\110 \116\104\101 \110\101\120\116 \102\105\114\101 \099\097\108\108 \105\102 \100\101\102\101\114\114\101\100\061\116\114\117\101 \097\110\100 \113\117\101\117\101\105\110\103\061\102\097\108\115\101",function()local v=d.new(true)local z=v:promise();(expect(z:getStatus())).to.equal(j.Status.Started)v:fire("\109\101\115\115\097\103\101");(expect(z:getStatus())).to.equal(j.Status.Started)task.defer(task.spawn,coroutine.running())coroutine.yield();(expect(z:expect())).to.equal("\109\101\115\115\097\103\101")v:destroy()end)it("\115\104\111\117\108\100 \114\101\116\117\114\110 \097 \112\114\111\109\105\115\101 \116\104\097\116 \114\101\115\111\108\118\101\115 \105\109\109\101\100\105\097\116\101\108\121 \119\105\116\104 \097\114\103\115 \112\111\112\112\101\100 \102\114\111\109 \116\104\101 \113\117\101\117\101 \105\102 \100\101\102\101\114\114\101\100\061\102\097\108\115\101 \097\110\100 \113\117\101\117\101\105\110\103\061\116\114\117\101",function()local v=d.new(false,true)v:fire(1)v:fire(2);(expect((v:promise()):expect())).to.equal(1);(expect((v:promise()):expect())).to.equal(2)v:destroy()end)it("\115\104\111\117\108\100 \114\101\116\117\114\110 \097 \112\114\111\109\105\115\101 \116\104\097\116 \114\101\115\111\108\118\101\115 \097\116 \116\104\101 \101\110\100 \111\102 \116\104\101 \102\114\097\109\101 \119\105\116\104 \097\114\103\115 \112\111\112\112\101\100 \102\114\111\109 \116\104\101 \113\117\101\117\101 \105\102 \100\101\102\101\114\114\101\100\061\116\114\117\101 \097\110\100 \113\117\101\117\101\105\110\103\061\116\114\117\101",function()local v=d.new(true,true)v:fire(1)v:fire(2)local z=v:promise()local T=v:promise();(expect(z:getStatus())).to.equal(j.Status.Started);(expect(T:getStatus())).to.equal(j.Status.Started)task.defer(task.spawn,coroutine.running())coroutine.yield();(expect(z:getStatus())).to.equal(j.Status.Resolved);(expect(T:getStatus())).to.equal(j.Status.Resolved);(expect(z:expect())).to.equal(1);(expect(T:expect())).to.equal(2)v:destroy()end)end)describe("\084\114\117\101\083\105\103\110\097\108\058\100\101\115\116\114\111\121",function()it("\115\104\111\117\108\100 \115\101\116 \100\101\115\116\114\111\121\101\100 \102\105\101\108\100 \116\111 \116\114\117\101",function()local v=d.new()v:destroy();(expect(v.destroyed)).to.equal(true)end)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\101\100 \099\111\110\110\101\099\116\105\111\110\115",function()local v=d.new()local j=v:connect(function() end)local z=v:connect(function() end);(expect(j.connected)).to.equal(true);(expect(z.connected)).to.equal(true)v:destroy();(expect(j.connected)).to.equal(false);(expect(z.connected)).to.equal(false)end)end)end
local RunService = game:GetService("RunService")

local Promise = require(script.Parent.Parent.Promise)

local TrueSignal = require(script.Parent)
local Connection = require(script.Parent.Connection)

return function()
    describe("TrueSignal.new", function()
        it("should create a new signal object", function()
            local signal = TrueSignal.new()

            expect(signal).to.be.a("table")
            expect(getmetatable(signal)).to.equal(TrueSignal)

            signal:destroy()
        end)
    end)
    
    describe("TrueSignal:fire", function()
        it("should fire connections with multiple args", function()
            local signal = TrueSignal.new()
            local done = {}

            local connection = signal:connect(function(...)
                table.insert(done, table.pack(...))
            end)

            signal:fire(1, 2, 3)
            signal:fire("Hello ", "friend")

            expect(done[1].n).to.equal(3)
            expect(done[1][1]).to.equal(1)
            expect(done[1][2]).to.equal(2)
            expect(done[1][3]).to.equal(3)

            expect(done[2].n).to.equal(2)
            expect(done[2][1]).to.equal("Hello ")
            expect(done[2][2]).to.equal("friend")

            connection:destroy()
            signal:destroy()
        end)

        it("should fire connected connections immediately if deferred=false", function()
            local signal = TrueSignal.new()
            local done1, done2 = {}, {}

            local connection1 = signal:connect(function(index)
                done1[index] = true
            end)

            signal:fire(1)

            local connection2 = signal:connect(function(index)
                done2[index] = true
            end)

            signal:fire(2)

            expect(done1[1]).to.be.ok()
            expect(done1[2]).to.be.ok()

            expect(done2[1]).to.never.be.ok()
            expect(done2[2]).to.be.ok()

            connection1:destroy()
            connection2:destroy()
            signal:destroy()
        end)

        it("should fire connected connections end-of-frame if deferred=true", function()
            local signal = TrueSignal.new(true)
            local done1, done2 = {}, {}

            local connection1 = signal:connect(function(index)
                done1[index] = true
            end)

            signal:fire(1)

            local connection2 = signal:connect(function(index)
                done2[index] = true
            end)

            signal:fire(2)

            expect(done1[1]).to.never.be.ok()
            expect(done1[2]).to.never.be.ok()
            
            expect(done2[1]).to.never.be.ok()
            expect(done2[2]).to.never.be.ok()

            task.defer(task.spawn, coroutine.running())
            coroutine.yield()
            
            expect(done1[1]).to.be.ok()
            expect(done1[2]).to.be.ok()
            
            expect(done2[1]).to.never.be.ok()
            expect(done2[2]).to.be.ok()

            connection1:destroy()
            connection2:destroy()
            signal:destroy()
        end)
    end)

    describe("TrueSignal:connect", function()
        it("should return a connection that is connected initially", function()
            local signal = TrueSignal.new()
            local connection = signal:connect(function() end)

            expect(connection).to.be.a("table")
            expect(getmetatable(connection)).to.equal(Connection)

            expect(connection.connected).to.equal(true)
            
            signal:destroy()
        end)

        it("should throw connection handler if connection is disconnected from inside handler if deferred=false and queueing=true", function()
            local signal = TrueSignal.new(false, true)

            local passes, fails = 0, 0
            local connection

            signal:fire()
            signal:fire()

            connection = signal:connect(function()
                local success = pcall(function()
                    connection:disconnect()
                end)

                if success then
                    passes += 1
                else
                    fails += 1
                end
            end)

            expect(passes).to.equal(0)
            expect(fails).to.equal(2)

            connection:destroy()
            signal:destroy()
        end)

        it("should fire the connection immediately with queued args if deferred=false and queueing=true", function()
            local signal = TrueSignal.new(false, true)
            local done = {}

            signal:fire(1)
            signal:fire(2)

            signal:connect(function(index)
                done[index] = true
            end)

            expect(done[1]).to.be.ok()
            expect(done[2]).to.be.ok()

            signal:destroy()
        end)

        it("should fire the connection end-of-frame with queued args if deferred=true and queueing=true", function()
            local signal = TrueSignal.new(true, true)
            local done = {}

            signal:fire(1)
            signal:fire(2)

            signal:connect(function(index)
                done[index] = true
            end)

            expect(done[1]).to.never.be.ok()
            expect(done[2]).to.never.be.ok()

            task.defer(task.spawn, coroutine.running())
            coroutine.yield()

            expect(done[1]).to.be.ok()
            expect(done[2]).to.be.ok()

            signal:destroy()
        end)
    end)

    describe("TrueSignal:flush", function()
        it("should remove any queued args and work properly if deferred=false", function()
            local signal = TrueSignal.new(false, true)
            
            signal:fire(1)
            signal:fire(2)

            signal:flush()

            signal:fire(3)

            expect(signal:wait()).to.equal(3)

            signal:destroy()
        end)

        it("should remove any queued args and work properly if deferred=true", function()
            local signal = TrueSignal.new(true, true)
            
            signal:fire(1)
            signal:fire(2)

            signal:flush()

            signal:fire(3)

            expect(signal:wait()).to.equal(3)

            signal:destroy()
        end)
    end)
    
    describe("TrueSignal:wait", function()
        it("should yield until signal is fired and return passed args from the fire call if deferred=false and queueing=false", function()
            local signal = TrueSignal.new()
            local message1, message2

            task.spawn(function()
                message1, message2 = signal:wait()
            end)

            expect(message1).to.never.be.ok()
            expect(message2).to.never.be.ok()

            signal:fire("two", "messages")

            expect(message1).to.equal("two")
            expect(message2).to.equal("messages")

            signal:destroy()
        end)

        it("should yield until end of frame when signal is fired and return passed args from the fire call if deferred=true and queueing=false", function()
            local signal = TrueSignal.new(true)
            local message1, message2

            task.spawn(function()
                message1, message2 = signal:wait()
            end)

            expect(message1).to.never.be.ok()
            expect(message2).to.never.be.ok()

            signal:fire("two", "messages")

            expect(message1).to.never.be.ok()
            expect(message2).to.never.be.ok()

            task.defer(task.spawn, coroutine.running())
            coroutine.yield()

            expect(message1).to.equal("two")
            expect(message2).to.equal("messages")

            signal:destroy()
        end)

        it("it should return args popped from the queue immediately deferred=false and queueing=true", function()
            local signal = TrueSignal.new(false, true)

            signal:fire(1)
            signal:fire(2)

            local popped1 = signal:wait()
            local popped2 = signal:wait()

            expect(popped1).to.equal(1)
            expect(popped2).to.equal(2)

            signal:destroy()
        end)

        it("it should return args popped from the queue at the end of the frame if deferred=true and queueing=true", function()
            local signal = TrueSignal.new(true, true)
            local popped1, popped2

            signal:fire(1)
            signal:fire(2)

            task.spawn(function()
                popped1 = signal:wait()
            end)
            
            task.spawn(function()
                popped2 = signal:wait()
            end)

            expect(popped1).to.never.be.ok()
            expect(popped2).to.never.be.ok()

            task.defer(task.spawn, coroutine.running())
            coroutine.yield()

            expect(popped1).to.equal(1)
            expect(popped2).to.equal(2)

            signal:destroy()
        end)
    end)
    
    describe("TrueSignal:promise", function()
        it("should return a promise that resolves immediately with the args passed in the next fire call if deferred=false and queueing=false", function()
            local signal = TrueSignal.new()
            local promise = signal:promise()

            expect(promise:getStatus()).to.equal(Promise.Status.Started)

            signal:fire("message")

            expect(promise:getStatus()).to.equal(Promise.Status.Resolved)
            expect(promise:expect()).to.equal("message")

            signal:destroy()
        end)

        it("should return a promise that resolves at the end of the frame with the args passed in the next fire call if deferred=true and queueing=false", function()
            local signal = TrueSignal.new(true)
            local promise = signal:promise()

            expect(promise:getStatus()).to.equal(Promise.Status.Started)

            signal:fire("message")

            expect(promise:getStatus()).to.equal(Promise.Status.Started)

            task.defer(task.spawn, coroutine.running())
            coroutine.yield()

            expect(promise:expect()).to.equal("message")

            signal:destroy()
        end)

        it("should return a promise that resolves immediately with args popped from the queue if deferred=false and queueing=true", function()
            local signal = TrueSignal.new(false, true)

            signal:fire(1)
            signal:fire(2)
            
            expect(signal:promise():expect()).to.equal(1)
            expect(signal:promise():expect()).to.equal(2)

            signal:destroy()
        end)

        it("should return a promise that resolves at the end of the frame with args popped from the queue if deferred=true and queueing=true", function()
            local signal = TrueSignal.new(true, true)

            signal:fire(1)
            signal:fire(2)
            
            local promise1 = signal:promise()
            local promise2 = signal:promise()
            
            expect(promise1:getStatus()).to.equal(Promise.Status.Started)
            expect(promise2:getStatus()).to.equal(Promise.Status.Started)

            task.defer(task.spawn, coroutine.running())
            coroutine.yield()

            expect(promise1:getStatus()).to.equal(Promise.Status.Resolved)
            expect(promise2:getStatus()).to.equal(Promise.Status.Resolved)

            expect(promise1:expect()).to.equal(1)
            expect(promise2:expect()).to.equal(2)

            signal:destroy()
        end)
    end)
    


    describe("TrueSignal:destroy", function()
        it("should set destroyed field to true", function()
            local signal = TrueSignal.new()

            signal:destroy()

            expect(signal.destroyed).to.equal(true)
        end)

        it("should disconnect all connected connections", function()
            local signal = TrueSignal.new()

            local connection1 = signal:connect(function() end)
            local connection2 = signal:connect(function() end)

            expect(connection1.connected).to.equal(true)
            expect(connection2.connected).to.equal(true)

            signal:destroy()

            expect(connection1.connected).to.equal(false)
            expect(connection2.connected).to.equal(false)
        end)
    end)
end