#include #include "CivilTime.h" #include "SessionClock.h" #include "FakeMonotonicClock.h" using namespace timbre_core; void setUp(void) {} void tearDown(void) {} static void test_fixed_utc_minus_six_conversion(void) { // 2024-01-01 00:00 UTC is 2023-12-31 18:00 in the fixed local zone. const CivilDateTime local = CivilTime::fromUnixSeconds(1704067200LL); TEST_ASSERT_EQUAL_INT(2023, local.year); TEST_ASSERT_EQUAL_INT(12, local.month); TEST_ASSERT_EQUAL_INT(31, local.day); TEST_ASSERT_EQUAL_INT(18, local.hour); TEST_ASSERT_EQUAL_INT(0, local.minute); TEST_ASSERT_EQUAL_INT(0, local.second); TEST_ASSERT_EQUAL_INT(0, local.weekday); // Sunday } static void test_leap_day_and_round_trip(void) { CivilDateTime leap; leap.year = 2024; leap.month = 2; leap.day = 29; leap.hour = 12; leap.minute = 34; leap.second = 56; leap.weekday = CivilTime::weekdayForDate(leap.year, leap.month, leap.day); TEST_ASSERT_TRUE(leap.valid()); std::int64_t epoch = 0; TEST_ASSERT_TRUE(CivilTime::toUnixSeconds(leap, epoch)); const CivilDateTime roundTrip = CivilTime::fromUnixSeconds(epoch); TEST_ASSERT_TRUE(roundTrip == leap); TEST_ASSERT_TRUE(CivilTime::isLeapYear(2024)); TEST_ASSERT_FALSE(CivilTime::isLeapYear(1900)); TEST_ASSERT_TRUE(CivilTime::isLeapYear(2000)); TEST_ASSERT_EQUAL_INT(29, CivilTime::daysInMonth(2024, 2)); TEST_ASSERT_EQUAL_INT(28, CivilTime::daysInMonth(1900, 2)); } static void test_pre_epoch_values_use_floor_seconds(void) { const CivilDateTime local = CivilTime::fromUnixSeconds(-1); TEST_ASSERT_EQUAL_INT(1969, local.year); TEST_ASSERT_EQUAL_INT(12, local.month); TEST_ASSERT_EQUAL_INT(31, local.day); // UTC-1 is 17:59:59 in the fixed UTC-06:00 local zone. TEST_ASSERT_EQUAL_INT(17, local.hour); TEST_ASSERT_EQUAL_INT(59, local.minute); TEST_ASSERT_EQUAL_INT(59, local.second); const CivilDateTime invalid{CivilDateTime{2023, 2, 29, 0, 0, 0, 0}}; TEST_ASSERT_FALSE(invalid.valid()); } static void test_date_and_minute_keys_are_stable(void) { CivilDateTime value{2024, 1, 1, 8, 5, 0, 0}; const LocalMinuteKey first = LocalMinuteKey::from(value); TEST_ASSERT_EQUAL_INT64(20240101LL, first.date); TEST_ASSERT_EQUAL_INT(485, first.minuteOfDay); // Keep the same local time so the serial difference is exactly one day. CivilDateTime nextDay{2024, 1, 2, 8, 5, 0, 0}; const LocalMinuteKey second = LocalMinuteKey::from(nextDay); TEST_ASSERT_TRUE(first < second); TEST_ASSERT_TRUE(first != second); TEST_ASSERT_EQUAL_INT64(1LL * 1440LL, second.serial() - first.serial()); } static void test_session_clock_anchors_first_observation_without_catchup(void) { FakeMonotonicClock source(100); SessionClock clock(source); const SessionClock::Observation first = clock.observe(1704067200LL); TEST_ASSERT_TRUE(first.valid); TEST_ASSERT_TRUE(first.firstObservation); TEST_ASSERT_EQUAL_UINT64(100, first.monotonicMs); source.advance(5000); const SessionClock::Observation later = clock.observe(1704067205LL); TEST_ASSERT_TRUE(later.valid); TEST_ASSERT_FALSE(later.firstObservation); TEST_ASSERT_FALSE(later.monotonicRegression); TEST_ASSERT_EQUAL_INT(2023, later.local.year); TEST_ASSERT_EQUAL_INT(18, later.local.hour); TEST_ASSERT_EQUAL_INT(0, later.local.minute); TEST_ASSERT_EQUAL_INT(5, later.local.second); source.advance(1000); const SessionClock::Observation tick = clock.tick(); TEST_ASSERT_TRUE(tick.valid); TEST_ASSERT_EQUAL_INT(6, tick.local.second); TEST_ASSERT_EQUAL_UINT64(6000, clock.sessionElapsedMs()); } int main(int, char**) { UNITY_BEGIN(); RUN_TEST(test_fixed_utc_minus_six_conversion); RUN_TEST(test_leap_day_and_round_trip); RUN_TEST(test_pre_epoch_values_use_floor_seconds); RUN_TEST(test_date_and_minute_keys_are_stable); RUN_TEST(test_session_clock_anchors_first_observation_without_catchup); return UNITY_END(); }