return function()local Z=require(script.Parent)describe("\084\114\111\118\101",function()local X beforeEach(function()X=Z.new()end)afterEach(function()if X then X:Destroy()X=nil end end)it("\115\104\111\117\108\100 \097\100\100 \097\110\100 \099\108\101\097\110 \117\112 \114\111\098\108\111\120 \105\110\115\116\097\110\099\101",function()local Z=Instance.new("\080\097\114\116")Z.Parent=workspace X:Add(Z)X:Destroy();(expect(Z.Parent)).to.equal(nil)end)it("\115\104\111\117\108\100 \097\100\100 \097\110\100 \099\108\101\097\110 \117\112 \114\111\098\108\111\120 \099\111\110\110\101\099\116\105\111\110",function()local Z=workspace.Changed:Connect(function() end)X:Add(Z)X:Destroy();(expect(Z.Connected)).to.equal(false)end)it("\115\104\111\117\108\100 \097\100\100 \097\110\100 \099\108\101\097\110 \117\112 \097 \116\097\098\108\101 \119\105\116\104 \097 \100\101\115\116\114\111\121 \109\101\116\104\111\100",function()local Z={Destroyed=false}function Z.Destroy(Z)Z.Destroyed=true end X:Add(Z)X:Destroy();(expect(Z.Destroyed)).to.equal(true)end)it("\115\104\111\117\108\100 \097\100\100 \097\110\100 \099\108\101\097\110 \117\112 \097 \116\097\098\108\101 \119\105\116\104 \097 \100\105\115\099\111\110\110\101\099\116 \109\101\116\104\111\100",function()local Z={Connected=true}function Z.Disconnect(Z)Z.Connected=false end X:Add(Z)X:Destroy();(expect(Z.Connected)).to.equal(false)end)it("\115\104\111\117\108\100 \097\100\100 \097\110\100 \099\108\101\097\110 \117\112 \097 \102\117\110\099\116\105\111\110",function()local Z=false X:Add(function()Z=true end)X:Destroy();(expect(Z)).to.equal(true)end)it("\115\104\111\117\108\100 \097\108\108\111\119 \097 \099\117\115\116\111\109 \099\108\101\097\110\117\112 \109\101\116\104\111\100",function()local Z={Cleaned=false}function Z.Cleanup(Z)Z.Cleaned=true end X:Add(Z,"\067\108\101\097\110\117\112")X:Destroy();(expect(Z.Cleaned)).to.equal(true)end)it("\115\104\111\117\108\100 \114\101\116\117\114\110 \116\104\101 \111\098\106\101\099\116 \112\097\115\115\101\100 \116\111 \097\100\100",function()local Z=Instance.new("\080\097\114\116")local v=X:Add(Z);(expect(Z)).to.equal(v)X:Destroy()end)it("\115\104\111\117\108\100 \102\097\105\108 \116\111 \097\100\100 \111\098\106\101\099\116 \119\105\116\104\111\117\116 \112\114\111\112\101\114 \099\108\101\097\110\117\112 \109\101\116\104\111\100",function()local Z={};(expect(function()X:Add(Z)end)).to.throw()end)it("\115\104\111\117\108\100 \099\111\110\115\116\114\117\099\116 \097\110 \111\098\106\101\099\116 \097\110\100 \097\100\100 \105\116",function()local Z={}Z.__index=Z function Z.new(X)local v=setmetatable({},Z)v._msg=X v._destroyed=false return v end function Z.Destroy(Z)Z._destroyed=true end local v="\097\098\099"local o=X:Construct(Z,v);(expect(typeof(o))).to.equal("\116\097\098\108\101");(expect(getmetatable(o))).to.equal(Z);(expect(o._msg)).to.equal(v);(expect(o._destroyed)).to.equal(false)X:Destroy();(expect(o._destroyed)).to.equal(true)end)it("\115\104\111\117\108\100 \099\111\110\110\101\099\116 \116\111 \097 \115\105\103\110\097\108",function()local Z=X:Connect(workspace.Changed,function() end);(expect(typeof(Z))).to.equal("\082\066\088\083\099\114\105\112\116\067\111\110\110\101\099\116\105\111\110");(expect(Z.Connected)).to.equal(true)X:Destroy();(expect(Z.Connected)).to.equal(false)end)it("\115\104\111\117\108\100 \114\101\109\111\118\101 \097\110 \111\098\106\101\099\116",function()local Z=X:Connect(workspace.Changed,function() end);(expect(X:Remove(Z))).to.equal(true);(expect(Z.Connected)).to.equal(false)end)it("\115\104\111\117\108\100 \110\111\116 \114\101\109\111\118\101 \097\110 \111\098\106\101\099\116 \110\111\116 \105\110 \116\104\101 \116\114\111\118\101",function()local Z=workspace.Changed:Connect(function() end);(expect(X:Remove(Z))).to.equal(false);(expect(Z.Connected)).to.equal(true)Z:Disconnect()end)it("\115\104\111\117\108\100 \097\116\116\097\099\104 \116\111 \105\110\115\116\097\110\099\101",function()local Z=Instance.new("\080\097\114\116")Z.Parent=workspace local v=X:AttachToInstance(Z);(expect(v.Connected)).to.equal(true)Z:Destroy();(expect(v.Connected)).to.equal(false)end)it("\115\104\111\117\108\100 \102\097\105\108 \116\111 \097\116\116\097\099\104 \116\111 \105\110\115\116\097\110\099\101 \110\111\116 \105\110 \104\105\101\114\097\114\099\104\121",function()local Z=Instance.new("\080\097\114\116");(expect(function()X:AttachToInstance(Z)end)).to.throw()end)it("\115\104\111\117\108\100 \101\120\116\101\110\100 \105\116\115\101\108\102",function()local v=X:Extend()local o=false v:Add(function()o=true end);(expect(v)).to.be.a("\116\097\098\108\101");(expect(getmetatable(v))).to.equal(Z)X:Clean();(expect(o)).to.equal(true)end)it("\115\104\111\117\108\100 \099\108\111\110\101 \097\110 \105\110\115\116\097\110\099\101",function()local Z="\084\114\111\118\101\067\108\111\110\101\084\101\115\116"local v=X:Construct(Instance.new,"\080\097\114\116")v.Name=Z local o=X:Clone(v);(expect(typeof(o))).to.equal("\073\110\115\116\097\110\099\101");(expect(o)).to.never.equal(v);(expect(o.Name)).to.equal(Z);(expect(v.Name)).to.equal(o.Name)end)it("\115\104\111\117\108\100 \099\108\101\097\110 \117\112 \097 \116\104\114\101\097\100",function()local Z=coroutine.create(function() end)X:Add(Z);(expect(coroutine.status(Z))).to.equal("\115\117\115\112\101\110\100\101\100")X:Clean();(expect(coroutine.status(Z))).to.equal("\100\101\097\100")end)end)end
return function()
	local Trove = require(script.Parent)

	describe("Trove", function()
		local trove

		beforeEach(function()
			trove = Trove.new()
		end)

		afterEach(function()
			if trove then
				trove:Destroy()
				trove = nil
			end
		end)

		it("should add and clean up roblox instance", function()
			local part = Instance.new("Part")
			part.Parent = workspace
			trove:Add(part)
			trove:Destroy()
			expect(part.Parent).to.equal(nil)
		end)

		it("should add and clean up roblox connection", function()
			local connection = workspace.Changed:Connect(function() end)
			trove:Add(connection)
			trove:Destroy()
			expect(connection.Connected).to.equal(false)
		end)

		it("should add and clean up a table with a destroy method", function()
			local tbl = { Destroyed = false }
			function tbl:Destroy()
				self.Destroyed = true
			end
			trove:Add(tbl)
			trove:Destroy()
			expect(tbl.Destroyed).to.equal(true)
		end)

		it("should add and clean up a table with a disconnect method", function()
			local tbl = { Connected = true }
			function tbl:Disconnect()
				self.Connected = false
			end
			trove:Add(tbl)
			trove:Destroy()
			expect(tbl.Connected).to.equal(false)
		end)

		it("should add and clean up a function", function()
			local fired = false
			trove:Add(function()
				fired = true
			end)
			trove:Destroy()
			expect(fired).to.equal(true)
		end)

		it("should allow a custom cleanup method", function()
			local tbl = { Cleaned = false }
			function tbl:Cleanup()
				self.Cleaned = true
			end
			trove:Add(tbl, "Cleanup")
			trove:Destroy()
			expect(tbl.Cleaned).to.equal(true)
		end)

		it("should return the object passed to add", function()
			local part = Instance.new("Part")
			local part2 = trove:Add(part)
			expect(part).to.equal(part2)
			trove:Destroy()
		end)

		it("should fail to add object without proper cleanup method", function()
			local tbl = {}
			expect(function()
				trove:Add(tbl)
			end).to.throw()
		end)

		it("should construct an object and add it", function()
			local class = {}
			class.__index = class
			function class.new(msg)
				local self = setmetatable({}, class)
				self._msg = msg
				self._destroyed = false
				return self
			end
			function class:Destroy()
				self._destroyed = true
			end
			local msg = "abc"
			local obj = trove:Construct(class, msg)
			expect(typeof(obj)).to.equal("table")
			expect(getmetatable(obj)).to.equal(class)
			expect(obj._msg).to.equal(msg)
			expect(obj._destroyed).to.equal(false)
			trove:Destroy()
			expect(obj._destroyed).to.equal(true)
		end)

		it("should connect to a signal", function()
			local connection = trove:Connect(workspace.Changed, function() end)
			expect(typeof(connection)).to.equal("RBXScriptConnection")
			expect(connection.Connected).to.equal(true)
			trove:Destroy()
			expect(connection.Connected).to.equal(false)
		end)

		it("should remove an object", function()
			local connection = trove:Connect(workspace.Changed, function() end)
			expect(trove:Remove(connection)).to.equal(true)
			expect(connection.Connected).to.equal(false)
		end)

		it("should not remove an object not in the trove", function()
			local connection = workspace.Changed:Connect(function() end)
			expect(trove:Remove(connection)).to.equal(false)
			expect(connection.Connected).to.equal(true)
			connection:Disconnect()
		end)

		it("should attach to instance", function()
			local part = Instance.new("Part")
			part.Parent = workspace
			local connection = trove:AttachToInstance(part)
			expect(connection.Connected).to.equal(true)
			part:Destroy()
			expect(connection.Connected).to.equal(false)
		end)

		it("should fail to attach to instance not in hierarchy", function()
			local part = Instance.new("Part")
			expect(function()
				trove:AttachToInstance(part)
			end).to.throw()
		end)

		it("should extend itself", function()
			local subTrove = trove:Extend()
			local called = false
			subTrove:Add(function()
				called = true
			end)
			expect(subTrove).to.be.a("table")
			expect(getmetatable(subTrove)).to.equal(Trove)
			trove:Clean()
			expect(called).to.equal(true)
		end)

		it("should clone an instance", function()
			local name = "TroveCloneTest"
			local p1 = trove:Construct(Instance.new, "Part")
			p1.Name = name
			local p2 = trove:Clone(p1)
			expect(typeof(p2)).to.equal("Instance")
			expect(p2).to.never.equal(p1)
			expect(p2.Name).to.equal(name)
			expect(p1.Name).to.equal(p2.Name)
		end)

		it("should clean up a thread", function()
			local co = coroutine.create(function() end)
			trove:Add(co)
			expect(coroutine.status(co)).to.equal("suspended")
			trove:Clean()
			expect(coroutine.status(co)).to.equal("dead")
		end)
	end)
end
