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

End-to-end integration tests for the full proc pipeline. More...

#include "ProcChanceTestHelper.h"
#include "ProcEventInfoHelper.h"
#include "AuraStub.h"
#include "UnitStub.h"
#include "gtest/gtest.h"

Go to the source code of this file.

Classes

class  SpellProcPipelineTest
 

Functions

 TEST_F (SpellProcPipelineTest, FullFlow_BasicProc_100Percent)
 
 TEST_F (SpellProcPipelineTest, FullFlow_BasicProc_0Percent)
 
 TEST_F (SpellProcPipelineTest, FullFlow_WithCooldown)
 
 TEST_F (SpellProcPipelineTest, FullFlow_WithCharges)
 
 TEST_F (SpellProcPipelineTest, FullFlow_WithStacks)
 
 TEST_F (SpellProcPipelineTest, Combined_ChargesAndCooldown)
 
 TEST_F (SpellProcPipelineTest, Combined_PPM_AndCooldown)
 
 TEST_F (SpellProcPipelineTest, Combined_Level60Reduction_WithCooldown)
 
 TEST_F (SpellProcPipelineTest, Scenario_OmenOfClarity)
 
 TEST_F (SpellProcPipelineTest, Scenario_LeaderOfThePack)
 
 TEST_F (SpellProcPipelineTest, Scenario_ArtOfWar)
 
 TEST_F (SpellProcPipelineTest, Scenario_LightningShield)
 
 TEST_F (SpellProcPipelineTest, Scenario_WanderingPlague)
 
 TEST_F (SpellProcPipelineTest, EdgeCase_NoAura_NoProcPossible)
 
 TEST_F (SpellProcPipelineTest, EdgeCase_ZeroCooldown_AllowsRapidProcs)
 
 TEST_F (SpellProcPipelineTest, EdgeCase_VeryLongCooldown)
 
 TEST_F (SpellProcPipelineTest, EdgeCase_ManyCharges)
 
 TEST_F (SpellProcPipelineTest, ActorLevel_AffectsProcChance)
 
 TEST_F (SpellProcPipelineTest, WeaponSpeed_AffectsPPMChance)
 

Detailed Description

End-to-end integration tests for the full proc pipeline.

Tests the complete proc execution flow:

  1. Cooldown check (IsProcOnCooldown)
  2. Chance calculation (CalcProcChance)
  3. Roll check (rand_chance)
  4. Cooldown application
  5. Charge consumption (ConsumeProcCharges)

Definition in file SpellProcPipelineTest.cpp.

Function Documentation

◆ TEST_F() [1/19]

TEST_F ( SpellProcPipelineTest  ,
ActorLevel_AffectsProcChance   
)
417{
418 _scenario->WithAura(12345);
419 _scenario->WithActorLevel(60);
420
421 auto procEntry = SpellProcEntryBuilder()
422 .WithChance(30.0f)
424 .Build();
425
426 // At level 60, full 30% chance
427 // Roll of 25 should pass
428 EXPECT_TRUE(_scenario->SimulateProc(procEntry, 25.0f));
429
430 // Reset
431 _scenario->GetAura()->ResetProcCooldown();
432
433 // Change to level 80
434 _scenario->WithActorLevel(80);
435
436 // At level 80, only 10% chance
437 // Roll of 25 should fail
438 EXPECT_FALSE(_scenario->SimulateProc(procEntry, 25.0f));
439}
@ PROC_ATTR_REDUCE_PROC_60
Definition SpellMgr.h:281
Builder class for creating SpellProcEntry test instances.
Definition ProcEventInfoHelper.h:141
SpellProcEntry Build() const
Definition ProcEventInfoHelper.h:226
SpellProcEntryBuilder & WithChance(float chance)
Definition ProcEventInfoHelper.h:208
SpellProcEntryBuilder & WithAttributesMask(uint32 attributesMask)
Definition ProcEventInfoHelper.h:190

References SpellProcEntryBuilder::Build(), PROC_ATTR_REDUCE_PROC_60, SpellProcEntryBuilder::WithAttributesMask(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [2/19]

TEST_F ( SpellProcPipelineTest  ,
Combined_ChargesAndCooldown   
)
147{
148 _scenario->WithAura(12345, 5); // 5 charges
149
150 auto procEntry = SpellProcEntryBuilder()
151 .WithChance(100.0f)
152 .WithCooldown(500ms)
153 .Build();
154
155 // First proc at t=0
156 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
157 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 4);
158
159 // Blocked at t=0 (cooldown)
160 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
161 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 4);
162
163 // Wait and proc again at t=600ms
164 _scenario->AdvanceTime(600ms);
165 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
166 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 3);
167
168 // Blocked at t=600ms
169 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
170 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 3);
171}
SpellProcEntryBuilder & WithCooldown(Milliseconds cooldown)
Definition ProcEventInfoHelper.h:214

References SpellProcEntryBuilder::Build(), SpellProcEntryBuilder::WithChance(), and SpellProcEntryBuilder::WithCooldown().

◆ TEST_F() [3/19]

TEST_F ( SpellProcPipelineTest  ,
Combined_Level60Reduction_WithCooldown   
)
197{
198 _scenario->WithAura(12345);
199 _scenario->WithActorLevel(80);
200
201 auto procEntry = SpellProcEntryBuilder()
202 .WithChance(30.0f)
204 .WithCooldown(1000ms)
205 .Build();
206
207 // Level 80: 30% * (1 - 20/30) = 10% effective chance
208 // Roll of 5 should pass
209 EXPECT_TRUE(_scenario->SimulateProc(procEntry, 5.0f));
210
211 // Blocked by cooldown
212 EXPECT_FALSE(_scenario->SimulateProc(procEntry, 5.0f));
213
214 // Wait and try again
215 _scenario->AdvanceTime(1100ms);
216
217 // Roll of 15 should fail (10% chance)
218 EXPECT_FALSE(_scenario->SimulateProc(procEntry, 15.0f));
219}

References SpellProcEntryBuilder::Build(), PROC_ATTR_REDUCE_PROC_60, SpellProcEntryBuilder::WithAttributesMask(), SpellProcEntryBuilder::WithChance(), and SpellProcEntryBuilder::WithCooldown().

◆ TEST_F() [4/19]

TEST_F ( SpellProcPipelineTest  ,
Combined_PPM_AndCooldown   
)
174{
175 _scenario->WithAura(12345);
176 _scenario->WithWeaponSpeed(0, 2500); // BASE_ATTACK = 2500ms
177
178 auto procEntry = SpellProcEntryBuilder()
179 .WithProcsPerMinute(6.0f) // 25% with 2500ms weapon
180 .WithCooldown(1000ms)
181 .Build();
182
183 // First proc (roll 0 = always pass)
184 EXPECT_TRUE(_scenario->SimulateProc(procEntry, 0.0f));
185
186 // Blocked by cooldown even if roll would pass
187 EXPECT_FALSE(_scenario->SimulateProc(procEntry, 0.0f));
188
189 // Wait for cooldown
190 _scenario->AdvanceTime(1100ms);
191
192 // Can proc again
193 EXPECT_TRUE(_scenario->SimulateProc(procEntry, 0.0f));
194}
SpellProcEntryBuilder & WithProcsPerMinute(float ppm)
Definition ProcEventInfoHelper.h:202

References SpellProcEntryBuilder::Build(), SpellProcEntryBuilder::WithCooldown(), and SpellProcEntryBuilder::WithProcsPerMinute().

◆ TEST_F() [5/19]

TEST_F ( SpellProcPipelineTest  ,
EdgeCase_ManyCharges   
)
395{
396 _scenario->WithAura(12345, 100); // 100 charges
397
398 auto procEntry = SpellProcEntryBuilder()
399 .WithChance(100.0f)
400 .Build();
401
402 // Consume all charges
403 for (int i = 100; i > 0; --i)
404 {
405 EXPECT_EQ(_scenario->GetAura()->GetCharges(), i);
406 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
407 }
408
409 EXPECT_TRUE(_scenario->GetAura()->IsRemoved());
410}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [6/19]

TEST_F ( SpellProcPipelineTest  ,
EdgeCase_NoAura_NoProcPossible   
)
349{
350 // Don't set up aura
351 auto procEntry = SpellProcEntryBuilder()
352 .WithChance(100.0f)
353 .Build();
354
355 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
356}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [7/19]

TEST_F ( SpellProcPipelineTest  ,
EdgeCase_VeryLongCooldown   
)
374{
375 _scenario->WithAura(12345);
376
377 auto procEntry = SpellProcEntryBuilder()
378 .WithChance(100.0f)
379 .WithCooldown(300000ms) // 5 minute cooldown
380 .Build();
381
382 // First proc
383 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
384
385 // Blocked even after 4 minutes
386 _scenario->AdvanceTime(240000ms);
387 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
388
389 // Allowed after 5 minutes
390 _scenario->AdvanceTime(60001ms);
391 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
392}

References SpellProcEntryBuilder::Build(), SpellProcEntryBuilder::WithChance(), and SpellProcEntryBuilder::WithCooldown().

◆ TEST_F() [8/19]

TEST_F ( SpellProcPipelineTest  ,
EdgeCase_ZeroCooldown_AllowsRapidProcs   
)
359{
360 _scenario->WithAura(12345);
361
362 auto procEntry = SpellProcEntryBuilder()
363 .WithChance(100.0f)
364 .WithCooldown(0ms)
365 .Build();
366
367 // Multiple rapid procs should all succeed
368 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
369 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
370 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
371}

References SpellProcEntryBuilder::Build(), SpellProcEntryBuilder::WithChance(), and SpellProcEntryBuilder::WithCooldown().

◆ TEST_F() [9/19]

TEST_F ( SpellProcPipelineTest  ,
FullFlow_BasicProc_0Percent   
)
67{
68 _scenario->WithAura(12345);
69
70 auto procEntry = SpellProcEntryBuilder()
71 .WithChance(0.0f)
72 .Build();
73
74 // 0% chance should never proc (when roll is > 0)
75 EXPECT_FALSE(_scenario->SimulateProc(procEntry, 50.0f));
76}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [10/19]

TEST_F ( SpellProcPipelineTest  ,
FullFlow_BasicProc_100Percent   
)
55{
56 _scenario->WithAura(12345);
57
58 auto procEntry = SpellProcEntryBuilder()
59 .WithChance(100.0f)
60 .Build();
61
62 // 100% chance should always proc
63 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
64}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [11/19]

TEST_F ( SpellProcPipelineTest  ,
FullFlow_WithCharges   
)
101{
102 _scenario->WithAura(12345, 3); // 3 charges
103
104 auto procEntry = SpellProcEntryBuilder()
105 .WithChance(100.0f)
106 .Build();
107
108 // First proc - 3 -> 2 charges
109 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
110 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 2);
111
112 // Second proc - 2 -> 1 charges
113 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
114 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 1);
115
116 // Third proc - 1 -> 0 charges, aura removed
117 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
118 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 0);
119 EXPECT_TRUE(_scenario->GetAura()->IsRemoved());
120}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [12/19]

TEST_F ( SpellProcPipelineTest  ,
FullFlow_WithCooldown   
)
79{
80 _scenario->WithAura(12345);
81
82 auto procEntry = SpellProcEntryBuilder()
83 .WithChance(100.0f)
84 .WithCooldown(1000ms)
85 .Build();
86
87 // First proc succeeds
88 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
89
90 // Second proc blocked by cooldown
91 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
92
93 // Wait for cooldown
94 _scenario->AdvanceTime(1100ms);
95
96 // Third proc succeeds
97 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
98}

References SpellProcEntryBuilder::Build(), SpellProcEntryBuilder::WithChance(), and SpellProcEntryBuilder::WithCooldown().

◆ TEST_F() [13/19]

TEST_F ( SpellProcPipelineTest  ,
FullFlow_WithStacks   
)
123{
124 _scenario->WithAura(12345, 0, 5); // 5 stacks, no charges
125
126 auto procEntry = SpellProcEntryBuilder()
127 .WithChance(100.0f)
129 .Build();
130
131 // Each proc consumes one stack
132 for (int i = 5; i > 0; --i)
133 {
134 EXPECT_EQ(_scenario->GetAura()->GetStackAmount(), i);
135 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
136 }
137
138 EXPECT_EQ(_scenario->GetAura()->GetStackAmount(), 0);
139 EXPECT_TRUE(_scenario->GetAura()->IsRemoved());
140}
@ PROC_ATTR_USE_STACKS_FOR_CHARGES
Definition SpellMgr.h:280

References SpellProcEntryBuilder::Build(), PROC_ATTR_USE_STACKS_FOR_CHARGES, SpellProcEntryBuilder::WithAttributesMask(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [14/19]

TEST_F ( SpellProcPipelineTest  ,
Scenario_ArtOfWar   
)
276{
277 // Art of War: 2 charges (typically)
278 _scenario->WithAura(53486, 2); // Art of War
279
280 auto procEntry = SpellProcEntryBuilder()
281 .WithChance(100.0f)
282 .Build();
283
284 // First Exorcism - consumes charge
285 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
286 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 1);
287
288 // Second Exorcism - consumes last charge
289 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
290 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 0);
291 EXPECT_TRUE(_scenario->GetAura()->IsRemoved());
292}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [15/19]

TEST_F ( SpellProcPipelineTest  ,
Scenario_LeaderOfThePack   
)
250{
251 // Leader of the Pack: 6 second ICD
252 _scenario->WithAura(24932);
253
254 auto procEntry = SpellProcEntryBuilder()
255 .WithChance(100.0f)
256 .WithCooldown(6000ms)
257 .Build();
258
259 // First crit - procs
260 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
261
262 // Second crit at 1 second - blocked
263 _scenario->AdvanceTime(1000ms);
264 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
265
266 // Third crit at 5 seconds - blocked
267 _scenario->AdvanceTime(4000ms);
268 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
269
270 // Fourth crit at 6.1 seconds - allowed
271 _scenario->AdvanceTime(1100ms);
272 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
273}

References SpellProcEntryBuilder::Build(), SpellProcEntryBuilder::WithChance(), and SpellProcEntryBuilder::WithCooldown().

◆ TEST_F() [16/19]

TEST_F ( SpellProcPipelineTest  ,
Scenario_LightningShield   
)
295{
296 // Lightning Shield: 3 charges (orbs)
297 _scenario->WithAura(324, 3); // Lightning Shield Rank 1
298
299 auto procEntry = SpellProcEntryBuilder()
300 .WithChance(100.0f)
301 .Build();
302
303 // First hit - uses orb
304 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
305 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 2);
306
307 // Second hit - uses orb
308 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
309 EXPECT_EQ(_scenario->GetAura()->GetCharges(), 1);
310
311 // Third hit - last orb, aura removed
312 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
313 EXPECT_TRUE(_scenario->GetAura()->IsRemoved());
314}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithChance().

◆ TEST_F() [17/19]

TEST_F ( SpellProcPipelineTest  ,
Scenario_OmenOfClarity   
)
226{
227 // Omen of Clarity: 6 PPM, no cooldown, no charges
228 _scenario->WithAura(16864); // Omen of Clarity
229 _scenario->WithWeaponSpeed(0, 2500); // Staff
230
231 auto procEntry = SpellProcEntryBuilder()
232 .WithProcsPerMinute(6.0f) // 25% with 2500ms
233 .Build();
234
235 // Simulate multiple hits
236 int procCount = 0;
237 for (int i = 0; i < 10; ++i)
238 {
239 // Roll values simulating ~25% success rate
240 float roll = (i % 4 == 0) ? 10.0f : 50.0f;
241 if (_scenario->SimulateProc(procEntry, roll))
242 procCount++;
243 }
244
245 // With deterministic rolls, should have 3 procs (indexes 0, 4, 8).
246 EXPECT_EQ(procCount, 3);
247}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithProcsPerMinute().

◆ TEST_F() [18/19]

TEST_F ( SpellProcPipelineTest  ,
Scenario_WanderingPlague   
)
317{
318 // Wandering Plague: 1 second ICD
319 _scenario->WithAura(49217);
320
321 auto procEntry = SpellProcEntryBuilder()
322 .WithChance(100.0f)
323 .WithCooldown(1000ms)
324 .Build();
325
326 // First tick procs
327 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
328
329 // Rapid ticks blocked
330 _scenario->AdvanceTime(200ms);
331 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
332
333 _scenario->AdvanceTime(200ms);
334 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
335
336 _scenario->AdvanceTime(200ms);
337 EXPECT_FALSE(_scenario->SimulateProc(procEntry));
338
339 // After 1 second total, can proc again
340 _scenario->AdvanceTime(600ms);
341 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
342}

References SpellProcEntryBuilder::Build(), SpellProcEntryBuilder::WithChance(), and SpellProcEntryBuilder::WithCooldown().

◆ TEST_F() [19/19]

TEST_F ( SpellProcPipelineTest  ,
WeaponSpeed_AffectsPPMChance   
)
442{
443 _scenario->WithAura(12345);
444
445 auto procEntry = SpellProcEntryBuilder()
446 .WithProcsPerMinute(6.0f)
447 .Build();
448
449 // Fast dagger (1400ms): 14% chance
450 _scenario->WithWeaponSpeed(0, 1400);
451 // Roll of 10 should pass (< 14%)
452 EXPECT_TRUE(_scenario->SimulateProc(procEntry, 10.0f));
453
454 // Reset cooldown
455 _scenario->GetAura()->ResetProcCooldown();
456
457 // Slow 2H (3300ms): 33% chance
458 _scenario->WithWeaponSpeed(0, 3300);
459 // Roll of 30 should pass (< 33%)
460 EXPECT_TRUE(_scenario->SimulateProc(procEntry, 30.0f));
461}

References SpellProcEntryBuilder::Build(), and SpellProcEntryBuilder::WithProcsPerMinute().