public void CheckLocalAndRemoteBinaryFailsInvalidRemoteBuildId()
        {
            fileSystem.AddDirectory(searchPaths[0]);
            fileSystem.AddFile(localPaths[0], new MockFileData(""));
            binaryFileUtil.ReadBuildIdAsync(localPaths[0]).Returns(Task.FromResult(validBuildId));

            binaryFileUtil.ReadBuildIdAsync(remoteTargetPath, target).Returns(
                Task.FromException <BuildId>(new InvalidBuildIdException("test")));

            Exception ex = Assert.ThrowsAsync <PreflightBinaryCheckerException>(async() =>
                                                                                await action.RecordAsync(checker.CheckLocalAndRemoteBinaryOnLaunchAsync(
                                                                                                             searchPaths, executable, target, remoteTargetPath, action)));

            Assert.IsInstanceOf <BinaryFileUtilException>(ex.InnerException);
            Assert.AreEqual(ErrorStrings.FailedToCheckRemoteBuildIdWithExplanation(
                                ex.InnerException.Message), ex.Message);

            metrics.Received().RecordEvent(
                DeveloperEventType.Types.Type.VsiDebugPreflightBinaryCheck,
                Arg.Is <DeveloperLogEvent>(m =>
                                           m.StatusCode == DeveloperEventStatus.Types.Code.InvalidConfiguration &&
                                           m.DebugPreflightCheckData.CheckType ==
                                           DebugPreflightCheckData.Types.CheckType.RunAndAttach &&
                                           m.DebugPreflightCheckData.RemoteBuildIdCheckResult ==
                                           DebugPreflightCheckData.Types.RemoteBuildIdCheckResult
                                           .InvalidRemoteBuildId));
        }
        public void CheckRemoteBinaryFailsToRunRemoteCommand()
        {
            binaryFileUtil.ReadBuildIdAsync(remoteTargetPath, target).Returns(
                Task.FromException <BuildId>(new BinaryFileUtilException("test",
                                                                         new ProcessException("inner"))));

            Exception ex = Assert.ThrowsAsync <PreflightBinaryCheckerException>(async() =>
                                                                                await action.RecordAsync(checker.CheckRemoteBinaryOnAttachAsync(remoteTargetPid,
                                                                                                                                                target, action)));

            Assert.IsInstanceOf <BinaryFileUtilException>(ex.InnerException);
            Assert.AreEqual(ErrorStrings.FailedToCheckRemoteBuildIdWithExplanation(
                                ex.InnerException.Message), ex.Message);

            metrics.Received().RecordEvent(
                DeveloperEventType.Types.Type.VsiDebugPreflightBinaryCheck,
                Arg.Is <DeveloperLogEvent>(m =>
                                           m.StatusCode ==
                                           DeveloperEventStatus.Types.Code.ExternalToolUnavailable &&
                                           m.DebugPreflightCheckData.CheckType ==
                                           DebugPreflightCheckData.Types.CheckType.AttachOnly &&
                                           m.DebugPreflightCheckData.RemoteBuildIdCheckResult ==
                                           DebugPreflightCheckData.Types.RemoteBuildIdCheckResult
                                           .RemoteCommandError));
        }
        /// <summary>
        /// Check that the specified remote process's binary has a valid build id. Log messages and
        /// record metrics to indicate the result of the checks.
        /// </summary>
        /// <param name="pid">Process ID of the remote process that we will check</param>
        /// <param name="target">The machine that should have a valid remote binary</param>
        public async Task CheckRemoteBinaryOnAttachAsync(uint pid, SshTarget target,
                                                         IAction action)
        {
            var remoteTargetPath = string.Format(PID_EXE_PATH_TEMPLATE, pid);

            try
            {
                var dataRecorder =
                    new DataRecorder(action, DebugPreflightCheckData.Types.CheckType.AttachOnly);

                BuildId remoteBuildId;
                try
                {
                    remoteBuildId = await binaryFileUtil.ReadBuildIdAsync(remoteTargetPath,
                                                                          target);
                }
                catch (BinaryFileUtilException e) when(dataRecorder.RemoteBuildIdError(e))
                {
                    Debug.Fail("Exception should never be caught");
                    throw;
                }

                // Log the remote Build ID for debugging purposes.
                dataRecorder.ValidRemoveBuildId();
                Trace.WriteLine("Remote build ID: " + remoteBuildId.ToString());
            }
            catch (BinaryFileUtilException e)
            {
                Trace.WriteLine($"Failed to read build ID for '{remoteTargetPath}' " +
                                $"on '{target.GetString()}': " + e.ToString());
                throw new PreflightBinaryCheckerException(
                          ErrorStrings.FailedToCheckRemoteBuildIdWithExplanation(e.Message), e);
            }
        }
        /// <summary>
        /// Check that the remote binary for the given executable exists and has a valid build id.
        /// Look for a local copy of the binary based on the name and build id. Log messages and
        /// record metrics to indicate the result of the checks.
        /// </summary>
        /// <remarks>
        /// It is assumed the remote binary is at YetiConstants.RemoteDeployPath.
        /// </remarks>
        /// <param name="libPaths">LLDB search paths to check for local binaries</param>
        /// <param name="executable">Name of the binary to look for locally and remotely</param>
        /// <param name="target">The machine that should have a valid remote binary</param>
        public async Task CheckLocalAndRemoteBinaryOnLaunchAsync(
            IEnumerable <string> libPaths, string executable, SshTarget target,
            string remoteTargetPath, IAction action)
        {
            // Check that the remote binary has a build id and try to match it against
            // the local candidates to find the matching local binary.
            IEnumerable <string> localCandidatePaths = new List <string>();

            try
            {
                var dataRecorder = new DataRecorder(action,
                                                    DebugPreflightCheckData.Types.CheckType.RunAndAttach);
                // Get the remote build id and only continue if this step succeeds.
                BuildId remoteBuildId;
                try
                {
                    remoteBuildId = await binaryFileUtil.ReadBuildIdAsync(remoteTargetPath,
                                                                          target);
                }
                catch (BinaryFileUtilException e) when(dataRecorder.RemoteBuildIdError(e))
                {
                    Debug.Fail("Exception should never be caught");
                    throw;
                }

                // Log the remote Build ID for debugging purposes.
                dataRecorder.ValidRemoveBuildId();
                Trace.WriteLine("Remote build ID: " + remoteBuildId.ToString());

                // Make sure there is a local binary with the same name.
                localCandidatePaths =
                    FindExecutableCandidates(libPaths, executable);
                if (!localCandidatePaths.Any())
                {
                    dataRecorder.LocalBinaryCheckResult(
                        DebugPreflightCheckData.Types.LocalBinarySearchResult.NoCandidates);
                    throw new NoLocalCandidatesException();
                }

                // Check local candidates to find one matching the remote build id.
                // Ignore local candidates that are missing a build id.
                if (await HasMatchingBuildIdAsync(
                        localCandidatePaths, executable, remoteTargetPath, remoteBuildId))
                {
                    dataRecorder.LocalBinaryCheckResult(
                        DebugPreflightCheckData.Types.LocalBinarySearchResult.BinaryMatch);
                }
                else
                {
                    dataRecorder.LocalBinaryCheckResult(
                        DebugPreflightCheckData.Types.LocalBinarySearchResult.BinaryMismatch);
                    throw new NoMatchingLocalCandidatesException();
                }
            }
            catch (BinaryFileUtilException e)
            {
                Trace.WriteLine($"Failed to read build ID for '{remoteTargetPath}' " +
                                $"on '{target.GetString()}': " + e.ToString());
                throw new PreflightBinaryCheckerException(
                          ErrorStrings.FailedToCheckRemoteBuildIdWithExplanation(e.Message), e);
            }
            catch (NoLocalCandidatesException e)
            {
                Trace.WriteLine($"Unable to find executable '{executable}' on LLDB search paths.");
                throw new PreflightBinaryCheckerException(
                          ErrorStrings.UnableToFindExecutable(executable),
                          ErrorStrings.ExecutableCheckDetails(libPaths), e);
            }
            catch (NoMatchingLocalCandidatesException e)
            {
                Trace.WriteLine(
                    $"No local copy of '{executable}' matched the build ID of the remote binary");
                throw new PreflightBinaryCheckerException(
                          ErrorStrings.UnableToFindExecutableMatchingRemoteBinary(executable,
                                                                                  remoteTargetPath),
                          ErrorStrings.BuildIdCheckDetails(localCandidatePaths, libPaths), e);
            }
        }