1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2025-03-17 21:08:08 +02:00

module startup/teardown hooks

This commit is contained in:
James Gillham 2020-09-25 20:33:46 +01:00
parent a1908b93d5
commit df47b5aa02
6 changed files with 232 additions and 28 deletions

View File

@ -8,6 +8,20 @@ local config = global.config.landfill_remover
Declare.module(
'landfill remover',
function()
Declare.module_startup(
function()
game.print('landfill remover startup')
error('landfill remover startup')
end
)
Declare.module_teardown(
function()
game.print('landfill remover teardown')
error('landfill remover teardown')
end
)
Declare.test(
'can remove landfill',
function()

View File

@ -15,6 +15,7 @@ local function init_inner(module, depth)
count = count + 1
tests[#tests + 1] = {
name = name,
module = module,
func = func,
steps = nil,
current_step = nil,
@ -46,38 +47,100 @@ function Public.get_root_modules()
return ModuleStore.root_module
end
local function prepare_pre_module_hooks(module, runnables)
local startup_func = module.startup_func
if startup_func then
runnables[#runnables + 1] = {
is_hook = true,
name = 'startup',
module = module,
func = startup_func,
steps = Steps.new(),
current_step = 0,
error = nil
}
end
end
local function build_pre_module_hooks(module, runnables)
if module == nil then
return
end
build_pre_module_hooks(module.parent, runnables)
prepare_pre_module_hooks(module, runnables)
end
local function prepare_post_module_hooks(module, runnables)
local teardown_func = module.teardown_func
if teardown_func then
runnables[#runnables + 1] = {
is_hook = true,
name = 'teardown',
module = module,
func = teardown_func,
steps = Steps.new(),
current_step = 0,
error = nil
}
end
end
local function build_post_module_hooks(module, runnables)
if module == nil then
return
end
prepare_post_module_hooks(module, runnables)
build_post_module_hooks(module.parent, runnables)
end
local function prepare_test(test)
test.steps = Steps.new()
test.current_step = 0
test.passed = nil
test.error = nil
return test
end
local function prepare_module(module, tests)
local function prepare_module(module, runnables)
module.passed = nil
prepare_pre_module_hooks(module, runnables)
for _, test in pairs(module.tests) do
prepare_test(test)
tests[#tests + 1] = test
runnables[#runnables + 1] = test
end
for _, child in pairs(module.children) do
prepare_module(child, tests)
prepare_module(child, runnables)
end
prepare_post_module_hooks(module, runnables)
end
function Public.build_test_for_run(test)
Public.init()
prepare_test(test)
return test
local runnables = {}
build_pre_module_hooks(test.module, runnables)
runnables[#runnables + 1] = prepare_test(test)
build_post_module_hooks(test.module, runnables)
return runnables
end
function Public.build_module_for_run(module)
Public.init()
local tests = {}
prepare_module(module, tests)
return tests
local runnables = {}
build_pre_module_hooks(module.parent, runnables)
prepare_module(module, runnables)
build_post_module_hooks(module.parent, runnables)
return runnables
end
return Public

View File

@ -4,5 +4,7 @@ local Public = {}
Public.module = ModuleStore.module
Public.test = ModuleStore.test
Public.module_startup = ModuleStore.module_startup
Public.module_teardown = ModuleStore.module_teardown
return Public

View File

@ -3,7 +3,16 @@ local Public = {}
local function new_module(module_name)
return {
name = module_name,
parent = nil,
children = {},
startup_func = nil,
startup_steps = nil,
startup_current_step = nil,
startup_error = nil,
teardown_func = nil,
teardown_steps = nil,
teardown_current_step = nil,
teardown_error = nil,
test_funcs = {},
tests = nil,
is_open = true,
@ -25,6 +34,7 @@ local function add_module(module_name, module_func, parent)
if not module then
module = new_module(module_name)
parent_children[module_name] = module
module.parent = parent_module
end
parent_module = module
@ -75,4 +85,36 @@ function Public.test(test_name, test_func)
test_funcs[test_name] = test_func
end
function Public.module_startup(startup_func)
if type(startup_func) ~= 'function' then
error('startup_func must be of type function.')
end
if parent_module == nil then
error('root module can not have startup_func.')
end
if parent_module.startup_func ~= nil then
error('startup_func can not be declared twice for the same module.')
end
parent_module.startup_func = startup_func
end
function Public.module_teardown(teardown_func)
if type(teardown_func) ~= 'function' then
error('teardown_func must be of type function.')
end
if parent_module == nil then
error('root module can not have teardown_func.')
end
if parent_module.teardown_func ~= nil then
error('teardown_func can not be declared twice for the same module.')
end
parent_module.teardown_func = teardown_func
end
return Public

View File

@ -12,6 +12,8 @@ Public.events = {
tests_run_finished = Event.generate_event_name('test_run_finished')
}
local run_tests_token
local function print_summary(count, fail_count)
local pass_count = count - fail_count
game.print(table.concat {pass_count, ' of ', count, ' tests passed.'})
@ -61,6 +63,55 @@ local function print_success(test_name)
game.print(table.concat {"Passed - '", test_name, "'"}, {g = 1})
end
local function print_hook_error(hook)
game.print(table.concat {'Failed ', hook.name, " hook -':", tostring(hook.error)}, {r = 1})
end
local function record_hook_error_in_module(hook)
if hook.name == 'startup' then
hook.module.startup_error = hook.error
elseif hook.name == 'teardown' then
hook.module.teardown_error = hook.error
end
end
local function run_hook(hook)
local steps = hook.steps
local current_step = hook.current_step
local func
if current_step == 0 then
func = hook.func
else
func = steps[current_step].func
end
local success, return_value = pcall(func, steps)
if not success then
hook.error = return_value
print_hook_error(hook)
record_hook_error_in_module(hook)
return false
end
if current_step == #steps then
return true
end
return nil
end
local function do_hook(hook, data)
local hook_success = run_hook(hook)
if hook_success == nil then
hook.current_step = hook.current_step + 1
else
data.index = data.index + 1
end
Task.set_timeout_in_ticks(1, run_tests_token, data)
return
end
local function run_test(test)
local steps = test.steps
local current_step = test.current_step
@ -88,29 +139,20 @@ local function run_test(test)
return nil
end
local run_tests_token
local function run_tests(data)
local index = data.index
local test = data.tests[index]
if test == nil then
finish_test_run(data)
return
end
local function do_test(test, data)
local success = run_test(test)
if success == false then
data.count = data.count + 1
data.fail_count = data.fail_count + 1
data.index = index + 1
data.index = data.index + 1
Task.set_timeout_in_ticks(1, run_tests_token, data)
return
end
if success == true then
data.count = data.count + 1
data.index = index + 1
data.index = data.index + 1
Task.set_timeout_in_ticks(1, run_tests_token, data)
return
end
@ -121,20 +163,37 @@ local function run_tests(data)
Task.set_timeout_in_ticks(step.delay or 1, run_tests_token, data)
end
local function run_tests(data)
local index = data.index
local runnable = data.runnables[index]
if runnable == nil then
finish_test_run(data)
return
end
if runnable.is_hook then
do_hook(runnable, data)
return
end
do_test(runnable, data)
end
run_tests_token = Token.register(run_tests)
local function run(tests)
run_tests({tests = tests, index = 1, count = 0, fail_count = 0})
local function run(runnables)
run_tests({runnables = runnables, index = 1, count = 0, fail_count = 0})
end
function Public.run_module(module)
local tests = Builder.build_module_for_run(module or ModuleStore.root_module)
run(tests)
local runnables = Builder.build_module_for_run(module or ModuleStore.root_module)
run(runnables)
end
function Public.run_test(test)
Builder.build_test_for_run(test)
run({test})
local runnables = Builder.build_test_for_run(test)
run(runnables)
end
return Public

View File

@ -23,6 +23,15 @@ local error_test_box_name = Gui.uid_name()
local selected_modules = {}
local selected_tests = {}
local function get_module_state(module)
local passed = module.passed
if passed == false or module.startup_error or module.teardown_error then
return false
end
return passed
end
local function set_selected_style(style, selected)
if selected then
style.font_color = Color.orange
@ -77,7 +86,7 @@ local function draw_tests_module(container, module)
local is_selected = selected_modules[module]
set_selected_style(label_style, is_selected)
if not is_selected then
set_passed_style(label_style, module.passed)
set_passed_style(label_style, get_module_state(module))
end
Gui.set_data(label, {module = module, container = container})
@ -221,7 +230,22 @@ Gui.on_click(
end
local error_text_box = get_error_text_box(event.player)
error_text_box.text = ''
if is_selected then
local errors = {}
if module.startup_error then
errors[#errors + 1] = 'startup error: '
errors[#errors + 1] = module.startup_error
errors[#errors + 1] = '\n\n'
end
if module.teardown_error then
errors[#errors + 1] = 'teardown error: '
errors[#errors + 1] = module.teardown_error
end
error_text_box.text = table.concat(errors)
else
error_text_box.text = ''
end
redraw_tests(container)
end