|
|
|
@ -2,6 +2,7 @@
|
|
|
|
|
--@ module = true
|
|
|
|
|
|
|
|
|
|
local expect = require('test_util.expect')
|
|
|
|
|
local gui = require('gui')
|
|
|
|
|
local helpdb = require('helpdb')
|
|
|
|
|
local json = require('json')
|
|
|
|
|
local mock = require('test_util.mock')
|
|
|
|
@ -151,33 +152,37 @@ end
|
|
|
|
|
test_envvars.require = clean_require
|
|
|
|
|
test_envvars.reqscript = clean_reqscript
|
|
|
|
|
|
|
|
|
|
local function is_title_screen(scr)
|
|
|
|
|
scr = scr or dfhack.gui.getCurViewscreen()
|
|
|
|
|
return df.viewscreen_titlest:is_instance(scr)
|
|
|
|
|
local function is_title_screen()
|
|
|
|
|
return dfhack.gui.matchFocusString('title/Default')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- This only handles pre-fortress-load screens. It will time out if the player
|
|
|
|
|
-- has already loaded a fortress or is in any screen that can't get to the title
|
|
|
|
|
-- screen by sending ESC keys.
|
|
|
|
|
local function ensure_title_screen()
|
|
|
|
|
local function wait_for(ms, desc, predicate)
|
|
|
|
|
local start_ms = dfhack.getTickCount()
|
|
|
|
|
local prev_ms = start_ms
|
|
|
|
|
while df.viewscreen_initial_prepst:is_instance(dfhack.gui.getCurViewscreen()) do
|
|
|
|
|
while not predicate() do
|
|
|
|
|
delay(10)
|
|
|
|
|
-- wait up to 1 minute for the game to load and show the title screen
|
|
|
|
|
local now_ms = dfhack.getTickCount()
|
|
|
|
|
if now_ms - start_ms > 60000 then
|
|
|
|
|
qerror(('Could not find title screen (timed out at %s)'):format(
|
|
|
|
|
dfhack.gui.getCurFocus(true)[1]))
|
|
|
|
|
if now_ms - start_ms > ms then
|
|
|
|
|
qerror(('%s took too long (timed out at %s)'):format(
|
|
|
|
|
desc, dfhack.gui.getCurFocus(true)[1]))
|
|
|
|
|
end
|
|
|
|
|
if now_ms - prev_ms > 1000 then
|
|
|
|
|
print('Waiting for game to load and show title screen...')
|
|
|
|
|
print(('Waiting for %s...'):format(desc))
|
|
|
|
|
prev_ms = now_ms
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- This only handles pre-fortress-load screens. It will time out if the player
|
|
|
|
|
-- has already loaded a fortress or is in any screen that can't get to the title
|
|
|
|
|
-- screen by sending ESC keys.
|
|
|
|
|
local function ensure_title_screen()
|
|
|
|
|
if df.viewscreen_dwarfmodest:is_instance(dfhack.gui.getDFViewscreen(true)) then
|
|
|
|
|
qerror('Cannot reach title screen from loaded fort')
|
|
|
|
|
end
|
|
|
|
|
for i = 1, 100 do
|
|
|
|
|
local scr = dfhack.gui.getCurViewscreen()
|
|
|
|
|
if is_title_screen(scr) then
|
|
|
|
|
if is_title_screen() then
|
|
|
|
|
print('Found title screen')
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
@ -189,54 +194,94 @@ local function ensure_title_screen()
|
|
|
|
|
dfhack.gui.getCurFocus(true)[1]))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function is_fortress(focus_string)
|
|
|
|
|
focus_string = focus_string or dfhack.gui.getCurFocus(true)
|
|
|
|
|
return focus_string == 'dwarfmode/Default'
|
|
|
|
|
local function is_fortress()
|
|
|
|
|
return dfhack.gui.matchFocusString('dwarfmode/Default')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- error out if we're not running in a CI environment
|
|
|
|
|
-- the tests may corrupt saves, and we don't want to unexpectedly ruin a real player save
|
|
|
|
|
-- this heuristic is not perfect, but it should be able to detect most cases
|
|
|
|
|
local function ensure_ci_save(scr)
|
|
|
|
|
if #scr.savegame_header ~= 1
|
|
|
|
|
or #scr.savegame_header_world ~= 1
|
|
|
|
|
or not string.find(scr.savegame_header[0].fort_name, 'Dream')
|
|
|
|
|
then
|
|
|
|
|
qerror('Unexpected test save in slot 0; please manually load a fort for ' ..
|
|
|
|
|
'running fortress mode tests. note that tests may alter or corrupt the ' ..
|
|
|
|
|
'fort! Do not save after running tests.')
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function click_top_title_button(scr)
|
|
|
|
|
local sw, sh = dfhack.screen.getWindowSize()
|
|
|
|
|
df.global.gps.mouse_x = sw // 2
|
|
|
|
|
df.global.gps.precise_mouse_x = df.global.gps.mouse_x * df.global.gps.tile_pixel_x
|
|
|
|
|
if sh < 60 then
|
|
|
|
|
df.global.gps.mouse_y = 25
|
|
|
|
|
else
|
|
|
|
|
df.global.gps.mouse_y = (sh // 2) + 3
|
|
|
|
|
end
|
|
|
|
|
df.global.gps.precise_mouse_y = df.global.gps.mouse_y * df.global.gps.tile_pixel_y
|
|
|
|
|
df.global.enabler.tracking_on = 1
|
|
|
|
|
df.global.enabler.mouse_lbut = 1
|
|
|
|
|
df.global.enabler.mouse_lbut_down = 1
|
|
|
|
|
gui.simulateInput(scr, '_MOUSE_L')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function load_first_save(scr)
|
|
|
|
|
if #scr.savegame_header == 0 then
|
|
|
|
|
qerror('no savegames available to load')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
click_top_title_button(scr)
|
|
|
|
|
wait_for(1000, 'world list', function()
|
|
|
|
|
return scr.mode == 2
|
|
|
|
|
end)
|
|
|
|
|
click_top_title_button(scr)
|
|
|
|
|
wait_for(1000, 'savegame list', function()
|
|
|
|
|
return scr.mode == 3
|
|
|
|
|
end)
|
|
|
|
|
click_top_title_button(scr)
|
|
|
|
|
wait_for(1000, 'loadgame progress bar', function()
|
|
|
|
|
return dfhack.gui.matchFocusString('loadgame')
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Requires that a fortress game is already loaded or is ready to be loaded via
|
|
|
|
|
-- the "Continue Playing" option in the title screen. Otherwise the function
|
|
|
|
|
-- the "Continue active game" option in the title screen. Otherwise the function
|
|
|
|
|
-- will time out and/or exit with error.
|
|
|
|
|
local function ensure_fortress(config)
|
|
|
|
|
local focus_string = dfhack.gui.getCurFocus(true)
|
|
|
|
|
for screen_timeout = 1,10 do
|
|
|
|
|
if is_fortress(focus_string) then
|
|
|
|
|
print('Loaded fortress map')
|
|
|
|
|
if is_fortress() then
|
|
|
|
|
print('Fortress map is loaded')
|
|
|
|
|
-- pause the game (if it's not already paused)
|
|
|
|
|
dfhack.gui.resetDwarfmodeView(true)
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
local scr = dfhack.gui.getCurViewscreen(true)
|
|
|
|
|
if focus_string == 'title' or
|
|
|
|
|
focus_string == 'dfhack/lua/load_screen' then
|
|
|
|
|
local scr = dfhack.gui.getCurViewscreen()
|
|
|
|
|
if dfhack.gui.matchFocusString('title/Default', scr) then
|
|
|
|
|
print('Attempting to load the test fortress')
|
|
|
|
|
-- TODO: reinstate loading of a specified save dir; for now
|
|
|
|
|
-- just load the first possible save, which will at least let us
|
|
|
|
|
-- run fortress tests in CI
|
|
|
|
|
-- qerror()'s on falure
|
|
|
|
|
dfhack.run_script('load-save', config.save_dir)
|
|
|
|
|
elseif focus_string ~= 'loadgame' then
|
|
|
|
|
-- dfhack.run_script('load-save', config.save_dir)
|
|
|
|
|
ensure_ci_save(scr)
|
|
|
|
|
load_first_save(scr)
|
|
|
|
|
elseif not dfhack.gui.matchFocusString('loadgame', scr) then
|
|
|
|
|
-- if we're not actively loading a game, hope we're in
|
|
|
|
|
-- a screen where hitting ESC will get us to the game map
|
|
|
|
|
-- or the title screen
|
|
|
|
|
scr:feed_key(df.interface_key.LEAVESCREEN)
|
|
|
|
|
end
|
|
|
|
|
-- wait for current screen to change
|
|
|
|
|
local prev_focus_string = focus_string
|
|
|
|
|
for frame_timeout = 1,100 do
|
|
|
|
|
delay(10)
|
|
|
|
|
focus_string = dfhack.gui.getCurFocus(true)
|
|
|
|
|
if focus_string ~= prev_focus_string then
|
|
|
|
|
goto next_screen
|
|
|
|
|
end
|
|
|
|
|
if frame_timeout % 10 == 0 then
|
|
|
|
|
print(string.format(
|
|
|
|
|
'Loading fortress (currently at screen: %s)',
|
|
|
|
|
focus_string))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
print('Timed out waiting for screen to change')
|
|
|
|
|
break
|
|
|
|
|
::next_screen::
|
|
|
|
|
local prev_focus_string = dfhack.gui.getCurFocus()[1]
|
|
|
|
|
wait_for(60000, 'screen change', function()
|
|
|
|
|
return dfhack.gui.getCurFocus()[1] ~= prev_focus_string
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
qerror(string.format('Could not load fortress (timed out at %s)',
|
|
|
|
|
focus_string))
|
|
|
|
|
table.concat(dfhack.gui.getCurFocus(), ' ')))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local MODES = {
|
|
|
|
@ -578,6 +623,10 @@ local function filter_tests(tests, config)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function run_tests(tests, status, counts, config)
|
|
|
|
|
wait_for(60000, 'game load', function()
|
|
|
|
|
local scr = dfhack.gui.getDFViewscreen()
|
|
|
|
|
return not df.viewscreen_initial_prepst:is_instance(scr)
|
|
|
|
|
end)
|
|
|
|
|
print(('Running %d tests'):format(#tests))
|
|
|
|
|
local start_ms = dfhack.getTickCount()
|
|
|
|
|
local num_skipped = 0
|
|
|
|
@ -596,12 +645,13 @@ local function run_tests(tests, status, counts, config)
|
|
|
|
|
goto skip
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
-- pre-emptively mark the test as failed in case we induce a crash
|
|
|
|
|
status[test.full_name] = TestStatus.FAILED
|
|
|
|
|
save_test_status(status)
|
|
|
|
|
if run_test(test, status, counts) then
|
|
|
|
|
status[test.full_name] = TestStatus.PASSED
|
|
|
|
|
else
|
|
|
|
|
status[test.full_name] = TestStatus.FAILED
|
|
|
|
|
save_test_status(status)
|
|
|
|
|
end
|
|
|
|
|
save_test_status(status)
|
|
|
|
|
::skip::
|
|
|
|
|
end
|
|
|
|
|
local elapsed_ms = dfhack.getTickCount() - start_ms
|
|
|
|
|