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
test
INTERFACES
module.cppm
test.cppm
expects.cppm
registry.cppm
@ -39,6 +40,8 @@ add_module(
components.cppm
DEPENDENCIES
preliminary
TESTS
vec2.test.cpp
)
add_module(

View file

@ -1,48 +1,42 @@
import preliminary;
import test;
import assets.metadata;
import assets.shader;
import logger;
import logger;
import test.test;
import test.expects;
using ::lt::assets::AssetMetadata;
using ::lt::assets::Blob;
using ::lt::assets::BlobMetadata;
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 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 = [] {
std::filesystem::current_path(test_data_path);
std::filesystem::create_directories(tmp_path);
Case { "happy path won't throw" } = [] {
Case { "happy paths" } = [] {
auto shader_asset = ShaderAsset { "triangle.frag.asset" };
};
Case { "many won't freeze/throw" } = [] {
for (auto idx : std::views::iota(0u, 1'000u))
{
ignore = idx;
auto shader_asset = ShaderAsset { "triangle.frag.asset" };
}
};
Case { "unhappy path throws" } = [] {
Case { "unhappy paths" } = [] {
// non-existent file
expect_throw([] { ShaderAsset { "path" }; });
// incompatible type
expect_throw([] { ShaderAsset { "dummytext" }; });
// random stressing
// some random stressing
expect_throw([] {
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 = [] {
Case { "" } = [] {
Case { "Unpacking packed data returns the same data" } = [] {
const auto out_path = tmp_path / "shader_packing";
auto dummy_blob = lt::assets::Blob {};
for (auto idx : std::views::iota(0u, 255u))
{
dummy_blob.emplace_back(static_cast<byte>(idx));
}
constexpr auto blob_size = size_t { 255u };
auto blob = generate_blob(blob_size);
const auto expected_size = //
sizeof(AssetMetadata::type) //
@ -70,7 +70,7 @@ Suite packing = "shader_pack"_suite = [] {
+ sizeof(BlobMetadata::compression_type) //
+ sizeof(BlobMetadata::compressed_size) //
+ sizeof(BlobMetadata::uncompressed_size) //
+ dummy_blob.size();
+ blob.size();
ShaderAsset::pack(
out_path,
@ -81,7 +81,7 @@ Suite packing = "shader_pack"_suite = [] {
ShaderAsset::Metadata {
.type = ShaderAsset::Type::vertex,
},
std::move(dummy_blob)
std::move(blob)
);
auto stream = std::ifstream {
@ -104,12 +104,12 @@ Suite packing = "shader_pack"_suite = [] {
const auto &metadata = shader_asset.get_metadata();
expect_eq(metadata.type, ShaderAsset::Type::vertex);
auto blob = shader_asset.unpack(ShaderAsset::BlobTag::code);
expect_eq(blob.size(), 255u);
auto unpakced_blob = shader_asset.unpack(ShaderAsset::BlobTag::code);
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 test.test;
import test.expects;
using ::lt::ecs::EntityId;
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
{
@ -61,11 +52,14 @@ struct std::formatter<Component_B>
};
Suite raii = "raii"_suite = [] {
Case { "happy path won't throw" } = [] {
Case { "happy paths" } = [] {
ignore = Registry {};
};
Case { "many won't freeze/throw" } = [] {
Case { "unhappy paths" } = [] {
};
Case { "many" } = [] {
for (auto idx : std::views::iota(0, 100'000))
{
ignore = idx;
@ -73,9 +67,6 @@ Suite raii = "raii"_suite = [] {
}
};
Case { "unhappy path throws" } = [] {
};
Case { "post construct has correct state" } = [] {
auto registry = Registry {};
expect_eq(registry.get_entity_count(), 0);
@ -227,7 +218,7 @@ Suite each = "each"_suite = [] {
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))
{
auto entity = EntityId {};

View file

@ -51,6 +51,8 @@ public:
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)
{
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
{
ensure(
identifier < m_sparse.size(),
"Failed to ensure: identifier < m_sparse.size() [{} < {}]",
identifier,
m_sparse.size()
);
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 &[last_entity, last_component] = m_dense.back();

View file

@ -1,31 +1,28 @@
import preliminary;
import test;
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 Set = lt::ecs::SparseSet<Value_T>;
constexpr auto capacity = 100;
Suite raii = "raii"_suite = [] {
Case { "happy path won't throw" } = [] {
Case { "happy paths" } = [] {
ignore = Set {};
ignore = Set { Set::max_capacity };
};
Case { "unhappy path throws" } = [] {
Case { "unhappy paths" } = [] {
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" } = [&] {
auto set = Set { capacity };
expect_eq(set.get_size(), 0);
@ -34,7 +31,29 @@ Suite raii = "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 {};
for (auto idx : std::views::iota(0, 10'000))
{
@ -160,5 +179,10 @@ Suite clear = "clear"_suite = [] {
set.clear();
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.codes;
import std;
import test.test;
import test.expects;
import surface.events;
import memory.scope;
import memory.reference;
@ -12,22 +9,10 @@ import ecs.entity;
import ecs.registry;
import surface.system;
using ::lt::input::InputComponent;
using ::lt::input::System;
// NOLINTBEGIN
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
[[nodiscard]] auto tick_info() -> lt::app::TickInfo
{
return {
.delta_time = std::chrono::milliseconds { 16 },
@ -39,12 +24,12 @@ using test::Suite;
class Fixture
{
public:
[[nodiscard]] auto registry() -> memory::Ref<ecs::Registry>
[[nodiscard]] auto registry() -> lt::memory::Ref<lt::ecs::Registry>
{
return m_registry;
}
auto add_input_component() -> ecs::EntityId
auto add_input_component() -> lt::ecs::EntityId
{
auto entity = m_registry->create_entity();
m_registry->add<InputComponent>(entity, {});
@ -52,7 +37,7 @@ public:
return entity;
}
auto add_surface_component() -> ecs::EntityId
auto add_surface_component() -> lt::ecs::EntityId
{
auto entity = m_registry->create_entity();
m_surface_system.create_surface_component(
@ -64,17 +49,21 @@ public:
}
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 = [] {
Case { "happy path won't throw" } = [&] {
Case { "happy paths" } = [&] {
System { Fixture {}.registry() };
};
Case { "many won't freeze/throw" } = [&] {
Case { "unhappy paths" } = [] {
expect_throw([] { ignore = System { {} }; });
};
Case { "many" } = [&] {
auto fixture = Fixture {};
for (auto idx : std::views::iota(0, 10'000))
{
@ -82,10 +71,6 @@ Suite raii = "raii"_suite = "raii"_suite = [] {
ignore = System { fixture.registry() };
}
};
Case { "unhappy path throws" } = [] {
expect_throw([] { ignore = System { {} }; });
};
};
Suite system_events = "system_events"_suite = [] {
@ -122,7 +107,7 @@ Suite registry_events = "registry_events"_suite = [] {
Case { "on_destrroy<InputComponent>" } = [] {
auto fixture = Fixture {};
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_b = fixture.add_input_component();
@ -154,7 +139,7 @@ Suite tick = "tick"_suite = [] {
auto system = System { fixture.registry() };
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 = 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());
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());
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());
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());
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());
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
* 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;

View file

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

View file

@ -5,9 +5,10 @@ import math.vec2;
import math.vec3;
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
{
using Column_T = vec4_impl<T>;
@ -79,34 +80,38 @@ struct mat4_impl
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>
{
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>
{
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>
{
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>
{
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

View file

@ -2,9 +2,9 @@ export module math.vec2;
import preliminary;
namespace lt::math {
export namespace lt::math {
export template<typename T = f32>
template<typename T = f32>
requires(std::is_arithmetic_v<T>)
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

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 math.vec2;
namespace lt::math {
export namespace lt::math {
export template<typename T = f32>
template<typename T = f32>
requires(std::is_arithmetic_v<T>)
struct vec3_impl
{
@ -102,11 +102,11 @@ struct vec3_impl
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

View file

@ -4,9 +4,9 @@ import preliminary;
import math.vec2;
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>)
struct vec4_impl
{
@ -121,11 +121,11 @@ struct vec4_impl
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

View file

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

View file

@ -2,7 +2,7 @@ import renderer.frontend;
import renderer.test_utils;
Suite raii = "debugger_raii"_suite = [] {
Case { "happy path won't throw" } = [] {
Case { "happy paths" } = [] {
ignore = lt::renderer::create_debugger(
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([] {
ignore = lt::renderer::create_debugger(
lt::renderer::Api::vulkan,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,9 @@
export module renderer.test_utils;
export import preliminary;
export import logger;
export import test;
export import surface.system;
export import ecs.registry;
export import renderer.factory;
export import test.test;
export import test.expects;
export import memory.reference;
export import renderer.frontend;
export import renderer.system;
@ -15,15 +12,6 @@ export import math.vec3;
export import math.vec4;
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 {
constexpr auto api = lt::renderer::Api::vulkan;

View file

@ -1,3 +1,3 @@
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 test.test;
import time;
import test.expects;
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)
{
log::debug("Setting window position to: {}, {}", request.position.x, request.position.y);
SetWindowPos(
surface.m_native_data.window,
{},

View file

@ -1,26 +1,16 @@
import preliminary;
import test.test;
import test;
import time;
import test.expects;
import surface.system;
import surface.events;
import surface.requests;
import ecs.registry;
import memory.scope;
import memory.reference;
import logger;
import math.vec2;
import app.system;
using ::lt::surface::SurfaceComponent;
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
{
@ -87,12 +77,16 @@ private:
};
Suite raii = "raii"_suite = [] {
Case { "happy path won't throw" } = [] {
Case { "happy paths" } = [] {
auto fixture = Fixture {};
auto system = System { fixture.registry() };
};
Case { "many won't freeze/throw" } = [] {
Case { "unhappy paths" } = [] {
expect_throw([] { ignore = System { {} }; });
};
Case { "many" } = [] {
auto fixture = Fixture {};
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" } = [] {
auto fixture = Fixture {};
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_position(), position);
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.test;
import test;
import test.registry;
import preliminary;
using namespace ::lt::test;
void parse_option(std::string_view argument, Registry::Options &options)
void parse_option(std::string_view argument, lt::test::Registry::Options &options)
{
constexpr auto case_str = std::string_view { "--case=" };
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")
{
options.execution_policy = Registry::ExecutionPolicy::stats;
options.execution_policy = lt::test::Registry::ExecutionPolicy::stats;
return;
}
@ -56,7 +51,7 @@ try
{
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)
{
// 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)
{

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.test;
import test.expects;
using lt::test::Case;
using lt::test::Suite;
using lt::test::operator""_suite;
import test;
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 { "" } = [] {
};

View file

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