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
 

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)
 

Function Documentation

◆ TEST_F() [1/43]

TEST_F ( HolidayDateCalculatorTest  ,
Brewfest_FixedSept13   
)
404{
405 // Brewfest is now fixed: prep starts Sept 13, main event Sept 20
406 // This avoids any potential overlap with Pirates' Day (Sept 19)
407 HolidayRule brewfest = { 372, HolidayCalculationType::FIXED_DATE, 9, 13, 0, 0 };
408
409 for (int year = 2000; year <= 2030; ++year)
410 {
411 std::tm date = HolidayDateCalculator::CalculateHolidayDate(brewfest, year);
412
413 SCOPED_TRACE("Year: " + std::to_string(year));
414
415 EXPECT_EQ(date.tm_year + 1900, year);
416 EXPECT_EQ(date.tm_mon + 1, 9); // September
417 EXPECT_EQ(date.tm_mday, 13); // Always Sept 13
418 }
419}
static std::tm CalculateHolidayDate(const HolidayRule &rule, int year)
Definition HolidayDateCalculator.cpp:415
Definition HolidayDateCalculator.h:49

References HolidayDateCalculator::CalculateHolidayDate(), and FIXED_DATE.

◆ TEST_F() [2/43]

TEST_F ( HolidayDateCalculatorTest  ,
Brewfest_NoPiratesDayConflict   
)
422{
423 // Brewfest main event (Sept 20) is always after Pirates' Day (Sept 19)
424 HolidayRule brewfest = { 372, HolidayCalculationType::FIXED_DATE, 9, 13, 0, 0 };
425 HolidayRule piratesDay = { 398, HolidayCalculationType::FIXED_DATE, 9, 19, 0, 0 };
426
427 for (int year = 2000; year <= 2030; ++year)
428 {
429 std::tm brewfestDate = HolidayDateCalculator::CalculateHolidayDate(brewfest, year);
430 std::tm piratesDate = HolidayDateCalculator::CalculateHolidayDate(piratesDay, year);
431
432 SCOPED_TRACE("Year: " + std::to_string(year));
433
434 // Brewfest prep is Sept 13, main event is Sept 20
435 // Pirates' Day is Sept 19, which falls between prep and main event
436 EXPECT_EQ(brewfestDate.tm_mday, 13); // Brewfest prep
437 EXPECT_EQ(piratesDate.tm_mday, 19); // Pirates' Day
438 // Main event (Sept 20) > Pirates' Day (Sept 19)
439 }
440}

References HolidayDateCalculator::CalculateHolidayDate(), and FIXED_DATE.

◆ TEST_F() [3/43]

TEST_F ( HolidayDateCalculatorTest  ,
CenturyBoundaries   
)
786{
787 // Test calculations around century boundaries (which affect leap year rules)
788 std::vector<int> centuryYears = { 1900, 2000, 2100, 2200 };
789
790 for (int year : centuryYears)
791 {
792 SCOPED_TRACE("Century year: " + std::to_string(year));
793
794 // Easter
796 EXPECT_EQ(easter.tm_wday, 0) << "Easter should be Sunday";
797 EXPECT_TRUE(easter.tm_mon == 2 || easter.tm_mon == 3) << "Easter should be in March or April";
798
799 // Thanksgiving
800 std::tm thanksgiving = HolidayDateCalculator::CalculateNthWeekday(year, 11, Weekday::THURSDAY, 4);
801 EXPECT_EQ(thanksgiving.tm_wday, static_cast<int>(Weekday::THURSDAY));
802 EXPECT_EQ(thanksgiving.tm_mon + 1, 11);
803 }
804}
static std::tm CalculateNthWeekday(int year, int month, Weekday weekday, int n)
Definition HolidayDateCalculator.cpp:112
static std::tm CalculateEasterSunday(int year)
Definition HolidayDateCalculator.cpp:84

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

◆ TEST_F() [4/43]

TEST_F ( HolidayDateCalculatorTest  ,
ChildrensWeek_FirstMondayOnOrAfterApr25   
)
563{
564 // Verify "first Monday on or after Apr 25" calculation (Monday closest to May 1)
565 struct ChildrensWeekTestCase { int year; int expectedMonth; int expectedDay; };
566 std::vector<ChildrensWeekTestCase> testCases = {
567 { 2023, 5, 1 }, // Apr 25 is Tue, first Mon after is May 1
568 { 2024, 4, 29 }, // Apr 25 is Thu, first Mon after is Apr 29
569 { 2025, 4, 28 }, // Apr 25 is Fri, first Mon after is Apr 28
570 { 2026, 4, 27 }, // Apr 25 is Sat, first Mon after is Apr 27
571 { 2027, 4, 26 }, // Apr 25 is Sun, first Mon after is Apr 26
572 };
573
574 for (auto const& tc : testCases)
575 {
577
578 SCOPED_TRACE("Year: " + std::to_string(tc.year));
579
580 EXPECT_EQ(date.tm_year + 1900, tc.year);
581 EXPECT_EQ(date.tm_mon + 1, tc.expectedMonth);
582 EXPECT_EQ(date.tm_mday, tc.expectedDay);
583 EXPECT_EQ(date.tm_wday, 1); // Monday
584 }
585}
static std::tm CalculateWeekdayOnOrAfter(int year, int month, int day, Weekday weekday)
Definition HolidayDateCalculator.cpp:132

References HolidayDateCalculator::CalculateWeekdayOnOrAfter(), and MONDAY.

◆ TEST_F() [5/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_AlwaysSunday_AllLocations_2000_2030   
)
1148{
1149 // Verify all Darkmoon Faire dates are Sundays for entire valid range
1150 // 3 locations: offset 0 (Elwynn), offset 1 (Mulgore), offset 2 (Terokkar)
1151 for (int offset = 0; offset <= 2; ++offset)
1152 {
1153 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(offset, 2000, 31);
1154
1155 SCOPED_TRACE("Location offset: " + std::to_string(offset));
1156
1157 for (size_t i = 0; i < dates.size(); ++i)
1158 {
1159 std::tm date = HolidayDateCalculator::UnpackDate(dates[i]);
1160 EXPECT_EQ(date.tm_wday, 0)
1161 << "Date " << (date.tm_year + 1900) << "-" << (date.tm_mon + 1) << "-" << date.tm_mday
1162 << " should be Sunday";
1163 }
1164 }
1165}
static std::vector< uint32_t > GetDarkmoonFaireDates(int locationOffset, int startYear, int numYears, int dayOffset=0)
Definition HolidayDateCalculator.cpp:554
static std::tm UnpackDate(uint32_t packed)
Definition HolidayDateCalculator.cpp:528

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

◆ TEST_F() [6/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_CalculateHolidayDate_ReturnsFirstOccurrence   
)
1168{
1169 // Using CalculateHolidayDate with DARKMOON_FAIRE should return the first occurrence of the year
1170 // Elwynn (offset 0) = Mar/Jun/Sep/Dec, Mulgore (offset 1) = Jan/Apr/Jul/Oct, Terokkar (offset 2) = Feb/May/Aug/Nov
1171 HolidayRule elwynnRule = { 374, HolidayCalculationType::DARKMOON_FAIRE, 0, 0, 0, -2 };
1172 HolidayRule mulgoreRule = { 375, HolidayCalculationType::DARKMOON_FAIRE, 1, 0, 0, -2 };
1173 HolidayRule terokkarRule = { 376, HolidayCalculationType::DARKMOON_FAIRE, 2, 0, 0, -2 };
1174
1175 // 2025 first occurrences:
1176 // Elwynn (offset 0): March (first month where month % 3 == 0)
1177 std::tm elwynn2025 = HolidayDateCalculator::CalculateHolidayDate(elwynnRule, 2025);
1178 EXPECT_EQ(elwynn2025.tm_mon + 1, 3) << "Elwynn first occurrence should be March";
1179 EXPECT_EQ(elwynn2025.tm_wday, 0) << "Should be Sunday";
1180
1181 // Mulgore (offset 1): January (first month where month % 3 == 1)
1182 std::tm mulgore2025 = HolidayDateCalculator::CalculateHolidayDate(mulgoreRule, 2025);
1183 EXPECT_EQ(mulgore2025.tm_mon + 1, 1) << "Mulgore first occurrence should be January";
1184 EXPECT_EQ(mulgore2025.tm_wday, 0) << "Should be Sunday";
1185
1186 // Terokkar (offset 2): February (first month where month % 3 == 2)
1187 std::tm terokkar2025 = HolidayDateCalculator::CalculateHolidayDate(terokkarRule, 2025);
1188 EXPECT_EQ(terokkar2025.tm_mon + 1, 2) << "Terokkar first occurrence should be February";
1189 EXPECT_EQ(terokkar2025.tm_wday, 0) << "Should be Sunday";
1190}

References HolidayDateCalculator::CalculateHolidayDate(), and DARKMOON_FAIRE.

◆ TEST_F() [7/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_FirstSundayOfMonth_KnownDates   
)
1024{
1025 // Verify first Sunday calculation against known dates
1026 struct FirstSundayTestCase { int year; int month; int expectedDay; };
1027 std::vector<FirstSundayTestCase> testCases = {
1028 // 2024
1029 { 2024, 1, 7 }, // Jan 2024: First Sunday = Jan 7
1030 { 2024, 2, 4 }, // Feb 2024: First Sunday = Feb 4
1031 { 2024, 3, 3 }, // Mar 2024: First Sunday = Mar 3
1032 { 2024, 4, 7 }, // Apr 2024: First Sunday = Apr 7
1033 { 2024, 9, 1 }, // Sep 2024: First Sunday = Sep 1
1034 { 2024, 12, 1 }, // Dec 2024: First Sunday = Dec 1
1035 // 2025
1036 { 2025, 1, 5 }, // Jan 2025: First Sunday = Jan 5
1037 { 2025, 2, 2 }, // Feb 2025: First Sunday = Feb 2
1038 { 2025, 3, 2 }, // Mar 2025: First Sunday = Mar 2
1039 { 2025, 6, 1 }, // Jun 2025: First Sunday = Jun 1
1040 { 2025, 9, 7 }, // Sep 2025: First Sunday = Sep 7
1041 { 2025, 12, 7 }, // Dec 2025: First Sunday = Dec 7
1042 // 2026
1043 { 2026, 1, 4 }, // Jan 2026: First Sunday = Jan 4
1044 { 2026, 3, 1 }, // Mar 2026: First Sunday = Mar 1
1045 };
1046
1047 for (auto const& tc : testCases)
1048 {
1049 std::tm date = HolidayDateCalculator::CalculateNthWeekday(tc.year, tc.month, Weekday::SUNDAY, 1);
1050
1051 SCOPED_TRACE("Year: " + std::to_string(tc.year) + " Month: " + std::to_string(tc.month));
1052
1053 EXPECT_EQ(date.tm_year + 1900, tc.year);
1054 EXPECT_EQ(date.tm_mon + 1, tc.month);
1055 EXPECT_EQ(date.tm_mday, tc.expectedDay);
1056 EXPECT_EQ(date.tm_wday, 0) << "Should be Sunday";
1057 }
1058}

References HolidayDateCalculator::CalculateNthWeekday(), and SUNDAY.

◆ TEST_F() [8/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_GetDates_Elwynn   
)
1061{
1062 // Elwynn (offset 0): Mar, Jun, Sep, Dec
1063 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(0, 2025, 1);
1064
1065 // Should have exactly 4 dates for one year
1066 EXPECT_EQ(dates.size(), 4u);
1067
1068 // Verify each date is in the correct month and is a Sunday
1069 std::vector<int> expectedMonths = { 3, 6, 9, 12 };
1070 for (size_t i = 0; i < dates.size(); ++i)
1071 {
1072 std::tm date = HolidayDateCalculator::UnpackDate(dates[i]);
1073
1074 SCOPED_TRACE("Date index: " + std::to_string(i));
1075
1076 EXPECT_EQ(date.tm_year + 1900, 2025);
1077 EXPECT_EQ(date.tm_mon + 1, expectedMonths[i]);
1078 EXPECT_EQ(date.tm_wday, 0) << "Should be Sunday";
1079 EXPECT_GE(date.tm_mday, 1);
1080 EXPECT_LE(date.tm_mday, 7) << "First Sunday must be within first 7 days";
1081 }
1082}

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

◆ TEST_F() [9/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_GetDates_Mulgore   
)
1085{
1086 // Mulgore (offset 1): Jan, Apr, Jul, Oct
1087 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(1, 2025, 1);
1088
1089 // Should have exactly 4 dates for one year
1090 EXPECT_EQ(dates.size(), 4u);
1091
1092 std::vector<int> expectedMonths = { 1, 4, 7, 10 };
1093 for (size_t i = 0; i < dates.size(); ++i)
1094 {
1095 std::tm date = HolidayDateCalculator::UnpackDate(dates[i]);
1096
1097 SCOPED_TRACE("Date index: " + std::to_string(i));
1098
1099 EXPECT_EQ(date.tm_year + 1900, 2025);
1100 EXPECT_EQ(date.tm_mon + 1, expectedMonths[i]);
1101 EXPECT_EQ(date.tm_wday, 0) << "Should be Sunday";
1102 }
1103}

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

◆ TEST_F() [10/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_GetDates_MultiYear   
)
1127{
1128 // Get 4 years of dates for Elwynn (offset 0)
1129 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(0, 2025, 4);
1130
1131 // 4 dates per year * 4 years = 16 dates
1132 EXPECT_EQ(dates.size(), 16u);
1133
1134 // Verify dates are in chronological order
1135 for (size_t i = 1; i < dates.size(); ++i)
1136 {
1137 std::tm prev = HolidayDateCalculator::UnpackDate(dates[i - 1]);
1138 std::tm curr = HolidayDateCalculator::UnpackDate(dates[i]);
1139
1140 time_t prevTime = mktime(&prev);
1141 time_t currTime = mktime(&curr);
1142
1143 EXPECT_GT(currTime, prevTime) << "Dates should be in chronological order";
1144 }
1145}

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

◆ TEST_F() [11/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_GetDates_Terokkar   
)
1106{
1107 // Terokkar (offset 2): Feb, May, Aug, Nov
1108 std::vector<uint32_t> dates = HolidayDateCalculator::GetDarkmoonFaireDates(2, 2025, 1);
1109
1110 // Should have exactly 4 dates for one year
1111 EXPECT_EQ(dates.size(), 4u);
1112
1113 std::vector<int> expectedMonths = { 2, 5, 8, 11 };
1114 for (size_t i = 0; i < dates.size(); ++i)
1115 {
1116 std::tm date = HolidayDateCalculator::UnpackDate(dates[i]);
1117
1118 SCOPED_TRACE("Date index: " + std::to_string(i));
1119
1120 EXPECT_EQ(date.tm_year + 1900, 2025);
1121 EXPECT_EQ(date.tm_mon + 1, expectedMonths[i]);
1122 EXPECT_EQ(date.tm_wday, 0) << "Should be Sunday";
1123 }
1124}

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

◆ TEST_F() [12/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_InHolidayRules   
)
1220{
1221 // Verify all three Darkmoon Faire locations are in the HolidayRules
1222 auto const& rules = HolidayDateCalculator::GetHolidayRules();
1223
1224 bool foundElwynn = false, foundMulgore = false, foundTerokkar = false;
1225 for (auto const& rule : rules)
1226 {
1227 if (rule.holidayId == 374 && rule.type == HolidayCalculationType::DARKMOON_FAIRE)
1228 foundElwynn = true;
1229 if (rule.holidayId == 375 && rule.type == HolidayCalculationType::DARKMOON_FAIRE)
1230 foundMulgore = true;
1231 if (rule.holidayId == 376 && rule.type == HolidayCalculationType::DARKMOON_FAIRE)
1232 foundTerokkar = true;
1233 }
1234
1235 EXPECT_TRUE(foundElwynn) << "Darkmoon Faire Elwynn (374) should be in HolidayRules";
1236 EXPECT_TRUE(foundMulgore) << "Darkmoon Faire Mulgore (375) should be in HolidayRules";
1237 EXPECT_TRUE(foundTerokkar) << "Darkmoon Faire Terokkar (376) should be in HolidayRules";
1238}
static const std::vector< HolidayRule > & GetHolidayRules()
Definition HolidayDateCalculator.cpp:79

References DARKMOON_FAIRE, and HolidayDateCalculator::GetHolidayRules().

◆ TEST_F() [13/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_LocationRotation   
)
998{
999 // Verify the 3-location rotation pattern:
1000 // Mulgore (offset 1): Jan, Apr, Jul, Oct - month % 3 == 1
1001 // Terokkar (offset 2): Feb, May, Aug, Nov - month % 3 == 2
1002 // Elwynn (offset 0): Mar, Jun, Sep, Dec - month % 3 == 0
1003
1004 // Mulgore months (offset 1)
1005 EXPECT_EQ(1 % 3, 1);
1006 EXPECT_EQ(4 % 3, 1);
1007 EXPECT_EQ(7 % 3, 1);
1008 EXPECT_EQ(10 % 3, 1);
1009
1010 // Terokkar months (offset 2)
1011 EXPECT_EQ(2 % 3, 2);
1012 EXPECT_EQ(5 % 3, 2);
1013 EXPECT_EQ(8 % 3, 2);
1014 EXPECT_EQ(11 % 3, 2);
1015
1016 // Elwynn months (offset 0)
1017 EXPECT_EQ(3 % 3, 0);
1018 EXPECT_EQ(6 % 3, 0);
1019 EXPECT_EQ(9 % 3, 0);
1020 EXPECT_EQ(12 % 3, 0);
1021}

◆ TEST_F() [14/43]

TEST_F ( HolidayDateCalculatorTest  ,
DarkmoonFaire_NoOverlap_AllLocations   
)
1193{
1194 // Verify all three locations don't share dates (they're in different months)
1195 for (int year = 2000; year <= 2030; ++year)
1196 {
1197 std::vector<uint32_t> elwynn = HolidayDateCalculator::GetDarkmoonFaireDates(0, year, 1); // Mar/Jun/Sep/Dec
1198 std::vector<uint32_t> mulgore = HolidayDateCalculator::GetDarkmoonFaireDates(1, year, 1); // Jan/Apr/Jul/Oct
1199 std::vector<uint32_t> terokkar = HolidayDateCalculator::GetDarkmoonFaireDates(2, year, 1); // Feb/May/Aug/Nov
1200
1201 SCOPED_TRACE("Year: " + std::to_string(year));
1202
1203 // Check no overlap between any locations
1204 for (auto const& e : elwynn)
1205 {
1206 for (auto const& m : mulgore)
1207 EXPECT_NE(e, m) << "Elwynn and Mulgore should not share dates";
1208 for (auto const& t : terokkar)
1209 EXPECT_NE(e, t) << "Elwynn and Terokkar should not share dates";
1210 }
1211 for (auto const& m : mulgore)
1212 {
1213 for (auto const& t : terokkar)
1214 EXPECT_NE(m, t) << "Mulgore and Terokkar should not share dates";
1215 }
1216 }
1217}

References HolidayDateCalculator::GetDarkmoonFaireDates().

◆ TEST_F() [15/43]

TEST_F ( HolidayDateCalculatorTest  ,
EasterSunday_AlwaysSunday_1900_2200   
)
135{
136 // Verify Easter is always on Sunday for entire range
137 for (int year = 1900; year <= 2200; ++year)
138 {
140 EXPECT_EQ(easter.tm_wday, 0) << "Easter " << year << " should be Sunday";
141 }
142}

References HolidayDateCalculator::CalculateEasterSunday().

◆ TEST_F() [16/43]

TEST_F ( HolidayDateCalculatorTest  ,
EasterSunday_KnownDates   
)
62{
63 // Verify against known Easter dates
64 // Source: https://www.census.gov/data/software/x13as/genhol/easter-dates.html
65 struct EasterTestCase { int year; int month; int day; };
66 std::vector<EasterTestCase> testCases = {
67 // Historical dates
68 { 1900, 4, 15 },
69 { 1901, 4, 7 },
70 { 1950, 4, 9 },
71 { 1999, 4, 4 },
72 // Recent dates
73 { 2000, 4, 23 },
74 { 2010, 4, 4 },
75 { 2020, 4, 12 },
76 { 2021, 4, 4 },
77 { 2022, 4, 17 },
78 { 2023, 4, 9 },
79 { 2024, 3, 31 },
80 { 2025, 4, 20 },
81 { 2026, 4, 5 },
82 { 2027, 3, 28 },
83 { 2028, 4, 16 },
84 { 2029, 4, 1 },
85 { 2030, 4, 21 },
86 // Future dates
87 { 2050, 4, 10 },
88 { 2100, 3, 28 },
89 { 2150, 4, 12 },
90 { 2200, 4, 6 },
91 };
92
93 for (auto const& tc : testCases)
94 {
95 std::tm easter = HolidayDateCalculator::CalculateEasterSunday(tc.year);
96 SCOPED_TRACE("Year: " + std::to_string(tc.year));
97 ExpectDate(easter, tc.year, tc.month, tc.day);
98 }
99}

References HolidayDateCalculator::CalculateEasterSunday().

◆ TEST_F() [17/43]

TEST_F ( HolidayDateCalculatorTest  ,
EasterSunday_ValidDateRange_1900_2200   
)
102{
103 // Easter must always fall between March 22 and April 25 (inclusive)
104 for (int year = 1900; year <= 2200; ++year)
105 {
107
108 SCOPED_TRACE("Year: " + std::to_string(year));
109
110 // Verify year is correct
111 EXPECT_EQ(easter.tm_year + 1900, year);
112
113 // Easter must be in March or April
114 EXPECT_TRUE(easter.tm_mon == 2 || easter.tm_mon == 3)
115 << "Easter must be in March (2) or April (3), got month " << easter.tm_mon;
116
117 // Easter range: March 22 - April 25
118 if (easter.tm_mon == 2) // March
119 {
120 EXPECT_GE(easter.tm_mday, 22) << "Easter in March must be >= 22";
121 EXPECT_LE(easter.tm_mday, 31) << "Easter in March must be <= 31";
122 }
123 else // April
124 {
125 EXPECT_GE(easter.tm_mday, 1) << "Easter in April must be >= 1";
126 EXPECT_LE(easter.tm_mday, 25) << "Easter in April must be <= 25";
127 }
128
129 // Easter must be a Sunday
130 EXPECT_EQ(easter.tm_wday, 0) << "Easter must be a Sunday";
131 }
132}

References HolidayDateCalculator::CalculateEasterSunday().

◆ TEST_F() [18/43]

TEST_F ( HolidayDateCalculatorTest  ,
FixedDateHolidays_ConsistentAcrossYears_1900_2200   
)
370{
371 // Fixed date holidays should have same month/day every year
372 // Note: Brewfest, Harvest Festival, and Winter Veil are now dynamic (not fixed date)
373 struct FixedHolidayTestCase { uint32_t holidayId; int month; int day; const char* name; };
374 std::vector<FixedHolidayTestCase> testCases = {
375 { 341, 6, 21, "Midsummer Fire Festival" },
376 { 62, 7, 4, "Fireworks Spectacular" },
377 { 398, 9, 19, "Pirates' Day" },
378 { 324, 10, 18, "Hallow's End" },
379 { 409, 11, 1, "Day of the Dead" },
380 };
381
382 for (auto const& tc : testCases)
383 {
384 HolidayRule rule = { tc.holidayId, HolidayCalculationType::FIXED_DATE, tc.month, tc.day, 0, 0 };
385
386 for (int year = 1900; year <= 2200; ++year)
387 {
388 std::tm date = HolidayDateCalculator::CalculateHolidayDate(rule, year);
389
390 SCOPED_TRACE(std::string(tc.name) + " Year: " + std::to_string(year));
391
392 EXPECT_EQ(date.tm_year + 1900, year);
393 EXPECT_EQ(date.tm_mon + 1, tc.month);
394 EXPECT_EQ(date.tm_mday, tc.day);
395 }
396 }
397}
uint32_t holidayId
Definition HolidayDateCalculator.h:50

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

◆ TEST_F() [19/43]

TEST_F ( HolidayDateCalculatorTest  ,
GetPackedHolidayDate_UnknownHoliday   
)
719{
720 // Unknown holiday ID should return 0
721 uint32_t result = HolidayDateCalculator::GetPackedHolidayDate(99999, 2025);
722 EXPECT_EQ(result, 0u);
723}
static uint32_t GetPackedHolidayDate(uint32_t holidayId, int year)
Definition HolidayDateCalculator.cpp:541

References HolidayDateCalculator::GetPackedHolidayDate().

◆ TEST_F() [20/43]

TEST_F ( HolidayDateCalculatorTest  ,
HarvestFestival_AlwaysInSeptember_1900_2200   
)
470{
471 HolidayRule harvestFestival = { 321, HolidayCalculationType::AUTUMN_EQUINOX, 0, 0, 0, -2 };
472
473 for (int year = 1900; year <= 2200; ++year)
474 {
475 std::tm date = HolidayDateCalculator::CalculateHolidayDate(harvestFestival, year);
476
477 SCOPED_TRACE("Year: " + std::to_string(year));
478
479 // Harvest Festival should always be in September (2 days before Sept 22-23 equinox)
480 EXPECT_EQ(date.tm_mon + 1, 9) << "Harvest Festival should be in September";
481 // Should be around Sept 20-21
482 EXPECT_GE(date.tm_mday, 18) << "Harvest Festival should be >= Sept 18";
483 EXPECT_LE(date.tm_mday, 22) << "Harvest Festival should be <= Sept 22";
484 }
485}

References AUTUMN_EQUINOX, and HolidayDateCalculator::CalculateHolidayDate().

◆ TEST_F() [21/43]

TEST_F ( HolidayDateCalculatorTest  ,
HarvestFestival_AutumnEquinoxBased   
)
448{
449 HolidayRule harvestFestival = { 321, HolidayCalculationType::AUTUMN_EQUINOX, 0, 0, 0, -2 };
450
451 // Autumn equinox is typically Sept 22-23, so Harvest Festival is Sept 20-21
452 for (int year = 2000; year <= 2030; ++year)
453 {
454 std::tm equinox = HolidayDateCalculator::CalculateAutumnEquinox(year);
455 std::tm harvest = HolidayDateCalculator::CalculateHolidayDate(harvestFestival, year);
456
457 SCOPED_TRACE("Year: " + std::to_string(year));
458
459 // Harvest should be exactly 2 days before equinox
460 // Convert to time_t to handle month boundaries correctly
461 time_t equinoxTime = mktime(&equinox);
462 time_t harvestTime = mktime(&harvest);
463
464 double diffDays = difftime(equinoxTime, harvestTime) / (60 * 60 * 24);
465 EXPECT_NEAR(diffDays, 2.0, 0.1) << "Harvest Festival should be 2 days before equinox";
466 }
467}
static std::tm CalculateAutumnEquinox(int year)
Definition HolidayDateCalculator.cpp:311

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

◆ TEST_F() [22/43]

TEST_F ( HolidayDateCalculatorTest  ,
LeapYear_AllYears_1900_2200   
)
695{
696 // Verify calculations work correctly for all years, checking leap year logic
697 for (int year = 1900; year <= 2200; ++year)
698 {
699 bool expectedLeap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
700
701 SCOPED_TRACE("Year: " + std::to_string(year));
702 EXPECT_EQ(IsLeapYear(year), expectedLeap);
703
704 // Easter calculation should always work regardless of leap year
706 EXPECT_EQ(easter.tm_wday, 0) << "Easter should be Sunday";
707
708 // All holiday calculations should produce valid dates
709 for (auto const& rule : HolidayDateCalculator::GetHolidayRules())
710 {
711 std::tm date = HolidayDateCalculator::CalculateHolidayDate(rule, year);
712 EXPECT_TRUE(IsValidDate(year, date.tm_mon + 1, date.tm_mday))
713 << "Invalid date for holiday " << rule.holidayId;
714 }
715 }
716}
Definition HolidayDateCalculator.h:59

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

◆ TEST_F() [23/43]

TEST_F ( HolidayDateCalculatorTest  ,
LoveIsInTheAir_FirstMondayOnOrAfterFeb3   
)
536{
537 // Verify "first Monday on or after Feb 3" calculation
538 struct LoveTestCase { int year; int expectedDay; };
539 std::vector<LoveTestCase> testCases = {
540 { 2024, 5 }, // Feb 3 is Sat, first Mon after is Feb 5
541 { 2025, 3 }, // Feb 3 is Mon, so Feb 3
542 { 2026, 9 }, // Feb 3 is Tue, first Mon after is Feb 9
543 { 2027, 8 }, // Feb 3 is Wed, first Mon after is Feb 8
544 { 2028, 7 }, // Feb 3 is Thu, first Mon after is Feb 7
545 { 2029, 5 }, // Feb 3 is Sat, first Mon after is Feb 5
546 { 2030, 4 }, // Feb 3 is Sun, first Mon after is Feb 4
547 };
548
549 for (auto const& tc : testCases)
550 {
552
553 SCOPED_TRACE("Year: " + std::to_string(tc.year));
554
555 EXPECT_EQ(date.tm_year + 1900, tc.year);
556 EXPECT_EQ(date.tm_mon + 1, 2); // February
557 EXPECT_EQ(date.tm_mday, tc.expectedDay);
558 EXPECT_EQ(date.tm_wday, 1); // Monday
559 }
560}

References HolidayDateCalculator::CalculateWeekdayOnOrAfter(), and MONDAY.

◆ TEST_F() [24/43]

TEST_F ( HolidayDateCalculatorTest  ,
LunarFestival_DayBeforeChineseNewYear   
)
898{
899 // Lunar Festival starts 1 day before Chinese New Year
900 // Test with -1 offset applied
901 HolidayRule lunarFestival = { 327, HolidayCalculationType::LUNAR_NEW_YEAR, 0, 0, 0, -1 };
902
903 for (int year = 2000; year <= 2100; ++year)
904 {
905 std::tm fromRule = HolidayDateCalculator::CalculateHolidayDate(lunarFestival, year);
907
908 // Expected: CNY - 1 day
909 std::tm expected = cny;
910 expected.tm_mday -= 1;
911 mktime(&expected);
912
913 SCOPED_TRACE("Year: " + std::to_string(year));
914
915 // Lunar Festival should be 1 day before Chinese New Year
916 EXPECT_EQ(fromRule.tm_year, expected.tm_year);
917 EXPECT_EQ(fromRule.tm_mon, expected.tm_mon);
918 EXPECT_EQ(fromRule.tm_mday, expected.tm_mday);
919 }
920}
static std::tm CalculateLunarNewYear(int year)
Definition HolidayDateCalculator.cpp:261

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

◆ TEST_F() [25/43]

TEST_F ( HolidayDateCalculatorTest  ,
LunarFestival_KnownDates   
)
923{
924 // Verify Lunar Festival (CNY - 1 day) against known official dates
925 // Source: WoW official event announcements
926 struct LunarFestivalTestCase { int year; int month; int day; };
927 std::vector<LunarFestivalTestCase> testCases = {
928 { 2024, 2, 9 }, // CNY Feb 10 - 1 = Feb 9
929 { 2025, 1, 28 }, // CNY Jan 29 - 1 = Jan 28 (confirmed official)
930 { 2026, 2, 16 }, // CNY Feb 17 - 1 = Feb 16
931 { 2027, 2, 5 }, // CNY Feb 6 - 1 = Feb 5
932 };
933
934 HolidayRule lunarFestival = { 327, HolidayCalculationType::LUNAR_NEW_YEAR, 0, 0, 0, -1 };
935
936 for (auto const& tc : testCases)
937 {
938 std::tm date = HolidayDateCalculator::CalculateHolidayDate(lunarFestival, tc.year);
939 SCOPED_TRACE("Year: " + std::to_string(tc.year));
940 ExpectDate(date, tc.year, tc.month, tc.day);
941 }
942}

References HolidayDateCalculator::CalculateHolidayDate(), and LUNAR_NEW_YEAR.

◆ TEST_F() [26/43]

TEST_F ( HolidayDateCalculatorTest  ,
LunarNewYear_19YearMetonicCycle   
)
971{
972 // The Chinese calendar roughly follows a 19-year Metonic cycle
973 // Dates should approximately repeat every 19 years (within a few days)
974 for (int year = 1900; year <= 2180; ++year)
975 {
977 std::tm date2 = HolidayDateCalculator::CalculateLunarNewYear(year + 19);
978
979 SCOPED_TRACE("Comparing year " + std::to_string(year) + " with " + std::to_string(year + 19));
980
981 // Convert to day-of-year for easier comparison
982 int doy1 = (date1.tm_mon == 0) ? date1.tm_mday : 31 + date1.tm_mday;
983 int doy2 = (date2.tm_mon == 0) ? date2.tm_mday : 31 + date2.tm_mday;
984
985 // The Metonic cycle is approximate - typically within a few days, but can shift
986 // by up to a lunar month (~29 days) at cycle boundaries due to intercalary months
987 int diff = std::abs(doy1 - doy2);
988 EXPECT_LE(diff, 30) << "19-year Metonic cycle should keep dates within one lunar month";
989 }
990}

References HolidayDateCalculator::CalculateLunarNewYear().

◆ TEST_F() [27/43]

TEST_F ( HolidayDateCalculatorTest  ,
LunarNewYear_KnownDates   
)
812{
813 // Verify against known Chinese New Year dates
814 // Source: Official astronomical calculations and historical records
815 struct LunarNewYearTestCase { int year; int month; int day; };
816 std::vector<LunarNewYearTestCase> testCases = {
817 // Historical dates (2000-2010)
818 { 2000, 2, 5 },
819 { 2001, 1, 24 },
820 { 2002, 2, 12 },
821 { 2003, 2, 1 },
822 { 2004, 1, 22 },
823 { 2005, 2, 9 },
824 { 2006, 1, 29 },
825 { 2007, 2, 18 },
826 { 2008, 2, 7 },
827 { 2009, 1, 26 },
828 { 2010, 2, 14 },
829 // Recent dates (2011-2020)
830 { 2011, 2, 3 },
831 { 2012, 1, 23 },
832 { 2013, 2, 10 },
833 { 2014, 1, 31 },
834 { 2015, 2, 19 },
835 { 2016, 2, 8 },
836 { 2017, 1, 28 },
837 { 2018, 2, 16 },
838 { 2019, 2, 5 },
839 { 2020, 1, 25 },
840 // Current and near-future dates (2021-2031)
841 { 2021, 2, 12 },
842 { 2022, 2, 1 },
843 { 2023, 1, 22 },
844 { 2024, 2, 10 },
845 { 2025, 1, 29 },
846 { 2026, 2, 17 },
847 { 2027, 2, 6 },
848 { 2028, 1, 26 },
849 { 2029, 2, 13 },
850 { 2030, 2, 3 },
851 { 2031, 1, 23 },
852 };
853
854 for (auto const& tc : testCases)
855 {
856 std::tm lunarNewYear = HolidayDateCalculator::CalculateLunarNewYear(tc.year);
857 SCOPED_TRACE("Year: " + std::to_string(tc.year));
858 ExpectDate(lunarNewYear, tc.year, tc.month, tc.day);
859 }
860}

References HolidayDateCalculator::CalculateLunarNewYear().

◆ TEST_F() [28/43]

TEST_F ( HolidayDateCalculatorTest  ,
LunarNewYear_NoRepeatedDates   
)
945{
946 // Each year should have a unique Chinese New Year date
947 // (no two consecutive years should have the exact same month/day)
948 int prevMonth = -1;
949 int prevDay = -1;
950
951 for (int year = 1900; year <= 2200; ++year)
952 {
953 std::tm lunarNewYear = HolidayDateCalculator::CalculateLunarNewYear(year);
954
955 if (prevMonth != -1)
956 {
957 // The date should be different from previous year
958 // (due to ~11 day lunar cycle drift)
959 bool sameDate = (lunarNewYear.tm_mon == prevMonth && lunarNewYear.tm_mday == prevDay);
960 EXPECT_FALSE(sameDate)
961 << "Year " << year << " has same date as previous year: "
962 << (lunarNewYear.tm_mon + 1) << "/" << lunarNewYear.tm_mday;
963 }
964
965 prevMonth = lunarNewYear.tm_mon;
966 prevDay = lunarNewYear.tm_mday;
967 }
968}

References HolidayDateCalculator::CalculateLunarNewYear().

◆ TEST_F() [29/43]

TEST_F ( HolidayDateCalculatorTest  ,
LunarNewYear_ValidDateRange_1900_2200   
)
863{
864 // Chinese New Year must always fall between January 21 and February 20 (inclusive)
865 // This is a fundamental property of the lunisolar calendar
866 for (int year = 1900; year <= 2200; ++year)
867 {
868 std::tm lunarNewYear = HolidayDateCalculator::CalculateLunarNewYear(year);
869
870 SCOPED_TRACE("Year: " + std::to_string(year));
871
872 // Verify year is correct
873 EXPECT_EQ(lunarNewYear.tm_year + 1900, year);
874
875 // Chinese New Year must be in January or February
876 EXPECT_TRUE(lunarNewYear.tm_mon == 0 || lunarNewYear.tm_mon == 1)
877 << "Lunar New Year must be in January (0) or February (1), got month " << lunarNewYear.tm_mon;
878
879 // Valid range: January 21 - February 20
880 if (lunarNewYear.tm_mon == 0) // January
881 {
882 EXPECT_GE(lunarNewYear.tm_mday, 21) << "Lunar New Year in January must be >= 21";
883 EXPECT_LE(lunarNewYear.tm_mday, 31) << "Lunar New Year in January must be <= 31";
884 }
885 else // February
886 {
887 EXPECT_GE(lunarNewYear.tm_mday, 1) << "Lunar New Year in February must be >= 1";
888 EXPECT_LE(lunarNewYear.tm_mday, 20) << "Lunar New Year in February must be <= 20";
889 }
890
891 // Verify it's a valid calendar date
892 EXPECT_TRUE(IsValidDate(year, lunarNewYear.tm_mon + 1, lunarNewYear.tm_mday))
893 << "Lunar New Year should be a valid calendar date";
894 }
895}

References HolidayDateCalculator::CalculateLunarNewYear().

◆ TEST_F() [30/43]

TEST_F ( HolidayDateCalculatorTest  ,
Noblegarden_DayAfterEaster_1900_2200   
)
305{
306 // Noblegarden should be Easter + 1 day (Monday after Easter) for all years
307 for (int year = 1900; year <= 2200; ++year)
308 {
310
311 // Calculate expected Noblegarden date (Easter + 1)
312 std::tm expectedNoblegarden = easter;
313 expectedNoblegarden.tm_mday += 1;
314 mktime(&expectedNoblegarden); // Normalize (handles month rollover)
315
316 // Get calculated Noblegarden from holiday rule
317 HolidayRule noblegarden = { 181, HolidayCalculationType::EASTER_OFFSET, 0, 0, 0, 1 };
318 std::tm calculated = HolidayDateCalculator::CalculateHolidayDate(noblegarden, year);
319
320 SCOPED_TRACE("Year: " + std::to_string(year));
321
322 EXPECT_EQ(calculated.tm_year, expectedNoblegarden.tm_year);
323 EXPECT_EQ(calculated.tm_mon, expectedNoblegarden.tm_mon);
324 EXPECT_EQ(calculated.tm_mday, expectedNoblegarden.tm_mday);
325
326 // Noblegarden should be Monday (1 day after Easter Sunday)
327 EXPECT_EQ(calculated.tm_wday, 1) << "Noblegarden should be Monday";
328 }
329}

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

◆ TEST_F() [31/43]

TEST_F ( HolidayDateCalculatorTest  ,
NthWeekday_AllWeekdays_1900_2200   
)
176{
177 // Test first occurrence of each weekday in January for all years
178 for (int year = 1900; year <= 2200; ++year)
179 {
180 for (int weekday = 0; weekday <= 6; ++weekday)
181 {
182 std::tm date = HolidayDateCalculator::CalculateNthWeekday(year, 1, static_cast<Weekday>(weekday), 1);
183
184 SCOPED_TRACE("Year: " + std::to_string(year) + " Weekday: " + std::to_string(weekday));
185
186 // Must be in January
187 EXPECT_EQ(date.tm_mon + 1, 1);
188
189 // Must be the correct weekday
190 EXPECT_EQ(date.tm_wday, weekday);
191
192 // First occurrence must be within first 7 days
193 EXPECT_GE(date.tm_mday, 1);
194 EXPECT_LE(date.tm_mday, 7);
195 }
196 }
197}
Weekday
Definition HolidayDateCalculator.h:38

References HolidayDateCalculator::CalculateNthWeekday().

◆ TEST_F() [32/43]

TEST_F ( HolidayDateCalculatorTest  ,
NthWeekday_SecondThirdFourth_Validation   
)
200{
201 // Verify 2nd, 3rd, 4th occurrences are exactly 7 days apart
202 for (int year = 2000; year <= 2100; ++year)
203 {
204 for (int month = 1; month <= 12; ++month)
205 {
206 std::tm first = HolidayDateCalculator::CalculateNthWeekday(year, month, Weekday::MONDAY, 1);
207 std::tm second = HolidayDateCalculator::CalculateNthWeekday(year, month, Weekday::MONDAY, 2);
208 std::tm third = HolidayDateCalculator::CalculateNthWeekday(year, month, Weekday::MONDAY, 3);
209 std::tm fourth = HolidayDateCalculator::CalculateNthWeekday(year, month, Weekday::MONDAY, 4);
210
211 SCOPED_TRACE("Year: " + std::to_string(year) + " Month: " + std::to_string(month));
212
213 EXPECT_EQ(second.tm_mday - first.tm_mday, 7);
214 EXPECT_EQ(third.tm_mday - second.tm_mday, 7);
215 EXPECT_EQ(fourth.tm_mday - third.tm_mday, 7);
216 }
217 }
218}

References HolidayDateCalculator::CalculateNthWeekday(), and MONDAY.

◆ TEST_F() [33/43]

TEST_F ( HolidayDateCalculatorTest  ,
NthWeekday_Thanksgiving_1900_2200   
)
149{
150 // Verify 4th Thursday of November for all years
151 for (int year = 1900; year <= 2200; ++year)
152 {
154
155 SCOPED_TRACE("Year: " + std::to_string(year));
156
157 // Must be in November
158 EXPECT_EQ(date.tm_mon + 1, 11);
159
160 // Must be a Thursday
161 EXPECT_EQ(date.tm_wday, static_cast<int>(Weekday::THURSDAY));
162
163 // 4th Thursday must be between 22nd and 28th
164 EXPECT_GE(date.tm_mday, 22);
165 EXPECT_LE(date.tm_mday, 28);
166
167 // Verify it's actually the 4th occurrence
168 // First Thursday can be 1-7, so 4th is (first + 21) which gives range 22-28
169 int firstThursday = date.tm_mday - 21;
170 EXPECT_GE(firstThursday, 1);
171 EXPECT_LE(firstThursday, 7);
172 }
173}

References HolidayDateCalculator::CalculateNthWeekday(), and THURSDAY.

◆ TEST_F() [34/43]

TEST_F ( HolidayDateCalculatorTest  ,
PackDate_RoundTrip   
)
226{
227 // Test that pack/unpack preserves date information correctly
228 struct PackTestCase { int year; int month; int day; };
229 std::vector<PackTestCase> testCases = {
230 { 2000, 1, 1 }, // Min year in pack range
231 { 2000, 12, 31 }, // End of min year
232 { 2015, 6, 15 }, // Mid range
233 { 2020, 1, 24 }, // Lunar Festival 2020
234 { 2025, 11, 27 }, // Thanksgiving 2025
235 { 2030, 4, 28 }, // Noblegarden 2030
236 { 2031, 12, 31 }, // Max year in pack range
237 };
238
239 for (auto const& tc : testCases)
240 {
241 std::tm date = {};
242 date.tm_year = tc.year - 1900;
243 date.tm_mon = tc.month - 1;
244 date.tm_mday = tc.day;
245 mktime(&date);
246
247 uint32_t packed = HolidayDateCalculator::PackDate(date);
248 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
249
250 SCOPED_TRACE("Date: " + std::to_string(tc.year) + "-" +
251 std::to_string(tc.month) + "-" + std::to_string(tc.day));
252 ExpectDate(unpacked, tc.year, tc.month, tc.day);
253 }
254}
static uint32_t PackDate(const std::tm &date)
Definition HolidayDateCalculator.cpp:508

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

◆ TEST_F() [35/43]

TEST_F ( HolidayDateCalculatorTest  ,
PackDate_YearBeyond2031   
)
726{
727 // WoW's packed date format uses 5 bits for year offset from 2000
728 // - Offsets 0-30 represent specific years 2000-2030
729 // - Offset 31 is a special marker meaning "repeats every year" (used for fixed-date holidays)
730 // This is a Blizzard client limitation, not an emulator design choice
731 //
732 // Years beyond 2031 will overflow when unpacked due to the 5-bit mask
733
734 // Test year 2032 (offset 32) - demonstrates the overflow behavior
735 {
736 std::tm date = {};
737 date.tm_year = 2032 - 1900;
738 date.tm_mon = 5; // June
739 date.tm_mday = 15;
740 mktime(&date);
741
742 uint32_t packed = HolidayDateCalculator::PackDate(date);
743 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
744
745 // Year offset 32 masked with 0x1F (5 bits) = 0, so unpacked year = 2000
746 // This documents the WoW client's inherent year 2031 limitation
747 EXPECT_EQ(unpacked.tm_year + 1900, 2000) << "Year 2032 wraps to 2000 due to 5-bit WoW client format";
748 EXPECT_EQ(unpacked.tm_mon + 1, 6);
749 EXPECT_EQ(unpacked.tm_mday, 15);
750 }
751
752 // Test year 2035 (offset 35)
753 {
754 std::tm date = {};
755 date.tm_year = 2035 - 1900;
756 date.tm_mon = 0; // January
757 date.tm_mday = 1;
758 mktime(&date);
759
760 uint32_t packed = HolidayDateCalculator::PackDate(date);
761 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
762
763 // Year offset 35 masked with 0x1F = 3, so unpacked year = 2003
764 EXPECT_EQ(unpacked.tm_year + 1900, 2003) << "Year 2035 wraps to 2003 due to 5-bit WoW client format";
765 }
766
767 // Test boundary: year 2030 is the last fully usable year (offset 30)
768 // (offset 31 is reserved for "repeating yearly" holidays)
769 {
770 std::tm date = {};
771 date.tm_year = 2030 - 1900;
772 date.tm_mon = 11; // December
773 date.tm_mday = 31;
774 mktime(&date);
775
776 uint32_t packed = HolidayDateCalculator::PackDate(date);
777 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
778
779 EXPECT_EQ(unpacked.tm_year + 1900, 2030) << "Year 2030 should pack/unpack correctly";
780 EXPECT_EQ(unpacked.tm_mon + 1, 12);
781 EXPECT_EQ(unpacked.tm_mday, 31);
782 }
783}

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

◆ TEST_F() [36/43]

TEST_F ( HolidayDateCalculatorTest  ,
PackUnpack_Roundtrip_FullRange   
)
257{
258 // Test pack/unpack for entire valid range (2000-2031)
259 for (int year = 2000; year <= 2031; ++year)
260 {
261 for (int month = 1; month <= 12; ++month)
262 {
263 for (int day = 1; day <= 28; ++day) // Safe range for all months
264 {
265 std::tm original = {};
266 original.tm_year = year - 1900;
267 original.tm_mon = month - 1;
268 original.tm_mday = day;
269 mktime(&original);
270
271 uint32_t packed = HolidayDateCalculator::PackDate(original);
272 std::tm unpacked = HolidayDateCalculator::UnpackDate(packed);
273
274 EXPECT_EQ(original.tm_year, unpacked.tm_year);
275 EXPECT_EQ(original.tm_mon, unpacked.tm_mon);
276 EXPECT_EQ(original.tm_mday, unpacked.tm_mday);
277 }
278 }
279 }
280}

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

◆ TEST_F() [37/43]

TEST_F ( HolidayDateCalculatorTest  ,
PilgrimsBounty_SundayBeforeThanksgiving_1900_2200   
)
336{
337 // Pilgrim's Bounty = Sunday before Thanksgiving (4th Thursday - 4 days)
338 for (int year = 1900; year <= 2200; ++year)
339 {
340 // Calculate 4th Thursday of November
341 std::tm thanksgiving = HolidayDateCalculator::CalculateNthWeekday(year, 11, Weekday::THURSDAY, 4);
342
343 // Pilgrim's Bounty starts on Sunday before (4 days earlier)
344 std::tm expectedPilgrims = thanksgiving;
345 expectedPilgrims.tm_mday -= 4;
346 mktime(&expectedPilgrims);
347
348 // Get calculated date using rule with -4 offset
349 HolidayRule pilgrimsBounty = { 404, HolidayCalculationType::NTH_WEEKDAY, 11, 4, static_cast<int>(Weekday::THURSDAY), -4 };
350 std::tm date = HolidayDateCalculator::CalculateHolidayDate(pilgrimsBounty, year);
351
352 SCOPED_TRACE("Year: " + std::to_string(year));
353
354 EXPECT_EQ(date.tm_year + 1900, year);
355 EXPECT_EQ(date.tm_mon + 1, 11); // November
356 EXPECT_EQ(date.tm_wday, 0); // Sunday
357 EXPECT_EQ(date.tm_mday, expectedPilgrims.tm_mday);
358
359 // Sunday before 4th Thursday should be between 18th and 24th
360 EXPECT_GE(date.tm_mday, 18);
361 EXPECT_LE(date.tm_mday, 24);
362 }
363}

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

◆ TEST_F() [38/43]

TEST_F ( HolidayDateCalculatorTest  ,
StressTest_AllCalculations_1900_2200   
)
649{
650 // Run all holiday calculations for entire range to ensure no crashes
651 const std::vector<HolidayRule>& rules = HolidayDateCalculator::GetHolidayRules();
652
653 int totalCalculations = 0;
654
655 for (int year = 1900; year <= 2200; ++year)
656 {
657 // Test Easter
659 EXPECT_TRUE(IsValidDate(year, easter.tm_mon + 1, easter.tm_mday))
660 << "Invalid Easter date for year " << year;
661 totalCalculations++;
662
663 // Test all weekday calculations
664 for (int month = 1; month <= 12; ++month)
665 {
666 for (int n = 1; n <= 4; ++n)
667 {
669 EXPECT_TRUE(IsValidDate(year, date.tm_mon + 1, date.tm_mday))
670 << "Invalid NthWeekday date for year " << year << " month " << month << " n=" << n;
671 totalCalculations++;
672 }
673 }
674
675 // Test all holiday rules
676 for (auto const& rule : rules)
677 {
678 std::tm date = HolidayDateCalculator::CalculateHolidayDate(rule, year);
679 EXPECT_TRUE(IsValidDate(year, date.tm_mon + 1, date.tm_mday))
680 << "Invalid holiday date for holiday " << rule.holidayId << " year " << year;
681 totalCalculations++;
682 }
683 }
684
685 // Verify we ran a significant number of calculations
686 // 301 years * (1 Easter + 48 NthWeekday + 6 holidays) = 301 * 55 = 16555
687 EXPECT_GT(totalCalculations, 15000) << "Should have run many calculations";
688}

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

◆ TEST_F() [39/43]

TEST_F ( HolidayDateCalculatorTest  ,
UnpackDate_KnownValues   
)
283{
284 struct UnpackTestCase { uint32_t packed; int year; int month; int day; };
285 std::vector<UnpackTestCase> testCases = {
286 { 335921152, 2020, 1, 24 },
287 { 352681984, 2021, 1, 23 },
288 { 336707584, 2020, 2, 8 },
289 { 346390592, 2020, 11, 23 },
290 };
291
292 for (auto const& tc : testCases)
293 {
294 std::tm date = HolidayDateCalculator::UnpackDate(tc.packed);
295 SCOPED_TRACE("Packed: " + std::to_string(tc.packed));
296 ExpectDate(date, tc.year, tc.month, tc.day);
297 }
298}

References HolidayDateCalculator::UnpackDate().

◆ TEST_F() [40/43]

TEST_F ( HolidayDateCalculatorTest  ,
WeekdayOnOrAfter_AlwaysCorrectWeekday_1900_2200   
)
588{
589 // Verify the result is always the correct weekday for entire range
590 for (int year = 1900; year <= 2200; ++year)
591 {
592 for (int weekday = 0; weekday <= 6; ++weekday)
593 {
594 std::tm date = HolidayDateCalculator::CalculateWeekdayOnOrAfter(year, 2, 3, static_cast<Weekday>(weekday));
595
596 SCOPED_TRACE("Year: " + std::to_string(year) + " Weekday: " + std::to_string(weekday));
597
598 EXPECT_EQ(date.tm_wday, weekday);
599 EXPECT_EQ(date.tm_mon + 1, 2); // Should stay in February
600 EXPECT_GE(date.tm_mday, 3); // Should be on or after Feb 3
601 EXPECT_LE(date.tm_mday, 9); // At most 6 days later
602 }
603 }
604}

References HolidayDateCalculator::CalculateWeekdayOnOrAfter().

◆ TEST_F() [41/43]

TEST_F ( HolidayDateCalculatorTest  ,
WeekdayOnOrAfter_MonthBoundary_RollsIntoNextMonth   
)
607{
608 // Test dates near month-end that may roll into the next month
609 // Apr 25 looking for Monday can roll into May (e.g., if Apr 25 is Sunday, Monday is May 1)
610
611 for (int year = 1900; year <= 2200; ++year)
612 {
613 // Test Apr 25 (Children's Week reference date)
615 SCOPED_TRACE("Apr 25, Year: " + std::to_string(year));
616 EXPECT_EQ(apr25.tm_wday, 1); // Monday
617 EXPECT_TRUE(apr25.tm_mon == 3 || apr25.tm_mon == 4); // April or May (0-indexed: 3 or 4)
618 // If still in April, must be >= 25. If in May, can be 1-6.
619 if (apr25.tm_mon == 3)
620 EXPECT_GE(apr25.tm_mday, 25);
621 else
622 EXPECT_LE(apr25.tm_mday, 6);
623
624 // Test Apr 30 - more likely to roll into May
626 SCOPED_TRACE("Apr 30, Year: " + std::to_string(year));
627 EXPECT_EQ(apr30.tm_wday, 1); // Monday
628 EXPECT_TRUE(apr30.tm_mon == 3 || apr30.tm_mon == 4); // April or May
629 if (apr30.tm_mon == 3)
630 EXPECT_EQ(apr30.tm_mday, 30); // Apr 30 must be the Monday
631 else
632 EXPECT_LE(apr30.tm_mday, 6); // May 1-6
633
634 // Test Dec 31 - can roll into January of next year
636 SCOPED_TRACE("Dec 31, Year: " + std::to_string(year));
637 EXPECT_EQ(dec31.tm_wday, 1); // Monday
638 // Could be Dec 31 or Jan 1-6 of next year
639 EXPECT_TRUE((dec31.tm_mon == 11 && dec31.tm_mday == 31) ||
640 (dec31.tm_mon == 0 && dec31.tm_mday <= 6));
641 }
642}

References HolidayDateCalculator::CalculateWeekdayOnOrAfter(), and MONDAY.

◆ TEST_F() [42/43]

TEST_F ( HolidayDateCalculatorTest  ,
WinterVeil_AlwaysInDecember_1900_2200   
)
514{
515 HolidayRule winterVeil = { 141, HolidayCalculationType::WINTER_SOLSTICE, 0, 0, 0, -6 };
516
517 for (int year = 1900; year <= 2200; ++year)
518 {
519 std::tm date = HolidayDateCalculator::CalculateHolidayDate(winterVeil, year);
520
521 SCOPED_TRACE("Year: " + std::to_string(year));
522
523 // Winter Veil should always be in December
524 EXPECT_EQ(date.tm_mon + 1, 12) << "Winter Veil should be in December";
525 // Should be around Dec 15-16 (6 days before Dec 21-22)
526 EXPECT_GE(date.tm_mday, 14) << "Winter Veil should be >= Dec 14";
527 EXPECT_LE(date.tm_mday, 17) << "Winter Veil should be <= Dec 17";
528 }
529}

References HolidayDateCalculator::CalculateHolidayDate(), and WINTER_SOLSTICE.

◆ TEST_F() [43/43]

TEST_F ( HolidayDateCalculatorTest  ,
WinterVeil_WinterSolsticeBased   
)
493{
494 HolidayRule winterVeil = { 141, HolidayCalculationType::WINTER_SOLSTICE, 0, 0, 0, -6 };
495
496 // Winter solstice is typically Dec 21-22, so Winter Veil starts Dec 15-16
497 for (int year = 2000; year <= 2030; ++year)
498 {
499 std::tm solstice = HolidayDateCalculator::CalculateWinterSolstice(year);
500 std::tm winterVeilDate = HolidayDateCalculator::CalculateHolidayDate(winterVeil, year);
501
502 SCOPED_TRACE("Year: " + std::to_string(year));
503
504 // Winter Veil should be exactly 6 days before solstice
505 time_t solsticeTime = mktime(&solstice);
506 time_t winterVeilTime = mktime(&winterVeilDate);
507
508 double diffDays = difftime(solsticeTime, winterVeilTime) / (60 * 60 * 24);
509 EXPECT_NEAR(diffDays, 6.0, 0.1) << "Winter Veil should be 6 days before solstice";
510 }
511}
static std::tm CalculateWinterSolstice(int year)
Definition HolidayDateCalculator.cpp:366

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