1919
2020namespace Microsoft . Build . UnitTests . BackEnd
2121{
22+ using System . Linq ;
23+ using FluentAssertions ;
2224 using Microsoft . Build . Unittest ;
2325
2426 /// <summary>
2527 /// Tests of the scheduler.
2628 /// </summary>
27- // Ignore: Causing issues with other tests
28- // NOTE: marked as "internal" to disable the entire test class, as was done for MSTest.
2929 public class Scheduler_Tests : IDisposable
3030 {
3131 /// <summary>
@@ -58,6 +58,11 @@ public class Scheduler_Tests : IDisposable
5858 /// </summary>
5959 private BuildParameters _parameters ;
6060
61+ /// <summary>
62+ /// Configuration ID.
63+ /// </summary>
64+ private const int DefaultConfigId = 99 ;
65+
6166 /// <summary>
6267 /// Set up
6368 /// </summary>
@@ -71,8 +76,8 @@ public Scheduler_Tests()
7176 _host = new MockHost ( ) ;
7277 _scheduler = new Scheduler ( ) ;
7378 _scheduler . InitializeComponent ( _host ) ;
74- CreateConfiguration ( 99 , "parent.proj" ) ;
75- _defaultParentRequest = CreateBuildRequest ( 99 , 99 , Array . Empty < string > ( ) , null ) ;
79+ CreateConfiguration ( DefaultConfigId , "parent.proj" ) ;
80+ _defaultParentRequest = CreateBuildRequest ( 99 , DefaultConfigId , Array . Empty < string > ( ) , null ) ;
7681
7782 // Set up the scheduler with one node to start with.
7883 _scheduler . ReportNodesCreated ( new NodeInfo [ ] { new NodeInfo ( 1 , NodeProviderType . InProc ) } ) ;
@@ -387,8 +392,8 @@ public void VerifyRequestOrderingDoesNotAffectNodeCreationCountWithInProcAndAnyR
387392 _parameters . ShutdownInProcNodeOnBuildFinish = true ;
388393 _buildManager = new BuildManager ( ) ;
389394
390- CreateConfiguration ( 99 , "parent.proj" ) ;
391- _defaultParentRequest = CreateBuildRequest ( 99 , 99 , Array . Empty < string > ( ) , null ) ;
395+ CreateConfiguration ( DefaultConfigId , "parent.proj" ) ;
396+ _defaultParentRequest = CreateBuildRequest ( 99 , DefaultConfigId , Array . Empty < string > ( ) , null ) ;
392397
393398 CreateConfiguration ( 1 , "foo.proj" ) ;
394399 BuildRequest request1 = CreateBuildRequest ( 1 , 1 , new string [ ] { "foo" } , NodeAffinity . Any , _defaultParentRequest ) ;
@@ -579,8 +584,8 @@ public void VerifyNoOverCreationOfNodesWithBuildLoop()
579584 _parameters . ShutdownInProcNodeOnBuildFinish = true ;
580585 _buildManager = new BuildManager ( ) ;
581586
582- CreateConfiguration ( 99 , "parent.proj" ) ;
583- _defaultParentRequest = CreateBuildRequest ( 99 , 99 , Array . Empty < string > ( ) , null ) ;
587+ CreateConfiguration ( DefaultConfigId , "parent.proj" ) ;
588+ _defaultParentRequest = CreateBuildRequest ( 99 , DefaultConfigId , Array . Empty < string > ( ) , null ) ;
584589
585590 CreateConfiguration ( 1 , "foo.proj" ) ;
586591 BuildRequest request1 = CreateBuildRequest ( 1 , 1 , new string [ ] { "foo" } , NodeAffinity . OutOfProc , _defaultParentRequest ) ;
@@ -769,12 +774,13 @@ private BuildResult CacheBuildResult(BuildRequest request, string target, WorkUn
769774 }
770775
771776 /// <summary>
772- /// Creates a build result for a request
777+ /// Creates a build result for a request.
773778 /// </summary>
774779 private BuildResult CreateBuildResult ( BuildRequest request , string target , WorkUnitResult workUnitResult )
775780 {
776781 BuildResult result = new BuildResult ( request ) ;
777782 result . AddResultsForTarget ( target , new TargetResult ( Array . Empty < TaskItem > ( ) , workUnitResult ) ) ;
783+
778784 return result ;
779785 }
780786
@@ -789,23 +795,30 @@ private BuildRequest CreateBuildRequest(int nodeRequestId, int configId)
789795 /// <summary>
790796 /// Creates a build request.
791797 /// </summary>
792- private BuildRequest CreateBuildRequest ( int nodeRequestId , int configId , string [ ] targets )
798+ private BuildRequest CreateBuildRequest ( int nodeRequestId , int configId , string [ ] targets , BuildRequestDataFlags buildRequestDataFlags = BuildRequestDataFlags . None )
793799 {
794- return CreateBuildRequest ( nodeRequestId , configId , targets , _defaultParentRequest ) ;
800+ return CreateBuildRequest ( nodeRequestId , configId , targets , _defaultParentRequest , buildRequestDataFlags ) ;
795801 }
796802
797803 /// <summary>
798804 /// Creates a build request.
799805 /// </summary>
800- private BuildRequest CreateBuildRequest ( int nodeRequestId , int configId , string [ ] targets , BuildRequest parentRequest )
806+ private BuildRequest CreateBuildRequest ( int nodeRequestId , int configId , string [ ] targets , BuildRequest parentRequest , BuildRequestDataFlags buildRequestDataFlags = BuildRequestDataFlags . None )
801807 {
802- return CreateBuildRequest ( nodeRequestId , configId , targets , NodeAffinity . Any , parentRequest ) ;
808+ return CreateBuildRequest ( nodeRequestId , configId , targets , NodeAffinity . Any , parentRequest , buildRequestDataFlags : buildRequestDataFlags ) ;
803809 }
804810
805811 /// <summary>
806812 /// Creates a build request.
807813 /// </summary>
808- private BuildRequest CreateBuildRequest ( int nodeRequestId , int configId , string [ ] targets , NodeAffinity nodeAffinity , BuildRequest parentRequest , ProxyTargets proxyTargets = null )
814+ private BuildRequest CreateBuildRequest (
815+ int nodeRequestId ,
816+ int configId ,
817+ string [ ] targets ,
818+ NodeAffinity nodeAffinity ,
819+ BuildRequest parentRequest ,
820+ ProxyTargets proxyTargets = null ,
821+ BuildRequestDataFlags buildRequestDataFlags = BuildRequestDataFlags . None )
809822 {
810823 ( targets == null ^ proxyTargets == null ) . ShouldBeTrue ( ) ;
811824
@@ -826,16 +839,19 @@ private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[
826839 targets ,
827840 hostServices ,
828841 BuildEventContext . Invalid ,
829- parentRequest ) ;
842+ parentRequest ,
843+ buildRequestDataFlags : buildRequestDataFlags ) ;
830844 }
831845
832846 parentRequest . ShouldBeNull ( ) ;
847+
833848 return new BuildRequest (
834849 submissionId : 1 ,
835850 nodeRequestId ,
836851 configId ,
837852 proxyTargets ,
838- hostServices ) ;
853+ hostServices ,
854+ buildRequestDataFlags : buildRequestDataFlags ) ;
839855 }
840856
841857 private BuildRequest CreateProxyBuildRequest ( int nodeRequestId , int configId , ProxyTargets proxyTargets , BuildRequest parentRequest )
@@ -849,6 +865,69 @@ private BuildRequest CreateProxyBuildRequest(int nodeRequestId, int configId, Pr
849865 proxyTargets ) ;
850866 }
851867
868+ /// <summary>
869+ /// The test checks how scheduler handles the duplicated requests and cache MISS for this case.
870+ /// It's expected to have the duplicated request rescheduled for the execution.
871+ /// </summary>
872+ [ Fact ]
873+ public void ReportResultTest_NoCacheHitForDupes ( )
874+ {
875+ // Create a duplicate of the existing _defaultParentRequest, but with a different build request flag, so we can't get the result from the cache.
876+ BuildRequest duplicateRequest = CreateBuildRequest ( 2 , configId : DefaultConfigId , Array . Empty < string > ( ) , parentRequest : null , BuildRequestDataFlags . ProvideSubsetOfStateAfterBuild ) ;
877+
878+ // Schedule the duplicate request -> it goes to unscheduled request due to duplicated configId
879+ _scheduler . ReportRequestBlocked ( 2 , new BuildRequestBlocker ( - 1 , Array . Empty < string > ( ) , [ duplicateRequest ] ) ) ;
880+
881+ // try to get a result for the parent request and see if we get a result for the duplicate request
882+ var results = _scheduler . ReportResult ( 1 , CreateBuildResult ( _defaultParentRequest , "" , BuildResultUtilities . GetSuccessResult ( ) ) )
883+ . ToList ( ) ;
884+
885+ results . ShouldNotBeNull ( ) ;
886+ results . Count . ShouldBe ( 2 ) ;
887+
888+ // Completed _defaultParentRequest
889+ results [ 0 ] . BuildResult . ShouldNotBeNull ( ) ;
890+ results [ 0 ] . BuildResult . BuildRequestDataFlags . ShouldBe ( BuildRequestDataFlags . None ) ;
891+ results [ 0 ] . Action . ShouldBe ( ScheduleActionType . SubmissionComplete ) ;
892+
893+ // The automatically scheduled duplicated request.
894+ results [ 1 ] . BuildResult . ShouldBeNull ( ) ;
895+ results [ 1 ] . NodeId . Should ( ) . Be ( 1 ) ;
896+ results [ 1 ] . Action . ShouldBe ( ScheduleActionType . Schedule ) ;
897+ results [ 1 ] . BuildRequest . BuildRequestDataFlags . ShouldBe ( BuildRequestDataFlags . ProvideSubsetOfStateAfterBuild ) ;
898+ }
899+
900+ /// <summary>
901+ /// The test checks how scheduler handles the duplicated requests and cache HIT for this case.
902+ /// It's expected to have an immediate result for the duplicated request.
903+ /// </summary>
904+ [ Fact ]
905+ public void ReportResultTest_CacheHitForDupes ( )
906+ {
907+ // Create a duplicate of the existing _defaultParentRequest.
908+ BuildRequest duplicateRequest = CreateBuildRequest ( 2 , configId : DefaultConfigId , Array . Empty < string > ( ) , parentRequest : null , BuildRequestDataFlags . None ) ;
909+
910+ // Schedule the duplicate request -> it goes to unscheduled request due to duplicated configId
911+ _scheduler . ReportRequestBlocked ( 1 , new BuildRequestBlocker ( - 1 , Array . Empty < string > ( ) , [ duplicateRequest ] ) ) ;
912+
913+ // try to get a result for the parent request and see if we get a result for the duplicate request.
914+ var results = _scheduler . ReportResult ( 1 , CreateBuildResult ( duplicateRequest , "" , BuildResultUtilities . GetSuccessResult ( ) ) )
915+ . ToList ( ) ;
916+
917+ results . ShouldNotBeNull ( ) ;
918+ results . Count . ShouldBe ( 2 ) ;
919+
920+ // Completed _defaultParentRequest
921+ results [ 0 ] . BuildResult . ShouldNotBeNull ( ) ;
922+ results [ 0 ] . BuildResult . BuildRequestDataFlags . ShouldBe ( BuildRequestDataFlags . None ) ;
923+ results [ 0 ] . Action . ShouldBe ( ScheduleActionType . SubmissionComplete ) ;
924+
925+ // We hit cache and completed the duplicate request.
926+ results [ 1 ] . BuildResult . ShouldNotBeNull ( ) ;
927+ results [ 1 ] . BuildResult . BuildRequestDataFlags . ShouldBe ( BuildRequestDataFlags . None ) ;
928+ results [ 1 ] . Action . ShouldBe ( ScheduleActionType . SubmissionComplete ) ;
929+ }
930+
852931 /// <summary>
853932 /// Method that fakes the actions done by BuildManager.PerformSchedulingActions
854933 /// </summary>
0 commit comments