From 063602349c3578caa9828b5475b8760434b88b61 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 18 Jul 2018 14:29:13 -0400 Subject: [PATCH] Add a basic test library and a couple GUI module tests (squashed) Also modified Core/Console a bit to get this to actually produce output on Travis (DFHACK_DISABLE_CONSOLE now allows console output, just not input) Squashed merge from lethosor/tests --- .travis.yml | 15 ++-- CMakeLists.txt | 1 + library/Core.cpp | 6 +- test/gui.lua | 25 +++++++ test/main.lua | 153 +++++++++++++++++++++++++++++++++++++- travis/dfhack_travis.init | 4 +- travis/run-tests.py | 7 +- 7 files changed, 198 insertions(+), 13 deletions(-) create mode 100644 test/gui.lua diff --git a/.travis.yml b/.travis.yml index 33f33a274..619b48d4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ before_install: - sh travis/build-lua.sh - sh travis/download-df.sh - echo "export DFHACK_HEADLESS=1" >> "$HOME/.dfhackrc" +- echo "export DFHACK_DISABLE_CONSOLE=1" >> "$HOME/.dfhackrc" script: - export PATH="$PATH:$HOME/lua53/bin" - git tag tmp-travis-build @@ -46,17 +47,19 @@ script: - python travis/script-syntax.py --ext=rb --cmd="ruby -c" - mkdir build-travis - cd build-travis -- cmake .. -G Ninja -DCMAKE_C_COMPILER=gcc-$GCC_VERSION -DCMAKE_CXX_COMPILER=g++-$GCC_VERSION -DDFHACK_BUILD_ARCH=64 -DBUILD_DOCS:BOOL=ON -DCMAKE_INSTALL_PREFIX="$DF_FOLDER" +- cmake .. -G Ninja -DCMAKE_C_COMPILER=gcc-$GCC_VERSION -DCMAKE_CXX_COMPILER=g++-$GCC_VERSION -DDFHACK_BUILD_ARCH=64 -DBUILD_DOCS:BOOL=ON -DBUILD_SUPPORTED:BOOL=OFF -DCMAKE_INSTALL_PREFIX="$DF_FOLDER" - ninja -j3 install - mv "$DF_FOLDER"/dfhack.init-example "$DF_FOLDER"/dfhack.init - cd .. - cp travis/dfhack_travis.init "$DF_FOLDER"/ - python travis/run-tests.py "$DF_FOLDER" - python travis/check-rpc.py "$DF_FOLDER/dfhack-rpc.txt" +- cat "$DF_FOLDER/stderr.log" +- rm -rf "$DF_FOLDER/hack" notifications: email: false - irc: - channels: - - "chat.freenode.net#dfhack" - on_success: change - on_failure: always + # irc: + # channels: + # - "chat.freenode.net#dfhack" + # on_success: change + # on_failure: always diff --git a/CMakeLists.txt b/CMakeLists.txt index b27a23cd9..3237dabdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,6 +406,7 @@ file(WRITE "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" ${DFHACK_SETARCH}) install(FILES "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" DESTINATION "${DFHACK_DATA_DESTINATION}") install(DIRECTORY dfhack-config/ DESTINATION dfhack-config/default) +install(DIRECTORY test DESTINATION "${DFHACK_DATA_DESTINATION}") #build the plugins IF(BUILD_PLUGINS) diff --git a/library/Core.cpp b/library/Core.cpp index 69ef5a132..56c400e87 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1476,6 +1476,10 @@ void fIOthread(void * iodata) dfhack_version_desc().c_str()); int clueless_counter = 0; + + if (getenv("DFHACK_DISABLE_CONSOLE")) + return; + while (true) { string command = ""; @@ -1705,7 +1709,7 @@ bool Core::Init() cerr << "Headless mode not supported on Windows" << endl; #endif } - if ((is_text_mode && !is_headless) || getenv("DFHACK_DISABLE_CONSOLE")) + if (is_text_mode && !is_headless) { con.init(true); cerr << "Console is not available. Use dfhack-run to send commands.\n"; diff --git a/test/gui.lua b/test/gui.lua new file mode 100644 index 000000000..1550fc1de --- /dev/null +++ b/test/gui.lua @@ -0,0 +1,25 @@ +function test.getCurViewscreen() + local scr = dfhack.gui.getCurViewscreen() + local scr2 = df.global.gview.view + for i = 1, 100 do + if scr2.child then + scr2 = scr2.child + else + break + end + end + expect.eq(scr, scr2) +end + +function test.getViewscreenByType() + local scr = dfhack.gui.getCurViewscreen() + local scr2 = dfhack.gui.getViewscreenByType(scr._type) + expect.eq(scr, scr2) + + local bad_type = df.viewscreen_titlest + if scr._type == bad_type then + bad_type = df.viewscreen_optionst + end + local scr_bad = dfhack.gui.getViewscreenByType(bad_type) + expect.eq(scr_bad, nil) +end diff --git a/test/main.lua b/test/main.lua index 0085a9711..788ae2487 100644 --- a/test/main.lua +++ b/test/main.lua @@ -1,10 +1,157 @@ +local args = {...} +local done_command = args[1] + +expect = {} +function expect.true_(value) + return not not value +end +function expect.false_(value) + return not value +end +function expect.eq(a, b) + return a == b +end +function expect.ne(a, b) + return a ~= b +end +function expect.error(func, ...) + local ok, ret = pcall(func, ...) + if ok then + return false, 'no error raised by function call' + else + return true + end +end + +function build_test_env() + local env = { + test = {}, + expect = {}, + } + local private = { + checks = 0, + checks_ok = 0, + } + for name, func in pairs(expect) do + env.expect[name] = function(...) + private.checks = private.checks + 1 + local ok, msg = func(...) + if ok then + private.checks_ok = private.checks_ok + 1 + else + dfhack.printerr('Check failed! ' .. (msg or '(no message)')) + local info = debug.getinfo(2) + dfhack.printerr((' at %s:%d'):format(info.short_src, info.currentline)) + print('') + end + end + end + setmetatable(env, {__index = _G}) + return env, private +end + +function get_test_files() + local files = {} + for _, entry in ipairs(dfhack.filesystem.listdir_recursive(dfhack.getHackPath() .. 'test')) do + if not entry.isdir and not entry.path:match('main.lua') then + table.insert(files, entry.path) + end + end + table.sort(files) + return files +end + function set_test_stage(stage) local f = io.open('test_stage.txt', 'w') f:write(stage) f:close() end -print('running tests') +function finish_tests(ok) + if ok then + print('Tests finished') + else + dfhack.printerr('Tests failed!') + end + if done_command then + dfhack.run_command(done_command) + end +end + +function main() + local files = get_test_files() + + local counts = { + tests = 0, + tests_ok = 0, + checks = 0, + checks_ok = 0, + file_errors = 0, + } + local passed = true + + print('Running tests') + for _, file in ipairs(files) do + print('Running file: ' .. file:sub(file:find('test'), -1)) + local env, env_private = build_test_env() + local code, err = loadfile(file, 't', env) + if not code then + passed = false + counts.file_errors = counts.file_errors + 1 + dfhack.printerr('Failed to load file: ' .. tostring(err)) + else + local ok, err = pcall(code) + if not ok then + passed = false + counts.file_errors = counts.file_errors + 1 + dfhack.printerr('Error when running file: ' .. tostring(err)) + else + for name, test in pairs(env.test) do + env_private.checks = 0 + env_private.checks_ok = 0 + counts.tests = counts.tests + 1 + local ok, err = pcall(test) + if not ok then + dfhack.printerr('test errored: ' .. name .. ': ' .. tostring(err)) + passed = false + elseif env_private.checks ~= env_private.checks_ok then + dfhack.printerr('test failed: ' .. name) + passed = false + else + print('test passed: ' .. name) + counts.tests_ok = counts.tests_ok + 1 + end + counts.checks = counts.checks + (tonumber(env_private.checks) or 0) + counts.checks_ok = counts.checks_ok + (tonumber(env_private.checks_ok) or 0) + end + end + end + end + + print('\nTest summary:') + print(('%d/%d tests passed'):format(counts.tests_ok, counts.tests)) + print(('%d/%d checks passed'):format(counts.checks_ok, counts.checks)) + print(('%d test files failed to load'):format(counts.file_errors)) + + set_test_stage(passed and 'done' or 'fail') + finish_tests(passed) +end + +local check_count = 0 +function check_title() + local scr = dfhack.gui.getCurViewscreen() + if df.viewscreen_titlest:is_instance(scr) then + print('Found title screen') + main() + else + check_count = check_count + 1 + if check_count > 100 then + qerror('Could not find title screen') + end + scr:feed_key(df.interface_key.LEAVESCREEN) + dfhack.timeout(10, 'frames', check_title) + end +end -set_test_stage('done') -dfhack.run_command('die') +print('Looking for title screen...') +check_title() diff --git a/travis/dfhack_travis.init b/travis/dfhack_travis.init index eb7858bbe..9c74dc3b4 100644 --- a/travis/dfhack_travis.init +++ b/travis/dfhack_travis.init @@ -1,4 +1,4 @@ devel/dump-rpc dfhack-rpc.txt -:lua dfhack.internal.addScriptPath(os.getenv('TRAVIS_BUILD_DIR')) -test/main +:lua dfhack.internal.addScriptPath(dfhack.getHackPath()) +test/main die diff --git a/travis/run-tests.py b/travis/run-tests.py index a8265088c..ae48074b5 100644 --- a/travis/run-tests.py +++ b/travis/run-tests.py @@ -23,8 +23,13 @@ while True: print('Done!') os.remove(test_stage) sys.exit(0) + elif stage == 'fail': + print('Failed!') + os.remove(test_stage) + sys.exit(1) if tries > MAX_TRIES: print('Too many tries - aborting') sys.exit(1) - os.system(dfhack) + process = subprocess.Popen([dfhack], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + process.communicate()