diff --git a/ci/Test.luau b/ci/Test.luau index 90af6f13..72db9369 100644 --- a/ci/Test.luau +++ b/ci/Test.luau @@ -217,6 +217,8 @@ export type TestContext = { Test: (self: TestContext, name: string, fn: () -> ()) -> (), Describe: (self: TestContext, name: string, fn: () -> ()) -> (), Expect: (self: TestContext, value: any) -> TestExpect, + BeforeEach: (self: TestContext, fn: () -> ()) -> (), + AfterEach: (self: TestContext, fn: () -> ()) -> (), } local TestContext = {} @@ -230,6 +232,8 @@ function TestContext.new(root: string): TestContext Name = root, Items = {}, AnyFail = false, + BeforeEachFns = {}, + AfterEachFns = {}, }, }, TestContext) :: any @@ -239,6 +243,9 @@ function TestContext.new(root: string): TestContext end function TestContext:Test(name: string, fn: () -> ()) + for _, fn in self.Current.BeforeEachFns do + fn() + end local success, err = pcall(fn) self.Current.Items[name] = { Success = success, @@ -249,6 +256,9 @@ function TestContext:Test(name: string, fn: () -> ()) self.TotalFails += 1 self.Current.AnyFail = true end + for _, fn in self.Current.AfterEachFns do + fn() + end end function TestContext:Describe(name: string, fn: () -> ()) @@ -257,6 +267,8 @@ function TestContext:Describe(name: string, fn: () -> ()) Name = name, Items = {}, AnyFail = false, + BeforeEachFns = {}, + AfterEachFns = {}, } self.Current = group parentGroup.Items[name] = group @@ -277,6 +289,14 @@ function TestContext:Expect(value: any): TestExpect return setmetatable({ Value = resolvedValue, Success = success, Err = err, Flip = false }, TestExpect) :: any end +function TestContext:BeforeEach(fn: () -> ()) + table.insert(self.Current.BeforeEachFns, fn) +end + +function TestContext:AfterEach(fn: () -> ()) + table.insert(self.Current.AfterEachFns, fn) +end + ------------------------------------------------------------------------------------------------------------ local Test = {} @@ -351,7 +371,6 @@ function Test.run(ancestors: { Instance }) local outputTxt = table.concat(output, "\n") return { - Results = results, AllPass = allPass, Output = outputTxt, } diff --git a/modules/trove/init.spec.luau b/modules/trove/init.spec.luau deleted file mode 100644 index 1012428a..00000000 --- a/modules/trove/init.spec.luau +++ /dev/null @@ -1,196 +0,0 @@ -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) - - it("should not allow objects added during cleanup", function() - expect(function() - trove:Add(function() - trove:Add(function() end) - end) - trove:Clean() - end).to.throw() - end) - - it("should not allow objects to be removed during cleanup", function() - expect(function() - local f = function() end - trove:Add(f) - trove:Add(function() - trove:Remove(f) - end) - trove:Clean() - end).to.throw() - end) - end) -end diff --git a/modules/trove/init.test.luau b/modules/trove/init.test.luau new file mode 100644 index 00000000..65cebc84 --- /dev/null +++ b/modules/trove/init.test.luau @@ -0,0 +1,200 @@ +local ServerScriptService = game:GetService("ServerScriptService") + +local Test = require(ServerScriptService.TestRunner.Test) + +return function(ctx: Test.TestContext) + local Trove = require(script.Parent) + + ctx:Describe("Trove", function() + local trove + + ctx:BeforeEach(function() + trove = Trove.new() + end) + + ctx:AfterEach(function() + if trove then + trove:Destroy() + trove = nil + end + end) + + ctx:Test("should add and clean up roblox instance", function() + local part = Instance.new("Part") + part.Parent = workspace + trove:Add(part) + trove:Destroy() + ctx:Expect(part.Parent):ToBeNil() + end) + + ctx:Test("should add and clean up roblox connection", function() + local connection = workspace.Changed:Connect(function() end) + trove:Add(connection) + trove:Destroy() + ctx:Expect(connection.Connected):ToBe(false) + end) + + ctx:Test("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() + ctx:Expect(tbl.Destroyed):ToBe(true) + end) + + ctx:Test("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() + ctx:Expect(tbl.Connected):ToBe(false) + end) + + ctx:Test("should add and clean up a function", function() + local fired = false + trove:Add(function() + fired = true + end) + trove:Destroy() + ctx:Expect(fired):ToBe(true) + end) + + ctx:Test("should allow a custom cleanup method", function() + local tbl = { Cleaned = false } + function tbl:Cleanup() + self.Cleaned = true + end + trove:Add(tbl, "Cleanup") + trove:Destroy() + ctx:Expect(tbl.Cleaned):ToBe(true) + end) + + ctx:Test("should return the object passed to add", function() + local part = Instance.new("Part") + local part2 = trove:Add(part) + ctx:Expect(part):ToBe(part2) + trove:Destroy() + end) + + ctx:Test("should fail to add object without proper cleanup method", function() + local tbl = {} + ctx:Expect(function() + trove:Add(tbl) + end):ToThrow() + end) + + ctx:Test("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) + ctx:Expect(typeof(obj)):ToBe("table") + ctx:Expect(getmetatable(obj)):ToBe(class) + ctx:Expect(obj._msg):ToBe(msg) + ctx:Expect(obj._destroyed):ToBe(false) + trove:Destroy() + ctx:Expect(obj._destroyed):ToBe(true) + end) + + ctx:Test("should connect to a signal", function() + local connection = trove:Connect(workspace.Changed, function() end) + ctx:Expect(typeof(connection)):ToBe("RBXScriptConnection") + ctx:Expect(connection.Connected):ToBe(true) + trove:Destroy() + ctx:Expect(connection.Connected):ToBe(false) + end) + + ctx:Test("should remove an object", function() + local connection = trove:Connect(workspace.Changed, function() end) + ctx:Expect(trove:Remove(connection)):ToBe(true) + ctx:Expect(connection.Connected):ToBe(false) + end) + + ctx:Test("should not remove an object not in the trove", function() + local connection = workspace.Changed:Connect(function() end) + ctx:Expect(trove:Remove(connection)):ToBe(false) + ctx:Expect(connection.Connected):ToBe(true) + connection:Disconnect() + end) + + ctx:Test("should attach to instance", function() + local part = Instance.new("Part") + part.Parent = workspace + local connection = trove:AttachToInstance(part) + ctx:Expect(connection.Connected):ToBe(true) + part:Destroy() + ctx:Expect(connection.Connected):ToBe(false) + end) + + ctx:Test("should fail to attach to instance not in hierarchy", function() + local part = Instance.new("Part") + ctx:Expect(function() + trove:AttachToInstance(part) + end):ToThrow() + end) + + ctx:Test("should extend itself", function() + local subTrove = trove:Extend() + local called = false + subTrove:Add(function() + called = true + end) + ctx:Expect(typeof(subTrove)):ToBe("table") + ctx:Expect(getmetatable(subTrove)):ToBe(Trove) + trove:Clean() + ctx:Expect(called):ToBe(true) + end) + + ctx:Test("should clone an instance", function() + local name = "TroveCloneTest" + local p1 = trove:Construct(Instance.new, "Part") + p1.Name = name + local p2 = trove:Clone(p1) + ctx:Expect(typeof(p2)):ToBe("Instance") + ctx:Expect(p2):Not():ToBe(p1) + ctx:Expect(p2.Name):ToBe(name) + ctx:Expect(p1.Name):ToBe(p2.Name) + end) + + ctx:Test("should clean up a thread", function() + local co = coroutine.create(function() end) + trove:Add(co) + ctx:Expect(coroutine.status(co)):ToBe("suspended") + trove:Clean() + ctx:Expect(coroutine.status(co)):ToBe("dead") + end) + + ctx:Test("should not allow objects added during cleanup", function() + ctx:Expect(function() + trove:Add(function() + trove:Add(function() end) + end) + trove:Clean() + end):ToThrow() + end) + + ctx:Test("should not allow objects to be removed during cleanup", function() + ctx:Expect(function() + local f = function() end + trove:Add(f) + trove:Add(function() + trove:Remove(f) + end) + trove:Clean() + end):ToThrow() + end) + end) +end