Compare commits

..

No commits in common. "ee4483bfbb1746727e44c323a4ac540b482d770d" and "1247b1ac69da2aeb0f58185b513c75a928186f52" have entirely different histories.

12 changed files with 16 additions and 344 deletions

View file

@ -21,4 +21,3 @@ add_subdirectory(./app)
# apps
add_subdirectory(./mirror)
add_subdirectory(test)

View file

@ -103,8 +103,7 @@ void Application::update_layers()
{
for (auto &it : *m_layer_stack)
{
// narrowing double -> float
it->on_update(static_cast<float>(m_timer.elapsed_time().count()));
it->on_update(m_timer.get_elapsed_time());
}
// TODO(Light): each layer should have their own "delta time"

View file

@ -1,4 +0,0 @@
add_library_module(test test.cpp entrypoint.cpp)
add_executable(test_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/test.cpp)
target_link_libraries(test_tests PRIVATE test)

View file

@ -1,128 +0,0 @@
#pragma once
#include <concepts>
#include <format>
#include <source_location>
namespace lt::test {
template<typename T>
concept Printable = requires(std::ostream &os, T t) {
{ os << t } -> std::same_as<std::ostream &>;
};
template<typename T>
concept Testable = Printable<T> && std::equality_comparable<T>;
constexpr void expect_eq(
Testable auto lhs,
Testable auto rhs,
std::source_location source_location = std::source_location::current()
)
{
if (lhs != rhs)
{
throw std::runtime_error {
std::format(
"Failed equality expectation:\n"
"\tactual: {}\n"
"\texpected: {}\n"
"\tlocation: {}:{}",
lhs,
rhs,
source_location.file_name(),
source_location.line()
),
};
}
}
constexpr void expect_ne(
Testable auto lhs,
Testable auto rhs,
std::source_location source_location = std::source_location::current()
)
{
if (lhs == rhs)
{
throw std::runtime_error {
std::format(
"Failed un-equality expectation:\n"
"\tactual: {}\n"
"\texpected: {}\n"
"\tlocation: {}:{}",
lhs,
rhs,
source_location.file_name(),
source_location.line()
),
};
}
}
constexpr void expect_true(
bool expression,
std::source_location source_location = std::source_location::current()
)
{
if (!expression)
{
throw std::runtime_error {
std::format(
"Failed true expectation:\n"
"\tactual: {}\n"
"\texpected: true\n"
"\tlocation: {}:{}",
expression,
source_location.file_name(),
source_location.line()
),
};
}
}
constexpr void expect_false(
bool expression,
std::source_location source_location = std::source_location::current()
)
{
if (expression)
{
throw std::runtime_error {
std::format(
"Failed false expectation:\n"
"\tactual: {}\n"
"\texpected: true\n"
"\tlocation: {}:{}",
expression,
source_location.file_name(),
source_location.line()
),
};
}
}
constexpr void expect_le(
Testable auto lhs,
Testable auto rhs,
std::source_location source_location = std::source_location::current()
)
{
if (lhs > rhs)
{
throw std::runtime_error {
std::format(
"Failed false expectation:\n"
"\tactual: {}\n"
"\texpected: >= {}\n"
"\tlocation: {}:{}",
lhs,
rhs,
source_location.file_name(),
source_location.line()
),
};
}
}
} // namespace lt::test

View file

@ -1,101 +0,0 @@
#pragma once
#include <concepts>
#include <test/expects.hpp>
namespace lt::test {
namespace concepts {
template<typename T>
concept printable = requires(std::ostream &os, T t) {
{ os << t } -> std::same_as<std::ostream &>;
};
template<
class T,
auto expr =
[] {
}>
concept test = requires(T test) {
{ test.name } -> printable;
{ test = expr } -> std::same_as<void>;
};
} // namespace concepts
struct Case
{
auto operator=(std::invocable auto test) -> void // NOLINT
{
std::cout << "Running... " << name;
try
{
test();
}
catch (const std::exception &exp)
{
std::cout << " --> FAIL !" << '\n';
std::cout << exp.what() << "\n\n";
return; // TODO(Light): Should we run the remaining tests after a failure?
}
std::cout << " --> SUCCESS :D" << "\n";
}
std::string_view name;
};
namespace details {
class Registry
{
public:
using Suite = void (*)();
static void register_suite(Suite suite)
{
instance().m_suites.emplace_back(suite);
}
static void run_all()
{
for (auto &test : instance().m_suites)
{
test();
}
}
private:
Registry() = default;
[[nodiscard]] static auto instance() -> Registry &
{
static auto registry = Registry {};
return registry;
}
std::vector<void (*)()> m_suites;
};
} // namespace details
struct TestSuite
{
template<class TSuite>
constexpr TestSuite(TSuite suite)
{
#ifndef LIGHT_SKIP_TESTS
details::Registry::register_suite(+suite);
#endif
}
};
using Suite = const TestSuite;
} // namespace lt::test

View file

@ -1,15 +0,0 @@
#include <test/test.hpp>
auto main() -> int32_t
try
{
using namespace ::lt::test;
using namespace ::lt::test::details;
Registry::run_all();
}
catch (const std::exception &exp)
{
std::cout << "Terminated after uncaught exception:\n"; // NOLINT
std::cout << "exception.what: " << exp.what();
}

View file

@ -1,25 +0,0 @@
#include <test/test.hpp>
lt::test::Suite meta = []() {
using lt::test::expect_eq;
lt::test::Case { "test_1" } = [] {
expect_eq(5, 5);
};
lt::test::Case { "test_2" } = [] {
expect_eq(20.0, 20.0);
};
lt::test::Case { "test_3" } = [] {
expect_eq(true, false);
};
lt::test::Case { "test_4" } = [] {
expect_eq(true, 1);
};
lt::test::Case { "test_5" } = [] {
throw std::runtime_error("Uncaught std exception!");
};
};

View file

@ -1,6 +1 @@
add_library_module(time timer.cpp)
add_executable(timer_tests ${CMAKE_CURRENT_SOURCE_DIR}/src/timer.tests.cpp)
target_include_directories(timer_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(timer_tests PRIVATE time test)

View file

@ -4,22 +4,28 @@
namespace lt {
/** Simple timer class to keep track of the elapsed time */
class Timer
{
public:
using Timepoint = std::chrono::time_point<std::chrono::steady_clock>;
using Duration = std::chrono::duration<double>;
using Clock = std::chrono::steady_clock;
Timer();
Timer(Timepoint start = Clock::now());
[[nodiscard]] auto get_elapsed_time() const -> float
{
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::steady_clock;
void reset(Timepoint start = Clock::now());
auto rep = duration_cast<milliseconds>(steady_clock::now() - m_start).count();
return static_cast<float>(rep) / 1000.f;
}
[[nodiscard]] auto elapsed_time() const -> Duration;
void reset()
{
m_start = std::chrono::steady_clock::now();
}
private:
Timepoint m_start;
std::chrono::time_point<std::chrono::steady_clock> m_start;
};
} // namespace lt

View file

@ -2,18 +2,8 @@
namespace lt {
Timer::Timer(Timepoint start): m_start(start)
Timer::Timer(): m_start(std::chrono::steady_clock::now())
{
}
void Timer::reset(Timepoint start)
{
m_start = start;
}
[[nodiscard]] auto Timer::elapsed_time() const -> Duration
{
return { std::chrono::steady_clock::now() - m_start };
}
} // namespace lt

View file

@ -1,44 +0,0 @@
#include <ranges>
#include <test/test.hpp>
#include <time/timer.hpp>
namespace lt {
using lt::test::expect_le;
lt::test::Suite raii = [] {
lt::test::Case { "default" } = [] {
auto timer = Timer {};
};
lt::test::Case { "plenty" } = [] {
for (auto i : std::views::iota(0, 101))
{
auto timer = Timer {};
}
};
lt::test::Case { "unhappy" } = [] {
};
lt::test::Case { "has sane elapsed time" } = [] {
auto elapsed_time = Timer {}.elapsed_time();
expect_le(elapsed_time, std::chrono::seconds { 1 });
};
};
lt::test::Suite reset = [] {
lt::test::Case { "non-throwing" } = [] {
auto timer = Timer {};
timer.reset();
};
lt::test::Case { "resets elapsed time" } = [] {
auto timer = Timer {};
auto elapsed_time = timer.elapsed_time();
timer.reset();
};
};
} // namespace lt