/// <summary>
        /// Handles Partial Run Complete event coming from a specific concurrent proxy exceution manager
        /// Each concurrent proxy execution manager will signal the parallel execution manager when its complete
        /// </summary>
        /// <param name="proxyExecutionManager">Concurrent Execution manager that completed the run</param>
        /// <param name="testRunCompleteArgs">RunCompleteArgs for the concurrent run</param>
        /// <param name="lastChunkArgs">LastChunk testresults for the concurrent run</param>
        /// <param name="runContextAttachments">RunAttachments for the concurrent run</param>
        /// <param name="executorUris">ExecutorURIs of the adapters involved in executing the tests</param>
        /// <returns>True if parallel run is complete</returns>
        public bool HandlePartialRunComplete(
            IProxyExecutionManager proxyExecutionManager,
            TestRunCompleteEventArgs testRunCompleteArgs,
            TestRunChangedEventArgs lastChunkArgs,
            ICollection <AttachmentSet> runContextAttachments,
            ICollection <string> executorUris)
        {
            var allRunsCompleted = false;

            if (!this.SharedHosts)
            {
                this.concurrentManagerHandlerMap.Remove(proxyExecutionManager);
                proxyExecutionManager.Close();

                proxyExecutionManager = CreateNewConcurrentManager();

                var parallelEventsHandler = new ParallelRunEventsHandler(
                    proxyExecutionManager,
                    this.currentRunEventsHandler,
                    this,
                    this.currentRunDataAggregator);
                this.concurrentManagerHandlerMap.Add(proxyExecutionManager, parallelEventsHandler);
            }

            // In Case of Cancel or Abort, no need to trigger run for rest of the data
            // If there are no more sources/testcases, a parallel executor is truly done with execution
            if (testRunCompleteArgs.IsAborted || testRunCompleteArgs.IsCanceled || !this.StartTestRunOnConcurrentManager(proxyExecutionManager))
            {
                lock (this.executionStatusLockObject)
                {
                    // Each concurrent Executor calls this method
                    // So, we need to keep track of total runcomplete calls
                    this.runCompletedClients++;
                    allRunsCompleted = this.runCompletedClients == this.concurrentManagerInstances.Length;
                }

                // verify that all executors are done with the execution and there are no more sources/testcases to execute
                if (allRunsCompleted)
                {
                    // Reset enumerators
                    this.sourceEnumerator       = null;
                    this.testCaseListEnumerator = null;

                    this.currentRunDataAggregator = null;
                    this.currentRunEventsHandler  = null;

                    // Dispose concurrent executors
                    // Do not do the cleanuptask in the current thread as we will unncessarily add to execution time
                    this.lastParallelRunCleanUpTask = Task.Run(() =>
                    {
                        this.UpdateParallelLevel(0);
                    });
                }
            }

            return(allRunsCompleted);
        }
        private int StartTestRunPrivate(ITestRunEventsHandler runEventsHandler)
        {
            this.currentRunEventsHandler = runEventsHandler;

            // Cleanup Task for cleaning up the parallel executors except for the default one
            // We do not do this in Sync so that this task does not add up to execution time
            if (this.lastParallelRunCleanUpTask != null)
            {
                try
                {
                    this.lastParallelRunCleanUpTask.Wait();
                }
                catch (Exception ex)
                {
                    // if there is an exception disposing off concurrent executors ignore it
                    if (EqtTrace.IsWarningEnabled)
                    {
                        EqtTrace.Warning("ParallelTestRunnerServiceClient: Exception while invoking an action on DiscoveryManager: {0}", ex);
                    }
                }

                this.lastParallelRunCleanUpTask = null;
            }

            // Reset the runcomplete data
            this.runCompletedClients = 0;

            // One data aggregator per parallel run
            this.currentRunDataAggregator    = new ParallelRunDataAggregator();
            this.concurrentManagerHandlerMap = new Dictionary <IProxyExecutionManager, ITestRunEventsHandler>();

            for (int i = 0; i < this.concurrentManagerInstances.Length; i++)
            {
                var concurrentManager = this.concurrentManagerInstances[i];

                var parallelEventsHandler = new ParallelRunEventsHandler(
                    concurrentManager,
                    runEventsHandler,
                    this,
                    this.currentRunDataAggregator);
                this.concurrentManagerHandlerMap.Add(concurrentManager, parallelEventsHandler);

                Task.Run(() => this.StartTestRunOnConcurrentManager(concurrentManager));
            }

            return(1);
        }