refactor: major qol changes
Some checks reported errors
continuous-integration/drone/push Build was killed

This commit is contained in:
light7734 2026-01-20 13:22:30 +03:30
parent b372b95ede
commit abb9c1b1ec
30 changed files with 231 additions and 320 deletions

View file

@ -14,6 +14,7 @@ add_module(
NAME NAME
test test
INTERFACES INTERFACES
module.cppm
test.cppm test.cppm
expects.cppm expects.cppm
registry.cppm registry.cppm
@ -39,6 +40,8 @@ add_module(
components.cppm components.cppm
DEPENDENCIES DEPENDENCIES
preliminary preliminary
TESTS
vec2.test.cpp
) )
add_module( add_module(

View file

@ -1,48 +1,42 @@
import preliminary; import test;
import assets.metadata; import assets.metadata;
import assets.shader; import assets.shader;
import logger;
import logger;
import test.test;
import test.expects;
using ::lt::assets::AssetMetadata; using ::lt::assets::AssetMetadata;
using ::lt::assets::Blob;
using ::lt::assets::BlobMetadata; using ::lt::assets::BlobMetadata;
using ::lt::assets::ShaderAsset; using ::lt::assets::ShaderAsset;
using ::lt::test::Case;
using ::lt::test::expect_eq;
using ::lt::test::expect_throw;
using ::lt::test::expect_true;
using ::lt::test::Suite;
using ::lt::test::operator""_suite;
const auto test_data_path = std::filesystem::path { "./data/test_assets" }; const auto test_data_path = std::filesystem::path { "./data/test_assets" };
const auto tmp_path = std::filesystem::path { "/tmp/lt_assets_tests/" }; const auto tmp_path = std::filesystem::path { "/tmp/lt_assets_tests/" };
[[nodiscard]] auto generate_blob(size_t size) -> Blob
{
auto blob = Blob {};
for (auto idx : std::views::iota(0u, size))
{
blob.emplace_back(static_cast<byte>(idx));
}
return blob;
}
Suite raii = "shader_raii"_suite = [] { Suite raii = "shader_raii"_suite = [] {
std::filesystem::current_path(test_data_path); std::filesystem::current_path(test_data_path);
std::filesystem::create_directories(tmp_path); std::filesystem::create_directories(tmp_path);
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
auto shader_asset = ShaderAsset { "triangle.frag.asset" }; auto shader_asset = ShaderAsset { "triangle.frag.asset" };
}; };
Case { "many won't freeze/throw" } = [] { Case { "unhappy paths" } = [] {
for (auto idx : std::views::iota(0u, 1'000u))
{
ignore = idx;
auto shader_asset = ShaderAsset { "triangle.frag.asset" };
}
};
Case { "unhappy path throws" } = [] {
// non-existent file // non-existent file
expect_throw([] { ShaderAsset { "path" }; }); expect_throw([] { ShaderAsset { "path" }; });
// incompatible type // incompatible type
expect_throw([] { ShaderAsset { "dummytext" }; }); expect_throw([] { ShaderAsset { "dummytext" }; });
// random stressing // some random stressing
expect_throw([] { expect_throw([] {
for (auto idx : std::views::iota(0u, 1'000u)) for (auto idx : std::views::iota(0u, 1'000u))
{ {
@ -50,16 +44,22 @@ Suite raii = "shader_raii"_suite = [] {
} }
}); });
}; };
Case { "many" } = [] {
for (auto idx : std::views::iota(0u, 1'000u))
{
ignore = idx;
auto shader_asset = ShaderAsset { "triangle.frag.asset" };
}
};
}; };
Suite packing = "shader_pack"_suite = [] { Suite packing = "shader_pack"_suite = [] {
Case { "" } = [] { Case { "Unpacking packed data returns the same data" } = [] {
const auto out_path = tmp_path / "shader_packing"; const auto out_path = tmp_path / "shader_packing";
auto dummy_blob = lt::assets::Blob {}; constexpr auto blob_size = size_t { 255u };
for (auto idx : std::views::iota(0u, 255u))
{ auto blob = generate_blob(blob_size);
dummy_blob.emplace_back(static_cast<byte>(idx));
}
const auto expected_size = // const auto expected_size = //
sizeof(AssetMetadata::type) // sizeof(AssetMetadata::type) //
@ -70,7 +70,7 @@ Suite packing = "shader_pack"_suite = [] {
+ sizeof(BlobMetadata::compression_type) // + sizeof(BlobMetadata::compression_type) //
+ sizeof(BlobMetadata::compressed_size) // + sizeof(BlobMetadata::compressed_size) //
+ sizeof(BlobMetadata::uncompressed_size) // + sizeof(BlobMetadata::uncompressed_size) //
+ dummy_blob.size(); + blob.size();
ShaderAsset::pack( ShaderAsset::pack(
out_path, out_path,
@ -81,7 +81,7 @@ Suite packing = "shader_pack"_suite = [] {
ShaderAsset::Metadata { ShaderAsset::Metadata {
.type = ShaderAsset::Type::vertex, .type = ShaderAsset::Type::vertex,
}, },
std::move(dummy_blob) std::move(blob)
); );
auto stream = std::ifstream { auto stream = std::ifstream {
@ -104,12 +104,12 @@ Suite packing = "shader_pack"_suite = [] {
const auto &metadata = shader_asset.get_metadata(); const auto &metadata = shader_asset.get_metadata();
expect_eq(metadata.type, ShaderAsset::Type::vertex); expect_eq(metadata.type, ShaderAsset::Type::vertex);
auto blob = shader_asset.unpack(ShaderAsset::BlobTag::code); auto unpakced_blob = shader_asset.unpack(ShaderAsset::BlobTag::code);
expect_eq(blob.size(), 255u); expect_eq(unpakced_blob.size(), blob_size);
for (auto idx : std::views::iota(0u, 255u)) for (auto idx : std::views::iota(0u, blob_size))
{ {
expect_eq(blob[idx], static_cast<byte>(idx)); expect_eq(unpakced_blob[idx], static_cast<byte>(idx));
} }
}; };
}; };

View file

@ -1,17 +1,8 @@
import preliminary; import test;
import ecs.registry; import ecs.registry;
import test.test;
import test.expects;
using ::lt::ecs::EntityId; using ::lt::ecs::EntityId;
using ::lt::ecs::Registry; using ::lt::ecs::Registry;
using ::lt::test::Case;
using ::lt::test::expect_eq;
using ::lt::test::expect_false;
using ::lt::test::expect_true;
using ::lt::test::expect_unreachable;
using ::lt::test::Suite;
using ::lt::test::operator""_suite;
struct Component struct Component
{ {
@ -61,11 +52,14 @@ struct std::formatter<Component_B>
}; };
Suite raii = "raii"_suite = [] { Suite raii = "raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
ignore = Registry {}; ignore = Registry {};
}; };
Case { "many won't freeze/throw" } = [] { Case { "unhappy paths" } = [] {
};
Case { "many" } = [] {
for (auto idx : std::views::iota(0, 100'000)) for (auto idx : std::views::iota(0, 100'000))
{ {
ignore = idx; ignore = idx;
@ -73,9 +67,6 @@ Suite raii = "raii"_suite = [] {
} }
}; };
Case { "unhappy path throws" } = [] {
};
Case { "post construct has correct state" } = [] { Case { "post construct has correct state" } = [] {
auto registry = Registry {}; auto registry = Registry {};
expect_eq(registry.get_entity_count(), 0); expect_eq(registry.get_entity_count(), 0);
@ -227,7 +218,7 @@ Suite each = "each"_suite = [] {
component_map_a[entity] = component; component_map_a[entity] = component;
} }
auto component_map_b = std::unordered_map<lt::ecs::EntityId, Component_B> {}; auto component_map_b = std::unordered_map<EntityId, Component_B> {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
auto entity = EntityId {}; auto entity = EntityId {};

View file

@ -51,6 +51,8 @@ public:
auto insert(Identifier_T identifier, Value_T value) -> Dense_T & auto insert(Identifier_T identifier, Value_T value) -> Dense_T &
{ {
ensure(identifier < max_capacity, "SparseSet::insert: identifier < max_capacity");
if (m_sparse.size() < identifier + 1) if (m_sparse.size() < identifier + 1)
{ {
auto new_capacity = std::max(static_cast<size_t>(identifier + 1), m_sparse.size() * 2); auto new_capacity = std::max(static_cast<size_t>(identifier + 1), m_sparse.size() * 2);
@ -70,7 +72,27 @@ public:
*/ */
void remove(Identifier_T identifier) override void remove(Identifier_T identifier) override
{ {
ensure(
identifier < m_sparse.size(),
"Failed to ensure: identifier < m_sparse.size() [{} < {}]",
identifier,
m_sparse.size()
);
auto &idx = m_sparse[identifier]; auto &idx = m_sparse[identifier];
ensure(
idx != null_identifier,
"Failed to ensure: idx != null_identifier [{} != {}]",
idx,
null_identifier
);
ensure(
idx < m_dense.size(),
"Failed to ensure: idx < m_dense.size() [{} < {}]",
idx,
m_dense.size()
);
auto &[entity, component] = m_dense[idx]; auto &[entity, component] = m_dense[idx];
auto &[last_entity, last_component] = m_dense.back(); auto &[last_entity, last_component] = m_dense.back();

View file

@ -1,31 +1,28 @@
import preliminary; import test;
import ecs.sparse_set; import ecs.sparse_set;
import test.test;
import test.expects;
using ::lt::test::Case;
using ::lt::test::expect_eq;
using ::lt::test::expect_false;
using ::lt::test::expect_ne;
using ::lt::test::expect_throw;
using ::lt::test::expect_true;
using ::lt::test::Suite;
using ::lt::test::operator""_suite;
using Value_T = i32; using Value_T = i32;
using Set = lt::ecs::SparseSet<Value_T>; using Set = lt::ecs::SparseSet<Value_T>;
constexpr auto capacity = 100; constexpr auto capacity = 100;
Suite raii = "raii"_suite = [] { Suite raii = "raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
ignore = Set {}; ignore = Set {};
ignore = Set { Set::max_capacity }; ignore = Set { Set::max_capacity };
}; };
Case { "unhappy path throws" } = [] { Case { "unhappy paths" } = [] {
expect_throw([] { ignore = Set { Set::max_capacity + 1 }; }); expect_throw([] { ignore = Set { Set::max_capacity + 1 }; });
}; };
Case { "many" } = [] {
for (auto idx : std::views::iota(0, 1'000))
{
ignore = Set { static_cast<size_t>(idx) };
}
};
Case { "post construct has correct state" } = [&] { Case { "post construct has correct state" } = [&] {
auto set = Set { capacity }; auto set = Set { capacity };
expect_eq(set.get_size(), 0); expect_eq(set.get_size(), 0);
@ -34,7 +31,29 @@ Suite raii = "raii"_suite = [] {
}; };
Suite element_raii = "element_raii"_suite = [] { Suite element_raii = "element_raii"_suite = [] {
Case { "many inserts/removes won't freeze/throw" } = [] { Case { "happy paths" } = [] {
auto set = Set { capacity };
set.insert(0, {});
set.remove(0);
};
Case { "unhappy paths" } = [] {
expect_throw([] {
auto set = Set { capacity };
set.insert(Set::max_capacity + 1, {});
});
expect_throw([] {
auto set = Set { capacity };
set.insert(0, {});
set.insert(1, {});
set.insert(2, {});
set.remove(3);
});
};
Case { "many" } = [] {
auto set = Set {}; auto set = Set {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
@ -160,5 +179,10 @@ Suite clear = "clear"_suite = [] {
set.clear(); set.clear();
expect_eq(set.get_size(), 0); expect_eq(set.get_size(), 0);
for (auto idx : std::views::iota(0, 10'000))
{
expect_throw([&] { ignore = set.at(idx); });
}
}; };
}; };

View file

@ -1,9 +1,6 @@
import std; import test;
import input.system; import input.system;
import input.codes; import input.codes;
import std;
import test.test;
import test.expects;
import surface.events; import surface.events;
import memory.scope; import memory.scope;
import memory.reference; import memory.reference;
@ -12,22 +9,10 @@ import ecs.entity;
import ecs.registry; import ecs.registry;
import surface.system; import surface.system;
using ::lt::input::InputComponent;
using ::lt::input::System;
// NOLINTBEGIN [[nodiscard]] auto tick_info() -> lt::app::TickInfo
using namespace lt;
using input::InputComponent;
using input::System;
using test::Case;
using test::expect_eq;
using test::expect_false;
using test::expect_ne;
using test::expect_not_nullptr;
using test::operator""_suite;
using test::expect_throw;
using test::Suite;
// NOLINTEND
[[nodiscard]] auto tick_info() -> app::TickInfo
{ {
return { return {
.delta_time = std::chrono::milliseconds { 16 }, .delta_time = std::chrono::milliseconds { 16 },
@ -39,12 +24,12 @@ using test::Suite;
class Fixture class Fixture
{ {
public: public:
[[nodiscard]] auto registry() -> memory::Ref<ecs::Registry> [[nodiscard]] auto registry() -> lt::memory::Ref<lt::ecs::Registry>
{ {
return m_registry; return m_registry;
} }
auto add_input_component() -> ecs::EntityId auto add_input_component() -> lt::ecs::EntityId
{ {
auto entity = m_registry->create_entity(); auto entity = m_registry->create_entity();
m_registry->add<InputComponent>(entity, {}); m_registry->add<InputComponent>(entity, {});
@ -52,7 +37,7 @@ public:
return entity; return entity;
} }
auto add_surface_component() -> ecs::EntityId auto add_surface_component() -> lt::ecs::EntityId
{ {
auto entity = m_registry->create_entity(); auto entity = m_registry->create_entity();
m_surface_system.create_surface_component( m_surface_system.create_surface_component(
@ -64,17 +49,21 @@ public:
} }
private: private:
memory::Ref<ecs::Registry> m_registry = memory::create_ref<ecs::Registry>(); lt::memory::Ref<lt::ecs::Registry> m_registry = lt::memory::create_ref<lt::ecs::Registry>();
surface::System m_surface_system = surface::System { m_registry }; lt::surface::System m_surface_system = lt::surface::System { m_registry };
}; };
Suite raii = "raii"_suite = "raii"_suite = [] { Suite raii = "raii"_suite = "raii"_suite = [] {
Case { "happy path won't throw" } = [&] { Case { "happy paths" } = [&] {
System { Fixture {}.registry() }; System { Fixture {}.registry() };
}; };
Case { "many won't freeze/throw" } = [&] { Case { "unhappy paths" } = [] {
expect_throw([] { ignore = System { {} }; });
};
Case { "many" } = [&] {
auto fixture = Fixture {}; auto fixture = Fixture {};
for (auto idx : std::views::iota(0, 10'000)) for (auto idx : std::views::iota(0, 10'000))
{ {
@ -82,10 +71,6 @@ Suite raii = "raii"_suite = "raii"_suite = [] {
ignore = System { fixture.registry() }; ignore = System { fixture.registry() };
} }
}; };
Case { "unhappy path throws" } = [] {
expect_throw([] { ignore = System { {} }; });
};
}; };
Suite system_events = "system_events"_suite = [] { Suite system_events = "system_events"_suite = [] {
@ -122,7 +107,7 @@ Suite registry_events = "registry_events"_suite = [] {
Case { "on_destrroy<InputComponent>" } = [] { Case { "on_destrroy<InputComponent>" } = [] {
auto fixture = Fixture {}; auto fixture = Fixture {};
auto registry = fixture.registry(); auto registry = fixture.registry();
auto system = memory::create_scope<System>(registry); auto system = lt::memory::create_scope<System>(registry);
auto entity_a = fixture.add_input_component(); auto entity_a = fixture.add_input_component();
auto entity_b = fixture.add_input_component(); auto entity_b = fixture.add_input_component();
@ -154,7 +139,7 @@ Suite tick = "tick"_suite = [] {
auto system = System { fixture.registry() }; auto system = System { fixture.registry() };
auto surface_entity = fixture.add_surface_component(); auto surface_entity = fixture.add_surface_component();
auto &surface = registry->get<surface::SurfaceComponent>(surface_entity); auto &surface = registry->get<lt::surface::SurfaceComponent>(surface_entity);
auto input_entity = fixture.add_input_component(); auto input_entity = fixture.add_input_component();
auto &input = registry->get<InputComponent>(input_entity); auto &input = registry->get<InputComponent>(input_entity);
@ -166,24 +151,25 @@ Suite tick = "tick"_suite = [] {
} }
); );
expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive); using enum ::lt::input::InputAction::State;
expect_eq(input.get_action(action_key).state, inactive);
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive); expect_eq(input.get_action(action_key).state, inactive);
surface.push_event(surface::KeyPressedEvent(Key::a)); surface.push_event(lt::surface::KeyPressedEvent(Key::a));
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, input::InputAction::State::triggered); expect_eq(input.get_action(action_key).state, triggered);
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, input::InputAction::State::active); expect_eq(input.get_action(action_key).state, active);
system.tick(tick_info()); system.tick(tick_info());
system.tick(tick_info()); system.tick(tick_info());
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, input::InputAction::State::active); expect_eq(input.get_action(action_key).state, active);
surface.push_event(surface::KeyReleasedEvent(Key::a)); surface.push_event(lt::surface::KeyReleasedEvent(Key::a));
system.tick(tick_info()); system.tick(tick_info());
expect_eq(input.get_action(action_key).state, input::InputAction::State::inactive); expect_eq(input.get_action(action_key).state, inactive);
}; };
}; };

View file

@ -5,7 +5,7 @@
* *
* Hence, both `Surface` and `Input` needs to agree to the same input codes, while `Input` depends * Hence, both `Surface` and `Input` needs to agree to the same input codes, while `Input` depends
* on `Surface`. The simplest solution is to keep the codes in a 3rd module and make both depend on * on `Surface`. The simplest solution is to keep the codes in a 3rd module and make both depend on
* it. * it. (I did not want to give `Surface` the responsibility of defining input codes...)
*/ */
export module input.codes; export module input.codes;

View file

@ -1,11 +1,7 @@
import logger; import test;
import test.test;
using ::lt::test::Case;
using ::lt::test::Suite;
Suite suite = [] { Suite suite = [] {
Case { "no format" } = [] { Case { "formatless" } = [] {
lt::log::trace("trace"); lt::log::trace("trace");
lt::log::debug("debug"); lt::log::debug("debug");
lt::log::info("info"); lt::log::info("info");

View file

@ -5,9 +5,10 @@ import math.vec2;
import math.vec3; import math.vec3;
import math.vec4; import math.vec4;
namespace lt::math { export namespace lt::math {
export template<typename T = f32> template<typename T = f32>
requires(std::is_arithmetic_v<T>)
struct mat4_impl struct mat4_impl
{ {
using Column_T = vec4_impl<T>; using Column_T = vec4_impl<T>;
@ -79,34 +80,38 @@ struct mat4_impl
std::array<Column_T, 4u> values; std::array<Column_T, 4u> values;
}; };
export template<typename T> /** @todo(Light): Implement */
template<typename T>
[[nodiscard]] auto translate(const vec3_impl<T> &value) -> mat4_impl<T> [[nodiscard]] auto translate(const vec3_impl<T> &value) -> mat4_impl<T>
{ {
return mat4_impl<T> {}; return mat4_impl<T> {};
} }
export template<typename T> /** @todo(Light): Implement */
template<typename T>
[[nodiscard]] auto rotate(f32 value, const vec3_impl<T> &xyz) -> mat4_impl<T> [[nodiscard]] auto rotate(f32 value, const vec3_impl<T> &xyz) -> mat4_impl<T>
{ {
return mat4_impl<T> {}; return mat4_impl<T> {};
} }
export template<typename T> /** @todo(Light): Implement */
template<typename T>
[[nodiscard]] auto scale(const vec3_impl<T> &value) -> mat4_impl<T> [[nodiscard]] auto scale(const vec3_impl<T> &value) -> mat4_impl<T>
{ {
return mat4_impl<T> {}; return mat4_impl<T> {};
} }
export template<typename T> /** @todo(Light): Implement */
template<typename T>
[[nodiscard]] auto inverse(const mat4_impl<T> &value) -> mat4_impl<T> [[nodiscard]] auto inverse(const mat4_impl<T> &value) -> mat4_impl<T>
{ {
return mat4_impl<T> {}; return mat4_impl<T> {};
} }
export using mat4 = mat4_impl<f32>; using mat4 = mat4_impl<f32>;
export using imat4 = mat4_impl<i32>; using imat4 = mat4_impl<i32>;
export using umat4 = mat4_impl<u32>; using umat4 = mat4_impl<u32>;
} // namespace lt::math } // namespace lt::math

View file

@ -2,9 +2,9 @@ export module math.vec2;
import preliminary; import preliminary;
namespace lt::math { export namespace lt::math {
export template<typename T = f32> template<typename T = f32>
requires(std::is_arithmetic_v<T>) requires(std::is_arithmetic_v<T>)
struct vec2_impl struct vec2_impl
{ {
@ -88,11 +88,11 @@ struct vec2_impl
}; };
export using vec2 = vec2_impl<f32>; using vec2 = vec2_impl<f32>;
export using ivec2 = vec2_impl<i32>; using ivec2 = vec2_impl<i32>;
export using uvec2 = vec2_impl<u32>; using uvec2 = vec2_impl<u32>;
} // namespace lt::math } // namespace lt::math

View file

@ -0,0 +1,7 @@
import test;
import math.vec2;
Suite raii = "raii"_suite = [] {
Case { "happy path" } = [] {
};
};

View file

@ -3,9 +3,9 @@ export module math.vec3;
import preliminary; import preliminary;
import math.vec2; import math.vec2;
namespace lt::math { export namespace lt::math {
export template<typename T = f32> template<typename T = f32>
requires(std::is_arithmetic_v<T>) requires(std::is_arithmetic_v<T>)
struct vec3_impl struct vec3_impl
{ {
@ -102,11 +102,11 @@ struct vec3_impl
T z; T z;
}; };
export using vec3 = vec3_impl<f32>; using vec3 = vec3_impl<f32>;
export using ivec3 = vec3_impl<i32>; using ivec3 = vec3_impl<i32>;
export using uvec3 = vec3_impl<u32>; using uvec3 = vec3_impl<u32>;
} // namespace lt::math } // namespace lt::math

View file

@ -4,9 +4,9 @@ import preliminary;
import math.vec2; import math.vec2;
import math.vec3; import math.vec3;
namespace lt::math { export namespace lt::math {
export template<typename T = f32> template<typename T = f32>
requires(std::is_arithmetic_v<T>) requires(std::is_arithmetic_v<T>)
struct vec4_impl struct vec4_impl
{ {
@ -121,11 +121,11 @@ struct vec4_impl
T w; T w;
}; };
export using vec4 = vec4_impl<f32>; using vec4 = vec4_impl<f32>;
export using ivec4 = vec4_impl<i32>; using ivec4 = vec4_impl<i32>;
export using uvec4 = vec4_impl<u32>; using uvec4 = vec4_impl<u32>;
} // namespace lt::math } // namespace lt::math

View file

@ -7,12 +7,12 @@ using ::std::this_thread::sleep_for;
// TODO(Light): finish these (and many other) tests... // TODO(Light): finish these (and many other) tests...
Suite raii = "buffer_raii"_suite = [] { Suite raii = "buffer_raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
}; };
sleep_for(std::chrono::milliseconds { 500u }); sleep_for(std::chrono::milliseconds { 500u });
Case { "unhappy path throws" } = [] { Case { "unhappy paths" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
}; };
sleep_for(std::chrono::milliseconds { 500u }); sleep_for(std::chrono::milliseconds { 500u });

View file

@ -2,7 +2,7 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "debugger_raii"_suite = [] { Suite raii = "debugger_raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
ignore = lt::renderer::create_debugger( ignore = lt::renderer::create_debugger(
lt::renderer::Api::vulkan, lt::renderer::Api::vulkan,
lt::renderer::get_instance(lt::renderer::Api::vulkan), lt::renderer::get_instance(lt::renderer::Api::vulkan),
@ -14,7 +14,7 @@ Suite raii = "debugger_raii"_suite = [] {
); );
}; };
Case { "unhappy path throws" } = [] { Case { "unhappy paths" } = [] {
expect_throw([] { expect_throw([] {
ignore = lt::renderer::create_debugger( ignore = lt::renderer::create_debugger(
lt::renderer::Api::vulkan, lt::renderer::Api::vulkan,

View file

@ -2,12 +2,12 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "device_raii"_suite = [] { Suite raii = "device_raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
auto fixture = Fixture_SurfaceGpu {}; auto fixture = Fixture_SurfaceGpu {};
ignore = lt::renderer::create_device(constants::api, fixture.gpu(), fixture.surface()); ignore = lt::renderer::create_device(constants::api, fixture.gpu(), fixture.surface());
}; };
Case { "unhappy path throws" } = [] { Case { "unhappy paths" } = [] {
auto fixture = Fixture_SurfaceGpu {}; auto fixture = Fixture_SurfaceGpu {};
expect_throw([&] { expect_throw([&] {

View file

@ -2,7 +2,7 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "pass_raii"_suite = [] { Suite raii = "pass_raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
ignore = lt::renderer::create_pass( ignore = lt::renderer::create_pass(
constants::api, constants::api,
@ -17,7 +17,7 @@ Suite raii = "pass_raii"_suite = [] {
); );
}; };
Case { "unhappy path throws" } = [] { Case { "unhappy paths" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
expect_throw([&] { expect_throw([&] {
ignore = lt::renderer::create_pass( ignore = lt::renderer::create_pass(

View file

@ -2,7 +2,7 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "renderer_raii"_suite = [] { Suite raii = "renderer_raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
ignore = lt::renderer::create_renderer( ignore = lt::renderer::create_renderer(
constants::api, constants::api,
@ -18,7 +18,7 @@ Suite raii = "renderer_raii"_suite = [] {
); );
}; };
Case { "unhappy path throws" } = [] { Case { "unhappy paths" } = [] {
auto fixture = FixtureDeviceSwapchain {}; auto fixture = FixtureDeviceSwapchain {};
expect_throw([&] { expect_throw([&] {

View file

@ -2,7 +2,7 @@ import renderer.frontend;
import renderer.test_utils; import renderer.test_utils;
Suite raii = "surface"_suite = [] { Suite raii = "surface"_suite = [] {
Case { "happy path won't throw" } = [&] { Case { "happy paths" } = [&] {
auto fixture = Fixture_SurfaceSystem {}; auto fixture = Fixture_SurfaceSystem {};
const auto surface = lt::renderer::create_surface( const auto surface = lt::renderer::create_surface(
@ -16,7 +16,7 @@ Suite raii = "surface"_suite = [] {
expect_eq(y, constants::resolution.y); expect_eq(y, constants::resolution.y);
}; };
Case { "unhappy path throws" } = [&] { Case { "unhappy paths" } = [&] {
auto registry = lt::memory::create_ref<lt::ecs::Registry>(); auto registry = lt::memory::create_ref<lt::ecs::Registry>();
auto entity = lt::ecs::Entity { registry, registry->create_entity() }; auto entity = lt::ecs::Entity { registry, registry->create_entity() };
auto system = lt::surface::System(registry); auto system = lt::surface::System(registry);

View file

@ -18,7 +18,7 @@ struct RendererContext
}; };
Suite raii = "system_raii"_suite = [] { Suite raii = "system_raii"_suite = [] {
Case { "happy path has no errors" } = [] { Case { "happy paths" } = [] {
auto fixture = Fixture_RendererSystem {}; auto fixture = Fixture_RendererSystem {};
expect_false(fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::error)); expect_false(fixture.has_any_messages_of(lt::renderer::IDebugger::MessageSeverity::error));
expect_false( expect_false(
@ -26,7 +26,7 @@ Suite raii = "system_raii"_suite = [] {
); );
}; };
Case { "unhappy path throws" } = [] { Case { "unhappy paths" } = [] {
auto fixture = Fixture_SurfaceSystem {}; auto fixture = Fixture_SurfaceSystem {};
auto empty_entity = lt::ecs::Entity { fixture.registry(), auto empty_entity = lt::ecs::Entity { fixture.registry(),
fixture.registry()->create_entity() }; fixture.registry()->create_entity() };

View file

@ -1,12 +1,9 @@
export module renderer.test_utils; export module renderer.test_utils;
export import preliminary; export import test;
export import logger;
export import surface.system; export import surface.system;
export import ecs.registry; export import ecs.registry;
export import renderer.factory; export import renderer.factory;
export import test.test;
export import test.expects;
export import memory.reference; export import memory.reference;
export import renderer.frontend; export import renderer.frontend;
export import renderer.system; export import renderer.system;
@ -15,15 +12,6 @@ export import math.vec3;
export import math.vec4; export import math.vec4;
export import math.mat4; export import math.mat4;
export using ::lt::test::Case;
export using ::lt::test::expect_eq;
export using ::lt::test::expect_false;
export using ::lt::test::expect_not_nullptr;
export using ::lt::test::expect_throw;
export using ::lt::test::operator""_suite;
export using ::lt::test::expect_true;
export using ::lt::test::Suite;
export namespace constants { export namespace constants {
constexpr auto api = lt::renderer::Api::vulkan; constexpr auto api = lt::renderer::Api::vulkan;

View file

@ -1,3 +1,3 @@
add_executable(sandbox sandbox.cpp) add_executable(sandbox sandbox.cpp)
target_link_libraries(sandbox PRIVATE preliminary logger bitwise env memory time test math assets app ecs surface renderer input mirror) target_link_libraries(sandbox PRIVATE preliminary logger bitwise memory time test math assets app ecs surface renderer input mirror)

View file

@ -1,5 +1,4 @@
import preliminary; import preliminary;
import test.test;
import time; import time;
import test.expects; import test.expects;
import surface.system; import surface.system;

View file

@ -1099,6 +1099,7 @@ void System::modify_resolution(SurfaceComponent &surface, const ModifyResolution
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request) void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
{ {
log::debug("Setting window position to: {}, {}", request.position.x, request.position.y);
SetWindowPos( SetWindowPos(
surface.m_native_data.window, surface.m_native_data.window,
{}, {},

View file

@ -1,26 +1,16 @@
import preliminary; import test;
import test.test;
import time; import time;
import test.expects;
import surface.system; import surface.system;
import surface.events; import surface.events;
import surface.requests; import surface.requests;
import ecs.registry; import ecs.registry;
import memory.scope; import memory.scope;
import memory.reference; import memory.reference;
import logger;
import math.vec2; import math.vec2;
import app.system; import app.system;
using ::lt::surface::SurfaceComponent; using ::lt::surface::SurfaceComponent;
using ::lt::surface::System; using ::lt::surface::System;
using ::lt::test::Case;
using ::lt::test::expect_eq;
using ::lt::test::expect_ne;
using ::lt::test::expect_not_nullptr;
using ::lt::test::expect_throw;
using ::lt::test::Suite;
using ::lt::test::operator""_suite;
[[nodiscard]] auto tick_info() -> lt::app::TickInfo [[nodiscard]] auto tick_info() -> lt::app::TickInfo
{ {
@ -87,12 +77,16 @@ private:
}; };
Suite raii = "raii"_suite = [] { Suite raii = "raii"_suite = [] {
Case { "happy path won't throw" } = [] { Case { "happy paths" } = [] {
auto fixture = Fixture {}; auto fixture = Fixture {};
auto system = System { fixture.registry() }; auto system = System { fixture.registry() };
}; };
Case { "many won't freeze/throw" } = [] { Case { "unhappy paths" } = [] {
expect_throw([] { ignore = System { {} }; });
};
Case { "many" } = [] {
auto fixture = Fixture {}; auto fixture = Fixture {};
for (auto idx : std::views::iota(0, 250)) for (auto idx : std::views::iota(0, 250))
{ {
@ -101,10 +95,6 @@ Suite raii = "raii"_suite = [] {
} }
}; };
Case { "unhappy path throws" } = [] {
expect_throw([] { ignore = System { {} }; });
};
Case { "post construct has correct state" } = [] { Case { "post construct has correct state" } = [] {
auto fixture = Fixture {}; auto fixture = Fixture {};
auto system = System { fixture.registry() }; auto system = System { fixture.registry() };
@ -269,15 +259,5 @@ Suite tick_handles_requests = "tick_handles_requests"_suite = [] {
expect_eq(surface.get_title(), title); expect_eq(surface.get_title(), title);
expect_eq(surface.get_position(), position); expect_eq(surface.get_position(), position);
expect_eq(surface.get_resolution(), resolution); expect_eq(surface.get_resolution(), resolution);
lt::log::debug("EVENT COUNT: {}", surface.peek_events().size());
for (const auto &event : surface.peek_events())
{
const auto visitor = overloads {
[&](auto event) { lt::log::debug("event: {}", event.to_string()); },
};
std::visit(visitor, event);
}
}; };
}; };

View file

@ -1,12 +1,7 @@
import logger; import test;
import test.test;
import test.registry; import test.registry;
import preliminary; void parse_option(std::string_view argument, lt::test::Registry::Options &options)
using namespace ::lt::test;
void parse_option(std::string_view argument, Registry::Options &options)
{ {
constexpr auto case_str = std::string_view { "--case=" }; constexpr auto case_str = std::string_view { "--case=" };
constexpr auto suite_str = std::string_view { "--suite=" }; constexpr auto suite_str = std::string_view { "--suite=" };
@ -19,7 +14,7 @@ void parse_option(std::string_view argument, Registry::Options &options)
if (argument.starts_with("--mode=") && argument.substr(7ul) == "stats") if (argument.starts_with("--mode=") && argument.substr(7ul) == "stats")
{ {
options.execution_policy = Registry::ExecutionPolicy::stats; options.execution_policy = lt::test::Registry::ExecutionPolicy::stats;
return; return;
} }
@ -56,7 +51,7 @@ try
{ {
auto raw_arguments = std::span<char *>(argv, argc); auto raw_arguments = std::span<char *>(argv, argc);
auto options = Registry::Options {}; auto options = lt::test::Registry::Options {};
for (auto idx = 0; auto &raw_argument : raw_arguments) for (auto idx = 0; auto &raw_argument : raw_arguments)
{ {
// First argument is the "cwd' // First argument is the "cwd'
@ -83,7 +78,7 @@ try
} }
} }
return static_cast<i32>(Registry::run_all(options)); return static_cast<i32>(lt::test::Registry::run_all(options));
} }
catch (const std::exception &exp) catch (const std::exception &exp)
{ {

View file

@ -1,86 +0,0 @@
#include <cstring>
#include <test/test.hpp>
namespace lt::test {
class FuzzDataProvider
{
public:
FuzzDataProvider(const uint8_t *data, size_t size): m_data(data, size)
{
}
template<typename T>
requires(
std::is_trivially_constructible_v<T> //
&& std::is_trivially_copy_constructible_v<T> //
&& std::is_trivially_copy_assignable_v<T>
)
auto consume() -> std::optional<T>
{
if (m_data.size() < sizeof(T))
{
return std::nullopt;
}
T value;
std::memcpy(&value, m_data.data(), sizeof(T));
m_data = m_data.subspan(sizeof(T));
return value;
}
auto consume_string(size_t size) -> std::optional<std::string>
{
if (m_data.size() < size)
{
return std::nullopt;
}
// NOLINTNEXTLINE
auto value = std::string { (const char *)m_data.data(), size };
m_data = m_data.subspan(size);
return value;
}
auto consume_remaining_as_string() -> std::string
{
if (m_data.empty())
{
return std::string {};
}
return { m_data.begin(), m_data.end() };
};
private:
std::span<const uint8_t> m_data;
};
} // namespace lt::test
namespace lt::test {
auto process_fuzz_input(const uint8_t *data, size_t size) -> int32_t
try
{
return details::Registry::process_fuzz_input(data, size);
}
catch (const std::exception &exp)
{
std::println("Fuzz input resulted in uncaught exception:");
std::println("\twhat: {}", exp.what());
std::println("\tinput size: {}", size);
return EXIT_FAILURE;
}
}; // namespace lt::test
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
return lt::test::process_fuzz_input(data, size);
}

23
modules/test/module.cppm Normal file
View file

@ -0,0 +1,23 @@
export module test;
export import preliminary;
export import test.test;
export import test.expects;
export import test.expects;
export import logger;
export using ::lt::test::Suite;
export using ::lt::test::Case;
export using ::lt::test::expect_eq;
export using ::lt::test::expect_ne;
export using ::lt::test::expect_le;
export using ::lt::test::expect_true;
export using ::lt::test::expect_false;
export using ::lt::test::expect_throw;
export using ::lt::test::expect_not_nullptr;
export using ::lt::test::expect_unreachable;
export using ::lt::test::operator""_suite;

View file

@ -1,20 +1,6 @@
import preliminary; import test;
import test.test;
import test.expects;
using lt::test::Case;
using lt::test::Suite;
using lt::test::operator""_suite;
Suite expects = "expects"_suite = []() { Suite expects = "expects"_suite = []() {
using lt::test::expect_unreachable;
using lt::test::expect_true;
using lt::test::expect_false;
using lt::test::expect_eq;
using lt::test::expect_ne;
using lt::test::expect_le;
using lt::test::expect_throw;
Case { "" } = [] { Case { "" } = [] {
}; };

View file

@ -1,31 +1,22 @@
import preliminary; import test;
import time; import time;
import test.test;
import test.expects;
using ::lt::test::Case;
using ::lt::test::expect_le;
using ::lt::test::operator""_suite;
using ::lt::test::Suite;
using ::lt::time::Timer; using ::lt::time::Timer;
// error margin is high since run-time may slow down extremely due to /* @note: error margin is high since run-time may slow down extremely due to
// sanitization/debugging or execution through valgrind... * sanitization/debugging or execution through valgrind...
// * <1us error margin is tested manually in release builds and it works fine.
// <1us error margin is tested manually in release builds and it works fine. **/
constexpr auto max_error_margin = std::chrono::milliseconds { 1 }; constexpr auto max_error_margin = std::chrono::milliseconds { 1 };
Suite raii = "raii"_suite = [] { Suite raii = "raii"_suite = [] {
using std::chrono::microseconds; using std::chrono::microseconds;
Case { "default" } = [] { Case { "happy paths" } = [] {
Timer {}; Timer {};
}; };
Case { "unhappy path throws" } = [] { Case { "many" } = [] {
};
Case { "plenty" } = [] {
for (auto idx : std::views::iota(0, 100'001)) for (auto idx : std::views::iota(0, 100'001))
{ {
ignore = idx; ignore = idx;
@ -38,7 +29,7 @@ Suite reset_and_elapsed_time = "reset_and_elapsed_time"_suite = [] {
using std::chrono::hours; using std::chrono::hours;
using std::chrono::microseconds; using std::chrono::microseconds;
Case { "won't throw" } = [] { Case { "happy path" } = [] {
Timer {}.reset(); Timer {}.reset();
ignore = Timer {}.elapsed_time(); ignore = Timer {}.elapsed_time();
}; };