@@ -4,19 +4,33 @@ namespace Sentry.Tests;
4
4
5
5
public class MetricAggregatorTests
6
6
{
7
- class Fixture
7
+ private class Fixture
8
8
{
9
- public SentryOptions Options { get ; set ; } = new ( ) ;
10
- public IHub Hub { get ; set ; } = Substitute . For < IHub > ( ) ;
11
- public IMetricHub MetricHub { get ; set ; } = Substitute . For < IMetricHub > ( ) ;
12
- public bool DisableFlushLoop { get ; set ; } = true ;
13
- public TimeSpan ? FlushInterval { get ; set ; }
14
- public MetricAggregator GetSut ( )
15
- => new ( Options , MetricHub , disableLoopTask : DisableFlushLoop , flushInterval : FlushInterval ) ;
9
+ public readonly IDiagnosticLogger Logger ;
10
+ public readonly SentryOptions Options ;
11
+
12
+ public readonly IMetricHub MetricHub ;
13
+ public bool DisableFlushLoop ;
14
+ public readonly CancellationTokenSource CancellationTokenSource ;
15
+
16
+ public Fixture ( )
17
+ {
18
+ Logger = Substitute . For < IDiagnosticLogger > ( ) ;
19
+ Options = new SentryOptions
20
+ {
21
+ Debug = true ,
22
+ DiagnosticLogger = Logger
23
+ } ;
24
+ MetricHub = Substitute . For < IMetricHub > ( ) ;
25
+ DisableFlushLoop = true ;
26
+ CancellationTokenSource = new CancellationTokenSource ( ) ;
27
+ }
28
+
29
+ public MetricAggregator GetSut ( ) => new ( Options , MetricHub , CancellationTokenSource , DisableFlushLoop ) ;
16
30
}
17
31
18
32
// private readonly Fixture _fixture = new();
19
- private static readonly Fixture _fixture = new ( ) ;
33
+ private readonly Fixture _fixture = new ( ) ;
20
34
21
35
[ Fact ]
22
36
public void GetMetricBucketKey_GeneratesExpectedKey ( )
@@ -176,7 +190,8 @@ public async Task GetFlushableBuckets_IsThreadsafe()
176
190
var sent = 0 ;
177
191
MetricHelper . FlushShift = 0.0 ;
178
192
_fixture . DisableFlushLoop = false ;
179
- _fixture . FlushInterval = TimeSpan . FromMilliseconds ( 100 ) ;
193
+ // TODO: Remove
194
+ // _fixture.FlushInterval = TimeSpan.FromMilliseconds(100);
180
195
_fixture . MetricHub . CaptureMetrics ( Arg . Do < IEnumerable < Metric > > ( metrics =>
181
196
{
182
197
foreach ( var metric in metrics )
@@ -339,4 +354,126 @@ public void RecordCodeLocation_BadStackLevel_AddsToSeenButNotPending()
339
354
340
355
sut . _pendingLocations . SelectMany ( x => x . Value ) . Should ( ) . BeEmpty ( ) ;
341
356
}
357
+
358
+ [ Fact ]
359
+ public void Dispose_OnlyExecutesOnce ( )
360
+ {
361
+ // Arrange
362
+ _fixture . Logger . IsEnabled ( Arg . Any < SentryLevel > ( ) ) . Returns ( true ) ;
363
+ var sut = _fixture . GetSut ( ) ;
364
+
365
+ // Act
366
+ sut . Dispose ( ) ;
367
+ sut . Dispose ( ) ;
368
+ sut . Dispose ( ) ;
369
+
370
+ // Assert
371
+ _fixture . Logger . Received ( 2 ) . Log ( SentryLevel . Debug , MetricAggregator . AlreadyDisposedMessage , null ) ;
372
+ }
373
+
374
+ [ Fact ]
375
+ public void Dispose_StopsLoopTask ( )
376
+ {
377
+ // Arrange
378
+ _fixture . Logger . IsEnabled ( Arg . Any < SentryLevel > ( ) ) . Returns ( true ) ;
379
+ _fixture . DisableFlushLoop = false ;
380
+ _fixture . Options . ShutdownTimeout = TimeSpan . Zero ;
381
+ var sut = _fixture . GetSut ( ) ;
382
+
383
+ // Act
384
+ sut . Dispose ( ) ;
385
+
386
+ // Assert
387
+ _fixture . Logger . Received ( 1 ) . Log ( SentryLevel . Debug , MetricAggregator . DisposingMessage , null ) ;
388
+ sut . _loopTask . Status . Should ( ) . BeOneOf ( TaskStatus . RanToCompletion , TaskStatus . Faulted ) ;
389
+ }
390
+
391
+ [ Fact ]
392
+ public async Task Dispose_SwallowsException ( )
393
+ {
394
+ // Arrange
395
+ _fixture . CancellationTokenSource . Dispose ( ) ;
396
+ _fixture . DisableFlushLoop = false ;
397
+ var sut = _fixture . GetSut ( ) ;
398
+
399
+ // We expect an exception here, because we disposed the cancellation token source
400
+ await Assert . ThrowsAsync < ObjectDisposedException > ( ( ) => sut . _loopTask ) ;
401
+
402
+ // Act
403
+ await sut . DisposeAsync ( ) ;
404
+
405
+ // Assert
406
+ sut . _loopTask . Status . Should ( ) . Be ( TaskStatus . Faulted ) ;
407
+ }
408
+
409
+ [ Fact ]
410
+ public async Task Cancel_NonZeroTimeout_SchedulesShutdown ( )
411
+ {
412
+ // Arrange
413
+ _fixture . Logger . IsEnabled ( Arg . Any < SentryLevel > ( ) ) . Returns ( true ) ;
414
+ _fixture . DisableFlushLoop = false ;
415
+ _fixture . Options . ShutdownTimeout = TimeSpan . FromSeconds ( 1 ) ;
416
+ var sut = _fixture . GetSut ( ) ;
417
+
418
+ // Act
419
+ await _fixture . CancellationTokenSource . CancelAsync ( ) ;
420
+ await Task . Delay ( 1000 ) ;
421
+
422
+ // Assert
423
+ _fixture . Logger . Received ( 1 ) . Log ( SentryLevel . Debug , MetricAggregator . ShutdownScheduledMessage , null , Arg . Any < TimeSpan > ( ) ) ;
424
+ }
425
+
426
+ [ Fact ]
427
+ public async Task Cancel_ZeroTimeout_ShutdownImmediately ( )
428
+ {
429
+ // Arrange
430
+ _fixture . Logger . IsEnabled ( Arg . Any < SentryLevel > ( ) ) . Returns ( true ) ;
431
+ _fixture . DisableFlushLoop = false ;
432
+ _fixture . Options . ShutdownTimeout = TimeSpan . Zero ;
433
+ var sut = _fixture . GetSut ( ) ;
434
+
435
+ // Act
436
+ await _fixture . CancellationTokenSource . CancelAsync ( ) ;
437
+ await Task . Delay ( 1000 ) ;
438
+
439
+ // Assert
440
+ _fixture . Logger . Received ( 1 ) . Log ( SentryLevel . Debug , MetricAggregator . ShutdownImmediatelyMessage , null ) ;
441
+ }
442
+
443
+ [ Fact ]
444
+ public async Task FlushAsync_FlushesPendingLocations ( )
445
+ {
446
+ // Arrange
447
+ var type = MetricType . Counter ;
448
+ var key = "counter_key" ;
449
+ var unit = MeasurementUnit . None ;
450
+ var stackLevel = 1 ;
451
+ var timestamp = DateTimeOffset . Now . Subtract ( TimeSpan . FromSeconds ( 20 ) ) ;
452
+ var sut = _fixture . GetSut ( ) ;
453
+ sut . RecordCodeLocation ( type , key , unit , stackLevel , timestamp ) ;
454
+
455
+ // Act
456
+ await sut . FlushAsync ( ) ;
457
+
458
+ // Assert
459
+ _fixture . MetricHub . Received ( 1 ) . CaptureCodeLocations ( Arg . Any < CodeLocations > ( ) ) ;
460
+ }
461
+
462
+ [ Fact ]
463
+ public async Task FlushAsync_Cancel_Exists ( )
464
+ {
465
+ // Arrange
466
+ _fixture . DisableFlushLoop = false ;
467
+ _fixture . Logger . IsEnabled ( Arg . Any < SentryLevel > ( ) ) . Returns ( true ) ;
468
+ var cancellationTokenSource = new CancellationTokenSource ( ) ;
469
+ await cancellationTokenSource . CancelAsync ( ) ;
470
+ var sut = _fixture . GetSut ( ) ;
471
+
472
+ // Act
473
+ await sut . FlushAsync ( true , cancellationTokenSource . Token ) ;
474
+ // await Task.Delay(1000);
475
+
476
+ // Assert
477
+ _fixture . Logger . Received ( 1 ) . Log ( SentryLevel . Info , MetricAggregator . FlushShutdownMessage , null ) ;
478
+ }
342
479
}
0 commit comments