AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
SpellProcDBCValidationTest.cpp File Reference

Unit tests for validating spell_proc entries against Spell.dbc. More...

#include "SpellProcTestData.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <map>
#include <set>

Go to the source code of this file.

Classes

class  SpellProcDBCValidationTest
 
class  SpellProcDBCValidationParamTest
 

Functions

 TEST_P (SpellProcDBCValidationParamTest, EntryHasValidSpellId)
 
 INSTANTIATE_TEST_SUITE_P (AllSpellProcEntries, SpellProcDBCValidationParamTest, ::testing::ValuesIn(GetAllSpellProcTestEntries()), [](const testing::TestParamInfo< SpellProcTestEntry > &info) { int32_t id=info.param.SpellId;if(id< 0) return "SpellId_N"+std::to_string(-id);return "SpellId_"+std::to_string(id);})
 
 TEST_F (SpellProcDBCValidationTest, CountEntriesWithDBCData)
 
 TEST_F (SpellProcDBCValidationTest, CountEntriesAddingValue)
 
 TEST_F (SpellProcDBCValidationTest, CategorizeEntriesByFeature)
 
 TEST_F (SpellProcDBCValidationTest, IdentifyDBCOverrides)
 
 TEST_F (SpellProcDBCValidationTest, CountNegativeSpellIds)
 
 TEST_F (SpellProcDBCValidationTest, CoverageBySpellFamily)
 
 TEST_F (SpellProcDBCValidationTest, NoDuplicateSpellIds)
 
 TEST_F (SpellProcDBCValidationTest, AllEntriesHaveValidStructure)
 

Detailed Description

Unit tests for validating spell_proc entries against Spell.dbc.

Tests validate that spell_proc entries provide value beyond DBC defaults:

  • Entries that override DBC ProcFlags/ProcChance/ProcCharges
  • Entries that add new functionality (PPM, cooldowns, filtering)
  • Identification of potentially redundant entries

DBC DATA POPULATION STATUS

The DBC_ProcFlags, DBC_ProcChance, and DBC_ProcCharges fields in SpellProcTestEntry are currently populated with zeros (0, 0, 0) for all entries. To fully validate spell_proc entries against DBC:

  1. Use the generate_spell_proc_dbc_data.py script with MCP connection
  2. Or manually query: get_spell_dbc_proc_info(spell_id) for each spell

Tests that require DBC data will check HasDBCData() and skip appropriately. Once DBC data is populated, the statistics tests will show:

  • How many entries override DBC defaults
  • How many entries add new functionality not in DBC

- How many entries might be redundant (just duplicate DBC values)

Definition in file SpellProcDBCValidationTest.cpp.

Function Documentation

◆ INSTANTIATE_TEST_SUITE_P()

INSTANTIATE_TEST_SUITE_P ( AllSpellProcEntries  ,
SpellProcDBCValidationParamTest  ,
::testing::ValuesIn(GetAllSpellProcTestEntries())  ,
[] (const testing::TestParamInfo< SpellProcTestEntry > &info) { int32_t id=info.param.SpellId;if(id< 0) return "SpellId_N"+std::to_string(-id);return "SpellId_"+std::to_string(id);}   
)

◆ TEST_F() [1/8]

TEST_F ( SpellProcDBCValidationTest  ,
AllEntriesHaveValidStructure   
)
340{
341 for (auto const& entry : _allEntries)
342 {
343 // SpellId must be non-zero
344 EXPECT_NE(entry.SpellId, 0)
345 << "SpellId cannot be zero";
346
347 // If Chance is set, it should be reasonable (0-100, or 101 for 100% from DBC)
348 if (entry.Chance > 0)
349 {
350 EXPECT_LE(entry.Chance, 101.0f)
351 << "SpellId " << entry.SpellId << " has unusual Chance: " << entry.Chance;
352 }
353
354 // PPM should be reasonable (typically 0-60)
355 if (entry.ProcsPerMinute > 0)
356 {
357 EXPECT_LE(entry.ProcsPerMinute, 60.0f)
358 << "SpellId " << entry.SpellId << " has unusual PPM: " << entry.ProcsPerMinute;
359 }
360
361 // SpellPhaseMask should use valid values
362 if (entry.SpellPhaseMask != 0)
363 {
364 // Valid phase masks: PROC_SPELL_PHASE_CAST(1), PROC_SPELL_PHASE_HIT(2), PROC_SPELL_PHASE_FINISH(4)
365 EXPECT_LE(entry.SpellPhaseMask, 7u)
366 << "SpellId " << entry.SpellId << " has unusual SpellPhaseMask: " << entry.SpellPhaseMask;
367 }
368 }
369}

◆ TEST_F() [2/8]

TEST_F ( SpellProcDBCValidationTest  ,
CategorizeEntriesByFeature   
)
165{
166 size_t hasPPM = 0;
167 size_t hasCooldown = 0;
168 size_t hasSpellTypeMask = 0;
169 size_t hasSpellPhaseMask = 0;
170 size_t hasHitMask = 0;
171 size_t hasAttributesMask = 0;
172 size_t hasSpellFamilyMask = 0;
173 size_t hasSchoolMask = 0;
174 size_t hasCharges = 0;
175 size_t hasDisableEffectsMask = 0;
176
177 for (auto const& entry : _allEntries)
178 {
179 if (entry.ProcsPerMinute > 0) hasPPM++;
180 if (entry.Cooldown > 0) hasCooldown++;
181 if (entry.SpellTypeMask != 0) hasSpellTypeMask++;
182 if (entry.SpellPhaseMask != 0) hasSpellPhaseMask++;
183 if (entry.HitMask != 0) hasHitMask++;
184 if (entry.AttributesMask != 0) hasAttributesMask++;
185 if (entry.SpellFamilyMask0 != 0 || entry.SpellFamilyMask1 != 0 || entry.SpellFamilyMask2 != 0)
186 hasSpellFamilyMask++;
187 if (entry.SchoolMask != 0) hasSchoolMask++;
188 if (entry.Charges > 0) hasCharges++;
189 if (entry.DisableEffectsMask != 0) hasDisableEffectsMask++;
190 }
191
192 std::cout << "[ INFO ] Feature usage (adds value beyond DBC):\n"
193 << " PPM: " << hasPPM << "\n"
194 << " Cooldown: " << hasCooldown << "\n"
195 << " SpellTypeMask: " << hasSpellTypeMask << "\n"
196 << " SpellPhaseMask: " << hasSpellPhaseMask << "\n"
197 << " HitMask: " << hasHitMask << "\n"
198 << " AttributesMask: " << hasAttributesMask << "\n"
199 << " SpellFamilyMask: " << hasSpellFamilyMask << "\n"
200 << " SchoolMask: " << hasSchoolMask << "\n"
201 << " Charges: " << hasCharges << "\n"
202 << " DisableEffectsMask: " << hasDisableEffectsMask << std::endl;
203
204 // Most entries should use at least one extended feature
205 size_t usingExtendedFeatures = 0;
206 for (auto const& entry : _allEntries)
207 {
208 if (entry.ProcsPerMinute > 0 || entry.Cooldown > 0 ||
209 entry.SpellTypeMask != 0 || entry.SpellPhaseMask != 0 ||
210 entry.HitMask != 0 || entry.AttributesMask != 0 ||
211 entry.SpellFamilyMask0 != 0 || entry.SpellFamilyMask1 != 0 ||
212 entry.SpellFamilyMask2 != 0 || entry.SchoolMask != 0 ||
213 entry.DisableEffectsMask != 0)
214 {
215 usingExtendedFeatures++;
216 }
217 }
218
219 std::cout << "[ INFO ] Entries using extended features: " << usingExtendedFeatures
220 << " / " << _allEntries.size() << std::endl;
221
222 // At least 80% should use extended features
223 EXPECT_GT(usingExtendedFeatures, _allEntries.size() * 80 / 100)
224 << "Most entries should use extended features";
225}

◆ TEST_F() [3/8]

TEST_F ( SpellProcDBCValidationTest  ,
CountEntriesAddingValue   
)
129{
130 size_t addsValue = 0;
131 size_t potentiallyRedundant = 0;
132 size_t noDBCYet = 0;
133
134 for (auto const& entry : _allEntries)
135 {
136 // SKIP REASON: Entries without DBC data populated cannot be compared
137 // against DBC defaults. The HasDBCData() check returns false when
138 // DBC_ProcFlags, DBC_ProcChance, and DBC_ProcCharges are all zero.
139 // Once DBC data is populated via MCP tools, this count should be 0.
140 if (!entry.HasDBCData())
141 {
142 noDBCYet++;
143 continue;
144 }
145
146 if (entry.AddsValueBeyondDBC())
147 addsValue++;
148 else
149 potentiallyRedundant++;
150 }
151
152 std::cout << "[ INFO ] Entries adding value: " << addsValue << "\n";
153 std::cout << "[ INFO ] Potentially redundant: " << potentiallyRedundant << "\n";
154 std::cout << "[ INFO ] DBC data not yet populated: " << noDBCYet << std::endl;
155
156 // Most entries should add value (have PPM, cooldowns, filtering, etc.)
157 if (addsValue + potentiallyRedundant > 0)
158 {
159 float valueRate = static_cast<float>(addsValue) / (addsValue + potentiallyRedundant) * 100;
160 std::cout << "[ INFO ] Value-add rate: " << valueRate << "%" << std::endl;
161 }
162}

◆ TEST_F() [4/8]

TEST_F ( SpellProcDBCValidationTest  ,
CountEntriesWithDBCData   
)
108{
109 size_t withDBC = 0;
110 size_t withoutDBC = 0;
111
112 for (auto const& entry : _allEntries)
113 {
114 if (entry.HasDBCData())
115 withDBC++;
116 else
117 withoutDBC++;
118 }
119
120 std::cout << "[ INFO ] Entries with DBC data: " << withDBC << "\n";
121 std::cout << "[ INFO ] Entries without DBC data: " << withoutDBC << std::endl;
122
123 // All entries should eventually have DBC data
124 // For now, just verify the count
125 EXPECT_EQ(_allEntries.size(), 869u);
126}

◆ TEST_F() [5/8]

TEST_F ( SpellProcDBCValidationTest  ,
CountNegativeSpellIds   
)
262{
263 size_t negativeIds = 0;
264 size_t positiveIds = 0;
265
266 for (auto const& entry : _allEntries)
267 {
268 if (entry.SpellId < 0)
269 negativeIds++;
270 else
271 positiveIds++;
272 }
273
274 std::cout << "[ INFO ] Negative SpellIds (effect-specific): " << negativeIds << "\n";
275 std::cout << "[ INFO ] Positive SpellIds: " << positiveIds << std::endl;
276
277 // Both types should exist
278 EXPECT_GT(negativeIds, 0u) << "Should have some effect-specific (negative ID) entries";
279 EXPECT_GT(positiveIds, 0u) << "Should have some spell-wide (positive ID) entries";
280}

◆ TEST_F() [6/8]

TEST_F ( SpellProcDBCValidationTest  ,
CoverageBySpellFamily   
)
287{
288 std::map<uint32_t, size_t> familyCounts;
289 std::map<uint32_t, std::string> familyNames = {
290 {0, "Generic"}, {3, "Mage"}, {4, "Warrior"}, {5, "Warlock"},
291 {6, "Priest"}, {7, "Druid"}, {8, "Rogue"}, {9, "Hunter"},
292 {10, "Paladin"}, {11, "Shaman"}, {15, "DeathKnight"}
293 };
294
295 for (auto const& entry : _allEntries)
296 {
297 familyCounts[entry.SpellFamilyName]++;
298 }
299
300 std::cout << "[ INFO ] Entries by SpellFamily:\n";
301 for (auto const& [family, count] : familyCounts)
302 {
303 std::string name = familyNames.count(family) ? familyNames[family] : "Unknown";
304 std::cout << " " << name << " (" << family << "): " << count << "\n";
305 }
306
307 // Should have entries from multiple spell families
308 EXPECT_GT(familyCounts.size(), 5u) << "Should cover multiple spell families";
309}

◆ TEST_F() [7/8]

TEST_F ( SpellProcDBCValidationTest  ,
IdentifyDBCOverrides   
)
228{
229 size_t overridesProcFlags = 0;
230 size_t overridesChance = 0;
231 size_t overridesCharges = 0;
232
233 for (auto const& entry : _allEntries)
234 {
235 // SKIP REASON: Cannot compare against DBC defaults when DBC data
236 // is not populated. All 869 entries currently have DBC fields = 0.
237 // Once populated, this loop will count actual DBC overrides.
238 if (!entry.HasDBCData())
239 continue;
240
241 if (entry.ProcFlags != 0 && entry.ProcFlags != entry.DBC_ProcFlags)
242 overridesProcFlags++;
243
244 if (entry.Chance != 0 && static_cast<uint32_t>(entry.Chance) != entry.DBC_ProcChance)
245 overridesChance++;
246
247 if (entry.Charges != 0 && entry.Charges != entry.DBC_ProcCharges)
248 overridesCharges++;
249 }
250
251 std::cout << "[ INFO ] DBC Overrides:\n"
252 << " ProcFlags: " << overridesProcFlags << "\n"
253 << " Chance: " << overridesChance << "\n"
254 << " Charges: " << overridesCharges << std::endl;
255}

◆ TEST_F() [8/8]

TEST_F ( SpellProcDBCValidationTest  ,
NoDuplicateSpellIds   
)
316{
317 std::set<int32_t> seenIds;
318 std::vector<int32_t> duplicates;
319
320 for (auto const& entry : _allEntries)
321 {
322 if (seenIds.count(entry.SpellId))
323 duplicates.push_back(entry.SpellId);
324 else
325 seenIds.insert(entry.SpellId);
326 }
327
328 if (!duplicates.empty())
329 {
330 std::cout << "[ WARN ] Duplicate SpellIds found: ";
331 for (auto id : duplicates)
332 std::cout << id << " ";
333 std::cout << std::endl;
334 }
335
336 EXPECT_TRUE(duplicates.empty()) << "Should have no duplicate SpellIds";
337}
STL namespace.

◆ TEST_P()

TEST_P ( SpellProcDBCValidationParamTest  ,
EntryHasValidSpellId   
)
78{
79 auto const& entry = GetParam();
80 int32_t spellId = std::abs(entry.SpellId);
81
82 // Spell ID must be positive after abs
83 EXPECT_GT(spellId, 0) << "SpellId must be non-zero";
84
85 // Spell IDs in WotLK should be < 80000
86 EXPECT_LT(spellId, 80000u)
87 << "SpellId " << spellId << " seems out of range for WotLK";
88}