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   
)
418{
419 _scenario->WithAura(12345);
420 _scenario->WithActorLevel(60);
421
422 auto procEntry = SpellProcEntryBuilder()
423 .WithChance(30.0f)
425 .Build();
426
427 // At level 60, full 30% chance
428 // Roll of 25 should pass
429 EXPECT_TRUE(_scenario->SimulateProc(procEntry, 25.0f));
430
431 // Reset
432 _scenario->GetAura()->ResetProcCooldown();
433
434 // Change to level 80
435 _scenario->WithActorLevel(80);
436
437 // At level 80, only 10% chance
438 // Roll of 25 should fail
439 EXPECT_FALSE(_scenario->SimulateProc(procEntry, 25.0f));
440}
@ 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   
)
396{
397 _scenario->WithAura(12345, 100); // 100 charges
398
399 auto procEntry = SpellProcEntryBuilder()
400 .WithChance(100.0f)
401 .Build();
402
403 // Consume all charges
404 for (int i = 100; i > 0; --i)
405 {
406 EXPECT_EQ(_scenario->GetAura()->GetCharges(), i);
407 EXPECT_TRUE(_scenario->SimulateProc(procEntry));
408 }
409
410 EXPECT_TRUE(_scenario->GetAura()->IsRemoved());
411}

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

◆ TEST_F() [6/19]

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

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

◆ TEST_F() [7/19]

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

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

◆ TEST_F() [8/19]

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

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

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

◆ TEST_F() [15/19]

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

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

◆ TEST_F() [16/19]

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

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 // But our test is roll > chance check, so roll 10 fails against 25% chance
247 // Actually roll 0 always passes, non-zero rolls check roll > chance
248}

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

◆ TEST_F() [18/19]

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

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

◆ TEST_F() [19/19]

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

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