diff --git a/.gitignore b/.gitignore index 56e8496be5a..f42a68714e6 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,4 @@ projectfiles/VC201* *Neuer Ordner* Thumbs.db error.log +errors.log diff --git a/data/test/scenarios/break_replay_with_lua_random.cfg b/data/test/scenarios/break_replay_with_lua_random.cfg new file mode 100644 index 00000000000..74c0cb78409 --- /dev/null +++ b/data/test/scenarios/break_replay_with_lua_random.cfg @@ -0,0 +1,69 @@ +# The purpose of this test is to make a scenario that plays but goes out of sync with the replay. +# This is done as a sanity check of the unit testing mechanism. +# We use the unsynced rng, lua.random, to achieve this. +# Basically we keep giving side 1 a random amount of gold and recruiting as +# many woses as possible, killing them immediately to make space. We do this +# for 30 turns. In the replay we will actually have a random amount of gold +# each time, so if we have bad luck one turn we won't have enough to afford +# that many woses. +# For each turn its about 50-50 not to go out of sync, so the chance not to +# go out of sync should be roughly less than one in a billion. + +{GENERIC_UNIT_TEST "break_replay_with_lua_random" ( + [event] + name = start + [allow_recruit] + side=1 + type=Wose + [/allow_recruit] + [/event] + [event] + name = recruit + first_time_only = no + [kill] + x = $x1 + y = $y1 + animate=yes + [/kill] + [/event] + [event] + name = side 1 turn refresh + first_time_only=no + [lua] + code =<< + wesnoth.set_variable("rnd_num", math.random(200)) + >> + [/lua] + [modify_side] + side = 1 + gold = $rnd_num + [/modify_side] + [while] + {VARIABLE_CONDITIONAL rnd_num greater_than_equal_to 20} + [do] + [do_command] + [recruit] + type="Wose" + x,y=7,4 + [from] + x,y=7,3 + [/from] + [/recruit] + [/do_command] + {VARIABLE_OP rnd_num sub 20} + [/do] + [/while] + [end_turn] + [/end_turn] + [/event] + [event] + name = side 2 turn 30 + {RETURN ([true][/true])} + [/event] + [event] + name = side 2 turn refresh + first_time_only=no + [end_turn] + [/end_turn] + [/event] +)} diff --git a/data/test/scenarios/fixed_lua_random_replay_with_sync_choice.cfg b/data/test/scenarios/fixed_lua_random_replay_with_sync_choice.cfg new file mode 100644 index 00000000000..877b1644df0 --- /dev/null +++ b/data/test/scenarios/fixed_lua_random_replay_with_sync_choice.cfg @@ -0,0 +1,62 @@ +{GENERIC_UNIT_TEST "fixed_lua_random_replay_with_sync_choice" ( + [event] + name = start + [allow_recruit] + side=1 + type=Wose + [/allow_recruit] + [/event] + [event] + name = recruit + first_time_only = no + [kill] + x = $x1 + y = $y1 + animate=yes + [/kill] + [/event] + [event] + name = side 1 turn refresh + first_time_only=no + [lua] + code =<< + local result = wesnoth.synchronize_choice( + function() + return { value = math.random(200) } + end) + wesnoth.set_variable("rnd_num", result.value) + >> + [/lua] + [modify_side] + side = 1 + gold = $rnd_num + [/modify_side] + [while] + {VARIABLE_CONDITIONAL rnd_num greater_than_equal_to 20} + [do] + [do_command] + [recruit] + type="Wose" + x,y=7,4 + [from] + x,y=7,3 + [/from] + [/recruit] + [/do_command] + {VARIABLE_OP rnd_num sub 20} + [/do] + [/while] + [end_turn] + [/end_turn] + [/event] + [event] + name = side 2 turn 30 + {RETURN ([true][/true])} + [/event] + [event] + name = side 2 turn refresh + first_time_only=no + [end_turn] + [/end_turn] + [/event] +)} diff --git a/run_wml_tests b/run_wml_tests index 7930a4d96b2..8842559310a 100755 --- a/run_wml_tests +++ b/run_wml_tests @@ -45,8 +45,14 @@ get_code_string() 4) CodeString="FAIL (ERRORED REPLAY)" ;; + 134) + CodeString="FAIL (ASSERTION FAILURE ? ? ?)" + ;; + 139) + CodeString="FAIL (SEGFAULT ? ? ?)" + ;; *) - CodeString="? ? ?" + CodeString="FAIL (? ? ?)" ;; esac } @@ -62,6 +68,8 @@ check_errs() echo "This means wesnoth tried to kill the thread but SDL threw an error..." echo "(This happens occasionally when running the empty_test with a timeout.)" echo "Since we expected timeout, the test passes." + echo "" + echo "*However*, review the logs, because it may also mean an assertion failure..." fi return 0 elif [ "${2}" -eq 124 -a "${3}" -eq 2 -a $UnixTimeout -eq 1 ]; then @@ -220,6 +228,7 @@ fi AllPassed=1 FirstTest=1 +TotalPassed=0 for line in "${schedule[@]}" do @@ -233,6 +242,7 @@ do if [ $Verbose -ge 2 ]; then echo "good" fi + TotalPassed=$((TotalPassed+1)) else AllPassed=0 fi @@ -241,6 +251,7 @@ do if [ $Verbose -ge 2 ]; then echo "good" fi + TotalPassed=$((TotalPassed+1)) else AllPassed=0 fi @@ -249,6 +260,7 @@ do done if [ $AllPassed -eq 0 ]; then + echo "$TotalPassed" "out of" $TESTS "tests were correct." echo "Not all tests gave the correct result." echo "Check errors.log for error reports." exit 1 diff --git a/src/game_controller.cpp b/src/game_controller.cpp index d67f9b689f7..15417e5224a 100644 --- a/src/game_controller.cpp +++ b/src/game_controller.cpp @@ -462,7 +462,7 @@ int game_controller::unit_test() load_game_config_for_game(state_.classification()); try { - LEVEL_RESULT res = play_game(disp(),state_,resources::config_manager->game_config()); + LEVEL_RESULT res = play_game(disp(),state_,resources::config_manager->game_config(), IO_NONE, false, false, false, true); if (!(res == VICTORY || res == NONE)) { return 1; } @@ -482,6 +482,8 @@ int game_controller::unit_test() savegame::replay_savegame save(state_, compression::NONE); save.save_game_automatic(disp().video(), false, "unit_test_replay"); //false means don't check for overwrite + clear_loaded_game(); + //game::load_game_exception::game = *cmdline_opts_.load game::load_game_exception::game = "unit_test_replay"; // game::load_game_exception::game = "Unit_test_" + test_scenario_ + "_replay"; @@ -495,13 +497,15 @@ int game_controller::unit_test() } try { - LEVEL_RESULT res = play_game(disp(), state_, resources::config_manager->game_config()); + //LEVEL_RESULT res = play_game(disp(), state_, resources::config_manager->game_config(), IO_NONE, false,false,false,true); + LEVEL_RESULT res = ::play_replay(disp(), state_, resources::config_manager->game_config(), video_, true); if (!(res == VICTORY || res == NONE)) { std::cerr << "Observed failure on replay" << std::endl; return 4; } /*::play_replay(disp(),state_,resources::config_manager->game_config(), video_);*/ + clear_loaded_game(); } catch (game::load_game_exception &) { std::cerr << "Load_game_exception encountered during play_replay!" << std::endl; return 3; //failed to load replay diff --git a/src/playcampaign.cpp b/src/playcampaign.cpp index fad7b1c33a1..548240d28cb 100644 --- a/src/playcampaign.cpp +++ b/src/playcampaign.cpp @@ -206,8 +206,8 @@ static void generate_map(config const*& scenario) scenario = &new_scenario; } -void play_replay(display& disp, game_state& gamestate, const config& game_config, - CVideo& video) +LEVEL_RESULT play_replay(display& disp, game_state& gamestate, const config& game_config, + CVideo& video, bool is_unit_test) { std::string type = gamestate.classification().campaign_type; if(type.empty()) @@ -236,21 +236,44 @@ void play_replay(display& disp, game_state& gamestate, const config& game_config //if (gamestate.abbrev.empty()) // gamestate.abbrev = (*scenario)["abbrev"]; - play_replay_level(game_config, &starting_pos, video, gamestate); + LEVEL_RESULT res = play_replay_level(game_config, &starting_pos, video, gamestate, is_unit_test); gamestate.snapshot = config(); recorder.clear(); gamestate.replay_data.clear(); + return res; } catch(game::load_game_failed& e) { - gui2::show_error_message(disp.video(), _("The game could not be loaded: ") + e.message); + if (is_unit_test) { + std::cerr << std::string(_("The game could not be loaded: ")) + " (game::load_game_failed) " + e.message << std::endl; + return DEFEAT; + } else { + gui2::show_error_message(disp.video(), _("The game could not be loaded: ") + e.message); + } } catch(game::game_error& e) { - gui2::show_error_message(disp.video(), _("Error while playing the game: ") + e.message); + if (is_unit_test) { + std::cerr << std::string(_("Error while playing the game: ")) + " (game::game_error) " + e.message << std::endl; + return DEFEAT; + } else { + gui2::show_error_message(disp.video(), std::string(_("Error while playing the game: ")) + e.message); + } } catch(incorrect_map_format_error& e) { - gui2::show_error_message(disp.video(), std::string(_("The game map could not be loaded: ")) + e.message); + if (is_unit_test) { + std::cerr << std::string(_("The game map could not be loaded: ")) + " (incorrect_map_format_error) " + e.message << std::endl; + return DEFEAT; + } else { + gui2::show_error_message(disp.video(), std::string(_("The game map could not be loaded: ")) + e.message); + } } catch(twml_exception& e) { - e.show(disp); + if (is_unit_test) { + std::cerr << std::string("WML Exception: ") + e.user_message << std::endl; + std::cerr << std::string("Dev Message: ") + e.dev_message << std::endl; + return DEFEAT; + } else { + e.show(disp); + } } + return NONE; } static LEVEL_RESULT playsingle_scenario(const config& game_config, @@ -377,7 +400,7 @@ static LEVEL_RESULT playmp_scenario(const config& game_config, LEVEL_RESULT play_game(game_display& disp, game_state& gamestate, const config& game_config, io_type_t io_type, bool skip_replay, - bool network_game, bool blindfold_replay) + bool network_game, bool blindfold_replay, bool is_unit_test) { std::string type = gamestate.classification().campaign_type; if(type.empty()) @@ -512,6 +535,10 @@ LEVEL_RESULT play_game(game_display& disp, game_state& gamestate, return QUIT; } + if (is_unit_test) { + return res; + } + // Save-management options fire on game end. // This means: (a) we have a victory, or // or (b) we're multiplayer live, in which diff --git a/src/playcampaign.hpp b/src/playcampaign.hpp index 25416caf360..9ccbcb5d052 100644 --- a/src/playcampaign.hpp +++ b/src/playcampaign.hpp @@ -36,10 +36,12 @@ LEVEL_RESULT play_game(game_display& disp, game_state& state, io_type_t io_type=IO_NONE, bool skip_replay = false, bool network_game = false, - bool blindfold_replay = false); + bool blindfold_replay = false, + bool is_unit_test = false); -void play_replay(display& disp, game_state& state, - const config& game_config, CVideo& video); +LEVEL_RESULT play_replay(display& disp, game_state& state, + const config& game_config, CVideo& video, + bool is_unit_test = false); #endif // PLAYCAMPAIGN_H_INCLUDED diff --git a/src/replay_controller.cpp b/src/replay_controller.cpp index fc4cc9fa87e..3b07d45b604 100644 --- a/src/replay_controller.cpp +++ b/src/replay_controller.cpp @@ -40,7 +40,7 @@ static lg::log_domain log_replay("replay"); #define ERR_REPLAY LOG_STREAM(err, log_replay) LEVEL_RESULT play_replay_level(const config& game_config, - const config* level, CVideo& video, game_state& state_of_game) + const config* level, CVideo& video, game_state& state_of_game, bool is_unit_test) { try{ const int ticks = SDL_GetTicks(); @@ -59,6 +59,11 @@ LEVEL_RESULT play_replay_level(const config& game_config, //replay event-loop for (;;){ replaycontroller.play_slice(); + if (is_unit_test) { + if (replaycontroller.manage_noninteractively()) { + return VICTORY; + } + } } } catch(end_level_exception&){ @@ -352,14 +357,20 @@ void replay_controller::replay_next_side(){ void replay_controller::process_oos(const std::string& msg) const { - if (game_config::ignore_replay_errors) return; + if (game_config::ignore_replay_errors) { + return; + } std::stringstream message; message << _("The replay is corrupt/out of sync. It might not make much sense to continue. Do you want to save the game?"); message << "\n\n" << _("Error details:") << "\n\n" << msg; - savegame::oos_savegame save(to_config()); - save.save_game_interactive(resources::screen->video(), message.str(), gui::YES_NO); // can throw end_level_exception + if (non_interactive()) { + throw game::game_error(message.str()); //throw end_level_exception(DEFEAT); + } else { + savegame::oos_savegame save(to_config()); + save.save_game_interactive(resources::screen->video(), message.str(), gui::YES_NO); // can throw end_level_exception + } } void replay_controller::replay_show_everything(){ @@ -567,3 +578,14 @@ bool replay_controller::can_execute_command(const hotkey::hotkey_command& cmd, i return result; } } + +bool replay_controller::manage_noninteractively() { + if (recorder.at_end()) { + return true; + } else { + if (!is_playing_) { + play_replay(); + } + return false; + } +} diff --git a/src/replay_controller.hpp b/src/replay_controller.hpp index 2433d241ce5..9f5e17982e6 100644 --- a/src/replay_controller.hpp +++ b/src/replay_controller.hpp @@ -54,6 +54,8 @@ public: std::vector teams_start_; + bool manage_noninteractively(); + protected: virtual void init_gui(); @@ -98,6 +100,6 @@ private: LEVEL_RESULT play_replay_level(const config& terrain_config, const config* level, CVideo& video, - game_state& state_of_game); + game_state& state_of_game, bool is_unit_test = false); #endif diff --git a/wml_test_schedule b/wml_test_schedule index 28ab980e917..b5acb5c1012 100644 --- a/wml_test_schedule +++ b/wml_test_schedule @@ -7,6 +7,8 @@ 1 test_assert_fail 1 test_assert_fail_two 2 empty_test +4 break_replay_with_lua_random +0 fixed_lua_random_replay_with_sync_choice # # WML API tests #