Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Template for new versions:
- `buildingplan`: fix "Unlink all" only unlinking some mechanisms (or crashing) when freeing mechanisms from the building
- `tiletypes`: remove plants (including trees) rooted on a tile that is painted into something that cannot host them, so cleared ground no longer regrows floating trees
- `timestream`: deal properly with units who have breathing difficulties
- `timestream`: pass the validated numeric value to the fps setter so ``set fps`` with unusual numeric strings (e.g. ``60.9``) can't be silently misapplied
- `stocks`: overlay now resets scroll position when collapsing categories so the item list is no longer left blank and unscrollable
- Fixed persistent site data API (``dfhack.persistent.saveSiteData``/``getSiteData``) failing on newly reclaimed fortresses until the first save

Expand Down
2 changes: 1 addition & 1 deletion plugins/lua/timestream.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ local function do_set(setting_name, arg)
if setting_name ~= 'fps' or not numarg then
qerror('must specify setting and value')
end
timestream_setFps(arg)
timestream_setFps(numarg)
print(('set %s to %s'):format(setting_name, timestream_getFps()))
end

Expand Down
37 changes: 37 additions & 0 deletions test/plugins/autonestbox.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
config.mode = 'fortress'
config.target = 'autonestbox'

local autonestbox = require('plugins.autonestbox')

local function parse(args)
local opts = {}
autonestbox.parse_commandline(opts, args)
return opts
end

function test.now_flag()
expect.true_(parse({'now'}).now)
end

function test.help_flag()
expect.true_(parse({'help'}).help)
expect.true_(parse({'--help'}).help)
expect.true_(parse({'-h'}).help)
end

function test.help_does_not_set_now()
local opts = parse({'help', 'now'})
expect.true_(opts.help)
expect.nil_(opts.now)
end

function test.unknown_positionals_ignored()
local opts = parse({'bogus'})
expect.nil_(opts.now)
expect.nil_(opts.help)
end

function test.mixed_positionals()
local opts = parse({'bogus', 'now', 'other'})
expect.true_(opts.now)
end
118 changes: 118 additions & 0 deletions test/plugins/plant.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
config.mode = 'fortress'
config.target = 'plant'

local plant = require('plugins.plant')

-- parse_commandline takes C++-allocated outputs: an options table, two
-- coords, and a vector<int32_t> filter. stl vectors can't be created from
-- Lua, so borrow an int32 vector field from a temporary burrow.
local function parse(args)
local opts = {}
local pos1, pos2 = xyz2pos(0, 0, -30000), xyz2pos(0, 0, -30000)
local b = df.burrow:new()
local ok, err = pcall(plant.parse_commandline, opts, pos1, pos2, b.block_x, args)
-- copy out before deleting the burrow; the vector dangles with it
local filter = {}
for i = 0, #b.block_x - 1 do filter[i + 1] = b.block_x[i] end
local result = {opts=opts, pos1=pos1, pos2=pos2, filter=filter}
b:delete()
if not ok then error(err, 0) end
return result
end

local function find_valid_plant()
for _,p in ipairs(df.global.world.raws.plants.bushes) do
if p.id and #p.id > 0 then return p end
end
end

function test.modes_set_flags()
expect.eq(-2, parse({'list'}).opts.plant_idx)
expect.true_(parse({'grow'}).opts.grow)
expect.true_(parse({'remove'}).opts.del)
end

function test.create_requires_plant_id()
expect.error_match('Must specify plant_id',
function() parse({'create'}) end)
end

function test.create_resolves_plant_by_name_and_index()
local p = find_valid_plant()
if not p then return end
local by_name = parse({'create', p.id})
expect.true_(by_name.opts.create)
expect.eq(p.index, by_name.opts.plant_idx)
expect.eq(p.index, parse({'create', tostring(p.index)}).opts.plant_idx)
end

function test.create_unknown_plant_errors()
expect.error_match('Plant raw not found',
function() parse({'create', 'DFHACK_NO_SUCH_PLANT'}) end)
end

function test.invalid_and_missing_modes_error()
expect.error_match('Specify mode', function() parse({}) end)
expect.error_match('Invalid mode', function() parse({'bogus'}) end)
end

function test.too_many_positionals_error()
expect.error_match('Too many positionals',
function() parse({'grow', '1,1,100', '2,2,100', 'extra'}) end)
end

function test.age_option_parsing()
expect.eq(40320*3-1, parse({'grow', '--age', 'tree'}).opts.age)
expect.eq(40320*5-1, parse({'grow', '--age', '5'}).opts.age)
expect.eq(40320*1250-1, parse({'grow', '--age', '9999'}).opts.age)
expect.error_match('Invalid age',
function() parse({'grow', '--age', 'bogus'}) end)
end

function test.filter_and_exclude_populate_vector()
local p = find_valid_plant()
if not p then return end
local res = parse({'remove', '--filter', p.id})
expect.eq(1, #res.filter)
expect.eq(p.index, res.filter[1])
res = parse({'remove', '--exclude', p.id})
expect.true_(res.opts.filter_ex)
end

function test.double_filter_errors()
expect.error_match('Filter already defined',
function()
local p = find_valid_plant()
if not p then error('skip') end
parse({'remove', '--filter', p.id, '--exclude', p.id})
end)
end

function test.option_flags()
local opts = parse({'remove', '-s', '-p', '-t', '-d', '-z', '-n', '-c'}).opts
expect.true_(opts.shrubs)
expect.true_(opts.saplings)
expect.true_(opts.trees)
expect.true_(opts.dead)
expect.true_(opts.zlevel)
expect.true_(opts.dry_run)
expect.true_(opts.force)
end

function test.coords_assigned_to_positions()
local res = parse({'grow', '3,4,100', '7,8,101'})
expect.eq(3, res.pos1.x)
expect.eq(4, res.pos1.y)
expect.eq(100, res.pos1.z)
expect.eq(7, res.pos2.x)
expect.eq(8, res.pos2.y)
expect.eq(101, res.pos2.z)
end

function test.create_uses_third_positional_as_pos1()
local p = find_valid_plant()
if not p then return end
local res = parse({'create', p.id, '9,10,102'})
expect.eq(9, res.pos1.x)
expect.eq(102, res.pos1.z)
end
55 changes: 55 additions & 0 deletions test/plugins/prospector.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
config.mode = 'fortress'
config.target = 'prospector'

local prospector = require('plugins.prospector')

local function parse(args)
local opts = {}
prospector.parse_commandline(opts, args)
return opts
end

function test.help_flag()
expect.true_(parse({'--help'}).help)
expect.true_(parse({'-h'}).help)
end

function test.all_and_hell_set_hidden()
expect.true_(parse({'all'}).hidden)
expect.nil_(parse({'all'}).tube)
local opts = parse({'hell'})
expect.true_(opts.hidden)
expect.true_(opts.tube)
end

function test.unknown_keyword_errors()
expect.error_match('unknown keyword',
function() prospector.parse_commandline({}, {'bogus'}) end)
end

function test.show_sections_set_flags()
local opts = parse({'-s', 'gems,ores'})
expect.true_(opts.gems)
expect.true_(opts.ores)
-- selecting any section disables the rest
expect.false_(opts.summary)
expect.false_(opts.layers)
end

function test.show_all_sections()
local opts = parse({'-s', 'summary,liquids,layers,features,ores,gems,veins,shrubs,trees'})
for _,s in ipairs{'summary', 'liquids', 'layers', 'features', 'ores',
'gems', 'veins', 'shrubs', 'trees'} do
expect.true_(opts[s], 'expected section flag set: '..s)
end
end

function test.unknown_show_section_errors()
expect.error_match('unknown report section',
function() prospector.parse_commandline({}, {'-s', 'bogus'}) end)
end

function test.values_flag()
expect.true_(parse({'-v'}).value)
expect.true_(parse({'--values'}).value)
end
47 changes: 47 additions & 0 deletions test/plugins/tiletypes.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
config.mode = 'fortress'
config.target = 'tiletypes'

local tiletypes = require('plugins.tiletypes')

local function set_cursor(pos)
df.global.cursor.x = pos.x
df.global.cursor.y = pos.y
Expand Down Expand Up @@ -116,3 +118,48 @@ function test.here_point_bad_option_is_failure()
local _, status = dfhack.run_command_silent('tiletypes-here-point --bogus')
expect.eq(CR_FAILURE, status)
end

local function parse(args)
local opts = {cursor = xyz2pos(0, 0, 0)}
tiletypes.parse_commandline(opts, args)
return opts
end

function test.help_flag()
expect.true_(parse({'--help'}).help)
expect.true_(parse({'-h'}).help)
expect.true_(parse({'help'}).help)
expect.true_(parse({'?'}).help)
end

function test.quiet_flag()
expect.true_(parse({'-q'}).quiet)
expect.true_(parse({'--quiet'}).quiet)
end

function test.cursor_option()
local opts = parse({'-c', '5,7,99'})
expect.eq(5, opts.cursor.x)
expect.eq(7, opts.cursor.y)
expect.eq(99, opts.cursor.z)
end

function test.cursor_option_long_form()
local opts = parse({'--cursor', '1,2,3'})
expect.eq(1, opts.cursor.x)
expect.eq(2, opts.cursor.y)
expect.eq(3, opts.cursor.z)
end

function test.bad_cursor_errors()
expect.error(function()
tiletypes.parse_commandline({cursor = xyz2pos(0, 0, 0)},
{'-c', 'not-a-coord'})
end)
end

function test.positionals_do_not_set_flags()
local opts = parse({'paint', 'stone', 'microcline'})
expect.nil_(opts.help)
expect.nil_(opts.quiet)
end
47 changes: 47 additions & 0 deletions test/plugins/timestream.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
config.mode = 'fortress'
config.target = 'timestream'

local timestream = require('plugins.timestream')

local saved_fps = timestream.timestream_getFps()

config.wrapper = function(test_fn)
return dfhack.with_finalize(function()
timestream.timestream_setFps(saved_fps)
end, test_fn)
end

function test.status_and_unknown_command()
expect.true_(timestream.parse_commandline({}))
expect.true_(timestream.parse_commandline({'status'}))
expect.false_(timestream.parse_commandline({'help'}))
expect.false_(timestream.parse_commandline({'bogus'}))
end

function test.set_fps_roundtrip()
expect.true_(timestream.parse_commandline({'set', 'fps', '60'}))
expect.eq(60, timestream.timestream_getFps())
end

function test.set_fps_clamps_to_minimum()
-- the C++ layer clamps the target to at least 10 fps
expect.true_(timestream.parse_commandline({'set', 'fps', '5'}))
expect.eq(10, timestream.timestream_getFps())
end

function test.set_fps_rejects_bad_args()
expect.error_match('must specify setting and value',
function() timestream.parse_commandline({'set'}) end)
expect.error_match('must specify setting and value',
function() timestream.parse_commandline({'set', 'fps'}) end)
expect.error_match('must specify setting and value',
function() timestream.parse_commandline({'set', 'bogus', '60'}) end)
expect.error_match('must specify setting and value',
function() timestream.parse_commandline({'set', 'fps', 'fast'}) end)
end

function test.reset_restores_default()
timestream.timestream_setFps(43)
expect.true_(timestream.parse_commandline({'reset'}))
expect.ne(43, timestream.timestream_getFps())
end
Loading