private void DisplayComponent(IReadOnlyInputComponent inputComponent) { var score = inputComponent.GetMutationScore(); // Convert the threshold integer values to decimal values _chalk.Default($"[{ inputComponent.DetectedMutants.Count()}/{ inputComponent.TotalMutants.Count()} "); if (inputComponent.IsExcluded) { _chalk.DarkGray($"(Excluded)"); } else if (!score.HasValue) { _chalk.DarkGray($"(- %)"); } else { // print the score as a percentage string scoreText = $"({ (score.Value / 100).ToString("p", CultureInfo.InvariantCulture)})"; if (inputComponent.CheckHealth(_options.Thresholds) is Health.Good) { _chalk.Green(scoreText); } else if (inputComponent.CheckHealth(_options.Thresholds) is Health.Warning) { _chalk.Yellow(scoreText); } else if (inputComponent.CheckHealth(_options.Thresholds) is Health.Danger) { _chalk.Red(scoreText); } } _chalk.Default($"]{Environment.NewLine}"); }
private void MutantAnalyzerOnMutantExecuted(object sender, CppMutantEventArgs e) { lock (Sync) { var mutant = e.Mutant; var lineNumber = mutant.Mutation.LineNumber; var status = $"{Environment.NewLine}Line: {lineNumber} - {mutant.ResultStatus.ToString()} - {mutant.Mutation.DisplayName}".PrintWithDateTimeSimple(); if (mutant.ResultStatus == MutantStatus.Survived) { _chalk.Yellow($"{status}{Environment.NewLine}"); } else if (mutant.ResultStatus == MutantStatus.BuildError) { _chalk.Red($"{status}{Environment.NewLine}"); } else if (mutant.ResultStatus == MutantStatus.Timeout) { _chalk.Cyan($"{status}{Environment.NewLine}"); } else { _chalk.Green($"{status}{Environment.NewLine}"); } if (_options.EnableDiagnostics) { _chalk.Red($"{e.BuildLog.ConvertToPlainText()}{Environment.NewLine}"); _chalk.Red($"{e.TestLog.ConvertToPlainText()}{Environment.NewLine}"); } _mutantProgress++; UpdateProgress(); } }
public void OnAllMutantsTested(IReadOnlyInputComponent inputComponent) { // setup display handlers inputComponent.DisplayFolder = (int depth, IReadOnlyInputComponent current) => { // show depth _chalk.Default($"{new string('-', depth)} {Path.DirectorySeparatorChar}{Path.GetFileName(current.Name)} "); DisplayComponent(current); }; inputComponent.DisplayFile = (int depth, IReadOnlyInputComponent current) => { // show depth _chalk.Default($"{new string('-', depth)} {current.Name} "); DisplayComponent(current); foreach (var mutant in current.TotalMutants) { if (mutant.ResultStatus == MutantStatus.Killed || mutant.ResultStatus == MutantStatus.Timeout) { _chalk.Green($"[{mutant.ResultStatus}] "); } else if (mutant.ResultStatus == MutantStatus.NoCoverage) { _chalk.Yellow($"[{mutant.ResultStatus}] "); } else { _chalk.Red($"[{mutant.ResultStatus}] "); } _chalk.Default($"{mutant.Mutation.DisplayName} on line {mutant.Mutation.OriginalNode.GetLocation().GetLineSpan().StartLinePosition.Line + 1}: '{mutant.Mutation.OriginalNode}' ==> '{mutant.Mutation.ReplacementNode}'{Environment.NewLine}"); } }; // print empty line for readability _chalk.Default($"{Environment.NewLine}{Environment.NewLine}All mutants have been tested, and your mutation score has been calculated{Environment.NewLine}"); // start recursive invocation of handlers inputComponent.Display(1); }
private void DisplayComponent(ProjectComponent inputComponent, int filePathLength) { _chalk.Default($"│ {(inputComponent.RelativePathToProjectFile ?? "All files").PadRight(filePathLength)}│ "); var mutationScore = inputComponent.GetMutationScore(); if (inputComponent is FileLeaf && inputComponent.IsComponentExcluded(_options.FilePatterns)) { _chalk.DarkGray("Excluded"); } else if (double.IsNaN(mutationScore)) { _chalk.DarkGray(" N/A"); } else { var scoreText = $"{mutationScore * 100:N2}".PadLeft(8); var checkHealth = inputComponent.CheckHealth(_options.Thresholds); if (checkHealth is Health.Good) { _chalk.Green(scoreText); } else if (checkHealth is Health.Warning) { _chalk.Yellow(scoreText); } else if (checkHealth is Health.Danger) { _chalk.Red(scoreText); } } _chalk.Default($" │ {inputComponent.ReadOnlyMutants.Count(m => m.ResultStatus == MutantStatus.Killed),8}"); _chalk.Default($" │ {inputComponent.ReadOnlyMutants.Count(m => m.ResultStatus == MutantStatus.Timeout),9}"); _chalk.Default($" │ {inputComponent.TotalMutants.Count() - inputComponent.DetectedMutants.Count(),10}"); _chalk.Default($" │ {inputComponent.ReadOnlyMutants.Count(m => m.ResultStatus == MutantStatus.NoCoverage),8}"); _chalk.Default($" │ {inputComponent.ReadOnlyMutants.Count(m => m.ResultStatus == MutantStatus.CompileError),7}"); _chalk.Default($" │{Environment.NewLine}"); }
private static void ShowMessage(MuTestInputException strEx) { _chalk.Yellow("\nMuTest C++ failed to mutate your project. For more information see the logs below:\n"); _chalk.Red($"\n{strEx.Message} - {strEx.Details.ConvertToPlainText()}\n"); }
public async Task RunMutationTest(MuTestOptions options) { if (!File.Exists(MuTestSettings.MSBuildPath)) { throw new MuTestInputException($"Unable to locate MSBuild Path at {MuTestSettings.MSBuildPath}. Please update MSBuildPath in MuTest.Console.exe.config if you are using different version"); } if (!File.Exists(MuTestSettings.VSTestConsolePath)) { throw new MuTestInputException($"Unable to locate VS Test Console Path at {MuTestSettings.VSTestConsolePath}. Please update VSTestConsolePath in MuTest.Console.exe.config if you are using different version"); } if (!File.Exists(MuTestSettings.RunSettingsPath)) { throw new MuTestInputException($"Unable to locate tests run settings path at {MuTestSettings.RunSettingsPath}. Please update RunSettingsPath in MuTest.Console.exe.config if you are using different location"); } _stopwatch = new Stopwatch(); _stopwatch.Start(); _options = options; if (!_options.SkipTestProjectBuild) { var originalProject = _options.TestProjectParameter; if (_options.OptimizeTestProject && _options.MultipleTargetClasses.Count == 1) { var targetClass = _options.MultipleTargetClasses.First(); _options.TestProjectParameter = _options .TestProjectParameter .UpdateTestProject(targetClass.TestClassPath.GetClass().ClassName()); } await ExecuteBuild(); if (!originalProject.Equals(_options.TestProjectParameter, StringComparison.InvariantCultureIgnoreCase)) { if (File.Exists(_options.TestProjectParameter)) { File.Delete(_options.TestProjectParameter); } _options.TestProjectParameter = originalProject; } } var serial = 1; _chalk.Default("\n*********************************** Matched Classes ***********************************"); foreach (var srcClass in _options.MultipleTargetClasses) { _chalk.Green($"\n{serial++}. {srcClass.ClassName} in {srcClass.ClassPath}"); } _chalk.Default("\n***************************************************************************************"); var projectSummary = new ProjectSummary { SourceProject = _options.SourceProjectParameter, TestProject = _options.TestProjectParameter }; var mutantAnalyzer = new MutantAnalyzer(_chalk, MuTestSettings) { ProcessWholeProject = _options.ProcessWholeProject, BuildInReleaseMode = _options.BuildInReleaseModeParameter, ConcurrentTestRunners = _options.ConcurrentTestRunners, EnableDiagnostics = _options.EnableDiagnostics, ExecuteAllTests = _options.ExecuteAllTests, IncludeNestedClasses = _options.IncludeNestedClasses, IncludePartialClasses = _options.IncludePartialClasses || _options.UseClassFilter || _options.ExecuteAllTests, KilledThreshold = _options.KilledThreshold, NoCoverage = _options.NoCoverage, RegEx = _options.RegEx, Specific = _options.Specific, SurvivedThreshold = _options.SurvivedThreshold, TestProject = _options.TestProjectParameter, TestProjectLibrary = _options.TestProjectLibraryParameter, UseClassFilter = _options.UseClassFilter, X64TargetPlatform = _options.X64TargetPlatform, TestExecutionTime = _options.TestExecutionThreshold, MutantsPerLine = _options.MutantsPerLine }; foreach (var targetClass in _options.MultipleTargetClasses) { mutantAnalyzer.TestClass = targetClass.TestClassPath; mutantAnalyzer.UseExternalCodeCoverage = false; mutantAnalyzer.MutantExecutor = null; var sourceClass = targetClass.ClassPath; var className = targetClass.ClassName; mutantAnalyzer.SourceProjectLibrary = _options.SourceProjectLibraryParameter; try { var sourceHash = sourceClass.GetCodeFileContent().ComputeHash(); var testHash = targetClass.TestClassPath.GetCodeFileContent().ComputeHash(); var hash = $"{sourceHash}-{testHash}".ComputeHash(); await GetFromDB(hash); if (_source != null) { var testClaz = targetClass.TestClassPath.GetClass(); var loader = new SemanticsClassDeclarationLoader(); _source.Claz = loader.Load(sourceClass, _options.SourceProjectParameter, className); _source.ClassLibrary = _options.SourceProjectLibraryParameter; _source.ClassProject = _options.SourceProjectParameter; _source.FilePath = sourceClass; _source.TestClaz = new TestClassDetail { Claz = new ClassDeclaration(testClaz), FilePath = targetClass.TestClassPath, ClassProject = _options.TestProjectParameter, FullName = testClaz.FullName(), ClassLibrary = _options.TestProjectLibraryParameter, X64TargetPlatform = _options.X64TargetPlatform }; } if (_source == null) { mutantAnalyzer.ExternalCoveredMutants.Clear(); _source = await mutantAnalyzer.Analyze(sourceClass, className, _options.SourceProjectParameter); _source.SHA256 = hash; _source.StoreToDb = true; if (_source.ExternalCoveredClassesIncluded.Any() && _options.AnalyzeExternalCoveredClasses) { _chalk.Yellow("\n\nAnalyzing External Coverage..."); mutantAnalyzer.UseExternalCodeCoverage = true; foreach (var acc in _source.ExternalCoveredClassesIncluded) { mutantAnalyzer.ExternalCoveredMutants.AddRange(acc.MutantsLines); var projectFile = new FileInfo(acc.ClassPath).FindProjectFile(); mutantAnalyzer.SourceProjectLibrary = projectFile.FindLibraryPath()?.FullName; if (!string.IsNullOrWhiteSpace(mutantAnalyzer.SourceProjectLibrary)) { var accClass = await mutantAnalyzer.Analyze( acc.ClassPath, acc.ClassName, projectFile.FullName); accClass.CalculateMutationScore(); if (accClass.MutationScore.Survived == 0) { acc.ZeroSurvivedMutants = true; } } } mutantAnalyzer.ExternalCoveredMutants.Clear(); } } } catch (Exception ex) when(!(ex is MuTestInputException)) { throw; } finally { MutantExecutor = mutantAnalyzer.MutantExecutor ?? new MutantExecutor(_source, MuTestSettings); _stopwatch.Stop(); if (_source != null) { await GenerateReports(); if (!string.IsNullOrWhiteSpace(_options.ProcessWholeProject)) { projectSummary.Classes.Add(new ClassSummary { TargetClass = new TargetClass { ClassPath = _source.FilePath, ClassName = _source.Claz.Syntax.FullName(), TestClassPath = _source.TestClaz.FilePath }, MutationScore = _source.MutationScore, Coverage = _source.Coverage ?? new Coverage() }); } } } } if (!string.IsNullOrWhiteSpace(_options.ProcessWholeProject)) { projectSummary.CalculateMutationScore(); var builder = new StringBuilder(HtmlTemplate); builder.AppendLine("<fieldset style=\"margin-bottom:10; margin-top:10\">"); builder.AppendLine("Mutation Report".PrintImportantWithLegend()); builder.Append(" ".PrintWithPreTag()); builder.Append($"{"Source Project:".PrintImportant()} {projectSummary.SourceProject}".PrintWithPreTag()); builder.Append($"{"Test Project :".PrintImportant()} {projectSummary.TestProject}".PrintWithPreTag()); builder.Append(" ".PrintWithPreTag()); builder.AppendLine("<fieldset style=\"margin-bottom:10; margin-top:10\">"); builder.AppendLine("Classes Mutation".PrintImportantWithLegend()); foreach (var claz in projectSummary.Classes) { builder.AppendLine("<fieldset style=\"margin-bottom:10; margin-top:10\">"); builder.AppendLine($"{claz.TargetClass.ClassName} [{claz.TargetClass.ClassPath}]".PrintImportantWithLegend(color: Colors.BlueViolet)); builder.Append($"{claz.MutationScore.Mutation} - {claz.MutationScore}".PrintWithPreTagWithMarginImportant()); builder.Append($"Code Coverage - {claz.Coverage}".PrintWithPreTagWithMarginImportant()); builder.AppendLine("</fieldset>"); } builder.AppendLine("</fieldset>"); builder.AppendLine("<fieldset style=\"margin-bottom:10; margin-top:10\">"); builder.AppendLine("ProjectWise Summary".PrintImportantWithLegend()); builder.Append(projectSummary.MutationScore.ToString().PrintWithPreTagWithMarginImportant(color: Colors.BlueViolet)); builder.Append($"Coverage: Mutation({projectSummary.MutationScore.Mutation}) {projectSummary.Coverage}".PrintWithPreTagWithMarginImportant(color: Colors.Blue)); builder.AppendLine("</fieldset>"); builder.AppendLine("</fieldset>"); CreateHtmlReport(builder, ProjectSummary); await CreateJsonReport(ProjectSummary, projectSummary); } }
public async Task <SourceClassDetail> Analyze(string sourceClass, string className, string sourceProject) { var testClaz = TestClass.GetClass(); var semanticsClassDeclarationLoader = new SemanticsClassDeclarationLoader(); var source = new SourceClassDetail { ClassLibrary = SourceProjectLibrary, ClassProject = sourceProject, BuildInReleaseMode = BuildInReleaseMode, Claz = semanticsClassDeclarationLoader.Load(sourceClass, sourceProject, className), FullName = className, FilePath = sourceClass, X64TargetPlatform = X64TargetPlatform, TestClaz = new TestClassDetail { Claz = new ClassDeclaration(testClaz), BuildInReleaseMode = BuildInReleaseMode, ClassLibrary = TestProjectLibrary, ClassProject = TestProject, FilePath = TestClass, FullName = testClaz.FullName(), X64TargetPlatform = X64TargetPlatform } }; _chalk.Yellow($"\nProcessing source class {source.DisplayName}"); _chalk.Yellow($"\nTest class {source.TestClaz.DisplayName}"); source.TestClaz.PartialClasses.Clear(); source.TestClaz.PartialClasses.Add(new ClassDetail { Claz = source.TestClaz.Claz, FilePath = source.TestClaz.FilePath }); var baseListSyntax = source.TestClaz.Claz.Syntax.BaseList; if (baseListSyntax != null && baseListSyntax.Types.Any()) { foreach (var type in baseListSyntax.Types) { var typeSyntax = type.Type; var fileName = typeSyntax.ToString(); if (typeSyntax is GenericNameSyntax syntax) { fileName = syntax.Identifier.ValueText; } if (Path.GetDirectoryName(TestProject) .FindFile($"{fileName}.cs")? .GetCodeFileContent() .RootNode() is CompilationUnitSyntax baseFile) { source.TestClaz.BaseClass = baseFile; } } } if (IncludePartialClasses) { var testProjectFiles = Path.GetDirectoryName(TestClass).GetCSharpClassDeclarations(); var testClassDetails = testProjectFiles .SelectMany(cu => cu.CompilationUnitSyntax.DescendantNodes <ClassDeclarationSyntax>(), (cu, classDeclarationSyntax) => new TestClassDetail { FullName = $"{cu.CompilationUnitSyntax.NameSpace()}.{classDeclarationSyntax.Identifier.Text}", FilePath = cu.FileName, TotalNumberOfMethods = classDeclarationSyntax.DescendantNodes <MethodDeclarationSyntax>().Count, Claz = new ClassDeclaration(classDeclarationSyntax) }).Where(x => x.TotalNumberOfMethods > 0) .OrderByDescending(x => x.TotalNumberOfMethods) .ToList(); foreach (var data in testClassDetails) { if (!source.TestClaz.PartialClassNodesAdded && source.TestClaz.FullName == data.FullName && data.FilePath != source.TestClaz.FilePath) { source.TestClaz.PartialClasses.Add(data); } } source.TestClaz.PartialClassNodesAdded = true; } await Initialization(source); return(source); }
public void OnAllMutantsTested(IReadOnlyInputComponent reportComponent) { var rootFolderProcessed = false; // setup display handlers reportComponent.DisplayFolder = (int _, IReadOnlyInputComponent current) => { // show depth var continuationLines = ParentContinuationLines(current); var stringBuilder = new StringBuilder(); foreach (var item in continuationLines.SkipLast(1)) { stringBuilder.Append(item ? ContinueLine : NoLine); } var folderLines = string.Empty; if (continuationLines.Count > 0) { folderLines = continuationLines.Last() ? BranchLine : FinalBranchLine; } var name = current.Name; if (name == null && !rootFolderProcessed) { name = "All files"; rootFolderProcessed = true; } if (!string.IsNullOrWhiteSpace(name)) { _chalk.Default($"{stringBuilder}{folderLines}{name}"); DisplayComponent(current); } }; reportComponent.DisplayFile = (int _, IReadOnlyInputComponent current) => { // show depth var continuationLines = ParentContinuationLines(current); var stringBuilder = new StringBuilder(); foreach (var item in continuationLines.SkipLast(1)) { stringBuilder.Append(item ? ContinueLine : NoLine); } _chalk.Default($"{stringBuilder}{(continuationLines.Last() ? BranchLine : FinalBranchLine)}{current.Name}"); DisplayComponent(current); stringBuilder.Append(continuationLines.Last() ? ContinueLine : NoLine); var prefix = stringBuilder.ToString(); foreach (var mutant in current.TotalMutants) { var isLastMutant = current.TotalMutants.Last() == mutant; _chalk.Default($"{prefix}{(isLastMutant ? FinalBranchLine : BranchLine)}"); switch (mutant.ResultStatus) { case MutantStatus.Killed: case MutantStatus.Timeout: _chalk.Green($"[{mutant.ResultStatus}]"); break; case MutantStatus.NoCoverage: _chalk.Yellow($"[{mutant.ResultStatus}]"); break; default: _chalk.Red($"[{mutant.ResultStatus}]"); break; } _chalk.Default($" {mutant.Mutation.DisplayName} on line {mutant.Line}{Environment.NewLine}"); _chalk.Default($"{prefix}{(isLastMutant ? NoLine : ContinueLine)}{BranchLine}[-] {mutant.Mutation.OriginalNode}{Environment.NewLine}"); _chalk.Default($"{prefix}{(isLastMutant ? NoLine : ContinueLine)}{FinalBranchLine}[+] {mutant.Mutation.ReplacementNode}{Environment.NewLine}"); } }; // print empty line for readability _chalk.Default($"{Environment.NewLine}{Environment.NewLine}All mutants have been tested, and your mutation score has been calculated{Environment.NewLine}"); // start recursive invocation of handlers reportComponent.Display(1); }