]> git.tdb.fi Git - libs/game.git/commitdiff
Add test cases for SystemScheduler
authorMikko Rasa <tdb@tdb.fi>
Tue, 18 Mar 2025 11:38:56 +0000 (13:38 +0200)
committerMikko Rasa <tdb@tdb.fi>
Tue, 18 Mar 2025 11:38:56 +0000 (13:38 +0200)
source/game/systemscheduler.h
tests/scheduler.cpp [new file with mode: 0644]

index 94c27027baad5d7e4b84de49bcfaa69c9d259476..9101f810c95fe8342985dd93139824e03a847e9f 100644 (file)
@@ -46,6 +46,8 @@ private:
        static int get_data_order(const GraphNode &, const GraphNode &);
 
 public:
+       const std::vector<GraphNode> &get_graph() const { return nodes; }
+
        void run(Time::TimeDelta);
 };
 
diff --git a/tests/scheduler.cpp b/tests/scheduler.cpp
new file mode 100644 (file)
index 0000000..7b34ebf
--- /dev/null
@@ -0,0 +1,160 @@
+#include <msp/game/component.h>
+#include <msp/game/system.h>
+#include <msp/game/systemscheduler.h>
+#include <msp/test/test.h>
+
+using namespace Msp;
+
+class SchedulerTests: public Test::RegisteredTest<SchedulerTests>
+{
+public:
+       SchedulerTests();
+
+       static const char *get_name() { return "SystemScheduler"; }
+
+private:
+       void unrelated_components();
+       void chained_update();
+       void parallel_reads();
+       void ambiguous_data_order();
+};
+
+
+namespace {
+
+struct Env
+{
+       ThreadPool threads;
+       Game::Reflection::Reflector reflector;
+       DataFile::Collection res;
+       Game::Stage stage;
+
+       Env():
+               threads(1),
+               stage("stage", threads, reflector, res)
+       { }
+};
+
+struct A: Game::Component { };
+struct B: Game::Component { };
+struct C: Game::Component { };
+
+template<typename T, Game::System::DependencyFlags F>
+struct Dep
+{
+       using Type = T;
+       static constexpr Game::System::DependencyFlags FLAGS = F;
+};
+
+template<typename... Ds>
+struct Sys: Game::System
+{
+       template<typename D, typename... Rest>
+       void apply_deps()
+       {
+               declare_dependency<typename D::Type>(D::FLAGS);
+               if constexpr(sizeof...(Rest)>0)
+                       apply_deps<Rest...>();
+       }
+
+       Sys(Game::Stage &s):
+               System(s)
+       {
+               apply_deps<Ds...>();
+       }
+
+       void tick(Time::TimeDelta) override { }
+};
+
+using enum Game::System::DependencyFlags;
+
+} // anonymous namespace
+
+
+SchedulerTests::SchedulerTests()
+{
+       add(&SchedulerTests::unrelated_components, "Unrelated components");
+       add(&SchedulerTests::chained_update, "Chained update");
+       add(&SchedulerTests::parallel_reads, "Parallel reads");
+       add(&SchedulerTests::ambiguous_data_order, "Ambiguous data order").expect_throw<Game::scheduling_error>();
+}
+
+void SchedulerTests::unrelated_components()
+{
+       Env env;
+
+       auto &sys1 = env.stage.add_system<Sys<Dep<A, READ_OLD>>>();
+       auto &sys2 = env.stage.add_system<Sys<Dep<B, READ_OLD>>>();
+
+       Game::SystemScheduler scheduler(env.reflector);
+       scheduler.add_system(sys1);
+       scheduler.add_system(sys2);
+
+       scheduler.schedule();
+       const auto &graph = scheduler.get_graph();
+
+       EXPECT_EQUAL(graph.size(), 2);
+       EXPECT_EQUAL(graph[0].prerequisites, 0);
+       EXPECT_EQUAL(graph[1].prerequisites, 0);
+}
+
+void SchedulerTests::chained_update()
+{
+       Env env;
+
+       auto &sys1 = env.stage.add_system<Sys<Dep<A, UPDATE>>>();
+       auto &sys2 = env.stage.add_system<Sys<Dep<A, CHAINED_UPDATE>>>();
+       auto &sys3 = env.stage.add_system<Sys<Dep<A, READ_FRESH>>>();
+
+       Game::SystemScheduler scheduler(env.reflector);
+       scheduler.add_system(sys1);
+       scheduler.add_system(sys2);
+       scheduler.add_system(sys3);
+
+       scheduler.schedule();
+       const auto &graph = scheduler.get_graph();
+
+       EXPECT_EQUAL(graph.size(), 3);
+       EXPECT_EQUAL(graph[0].prerequisites, 0);
+       EXPECT_EQUAL(graph[1].prerequisites, 1);
+       EXPECT_EQUAL(graph[2].prerequisites, 3);
+}
+
+void SchedulerTests::parallel_reads()
+{
+       Env env;
+
+       auto &sys1 = env.stage.add_system<Sys<Dep<A, READ_OLD>>>();
+       auto &sys2 = env.stage.add_system<Sys<Dep<A, READ_OLD>, Dep<B, UPDATE>>>();
+       auto &sys3 = env.stage.add_system<Sys<Dep<A, READ_OLD>, Dep<C, UPDATE>>>();
+       auto &sys4 = env.stage.add_system<Sys<Dep<B, READ_FRESH>, Dep<C, READ_FRESH>>>();
+
+       Game::SystemScheduler scheduler(env.reflector);
+       scheduler.add_system(sys1);
+       scheduler.add_system(sys2);
+       scheduler.add_system(sys3);
+       scheduler.add_system(sys4);
+
+       scheduler.schedule();
+       const auto &graph = scheduler.get_graph();
+
+       EXPECT_EQUAL(graph.size(), 4);
+       EXPECT_EQUAL(graph[0].prerequisites, 0);
+       EXPECT_EQUAL(graph[1].prerequisites, 0);
+       EXPECT_EQUAL(graph[2].prerequisites, 0);
+       EXPECT_EQUAL(graph[3].prerequisites, 6);
+}
+
+void SchedulerTests::ambiguous_data_order()
+{
+       Env env;
+
+       auto &sys1 = env.stage.add_system<Sys<Dep<A, UPDATE>>>();
+       auto &sys2 = env.stage.add_system<Sys<Dep<A, UPDATE>>>();
+
+       Game::SystemScheduler scheduler(env.reflector);
+       scheduler.add_system(sys1);
+       scheduler.add_system(sys2);
+
+       scheduler.schedule();
+}