public void RecordExecutionRun(ExecutionRecorderParams recorderParams, ExecutionDetails executionDetails)
        {
            string mutexId = GetMutexId(recorderParams.ExecutionTestType);

            //Add a report URL
            var reportUrlFormat = "=HYPERLINK(\"https://{0}/services/reports/{1}?operation=download&user={2}&password={3}&format=html\")";
            var reportUrl       = string.Format(reportUrlFormat, recorderParams.Host, executionDetails.reportKey, recorderParams.UserName, recorderParams.Password);


            if (!string.IsNullOrEmpty(executionDetails.executionId))
            {
                string executionUrl = string.Format("https://{0}/services/executions/{1}?operation=status&user={2}&password={3}",
                                                    recorderParams.Host,
                                                    recorderParams.ExecutionId,
                                                    recorderParams.UserName,
                                                    recorderParams.Password);
                executionDetails.executionDetailsUrl = executionUrl;
            }

            if (!string.IsNullOrEmpty(executionDetails.reportKey))
            {
                executionDetails.reportUrl = reportUrl;
            }

            WriteReportDetails(executionDetails, recorderParams, mutexId);
        }
        public async Task <ExecutionDetails> GetExecutionDetails(ExecutionRecorderParams recorderParams)
        {
            ExecutionDetails executionDetails = null;

            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri("http://localhost:9000/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Add("Accept", "application/json");

                string executionUrl = string.Format("https://{0}/services/executions/{1}?operation=status&user={2}&password={3}",
                                                    recorderParams.Host,
                                                    recorderParams.ExecutionId,
                                                    recorderParams.UserName,
                                                    recorderParams.Password);

                // HTTP GET
                HttpResponseMessage response = await client.GetAsync(executionUrl);

                if (response.IsSuccessStatusCode)
                {
                    var executionJson = await response.Content.ReadAsStringAsync();

                    JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
                    executionDetails = jsonSerializer.Deserialize <ExecutionDetails>(executionJson);

                    //Add a report URL
                    var reportUrlFormat = "=HYPERLINK(\"https://{0}/services/reports/{1}?operation=download&user={2}&password={3}&format=html\")";
                    var reportUrl       = string.Format(reportUrlFormat, recorderParams.Host, executionDetails.reportKey, recorderParams.UserName, recorderParams.Password);
                    executionDetails.reportUrl           = reportUrl;
                    executionDetails.executionDetailsUrl = executionUrl;
                }
                else
                {
                    executionDetails             = new ExecutionDetails();
                    executionDetails.completed   = Constants.UNKNOWN;
                    executionDetails.executionId = recorderParams.ExecutionId;
                    executionDetails.status      = Constants.UNKNOWN;
                    executionDetails.description = "Call to get execution details failed. Status was " + response.StatusCode;
                    executionDetails.reason      = Constants.UNKNOWN;
                }

                if (executionDetails.status == Constants.COMPLETED && recorderParams.ExecutionErrors.Count > 0)
                {
                    var message = string.Format("Failed with {0} unhandled errors", recorderParams.ExecutionErrors.Count);
                    executionDetails.status      = message;
                    executionDetails.description = message;
                }
                else if (executionDetails.status == Constants.COMPLETED && recorderParams.ExecutionErrors.Count == 0)
                {
                    executionDetails.status = Constants.PASS;
                }
            }
            return(executionDetails);
        }
        private void WriteReportDetails(ExecutionDetails details, ExecutionRecorderParams recorderParams, string mutexId)
        {
            //We must use a global mutex here so we don't get lock contentions with other runs.

            // get application GUID as defined in AssemblyInfo.cs
            string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();

            // Need a place to store a return value in Mutex() constructor call
            bool createdNew;
            var  allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
            var  securitySettings  = new MutexSecurity();

            securitySettings.AddAccessRule(allowEveryoneRule);

            using (var mutex = new Mutex(false, mutexId, out createdNew, securitySettings))
            {
                var hasHandle = false;
                try
                {
                    try
                    {
                        hasHandle = mutex.WaitOne(12000, false);
                        if (hasHandle == false)
                        {
                            throw new TimeoutException("Timeout waiting for exclusive access");
                        }
                    }
                    catch (AbandonedMutexException)
                    {
                        // Log the fact that the mutex was abandoned in another process, it will still get acquired
                        hasHandle = true;
                    }

                    // !!!!!!! Perform your work here.
                    //Get the run identifier that ties parallel runs together - if none, just use now date
                    string runIdentifier = recorderParams.CurrentDevice.RunIdentifier ?? string.Format("{0:yyyy-MM-dd_hh-mm-ss-tt}", DateTime.Now);
                    string logName       = recorderParams.ExecutionTestType == TestType.Appium ? "AppiumTestsLog" : "SeleniumTestsLog";
                    string logFileName   = string.Format("{0}-{1}.csv", logName, runIdentifier);

                    string path = recorderParams.BaseProjectPath + @"\RunReports\" + logFileName;

                    //---Note: if you receive an exception here - You may need to run Visual Studio with Administrator priveleges
                    //			Just right click Visual Studio from the Start Menu and launch as Administrator
                    if (!File.Exists(path))
                    {
                        // Create file with a header if none exists
                        using (StreamWriter sw = File.CreateText(path))
                        {
                            sw.WriteLine(string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}",
                                                       "ExecutionId",
                                                       "ReportKey",
                                                       "ScriptName",
                                                       "DeviceName",
                                                       "DeviceId",
                                                       "ExecutionStatus",
                                                       "TestMethodName",
                                                       "Description",
                                                       "ReportDownloadLink",
                                                       "ExecutionDetailsLink"));
                        }
                    }

                    using (StreamWriter sw = File.AppendText(path))
                    {
                        foreach (ExecutionError errorItem in recorderParams.ExecutionErrors)
                        {
                            var errorLine = string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}",
                                                          details.executionId,
                                                          details.reportKey,
                                                          recorderParams.TestCaseName,
                                                          recorderParams.CurrentDevice.Name,
                                                          recorderParams.CurrentDevice.DeviceID,
                                                          "Unhandled Error Occurred",
                                                          errorItem.TestMethodName,
                                                          //Replace double quotes with single so we can output to CSV without it bleeding into other cells
                                                          "\"" + errorItem.Message.Replace("\"", "'") + " stacktrace: "
                                                          + errorItem.ExecutionException.StackTrace.Replace("\"", "'") + "\"",
                                                          details.reportUrl,
                                                          details.executionDetailsUrl);
                            sw.WriteLine(errorLine);
                        }



                        var newLine = string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}",
                                                    details.executionId,
                                                    details.reportKey,
                                                    recorderParams.TestCaseName,
                                                    recorderParams.CurrentDevice.Name,
                                                    recorderParams.CurrentDevice.DeviceID,
                                                    details.status,
                                                    details.testMethodName,
                                                    //Replace double quotes with single so we can output to CSV without it bleeding into other cells
                                                    "\"" + details.description.Replace("\"", "'") + "\"",
                                                    details.reportUrl,
                                                    details.executionDetailsUrl);
                        sw.WriteLine(newLine);
                    }
                }
                finally
                {
                    if (hasHandle)
                    {
                        mutex.ReleaseMutex();
                    }
                }
            }
        }