@@ -72,17 +72,42 @@ public void Execute(IExecutionContext context, Command command)
7272
7373 _telemetryProperties = new Dictionary < string , object > ( ) ;
7474 PopulateTelemetryData ( ) ;
75- LoadFeatureFlagState ( ) ;
75+
76+ // Create the connection once and share it between LoadFeatureFlagState and the
77+ // async publish task. Previously LoadFeatureFlagState created its own connection
78+ // in a using block that disposed immediately, poisoning the singleton
79+ // FeatureFlagService's _connection. Parsers (JUnitParser, etc.) later called
80+ // GetService<IFeatureFlagService>() and hit the dead connection.
81+ // The async task takes ownership of this connection and disposes it when done.
82+ var connection = WorkerUtilities . GetVssConnection ( context ) ;
83+ LoadFeatureFlagState ( connection ) ;
7684
7785 LoadPublishTestResultsInputs ( context , eventProperties , data ) ;
7886
7987 string teamProject = context . Variables . System_TeamProject ;
8088
8189 TestRunContext runContext = CreateTestRunContext ( ) ;
90+
91+ var executionContext = _executionContext ;
92+ var testResultFiles = _testResultFiles ;
93+ var testRunner = _testRunner ;
94+ var testCaseResults = _testCaseResults ;
95+ var testPlanId = _testPlanId ;
96+ var runTitle = _runTitle ;
97+ var publishTestResultsLibFeatureState = _publishTestResultsLibFeatureState ;
98+ var triggerCoverageMergeJobFeatureState = _triggerCoverageMergeJobFeatureState ;
99+ var failTaskOnFailedTests = _failTaskOnFailedTests ;
100+ var publishOptions = GetPublishOptions ( ) ;
101+ var telemetryProperties = _telemetryProperties ;
102+
82103 var commandContext = context . GetHostContext ( ) . CreateService < IAsyncCommandContext > ( ) ;
83104 commandContext . InitializeCommandContext ( context , StringUtil . Loc ( "PublishTestResults" ) ) ;
84- commandContext . Task = PublishTestRunDataAsync ( teamProject , runContext ) ;
85- _executionContext . AsyncCommands . Add ( commandContext ) ;
105+ commandContext . Task = PublishTestRunDataAsync (
106+ teamProject , runContext , executionContext , testResultFiles , testRunner ,
107+ testCaseResults , testPlanId , runTitle , publishTestResultsLibFeatureState ,
108+ triggerCoverageMergeJobFeatureState , failTaskOnFailedTests ,
109+ publishOptions , telemetryProperties , connection ) ;
110+ executionContext . AsyncCommands . Add ( commandContext ) ;
86111 }
87112
88113 private void LoadPublishTestResultsInputs ( IExecutionContext context , Dictionary < string , string > eventProperties , string data )
@@ -303,47 +328,63 @@ private PublishOptions GetPublishOptions()
303328 return publishOptions ;
304329 }
305330
306- private async Task PublishTestRunDataAsync ( string teamProject , TestRunContext testRunContext )
331+ private async Task PublishTestRunDataAsync (
332+ string teamProject ,
333+ TestRunContext testRunContext ,
334+ IExecutionContext executionContext ,
335+ List < string > testResultFiles ,
336+ string testRunner ,
337+ TestCaseResult [ ] testCaseResults ,
338+ string testPlanId ,
339+ string runTitle ,
340+ bool publishTestResultsLibFeatureState ,
341+ bool triggerCoverageMergeJobFeatureState ,
342+ bool failTaskOnFailedTests ,
343+ PublishOptions publishOptions ,
344+ Dictionary < string , object > telemetryProperties ,
345+ VssConnection connection )
307346 {
308347 bool isTestRunOutcomeFailed = false ;
309348
310- _telemetryProperties . Add ( "UsePublishTestResultsLib" , _publishTestResultsLibFeatureState ) ;
311- using ( var connection = WorkerUtilities . GetVssConnection ( _executionContext ) )
349+ telemetryProperties . Add ( "UsePublishTestResultsLib" , publishTestResultsLibFeatureState ) ;
350+ using ( connection )
312351 {
313-
314- //This check is to determine to use "Microsoft.TeamFoundation.PublishTestResults" Library or the agent code to parse and publish the test results.
315- if ( _publishTestResultsLibFeatureState )
352+ // Use CreateService instead of GetService to get per-invocation instances.
353+ // GetService returns a cached singleton, so a second Execute() call would
354+ // reinitialize the same instance while the first async task is still in-flight,
355+ // causing ObjectDisposedException when the second using block disposes the connection.
356+ if ( publishTestResultsLibFeatureState )
316357 {
317- var publisher = _executionContext . GetHostContext ( ) . GetService < ITestDataPublisher > ( ) ;
318- publisher . InitializePublisher ( _executionContext , teamProject , connection , _testRunner ) ;
358+ var publisher = executionContext . GetHostContext ( ) . CreateService < ITestDataPublisher > ( ) ;
359+ publisher . InitializePublisher ( executionContext , teamProject , connection , testRunner ) ;
319360
320- if ( ! _testCaseResults . IsNullOrEmpty ( ) && ! _testPlanId . IsNullOrEmpty ( ) )
361+ if ( ! testCaseResults . IsNullOrEmpty ( ) && ! testPlanId . IsNullOrEmpty ( ) )
321362 {
322- isTestRunOutcomeFailed = await publisher . PublishAsync ( testRunContext , _testResultFiles , _testCaseResults , GetPublishOptions ( ) , _executionContext . CancellationToken ) ;
363+ isTestRunOutcomeFailed = await publisher . PublishAsync ( testRunContext , testResultFiles , testCaseResults , publishOptions , executionContext . CancellationToken ) ;
323364 }
324365 else
325366 {
326- isTestRunOutcomeFailed = await publisher . PublishAsync ( testRunContext , _testResultFiles , GetPublishOptions ( ) , _executionContext . CancellationToken ) ;
367+ isTestRunOutcomeFailed = await publisher . PublishAsync ( testRunContext , testResultFiles , publishOptions , executionContext . CancellationToken ) ;
327368 }
328369 }
329370 else
330371 {
331- var publisher = _executionContext . GetHostContext ( ) . GetService < ILegacyTestRunDataPublisher > ( ) ;
332- publisher . InitializePublisher ( _executionContext , teamProject , connection , _testRunner , _publishRunLevelAttachments ) ;
372+ var publisher = executionContext . GetHostContext ( ) . CreateService < ILegacyTestRunDataPublisher > ( ) ;
373+ publisher . InitializePublisher ( executionContext , teamProject , connection , testRunner , publishOptions . IsAddTestRunAttachments ) ;
333374
334- isTestRunOutcomeFailed = await publisher . PublishAsync ( testRunContext , _testResultFiles , _runTitle , _executionContext . Variables . Build_BuildId , _mergeResults ) ;
375+ isTestRunOutcomeFailed = await publisher . PublishAsync ( testRunContext , testResultFiles , runTitle , executionContext . Variables . Build_BuildId , publishOptions . IsMergeTestResultsToSingleRun ) ;
335376 }
336377
337- if ( isTestRunOutcomeFailed && _failTaskOnFailedTests )
378+ if ( isTestRunOutcomeFailed && failTaskOnFailedTests )
338379 {
339- _executionContext . Result = TaskResult . Failed ;
340- _executionContext . Error ( StringUtil . Loc ( "FailedTestsInResults" ) ) ;
380+ executionContext . Result = TaskResult . Failed ;
381+ executionContext . Error ( StringUtil . Loc ( "FailedTestsInResults" ) ) ;
341382 }
342383
343- await PublishEventsAsync ( connection ) ;
344- if ( _triggerCoverageMergeJobFeatureState )
384+ await PublishEventsAsync ( connection , executionContext , telemetryProperties ) ;
385+ if ( triggerCoverageMergeJobFeatureState )
345386 {
346- TriggerCoverageMergeJob ( _testResultFiles , _executionContext ) ;
387+ TriggerCoverageMergeJob ( testResultFiles , executionContext ) ;
347388 }
348389 }
349390 }
@@ -354,7 +395,7 @@ private void TriggerCoverageMergeJob(List<string> resultFilesInput, IExecutionCo
354395 try
355396 {
356397 ITestResultsServer _testResultsServer = context . GetHostContext ( ) . GetService < ITestResultsServer > ( ) ;
357- using ( var connection = WorkerUtilities . GetVssConnection ( _executionContext ) )
398+ using ( var connection = WorkerUtilities . GetVssConnection ( context ) )
358399 {
359400 foreach ( var resultFile in resultFilesInput )
360401 {
@@ -374,14 +415,14 @@ private void TriggerCoverageMergeJob(List<string> resultFilesInput, IExecutionCo
374415 Path . GetExtension ( file ) . Equals ( ".coverage" , StringComparison . OrdinalIgnoreCase )
375416 )
376417 {
377- _testResultsServer . InitializeServer ( connection , _executionContext ) ;
418+ _testResultsServer . InitializeServer ( connection , context ) ;
378419 try
379420 {
380- var codeCoverageResults = _testResultsServer . UpdateCodeCoverageSummaryAsync ( connection , _executionContext . Variables . System_TeamProjectId . ToString ( ) , _executionContext . Variables . Build_BuildId . GetValueOrDefault ( ) ) ;
421+ var codeCoverageResults = _testResultsServer . UpdateCodeCoverageSummaryAsync ( connection , context . Variables . System_TeamProjectId . ToString ( ) , context . Variables . Build_BuildId . GetValueOrDefault ( ) ) ;
381422 }
382423 catch ( Exception e )
383424 {
384- _executionContext . Section ( $ "Could not queue code coverage merge:{ e } ") ;
425+ context . Section ( $ "Could not queue code coverage merge:{ e } ") ;
385426 }
386427 }
387428 }
@@ -391,28 +432,28 @@ private void TriggerCoverageMergeJob(List<string> resultFilesInput, IExecutionCo
391432 }
392433 catch ( Exception e )
393434 {
394- _executionContext . Debug ( $ "Exception in Method:{ e . Message } ") ;
435+ context . Debug ( $ "Exception in Method:{ e . Message } ") ;
395436 }
396437 }
397438
398- private async Task PublishEventsAsync ( VssConnection connection )
439+ private async Task PublishEventsAsync ( VssConnection connection , IExecutionContext executionContext , Dictionary < string , object > telemetryProperties )
399440 {
400441 try
401442 {
402443 CustomerIntelligenceEvent ciEvent = new CustomerIntelligenceEvent ( )
403444 {
404445 Area = _telemetryArea ,
405446 Feature = _telemetryFeature ,
406- Properties = _telemetryProperties
447+ Properties = telemetryProperties
407448 } ;
408449
409- var ciService = _executionContext . GetHostContext ( ) . GetService < ICustomerIntelligenceServer > ( ) ;
450+ var ciService = executionContext . GetHostContext ( ) . GetService < ICustomerIntelligenceServer > ( ) ;
410451 ciService . Initialize ( connection ) ;
411452 await ciService . PublishEventsAsync ( new CustomerIntelligenceEvent [ ] { ciEvent } ) ;
412453 }
413454 catch ( Exception ex )
414455 {
415- _executionContext . Debug ( StringUtil . Loc ( "TelemetryCommandFailed" , ex . Message ) ) ;
456+ executionContext . Debug ( StringUtil . Loc ( "TelemetryCommandFailed" , ex . Message ) ) ;
416457 }
417458 }
418459
@@ -432,15 +473,12 @@ private void PopulateTelemetryData()
432473 }
433474 }
434475
435- private void LoadFeatureFlagState ( )
476+ private void LoadFeatureFlagState ( VssConnection connection )
436477 {
437- using ( var connection = WorkerUtilities . GetVssConnection ( _executionContext ) )
438- {
439- var featureFlagService = _executionContext . GetHostContext ( ) . GetService < IFeatureFlagService > ( ) ;
440- featureFlagService . InitializeFeatureService ( _executionContext , connection ) ;
441- _publishTestResultsLibFeatureState = featureFlagService . GetFeatureFlagState ( TestResultsConstants . UsePublishTestResultsLibFeatureFlag , TestResultsConstants . TFSServiceInstanceGuid ) ;
442- _triggerCoverageMergeJobFeatureState = featureFlagService . GetFeatureFlagState ( CodeCoverageConstants . TriggerCoverageMergeJobFF , TestResultsConstants . TFSServiceInstanceGuid ) ;
443- }
478+ var featureFlagService = _executionContext . GetHostContext ( ) . GetService < IFeatureFlagService > ( ) ;
479+ featureFlagService . InitializeFeatureService ( _executionContext , connection ) ;
480+ _publishTestResultsLibFeatureState = featureFlagService . GetFeatureFlagState ( TestResultsConstants . UsePublishTestResultsLibFeatureFlag , TestResultsConstants . TFSServiceInstanceGuid ) ;
481+ _triggerCoverageMergeJobFeatureState = featureFlagService . GetFeatureFlagState ( CodeCoverageConstants . TriggerCoverageMergeJobFF , TestResultsConstants . TFSServiceInstanceGuid ) ;
444482 }
445483 }
446484
0 commit comments