private List <CoverageProject> GetCoverageProjects(TestConfiguration testConfiguration)
        {
            var userRunSettings      = testConfiguration.UserRunSettings;
            var runSettingsRetriever = new RunSettingsRetriever();
            var testContainers       = testConfiguration.Containers;

            return(testConfiguration.Containers.Select(container =>
            {
                var project = new CoverageProject();
                project.ProjectName = container.ProjectName;
                project.TestDllFile = container.Source;
                project.Is64Bit = container.TargetPlatform.ToString().ToLower().Equals("x64");

                var containerData = container.ProjectData;
                project.ProjectFile = container.ProjectData.ProjectFilePath;
                project.RunSettingsFile = ThreadHelper.JoinableTaskFactory.Run(() => runSettingsRetriever.GetRunSettingsFileAsync(userRunSettings, containerData));
                return project;
            }).ToList());
        }
        private void OperationState_StateChanged(object sender, OperationStateChangedEventArgs e)
        {
            try
            {
                if (e.State == TestOperationStates.TestExecutionStarting)
                {
                    StopCoverageProcess();                     // just to be sure
                }

                if (e.State == TestOperationStates.TestExecutionFinished)
                {
                    var settings = AppOptions.Get();

                    if (!settings.Enabled)
                    {
                        FCCEngine.CoverageLines.Clear();
                        UpdateMarginTags?.Invoke(this, null);
                        UpdateOutputWindow?.Invoke(this, null);
                        return;
                    }

                    Logger.Log("================================== START ==================================");

                    var darkMode = CurrentTheme.Equals("Dark", StringComparison.OrdinalIgnoreCase);

                    CoverageProject[] projects = null;
                    try
                    {
                        var testConfiguration = new Operation(e.Operation).Configuration;

                        var userRunSettings      = testConfiguration.UserRunSettings;
                        var runSettingsRetriever = new RunSettingsRetriever();
                        var testContainers       = testConfiguration.Containers;

                        projects = testConfiguration.Containers.Select(container =>
                        {
                            var project         = new CoverageProject();
                            project.ProjectName = container.ProjectName;
                            project.TestDllFile = container.Source;
                            project.Is64Bit     = container.TargetPlatform.ToString().ToLower().Equals("x64");

                            var containerData       = container.ProjectData;
                            project.ProjectFile     = container.ProjectData.ProjectFilePath;
                            project.RunSettingsFile = ThreadHelper.JoinableTaskFactory.Run(() => runSettingsRetriever.GetRunSettingsFileAsync(userRunSettings, containerData));
                            return(project);
                        }).ToArray();
                    }catch (Exception exc)
                    {
                        throw new Exception("Error test container discoverer reflection", exc);
                    }


                    _reloadCoverageThread = new Thread(() =>
                    {
                        try
                        {
                            // compute coverage

                            FCCEngine.ReloadCoverage(projects, darkMode);

                            // update margins

                            {
                                UpdateMarginTagsEventArgs updateMarginTagsEventArgs = null;

                                try
                                {
                                    updateMarginTagsEventArgs = new UpdateMarginTagsEventArgs
                                    {
                                    };
                                }
                                catch
                                {
                                    // ignore
                                }
                                finally
                                {
                                    UpdateMarginTags?.Invoke(this, updateMarginTagsEventArgs);
                                }
                            }

                            // update output window

                            {
                                UpdateOutputWindowEventArgs updateOutputWindowEventArgs = null;

                                try
                                {
                                    updateOutputWindowEventArgs = new UpdateOutputWindowEventArgs
                                    {
                                        HtmlContent = File.ReadAllText(FCCEngine.HtmlFilePath)
                                    };
                                }
                                catch
                                {
                                    // ignore
                                }
                                finally
                                {
                                    UpdateOutputWindow?.Invoke(this, updateOutputWindowEventArgs);
                                }
                            }

                            // log

                            Logger.Log("================================== DONE ===================================");
                        }
                        catch (Exception exception)
                        {
                            if (!(exception is ThreadAbortException) && _reloadCoverageThread != null)
                            {
                                Logger.Log("Error", exception);
                                Logger.Log("================================== ERROR ==================================");
                            }
                        }
                    });

                    _reloadCoverageThread.Start();
                }
            }
            catch (Exception exception)
            {
                Logger.Log("Error processing unit test events", exception);
            }
        }
        public static void LoadCoverageFromTestDllFile(string testDllFile, Action <Exception> callback = null)
        {
            ThreadPool.QueueUserWorkItem(x =>
            {
                try
                {
                    // project folder

                    var testProjectFolder = GetProjectFolderFromPath(testDllFile);

                    if (string.IsNullOrWhiteSpace(testProjectFolder))
                    {
                        throw new Exception("Could not establish project folder");
                    }

                    // coverage folder

                    var coverageFolder = Path.Combine(TempFolder, ConvertPathToName(testProjectFolder));

                    if (!Directory.Exists(coverageFolder))
                    {
                        Directory.CreateDirectory(coverageFolder);
                    }

                    // coverage file

                    var coverageFile = Path.Combine(coverageFolder, "coverage.json");

                    if (File.Exists(coverageFile))
                    {
                        File.Delete(coverageFile);
                    }

                    // sync built files to coverage folder

                    var buildFolder = Path.GetDirectoryName(testDllFile);
                    FileSynchronizationUtil.Synchronize(buildFolder, coverageFolder);

                    // coverage process

                    RunCoverlet(testDllFile, coverageFolder, coverageFile);

                    // reload

                    var coverageFileContent = File.ReadAllText(coverageFile);
                    var coverageFileJObject = JObject.Parse(coverageFileContent);

                    foreach (var coverageDllFileProperty in coverageFileJObject.Properties())
                    {
                        var coverageDllFileJObject = (JObject)coverageDllFileProperty.Value;

                        foreach (var sourceFileProperty in coverageDllFileJObject.Properties())
                        {
                            var sourceFilePath    = sourceFileProperty.Name;
                            var projectFolderPath = GetProjectFolderFromPath(sourceFilePath);

                            if (string.IsNullOrWhiteSpace(projectFolderPath))
                            {
                                continue;
                            }

                            var sourceFileProject = CoverageProjects.SingleOrDefault(pr => pr.FolderPath.Equals(projectFolderPath, StringComparison.OrdinalIgnoreCase));

                            if (sourceFileProject == null)
                            {
                                sourceFileProject = new CoverageProject
                                {
                                    FolderPath = projectFolderPath
                                };

                                CoverageProjects.Add(sourceFileProject);
                            }

                            var sourceFile = sourceFileProject.SourceFiles.SingleOrDefault(sf => sf.FilePath.Equals(sourceFilePath, StringComparison.OrdinalIgnoreCase));

                            if (sourceFile == null)
                            {
                                sourceFile = new CoverageSourceFile
                                {
                                    FilePath = sourceFilePath
                                };

                                sourceFileProject.SourceFiles.Add(sourceFile);
                            }

                            sourceFile.Lines.Clear();
                            var sourceFileJObject = (JObject)sourceFileProperty.Value;

                            foreach (var classProperty in sourceFileJObject.Properties())
                            {
                                var className    = classProperty.Name;
                                var classJObject = (JObject)classProperty.Value;

                                foreach (var methodProperty in classJObject.Properties())
                                {
                                    var methodName    = methodProperty.Name;
                                    var methodJObject = (JObject)methodProperty.Value;
                                    var linesJObject  = (JObject)methodJObject["Lines"];

                                    foreach (var lineProperty in linesJObject.Properties())
                                    {
                                        var lineNumber   = int.Parse(lineProperty.Name);
                                        var lineHitCount = lineProperty.Value.Value <int>();

                                        sourceFile.Lines.Add(new CoverageLine
                                        {
                                            ClassName  = className,
                                            MethodName = methodName,
                                            LineNumber = lineNumber,
                                            HitCount   = lineHitCount
                                        });
                                    }
                                }
                            }
                        }
                    }

                    // callback

                    callback?.Invoke(null);
                }
                catch (Exception ex)
                {
                    callback?.Invoke(ex);
                }
            });
        }
        private void OperationState_StateChanged(object sender, OperationStateChangedEventArgs e)
        {
            try
            {
                if (e.State == TestOperationStates.TestExecutionStarting)
                {
                    try
                    {
                        _reloadCoverageThread?.Abort();
                    }
                    catch
                    {
                        // ignore
                    }
                    finally
                    {
                        _reloadCoverageThread = null;
                        FCCEngine.ClearProcesses();
                    }
                }

                if (e.State == TestOperationStates.TestExecutionFinished)
                {
                    var settings = AppOptions.Get();

                    if (!settings.Enabled)
                    {
                        FCCEngine.CoverageLines.Clear();
                        UpdateMarginTags?.Invoke(this, null);
                        UpdateOutputWindow?.Invoke(this, null);
                        return;
                    }

                    Logger.Log("================================== START ==================================");

                    var operationType     = e.Operation.GetType();
                    var darkMode          = CurrentTheme.Equals("Dark", StringComparison.OrdinalIgnoreCase);
                    var testConfiguration = (operationType.GetProperty("Configuration") ?? operationType.GetProperty("Configuration", BindingFlags.Instance | BindingFlags.NonPublic)).GetValue(e.Operation);
                    var testContainers    = ((IEnumerable <object>)testConfiguration.GetType().GetProperty("Containers").GetValue(testConfiguration)).ToArray();
                    var projects          = new List <CoverageProject>();

                    foreach (var container in testContainers)
                    {
                        var project           = new CoverageProject();
                        var containerType     = container.GetType();
                        var containerData     = containerType.GetProperty("ProjectData").GetValue(container);
                        var containerDataType = containerData.GetType();

                        project.ProjectGuid = containerType.GetProperty("ProjectGuid").GetValue(container).ToString();
                        project.ProjectName = containerType.GetProperty("ProjectName").GetValue(container).ToString();
                        project.TestDllFileInOutputFolder = containerType.GetProperty("Source").GetValue(container).ToString();
                        project.TestDllCompilationMode    = AssemblyUtil.GetCompilationMode(project.TestDllFileInOutputFolder);
                        project.ProjectFile = containerDataType.GetProperty("ProjectFilePath", BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic).GetValue(containerData).ToString();

                        var defaultOutputFolder = Path.GetDirectoryName(containerDataType.GetProperty("DefaultOutputPath", BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic).GetValue(containerData).ToString());
                        project.WorkFolder = Path.Combine(Path.GetDirectoryName(defaultOutputFolder), "fine-code-coverage");

                        projects.Add(project);
                    }

                    _reloadCoverageThread = new Thread(() =>
                    {
                        try
                        {
                            // compute coverage

                            FCCEngine.ReloadCoverage(projects.ToArray(), darkMode);

                            // update margins

                            {
                                UpdateMarginTagsEventArgs updateMarginTagsEventArgs = null;

                                try
                                {
                                    updateMarginTagsEventArgs = new UpdateMarginTagsEventArgs
                                    {
                                    };
                                }
                                catch
                                {
                                    // ignore
                                }
                                finally
                                {
                                    UpdateMarginTags?.Invoke(this, updateMarginTagsEventArgs);
                                }
                            }

                            // update output window

                            {
                                UpdateOutputWindowEventArgs updateOutputWindowEventArgs = null;

                                try
                                {
                                    updateOutputWindowEventArgs = new UpdateOutputWindowEventArgs
                                    {
                                        HtmlContent = File.ReadAllText(FCCEngine.HtmlFilePath)
                                    };
                                }
                                catch
                                {
                                    // ignore
                                }
                                finally
                                {
                                    UpdateOutputWindow?.Invoke(this, updateOutputWindowEventArgs);
                                }
                            }

                            // log

                            Logger.Log("================================== DONE ===================================");
                        }
                        catch (Exception exception)
                        {
                            if (!(exception is ThreadAbortException) && _reloadCoverageThread != null)
                            {
                                Logger.Log("Error", exception);
                                Logger.Log("================================== ERROR ==================================");
                            }
                        }
                    });

                    _reloadCoverageThread.Start();
                }
            }
            catch (Exception exception)
            {
                Logger.Log("Error processing unit test events", exception);
            }
        }