private void StartTests(IUnitTestRun run, TaskCompletionSource <bool> tcs, Lifetime taskLifetime)
        {
            myLogger.Trace("RunUnitTestLaunch.Start.");
            var rdTask = myBackendUnityHost.BackendUnityModel.Value.RunUnitTestLaunch.Start(Unit.Instance);

            rdTask?.Result.Advise(taskLifetime, res =>
            {
                myLogger.Trace($"RunUnitTestLaunch result = {res.Result}");
                if (!res.Result)
                {
                    var defaultMessage = "Failed to start tests in Unity.";

                    var isCoverage =
                        run.HostController.HostId != WellKnownHostProvidersIds.DebugProviderId &&
                        run.HostController.HostId != WellKnownHostProvidersIds.RunProviderId;

                    if (myPackageValidator.HasNonCompatiblePackagesCombination(isCoverage, out var message))
                    {
                        defaultMessage = $"{defaultMessage} {message}";
                    }

                    if (myBackendUnityHost.BackendUnityModel.Value.UnitTestLaunch.Value.TestMode == TestMode.Play)
                    {
                        if (!myPackageValidator.CanRunPlayModeTests(out var playMessage))
                        {
                            defaultMessage = $"{defaultMessage} {playMessage}";
                        }
                    }
        private void RefreshAndRunTask(IUnitTestRun run, TaskCompletionSource <bool> tcs, Lifetime taskLifetime)
        {
            var cancellationTs = run.GetData(ourCancellationTokenSourceKey);

            myLogger.Verbose("Before calling Refresh.");
            Refresh(run.Lifetime, cancellationTs.NotNull().Token).GetAwaiter().OnCompleted(() =>
            {
                // KS: Can't use run.Lifetime for ExecuteOrQueueEx here and in all similar places: run.Lifetime is terminated when
                // Unit Test Session is closed from UI without cancelling the run. This will leave task completion source in running state forever.
                mySolution.Locks.ExecuteOrQueueEx(myLifetime, "Check compilation", () =>
                {
                    if (!run.Lifetime.IsAlive || cancellationTs.IsCancellationRequested)
                    {
                        tcs.SetCanceled();
                        return;
                    }

                    if (myEditorProtocol.UnityModel.Value == null)
                    {
                        myLogger.Verbose("Unity Editor connection unavailable.");
                        tcs.SetException(new Exception("Unity Editor connection unavailable."));
                        return;
                    }

                    var task = myEditorProtocol.UnityModel.Value.GetCompilationResult.Start(Unit.Instance);
                    task.Result.AdviseNotNull(myLifetime, result =>
                    {
                        if (!run.Lifetime.IsAlive || cancellationTs.IsCancellationRequested)
                        {
                            tcs.SetCanceled();
                        }
                        else if (!result.Result)
                        {
                            tcs.SetException(new Exception("There are errors during compilation in Unity."));

                            mySolution.Locks.ExecuteOrQueueEx(run.Lifetime, "RunViaUnityEditorStrategy compilation failed",
                                                              () =>
                            {
                                var notification = new NotificationModel("Compilation failed",
                                                                         "Script compilation in Unity failed, so tests were not started.", true,
                                                                         RdNotificationEntryType.INFO);
                                myNotificationsModel.Notification(notification);
                            });
                            myUnityHost.PerformModelAction(model => model.ActivateUnityLogView());
                        }
                        else
                        {
                            var launch = SetupLaunch(run);
                            mySolution.Locks.ExecuteOrQueueEx(myLifetime, "ExecuteRunUT", () =>
                            {
                                if (!run.Lifetime.IsAlive || cancellationTs.IsCancellationRequested)
                                {
                                    tcs.SetCanceled();
                                    return;
                                }

                                if (myEditorProtocol.UnityModel.Value == null)
                                {
                                    tcs.SetException(new Exception("Unity Editor connection unavailable."));
                                    return;
                                }

                                myEditorProtocol.UnityModel.ViewNotNull(taskLifetime, (lt, model) =>
                                {
                                    // recreate UnitTestLaunch in case of AppDomain.Reload, which is the case with PlayMode tests
                                    model.UnitTestLaunch.SetValue(launch);
                                    SubscribeResults(run, lt, tcs, launch);
                                });

                                myUnityProcessId.When(taskLifetime, (int?)null, _ => tcs.TrySetException(new Exception("Unity Editor has been closed.")));

                                var rdTask = myEditorProtocol.UnityModel.Value.RunUnitTestLaunch.Start(Unit.Instance);
                                rdTask?.Result.Advise(taskLifetime, res =>
                                {
                                    myLogger.Trace($"RunUnitTestLaunch result = {res.Result}");
                                    if (!res.Result)
                                    {
                                        var defaultMessage = "Failed to start tests in Unity.";

                                        var isCoverage =
                                            run.HostController.HostId != WellKnownHostProvidersIds.DebugProviderId &&
                                            run.HostController.HostId != WellKnownHostProvidersIds.RunProviderId;

                                        if (myPackageValidator.HasNonCompatiblePackagesCombination(isCoverage, out var message))
                                        {
                                            defaultMessage = $"{defaultMessage} {message}";
                                        }

                                        if (myEditorProtocol.UnityModel.Value.UnitTestLaunch.Value.TestMode == TestMode.Play)
                                        {
                                            if (!myPackageValidator.CanRunPlayModeTests(out var playMessage))
                                            {
                                                defaultMessage = $"{defaultMessage} {playMessage}";
                                            }
                                        }

                                        tcs.TrySetException(new Exception(defaultMessage));
                                    }
                                });
                            });
                        }
                    });
        private void RefreshAndRunTask(IUnitTestRun run, TaskCompletionSource <bool> tcs, Lifetime taskLifetime)
        {
            var cancellationTs    = run.GetData(ourCancellationTokenSourceKey);
            var cancellationToken = cancellationTs.NotNull().Token;

            myLogger.Trace("Before calling Refresh.");
            Refresh(run.Lifetime, tcs, cancellationToken).ContinueWith(__ =>
            {
                if (tcs.Task.IsCanceled || tcs.Task.IsFaulted) // Refresh failed or was stopped
                {
                    return;
                }

                myLogger.Trace("Refresh. OnCompleted.");
                // KS: Can't use run.Lifetime for ExecuteOrQueueEx here and in all similar places: run.Lifetime is terminated when
                // Unit Test Session is closed from UI without cancelling the run. This will leave task completion source in running state forever.
                mySolution.Locks.ExecuteOrQueueEx(myLifetime, "Check compilation", () =>
                {
                    if (!run.Lifetime.IsAlive || cancellationTs.IsCancellationRequested)
                    {
                        tcs.TrySetCanceled();
                        return;
                    }

                    var launch = SetupLaunch(run);

                    if (myEditorProtocol.UnityModel.Value == null)
                    {
                        tcs.SetException(new Exception("Unity Editor connection unavailable."));
                        return;
                    }

                    myEditorProtocol.UnityModel.ViewNotNull(taskLifetime, (lt, model) =>
                    {
                        // recreate UnitTestLaunch in case of AppDomain.Reload, which is the case with PlayMode tests
                        myLogger.Trace("UnitTestLaunch.SetValue.");
                        model.UnitTestLaunch.SetValue(launch);
                        SubscribeResults(run, lt, tcs, launch);
                    });

                    myLogger.Trace("RunUnitTestLaunch.Start.");
                    var rdTask = myEditorProtocol.UnityModel.Value.RunUnitTestLaunch.Start(Unit.Instance);
                    rdTask?.Result.Advise(taskLifetime, res =>
                    {
                        myLogger.Trace($"RunUnitTestLaunch result = {res.Result}");
                        if (!res.Result)
                        {
                            var defaultMessage = "Failed to start tests in Unity.";

                            var isCoverage =
                                run.HostController.HostId != WellKnownHostProvidersIds.DebugProviderId &&
                                run.HostController.HostId != WellKnownHostProvidersIds.RunProviderId;

                            if (myPackageValidator.HasNonCompatiblePackagesCombination(isCoverage, out var message))
                            {
                                defaultMessage = $"{defaultMessage} {message}";
                            }

                            if (myEditorProtocol.UnityModel.Value.UnitTestLaunch.Value.TestMode == TestMode.Play)
                            {
                                if (!myPackageValidator.CanRunPlayModeTests(out var playMessage))
                                {
                                    defaultMessage = $"{defaultMessage} {playMessage}";
                                }
                            }

                            tcs.TrySetException(new Exception(defaultMessage));
                        }
                    });