diff --git a/modules/surface/CMakeLists.txt b/modules/surface/CMakeLists.txt index 38803db..3bf9473 100644 --- a/modules/surface/CMakeLists.txt +++ b/modules/surface/CMakeLists.txt @@ -20,4 +20,4 @@ target_link_libraries(surface PUBLIC ) add_test_module(surface system.test.cpp) -# add_fuzz_module(surface system.fuzz.cpp) +add_fuzz_module(surface system.fuzz.cpp) diff --git a/modules/surface/private/system.test.cpp b/modules/surface/private/system.test.cpp index 0d79e38..3ea6643 100644 --- a/modules/surface/private/system.test.cpp +++ b/modules/surface/private/system.test.cpp @@ -85,203 +85,197 @@ private: Suite raii = "raii"_suite = [] { - // should trigger memory sanitizer error - // - int x; // uninitialized - int y = x + 1; // use of uninitialized value - printf("y = %d\n", y); + Case { "happy path won't throw" } = [] { + auto fixture = Fixture {}; + ignore = System { fixture.registry() }; + }; + + Case { "many won't freeze/throw" } = [] { + auto fixture = Fixture {}; + for (auto idx : std::views::iota(0, 250)) + { + ignore = System { fixture.registry() }; + } + }; + + Case { "unhappy path throws" } = [] { + expect_throw([] { ignore = System { {} }; }); + }; + + Case { "post construct has correct state" } = [] { + auto fixture = Fixture {}; + auto system = System { fixture.registry() }; + expect_eq(fixture.registry()->view().get_size(), 0); + }; + + Case { "post destruct has correct state" } = [] { + auto fixture = Fixture {}; + auto system = memory::create_scope(fixture.registry()); + + fixture.create_component(); + expect_eq(fixture.registry()->view().get_size(), 1); + + system.reset(); + expect_eq(fixture.registry()->view().get_size(), 0); + }; +}; + +Suite system_events = "system_events"_suite = [] { + Case { "on_register won't throw" } = [] { + auto fixture = Fixture {}; + auto system = System { fixture.registry() }; + + system.on_register(); + expect_eq(fixture.registry()->view().get_size(), 0); + }; + + Case { "on_unregister won't throw" } = [] { + auto fixture = Fixture {}; + auto system = System { fixture.registry() }; + + system.on_register(); + system.on_unregister(); + expect_eq(fixture.registry()->view().get_size(), 0); + }; +}; + +Suite registry_events = "registry_events"_suite = [] { + Case { "on_construct initializes component" } = [] { + auto fixture = Fixture {}; + + const auto &component = fixture.create_component(); + expect_eq(fixture.registry()->view().get_size(), 1); + fixture.check_values(*component); + }; + + Case { "unhappy on_construct throws" } = [] { + auto fixture = Fixture {}; + auto system = System { fixture.registry() }; + + expect_throw([&] { fixture.create_component({ .resolution = { width, 0 } }); }); + + expect_throw([&] { fixture.create_component({ .resolution = { 0, height } }); }); + + expect_throw([&] { + fixture.create_component( + { .title = "", .resolution = { SurfaceComponent::max_dimension + 1, height } } + ); + }); + + expect_throw([&] { + fixture.create_component( + { .title = "", .resolution = { width, SurfaceComponent::max_dimension + 1 } } + ); + }); + + auto big_str = std::string {}; + big_str.resize(SurfaceComponent::max_title_length + 1); + expect_throw([&] { + fixture.create_component({ .title = big_str, .resolution = { width, height } }); + }); + }; + + Case { "unhappy on_construct removes component" } = [] { + auto fixture = Fixture {}; + auto system = System { fixture.registry() }; + + expect_throw([&] { fixture.create_component({ .resolution = { width, 0 } }); }); + expect_eq(fixture.registry()->view().get_size(), 0); + }; + + Case { "on_destrroy cleans up component" } = [] { + auto fixture = Fixture {}; + auto system = memory::create_scope(fixture.registry()); + + const auto &component = fixture.create_component(); + expect_eq(fixture.registry()->view().get_size(), 1); + fixture.check_values(*component); + + system.reset(); + expect_eq(fixture.registry()->view().get_size(), 0); + }; +}; + +Suite tick = "tick"_suite = [] { + Case { "ticking on empty registry won't throw" } = [] { + auto fixture = Fixture {}; + System { fixture.registry() }.tick(tick_info()); + }; + + Case { "ticking on non-empty registry won't throw" } = [] { + auto fixture = Fixture {}; + auto system = System { fixture.registry() }; + + fixture.create_component(); + system.tick(tick_info()); + }; +}; + +Suite tick_handles_events = "tick_handles_events"_suite = [] { + Case { "ticking clears previous tick's events" } = [] { + auto fixture = Fixture {}; + auto system = System { fixture.registry() }; + auto &surface = **fixture.create_component(); + + // flush window-creation events + system.tick(tick_info()); + expect_eq(surface.peek_events().size(), 0); + + surface.push_event(surface::MovedEvent({}, {})); + expect_eq(surface.peek_events().size(), 1); + + surface.push_event(surface::ButtonPressedEvent({})); + expect_eq(surface.peek_events().size(), 2); + + system.tick(tick_info()); + expect_eq(surface.peek_events().size(), 0); + }; +}; + +Suite tick_handles_requests = "tick_handles_requests"_suite = [] { + Case { "ticking clears requests" } = [] { + auto fixture = Fixture {}; + auto system = System { fixture.registry() }; + auto &surface = **fixture.create_component(); + + constexpr auto title = "ABC"; + constexpr auto position = math::ivec2 { 50, 50 }; + constexpr auto resolution = math::uvec2 { 50, 50 }; + + expect_eq(surface.peek_requests().size(), 0); + + surface.push_request(surface::ModifyVisibilityRequest(true)); + expect_eq(surface.peek_requests().size(), 1); + system.tick(tick_info()); + expect_eq(surface.peek_requests().size(), 0); + + surface.push_request(surface::ModifyTitleRequest(title)); + expect_eq(surface.peek_requests().size(), 1); + + surface.push_request(surface::ModifyResolutionRequest(resolution)); + surface.push_request(surface::ModifyPositionRequest(position)); + expect_eq(surface.peek_requests().size(), 1 + 2); + + surface.push_request(surface::ModifyVisibilityRequest(false)); + surface.push_request(surface::ModifyVisibilityRequest(true)); + surface.push_request(surface::ModifyVisibilityRequest(false)); + expect_eq(surface.peek_requests().size(), 1 + 2 + 3); + + system.tick(tick_info()); + expect_eq(surface.peek_requests().size(), 0); + + expect_eq(surface.get_title(), title); + expect_eq(surface.get_position(), position); + expect_eq(surface.get_resolution(), resolution); + + log_dbg("EVENT COUNT: {}", surface.peek_events().size()); + for (const auto &event : surface.peek_events()) + { + const auto visitor = overloads { + [&](auto event) { log_dbg("event: {}", event.to_string()); }, + }; + + std::visit(visitor, event); + } + }; }; -// Case { "happy path won't throw" } = [] { -// auto fixture = Fixture {}; -// ignore = System { fixture.registry() }; -// }; -// -// Case { "many won't freeze/throw" } = [] { -// auto fixture = Fixture {}; -// for (auto idx : std::views::iota(0, 250)) -// { -// ignore = System { fixture.registry() }; -// } -// }; -// -// Case { "unhappy path throws" } = [] { -// expect_throw([] { ignore = System { {} }; }); -// }; -// -// Case { "post construct has correct state" } = [] { -// auto fixture = Fixture {}; -// auto system = System { fixture.registry() }; -// expect_eq(fixture.registry()->view().get_size(), 0); -// }; -// -// Case { "post destruct has correct state" } = [] { -// auto fixture = Fixture {}; -// auto system = memory::create_scope(fixture.registry()); -// -// fixture.create_component(); -// expect_eq(fixture.registry()->view().get_size(), 1); -// -// system.reset(); -// expect_eq(fixture.registry()->view().get_size(), 0); -// }; -// }; -// -// Suite system_events = "system_events"_suite = [] { -// Case { "on_register won't throw" } = [] { -// auto fixture = Fixture {}; -// auto system = System { fixture.registry() }; -// -// system.on_register(); -// expect_eq(fixture.registry()->view().get_size(), 0); -// }; -// -// Case { "on_unregister won't throw" } = [] { -// auto fixture = Fixture {}; -// auto system = System { fixture.registry() }; -// -// system.on_register(); -// system.on_unregister(); -// expect_eq(fixture.registry()->view().get_size(), 0); -// }; -// }; -// -// Suite registry_events = "registry_events"_suite = [] { -// Case { "on_construct initializes component" } = [] { -// auto fixture = Fixture {}; -// -// const auto &component = fixture.create_component(); -// expect_eq(fixture.registry()->view().get_size(), 1); -// fixture.check_values(*component); -// }; -// -// Case { "unhappy on_construct throws" } = [] { -// auto fixture = Fixture {}; -// auto system = System { fixture.registry() }; -// -// expect_throw([&] { fixture.create_component({ .resolution = { width, 0 } }); }); -// -// expect_throw([&] { fixture.create_component({ .resolution = { 0, height } }); }); -// -// expect_throw([&] { -// fixture.create_component( -// { .title = "", .resolution = { SurfaceComponent::max_dimension + 1, height } } -// ); -// }); -// -// expect_throw([&] { -// fixture.create_component( -// { .title = "", .resolution = { width, SurfaceComponent::max_dimension + 1 } } -// ); -// }); -// -// auto big_str = std::string {}; -// big_str.resize(SurfaceComponent::max_title_length + 1); -// expect_throw([&] { -// fixture.create_component({ .title = big_str, .resolution = { width, height } }); -// }); -// }; -// -// Case { "unhappy on_construct removes component" } = [] { -// auto fixture = Fixture {}; -// auto system = System { fixture.registry() }; -// -// expect_throw([&] { fixture.create_component({ .resolution = { width, 0 } }); }); -// expect_eq(fixture.registry()->view().get_size(), 0); -// }; -// -// Case { "on_destrroy cleans up component" } = [] { -// auto fixture = Fixture {}; -// auto system = memory::create_scope(fixture.registry()); -// -// const auto &component = fixture.create_component(); -// expect_eq(fixture.registry()->view().get_size(), 1); -// fixture.check_values(*component); -// -// system.reset(); -// expect_eq(fixture.registry()->view().get_size(), 0); -// }; -// }; -// -// Suite tick = "tick"_suite = [] { -// Case { "ticking on empty registry won't throw" } = [] { -// auto fixture = Fixture {}; -// System { fixture.registry() }.tick(tick_info()); -// }; -// -// Case { "ticking on non-empty registry won't throw" } = [] { -// auto fixture = Fixture {}; -// auto system = System { fixture.registry() }; -// -// fixture.create_component(); -// system.tick(tick_info()); -// }; -// }; -// -// Suite tick_handles_events = "tick_handles_events"_suite = [] { -// Case { "ticking clears previous tick's events" } = [] { -// auto fixture = Fixture {}; -// auto system = System { fixture.registry() }; -// auto &surface = **fixture.create_component(); -// -// // flush window-creation events -// system.tick(tick_info()); -// expect_eq(surface.peek_events().size(), 0); -// -// surface.push_event(surface::MovedEvent({}, {})); -// expect_eq(surface.peek_events().size(), 1); -// -// surface.push_event(surface::ButtonPressedEvent({})); -// expect_eq(surface.peek_events().size(), 2); -// -// system.tick(tick_info()); -// expect_eq(surface.peek_events().size(), 0); -// }; -// }; -// -// Suite tick_handles_requests = "tick_handles_requests"_suite = [] { -// Case { "ticking clears requests" } = [] { -// auto fixture = Fixture {}; -// auto system = System { fixture.registry() }; -// auto &surface = **fixture.create_component(); -// -// constexpr auto title = "ABC"; -// constexpr auto position = math::ivec2 { 50, 50 }; -// constexpr auto resolution = math::uvec2 { 50, 50 }; -// -// expect_eq(surface.peek_requests().size(), 0); -// -// surface.push_request(surface::ModifyVisibilityRequest(true)); -// expect_eq(surface.peek_requests().size(), 1); -// system.tick(tick_info()); -// expect_eq(surface.peek_requests().size(), 0); -// -// surface.push_request(surface::ModifyTitleRequest(title)); -// expect_eq(surface.peek_requests().size(), 1); -// -// surface.push_request(surface::ModifyResolutionRequest(resolution)); -// surface.push_request(surface::ModifyPositionRequest(position)); -// expect_eq(surface.peek_requests().size(), 1 + 2); -// -// surface.push_request(surface::ModifyVisibilityRequest(false)); -// surface.push_request(surface::ModifyVisibilityRequest(true)); -// surface.push_request(surface::ModifyVisibilityRequest(false)); -// expect_eq(surface.peek_requests().size(), 1 + 2 + 3); -// -// system.tick(tick_info()); -// expect_eq(surface.peek_requests().size(), 0); -// -// expect_eq(surface.get_title(), title); -// expect_eq(surface.get_position(), position); -// expect_eq(surface.get_resolution(), resolution); -// -// log_dbg("EVENT COUNT: {}", surface.peek_events().size()); -// for (const auto &event : surface.peek_events()) -// { -// const auto visitor = overloads { -// [&](auto event) { log_dbg("event: {}", event.to_string()); }, -// }; -// -// std::visit(visitor, event); -// } -// }; -// }; diff --git a/tools/ci/amd64/clang/msan.sh b/tools/ci/amd64/clang/msan.sh index 3e7f6d1..68f1112 100755 --- a/tools/ci/amd64/clang/msan.sh +++ b/tools/ci/amd64/clang/msan.sh @@ -34,7 +34,7 @@ cmake . \ -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ -lc++ \ -lc++abi" \ -&& cmake --build ./build --target='surface_tests' -j`nproc` +&& cmake --build ./build -j`nproc` export MSAN_SYMBOLIZER_PATH="$(which llvm-symbolizer)" export MSAN_OPTIONS="fast_unwind_on_malloc=0:verbosity=1:report_umrs=1"