Merge pull request #3811 from myk002/myk_fortress_ci

enable fortress mode tests in CI
develop
Myk 2023-09-27 15:52:58 -07:00 committed by GitHub
commit e96a1087d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 67 deletions

@ -67,19 +67,25 @@ jobs:
compiler: msvc
plugins: "default"
config: "empty"
# TODO: uncomment once we have a linux build we can download from bay12
# - os: ubuntu
# compiler: gcc-10
# plugins: "default"
# config: "default"
# - os: ubuntu
# compiler: gcc-12
# plugins: "all"
# config: "default"
- os: ubuntu
compiler: gcc-10
plugins: "default"
config: "default"
- os: ubuntu
compiler: gcc-12
plugins: "all"
config: "default"
steps:
- name: Set env
shell: bash
run: echo "DF_FOLDER=DF" >> $GITHUB_ENV
- name: Install dependencies
if: matrix.os == 'ubuntu'
run: |
sudo apt-get update
sudo apt-get install \
libsdl2-2.0-0 \
libsdl2-image-2.0-0
- name: Clone DFHack
uses: actions/checkout@v3
with:
@ -117,8 +123,14 @@ jobs:
- name: Install DFHack
shell: bash
run: tar xjf test-${{ matrix.compiler }}.tar.bz2 -C ${{ env.DF_FOLDER }}
- name: Start X server
if: matrix.os == 'ubuntu'
run: Xvfb :0 -screen 0 1600x1200x24 &
- name: Run lua tests
timeout-minutes: 10
env:
DISPLAY: :0
TERM: xterm-256color
run: python ci/run-tests.py --keep-status "${{ env.DF_FOLDER }}"
- name: Check RPC interface
run: python ci/check-rpc.py "${{ env.DF_FOLDER }}/dfhack-rpc.txt"

@ -18,7 +18,7 @@ elif test "$OS_TARGET" = "ubuntu"; then
WGET=wget
df_url="${df_url}_linux.tar.bz2"
df_archive_name="df.tar.bz2"
df_extract_cmd="tar -x -j --strip-components=1 -f"
df_extract_cmd="tar -x -j -C ${DF_FOLDER} -f"
else
echo "Unhandled OS target: ${OS_TARGET}"
exit 1
@ -29,22 +29,25 @@ if ! $WGET -v "$df_url" -O "$df_archive_name"; then
exit 1
fi
md5sum "$df_archive_name"
save_url="https://dffd.bay12games.com/download.php?id=15434&f=dreamfort.7z"
save_archive_name="test_save.7z"
save_extract_cmd="7z x -oDF/save"
save_extract_cmd="7z x -o${DF_FOLDER}/save"
if ! $WGET -v "$save_url" -O "$save_archive_name"; then
echo "Failed to download test save from $save_url"
exit 1
fi
md5sum "$save_archive_name"
echo Extracting
mkdir -p ${DF_FOLDER}
$df_extract_cmd "$df_archive_name"
$save_extract_cmd "$save_archive_name"
mv DF/save/* DF/save/region1
mv ${DF_FOLDER}/save/* ${DF_FOLDER}/save/region1
echo Done
ls -l
md5sum "$df_archive_name" "$save_archive_name"

@ -65,14 +65,12 @@ if not os.path.exists(init_txt_path):
shutil.copyfile(init_txt_path, init_txt_path + '.orig')
with open(init_txt_path) as f:
init_contents = f.read()
init_contents = change_setting(init_contents, 'INTRO', 'NO')
init_contents = change_setting(init_contents, 'SOUND', 'NO')
init_contents = change_setting(init_contents, 'WINDOWED', 'YES')
init_contents = change_setting(init_contents, 'WINDOWEDX', '80')
init_contents = change_setting(init_contents, 'WINDOWEDY', '25')
init_contents = change_setting(init_contents, 'FPS', 'YES')
if args.headless:
init_contents = change_setting(init_contents, 'PRINT_MODE', 'TEXT')
init_contents = change_setting(init_contents, 'WINDOWEDX', '1200')
init_contents = change_setting(init_contents, 'WINDOWEDY', '800')
#if args.headless:
# init_contents = change_setting(init_contents, 'PRINT_MODE', 'TEXT')
init_path = 'dfhack-config/init'
if not os.path.isdir('hack/init'):

@ -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

@ -1,5 +1,5 @@
config.mode = 'fortress'
--config.target = 'orders'
config.target = 'orders'
local FILE_PATH_PATTERN = 'dfhack-config/orders/%s.json'