From 816cd5cf27079a45afdccdbb690f81d55a879784 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 7 May 2021 14:07:37 -0700 Subject: [PATCH] add unit tests --- plugins/lua/blueprint.lua | 37 ++++--- test/plugins/blueprint.lua | 202 +++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+), 19 deletions(-) create mode 100644 test/plugins/blueprint.lua diff --git a/plugins/lua/blueprint.lua b/plugins/lua/blueprint.lua index 60319b54c..5dacf40bc 100644 --- a/plugins/lua/blueprint.lua +++ b/plugins/lua/blueprint.lua @@ -5,8 +5,7 @@ local utils = require('utils') -- the info here is very basic and minimal, so hopefully we won't need to change -- it when features are added and the full blueprint docs in Plugins.rst are -- updated. -local help_text = -[=[ +local help_text = [=[ blueprint ========= @@ -32,6 +31,8 @@ blueprint 30 40 bedrooms See the online DFHack documentation for more examples and details. ]=] +function print_help() print(help_text) end + valid_phases = utils.invert{ 'dig', 'build', @@ -39,10 +40,6 @@ valid_phases = utils.invert{ 'query', } -function print_help() - print(help_text) -end - local function parse_cursor(opts, arg) local _, _, x, y, z = arg:find('^(%d+),(%d+),(%d+)$') if not x then @@ -57,16 +54,8 @@ local function parse_cursor(opts, arg) opts.start.z = tonumber(z) end --- dimension must be a non-nil integer that is >= 1 (or at least non-zero if --- negative_ok is true) -local function is_bad_dim(dim, negative_ok) - return not dim - or (not negative_ok and dim < 1 or dim == 0) - or dim ~= math.floor(dim) -end - local function parse_positionals(opts, args, start_argidx) - local argidx = start_argidx + local argidx = start_argidx or 1 -- set defaults opts.name, opts.auto_phase = 'blueprint', true @@ -111,7 +100,15 @@ end function parse_gui_commandline(opts, args) local positionals = process_args(opts, args) if opts.help then return end - parse_positionals(opts, positionals, 1) + parse_positionals(opts, positionals) +end + +-- dimension must be a non-nil integer that is >= 1 (or at least non-zero if +-- negative_ok is true) +local function is_bad_dim(dim, negative_ok) + return not dim or + (not negative_ok and dim < 1 or dim == 0) or + dim ~= math.floor(dim) end function parse_commandline(opts, ...) @@ -120,7 +117,7 @@ function parse_commandline(opts, ...) local width, height = tonumber(positionals[1]), tonumber(positionals[2]) if is_bad_dim(width) or is_bad_dim(height) then - qerror(('invalid width and height: "%s" "%s"; width and height must' .. + qerror(('invalid width or height: "%s" "%s"; width and height must' .. ' be positive integers'):format(positionals[1], positionals[2])) end opts.width, opts.height, opts.depth = width, height, 1 @@ -160,8 +157,10 @@ local function do_blueprint(start_pos, end_pos, name, phase) local cursor = ('--cursor=%d,%d,%d'):format(x, y, z) - return dfhack.run_command{'blueprint', - width, height, depth, name, phase, cursor} + return dfhack.run_command('blueprint', + tostring(width), tostring(height), + tostring(depth), tostring(name), + phase, cursor) end for phase in pairs(valid_phases) do _ENV[phase] = function(s, e, n) do_blueprint(s, e, n, phase) end diff --git a/test/plugins/blueprint.lua b/test/plugins/blueprint.lua new file mode 100644 index 000000000..793fc0949 --- /dev/null +++ b/test/plugins/blueprint.lua @@ -0,0 +1,202 @@ +local b = require('plugins.blueprint') + +-- also covers code shared between parse_gui_commandline and parse_commandline +function test.parse_gui_commandline() + local opts = {} + b.parse_gui_commandline(opts, {}) + expect.table_eq({auto_phase=true, name='blueprint'}, opts) + + opts = {} + b.parse_gui_commandline(opts, {'help'}) + expect.table_eq({help=true}, opts) + + opts = {} + b.parse_gui_commandline(opts, {'--help'}) + expect.table_eq({help=true}, opts) + + opts = {} + b.parse_gui_commandline(opts, {'-h'}) + expect.table_eq({help=true}, opts) + + opts = {} + b.parse_gui_commandline(opts, {'--cursor=1,2,3'}) + expect.table_eq({auto_phase=true, name='blueprint', start={x=1,y=2,z=3}}, + opts) + + opts = {} + expect.error_match('invalid argument', + function() b.parse_gui_commandline( + opts, {'--cursor=-1,2,3'}) end, + 'negative coordinate') + + opts = {} + expect.error_match('invalid argument', + function() b.parse_gui_commandline( + opts, {'--cursor=1,b,3'}) end, + 'non-numeric coordinate') + + opts = {} + b.parse_gui_commandline(opts, {'imaname'}) + expect.table_eq({auto_phase=true, name='imaname'}, opts) + + opts = {} + expect.error_match('invalid basename', + function() b.parse_gui_commandline(opts, {''}) end) + + opts = {} + b.parse_gui_commandline(opts, {'imaname', 'dig', 'query'}) + expect.table_eq({auto_phase=false, name='imaname', dig=true, query=true}, + opts) + + opts = {} + b.parse_gui_commandline(opts, {'imaname', 'garbagephase'}) + expect.table_eq({auto_phase=true, name='imaname'}, opts) +end + +function test.parse_commandline() + local opts = {} + b.parse_commandline(opts, '1', '2') + expect.table_eq({auto_phase=true,name='blueprint',width=1,height=2,depth=1}, + opts) + + opts = {} + b.parse_commandline(opts, '1', '2', '3') + expect.table_eq({auto_phase=true,name='blueprint',width=1,height=2,depth=3}, + opts) + + opts = {} + b.parse_commandline(opts, '1', '2', '-3') + expect.table_eq({auto_phase=true,name='blueprint',width=1,height=2,depth=-3}, + opts) + + opts = {} + b.parse_commandline(opts, '1', '2', 'imaname') + expect.table_eq({auto_phase=true,name='imaname',width=1,height=2,depth=1}, + opts) + + opts = {} + b.parse_commandline(opts, '1', '2', '10imaname') + expect.table_eq({auto_phase=true,name='10imaname',width=1,height=2,depth=1}, + opts, 'invalid depth is considered a basename') + + opts = {} + b.parse_commandline(opts, '1', '2', '-10imaname') + expect.table_eq({auto_phase=true,name='-10imaname',width=1,height=2,depth=1}, + opts, 'invalid negative depth is considered a basename') + + opts = {} + b.parse_commandline(opts, '1', '2', '3', 'imaname') + expect.table_eq({auto_phase=true,name='imaname',width=1,height=2,depth=3}, + opts) + + opts = {} + expect.error_match('invalid width or height', + function() b.parse_commandline(opts) end, + 'missing width') + + opts = {} + expect.error_match('invalid width or height', + function() b.parse_commandline(opts, '10') end, + 'missing height') + + opts = {} + expect.error_match('invalid width or height', + function() b.parse_commandline(opts, '0') end, + 'zero height') + + opts = {} + expect.error_match('invalid width or height', + function() b.parse_commandline(opts, 'hi') end, + 'invalid width') + + opts = {} + expect.error_match('invalid width or height', + function() b.parse_commandline(opts, '10', 'hi') end, + 'invalid height') + + opts = {} + expect.error_match('invalid depth', + function() b.parse_commandline(opts, '1', '2', '0') end, + 'zero depth') +end + +function test.do_gui_no_arg() + local mock_print, mock_timeout, mock_run_script = + mock.func(), mock.func(), mock.func() + mock.patch( + { + {b, 'print', mock_print}, + {dfhack, 'timeout', mock_timeout}, + {dfhack, 'run_script', mock_run_script}, + }, + function() + b.do_gui('gui') + expect.eq(1, mock_print.call_count) + expect.eq(1, mock_timeout.call_count) + mock_timeout.call_args[1][3]() + expect.eq(1, mock_run_script.call_count) + expect.table_eq({'gui/blueprint'}, mock_run_script.call_args[1]) + end) +end + +function test.do_gui_with_args() + local mock_print, mock_timeout, mock_run_script = + mock.func(), mock.func(), mock.func() + mock.patch( + { + {b, 'print', mock_print}, + {dfhack, 'timeout', mock_timeout}, + {dfhack, 'run_script', mock_run_script}, + }, + function() + b.do_gui('gui', 'arg1', 'arg2', 'arg3') + expect.eq(1, mock_print.call_count) + expect.eq(1, mock_timeout.call_count) + mock_timeout.call_args[1][3]() + expect.eq(1, mock_run_script.call_count) + expect.table_eq({'gui/blueprint', 'arg1', 'arg2', 'arg3'}, + mock_run_script.call_args[1]) + end) +end + +function test.do_blueprint_positive_dims() + local mock_run_command = mock.func() + mock.patch(dfhack, 'run_command', mock_run_command, + function() + local spos = {x=10, y=20, z=30} + local epos = {x=11, y=21, z=31} + b.query(spos, epos, 'imaname') + expect.eq(1, mock_run_command.call_count) + expect.table_eq({'blueprint', '2', '2', '2', 'imaname', 'query', + '--cursor=10,20,30'}, + mock_run_command.call_args[1]) + end) +end + +function test.do_blueprint_negative_dims() + local mock_run_command = mock.func() + mock.patch(dfhack, 'run_command', mock_run_command, + function() + local spos = {x=11, y=21, z=31} + local epos = {x=10, y=20, z=30} + b.query(spos, epos, 'imaname') + expect.eq(1, mock_run_command.call_count) + expect.table_eq({'blueprint', '2', '2', '-2', 'imaname', 'query', + '--cursor=10,20,31'}, + mock_run_command.call_args[1]) + end) +end + +function test.do_blueprint_ensure_cursor_is_at_upper_left() + local mock_run_command = mock.func() + mock.patch(dfhack, 'run_command', mock_run_command, + function() + local spos = {x=11, y=20, z=30} + local epos = {x=10, y=21, z=31} + b.query(spos, epos, 'imaname') + expect.eq(1, mock_run_command.call_count) + expect.table_eq({'blueprint', '2', '2', '2', 'imaname', 'query', + '--cursor=10,20,30'}, + mock_run_command.call_args[1]) + end) +end