feat: initialize Nocal terminal calendar
This commit is contained in:
134
tests/domain_tests.cpp
Normal file
134
tests/domain_tests.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "nocal/domain/domain.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
int failures = 0;
|
||||
|
||||
void check(bool condition, std::string_view message)
|
||||
{
|
||||
if (!condition) {
|
||||
++failures;
|
||||
std::cerr << "FAIL: " << message << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
void check_throws(Function&& function, std::string_view message)
|
||||
{
|
||||
try {
|
||||
function();
|
||||
check(false, message);
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
}
|
||||
|
||||
void test_dates()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
const nocal::Date leap{2024y / February / 29d};
|
||||
check(nocal::valid_date(leap), "leap day is valid");
|
||||
check(nocal::format_date(leap) == "2024-02-29", "date formats as ISO 8601");
|
||||
check(nocal::parse_date("2024-02-29") == leap, "date parses from ISO 8601");
|
||||
check(nocal::from_sys_days(nocal::to_sys_days(leap) + days{1}) ==
|
||||
nocal::Date{2024y / March / 1d},
|
||||
"civil date arithmetic crosses a leap month");
|
||||
check(nocal::monday_first_weekday(nocal::Date{2026y / July / 6d}) == 0,
|
||||
"Monday has index zero");
|
||||
check(nocal::monday_first_weekday(nocal::Date{2026y / July / 12d}) == 6,
|
||||
"Sunday has index six");
|
||||
check_throws([] { (void)nocal::parse_date("2023-02-29"); },
|
||||
"invalid date is rejected");
|
||||
}
|
||||
|
||||
void test_month_layout()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
const auto layout = nocal::make_month_layout(2026y / July);
|
||||
check(layout.cells.size() == 42, "month layout always has 42 cells");
|
||||
check(layout.first_visible_date() == nocal::Date{2026y / June / 29d},
|
||||
"July 2026 begins after Monday-leading spill cells");
|
||||
check(layout.last_visible_date() == nocal::Date{2026y / August / 9d},
|
||||
"six-week grid has the expected final date");
|
||||
check(layout.at(0, 0).column == 0 && layout.at(5, 6).row == 5,
|
||||
"cell coordinates match their positions");
|
||||
check(!layout.at(0, 0).in_month && layout.at(0, 2).in_month,
|
||||
"spill cells are distinguished from current-month cells");
|
||||
const auto* july_17 = layout.find(nocal::Date{2026y / July / 17d});
|
||||
check(july_17 != nullptr && july_17->row == 2 && july_17->column == 4,
|
||||
"date lookup finds the correct Friday cell");
|
||||
check_throws([&] { (void)layout.at(6, 0); }, "out-of-grid access is rejected");
|
||||
}
|
||||
|
||||
nocal::Event event(std::string uid, nocal::Date start_date, unsigned start_hour,
|
||||
nocal::Date end_date, unsigned end_hour, bool all_day = false)
|
||||
{
|
||||
nocal::Event result;
|
||||
result.uid = std::move(uid);
|
||||
result.title = result.uid;
|
||||
result.start = nocal::make_local_time(start_date, start_hour);
|
||||
result.end = nocal::make_local_time(end_date, end_hour);
|
||||
result.all_day = all_day;
|
||||
return result;
|
||||
}
|
||||
|
||||
void test_event_queries()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
const nocal::Date previous{2026y / July / 16d};
|
||||
const nocal::Date day{2026y / July / 17d};
|
||||
const nocal::Date next{2026y / July / 18d};
|
||||
|
||||
std::vector<nocal::Event> events;
|
||||
events.push_back(event("late", day, 15, day, 16));
|
||||
events.push_back(event("all-day", day, 0, next, 0, true));
|
||||
events.push_back(event("early", day, 9, day, 10));
|
||||
events.push_back(event("carry", previous, 23, day, 1));
|
||||
events.push_back(event("ends-at-boundary", previous, 22, day, 0));
|
||||
events.push_back(event("next-day", next, 0, next, 1));
|
||||
const nocal::Date august_first{2026y / August / 1d};
|
||||
events.push_back(event("next-month", august_first, 0, august_first, 1));
|
||||
|
||||
nocal::Event instant = event("instant", day, 12, day, 12);
|
||||
events.push_back(instant);
|
||||
|
||||
const auto on_day = nocal::events_on_day(events, day);
|
||||
check(on_day.size() == 5, "day query uses overlap and half-open boundaries");
|
||||
check(on_day[0].get().uid == "all-day", "all-day events sort before timed events");
|
||||
check(on_day[1].get().uid == "carry", "overlapping prior-day event is included");
|
||||
check(on_day[2].get().uid == "early" && on_day[3].get().uid == "instant" &&
|
||||
on_day[4].get().uid == "late",
|
||||
"timed events sort by start time");
|
||||
check(nocal::event_occurs_on(instant, day),
|
||||
"zero-duration event occurs on the day containing its instant");
|
||||
|
||||
const auto in_july = nocal::events_in_month(events, 2026y / July);
|
||||
check(in_july.size() == 7, "month query excludes an event beginning August 1");
|
||||
|
||||
nocal::Event invalid = event("invalid", day, 12, day, 11);
|
||||
check(!nocal::event_overlaps(invalid, nocal::local_day_start(day),
|
||||
nocal::local_day_end(day)),
|
||||
"invalid backwards event never overlaps");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
test_dates();
|
||||
test_month_layout();
|
||||
test_event_queries();
|
||||
if (failures != 0) {
|
||||
std::cerr << failures << " domain test(s) failed\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "domain tests passed\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user