-
Notifications
You must be signed in to change notification settings - Fork 510
fix three plugin crash/UB bugs; test coverage for 14 plugins #5939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0a192a7
1db1882
1ae50a0
d4e1c76
079d8b8
cb0b86b
3c580f0
05b8137
43a414a
9787a8b
cc0ef10
f8351ea
d310a26
ef1c305
1f64b0d
85327b7
6d4be6f
4dfcb69
275d09c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| config.mode = 'fortress' | ||
| config.target = '3dveins' | ||
|
|
||
| function test.rewrites_veins() | ||
| local output, status = dfhack.run_command_silent('3dveins') | ||
| expect.eq(CR_OK, status) | ||
| expect.str_find('Writing tiles', output) | ||
| end | ||
|
|
||
| function test.verbose_option() | ||
| local _, status = dfhack.run_command_silent('3dveins', 'verbose') | ||
| expect.eq(CR_OK, status) | ||
| end | ||
|
|
||
| function test.bad_option_is_wrong_usage() | ||
| local _, status = dfhack.run_command_silent('3dveins', 'bogus') | ||
| expect.eq(CR_WRONG_USAGE, status) | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| config.mode = 'fortress' | ||
| config.target = 'autodump' | ||
|
|
||
| local dwarfmode = require('gui.dwarfmode') | ||
|
|
||
| local function find_floor_pos() | ||
| for _, block in ipairs(df.global.world.map.map_blocks) do | ||
| for x = 0, 15 do | ||
| for y = 0, 15 do | ||
| local tt = block.tiletype[x][y] | ||
| local des = block.designation[x][y] | ||
| local occ = block.occupancy[x][y] | ||
| if df.tiletype.attrs[tt].shape == df.tiletype_shape.FLOOR | ||
| and not des.hidden and occ.building == 0 then | ||
| return block.map_pos.x + x, block.map_pos.y + y, | ||
| block.map_pos.z | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| local function set_cursor(x, y, z) | ||
| dwarfmode.setCursorPos(xyz2pos(x, y, z)) | ||
| end | ||
|
|
||
| local function clear_cursor() | ||
| dwarfmode.clearCursorPos() | ||
| end | ||
|
|
||
| local function find_dumpable_item() | ||
| for _, item in ipairs(df.global.world.items.other.IN_PLAY) do | ||
| if not item.flags.dump and not item.flags.construction | ||
| and not item.flags.in_building and not item.flags.artifact | ||
| and not item.flags.in_job and not item.flags.forbid | ||
| and not item.flags.owned and not item.flags.in_inventory then | ||
| return item | ||
| end | ||
| end | ||
| end | ||
|
|
||
| local function block_has_item(block, id) | ||
| for _, bid in ipairs(block.items) do | ||
| if bid == id then return true end | ||
| end | ||
| return false | ||
| end | ||
|
|
||
| local function items_sorted(block) | ||
| local prev = -1 | ||
| for _, id in ipairs(block.items) do | ||
| if id <= prev then return false end | ||
| prev = id | ||
| end | ||
| return true | ||
| end | ||
|
|
||
| function test.dump_moves_item_to_cursor() | ||
| local x, y, z = find_floor_pos() | ||
| local item = find_dumpable_item() | ||
| expect.ne(nil, x, 'test needs a revealed floor tile') | ||
| expect.ne(nil, item, 'test needs a dumpable item') | ||
| local ix, iy, iz = dfhack.items.getPosition(item) | ||
| expect.ne(nil, ix, 'test item needs a real position') | ||
| local old_block = dfhack.maps.getTileBlock(ix, iy, iz) | ||
| local new_block = dfhack.maps.getTileBlock(x, y, z) | ||
|
|
||
| return dfhack.with_finalize(function() | ||
| item.flags.dump = false | ||
| clear_cursor() | ||
| end, function() | ||
| set_cursor(x, y, z) | ||
| item.flags.dump = true | ||
|
|
||
| local _, status = dfhack.run_command_silent('autodump') | ||
| expect.eq(CR_OK, status) | ||
| -- item was teleported to the cursor and marked as dumped | ||
| expect.eq(x, item.pos.x) | ||
| expect.eq(y, item.pos.y) | ||
| expect.eq(z, item.pos.z) | ||
| expect.false_(item.flags.dump) | ||
| expect.true_(item.flags.forbid) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TO BE CLEAR, I am NOT insisting that you exercise all or any of these possibliities. I will NOT hold up a merge over this. any test suite is better than none at all, and I am grateful to have these tests. this all looks good. another place to test is the it would be best to check that the item ids are edit: verifying removal of an item from a map_block is covered below. edit: it would be good to verify that the map_block.items list remains sorted. to test this properly, you probably need to find a map_block with a lot of items in it, i.e. edit: there is a edit: |
||
| expect.true_(item.flags.on_ground) | ||
| expect.false_(item.flags.in_inventory) | ||
| expect.false_(item.flags.in_building) | ||
|
|
||
| -- the destination tile's occupancy and the map_block item | ||
| -- vectors must reflect the move | ||
| local _, occ = dfhack.maps.getTileFlags(x, y, z) | ||
| expect.true_(occ.item) | ||
| expect.true_(block_has_item(new_block, item.id), | ||
| 'item id should be in the new map_block.items') | ||
| if old_block ~= new_block then | ||
| expect.false_(block_has_item(old_block, item.id), | ||
| 'item id should be removed from the old map_block.items') | ||
| end | ||
| expect.true_(items_sorted(new_block), | ||
| 'map_block.items should remain sorted') | ||
| end) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TO BE CLEAR, I am NOT insisting that you exercise all or any of these possibliities. I will NOT hold up a merge over this. any test suite is better than none at all, and I am grateful to have these tests. this test suite doesn't have anything that verifies the item is removed from the old location. fully exercising that will be rather hard, as there are the item was on the floor.
the item was in a building.
the item was in a container.
the item was in a unit's inventory.
there are also rare cases: the item is/was a projectile, the item is uncategorized, possibly others I can't think of. I think these don't need to be tested, as I am not convinced that this is all really messy and may not need to be verified for autodump, because autodump uses library calls that handle the messiness. OTOH I don't think we have anything else that verifies those calls. all of these tests will not be necessary for the destroy-item case, because that relies on the game doing the work. this got big. please do not feel any obligation here. |
||
| end | ||
|
|
||
| function test.conflicting_filters_is_wrong_usage() | ||
| local output, status = dfhack.run_command_silent('autodump', 'visible', 'hidden') | ||
| expect.eq(CR_WRONG_USAGE, status) | ||
| expect.str_find("both hidden and visible", output) | ||
| end | ||
|
|
||
| function test.bad_option_is_wrong_usage() | ||
| local _, status = dfhack.run_command_silent('autodump', 'bogus') | ||
| expect.eq(CR_WRONG_USAGE, status) | ||
| end | ||
|
|
||
| function test.destroy_and_undestroy() | ||
| local item = find_dumpable_item() | ||
| expect.ne(nil, item, 'test needs a dumpable item') | ||
|
|
||
| local was_paused = dfhack.world.ReadPauseState() | ||
| return dfhack.with_finalize(function() | ||
| dfhack.world.SetPauseState(was_paused) | ||
| dfhack.run_command_silent('autodump', 'undestroy') | ||
| item.flags.dump = false | ||
| item.flags.garbage_collect = false | ||
| item.flags.forbid = false | ||
| item.flags.hidden = false | ||
| end, function() | ||
| item.flags.dump = true | ||
| -- undestroy only restores marks made in the same frame, so the | ||
| -- game must stay paused between destroy and undestroy | ||
| dfhack.world.SetPauseState(true) | ||
|
|
||
| local _, status = dfhack.run_command_silent('autodump', 'destroy') | ||
| expect.eq(CR_OK, status) | ||
| expect.true_(item.flags.garbage_collect) | ||
| expect.true_(item.flags.forbid) | ||
| expect.true_(item.flags.hidden) | ||
|
|
||
| local output, status2 = dfhack.run_command_silent('autodump', 'undestroy') | ||
| expect.eq(CR_OK, status2) | ||
| expect.str_find('unmarked for destruction', output) | ||
| expect.false_(item.flags.garbage_collect) | ||
| expect.false_(item.flags.forbid) | ||
| expect.false_(item.flags.hidden) | ||
| -- undestroy restores the pre-destroy flags, including the dump | ||
| -- flag we set ourselves | ||
| expect.true_(item.flags.dump) | ||
| end) | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| config.mode = 'fortress' | ||
| config.target = 'changeitem' | ||
|
|
||
| local dwarfmode = require('gui.dwarfmode') | ||
|
|
||
| local function find_floor_item_pos() | ||
| for _, item in ipairs(df.global.world.items.other.IN_PLAY) do | ||
| if not item.flags.hidden and not item.flags.in_inventory | ||
| and not item.flags.in_job and not item.flags.construction then | ||
| local x, y, z = dfhack.items.getPosition(item) | ||
| if x then return item, x, y, z end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| local function set_cursor(x, y, z) | ||
| dwarfmode.setCursorPos(xyz2pos(x, y, z)) | ||
| end | ||
|
|
||
| local function clear_cursor() | ||
| dwarfmode.clearCursorPos() | ||
| end | ||
|
Comment on lines
+16
to
+22
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API updating is preferable but not necessary. |
||
|
|
||
| function test.here_quality_changes_item() | ||
| local item, x, y, z = find_floor_item_pos() | ||
| expect.ne(nil, item, 'test needs an item on the ground') | ||
| local orig_quality = item.quality | ||
|
|
||
| return dfhack.with_finalize(function() | ||
| item.quality = orig_quality | ||
| clear_cursor() | ||
| end, function() | ||
| set_cursor(x, y, z) | ||
| local output, status = dfhack.run_command_silent('changeitem', | ||
| 'here', 'q', '4') | ||
| expect.eq(CR_OK, status) | ||
| expect.str_find('items processed', output) | ||
| expect.eq(4, item.quality) | ||
| end) | ||
| end | ||
|
|
||
| function test.here_empty_tile_processes_nothing() | ||
| -- find a floor tile and put the cursor on it; if it happens to hold | ||
| -- items the count is still reported | ||
| local block = df.global.world.map.map_blocks[0] | ||
| local x, y, z = block.map_pos.x, block.map_pos.y, block.map_pos.z | ||
|
|
||
| return dfhack.with_finalize(clear_cursor, function() | ||
| set_cursor(x, y, z) | ||
| local output, status = dfhack.run_command_silent('changeitem', 'here') | ||
| expect.eq(CR_OK, status) | ||
| expect.str_find('items processed', output) | ||
| end) | ||
| end | ||
|
|
||
| function test.here_no_cursor_is_failure() | ||
| return dfhack.with_finalize(clear_cursor, function() | ||
| clear_cursor() | ||
| local output, status = dfhack.run_command_silent('changeitem', 'here') | ||
| expect.eq(CR_FAILURE, status) | ||
| expect.str_find('Cursor position not found', output) | ||
| end) | ||
| end | ||
|
|
||
| function test.no_selection_is_failure() | ||
| local output, status = dfhack.run_command_silent('changeitem') | ||
| expect.eq(CR_FAILURE, status) | ||
| expect.str_find('No item selected', output) | ||
| end | ||
|
|
||
| function test.material_missing_arg_is_wrong_usage() | ||
| local output, status = dfhack.run_command_silent('changeitem', 'm') | ||
| expect.eq(CR_WRONG_USAGE, status) | ||
| expect.str_find('no material specified', output) | ||
| end | ||
|
|
||
| function test.bad_quality_is_wrong_usage() | ||
| local _, status = dfhack.run_command_silent('changeitem', 'q', '9') | ||
| expect.eq(CR_WRONG_USAGE, status) | ||
| end | ||
|
|
||
| function test.bad_option_is_wrong_usage() | ||
| local _, status = dfhack.run_command_silent('changeitem', 'bogus') | ||
| expect.eq(CR_WRONG_USAGE, status) | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| config.mode = 'fortress' | ||
| config.target = 'changelayer' | ||
|
|
||
| local dwarfmode = require('gui.dwarfmode') | ||
|
|
||
| function test.no_material_is_wrong_usage() | ||
| local output, status = dfhack.run_command_silent('changelayer') | ||
| expect.eq(CR_WRONG_USAGE, status) | ||
| expect.str_find('specify a material', output) | ||
| end | ||
|
|
||
| function test.bad_material_is_failure() | ||
| local output, status = dfhack.run_command_silent('changelayer', 'BOGUSMAT') | ||
| expect.eq(CR_FAILURE, status) | ||
| expect.str_find('No such material', output) | ||
| end | ||
|
|
||
| function test.no_cursor_is_failure() | ||
| return dfhack.with_finalize(function() | ||
| dwarfmode.clearCursorPos() | ||
| end, function() | ||
| dwarfmode.clearCursorPos() | ||
| local output, status = dfhack.run_command_silent('changelayer', | ||
| 'GRANITE') | ||
| expect.eq(CR_FAILURE, status) | ||
| expect.str_find('No cursor', output) | ||
| end) | ||
| end | ||
|
|
||
| function test.help_is_wrong_usage() | ||
| local _, status = dfhack.run_command_silent('changelayer', '?') | ||
| expect.eq(CR_WRONG_USAGE, status) | ||
| end |
Uh oh!
There was an error while loading. Please reload this page.