From 40de9a8d2242c16fe5e2aad7c8304bd7e37f5e14 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Mon, 12 Jan 2026 23:15:31 -0800 Subject: [PATCH] Improving tests --- README.md | 8 +- docs/changes.md | 25 ++- init.lua | 22 +-- tests/runtests.lua | 340 ++++++++++++++++++++++-------------- tests/threadtests.lua | 2 +- tests/units/connections.lua | 71 ++++++++ 6 files changed, 308 insertions(+), 160 deletions(-) create mode 100644 tests/units/connections.lua diff --git a/README.md b/README.md index 050740d..2190b38 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -# Multi Version: 16.1.0 - The Flow State +# Multi Version: 16.3.0 - **Key Changes** -- Updated Processors to have more controlled over scheduled processes -- Forwarding connections -- New Timeout handling +- Fixed connection multiplying Refer to the [Change Log](https://github.com/rayaman/multi/blob/master/docs/changes.md) for more infromation @@ -12,7 +10,7 @@ My multitasking library for lua. It is a pure lua binding, with exceptions of th
-Progress is being made in [v16.2.0](https://github.com/rayaman/multi/tree/v16.2.0) +Progress is being made in [v16.3.0](https://github.com/rayaman/multi/tree/v16.3.0) ---
diff --git a/docs/changes.md b/docs/changes.md index 708311d..ac10f97 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -1,7 +1,8 @@ # Changelog Table of contents --- -[Update 16.2.0 - TimeStamps and UUIDs](#update-1620---timestamps-and-uuids-and-bugfixes)
+[Update 16.3.0 - Bug Fixes](#update-1620---bug-fixes)
+[Update 16.2.0 - TimeStamps and UUIDs](#update-1620---timestamps-and-uuids)
[Update 16.1.0 - The Flow State](#update-1610---the-flow-state)
[Update 16.0.1 - Bug fix](#update-1601---bug-fix)
[Update 16.0.0 - Connecting the dots](#update-1600---getting-the-priorities-straight)
@@ -61,6 +62,20 @@ Table of contents [Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)
[Version: EventManager 0.0.1 - In The Beginning things were very different](#version-eventmanager-001---in-the-beginning-things-were-very-different) +# Update 16.3.0 - Bug Fixes + +Added +--- + +Changed +--- +- `thread.kill(msg)` now accepts a message. If omitted will use default `thread killed!` + +Fixed +--- +- [Issue](https://github.com/rayaman/multi/issues/77) with concat of connections where conn .. func did not work properly +- [Issue](https://github.com/rayaman/multi/issues/67) with connection multiplying + # Update 16.2.0 - TimeStamps and UUIDs and bugfixes Added --- @@ -629,15 +644,13 @@ Added Output: ``` - I run before all and control if execution should continue! + I run before all and control if things go! Hi 3 Hi 1 - I run before all and control if execution should continue! - Hi 3 - Hi 2 Test 1 Test 2 Test 3 + I run after it all! ``` **Note:** Concat of connections does modify internal events on both connections depending on the direction func .. conn or conn .. func See implemention below: @@ -3672,4 +3685,4 @@ end event:Manager() --event:CManager() --event:UManager() -- One of the few things that lived in name and spirit the u just became lowercase haha -``` +``` \ No newline at end of file diff --git a/init.lua b/init.lua index cedf31a..f92111e 100644 --- a/init.lua +++ b/init.lua @@ -92,7 +92,7 @@ function multi.getTypes() return types end -multi.Version = "16.2.0" +multi.Version = "16.3.0" multi.Name = "root" multi.NIL = {Type="NIL"} local NIL = multi.NIL @@ -298,7 +298,7 @@ function multi:newConnection(protect,func,kill) local ref if type(obj1) == "function" and type(obj2) == "table" then cn(function(...) - if obj1(...) then + if obj1(...) == true then obj2:Fire(...) end end) @@ -320,7 +320,7 @@ function multi:newConnection(protect,func,kill) end end) end - return obj1 + return cn elseif type(obj1) == "table" and type(obj2) == "table" then -- else @@ -367,8 +367,6 @@ function multi:newConnection(protect,func,kill) if cn.__count[1] == cn.__hasInstances[1] then cn:Fire(...) cn.__count[1] = 0 - c1:Unlock(ref1) - c2:Unlock(ref2) end end) return cn @@ -698,15 +696,6 @@ function multi:isDone() return self.Active~=true end -local time = os.time -local ok, chronos = pcall(require, "chronos") -- hpc - -if ok then - math.randomseed(chronos.nanotime()*100000000) -else - math.randomseed(time()) -end - function multi:create(ref) ref.UID = multi.generate_uuid7() self.OnObjectCreated:Fire(ref, self) @@ -1517,8 +1506,9 @@ function thread.skip(n) return yield(CMD, t_skip, n or 1) end -function thread.kill() - multi.error("thread killed!") +function thread.kill(msg) + msg = msg or "thread killed!" + multi.error(msg) end function thread.yield() diff --git a/tests/runtests.lua b/tests/runtests.lua index 2e5726e..f7b5109 100644 --- a/tests/runtests.lua +++ b/tests/runtests.lua @@ -1,9 +1,74 @@ -package.path = "../?/init.lua;../?.lua;./init.lua;./?.lua;"..package.path +package.path = "../?/init.lua;../?.lua;./init.lua;./?.lua;" .. package.path local multi, thread = require("multi"):init{print=true,warn=true,error=true}--{priority=true} local good = false local proc = multi:newProcessor("Test") +local testing = {} + +function testing.False(value) + if value ~= false then + thread.kill("Expected value to be false") + end +end + +function testing.True(value) + if value ~= true then + thread.kill("Expected value to be true") + end +end + +function testing.IsNil(value) + if value ~= nil then + thread.kill("Expected nil, but got: " .. tostring(value) .."") + end +end + +function testing.NotNil(value) + if value == nil then + thread.kill("Expected value not to be nil.") + end +end + +function testing.Equal(expected, actual) + if expected ~= actual then + thread.kill("Not equal: \n" .. + "expected: " .. tostring(expected) .. "\n" .. + "actual : " .. tostring(actual) .. "\n") + end +end + +function testing.NotEqual(expected, actual) + if expected == actual then + thread.kill("Should not be: values are equal") + end +end + +function getOS() + -- ask LuaJIT first + if jit then + return jit.os + end + + -- Unix, Linux variants + local fh,err = assert(io.popen("uname -o 2>/dev/null","r")) + if fh then + osname = fh:read() + end + + return osname or "Windows" +end + +function ListFiles(path) + files = {} + if getOS() == "Windows" then + for dir in io.popen([[dir /A-d "]] .. path .. [[" /b]]):lines() do table.insert(files,dir) end + else + for dir in io.popen([[ls -pa "]] .. path .. [[" | grep -v /]]):lines() do table.insert(files,dir) end + end + return files +end + print("Version: "..multi.Version) proc.Start() @@ -13,147 +78,158 @@ proc:newAlarm(3):OnRing(function() end) runTest = thread:newFunction(function() - local alarms,tsteps,steps,loops,tloops,updaters,events=false,0,0,0,0,0,false - multi.print("Testing Basic Features. If this fails most other features will probably not work!") - proc:newAlarm(2):OnRing(function(a) - alarms = true - a:Destroy() - end) - proc:newTStep(1,10,1,.1):OnStep(function(t) - tsteps = tsteps + 1 - end):OnEnd(function(step) - step:Destroy() - end) - proc:newStep(1,10):OnStep(function(s) - steps = steps + 1 - end):OnEnd(function(step) - step:Destroy() - end) - local loop = proc:newLoop(function(l) - loops = loops + 1 - end) - proc:newTLoop(function(t) - tloops = tloops + 1 - end,.1) - local updater = proc:newUpdater(1):OnUpdate(function() - updaters = updaters + 1 - end) - local event = proc:newEvent(function() - return alarms - end) - event.OnEvent(function(evnt) - evnt:Destroy() - events = true - multi.success("Alarms: Ok") - multi.success("Events: Ok") - if tsteps == 10 then multi.success("TSteps: Ok") else multi.error("TSteps: Bad!") end - if steps == 10 then multi.success("Steps: Ok") else multi.error("Steps: Bad!") end - if loops > 100 then multi.success("Loops: Ok") else multi.error("Loops: Bad!") end - if tloops > 10 then multi.success("TLoops: Ok") else multi.error("TLoops: Bad!") end - if updaters > 100 then multi.success("Updaters: Ok") else multi.error("Updaters: Bad!") end - end) - thread.hold(event.OnEvent) - multi.print("Starting Connection and Thread tests!") - func = thread:newFunction(function(count) - multi.print("Starting Status test: ",count) - local a = 0 - while true do - a = a + 1 - thread.sleep(.1) - thread.pushStatus(a,count) - if a == count then break end - end - return "Done", true, math.random(1,10000) - end) - local ret = func(10) - local ret2 = func(15) - local ret3 = func(20) - local s1,s2,s3 = 0,0,0 - ret.OnError(function(...) - multi.error("Func 1:",...) - end) - ret2.OnError(function(...) - multi.error("Func 2:",...) - end) - ret3.OnError(function(...) - multi.error("Func 3:",...) - end) - ret.OnStatus(function(part,whole) - s1 = math.ceil((part/whole)*1000)/10 - end) - ret2.OnStatus(function(part,whole) - s2 = math.ceil((part/whole)*1000)/10 - end) - ret3.OnStatus(function(part,whole) - s3 = math.ceil((part/whole)*1000)/10 - end) + -- local alarms,tsteps,steps,loops,tloops,updaters,events=false,0,0,0,0,0,false + -- multi.print("Testing Basic Features. If this fails most other features will probably not work!") + -- proc:newAlarm(2):OnRing(function(a) + -- alarms = true + -- a:Destroy() + -- end) + -- proc:newTStep(1,10,1,.1):OnStep(function(t) + -- tsteps = tsteps + 1 + -- end):OnEnd(function(step) + -- step:Destroy() + -- end) + -- proc:newStep(1,10):OnStep(function(s) + -- steps = steps + 1 + -- end):OnEnd(function(step) + -- step:Destroy() + -- end) + -- local loop = proc:newLoop(function(l) + -- loops = loops + 1 + -- end) + -- proc:newTLoop(function(t) + -- tloops = tloops + 1 + -- end,.1) + -- local updater = proc:newUpdater(1):OnUpdate(function() + -- updaters = updaters + 1 + -- end) + -- local event = proc:newEvent(function() + -- return alarms + -- end) + -- event.OnEvent(function(evnt) + -- evnt:Destroy() + -- events = true + -- multi.success("Alarms: Ok") + -- multi.success("Events: Ok") + -- if tsteps == 10 then multi.success("TSteps: Ok") else multi.error("TSteps: Bad!") end + -- if steps == 10 then multi.success("Steps: Ok") else multi.error("Steps: Bad!") end + -- if loops > 100 then multi.success("Loops: Ok") else multi.error("Loops: Bad!") end + -- if tloops > 10 then multi.success("TLoops: Ok") else multi.error("TLoops: Bad!") end + -- if updaters > 100 then multi.success("Updaters: Ok") else multi.error("Updaters: Bad!") end + -- end) + -- thread.hold(event.OnEvent) + -- multi.print("Starting Connection and Thread tests!") + -- func = thread:newFunction(function(count) + -- multi.print("Starting Status test: ",count) + -- local a = 0 + -- while true do + -- a = a + 1 + -- thread.sleep(.1) + -- thread.pushStatus(a,count) + -- if a == count then break end + -- end + -- return "Done", true, math.random(1,10000) + -- end) + -- local ret = func(10) + -- local ret2 = func(15) + -- local ret3 = func(20) + -- local s1,s2,s3 = 0,0,0 + -- ret.OnError(function(...) + -- multi.error("Func 1:",...) + -- end) + -- ret2.OnError(function(...) + -- multi.error("Func 2:",...) + -- end) + -- ret3.OnError(function(...) + -- multi.error("Func 3:",...) + -- end) + -- ret.OnStatus(function(part,whole) + -- s1 = math.ceil((part/whole)*1000)/10 + -- end) + -- ret2.OnStatus(function(part,whole) + -- s2 = math.ceil((part/whole)*1000)/10 + -- end) + -- ret3.OnStatus(function(part,whole) + -- s3 = math.ceil((part/whole)*1000)/10 + -- end) - ret.OnReturn(function(...) - multi.success("Done 1",...) - end) - ret2.OnReturn(function(...) - multi.success("Done 2",...) - end) - ret3.OnReturn(function(...) - multi.success("Done 3",...) - end) + -- ret.OnReturn(function(...) + -- multi.success("Done 1",...) + -- end) + -- ret2.OnReturn(function(...) + -- multi.success("Done 2",...) + -- end) + -- ret3.OnReturn(function(...) + -- multi.success("Done 3",...) + -- end) - local err, timeout = thread.hold(ret.OnReturn * ret2.OnReturn * ret3.OnReturn) + -- local err, timeout = thread.hold(ret.OnReturn * ret2.OnReturn * ret3.OnReturn) - if s1 == 100 and s2 == 100 and s3 == 100 then - multi.success("Threads: All tests Ok") - else - if s1>0 and s2>0 and s3 > 0 then - multi.success("Thread OnStatus: Ok") - else - multi.error("Threads OnStatus or thread.hold(conn) Error!") - end - if timeout then - multi.error("Connection Error!") - else - multi.success("Connection Test 1: Ok") - end - multi.error("Connection holding Error!") - end + -- if s1 == 100 and s2 == 100 and s3 == 100 then + -- multi.success("Threads: All tests Ok") + -- else + -- if s1>0 and s2>0 and s3 > 0 then + -- multi.success("Thread OnStatus: Ok") + -- else + -- multi.error("Threads OnStatus or thread.hold(conn) Error!") + -- end + -- if timeout then + -- multi.error("Connection Error!") + -- else + -- multi.success("Connection Test 1: Ok") + -- end + -- multi.error("Connection holding Error!") + -- end - conn1 = proc:newConnection() - conn2 = proc:newConnection() - conn3 = proc:newConnection() - local c1,c2,c3,c4 = false,false,false,false + -- conn1 = proc:newConnection() + -- conn2 = proc:newConnection() + -- conn3 = proc:newConnection() + -- local c1,c2,c3,c4 = false,false,false,false - local a = conn1(function() - c1 = true - end) + -- local a = conn1(function() + -- c1 = true + -- end) - local b = conn2(function() - c2 = true - end) + -- local b = conn2(function() + -- c2 = true + -- end) - local c = conn3(function() - c3 = true - end) + -- local c = conn3(function() + -- c3 = true + -- end) - local d = conn3(function() - c4 = true - end) + -- local d = conn3(function() + -- c4 = true + -- end) - conn1:Fire() - conn2:Fire() - conn3:Fire() + -- conn1:Fire() + -- conn2:Fire() + -- conn3:Fire() - if c1 and c2 and c3 and c4 then - multi.success("Connection Test 2: Ok") - else - multi.error("Connection Test 2: Error") - end - c3 = false - c4 = false - conn3:Unconnect(d) - conn3:Fire() - if c3 and not(c4) then - multi.success("Connection Test 3: Ok") - else - multi.error("Connection Test 3: Error removing connection") + -- if c1 and c2 and c3 and c4 then + -- multi.success("Connection Test 2: Ok") + -- else + -- multi.error("Connection Test 2: Error") + -- end + -- c3 = false + -- c4 = false + -- conn3:Unconnect(d) + -- conn3:Fire() + -- if c3 and not(c4) then + -- multi.success("Connection Test 3: Ok") + -- else + -- multi.error("Connection Test 3: Error removing connection") + -- end + units = ListFiles("tests/units") + for _, file in pairs(units) do + multi.print("Running tests in: " .. file) + unit = loadfile("tests/units/" .. file)() + function Test(...) + unit.Test(...) + multi.success(file) + end + func = thread:newFunction(Test, true) + func(multi:newProcessor(file), thread, testing) end if not love then local ec = 0 diff --git a/tests/threadtests.lua b/tests/threadtests.lua index 9570a39..c124eb4 100644 --- a/tests/threadtests.lua +++ b/tests/threadtests.lua @@ -1,4 +1,4 @@ -package.path = "D:/VSCWorkspace/?/init.lua;D:/VSCWorkspace/?.lua;"..package.path +package.path = "../?/init.lua;../?.lua;./init.lua;./?.lua;" .. package.path package.cpath = "C:/luaInstalls/lua5.4/lib/lua/5.4/?/core.dll;" .. package.cpath multi, thread = require("multi"):init{error=true,warning=true,print=true, priority=true} proc = multi:newProcessor("Thread Test",true) diff --git a/tests/units/connections.lua b/tests/units/connections.lua new file mode 100644 index 0000000..70780c7 --- /dev/null +++ b/tests/units/connections.lua @@ -0,0 +1,71 @@ +function Test(multi, thread, t) + multi.print("Testing Connection operators") + + do + multi.print("Testing: conn1 + conn2") + local conn1 = multi:newConnection() + local conn2 = multi:newConnection() + local conn3 = conn1 + conn2 + local count = 0 + + conn3(function() + count = count + 1 + end) + + conn1:Fire() + t.Equal(1, count) + conn2:Fire() + t.Equal(2, count) + end + + do + multi.print("Testing: conn1 * conn2") + local conn1 = multi:newConnection() + local conn2 = multi:newConnection() + local conn3 = conn1 * conn2 + local count = 0 + + conn3(function() + count = count + 1 + end) + + conn1:Fire() + t.Equal(0, count) + conn2:Fire() + t.Equal(1, count) + end + + do + multi.print("Testing: conn .. function") + local called = false + local conn1 = multi:newConnection() + local conn2 = conn1 .. function() called = true end + + conn1(function() + t.False(called) + end) + + conn2:Fire() + t.True(called) + end + + do + multi.print("function .. conn") + local status = false + local conn1 = multi:newConnection() + local conn2 = function(test) return test end .. conn1 + + conn1(function() + status = true + end) + + conn2:Fire(false) + t.False(status) + conn2:Fire(true) + t.True(status) + end +end + +return { + Test = Test +} \ No newline at end of file