#include #include #include #include #include #include "slic3r/GUI/Lazy.hpp" using Slic3r::GUI::Lazy; using Slic3r::GUI::LazyBase; using Slic3r::GUI::LazyInstance; using Slic3r::GUI::StagedBuild; namespace { struct Plain { int value{ 1 }; }; struct One : LazyInstance { int value{ 2 }; }; // Two steps after the constructor. struct Staged : StagedBuild, LazyInstance { std::vector ran; Staged() { add_build_step([this] { ran.push_back(1); }); add_build_step([this] { ran.push_back(2); }); } void add_step(std::function step) { add_build_step(std::move(step)); } }; // Owns what the factories make, since a Lazy does not. template struct Made { std::vector> objects; T* make() { objects.push_back(std::make_unique()); return objects.back().get(); } typename Lazy::Factory factory() { return [this] { return make(); }; } }; } // namespace TEST_CASE("The factory runs on the first unit, not at construction", "[Lazy]") { Made made; Lazy lazy("plain", 0, made.factory()); REQUIRE(made.objects.empty()); REQUIRE_FALSE(lazy.built()); REQUIRE(lazy.pending()); REQUIRE(lazy.get() == nullptr); REQUIRE_FALSE(lazy.build_step()); // the only unit REQUIRE(made.objects.size() == 1); REQUIRE(lazy.built()); REQUIRE_FALSE(lazy.pending()); REQUIRE(lazy.get() == made.objects[0].get()); REQUIRE_FALSE(lazy.build_step()); REQUIRE(made.objects.size() == 1); } TEST_CASE("A staged type takes one unit for the constructor and one per step", "[Lazy]") { Made made; Lazy lazy("staged", 0, made.factory()); REQUIRE(lazy.build_step()); REQUIRE(made.objects.size() == 1); REQUIRE(lazy.get() == nullptr); // exists but incomplete REQUIRE(lazy.build_step()); REQUIRE(made.objects[0]->ran == std::vector{1}); REQUIRE_FALSE(lazy.build_step()); REQUIRE(made.objects[0]->ran == std::vector{1, 2}); REQUIRE(lazy.get() == made.objects[0].get()); } TEST_CASE("ensure builds whatever is left and is a no-op afterwards", "[Lazy]") { Made made; Lazy lazy("staged", 0, made.factory()); lazy.build_step(); Staged* s = lazy.ensure(); REQUIRE(s == made.objects[0].get()); REQUIRE(s->ran == std::vector{1, 2}); REQUIRE(lazy.ensure() == s); REQUIRE(made.objects.size() == 1); } TEST_CASE("when_built waits for completion, then runs at once", "[Lazy]") { Made made; Lazy lazy("staged", 0, made.factory()); std::vector seen; lazy.when_built([&](Staged& s) { seen.push_back(int(s.ran.size())); }); lazy.build_step(); lazy.build_step(); REQUIRE(seen.empty()); lazy.build_step(); REQUIRE(seen == std::vector{2}); lazy.when_built([&](Staged&) { seen.push_back(9); }); REQUIRE(seen == std::vector{2, 9}); } TEST_CASE("A LazyInstance type reaches its holder through the statics", "[Lazy]") { REQUIRE(One::if_built() == nullptr); REQUIRE(One::ensure() == nullptr); Made made; { Lazy lazy("one", 0, made.factory()); REQUIRE(One::if_built() == nullptr); One* one = One::ensure(); REQUIRE(one == made.objects[0].get()); REQUIRE(One::if_built() == one); int seen = 0; One::when_built([&](One& o) { seen = o.value; }); REQUIRE(seen == 2); } REQUIRE(One::if_built() == nullptr); } TEST_CASE("A newer holder replaces the registration; the older one leaves it alone", "[Lazy]") { Made made; auto first = std::make_unique>("first", 0, made.factory()); first->ensure(); Lazy second("second", 0, made.factory()); REQUIRE(One::if_built() == nullptr); // the new holder has not built yet second.ensure(); REQUIRE(One::if_built() == made.objects[1].get()); first.reset(); REQUIRE(One::if_built() == made.objects[1].get()); } TEST_CASE("The holder reports the name and order it was given", "[Lazy]") { Made made; Lazy lazy("plain", 7, made.factory()); LazyBase& base = lazy; REQUIRE(base.name() == "plain"); REQUIRE(base.prebuild_order() == 7); } TEST_CASE("A unit that re-enters the holder builds nothing twice", "[Lazy]") { Made made; Lazy* self = nullptr; int nested_units = 0; Lazy lazy("plain", 0, [&] { if (self->build_step()) // as if the constructor pumped the event loop into a slice ++nested_units; return made.make(); }); self = &lazy; REQUIRE_FALSE(lazy.build_step()); REQUIRE(nested_units == 0); REQUIRE(made.objects.size() == 1); REQUIRE(lazy.built()); } TEST_CASE("A factory that returns null leaves the holder unbuilt and not pending", "[Lazy]") { int calls = 0; Lazy lazy("plain", 0, [&] { ++calls; return static_cast(nullptr); }); REQUIRE_FALSE(lazy.build_step()); REQUIRE_FALSE(lazy.built()); REQUIRE_FALSE(lazy.pending()); REQUIRE(lazy.get() == nullptr); REQUIRE_FALSE(lazy.build_step()); // not retried REQUIRE(calls == 1); } TEST_CASE("ensure returns null for a factory that returned null", "[Lazy]") { Lazy lazy("plain", 0, [] { return static_cast(nullptr); }); REQUIRE(lazy.ensure() == nullptr); REQUIRE_FALSE(lazy.built()); } TEST_CASE("A nested ensure inside the factory returns null", "[Lazy]") { Made made; Lazy* self = nullptr; Plain* nested = reinterpret_cast(1); Lazy lazy("plain", 0, [&] { nested = self->ensure(); // as if the constructor pumped the event loop into a caller return made.make(); }); self = &lazy; Plain* built = lazy.ensure(); REQUIRE(built == made.objects[0].get()); REQUIRE(nested == nullptr); } TEST_CASE("A nested ensure during a staged step returns null", "[Lazy]") { Made made; Lazy* self = nullptr; Staged* nested = reinterpret_cast(1); Lazy lazy("staged", 0, [&] { Staged* s = made.make(); s->add_step([&] { nested = self->ensure(); }); // as if a step pumped the event loop into a caller return s; }); self = &lazy; Staged* built = lazy.ensure(); REQUIRE(built == made.objects[0].get()); REQUIRE(nested == nullptr); } TEST_CASE("A unit that throws leaves the holder free to build the rest", "[Lazy]") { Made made; bool thrown = false; Lazy lazy("staged", 0, [&] { Staged* s = made.make(); s->add_step([&] { thrown = true; throw std::runtime_error("step"); }); return s; }); lazy.build_step(); lazy.build_step(); lazy.build_step(); REQUIRE_THROWS(lazy.build_step()); REQUIRE(thrown); REQUIRE(lazy.pending()); REQUIRE_FALSE(lazy.build_step()); // the next unit runs REQUIRE(lazy.built()); }