1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-23 01:47:38 +00:00

Add a function for type-checking arguments (#207)

- Define an expect(index, actual_value, types...) helper function which
   takes an argument index, value and list of permissable types and
   ensures the value is of one of those types.
   If not, it will produce an error message with the expected and actual
   type, as well as the argument number and (if available) the function
   name.
 - Expose expect in the global scope as _G["~expect"], hopefully making
   it clear it is internal.
 - Replace most manual type checks with this helper method.
 - Write tests to ensure this argument validation works as expected

Also fix a couple of bugs exposed by this refactor and the subsequent
tests:
 - Make rednet checks a little more strict - rednet.close(false) is no
   longer valid.
 - Error when attempting to redirect the terminal to itself 
   (term.redirect(term)).
This commit is contained in:
hydraz
2019-05-30 15:36:28 -03:00
committed by SquidDev
parent 5592ebae7d
commit 9048deeb95
34 changed files with 1070 additions and 428 deletions

View File

@@ -27,6 +27,43 @@ local function check(func, arg, ty, val)
end
end
local active_stubs = {}
--- Stub a global variable with a specific value
--
-- @tparam string var The variable to stub
-- @param value The value to stub it with
local function stub(var, value)
if not active_stubs[var] then
active_stubs[var] = { value = _G[var] }
end
_G[var] = value
end
--- Capture the current global state of the computer
local function push_state()
local stubs = active_stubs
active_stubs = {}
return {
term = term.current(),
input = io.input(),
output = io.output(),
stubs = stubs,
}
end
--- Restore the global state of the computer to a previous version
local function pop_state(state)
for k, v in pairs(active_stubs) do _G[k] = v.value end
active_stubs = state.stubs
term.redirect(state.term)
io.input(state.input)
io.output(state.output)
end
local error_mt = { __tostring = function(self) return self.message end }
--- Attempt to execute the provided function, gathering a stack trace when it
@@ -50,10 +87,6 @@ local function try(fn)
return { message = err, trace = debug.traceback(nil, 2) }
end)
-- Restore a whole bunch of state
io.input(io.stdin)
io.output(io.stdout)
-- If we succeeded, propagate it
if ok then return ok, err end
@@ -196,13 +229,32 @@ function expect_mt:matches(value)
return self
end
--- Construct a new expectation from the provided value
--
-- @param value The value to apply assertions to
-- @return The new expectation
local function expect(value)
return setmetatable({ value = value}, expect_mt)
end
local expect = setmetatable( {
--- Construct an expectation on the error message calling this function
-- produces
--
-- @tparam fun The function to call
-- @param ... The function arguments
-- @return The new expectation
error = function(fun, ...)
local ok, res = pcall(fun, ...) local _, line = pcall(error, "", 2)
if ok then fail("expected function to error") end
if res:sub(1, #line) == line then
res = res:sub(#line + 1)
elseif res:sub(1, 7) == "pcall: " then
res = res:sub(8)
end
return setmetatable({ value = res }, expect_mt)
end,
}, {
--- Construct a new expectation from the provided value
--
-- @param value The value to apply assertions to
-- @return The new expectation
__call = function(_, value)
return setmetatable({ value = value }, expect_mt)
end
})
--- The stack of "describe"s.
local test_stack = { n = 0 }
@@ -302,10 +354,15 @@ end
do
-- Load in the tests from all our files
local env = setmetatable({
expect = expect, fail = fail,
describe = describe, it = it, pending = pending
}, { __index = _ENV })
local env = setmetatable({}, { __index = _ENV })
local function set_env(tbl)
for k in pairs(env) do env[k] = nil end
for k, v in pairs(tbl) do env[k] = v end
end
-- When declaring tests, you shouldn't be able to use test methods
set_env { describe = describe, it = it, pending = pending }
local suffix = "_spec.lua"
local function run_in(sub_dir)
@@ -326,6 +383,9 @@ do
end
run_in(root_dir)
-- When running tests, you shouldn't be able to declare new ones.
set_env { expect = expect, fail = fail, stub = stub }
end
-- Error if we've found no tests
@@ -359,9 +419,13 @@ local function do_run(test)
err = test.error
status = "error"
elseif test.action then
local state = push_state()
local ok
ok, err = try(test.action)
status = ok and "pass" or (err.fail and "fail" or "error")
pop_state(state)
end
-- If we've a boolean status, then convert it into a string