1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using System . Diagnostics . CodeAnalysis ;
3
4
using System . Linq ;
4
5
using System . Threading ;
6
+ using System . Threading . Tasks ;
5
7
using Hangfire . Common ;
6
8
using Hangfire . Server ;
7
9
using Moq ;
@@ -548,9 +550,95 @@ public void Run_ThrowsJobPerformanceException_InsteadOfOperationCanceled_Occurre
548
550
Assert . IsType < OperationCanceledException > ( exception . InnerException ) ;
549
551
}
550
552
551
- private BackgroundJobPerformer CreatePerformer ( )
553
+ #if ! NET452
554
+ [ Theory ]
555
+ [ MemberData ( nameof ( GetSchedulers ) ) ]
556
+ public void Run_FlowsAsyncLocal_ThroughFilters_AndSynchronousBackgroundJobMethod ( TaskScheduler scheduler )
552
557
{
553
- return new BackgroundJobPerformer ( _filterProvider . Object , _innerPerformer . Object ) ;
558
+ // Arrange
559
+ var id = Guid . NewGuid ( ) ;
560
+ _filters . Add ( new AsyncLocalFilter ( id ) ) ;
561
+ _context . BackgroundJob . Job = Job . FromExpression ( ( ) => AsyncLocalSync ( ) ) ;
562
+
563
+ var performer = CreatePerformer ( CreateInnerPerformer ( scheduler ) ) ;
564
+
565
+ // Act
566
+ var result = performer . Perform ( _context . Object ) ;
567
+
568
+ // Assert
569
+ Assert . Equal ( id , result ) ;
570
+ }
571
+
572
+ [ Theory ]
573
+ [ MemberData ( nameof ( GetSchedulers ) ) ]
574
+ public void Run_FlowsAsyncLocal_ThroughFilters_AndSimpleAsynchronousBackgroundJobMethod ( TaskScheduler scheduler )
575
+ {
576
+ // Arrange
577
+ var id = Guid . NewGuid ( ) ;
578
+ _filters . Add ( new AsyncLocalFilter ( id ) ) ;
579
+ _context . BackgroundJob . Job = Job . FromExpression ( ( ) => AsyncLocalSimpleAsync ( ) ) ;
580
+
581
+ var performer = CreatePerformer ( CreateInnerPerformer ( scheduler ) ) ;
582
+
583
+ // Act
584
+ var result = performer . Perform ( _context . Object ) ;
585
+
586
+ // Assert
587
+ Assert . Equal ( id , result ) ;
588
+ }
589
+
590
+ [ Theory ]
591
+ [ MemberData ( nameof ( GetSchedulers ) ) ]
592
+ public void Run_FlowsAsyncLocal_ThroughFilters_AndAsyncAwaitAsynchronousBackgroundJobMethod ( TaskScheduler scheduler )
593
+ {
594
+ // Arrange
595
+ var id = Guid . NewGuid ( ) ;
596
+ _filters . Add ( new AsyncLocalFilter ( id ) ) ;
597
+ _context . BackgroundJob . Job = Job . FromExpression ( ( ) => AsyncLocalAsyncAwait ( ) ) ;
598
+
599
+ var performer = CreatePerformer ( CreateInnerPerformer ( scheduler ) ) ;
600
+
601
+ // Act
602
+ var result = performer . Perform ( _context . Object ) ;
603
+
604
+ // Assert
605
+ Assert . Equal ( id , result ) ;
606
+ }
607
+
608
+ [ Theory ]
609
+ [ MemberData ( nameof ( GetSchedulers ) ) ]
610
+ public void Run_FlowsAsyncLocal_ThroughFilters_AndAsyncAwaitContinuationAsynchronousBackgroundJobMethod ( TaskScheduler scheduler )
611
+ {
612
+ // Arrange
613
+ var id = Guid . NewGuid ( ) ;
614
+ _filters . Add ( new AsyncLocalFilter ( id ) ) ;
615
+ _context . BackgroundJob . Job = Job . FromExpression ( ( ) => AsyncLocalAsyncAwaitContinuation ( ) ) ;
616
+
617
+ var performer = CreatePerformer ( CreateInnerPerformer ( scheduler ) ) ;
618
+
619
+ // Act
620
+ var result = performer . Perform ( _context . Object ) ;
621
+
622
+ // Assert
623
+ Assert . Equal ( id , result ) ;
624
+ }
625
+
626
+ private static CoreBackgroundJobPerformer CreateInnerPerformer ( TaskScheduler taskScheduler )
627
+ {
628
+ return new CoreBackgroundJobPerformer ( new JobActivator ( ) , taskScheduler ) ;
629
+ }
630
+
631
+ public static IEnumerable < object [ ] > GetSchedulers ( )
632
+ {
633
+ yield return new object [ ] { null } ;
634
+ yield return new object [ ] { TaskScheduler . Default } ;
635
+ yield return new object [ ] { new Hangfire . Processing . BackgroundTaskScheduler ( threadCount : 1 ) } ;
636
+ }
637
+ #endif
638
+
639
+ private BackgroundJobPerformer CreatePerformer ( IBackgroundJobPerformer inner = null )
640
+ {
641
+ return new BackgroundJobPerformer ( _filterProvider . Object , inner ?? _innerPerformer . Object ) ;
554
642
}
555
643
556
644
private Mock < T > CreateFilter < T > ( )
@@ -561,5 +649,57 @@ private Mock<T> CreateFilter<T>()
561
649
562
650
return filter ;
563
651
}
652
+
653
+ #if ! NET452
654
+ private static readonly AsyncLocal < Guid > Identifier = new AsyncLocal < Guid > ( ) ;
655
+
656
+ [ SuppressMessage ( "ReSharper" , "MemberCanBePrivate.Global" ) ]
657
+ [ SuppressMessage ( "ReSharper" , "UnusedMethodReturnValue.Global" ) ]
658
+ public static Guid AsyncLocalSync ( )
659
+ {
660
+ return Identifier . Value ;
661
+ }
662
+
663
+ [ SuppressMessage ( "ReSharper" , "MemberCanBePrivate.Global" ) ]
664
+ public static Task < Guid > AsyncLocalSimpleAsync ( )
665
+ {
666
+ return Task . FromResult ( Identifier . Value ) ;
667
+ }
668
+
669
+ [ SuppressMessage ( "ReSharper" , "MemberCanBePrivate.Global" ) ]
670
+ #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
671
+ public static async Task < Guid > AsyncLocalAsyncAwait ( )
672
+ #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
673
+ {
674
+ return Identifier . Value ;
675
+ }
676
+
677
+ [ SuppressMessage ( "ReSharper" , "MemberCanBePrivate.Global" ) ]
678
+ public static async Task < Guid > AsyncLocalAsyncAwaitContinuation ( )
679
+ {
680
+ await Task . Yield ( ) ;
681
+ return Identifier . Value ;
682
+ }
683
+
684
+ private sealed class AsyncLocalFilter : IServerFilter
685
+ {
686
+ private readonly Guid _identifier ;
687
+
688
+ public AsyncLocalFilter ( Guid identifier )
689
+ {
690
+ _identifier = identifier ;
691
+ }
692
+
693
+ public void OnPerforming ( PerformingContext context )
694
+ {
695
+ Identifier . Value = _identifier ;
696
+ }
697
+
698
+ public void OnPerformed ( PerformedContext context )
699
+ {
700
+ Assert . Equal ( _identifier , Identifier . Value ) ;
701
+ }
702
+ }
703
+ #endif
564
704
}
565
705
}
0 commit comments