AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
HolidayDateCalculatorTest.cpp File Reference
#include "HolidayDateCalculator.h"
#include "gtest/gtest.h"

Go to the source code of this file.

Classes

class  HolidayDateCalculatorTest
 
class  FindStartTimeForStageTest
 
class  FindLoopingStartTimeTest
 

Functions

 TEST_F (HolidayDateCalculatorTest, EasterSunday_KnownDates)
 
 TEST_F (HolidayDateCalculatorTest, EasterSunday_ValidDateRange_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, EasterSunday_AlwaysSunday_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, NthWeekday_Thanksgiving_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, NthWeekday_AllWeekdays_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, NthWeekday_SecondThirdFourth_Validation)
 
 TEST_F (HolidayDateCalculatorTest, PackDate_RoundTrip)
 
 TEST_F (HolidayDateCalculatorTest, PackUnpack_Roundtrip_FullRange)
 
 TEST_F (HolidayDateCalculatorTest, UnpackDate_KnownValues)
 
 TEST_F (HolidayDateCalculatorTest, Noblegarden_DayAfterEaster_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, PilgrimsBounty_SundayBeforeThanksgiving_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, FixedDateHolidays_ConsistentAcrossYears_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, Brewfest_FixedSept13)
 
 TEST_F (HolidayDateCalculatorTest, Brewfest_NoPiratesDayConflict)
 
 TEST_F (HolidayDateCalculatorTest, HarvestFestival_AutumnEquinoxBased)
 
 TEST_F (HolidayDateCalculatorTest, HarvestFestival_AlwaysInSeptember_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, WinterVeil_WinterSolsticeBased)
 
 TEST_F (HolidayDateCalculatorTest, WinterVeil_AlwaysInDecember_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, LoveIsInTheAir_FirstMondayOnOrAfterFeb3)
 
 TEST_F (HolidayDateCalculatorTest, ChildrensWeek_FirstMondayOnOrAfterApr25)
 
 TEST_F (HolidayDateCalculatorTest, WeekdayOnOrAfter_AlwaysCorrectWeekday_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, WeekdayOnOrAfter_MonthBoundary_RollsIntoNextMonth)
 
 TEST_F (HolidayDateCalculatorTest, StressTest_AllCalculations_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, LeapYear_AllYears_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, GetPackedHolidayDate_UnknownHoliday)
 
 TEST_F (HolidayDateCalculatorTest, PackDate_YearBeyond2031)
 
 TEST_F (HolidayDateCalculatorTest, CenturyBoundaries)
 
 TEST_F (HolidayDateCalculatorTest, LunarNewYear_KnownDates)
 
 TEST_F (HolidayDateCalculatorTest, LunarNewYear_ValidDateRange_1900_2200)
 
 TEST_F (HolidayDateCalculatorTest, LunarFestival_DayBeforeChineseNewYear)
 
 TEST_F (HolidayDateCalculatorTest, LunarFestival_KnownDates)
 
 TEST_F (HolidayDateCalculatorTest, LunarNewYear_NoRepeatedDates)
 
 TEST_F (HolidayDateCalculatorTest, LunarNewYear_19YearMetonicCycle)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_LocationRotation)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_FirstSundayOfMonth_KnownDates)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_GetDates_Elwynn)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_GetDates_Mulgore)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_GetDates_Terokkar)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_GetDates_MultiYear)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_AlwaysSunday_AllLocations_2000_2030)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_CalculateHolidayDate_ReturnsFirstOccurrence)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_NoOverlap_AllLocations)
 
 TEST_F (HolidayDateCalculatorTest, DarkmoonFaire_InHolidayRules)
 
 TEST_F (FindStartTimeForStageTest, Stage1_BeforeStart_SelectsFirstDate)
 
 TEST_F (FindStartTimeForStageTest, Stage1_DuringEvent_SelectsCurrentDate)
 
 TEST_F (FindStartTimeForStageTest, Stage1_AfterEnd_SelectsNextDate)
 
 TEST_F (FindStartTimeForStageTest, Stage2_DuringLateWindow_SelectsCurrentDate)
 
 TEST_F (FindStartTimeForStageTest, Stage2_AfterHolidayEnds_SelectsNextDate)
 
 TEST_F (FindStartTimeForStageTest, NoDates_ReturnsZero)
 
 TEST_F (FindStartTimeForStageTest, AllDatesPast_ReturnsZero)
 
 TEST_F (FindLoopingStartTimeTest, ZeroAnchor_ReturnsZero)
 
 TEST_F (FindLoopingStartTimeTest, FutureAnchor_NotRolled)
 
 TEST_F (FindLoopingStartTimeTest, AncientAnchor_RollsToMostRecentOccurrence)
 
 TEST_F (FindLoopingStartTimeTest, StageOffsetApplied)
 
 TEST_F (FindLoopingStartTimeTest, ZeroOccurence_ReturnsAnchor)
 

Function Documentation

◆ TEST_F() [1/55]

TEST_F ( FindLoopingStartTimeTest  ,
AncientAnchor_RollsToMostRecentOccurrence   
)
1480{
1481 uint32_t anchor = PackAnchor(2007, 10, 26, 0, 0);
1482 time_t curTime = MakeTime(2026, 2, 6, 12);
1483
1484 time_t result = HolidayDateCalculator::FindLoopingStartTime(anchor, 0, OCCURENCE_MIN, curTime);
1485
1486 time_t const period = static_cast<time_t>(OCCURENCE_MIN) * 60;
1487 time_t const base = MakeTime(2007, 10, 26);
1488
1489 EXPECT_LE(result, curTime);
1490 EXPECT_GT(result + period, curTime);
1491 EXPECT_EQ((result - base) % period, 0);
1492}
static time_t FindLoopingStartTime(uint32_t packedAnchor, time_t stageOffset, uint32_t occurenceMinutes, time_t curTime)
Definition HolidayDateCalculator.cpp:675

References HolidayDateCalculator::FindLoopingStartTime().

◆ TEST_F() [2/55]

TEST_F ( FindLoopingStartTimeTest  ,
FutureAnchor_NotRolled   
)
1471{
1472 uint32_t anchor = PackAnchor(2026, 10, 30, 0, 0);
1473 time_t curTime = MakeTime(2026, 6, 1);
1474 EXPECT_EQ(HolidayDateCalculator::FindLoopingStartTime(anchor, 0, OCCURENCE_MIN, curTime),
1475 MakeTime(2026, 10, 30));
1476}

References HolidayDateCalculator::FindLoopingStartTime().

◆ TEST_F() [3/55]

TEST_F ( FindLoopingStartTimeTest  ,
StageOffsetApplied   
)
1495{
1496 uint32_t anchor = PackAnchor(2007, 10, 26, 0, 0);
1497 time_t stageOffset = 96 * 3600;
1498 time_t curTime = MakeTime(2026, 2, 6, 12);
1499
1500 time_t result = HolidayDateCalculator::FindLoopingStartTime(anchor, stageOffset, OCCURENCE_MIN, curTime);
1501
1502 time_t const period = static_cast<time_t>(OCCURENCE_MIN) * 60;
1503 time_t const base = MakeTime(2007, 10, 26) + stageOffset;
1504 EXPECT_EQ((result - base) % period, 0);
1505}

References HolidayDateCalculator::FindLoopingStartTime().

◆ TEST_F() [4/55]

TEST_F ( FindLoopingStartTimeTest  ,
ZeroAnchor_ReturnsZero   
)
1465{
1466 time_t curTime = MakeTime(2026, 6, 1);
1467 EXPECT_EQ(HolidayDateCalculator::FindLoopingStartTime(0, 0, OCCURENCE_MIN, curTime), 0);
1468}

References HolidayDateCalculator::FindLoopingStartTime().

◆ TEST_F() [5/55]

TEST_F ( FindLoopingStartTimeTest  ,
ZeroOccurence_ReturnsAnchor   
)
1508{
1509 uint32_t anchor = PackAnchor(2007, 10, 26, 0, 0);
1510 time_t curTime = MakeTime(2026, 2, 6, 12);
1511 EXPECT_EQ(HolidayDateCalculator::FindLoopingStartTime(anchor, 0, 0, curTime),
1512 MakeTime(2007, 10, 26));
1513}

References HolidayDateCalculator::FindLoopingStartTime().

◆ TEST_F() [6/55]

TEST_F ( FindStartTimeForStageTest  ,
AllDatesPast_ReturnsZero   
)
1420{
1421 uint32_t dates[26] = {};
1422 PackTwoDates(dates, 2026, 1, 5, 2026, 2, 6);
1423
1424 time_t stageOffset = 0;
1425 uint32_t stageLengthMin = 72 * 60;
1426
1427 time_t curTime = MakeTime(2026, 6, 1); // way after both dates
1428 time_t result = HolidayDateCalculator::FindStartTimeForStage(dates, 26, stageOffset, stageLengthMin, curTime);
1429 EXPECT_EQ(result, 0);
1430}
static time_t FindStartTimeForStage(uint32_t const *packedDates, uint8_t numDates, time_t stageOffset, uint32_t stageLengthMinutes, time_t curTime)
Definition HolidayDateCalculator.cpp:651

References HolidayDateCalculator::FindStartTimeForStage().

◆ TEST_F() [7/55]

TEST_F ( FindStartTimeForStageTest  ,
NoDates_ReturnsZero   
)
1410{
1411 uint32_t dates[26] = {};
1412 time_t curTime = MakeTime(2026, 6, 1);
1413
1414 time_t result = HolidayDateCalculator::FindStartTimeForStage(dates, 26, 0, 168 * 60, curTime);
1415 EXPECT_EQ(result, 0);
1416}

References HolidayDateCalculator::FindStartTimeForStage().

◆ TEST_F() [8/55]

TEST_F ( FindStartTimeForStageTest  ,
Stage1_AfterEnd_SelectsNextDate   
)
1352{
1353 uint32_t dates[26] = {};
1354 PackTwoDates(dates, 2026, 3, 6, 2026, 4, 3);
1355
1356 time_t curTime = MakeTime(2026, 3, 20); // well past first event
1357 time_t stageOffset = 0;
1358 uint32_t stageLengthMin = 72 * 60; // 3 days
1359
1360 time_t result = HolidayDateCalculator::FindStartTimeForStage(dates, 26, stageOffset, stageLengthMin, curTime);
1361 EXPECT_EQ(result, MakeTime(2026, 4, 3));
1362}

References HolidayDateCalculator::FindStartTimeForStage().

◆ TEST_F() [9/55]

TEST_F ( FindStartTimeForStageTest  ,
Stage1_BeforeStart_SelectsFirstDate   
)
1324{
1325 uint32_t dates[26] = {};
1326 PackTwoDates(dates, 2026, 3, 6, 2026, 4, 3);
1327
1328 time_t curTime = MakeTime(2026, 3, 1);
1329 time_t stageOffset = 0;
1330 uint32_t stageLengthMin = 72 * 60; // 72 hours = 3 days
1331
1332 time_t result = HolidayDateCalculator::FindStartTimeForStage(dates, 26, stageOffset, stageLengthMin, curTime);
1333 EXPECT_EQ(result, MakeTime(2026, 3, 6));
1334}

References HolidayDateCalculator::FindStartTimeForStage().

◆ TEST_F() [10/55]

TEST_F ( FindStartTimeForStageTest  ,
Stage1_DuringEvent_SelectsCurrentDate   
)
1338{
1339 uint32_t dates[26] = {};
1340 PackTwoDates(dates, 2026, 3, 6, 2026, 4, 3);
1341
1342 time_t curTime = MakeTime(2026, 3, 7, 12); // mid-event
1343 time_t stageOffset = 0;
1344 uint32_t stageLengthMin = 72 * 60; // 3 days
1345
1346 time_t result = HolidayDateCalculator::FindStartTimeForStage(dates, 26, stageOffset, stageLengthMin, curTime);
1347 EXPECT_EQ(result, MakeTime(2026, 3, 6));
1348}

References HolidayDateCalculator::FindStartTimeForStage().

◆ TEST_F() [11/55]

TEST_F ( FindStartTimeForStageTest  ,
Stage2_AfterHolidayEnds_SelectsNextDate   
)
1394{
1395 uint32_t dates[26] = {};
1396 PackTwoDates(dates, 2026, 3, 6, 2026, 4, 3);
1397
1398 time_t stageOffset = 72 * 3600;
1399 uint32_t stageLengthMin = 168 * 60;
1400
1401 // curTime = Mar 20 (well past the entire holiday)
1402 time_t curTime = MakeTime(2026, 3, 20);
1403
1404 time_t result = HolidayDateCalculator::FindStartTimeForStage(dates, 26, stageOffset, stageLengthMin, curTime);
1405 EXPECT_EQ(result, MakeTime(2026, 4, 3) + stageOffset);
1406}

References HolidayDateCalculator::FindStartTimeForStage().

◆ TEST_F() [12/55]

TEST_F ( FindStartTimeForStageTest  ,
Stage2_DuringLateWindow_SelectsCurrentDate   
)
1369{
1370 // Simulate Darkmoon Faire:
1371 // Holiday starts Mar 6 (Friday, building phase)
1372 // Stage 1 (building): 72 hours = 3 days (Mar 6-9)
1373 // Stage 2 (active): 168 hours = 7 days (Mar 9-16)
1374 // Total holiday: Mar 6 - Mar 16 (10 days)
1375 // Next occurrence: Apr 3
1376 uint32_t dates[26] = {};
1377 PackTwoDates(dates, 2026, 3, 6, 2026, 4, 3);
1378
1379 time_t stageOffset = 72 * 3600; // Stage 1 = 72 hours in seconds
1380 uint32_t stageLengthMin = 168 * 60; // Stage 2 = 168 hours in minutes
1381
1382 // curTime = Mar 14 (day 8 of holiday, day 5 of stage 2)
1383 // Old bug: startTime(Mar 6) + 168h = Mar 13, so curTime > that -> SKIP to Apr 3!
1384 // Fixed: startTime(Mar 6) + 72h + 168h = Mar 16, so curTime < that -> correct
1385 time_t curTime = MakeTime(2026, 3, 14, 12);
1386
1387 time_t result = HolidayDateCalculator::FindStartTimeForStage(dates, 26, stageOffset, stageLengthMin, curTime);
1388 // Should return Mar 6 + stageOffset = Mar 9 (stage 2 start)
1389 EXPECT_EQ(result, MakeTime(2026, 3, 6) + stageOffset);
1390}

References HolidayDateCalculator::FindStartTimeForStage().

◆ TEST_F() [13/55]

TEST_F ( HolidayDateCalculatorTest  ,
Brewfest_FixedSept13   
)
443{
444 // Brewfest is now fixed: prep starts Sept 13, main event Sept 20
445 // This avoids any potential overlap with Pirates' Day (Sept 19)
446 HolidayRule brewfest = { 372, HolidayCalculationType::FIXED_DATE, 9, 13, 0, 0 };
447
448 for (int year = 2000; year <= 2030; ++year)
449 {
450 std::tm date = HolidayDateCalculator::CalculateHolidayDate(brewfest, year);
451
452 SCOPED_TRACE("Year: " + std::to_string(year));
453
454 EXPECT_EQ(date.tm_year + 1900, year);
455 EXPECT_EQ(date.tm_mon + 1, 9); // September
456 EXPECT_EQ(date.tm_mday, 13); // Always Sept 13
457 }
458}
static std::tm CalculateHolidayDate(HolidayRule const &rule, int year)
Definition HolidayDateCalculator.cpp:482
Definition HolidayDateCalculator.h:49

References HolidayDateCalculator::CalculateHolidayDate(), and FIXED_DATE.

◆ TEST_F() [14/55]

TEST_F ( HolidayDateCalculatorTest  ,
Brewfest_NoPiratesDayConflict   
)
461{
462 // Brewfest main event (Sept 20) is always after Pirates' Day (Sept 19)
463 HolidayRule brewfest = { 372, HolidayCalculationType::FIXED_DATE, 9, 13, 0, 0 };
464 HolidayRule piratesDay = { 398, HolidayCalculationType::FIXED_DATE, 9, 19, 0, 0 };
465
466 for (int year = 2000; year <= 2030; ++year)
467 {
468 std::tm brewfestDate = HolidayDateCalculator::CalculateHolidayDate(brewfest, year);
469 std::tm piratesDate = HolidayDateCalculator::CalculateHolidayDate(piratesDay, year);
470
471 SCOPED_TRACE("Year: " + std::to_string(year));
472
473 // Brewfest prep is Sept 13, main event is Sept 20
474 // Pirates' Day is Sept 19, which falls between prep and main event
475 EXPECT_EQ(brewfestDate.tm_mday, 13); // Brewfest prep
476 EXPECT_EQ(piratesDate.tm_mday, 19); // Pirates' Day
477 // Main event (Sept 20) > Pirates' Day (Sept 19)
478 }
479}

References HolidayDateCalculator::CalculateHolidayDate(), and FIXED_DATE.

◆ TEST_F() [15/55]

TEST_F ( HolidayDateCalculatorTest  ,
CenturyBoundaries   
)
825{
826 // Test calculations around century boundaries (which affect leap year rules)
827 std::vector<int> centuryYears = { 1900, 2000, 2100, 2200 };
828
829 for (int year : centuryYears)
830 {
831 SCOPED_TRACE("Century year: " + std::to_string(year));
832
833 // Easter
835 EXPECT_EQ(easter.tm_wday, 0) << "Easter should be Sunday";
836 EXPECT_TRUE(easter.tm_mon == 2 || easter.tm_mon == 3) << "Easter should be in March or April";
837
838 // Thanksgiving
839 std::tm thanksgiving = HolidayDateCalculator::CalculateNthWeekday(year, 11, Weekday::THURSDAY, 4);
840 EXPECT_EQ(thanksgiving.tm_wday, static_cast<int>(Weekday::THURSDAY));
841 EXPECT_EQ(thanksgiving.tm_mon + 1, 11);
842 }
843}
static std::tm CalculateNthWeekday(int year, int month, Weekday weekday, int n)
Definition HolidayDateCalculator.cpp:179
static std::tm CalculateEasterSunday(int year)
Definition HolidayDateCalculator.cpp:151

References HolidayDateCalculator::CalculateEasterSunday(), HolidayDateCalculator::CalculateNthWeekday(), and THURSDAY.

◆ TEST_F() [16/55]

TEST_F ( HolidayDateCalculatorTest  ,
ChildrensWeek_FirstMondayOnOrAfterApr25   
)
602{
603 // Verify "first Monday on or after Apr 25" calculation (Monday closest to May 1)
604 struct ChildrensWeekTestCase { int year; int expectedMonth; int expectedDay; };
605 std::vector<ChildrensWeekTestCase> testCases = {
606 { 2023, 5, 1 }, // Apr 25 is Tue, first Mon after is May 1
607 { 2024, 4, 29 }, // Apr 25 is Thu, first Mon after is Apr 29
608 { 2025, 4, 28 }, // Apr 25 is Fri, first Mon after is Apr 28
609 { 2026, 4, 27 }, // Apr 25 is Sat, first Mon after is Apr 27
610 { 2027, 4, 26 }, // Apr 25 is Sun, first Mon after is Apr 26
611 };
612
613 for (auto const& tc : testCases)
614 {
616
617 SCOPED_TRACE("Year: " + std::to_string(tc.year));
618
619 EXPECT_EQ(date.tm_year + 1900, tc.year);
620 EXPECT_EQ(date.tm_mon + 1, tc.expectedMonth);
621 EXPECT_EQ(date.tm_mday, tc.expectedDay);
622 EXPECT_EQ(date.tm_wday, 1); // Monday
623 }
624}
static std::tm CalculateWeekdayOnOrAfter(int year, int month, int day, Weekday weekday)
Definition HolidayDateCalculator.cpp:199

References HolidayDateCalculator::CalculateWeekdayOnOrAfter(), and MONDAY.

◆ TEST_F() [17/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_AlwaysSunday_AllLocations_2000_2030   
)
1187{
1188 // Verify all Darkmoon Faire dates are Sundays for entire valid range
1189 // 3 locations: offset 0 (Elwynn), offset 1 (Mulgore), offset 2 (Terokkar)
1190 for (int offset = 0; offset <= 2; ++offset)
1191 {
1192 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(offset, 2000, 31);
1193
1194 SCOPED_TRACE("Location offset: " + std::to_string(offset));
1195
1196 for (size_t i = 0; i < dates.size(); ++i)
1197 {
1198 std::tm date = HolidayDateCalculator::UnpackDate(dates[i]);
1199 EXPECT_EQ(date.tm_wday, 0)
1200 << "Date " << (date.tm_year + 1900) << "-" << (date.tm_mon + 1) << "-" << date.tm_mday
1201 << " should be Sunday";
1202 }
1203 }
1204}
static std::vector< uint32_t > GetDarkmoonFaireDates(int locationOffset, int startYear, int numYears, int dayOffset=0)
Definition HolidayDateCalculator.cpp:621
static std::tm UnpackDate(uint32_t packed)
Definition HolidayDateCalculator.cpp:595

References HolidayDateCalculator::GetDarkmoonFaireDates(), and HolidayDateCalculator::UnpackDate().

◆ TEST_F() [18/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_CalculateHolidayDate_ReturnsFirstOccurrence   
)
1207{
1208 // Using CalculateHolidayDate with DARKMOON_FAIRE should return the first occurrence of the year
1209 // Elwynn (offset 0) = Mar/Jun/Sep/Dec, Mulgore (offset 1) = Jan/Apr/Jul/Oct, Terokkar (offset 2) = Feb/May/Aug/Nov
1210 HolidayRule elwynnRule = { 374, HolidayCalculationType::DARKMOON_FAIRE, 0, 0, 0, -2 };
1211 HolidayRule mulgoreRule = { 375, HolidayCalculationType::DARKMOON_FAIRE, 1, 0, 0, -2 };
1212 HolidayRule terokkarRule = { 376, HolidayCalculationType::DARKMOON_FAIRE, 2, 0, 0, -2 };
1213
1214 // 2025 first occurrences:
1215 // Elwynn (offset 0): March (first month where month % 3 == 0)
1216 std::tm elwynn2025 = HolidayDateCalculator::CalculateHolidayDate(elwynnRule, 2025);
1217 EXPECT_EQ(elwynn2025.tm_mon + 1, 3) << "Elwynn first occurrence should be March";
1218 EXPECT_EQ(elwynn2025.tm_wday, 0) << "Should be Sunday";
1219
1220 // Mulgore (offset 1): January (first month where month % 3 == 1)
1221 std::tm mulgore2025 = HolidayDateCalculator::CalculateHolidayDate(mulgoreRule, 2025);
1222 EXPECT_EQ(mulgore2025.tm_mon + 1, 1) << "Mulgore first occurrence should be January";
1223 EXPECT_EQ(mulgore2025.tm_wday, 0) << "Should be Sunday";
1224
1225 // Terokkar (offset 2): February (first month where month % 3 == 2)
1226 std::tm terokkar2025 = HolidayDateCalculator::CalculateHolidayDate(terokkarRule, 2025);
1227 EXPECT_EQ(terokkar2025.tm_mon + 1, 2) << "Terokkar first occurrence should be February";
1228 EXPECT_EQ(terokkar2025.tm_wday, 0) << "Should be Sunday";
1229}

References HolidayDateCalculator::CalculateHolidayDate(), and DARKMOON_FAIRE.

◆ TEST_F() [19/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_FirstSundayOfMonth_KnownDates   
)
1063{
1064 // Verify first Sunday calculation against known dates
1065 struct FirstSundayTestCase { int year; int month; int expectedDay; };
1066 std::vector<FirstSundayTestCase> testCases = {
1067 // 2024
1068 { 2024, 1, 7 }, // Jan 2024: First Sunday = Jan 7
1069 { 2024, 2, 4 }, // Feb 2024: First Sunday = Feb 4
1070 { 2024, 3, 3 }, // Mar 2024: First Sunday = Mar 3
1071 { 2024, 4, 7 }, // Apr 2024: First Sunday = Apr 7
1072 { 2024, 9, 1 }, // Sep 2024: First Sunday = Sep 1
1073 { 2024, 12, 1 }, // Dec 2024: First Sunday = Dec 1
1074 // 2025
1075 { 2025, 1, 5 }, // Jan 2025: First Sunday = Jan 5
1076 { 2025, 2, 2 }, // Feb 2025: First Sunday = Feb 2
1077 { 2025, 3, 2 }, // Mar 2025: First Sunday = Mar 2
1078 { 2025, 6, 1 }, // Jun 2025: First Sunday = Jun 1
1079 { 2025, 9, 7 }, // Sep 2025: First Sunday = Sep 7
1080 { 2025, 12, 7 }, // Dec 2025: First Sunday = Dec 7
1081 // 2026
1082 { 2026, 1, 4 }, // Jan 2026: First Sunday = Jan 4
1083 { 2026, 3, 1 }, // Mar 2026: First Sunday = Mar 1
1084 };
1085
1086 for (auto const& tc : testCases)
1087 {
1088 std::tm date = HolidayDateCalculator::CalculateNthWeekday(tc.year, tc.month, Weekday::SUNDAY, 1);
1089
1090 SCOPED_TRACE("Year: " + std::to_string(tc.year) + " Month: " + std::to_string(tc.month));
1091
1092 EXPECT_EQ(date.tm_year + 1900, tc.year);
1093 EXPECT_EQ(date.tm_mon + 1, tc.month);
1094 EXPECT_EQ(date.tm_mday, tc.expectedDay);
1095 EXPECT_EQ(date.tm_wday, 0) << "Should be Sunday";
1096 }
1097}

References HolidayDateCalculator::CalculateNthWeekday(), and SUNDAY.

◆ TEST_F() [20/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_GetDates_Elwynn   
)
1100{
1101 // Elwynn (offset 0): Mar, Jun, Sep, Dec
1102 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(0, 2025, 1);
1103
1104 // Should have exactly 4 dates for one year
1105 EXPECT_EQ(dates.size(), 4u);
1106
1107 // Verify each date is in the correct month and is a Sunday
1108 std::vector<int> expectedMonths = { 3, 6, 9, 12 };
1109 for (size_t i = 0; i < dates.size(); ++i)
1110 {
1111 std::tm date = HolidayDateCalculator::UnpackDate(dates[i]);
1112
1113 SCOPED_TRACE("Date index: " + std::to_string(i));
1114
1115 EXPECT_EQ(date.tm_year + 1900, 2025);
1116 EXPECT_EQ(date.tm_mon + 1, expectedMonths[i]);
1117 EXPECT_EQ(date.tm_wday, 0) << "Should be Sunday";
1118 EXPECT_GE(date.tm_mday, 1);
1119 EXPECT_LE(date.tm_mday, 7) << "First Sunday must be within first 7 days";
1120 }
1121}

References HolidayDateCalculator::GetDarkmoonFaireDates(), and HolidayDateCalculator::UnpackDate().

◆ TEST_F() [21/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_GetDates_Mulgore   
)
1124{
1125 // Mulgore (offset 1): Jan, Apr, Jul, Oct
1126 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(1, 2025, 1);
1127
1128 // Should have exactly 4 dates for one year
1129 EXPECT_EQ(dates.size(), 4u);
1130
1131 std::vector<int> expectedMonths = { 1, 4, 7, 10 };
1132 for (size_t i = 0; i < dates.size(); ++i)
1133 {
1134 std::tm date = HolidayDateCalculator::UnpackDate(dates[i]);
1135
1136 SCOPED_TRACE("Date index: " + std::to_string(i));
1137
1138 EXPECT_EQ(date.tm_year + 1900, 2025);
1139 EXPECT_EQ(date.tm_mon + 1, expectedMonths[i]);
1140 EXPECT_EQ(date.tm_wday, 0) << "Should be Sunday";
1141 }
1142}

References HolidayDateCalculator::GetDarkmoonFaireDates(), and HolidayDateCalculator::UnpackDate().

◆ TEST_F() [22/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_GetDates_MultiYear   
)
1166{
1167 // Get 4 years of dates for Elwynn (offset 0)
1168 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(0, 2025, 4);
1169
1170 // 4 dates per year * 4 years = 16 dates
1171 EXPECT_EQ(dates.size(), 16u);
1172
1173 // Verify dates are in chronological order
1174 for (size_t i = 1; i < dates.size(); ++i)
1175 {
1176 std::tm prev = HolidayDateCalculator::UnpackDate(dates[i - 1]);
1177 std::tm curr = HolidayDateCalculator::UnpackDate(dates[i]);
1178
1179 time_t prevTime = mktime(&prev);
1180 time_t currTime = mktime(&curr);
1181
1182 EXPECT_GT(currTime, prevTime) << "Dates should be in chronological order";
1183 }
1184}

References HolidayDateCalculator::GetDarkmoonFaireDates(), and HolidayDateCalculator::UnpackDate().

◆ TEST_F() [23/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_GetDates_Terokkar   
)
1145{
1146 // Terokkar (offset 2): Feb, May, Aug, Nov
1147 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(2, 2025, 1);
1148
1149 // Should have exactly 4 dates for one year
1150 EXPECT_EQ(dates.size(), 4u);
1151
1152 std::vector<int> expectedMonths = { 2, 5, 8, 11 };
1153 for (size_t i = 0; i < dates.size(); ++i)
1154 {
1155 std::tm date = HolidayDateCalculator::UnpackDate(dates[i]);
1156
1157 SCOPED_TRACE("Date index: " + std::to_string(i));
1158
1159 EXPECT_EQ(date.tm_year + 1900, 2025);
1160 EXPECT_EQ(date.tm_mon + 1, expectedMonths[i]);
1161 EXPECT_EQ(date.tm_wday, 0) << "Should be Sunday";
1162 }
1163}

References HolidayDateCalculator::GetDarkmoonFaireDates(), and HolidayDateCalculator::UnpackDate().

◆ TEST_F() [24/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_InHolidayRules   
)
1259{
1260 // Verify all three Darkmoon Faire locations are in the HolidayRules
1261 auto const& rules = HolidayDateCalculator::GetHolidayRules();
1262
1263 bool foundElwynn = false, foundMulgore = false, foundTerokkar = false;
1264 for (auto const& rule : rules)
1265 {
1266 if (rule.holidayId == 374 && rule.type == HolidayCalculationType::DARKMOON_FAIRE)
1267 foundElwynn = true;
1268 if (rule.holidayId == 375 && rule.type == HolidayCalculationType::DARKMOON_FAIRE)
1269 foundMulgore = true;
1270 if (rule.holidayId == 376 && rule.type == HolidayCalculationType::DARKMOON_FAIRE)
1271 foundTerokkar = true;
1272 }
1273
1274 EXPECT_TRUE(foundElwynn) << "Darkmoon Faire Elwynn (374) should be in HolidayRules";
1275 EXPECT_TRUE(foundMulgore) << "Darkmoon Faire Mulgore (375) should be in HolidayRules";
1276 EXPECT_TRUE(foundTerokkar) << "Darkmoon Faire Terokkar (376) should be in HolidayRules";
1277}
static std::vector< HolidayRule > const & GetHolidayRules()
Definition HolidayDateCalculator.cpp:146

References DARKMOON_FAIRE, and HolidayDateCalculator::GetHolidayRules().

◆ TEST_F() [25/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_LocationRotation   
)
1037{
1038 // Verify the 3-location rotation pattern:
1039 // Mulgore (offset 1): Jan, Apr, Jul, Oct - month % 3 == 1
1040 // Terokkar (offset 2): Feb, May, Aug, Nov - month % 3 == 2
1041 // Elwynn (offset 0): Mar, Jun, Sep, Dec - month % 3 == 0
1042
1043 // Mulgore months (offset 1)
1044 EXPECT_EQ(1 % 3, 1);
1045 EXPECT_EQ(4 % 3, 1);
1046 EXPECT_EQ(7 % 3, 1);
1047 EXPECT_EQ(10 % 3, 1);
1048
1049 // Terokkar months (offset 2)
1050 EXPECT_EQ(2 % 3, 2);
1051 EXPECT_EQ(5 % 3, 2);
1052 EXPECT_EQ(8 % 3, 2);
1053 EXPECT_EQ(11 % 3, 2);
1054
1055 // Elwynn months (offset 0)
1056 EXPECT_EQ(3 % 3, 0);
1057 EXPECT_EQ(6 % 3, 0);
1058 EXPECT_EQ(9 % 3, 0);
1059 EXPECT_EQ(12 % 3, 0);
1060}

◆ TEST_F() [26/55]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_NoOverlap_AllLocations   
)
1232{
1233 // Verify all three locations don't share dates (they're in different months)
1234 for (int year = 2000; year <= 2030; ++year)
1235 {
1236 std::vector<uint32_t> elwynn = HolidayDateCalculator::GetDarkmoonFaireDates(0, year, 1); // Mar/Jun/Sep/Dec
1237 std::vector<uint32_t> mulgore = HolidayDateCalculator::GetDarkmoonFaireDates(1, year, 1); // Jan/Apr/Jul/Oct
1238 std::vector<uint32_t> terokkar = HolidayDateCalculator::GetDarkmoonFaireDates(2, year, 1); // Feb/May/Aug/Nov
1239
1240 SCOPED_TRACE("Year: " + std::to_string(year));
1241
1242 // Check no overlap between any locations
1243 for (auto const& e : elwynn)
1244 {
1245 for (auto const& m : mulgore)
1246 EXPECT_NE(e, m) << "Elwynn and Mulgore should not share dates";
1247 for (auto const& t : terokkar)
1248 EXPECT_NE(e, t) << "Elwynn and Terokkar should not share dates";
1249 }
1250 for (auto const& m : mulgore)
1251 {
1252 for (auto const& t : terokkar)
1253 EXPECT_NE(m, t) << "Mulgore and Terokkar should not share dates";
1254 }
1255 }
1256}

References HolidayDateCalculator::GetDarkmoonFaireDates().

◆ TEST_F() [27/55]

TEST_F ( HolidayDateCalculatorTest  ,
EasterSunday_AlwaysSunday_1900_2200   
)
174{
175 // Verify Easter is always on Sunday for entire range
176 for (int year = 1900; year <= 2200; ++year)
177 {
179 EXPECT_EQ(easter.tm_wday, 0) << "Easter " << year << " should be Sunday";
180 }
181}

References HolidayDateCalculator::CalculateEasterSunday().

◆ TEST_F() [28/55]

TEST_F ( HolidayDateCalculatorTest  ,
EasterSunday_KnownDates   
)
101{
102 // Verify against known Easter dates
103 // Source: https://www.census.gov/data/software/x13as/genhol/easter-dates.html
104 struct EasterTestCase { int year; int month; int day; };
105 std::vector<EasterTestCase> testCases = {
106 // Historical dates
107 { 1900, 4, 15 },
108 { 1901, 4, 7 },
109 { 1950, 4, 9 },
110 { 1999, 4, 4 },
111 // Recent dates
112 { 2000, 4, 23 },
113 { 2010, 4, 4 },
114 { 2020, 4, 12 },
115 { 2021, 4, 4 },
116 { 2022, 4, 17 },
117 { 2023, 4, 9 },
118 { 2024, 3, 31 },
119 { 2025, 4, 20 },
120 { 2026, 4, 5 },
121 { 2027, 3, 28 },
122 { 2028, 4, 16 },
123 { 2029, 4, 1 },
124 { 2030, 4, 21 },
125 // Future dates
126 { 2050, 4, 10 },
127 { 2100, 3, 28 },
128 { 2150, 4, 12 },
129 { 2200, 4, 6 },
130 };
131
132 for (auto const& tc : testCases)
133 {
134 std::tm easter = HolidayDateCalculator::CalculateEasterSunday(tc.year);
135 SCOPED_TRACE("Year: " + std::to_string(tc.year));
136 ExpectDate(easter, tc.year, tc.month, tc.day);
137 }
138}

References HolidayDateCalculator::CalculateEasterSunday().

◆ TEST_F() [29/55]

TEST_F ( HolidayDateCalculatorTest  ,
EasterSunday_ValidDateRange_1900_2200   
)
141{
142 // Easter must always fall between March 22 and April 25 (inclusive)
143 for (int year = 1900; year <= 2200; ++year)
144 {
146
147 SCOPED_TRACE("Year: " + std::to_string(year));
148
149 // Verify year is correct
150 EXPECT_EQ(easter.tm_year + 1900, year);
151
152 // Easter must be in March or April
153 EXPECT_TRUE(easter.tm_mon == 2 || easter.tm_mon == 3)
154 << "Easter must be in March (2) or April (3), got month " << easter.tm_mon;
155
156 // Easter range: March 22 - April 25
157 if (easter.tm_mon == 2) // March
158 {
159 EXPECT_GE(easter.tm_mday, 22) << "Easter in March must be >= 22";
160 EXPECT_LE(easter.tm_mday, 31) << "Easter in March must be <= 31";
161 }
162 else // April
163 {
164 EXPECT_GE(easter.tm_mday, 1) << "Easter in April must be >= 1";
165 EXPECT_LE(easter.tm_mday, 25) << "Easter in April must be <= 25";
166 }
167
168 // Easter must be a Sunday
169 EXPECT_EQ(easter.tm_wday, 0) << "Easter must be a Sunday";
170 }
171}

References HolidayDateCalculator::CalculateEasterSunday().

◆ TEST_F() [30/55]

TEST_F ( HolidayDateCalculatorTest  ,
FixedDateHolidays_ConsistentAcrossYears_1900_2200   
)
409{
410 // Fixed date holidays should have same month/day every year
411 // Note: Brewfest, Harvest Festival, and Winter Veil are now dynamic (not fixed date)
412 struct FixedHolidayTestCase { uint32_t holidayId; int month; int day; char const* name; };
413 std::vector<FixedHolidayTestCase> testCases = {
414 { 341, 6, 21, "Midsummer Fire Festival" },
415 { 62, 7, 4, "Fireworks Spectacular" },
416 { 398, 9, 19, "Pirates' Day" },
417 { 324, 10, 18, "Hallow's End" },
418 { 409, 11, 1, "Day of the Dead" },
419 };
420
421 for (auto const& tc : testCases)
422 {
423 HolidayRule rule = { tc.holidayId, HolidayCalculationType::FIXED_DATE, tc.month, tc.day, 0, 0 };
424
425 for (int year = 1900; year <= 2200; ++year)
426 {
427 std::tm date = HolidayDateCalculator::CalculateHolidayDate(rule, year);
428
429 SCOPED_TRACE(std::string(tc.name) + " Year: " + std::to_string(year));
430
431 EXPECT_EQ(date.tm_year + 1900, year);
432 EXPECT_EQ(date.tm_mon + 1, tc.month);
433 EXPECT_EQ(date.tm_mday, tc.day);
434 }
435 }
436}
uint32_t holidayId
Definition HolidayDateCalculator.h:50

References HolidayDateCalculator::CalculateHolidayDate(), FIXED_DATE, and HolidayRule::holidayId.

◆ TEST_F() [31/55]

TEST_F ( HolidayDateCalculatorTest  ,
GetPackedHolidayDate_UnknownHoliday   
)
758{
759 // Unknown holiday ID should return 0
760 uint32_t result = HolidayDateCalculator::GetPackedHolidayDate(99999, 2025);
761 EXPECT_EQ(result, 0u);
762}
static uint32_t GetPackedHolidayDate(uint32_t holidayId, int year)
Definition HolidayDateCalculator.cpp:608

References HolidayDateCalculator::GetPackedHolidayDate().

◆ TEST_F() [32/55]

TEST_F ( HolidayDateCalculatorTest  ,
HarvestFestival_AlwaysInSeptember_1900_2200   
)
509{
510 HolidayRule harvestFestival = { 321, HolidayCalculationType::AUTUMN_EQUINOX, 0, 0, 0, -2 };
511
512 for (int year = 1900; year <= 2200; ++year)
513 {
514 std::tm date = HolidayDateCalculator::CalculateHolidayDate(harvestFestival, year);
515
516 SCOPED_TRACE("Year: " + std::to_string(year));
517
518 // Harvest Festival should always be in September (2 days before Sept 22-23 equinox)
519 EXPECT_EQ(date.tm_mon + 1, 9) << "Harvest Festival should be in September";
520 // Should be around Sept 20-21
521 EXPECT_GE(date.tm_mday, 18) << "Harvest Festival should be >= Sept 18";
522 EXPECT_LE(date.tm_mday, 22) << "Harvest Festival should be <= Sept 22";
523 }
524}

References AUTUMN_EQUINOX, and HolidayDateCalculator::CalculateHolidayDate().

◆ TEST_F() [33/55]

TEST_F ( HolidayDateCalculatorTest  ,
HarvestFestival_AutumnEquinoxBased   
)
487{
488 HolidayRule harvestFestival = { 321, HolidayCalculationType::AUTUMN_EQUINOX, 0, 0, 0, -2 };
489
490 // Autumn equinox is typically Sept 22-23, so Harvest Festival is Sept 20-21
491 for (int year = 2000; year <= 2030; ++year)
492 {
493 std::tm equinox = HolidayDateCalculator::CalculateAutumnEquinox(year);
494 std::tm harvest = HolidayDateCalculator::CalculateHolidayDate(harvestFestival, year);
495
496 SCOPED_TRACE("Year: " + std::to_string(year));
497
498 // Harvest should be exactly 2 days before equinox
499 // Convert to time_t to handle month boundaries correctly
500 time_t equinoxTime = mktime(&equinox);
501 time_t harvestTime = mktime(&harvest);
502
503 double diffDays = difftime(equinoxTime, harvestTime) / (60 * 60 * 24);
504 EXPECT_NEAR(diffDays, 2.0, 0.1) << "Harvest Festival should be 2 days before equinox";
505 }
506}
static std::tm CalculateAutumnEquinox(int year)
Definition HolidayDateCalculator.cpp:378

References AUTUMN_EQUINOX, HolidayDateCalculator::CalculateAutumnEquinox(), and HolidayDateCalculator::CalculateHolidayDate().

◆ TEST_F() [34/55]

TEST_F ( HolidayDateCalculatorTest  ,
LeapYear_AllYears_1900_2200   
)
734{
735 // Verify calculations work correctly for all years, checking leap year logic
736 for (int year = 1900; year <= 2200; ++year)
737 {
738 bool expectedLeap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
739
740 SCOPED_TRACE("Year: " + std::to_string(year));
741 EXPECT_EQ(IsLeapYear(year), expectedLeap);
742
743 // Easter calculation should always work regardless of leap year
745 EXPECT_EQ(easter.tm_wday, 0) << "Easter should be Sunday";
746
747 // All holiday calculations should produce valid dates
748 for (auto const& rule : HolidayDateCalculator::GetHolidayRules())
749 {
750 std::tm date = HolidayDateCalculator::CalculateHolidayDate(rule, year);
751 EXPECT_TRUE(IsValidDate(year, date.tm_mon + 1, date.tm_mday))
752 << "Invalid date for holiday " << rule.holidayId;
753 }
754 }
755}
static bool IsLeapYear(int year)
Definition HolidayDateCalculator.cpp:30
Definition HolidayDateCalculator.h:59

References HolidayDateCalculator::CalculateEasterSunday(), HolidayDateCalculator::CalculateHolidayDate(), HolidayDateCalculator::GetHolidayRules(), and IsLeapYear().

◆ TEST_F() [35/55]

TEST_F ( HolidayDateCalculatorTest  ,
LoveIsInTheAir_FirstMondayOnOrAfterFeb3   
)
575{
576 // Verify "first Monday on or after Feb 3" calculation
577 struct LoveTestCase { int year; int expectedDay; };
578 std::vector<LoveTestCase> testCases = {
579 { 2024, 5 }, // Feb 3 is Sat, first Mon after is Feb 5
580 { 2025, 3 }, // Feb 3 is Mon, so Feb 3
581 { 2026, 9 }, // Feb 3 is Tue, first Mon after is Feb 9
582 { 2027, 8 }, // Feb 3 is Wed, first Mon after is Feb 8
583 { 2028, 7 }, // Feb 3 is Thu, first Mon after is Feb 7
584 { 2029, 5 }, // Feb 3 is Sat, first Mon after is Feb 5
585 { 2030, 4 }, // Feb 3 is Sun, first Mon after is Feb 4
586 };
587
588 for (auto const& tc : testCases)
589 {
591
592 SCOPED_TRACE("Year: " + std::to_string(tc.year));
593
594 EXPECT_EQ(date.tm_year + 1900, tc.year);
595 EXPECT_EQ(date.tm_mon + 1, 2); // February
596 EXPECT_EQ(date.tm_mday, tc.expectedDay);
597 EXPECT_EQ(date.tm_wday, 1); // Monday
598 }
599}

References HolidayDateCalculator::CalculateWeekdayOnOrAfter(), and MONDAY.

◆ TEST_F() [36/55]

TEST_F ( HolidayDateCalculatorTest  ,
LunarFestival_DayBeforeChineseNewYear   
)
937{
938 // Lunar Festival starts 1 day before Chinese New Year
939 // Test with -1 offset applied
940 HolidayRule lunarFestival = { 327, HolidayCalculationType::LUNAR_NEW_YEAR, 0, 0, 0, -1 };
941
942 for (int year = 2000; year <= 2100; ++year)
943 {
944 std::tm fromRule = HolidayDateCalculator::CalculateHolidayDate(lunarFestival, year);
946
947 // Expected: CNY - 1 day
948 std::tm expected = cny;
949 expected.tm_mday -= 1;
950 mktime(&expected);
951
952 SCOPED_TRACE("Year: " + std::to_string(year));
953
954 // Lunar Festival should be 1 day before Chinese New Year
955 EXPECT_EQ(fromRule.tm_year, expected.tm_year);
956 EXPECT_EQ(fromRule.tm_mon, expected.tm_mon);
957 EXPECT_EQ(fromRule.tm_mday, expected.tm_mday);
958 }
959}
static std::tm CalculateLunarNewYear(int year)
Definition HolidayDateCalculator.cpp:328

References HolidayDateCalculator::CalculateHolidayDate(), HolidayDateCalculator::CalculateLunarNewYear(), and LUNAR_NEW_YEAR.

◆ TEST_F() [37/55]

TEST_F ( HolidayDateCalculatorTest  ,
LunarFestival_KnownDates   
)
962{
963 // Verify Lunar Festival (CNY - 1 day) against known official dates
964 // Source: WoW official event announcements
965 struct LunarFestivalTestCase { int year; int month; int day; };
966 std::vector<LunarFestivalTestCase> testCases = {
967 { 2024, 2, 9 }, // CNY Feb 10 - 1 = Feb 9
968 { 2025, 1, 28 }, // CNY Jan 29 - 1 = Jan 28 (confirmed official)
969 { 2026, 2, 16 }, // CNY Feb 17 - 1 = Feb 16
970 { 2027, 2, 5 }, // CNY Feb 6 - 1 = Feb 5
971 };
972
973 HolidayRule lunarFestival = { 327, HolidayCalculationType::LUNAR_NEW_YEAR, 0, 0, 0, -1 };
974
975 for (auto const& tc : testCases)
976 {
977 std::tm date = HolidayDateCalculator::CalculateHolidayDate(lunarFestival, tc.year);
978 SCOPED_TRACE("Year: " + std::to_string(tc.year));
979 ExpectDate(date, tc.year, tc.month, tc.day);
980 }
981}

References HolidayDateCalculator::CalculateHolidayDate(), and LUNAR_NEW_YEAR.

◆ TEST_F() [38/55]

TEST_F ( HolidayDateCalculatorTest  ,
LunarNewYear_19YearMetonicCycle   
)
1010{
1011 // The Chinese calendar roughly follows a 19-year Metonic cycle
1012 // Dates should approximately repeat every 19 years (within a few days)
1013 for (int year = 1900; year <= 2180; ++year)
1014 {
1016 std::tm date2 = HolidayDateCalculator::CalculateLunarNewYear(year + 19);
1017
1018 SCOPED_TRACE("Comparing year " + std::to_string(year) + " with " + std::to_string(year + 19));
1019
1020 // Convert to day-of-year for easier comparison
1021 int doy1 = (date1.tm_mon == 0) ? date1.tm_mday : 31 + date1.tm_mday;
1022 int doy2 = (date2.tm_mon == 0) ? date2.tm_mday : 31 + date2.tm_mday;
1023
1024 // The Metonic cycle is approximate - typically within a few days, but can shift
1025 // by up to a lunar month (~29 days) at cycle boundaries due to intercalary months
1026 int diff = std::abs(doy1 - doy2);
1027 EXPECT_LE(diff, 30) << "19-year Metonic cycle should keep dates within one lunar month";
1028 }
1029}

References HolidayDateCalculator::CalculateLunarNewYear().

◆ TEST_F() [39/55]

TEST_F ( HolidayDateCalculatorTest  ,
LunarNewYear_KnownDates   
)
851{
852 // Verify against known Chinese New Year dates
853 // Source: Official astronomical calculations and historical records
854 struct LunarNewYearTestCase { int year; int month; int day; };
855 std::vector<LunarNewYearTestCase> testCases = {
856 // Historical dates (2000-2010)
857 { 2000, 2, 5 },
858 { 2001, 1, 24 },
859 { 2002, 2, 12 },
860 { 2003, 2, 1 },
861 { 2004, 1, 22 },
862 { 2005, 2, 9 },
863 { 2006, 1, 29 },
864 { 2007, 2, 18 },
865 { 2008, 2, 7 },
866 { 2009, 1, 26 },
867 { 2010, 2, 14 },
868 // Recent dates (2011-2020)
869 { 2011, 2, 3 },
870 { 2012, 1, 23 },
871 { 2013, 2, 10 },
872 { 2014, 1, 31 },
873 { 2015, 2, 19 },
874 { 2016, 2, 8 },
875 { 2017, 1, 28 },
876 { 2018, 2, 16 },
877 { 2019, 2, 5 },
878 { 2020, 1, 25 },
879 // Current and near-future dates (2021-2031)
880 { 2021, 2, 12 },
881 { 2022, 2, 1 },
882 { 2023, 1, 22 },
883 { 2024, 2, 10 },
884 { 2025, 1, 29 },
885 { 2026, 2, 17 },
886 { 2027, 2, 6 },
887 { 2028, 1, 26 },
888 { 2029, 2, 13 },
889 { 2030, 2, 3 },
890 { 2031, 1, 23 },
891 };
892
893 for (auto const& tc : testCases)
894 {
895 std::tm lunarNewYear = HolidayDateCalculator::CalculateLunarNewYear(tc.year);
896 SCOPED_TRACE("Year: " + std::to_string(tc.year));
897 ExpectDate(lunarNewYear, tc.year, tc.month, tc.day);
898 }
899}

References HolidayDateCalculator::CalculateLunarNewYear().

◆ TEST_F() [40/55]

TEST_F ( HolidayDateCalculatorTest  ,
LunarNewYear_NoRepeatedDates   
)
984{
985 // Each year should have a unique Chinese New Year date
986 // (no two consecutive years should have the exact same month/day)
987 int prevMonth = -1;
988 int prevDay = -1;
989
990 for (int year = 1900; year <= 2200; ++year)
991 {
992 std::tm lunarNewYear = HolidayDateCalculator::CalculateLunarNewYear(year);
993
994 if (prevMonth != -1)
995 {
996 // The date should be different from previous year
997 // (due to ~11 day lunar cycle drift)
998 bool sameDate = (lunarNewYear.tm_mon == prevMonth && lunarNewYear.tm_mday == prevDay);
999 EXPECT_FALSE(sameDate)
1000 << "Year " << year << " has same date as previous year: "
1001 << (lunarNewYear.tm_mon + 1) << "/" << lunarNewYear.tm_mday;
1002 }
1003
1004 prevMonth = lunarNewYear.tm_mon;
1005 prevDay = lunarNewYear.tm_mday;
1006 }
1007}

References HolidayDateCalculator::CalculateLunarNewYear().

◆ TEST_F() [41/55]

TEST_F ( HolidayDateCalculatorTest  ,
LunarNewYear_ValidDateRange_1900_2200   
)
902{
903 // Chinese New Year must always fall between January 21 and February 20 (inclusive)
904 // This is a fundamental property of the lunisolar calendar
905 for (int year = 1900; year <= 2200; ++year)
906 {
907 std::tm lunarNewYear = HolidayDateCalculator::CalculateLunarNewYear(year);
908
909 SCOPED_TRACE("Year: " + std::to_string(year));
910
911 // Verify year is correct
912 EXPECT_EQ(lunarNewYear.tm_year + 1900, year);
913
914 // Chinese New Year must be in January or February
915 EXPECT_TRUE(lunarNewYear.tm_mon == 0 || lunarNewYear.tm_mon == 1)
916 << "Lunar New Year must be in January (0) or February (1), got month " << lunarNewYear.tm_mon;
917
918 // Valid range: January 21 - February 20
919 if (lunarNewYear.tm_mon == 0) // January
920 {
921 EXPECT_GE(lunarNewYear.tm_mday, 21) << "Lunar New Year in January must be >= 21";
922 EXPECT_LE(lunarNewYear.tm_mday, 31) << "Lunar New Year in January must be <= 31";
923 }
924 else // February
925 {
926 EXPECT_GE(lunarNewYear.tm_mday, 1) << "Lunar New Year in February must be >= 1";
927 EXPECT_LE(lunarNewYear.tm_mday, 20) << "Lunar New Year in February must be <= 20";
928 }
929
930 // Verify it's a valid calendar date
931 EXPECT_TRUE(IsValidDate(year, lunarNewYear.tm_mon + 1, lunarNewYear.tm_mday))
932 << "Lunar New Year should be a valid calendar date";
933 }
934}

References HolidayDateCalculator::CalculateLunarNewYear().

◆ TEST_F() [42/55]

TEST_F ( HolidayDateCalculatorTest  ,
Noblegarden_DayAfterEaster_1900_2200   
)
344{
345 // Noblegarden should be Easter + 1 day (Monday after Easter) for all years
346 for (int year = 1900; year <= 2200; ++year)
347 {
349
350 // Calculate expected Noblegarden date (Easter + 1)
351 std::tm expectedNoblegarden = easter;
352 expectedNoblegarden.tm_mday += 1;
353 NormalizeTm(expectedNoblegarden); // Normalize (handles month rollover)
354
355 // Get calculated Noblegarden from holiday rule
356 HolidayRule noblegarden = { 181, HolidayCalculationType::EASTER_OFFSET, 0, 0, 0, 1 };
357 std::tm calculated = HolidayDateCalculator::CalculateHolidayDate(noblegarden, year);
358
359 SCOPED_TRACE("Year: " + std::to_string(year));
360
361 EXPECT_EQ(calculated.tm_year, expectedNoblegarden.tm_year);
362 EXPECT_EQ(calculated.tm_mon, expectedNoblegarden.tm_mon);
363 EXPECT_EQ(calculated.tm_mday, expectedNoblegarden.tm_mday);
364
365 // Noblegarden should be Monday (1 day after Easter Sunday)
366 EXPECT_EQ(calculated.tm_wday, 1) << "Noblegarden should be Monday";
367 }
368}

References HolidayDateCalculator::CalculateEasterSunday(), HolidayDateCalculator::CalculateHolidayDate(), and EASTER_OFFSET.

◆ TEST_F() [43/55]

TEST_F ( HolidayDateCalculatorTest  ,
NthWeekday_AllWeekdays_1900_2200   
)
215{
216 // Test first occurrence of each weekday in January for all years
217 for (int year = 1900; year <= 2200; ++year)
218 {
219 for (int weekday = 0; weekday <= 6; ++weekday)
220 {
221 std::tm date = HolidayDateCalculator::CalculateNthWeekday(year, 1, static_cast<Weekday>(weekday), 1);
222
223 SCOPED_TRACE("Year: " + std::to_string(year) + " Weekday: " + std::to_string(weekday));
224
225 // Must be in January
226 EXPECT_EQ(date.tm_mon + 1, 1);
227
228 // Must be the correct weekday
229 EXPECT_EQ(date.tm_wday, weekday);
230
231 // First occurrence must be within first 7 days
232 EXPECT_GE(date.tm_mday, 1);
233 EXPECT_LE(date.tm_mday, 7);
234 }
235 }
236}
Weekday
Definition HolidayDateCalculator.h:38

References HolidayDateCalculator::CalculateNthWeekday().

◆ TEST_F() [44/55]

TEST_F ( HolidayDateCalculatorTest  ,
NthWeekday_SecondThirdFourth_Validation   
)
239{
240 // Verify 2nd, 3rd, 4th occurrences are exactly 7 days apart
241 for (int year = 2000; year <= 2100; ++year)
242 {
243 for (int month = 1; month <= 12; ++month)
244 {
245 std::tm first = HolidayDateCalculator::CalculateNthWeekday(year, month, Weekday::MONDAY, 1);
246 std::tm second = HolidayDateCalculator::CalculateNthWeekday(year, month, Weekday::MONDAY, 2);
247 std::tm third = HolidayDateCalculator::CalculateNthWeekday(year, month, Weekday::MONDAY, 3);
248 std::tm fourth = HolidayDateCalculator::CalculateNthWeekday(year, month, Weekday::MONDAY, 4);
249
250 SCOPED_TRACE("Year: " + std::to_string(year) + " Month: " + std::to_string(month));
251
252 EXPECT_EQ(second.tm_mday - first.tm_mday, 7);
253 EXPECT_EQ(third.tm_mday - second.tm_mday, 7);
254 EXPECT_EQ(fourth.tm_mday - third.tm_mday, 7);
255 }
256 }
257}

References HolidayDateCalculator::CalculateNthWeekday(), and MONDAY.

◆ TEST_F() [45/55]

TEST_F ( HolidayDateCalculatorTest  ,
NthWeekday_Thanksgiving_1900_2200   
)
188{
189 // Verify 4th Thursday of November for all years
190 for (int year = 1900; year <= 2200; ++year)
191 {
193
194 SCOPED_TRACE("Year: " + std::to_string(year));
195
196 // Must be in November
197 EXPECT_EQ(date.tm_mon + 1, 11);
198
199 // Must be a Thursday
200 EXPECT_EQ(date.tm_wday, static_cast<int>(Weekday::THURSDAY));
201
202 // 4th Thursday must be between 22nd and 28th
203 EXPECT_GE(date.tm_mday, 22);
204 EXPECT_LE(date.tm_mday, 28);
205
206 // Verify it's actually the 4th occurrence
207 // First Thursday can be 1-7, so 4th is (first + 21) which gives range 22-28
208 int firstThursday = date.tm_mday - 21;
209 EXPECT_GE(firstThursday, 1);
210 EXPECT_LE(firstThursday, 7);
211 }
212}

References HolidayDateCalculator::CalculateNthWeekday(), and THURSDAY.

◆ TEST_F() [46/55]

TEST_F ( HolidayDateCalculatorTest  ,
PackDate_RoundTrip   
)
265{
266 // Test that pack/unpack preserves date information correctly
267 struct PackTestCase { int year; int month; int day; };
268 std::vector<PackTestCase> testCases = {
269 { 2000, 1, 1 }, // Min year in pack range
270 { 2000, 12, 31 }, // End of min year
271 { 2015, 6, 15 }, // Mid range
272 { 2020, 1, 24 }, // Lunar Festival 2020
273 { 2025, 11, 27 }, // Thanksgiving 2025
274 { 2030, 4, 28 }, // Noblegarden 2030
275 { 2031, 12, 31 }, // Max year in pack range
276 };
277
278 for (auto const& tc : testCases)
279 {
280 std::tm date = {};
281 date.tm_year = tc.year - 1900;
282 date.tm_mon = tc.month - 1;
283 date.tm_mday = tc.day;
284 mktime(&date);
285
286 uint32_t packed = HolidayDateCalculator::PackDate(date);
287 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
288
289 SCOPED_TRACE("Date: " + std::to_string(tc.year) + "-" +
290 std::to_string(tc.month) + "-" + std::to_string(tc.day));
291 ExpectDate(unpacked, tc.year, tc.month, tc.day);
292 }
293}
static uint32_t PackDate(std::tm const &date)
Definition HolidayDateCalculator.cpp:575

References HolidayDateCalculator::PackDate(), and HolidayDateCalculator::UnpackDate().

◆ TEST_F() [47/55]

TEST_F ( HolidayDateCalculatorTest  ,
PackDate_YearBeyond2031   
)
765{
766 // WoW's packed date format uses 5 bits for year offset from 2000
767 // - Offsets 0-30 represent specific years 2000-2030
768 // - Offset 31 is a special marker meaning "repeats every year" (used for fixed-date holidays)
769 // This is a Blizzard client limitation, not an emulator design choice
770 //
771 // Years beyond 2031 will overflow when unpacked due to the 5-bit mask
772
773 // Test year 2032 (offset 32) - demonstrates the overflow behavior
774 {
775 std::tm date = {};
776 date.tm_year = 2032 - 1900;
777 date.tm_mon = 5; // June
778 date.tm_mday = 15;
779 mktime(&date);
780
781 uint32_t packed = HolidayDateCalculator::PackDate(date);
782 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
783
784 // Year offset 32 masked with 0x1F (5 bits) = 0, so unpacked year = 2000
785 // This documents the WoW client's inherent year 2031 limitation
786 EXPECT_EQ(unpacked.tm_year + 1900, 2000) << "Year 2032 wraps to 2000 due to 5-bit WoW client format";
787 EXPECT_EQ(unpacked.tm_mon + 1, 6);
788 EXPECT_EQ(unpacked.tm_mday, 15);
789 }
790
791 // Test year 2035 (offset 35)
792 {
793 std::tm date = {};
794 date.tm_year = 2035 - 1900;
795 date.tm_mon = 0; // January
796 date.tm_mday = 1;
797 mktime(&date);
798
799 uint32_t packed = HolidayDateCalculator::PackDate(date);
800 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
801
802 // Year offset 35 masked with 0x1F = 3, so unpacked year = 2003
803 EXPECT_EQ(unpacked.tm_year + 1900, 2003) << "Year 2035 wraps to 2003 due to 5-bit WoW client format";
804 }
805
806 // Test boundary: year 2030 is the last fully usable year (offset 30)
807 // (offset 31 is reserved for "repeating yearly" holidays)
808 {
809 std::tm date = {};
810 date.tm_year = 2030 - 1900;
811 date.tm_mon = 11; // December
812 date.tm_mday = 31;
813 mktime(&date);
814
815 uint32_t packed = HolidayDateCalculator::PackDate(date);
816 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
817
818 EXPECT_EQ(unpacked.tm_year + 1900, 2030) << "Year 2030 should pack/unpack correctly";
819 EXPECT_EQ(unpacked.tm_mon + 1, 12);
820 EXPECT_EQ(unpacked.tm_mday, 31);
821 }
822}

References HolidayDateCalculator::PackDate(), and HolidayDateCalculator::UnpackDate().

◆ TEST_F() [48/55]

TEST_F ( HolidayDateCalculatorTest  ,
PackUnpack_Roundtrip_FullRange   
)
296{
297 // Test pack/unpack for entire valid range (2000-2031)
298 for (int year = 2000; year <= 2031; ++year)
299 {
300 for (int month = 1; month <= 12; ++month)
301 {
302 for (int day = 1; day <= 28; ++day) // Safe range for all months
303 {
304 std::tm original = {};
305 original.tm_year = year - 1900;
306 original.tm_mon = month - 1;
307 original.tm_mday = day;
308 mktime(&original);
309
310 uint32_t packed = HolidayDateCalculator::PackDate(original);
311 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
312
313 EXPECT_EQ(original.tm_year, unpacked.tm_year);
314 EXPECT_EQ(original.tm_mon, unpacked.tm_mon);
315 EXPECT_EQ(original.tm_mday, unpacked.tm_mday);
316 }
317 }
318 }
319}

References HolidayDateCalculator::PackDate(), and HolidayDateCalculator::UnpackDate().

◆ TEST_F() [49/55]

TEST_F ( HolidayDateCalculatorTest  ,
PilgrimsBounty_SundayBeforeThanksgiving_1900_2200   
)
375{
376 // Pilgrim's Bounty = Sunday before Thanksgiving (4th Thursday - 4 days)
377 for (int year = 1900; year <= 2200; ++year)
378 {
379 // Calculate 4th Thursday of November
380 std::tm thanksgiving = HolidayDateCalculator::CalculateNthWeekday(year, 11, Weekday::THURSDAY, 4);
381
382 // Pilgrim's Bounty starts on Sunday before (4 days earlier)
383 std::tm expectedPilgrims = thanksgiving;
384 expectedPilgrims.tm_mday -= 4;
385 NormalizeTm(expectedPilgrims);
386
387 // Get calculated date using rule with -4 offset
388 HolidayRule pilgrimsBounty = { 404, HolidayCalculationType::NTH_WEEKDAY, 11, 4, static_cast<int>(Weekday::THURSDAY), -4 };
389 std::tm date = HolidayDateCalculator::CalculateHolidayDate(pilgrimsBounty, year);
390
391 SCOPED_TRACE("Year: " + std::to_string(year));
392
393 EXPECT_EQ(date.tm_year + 1900, year);
394 EXPECT_EQ(date.tm_mon + 1, 11); // November
395 EXPECT_EQ(date.tm_wday, 0); // Sunday
396 EXPECT_EQ(date.tm_mday, expectedPilgrims.tm_mday);
397
398 // Sunday before 4th Thursday should be between 18th and 24th
399 EXPECT_GE(date.tm_mday, 18);
400 EXPECT_LE(date.tm_mday, 24);
401 }
402}

References HolidayDateCalculator::CalculateHolidayDate(), HolidayDateCalculator::CalculateNthWeekday(), NTH_WEEKDAY, and THURSDAY.

◆ TEST_F() [50/55]

TEST_F ( HolidayDateCalculatorTest  ,
StressTest_AllCalculations_1900_2200   
)
688{
689 // Run all holiday calculations for entire range to ensure no crashes
690 std::vector<HolidayRule> const& rules = HolidayDateCalculator::GetHolidayRules();
691
692 int totalCalculations = 0;
693
694 for (int year = 1900; year <= 2200; ++year)
695 {
696 // Test Easter
698 EXPECT_TRUE(IsValidDate(year, easter.tm_mon + 1, easter.tm_mday))
699 << "Invalid Easter date for year " << year;
700 totalCalculations++;
701
702 // Test all weekday calculations
703 for (int month = 1; month <= 12; ++month)
704 {
705 for (int n = 1; n <= 4; ++n)
706 {
708 EXPECT_TRUE(IsValidDate(year, date.tm_mon + 1, date.tm_mday))
709 << "Invalid NthWeekday date for year " << year << " month " << month << " n=" << n;
710 totalCalculations++;
711 }
712 }
713
714 // Test all holiday rules
715 for (auto const& rule : rules)
716 {
717 std::tm date = HolidayDateCalculator::CalculateHolidayDate(rule, year);
718 EXPECT_TRUE(IsValidDate(year, date.tm_mon + 1, date.tm_mday))
719 << "Invalid holiday date for holiday " << rule.holidayId << " year " << year;
720 totalCalculations++;
721 }
722 }
723
724 // Verify we ran a significant number of calculations
725 // 301 years * (1 Easter + 48 NthWeekday + 6 holidays) = 301 * 55 = 16555
726 EXPECT_GT(totalCalculations, 15000) << "Should have run many calculations";
727}

References HolidayDateCalculator::CalculateEasterSunday(), HolidayDateCalculator::CalculateHolidayDate(), HolidayDateCalculator::CalculateNthWeekday(), HolidayDateCalculator::GetHolidayRules(), and THURSDAY.

◆ TEST_F() [51/55]

TEST_F ( HolidayDateCalculatorTest  ,
UnpackDate_KnownValues   
)
322{
323 struct UnpackTestCase { uint32_t packed; int year; int month; int day; };
324 std::vector<UnpackTestCase> testCases = {
325 { 335921152, 2020, 1, 24 },
326 { 352681984, 2021, 1, 23 },
327 { 336707584, 2020, 2, 8 },
328 { 346390592, 2020, 11, 23 },
329 };
330
331 for (auto const& tc : testCases)
332 {
333 std::tm date = HolidayDateCalculator::UnpackDate(tc.packed);
334 SCOPED_TRACE("Packed: " + std::to_string(tc.packed));
335 ExpectDate(date, tc.year, tc.month, tc.day);
336 }
337}

References HolidayDateCalculator::UnpackDate().

◆ TEST_F() [52/55]

TEST_F ( HolidayDateCalculatorTest  ,
WeekdayOnOrAfter_AlwaysCorrectWeekday_1900_2200   
)
627{
628 // Verify the result is always the correct weekday for entire range
629 for (int year = 1900; year <= 2200; ++year)
630 {
631 for (int weekday = 0; weekday <= 6; ++weekday)
632 {
633 std::tm date = HolidayDateCalculator::CalculateWeekdayOnOrAfter(year, 2, 3, static_cast<Weekday>(weekday));
634
635 SCOPED_TRACE("Year: " + std::to_string(year) + " Weekday: " + std::to_string(weekday));
636
637 EXPECT_EQ(date.tm_wday, weekday);
638 EXPECT_EQ(date.tm_mon + 1, 2); // Should stay in February
639 EXPECT_GE(date.tm_mday, 3); // Should be on or after Feb 3
640 EXPECT_LE(date.tm_mday, 9); // At most 6 days later
641 }
642 }
643}

References HolidayDateCalculator::CalculateWeekdayOnOrAfter().

◆ TEST_F() [53/55]

TEST_F ( HolidayDateCalculatorTest  ,
WeekdayOnOrAfter_MonthBoundary_RollsIntoNextMonth   
)
646{
647 // Test dates near month-end that may roll into the next month
648 // Apr 25 looking for Monday can roll into May (e.g., if Apr 25 is Sunday, Monday is May 1)
649
650 for (int year = 1900; year <= 2200; ++year)
651 {
652 // Test Apr 25 (Children's Week reference date)
654 SCOPED_TRACE("Apr 25, Year: " + std::to_string(year));
655 EXPECT_EQ(apr25.tm_wday, 1); // Monday
656 EXPECT_TRUE(apr25.tm_mon == 3 || apr25.tm_mon == 4); // April or May (0-indexed: 3 or 4)
657 // If still in April, must be >= 25. If in May, can be 1-6.
658 if (apr25.tm_mon == 3)
659 EXPECT_GE(apr25.tm_mday, 25);
660 else
661 EXPECT_LE(apr25.tm_mday, 6);
662
663 // Test Apr 30 - more likely to roll into May
665 SCOPED_TRACE("Apr 30, Year: " + std::to_string(year));
666 EXPECT_EQ(apr30.tm_wday, 1); // Monday
667 EXPECT_TRUE(apr30.tm_mon == 3 || apr30.tm_mon == 4); // April or May
668 if (apr30.tm_mon == 3)
669 EXPECT_EQ(apr30.tm_mday, 30); // Apr 30 must be the Monday
670 else
671 EXPECT_LE(apr30.tm_mday, 6); // May 1-6
672
673 // Test Dec 31 - can roll into January of next year
675 SCOPED_TRACE("Dec 31, Year: " + std::to_string(year));
676 EXPECT_EQ(dec31.tm_wday, 1); // Monday
677 // Could be Dec 31 or Jan 1-6 of next year
678 EXPECT_TRUE((dec31.tm_mon == 11 && dec31.tm_mday == 31) ||
679 (dec31.tm_mon == 0 && dec31.tm_mday <= 6));
680 }
681}

References HolidayDateCalculator::CalculateWeekdayOnOrAfter(), and MONDAY.

◆ TEST_F() [54/55]

TEST_F ( HolidayDateCalculatorTest  ,
WinterVeil_AlwaysInDecember_1900_2200   
)
553{
554 HolidayRule winterVeil = { 141, HolidayCalculationType::WINTER_SOLSTICE, 0, 0, 0, -6 };
555
556 for (int year = 1900; year <= 2200; ++year)
557 {
558 std::tm date = HolidayDateCalculator::CalculateHolidayDate(winterVeil, year);
559
560 SCOPED_TRACE("Year: " + std::to_string(year));
561
562 // Winter Veil should always be in December
563 EXPECT_EQ(date.tm_mon + 1, 12) << "Winter Veil should be in December";
564 // Should be around Dec 15-16 (6 days before Dec 21-22)
565 EXPECT_GE(date.tm_mday, 14) << "Winter Veil should be >= Dec 14";
566 EXPECT_LE(date.tm_mday, 17) << "Winter Veil should be <= Dec 17";
567 }
568}

References HolidayDateCalculator::CalculateHolidayDate(), and WINTER_SOLSTICE.

◆ TEST_F() [55/55]

TEST_F ( HolidayDateCalculatorTest  ,
WinterVeil_WinterSolsticeBased   
)
532{
533 HolidayRule winterVeil = { 141, HolidayCalculationType::WINTER_SOLSTICE, 0, 0, 0, -6 };
534
535 // Winter solstice is typically Dec 21-22, so Winter Veil starts Dec 15-16
536 for (int year = 2000; year <= 2030; ++year)
537 {
538 std::tm solstice = HolidayDateCalculator::CalculateWinterSolstice(year);
539 std::tm winterVeilDate = HolidayDateCalculator::CalculateHolidayDate(winterVeil, year);
540
541 SCOPED_TRACE("Year: " + std::to_string(year));
542
543 // Winter Veil should be exactly 6 days before solstice
544 time_t solsticeTime = mktime(&solstice);
545 time_t winterVeilTime = mktime(&winterVeilDate);
546
547 double diffDays = difftime(solsticeTime, winterVeilTime) / (60 * 60 * 24);
548 EXPECT_NEAR(diffDays, 6.0, 0.1) << "Winter Veil should be 6 days before solstice";
549 }
550}
static std::tm CalculateWinterSolstice(int year)
Definition HolidayDateCalculator.cpp:433

References HolidayDateCalculator::CalculateHolidayDate(), HolidayDateCalculator::CalculateWinterSolstice(), and WINTER_SOLSTICE.