diff --git a/data/scene/earth/earth.mod b/data/scene/earth/earth.mod index f3dbe2bd77..2c1afa8a85 100644 --- a/data/scene/earth/earth.mod +++ b/data/scene/earth/earth.mod @@ -12,8 +12,9 @@ return { } } }, + { -- The default reference frame for Earth-orbiting satellites - Name = "EarthInertial", + Name = "EarthInertial", Parent = "EarthBarycenter", Transform = { Rotation = { diff --git a/include/openspace/scripting/scriptscheduler.h b/include/openspace/scripting/scriptscheduler.h index b8b5630c07..b5450d7996 100644 --- a/include/openspace/scripting/scriptscheduler.h +++ b/include/openspace/scripting/scriptscheduler.h @@ -25,55 +25,43 @@ #ifndef __SCRIPTSCHEDULER_H__ #define __SCRIPTSCHEDULER_H__ -#include - +#include #include #include #include +namespace ghoul { +class Dictionary; +} // namespace ghoul + namespace openspace { - namespace scripting { - - -struct ReversibleLuaScript { - std::string forwardScript; - std::string backwardScript; -}; - -struct ScheduledScript { - ScheduledScript() : time(-DBL_MAX) { } - ScheduledScript(const ghoul::Dictionary& dict); - - double time; - ReversibleLuaScript script; - - static bool CompareByTime(const ScheduledScript& s1, const ScheduledScript& s2); -}; - - /** * Maintains an ordered list of ScheduledScripts and provides a simple * interface for retrieveing scheduled scripts */ class ScriptScheduler { public: + struct ScheduledScript { + ScheduledScript() = default; + ScheduledScript(const ghoul::Dictionary& dict); + + double time; + std::string forwardScript; + std::string backwardScript; + }; + /** - * Load a schedule from a Lua-file - * \param filename Lua file to load - * \param L an optional lua_State defining variables that may be used - * in the Lua-file. + * Load a schedule from a ghoul::Dictionary \p dictionary and adds the + * ScheduledScript%s to the list of stored scripts. + * \param dictionary Dictionary to read + * \throw SpecificationError If the dictionary does not adhere to the Documentation as + * specified in the openspace::Documentation function */ - void loadScripts(const std::string& filename, lua_State* L = nullptr); - - /** - * Load a schedule from a ghoul::Dictionary - * \param dict Dictionary to read - */ - void loadScripts(const ghoul::Dictionary& dict); + void loadScripts(const ghoul::Dictionary& dictionary); /** @@ -96,7 +84,7 @@ public: * * \returns the ordered queue of scripts . */ - std::queue progressTo(double newTime); +// std::queue progressTo(double newTime); /** * See progressTo(double newTime). @@ -104,10 +92,12 @@ public: * \param timeStr A string specifying the a new time stamp that the * scripts scheduler should progress to. */ - std::queue progressTo(const std::string& timeStr); - - +// std::queue progressTo(const std::string& timeStr); + std::pair< + std::vector::const_iterator, std::vector::const_iterator + > progressTo(double newTime); + /** * Returns the the j2000 time value that the script scheduler is currently at */ @@ -116,16 +106,19 @@ public: /** * \returns a vector of all scripts that has been loaded */ - const std::vector& allScripts() const; + std::vector allScripts() const; static LuaLibrary luaLibrary(); + + static openspace::Documentation Documentation(); private: - - std::vector _scheduledScripts; - - size_t _currentIndex = 0; + std::vector _timings; + std::vector _forwardScripts; + std::vector _backwardScripts; + + int _currentIndex = 0; double _currentTime; }; diff --git a/include/openspace/util/time.h b/include/openspace/util/time.h index 2275c95eec..9657b71dcb 100644 --- a/include/openspace/util/time.h +++ b/include/openspace/util/time.h @@ -59,10 +59,20 @@ class SyncBuffer; class Time { public: + /** + * Converts the \p timeString representing a date to a double precision + * value representing the ephemeris time; that is the number of TDB + * seconds past the J2000 epoch. + * \param timeString A string representing the time to be converted + * \return The converted time; the number of TDB seconds past the J2000 epoch, + * representing the passed \p timeString + * \pre \p timeString must not be empty + */ + static double convertTime(const std::string& time); + Time(double secondsJ2000 = -1); Time(const Time& other); - /** * Initializes the Time singleton. * \return true if the initialization succeeded, false diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2a6220dd90..5d2c4f25b5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -140,8 +140,9 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode_doc.inl ${OPENSPACE_BASE_DIR}/src/scripting/lualibrary.cpp ${OPENSPACE_BASE_DIR}/src/scripting/scriptengine.cpp - ${OPENSPACE_BASE_DIR}/src/scripting/scriptscheduler.cpp ${OPENSPACE_BASE_DIR}/src/scripting/scriptengine_lua.inl + ${OPENSPACE_BASE_DIR}/src/scripting/scriptscheduler.cpp + ${OPENSPACE_BASE_DIR}/src/scripting/scriptscheduler_lua.inl ${OPENSPACE_BASE_DIR}/src/util/blockplaneintersectiongeometry.cpp ${OPENSPACE_BASE_DIR}/src/util/boxgeometry.cpp ${OPENSPACE_BASE_DIR}/src/util/camera.cpp diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 4a96a5a2f3..2f20708ab0 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -867,11 +867,10 @@ void OpenSpaceEngine::preSynchronization() { _timeManager->preSynchronization(dt); auto scheduledScripts = _scriptScheduler->progressTo(Time::ref().j2000Seconds()); - while(scheduledScripts.size()){ - auto scheduledScript = scheduledScripts.front(); - LINFO(scheduledScript); - _scriptEngine->queueScript(scheduledScript, ScriptEngine::RemoteScripting::Yes); - scheduledScripts.pop(); + for (auto it = scheduledScripts.first; it != scheduledScripts.second; ++it) { + _scriptEngine->queueScript( + *it, ScriptEngine::RemoteScripting::Yes + ); } _interactionHandler->updateInputStates(dt); diff --git a/src/scripting/scriptscheduler.cpp b/src/scripting/scriptscheduler.cpp index a49f68dc03..05eb6fb76e 100644 --- a/src/scripting/scriptscheduler.cpp +++ b/src/scripting/scriptscheduler.cpp @@ -27,74 +27,154 @@ #include #include -#include // parse time +#include #include #include - -namespace openspace { -namespace scripting { +#include namespace { - const std::string _loggerCat = "ScriptScheduler"; - - const std::string KEY_TIME = "Time"; - const std::string KEY_FORWARD_SCRIPT = "ReversibleLuaScript.Forward"; - const std::string KEY_BACKWARD_SCRIPT = "ReversibleLuaScript.Backward"; + const char* KeyTime = "Time"; + const char* KeyForwardScript = "ForwardScript"; + const char* KeyBackwardScript = "BackwardScript"; + const char* KeyUniversalScript = "Script"; } +namespace openspace { -ScheduledScript::ScheduledScript(const ghoul::Dictionary& dict) - : ScheduledScript() // default init first +#include "scriptscheduler_lua.inl" + +namespace scripting { + +openspace::Documentation ScriptScheduler::Documentation() { + using namespace openspace::documentation; + + using TimeVerifier = StringVerifier; + using LuaScriptVerifier = StringVerifier; + + return{ + "Scheduled Scripts", + "core_scheduledscript", + { + { + "*", + new TableVerifier({ + { + KeyTime, + new TimeVerifier, + "The time at which, when the in game time passes it, the two " + "scripts will be executed. If the traversal is forwards (towards " + "+ infinity), the ForwardScript will be executed, otherwise the " + "BackwardScript will be executed instead.", + Optional::No + }, + { + KeyUniversalScript, + new LuaScriptVerifier, + "The Lua script that will be executed when the specified time is " + "passed independent of its direction. This script will be " + "executed before the specific scripts if both versions are " + "specified", + Optional::Yes + }, + { + KeyForwardScript, + new LuaScriptVerifier, + "The Lua script that is executed when OpenSpace passes the time " + "in a forward direction.", + Optional::Yes + }, + { + KeyBackwardScript, + new LuaScriptVerifier, + "The Lua script that is executed when OpenSpace passes the time " + "in a backward direction.", + Optional::Yes + } + }) + } + }, + Exhaustive::Yes + }; +} + +ScriptScheduler::ScheduledScript::ScheduledScript(const ghoul::Dictionary& dictionary) + : time(-std::numeric_limits::max()) { - std::string timeStr; - if (dict.getValue(KEY_TIME, timeStr)) { - time = SpiceManager::ref().ephemerisTimeFromDate(timeStr); + std::string timeStr = dictionary.value(KeyTime); + time = Time::ref().convertTime(timeStr); + + // If a universal script is specified, retrieve it and add a ; as a separator so that + // it can be added to the other scripts + std::string universal; + dictionary.getValue(KeyUniversalScript, universal); + if (!universal.empty()) { + universal += ";"; + } + + if (dictionary.hasKeyAndValue(KeyForwardScript)) { + forwardScript = + universal + dictionary.value(KeyForwardScript); + } + + if (dictionary.hasKeyAndValue(KeyBackwardScript)) { + backwardScript = + universal + dictionary.value(KeyBackwardScript); + } +} - if (!dict.getValue(KEY_FORWARD_SCRIPT, script.forwardScript)) { - LERROR("Unable to read " << KEY_FORWARD_SCRIPT); - } - if (!dict.getValue(KEY_BACKWARD_SCRIPT, script.backwardScript)) { - LERROR("Unable to read " << KEY_BACKWARD_SCRIPT); +void ScriptScheduler::loadScripts(const ghoul::Dictionary& dictionary) { + // Check if all of the scheduled scripts are formed correctly + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "ScriptScheduler" + ); + + // Create all the scheduled script first + std::vector scheduledScripts; + for (size_t i = 1; i <= dictionary.size(); ++i) { + const ghoul::Dictionary& timedScriptDict = dictionary.value( + std::to_string(i) + ); + scheduledScripts.emplace_back(timedScriptDict); + } + + // Sort scripts by time; use a stable_sort as the user might have had an intention + // specifying multiple scripts for the same time in a specific order + std::stable_sort( + scheduledScripts.begin(), + scheduledScripts.end(), + [](const ScheduledScript& lhs, const ScheduledScript& rhs) { + return lhs.time < rhs.time; } + ); + + // Move the scheduled scripts into their SOA alignment + // For the forward scripts, this is the forwards direction + // For the backward scripts, we insert them in the opposite order so that we can still + // return forward iterators to them in the progressTo method + for (ScheduledScript& script : scheduledScripts) { + _timings.push_back(script.time); + + _forwardScripts.push_back(std::move(script.forwardScript)); + + _backwardScripts.insert( + _backwardScripts.begin(), + std::move(script.backwardScript) + ); } - else { - LERROR("Unable to read " << KEY_TIME); - } -} - -bool ScheduledScript::CompareByTime(const ScheduledScript& s1, const ScheduledScript& s2){ - return s1.time < s2.time; -} - - - -void ScriptScheduler::loadScripts(const std::string& filepath, lua_State* L) { - ghoul::Dictionary timedScriptsDict; - try { - ghoul::lua::loadDictionaryFromFile(absPath(filepath), timedScriptsDict, L); - } - catch (const ghoul::RuntimeError& e) { - LERROR(e.what()); - return; - } - loadScripts(timedScriptsDict); -} - -void ScriptScheduler::loadScripts(const ghoul::Dictionary& dict) { - for (size_t i = 0; i < dict.size(); ++i) { - std::string id = std::to_string(i + 1); - const ghoul::Dictionary& timedScriptDict = dict.value(id); - _scheduledScripts.push_back(ScheduledScript(timedScriptDict)); - } - - // Sort scripts by time - std::stable_sort(_scheduledScripts.begin(), _scheduledScripts.end(), &ScheduledScript::CompareByTime); // Ensure _currentIndex and _currentTime is accurate after new scripts was added double lastTime = _currentTime; rewind(); progressTo(lastTime); + + ghoul_assert( + (_timings.size() == _forwardScripts.size()) && + (_timings.size() == _backwardScripts.size()), + "The SOA data structure has been mistreated and has different number of values" + ); } void ScriptScheduler::rewind() { @@ -102,91 +182,113 @@ void ScriptScheduler::rewind() { _currentTime = -DBL_MAX; } - void ScriptScheduler::clearSchedule() { rewind(); - _scheduledScripts.clear(); + _timings.clear(); + _forwardScripts.clear(); + _backwardScripts.clear(); } -std::queue ScriptScheduler::progressTo(double newTime) { - std::queue triggeredScripts; +std::pair< + std::vector::const_iterator, std::vector::const_iterator +> +ScriptScheduler::progressTo(double newTime) +{ + if (newTime == _currentTime) { + return { _forwardScripts.end(), _forwardScripts.end() }; + } + if (newTime > _currentTime) { - while(_currentIndex < _scheduledScripts.size() && _scheduledScripts[_currentIndex].time <= newTime){ - triggeredScripts.push(_scheduledScripts[_currentIndex].script.forwardScript); - _currentIndex++; - } + // Moving forward in time; we need to find the highest entry in the timings + // vector that is still smaller than the newTime + size_t prevIndex = _currentIndex; + auto it = std::upper_bound( + _timings.begin() + prevIndex, // We only need to start at the previous time + _timings.end(), + newTime + ); + + // How many values did we pass over? + int n = std::distance(_timings.begin() + prevIndex, it); + _currentIndex = prevIndex + n; + + // Update the new time + _currentTime = newTime; + + return { + _forwardScripts.begin() + prevIndex, + _forwardScripts.begin() + _currentIndex + }; } else { - while (0 < _currentIndex && _scheduledScripts[_currentIndex - 1].time > newTime) { - triggeredScripts.push(_scheduledScripts[_currentIndex - 1].script.backwardScript); - _currentIndex--; - } + // Moving backward in time; the need to find the lowest entry that is still bigger + // than the newTime + size_t prevIndex = _currentIndex; + auto it = std::lower_bound( + _timings.begin(), + _timings.begin() + prevIndex, // We can stop at the previous time + newTime + ); + + // How many values did we pass over? + int n = std::distance(it, _timings.begin() + prevIndex); + _currentIndex = prevIndex - n; + + // Update the new time + _currentTime = newTime; + + int size = _timings.size(); + return { + _backwardScripts.begin() + (size - prevIndex), + _backwardScripts.begin() + (size - _currentIndex) + }; } - - _currentTime = newTime; - return triggeredScripts; } -std::queue ScriptScheduler::progressTo(const std::string& timeStr) { - return std::move(progressTo(SpiceManager::ref().ephemerisTimeFromDate(timeStr))); -} - -double ScriptScheduler::currentTime() const { +double ScriptScheduler::currentTime() const { return _currentTime; }; -const std::vector& ScriptScheduler::allScripts() const { - return _scheduledScripts; -}; - - - -///////////////////////////////////////////////////////////////////// -// Lua library functions // -///////////////////////////////////////////////////////////////////// - -namespace luascriptfunctions { - int loadTimedScripts(lua_State* L) { - using ghoul::lua::luaTypeToString; - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - std::string missionFileName = luaL_checkstring(L, -1); - if (missionFileName.empty()) { - return luaL_error(L, "filepath string is empty"); - } +std::vector ScriptScheduler::allScripts() const { + std::vector result; + for (size_t i = 0; i < _timings.size(); ++i) { + ScheduledScript script; + script.time = _timings[i]; + script.forwardScript = _forwardScripts[i]; + script.backwardScript = _backwardScripts[i]; - OsEng.scriptScheduler().loadScripts(missionFileName, L); + result.push_back(std::move(script)); } - - int clear(lua_State* L) { - using ghoul::lua::luaTypeToString; - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - - OsEng.scriptScheduler().clearSchedule(); - } -} // namespace luascriptfunction - - + return result; +}; LuaLibrary ScriptScheduler::luaLibrary() { return { "scriptScheduler", { { - "load", - &luascriptfunctions::loadTimedScripts, + "loadFile", + &luascriptfunctions::loadFile, "string", - "Load timed scripts from file" + "Load timed scripts from a Lua script file that returns a list of " + "scheduled scripts." + }, + { + "loadScheduledScript", + &luascriptfunctions::loadScheduledScript, + "string, string, (string, string)", + "Load a single scheduled script. The first argument is the time at which " + "the scheduled script is triggered, the second argument is the script " + "that is executed in the forward direction, the optional third argument " + "is the script executed in the backwards direction, and the optional " + "last argument is the universal script, executed in either direction." + }, { "clear", &luascriptfunctions::clear, "", - "clears all scheduled scripts" + "Clears all scheduled scripts." }, } }; diff --git a/src/scripting/scriptscheduler_lua.inl b/src/scripting/scriptscheduler_lua.inl new file mode 100644 index 0000000000..a041e8a172 --- /dev/null +++ b/src/scripting/scriptscheduler_lua.inl @@ -0,0 +1,101 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +namespace luascriptfunctions { + +int loadFile(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) { + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + } + + std::string missionFileName = luaL_checkstring(L, -1); + if (missionFileName.empty()) { + return luaL_error(L, "filepath string is empty"); + } + + OsEng.scriptScheduler().loadScripts( + ghoul::lua::loadDictionaryFromFile(missionFileName, L) + ); + + return 0; +} + +int loadScheduledScript(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments == 2) { + OsEng.scriptScheduler().loadScripts({ + { + "1", + ghoul::Dictionary { + { KeyTime, std::string(luaL_checkstring(L, -2)) }, + { KeyForwardScript, std::string(luaL_checkstring(L, -1)) } + } + } + }); + } + else if (nArguments == 3) { + OsEng.scriptScheduler().loadScripts({ + { + "1", + ghoul::Dictionary { + { KeyTime, std::string(luaL_checkstring(L, -3)) }, + { KeyForwardScript, std::string(luaL_checkstring(L, -2)) }, + { KeyBackwardScript, std::string(luaL_checkstring(L, -1)) } + } + } + }); + } + else if (nArguments == 4) { + OsEng.scriptScheduler().loadScripts({ + { + "1", + ghoul::Dictionary { + { KeyTime, std::string(luaL_checkstring(L, -4)) }, + { KeyForwardScript, std::string(luaL_checkstring(L, -3)) }, + { KeyBackwardScript, std::string(luaL_checkstring(L, -2)) }, + { KeyUniversalScript, std::string(luaL_checkstring(L, -1)) } + } + } + }); + } + else { + return luaL_error(L, "Expected %i-%i arguments, got %i", 2, 4, nArguments); + } + + return 0; +} + +int clear(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) { + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + } + + OsEng.scriptScheduler().clearSchedule(); + + return 0; +} + +} // namespace luascriptfunction diff --git a/src/util/time.cpp b/src/util/time.cpp index 5f4f142323..89204ab623 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -34,12 +34,15 @@ #include #include - - - namespace openspace { Time* Time::_instance = nullptr; + + +double Time::convertTime(const std::string& time) { + ghoul_assert(!time.empty(), "timeString must not be empty"); + return SpiceManager::ref().ephemerisTimeFromDate(time); +} Time::Time(double secondsJ2000) : _time(secondsJ2000) diff --git a/tests/main.cpp b/tests/main.cpp index 354d923c9c..a0c491f33b 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -58,12 +58,16 @@ //#include #endif +#include + #include #include #include #include + #include +#include #include #include @@ -88,7 +92,7 @@ int main(int argc, char** argv) { testing::internal::CaptureStdout(); testing::internal::CaptureStderr(); #endif - + openspace::SpiceManager::deinitialize(); bool b = RUN_ALL_TESTS(); diff --git a/tests/test_scriptscheduler.inl b/tests/test_scriptscheduler.inl new file mode 100644 index 0000000000..1dee4d8cc8 --- /dev/null +++ b/tests/test_scriptscheduler.inl @@ -0,0 +1,806 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "gtest/gtest.h" + +#include + +// This include should be removed after the time class is not dependent on +// Spice anymore +#include +#include +#include + +#include + +class ScriptSchedulerTest : public testing::Test { +protected: + void SetUp() override { + openspace::SpiceManager::initialize(); + openspace::SpiceManager::ref().loadKernel( + "${TESTDIR}/SpiceTest/spicekernels/naif0008.tls" + ); + } + + void TearDown() override { + openspace::SpiceManager::deinitialize(); + } +}; + +TEST_F(ScriptSchedulerTest, SimpleForward) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 02")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03") + ); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, MultipleForwardSingleJump) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + + }; + + ScriptScheduler scheduler; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary1 }, + { "2", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 02")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 06")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript2", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, MultipleForwardOrdering) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + ScriptScheduler scheduler; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary1 }, + { "2", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 02")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 06")); + ASSERT_EQ(2, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + EXPECT_EQ("ForwardScript2", *(std::next(res.first))); +} + +TEST_F(ScriptSchedulerTest, SimpleBackward) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 05")); + scheduler.loadScripts({ + { "1", testDictionary } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 02")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript1", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, MultipleBackwardSingleJump) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 07")); + scheduler.loadScripts({ + { "1", testDictionary1 }, + { "2", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 06")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript2", *(res.first)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript1", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, MultipleBackwardOrdering) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 07")); + scheduler.loadScripts({ + { "1", testDictionary1 }, + { "2", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 06")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + ASSERT_EQ(2, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript2", *(res.first)); + EXPECT_EQ("BackwardScript1", *(std::next(res.first))); +} + +TEST_F(ScriptSchedulerTest, Empty) { + using namespace openspace::scripting; + + static const std::vector TestTimes = { + 0.0, 1.0, -1.0, std::numeric_limits::min(), + -std::numeric_limits::max(), std::numeric_limits::max() + }; + + // First test if a new ScriptScheduler will return an empty list + for (double t : TestTimes) { + ScriptScheduler scheduler; + auto res = scheduler.progressTo(t); + EXPECT_EQ(res.first, res.second); + } + + // Then test the same thing but keeping the same ScriptScheduler + ScriptScheduler scheduler; + for (double t : TestTimes) { + auto res = scheduler.progressTo(t); + EXPECT_EQ(res.first, res.second); + } +} + +TEST_F(ScriptSchedulerTest, ForwardBackwards) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary1 }, + { "2", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript1", *(res.first)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 07")); + ASSERT_EQ(2, std::distance(res.first, res.second)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript2", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, Rewind) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary1 }, + { "2", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 07")); + ASSERT_EQ(2, std::distance(res.first, res.second)); + + scheduler.rewind(); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, CurrentTime) { + using namespace openspace::scripting; + + static const std::vector TestValues = { + 0.0, 1.0, 42.0, std::numeric_limits::min(), + -std::numeric_limits::max(), std::numeric_limits::max() + }; + + for (double t : TestValues) { + ScriptScheduler scheduler; + scheduler.progressTo(t); + EXPECT_EQ(t, scheduler.currentTime()); + } +} + +TEST_F(ScriptSchedulerTest, AllScripts) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + ghoul::Dictionary testDictionary3 = { + { "Time", "2000 JAN 10"s }, + { "ForwardScript", "ForwardScript3"s }, + { "BackwardScript", "BackwardScript3"s } + }; + + scheduler.loadScripts({ + { "1", testDictionary1 }, + { "2", testDictionary2 }, + { "3", testDictionary3 } + }); + + auto allScripts = scheduler.allScripts(); + ASSERT_EQ(3, allScripts.size()); + + EXPECT_LE(allScripts[0].time, allScripts[1].time); + EXPECT_LE(allScripts[1].time, allScripts[2].time); +} + +TEST_F(ScriptSchedulerTest, JumpEqual) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03 12:00:00"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + + auto res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 11:00:00") + ); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 12:00:00") + ); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + + res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 12:01:00") + ); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 12:00:00") + ); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript1", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, SameTime) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03 12:00:00"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + + auto res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 12:00:00") + ); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + + res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 12:00:00") + ); + ASSERT_EQ(res.first, res.second); +} + +TEST_F(ScriptSchedulerTest, MultiInnerJump) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03 12:00:00"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + + auto res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 10:00:00") + ); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 11:00:00") + ); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 13:00:00") + ); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + + res = scheduler.progressTo( + openspace::Time::ref().convertTime("2000 JAN 03 12:30:00") + ); + ASSERT_EQ(res.first, res.second); +} + +TEST_F(ScriptSchedulerTest, MultipleForwardSingleJumpMultipleLoad) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + + }; + + ScriptScheduler scheduler; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + + scheduler.loadScripts({ + { "1", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 02")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 06")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript2", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, MultipleForwardOrderingMultipleLoad) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + ScriptScheduler scheduler; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + scheduler.loadScripts({ + { "1", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 02")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 06")); + ASSERT_EQ(2, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + EXPECT_EQ("ForwardScript2", *(std::next(res.first))); +} + +TEST_F(ScriptSchedulerTest, MultipleBackwardSingleJumpMultipleLoad) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 07")); + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + scheduler.loadScripts({ + { "1", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 06")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript2", *(res.first)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript1", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, MultipleBackwardOrderingMultipleLoad) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 07")); + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + scheduler.loadScripts({ + { "1", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 06")); + ASSERT_EQ(res.first, res.second); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + ASSERT_EQ(2, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript2", *(res.first)); + EXPECT_EQ("BackwardScript1", *(std::next(res.first))); +} + +TEST_F(ScriptSchedulerTest, ForwardBackwardsMultipleLoad) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + scheduler.loadScripts({ + { "1", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript1", *(res.first)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 07")); + ASSERT_EQ(2, std::distance(res.first, res.second)); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("BackwardScript2", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, RewindMultipleLoad) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 01")); + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + scheduler.loadScripts({ + { "1", testDictionary2 } + }); + + auto res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 07")); + ASSERT_EQ(2, std::distance(res.first, res.second)); + + scheduler.rewind(); + + res = scheduler.progressTo(openspace::Time::ref().convertTime("2000 JAN 04")); + ASSERT_EQ(1, std::distance(res.first, res.second)); + EXPECT_EQ("ForwardScript1", *(res.first)); +} + +TEST_F(ScriptSchedulerTest, AllScriptsMultipleLoad) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + ghoul::Dictionary testDictionary3 = { + { "Time", "2000 JAN 10"s }, + { "ForwardScript", "ForwardScript3"s }, + { "BackwardScript", "BackwardScript3"s } + }; + + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + + scheduler.loadScripts({ + { "1", testDictionary2 } + }); + + scheduler.loadScripts({ + { "1", testDictionary3 } + }); + + auto allScripts = scheduler.allScripts(); + ASSERT_EQ(3, allScripts.size()); + + EXPECT_LE(allScripts[0].time, allScripts[1].time); + EXPECT_LE(allScripts[1].time, allScripts[2].time); +} + +TEST_F(ScriptSchedulerTest, AllScriptsMixedLoad) { + using namespace openspace::scripting; + using namespace std::string_literals; + + ScriptScheduler scheduler; + + + ghoul::Dictionary testDictionary1 = { + { "Time", "2000 JAN 03"s }, + { "ForwardScript", "ForwardScript1"s }, + { "BackwardScript", "BackwardScript1"s } + }; + + ghoul::Dictionary testDictionary2 = { + { "Time", "2000 JAN 05"s }, + { "ForwardScript", "ForwardScript2"s }, + { "BackwardScript", "BackwardScript2"s } + }; + + ghoul::Dictionary testDictionary3 = { + { "Time", "2000 JAN 10"s }, + { "ForwardScript", "ForwardScript3"s }, + { "BackwardScript", "BackwardScript3"s } + }; + + scheduler.loadScripts({ + { "1", testDictionary1 } + }); + + scheduler.loadScripts({ + { "1", testDictionary2 }, + { "2", testDictionary3 } + }); + + auto allScripts = scheduler.allScripts(); + ASSERT_EQ(3, allScripts.size()); + + EXPECT_LE(allScripts[0].time, allScripts[1].time); + EXPECT_LE(allScripts[1].time, allScripts[2].time); +}