summaryrefslogtreecommitdiffhomepage
path: root/test/save.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/save.cpp')
-rw-r--r--test/save.cpp174
1 files changed, 174 insertions, 0 deletions
diff --git a/test/save.cpp b/test/save.cpp
new file mode 100644
index 00000000..a2c7f48a
--- /dev/null
+++ b/test/save.cpp
@@ -0,0 +1,174 @@
+#include "app.hpp"
+#include "src/world.hpp"
+#include "loader/loader.hpp"
+#include "src/scenery.hpp"
+#include "src/critter.hpp"
+#include "src/light.hpp"
+#include "src/ground-atlas.hpp"
+#include "src/anim-atlas.hpp"
+#include "src/tile-iterator.hpp"
+#include "src/nanosecond.inl"
+#include <Corrade/Utility/Path.h>
+
+namespace floormat {
+
+namespace Path = Corrade::Utility::Path;
+
+chunk& test_app::make_test_chunk(world& w, chunk_coords_ ch)
+{
+ chunk& c = w[ch];
+ c.mark_modified();
+
+ auto metal2 = loader.wall_atlas("empty", loader_policy::warn);
+ auto tiles = loader.ground_atlas("tiles");
+ auto door = loader.scenery("door1");
+ auto table = loader.scenery("table1");
+ auto control_panel = loader.scenery("control panel (wall) 1");
+
+ constexpr auto N = TILE_MAX_DIM;
+ for (auto [x, k, pt] : c)
+ x.ground() = { tiles, variant_t(k % tiles->num_tiles()) };
+ control_panel.r = rotation::W;
+ constexpr auto K = N/2;
+ c[{K, K }].wall_north() = { metal2, 0 };
+ c[{K, K }].wall_west() = { metal2, 0 };
+ c[{K, K+1}].wall_north() = { metal2, 0 };
+ c[{K+1, K }].wall_west() = { metal2, 0 };
+ w.make_object<scenery>(w.make_id(), {ch, {3, 4}}, table);
+ w.make_object<scenery>(w.make_id(), {ch, {K, K+1}}, control_panel);
+
+ const auto add_player = [&](StringView name, Vector2i coord, bool playable) {
+ critter_proto cproto;
+ cproto.name = name;
+ cproto.playable = playable;
+ auto& p = *w.make_object<critter>(w.make_id(), global_coords{ch, {coord.x(), coord.y()}}, cproto);
+ p.frame = (uint16_t)coord.x();
+ };
+ add_player("Player 1", {12, 11}, true); // duplicate
+ add_player("Player 1", {13, 11}, true); // duplicate
+ add_player("Player 2", {14, 11}, false);
+ add_player("Player 3", {15, 11}, true);
+
+ {
+ auto& e = *w.make_object<scenery>(w.make_id(), {ch, {K+3, K+1}}, door);
+ const auto end = e.atlas->info().nframes-1;
+ constexpr auto dt = Second / 60;
+ fm_assert(e.frame == end);
+ { auto& x = std::get<door_scenery>(e.subtype);
+ fm_assert(!x.active);
+ e.activate(e.index());
+ fm_assert(x.active);
+ { auto index = e.index(); e.update(index, dt); }
+ fm_assert(e.frame != end);
+ for (int i = 0; i < 60*3; i++)
+ { auto index = e.index(); e.update(index, dt); }
+ fm_assert(e.frame == 0);
+ fm_assert(!x.active);
+ }
+ }
+ return c;
+}
+
+namespace {
+
+void assert_chunks_equal(const chunk& a, const chunk& b)
+{
+ fm_assert(a.objects().size() == b.objects().size());
+
+ for (auto i = 0uz; i < TILE_COUNT; i++)
+ {
+ const auto &a1 = a[i], &b1 = b[i];
+ fm_assert(a1 == b1);
+ }
+
+ for (auto i = 0uz; i < a.objects().size(); i++)
+ {
+ const auto& ae = *a.objects()[i];
+ const auto& be = *b.objects()[i];
+ const auto type = ae.type();
+ fm_assert(ae.type() == be.type());
+ fm_assert(type < object_type::COUNT && type != object_type::none);
+ switch (type)
+ {
+ case object_type::none:
+ case object_type::COUNT: std::unreachable();
+ case object_type::critter: {
+ const auto& e1 = static_cast<const critter&>(ae);
+ const auto& e2 = static_cast<const critter&>(be);
+ const auto p1 = critter_proto(e1), p2 = critter_proto(e2);
+ fm_assert(p1 == p2);
+ break;
+ }
+ case object_type::scenery: {
+ const auto& e1 = static_cast<const scenery&>(ae);
+ const auto& e2 = static_cast<const scenery&>(be);
+ const auto p1 = scenery_proto(e1), p2 = scenery_proto(e2);
+ fm_assert(p1 == p2);
+ break;
+ }
+ case object_type::light: {
+ const auto& e1 = static_cast<const light&>(ae);
+ const auto& e2 = static_cast<const light&>(be);
+ const auto p1 = light_proto(e1), p2 = light_proto(e2);
+ fm_assert(p1 == p2);
+ break;
+ }
+ }
+ }
+}
+
+void test_save1(StringView input, StringView tmp)
+{
+ if (Path::exists(tmp))
+ Path::remove(tmp);
+ chunk_coords_ coord{};
+ world w;
+ if (input)
+ w = world::deserialize(input, loader_policy::ignore);
+ else
+ {
+ coord = {1, 1, 0};
+ w = world();
+ auto& c = test_app::make_test_chunk(w, coord);
+ fm_assert(!c.empty(true));
+ }
+ w.serialize(tmp);
+ auto w2 = world::deserialize(tmp, loader_policy::ignore);
+ auto& c2 = w2[coord];
+ fm_assert(!c2.empty(true));
+ assert_chunks_equal(w[coord], c2);
+}
+
+void test_offset_frac(StringView tmp)
+{
+ // todo! test non-zero offset_frac
+}
+
+} // namespace
+
+void test_app::test_save()
+{
+ fm_assert(Path::exists(Path::join(loader.TEMP_PATH, "CMakeCache.txt")));
+ const auto tmp_filename = Path::join(loader.TEMP_PATH, "test/test-serializer1.dat"_s);
+
+ test_save1({}, tmp_filename);
+ test_offset_frac(tmp_filename);
+}
+
+void test_app::test_saves()
+{
+ fm_assert(Path::exists(Path::join(loader.TEMP_PATH, "CMakeCache.txt")));
+ const auto tmp_filename = Path::join(loader.TEMP_PATH, "test/test-serializer2.dat"_s);
+ const auto dir = Path::join(loader.TEMP_PATH, "test/save/"_s);
+ using LF = Path::ListFlag;
+ auto files = Path::list(dir, LF::SkipDirectories|LF::SkipSpecial|LF::SkipDotAndDotDot);
+ fm_assert(files);
+ for (const StringView file : *files)
+ {
+ fm_assert(file.hasSuffix(".dat"_s));
+ auto path = Path::join(dir, file);
+ test_save1(path, tmp_filename);
+ }
+}
+
+} // namespace floormat