@@ -22,10 +22,15 @@ namespace Microsoft.Build.Engine.UnitTests
2222 /// </summary>
2323 public class TaskHostFactoryLifecycle_E2E_Tests
2424 {
25- private static string AssemblyLocation { get ; } = Path . Combine ( Path . GetDirectoryName ( typeof ( TaskHostFactoryLifecycle_E2E_Tests ) . Assembly . Location ) ?? System . AppContext . BaseDirectory ) ;
25+ private static string AssemblyLocation { get ; } = Path . Combine ( Path . GetDirectoryName ( typeof ( TaskHostFactoryLifecycle_E2E_Tests ) . Assembly . Location ) ?? AppContext . BaseDirectory ) ;
2626
2727 private static string TestAssetsRootPath { get ; } = Path . Combine ( AssemblyLocation , "TestAssets" , "TaskHostLifecycle" ) ;
2828
29+ private const string TaskHostFactory = "TaskHostFactory" ;
30+ private const string AssemblyTaskFactory = "AssemblyTaskFactory" ;
31+ private const string CurrentRuntime = "CurrentRuntime" ;
32+ private const string NetRuntime = "NET" ;
33+
2934 private readonly ITestOutputHelper _output ;
3035
3136 public TaskHostFactoryLifecycle_E2E_Tests ( ITestOutputHelper output )
@@ -46,74 +51,71 @@ public TaskHostFactoryLifecycle_E2E_Tests(ITestOutputHelper output)
4651 /// <param name="taskFactoryToUse">The task factory to use (TaskHostFactory or AssemblyTaskFactory)</param>
4752 [ Theory ]
4853#if NET
49- [ InlineData ( " CurrentRuntime" , " AssemblyTaskFactory" ) ] // Match + No Explicit → in-proc
50- [ InlineData ( " CurrentRuntime" , " TaskHostFactory" ) ] // Match + Explicit → short-lived out-of-proc
54+ [ InlineData ( CurrentRuntime , AssemblyTaskFactory ) ] // Match + No Explicit → in-proc
55+ [ InlineData ( CurrentRuntime , TaskHostFactory ) ] // Match + Explicit → short-lived out-of-proc
5156#endif
52- [ InlineData ( "NET" , " AssemblyTaskFactory" ) ] // No Match + No Explicit → long-lived sidecar out-of-proc
53- [ InlineData ( "NET" , " TaskHostFactory" ) ] // No Match + Explicit → short-lived out-of-proc
57+ [ InlineData ( NetRuntime , AssemblyTaskFactory ) ] // No Match + No Explicit → long-lived sidecar out-of-proc
58+ [ InlineData ( NetRuntime , TaskHostFactory ) ] // No Match + Explicit → short-lived out-of-proc
5459 public void TaskHostLifecycle_ValidatesAllScenarios (
5560 string runtimeToUse ,
5661 string taskFactoryToUse )
5762 {
58- bool ? expectedNodeReuse ;
63+ bool ? expectedNodeReuse = DetermineExpectedNodeReuse ( runtimeToUse , taskFactoryToUse ) ;
5964
60- // TaskHostFactory is always short lived and out-of-proc
61- if ( taskFactoryToUse == "TaskHostFactory" )
62- {
63- expectedNodeReuse = false ;
64- }
65- // AssemblyTaskFactory behavior depends on runtime
66- else if ( taskFactoryToUse == "AssemblyTaskFactory" )
67- {
68- if ( runtimeToUse == "CurrentRuntime" )
69- {
70- // in-proc
71- expectedNodeReuse = null ;
72- }
73- else if ( runtimeToUse == "NET" )
74- {
75- // When running on .NET Framework: out-of-proc, otherwise on .NET in-proc.
76- expectedNodeReuse = RuntimeInformation . FrameworkDescription . StartsWith ( ".NET Framework" , StringComparison . OrdinalIgnoreCase ) ? true : null ;
77- }
78- else
79- {
80- throw new ArgumentOutOfRangeException ( nameof ( runtimeToUse ) , "Unknown runtime to use: " + runtimeToUse ) ;
81- }
82- }
83- else
65+ using TestEnvironment env = TestEnvironment . Create ( _output ) ;
66+
67+ string buildOutput = ExecuteBuildWithTaskHost ( runtimeToUse , taskFactoryToUse ) ;
68+
69+ ValidateTaskHostBehavior ( buildOutput , expectedNodeReuse ) ;
70+ }
71+
72+ private static bool ? DetermineExpectedNodeReuse ( string runtimeToUse , string taskFactoryToUse )
73+ => ( taskFactoryToUse , runtimeToUse ) switch
8474 {
85- throw new ArgumentOutOfRangeException ( nameof ( taskFactoryToUse ) , "Unknown task factory to use: " + taskFactoryToUse ) ;
86- }
75+ // TaskHostFactory is always short-lived and out-of-proc (nodereuse:False)
76+ ( TaskHostFactory , _ ) => false ,
8777
88- using TestEnvironment env = TestEnvironment . Create ( _output ) ;
78+ // AssemblyTaskFactory with CurrentRuntime runs in-proc
79+ ( AssemblyTaskFactory , CurrentRuntime ) => null ,
80+
81+ // AssemblyTaskFactory with NET runtime:
82+ // - On .NET Framework host: out-of-proc with long-lived sidecar (nodereuse:True)
83+ // - On .NET host: in-proc
84+ ( AssemblyTaskFactory , NetRuntime ) =>
85+ #if NET
86+ null , // On .NET host: in-proc execution
87+ #else
88+ true , // On .NET Framework host: out-of-proc with long-lived sidecar
89+ #endif
90+ _ => throw new ArgumentException( $ "Unknown combination: runtime={ runtimeToUse } , factory={ taskFactoryToUse } ")
91+ } ;
92+
93+ private string ExecuteBuildWithTaskHost ( string runtimeToUse , string taskFactoryToUse )
94+ {
8995 string testProjectPath = Path . Combine ( TestAssetsRootPath , "TaskHostLifecycleTestApp.csproj" ) ;
9096
91- string testTaskOutput = RunnerUtilities . ExecBootstrapedMSBuild (
92- $ "{ testProjectPath } -v:n -restore /p:RuntimeToUse={ runtimeToUse } /p:TaskFactoryToUse={ taskFactoryToUse } ",
93- out bool successTestTask ,
97+ string output = RunnerUtilities . ExecBootstrapedMSBuild (
98+ $ "{ testProjectPath } -v:n -restore /p:RuntimeToUse={ runtimeToUse } /p:TaskFactoryToUse={ taskFactoryToUse } ",
99+ out bool success ,
94100 outputHelper : _output ) ;
95101
96- successTestTask . ShouldBeTrue ( ) ;
102+ success . ShouldBeTrue ( "Build should succeed" ) ;
97103
98- // Verify execution mode (out-of-proc vs in-proc) and node reuse behavior
104+ return output ;
105+ }
106+
107+ private static void ValidateTaskHostBehavior ( string buildOutput , bool ? expectedNodeReuse )
108+ {
99109 if ( expectedNodeReuse . HasValue )
100110 {
101- // For out-of-proc scenarios, validate the task runs in a separate process
102- // by checking for the presence of command-line arguments that indicate task host execution
103- testTaskOutput . ShouldContain ( "/nodemode:" ,
104- customMessage : "Task should run out-of-proc and have /nodemode: in its command-line arguments" ) ;
111+ buildOutput . ShouldContain ( "/nodemode:" , customMessage : "Task should run out-of-proc and have /nodemode: in its command-line arguments" ) ;
105112
106- // Validate the nodereuse flag in the task's command-line arguments
107113 string expectedFlag = expectedNodeReuse . Value ? "/nodereuse:True" : "/nodereuse:False" ;
108- testTaskOutput . ShouldContain ( expectedFlag ,
109- customMessage : $ "Task should have { expectedFlag } in its command-line arguments") ;
114+ buildOutput . ShouldContain ( expectedFlag , customMessage : $ "Task should have { expectedFlag } in its command-line arguments") ;
110115 }
111116 else
112117 {
113- // For in-proc scenarios, validate the task does NOT run in a task host
114- // by ensuring task host specific command-line flags are not present
115- testTaskOutput . ShouldNotContain ( "/nodemode:" ,
116- customMessage : "Task should run in-proc and not have task host command-line arguments like /nodemode:" ) ;
118+ buildOutput . ShouldNotContain ( "/nodemode:" , customMessage : "Task should run in-proc and not have task host command-line arguments like /nodemode:" ) ;
117119 }
118120 }
119121 }
0 commit comments