예제 #1
0
        public override bool Execute()
        {
            WaitForDebuggerIfEnabled();

            IServiceCollection serviceCollection = new ServiceCollection();

            serviceCollection.AddTransient <IProcessExitHandler, ProcessExitHandler>();
            serviceCollection.AddTransient <IFileSystem, FileSystem>();
            serviceCollection.AddTransient <IConsole, SystemConsole>();
            serviceCollection.AddTransient <ILogger, MSBuildLogger>(_ => _logger);
            serviceCollection.AddTransient <IRetryHelper, RetryHelper>();
            // We cache resolutions
            serviceCollection.AddSingleton <ISourceRootTranslator, SourceRootTranslator>(serviceProvider => new SourceRootTranslator(_path, serviceProvider.GetRequiredService <ILogger>(), serviceProvider.GetRequiredService <IFileSystem>()));
            // We need to keep singleton/static semantics
            serviceCollection.AddSingleton <IInstrumentationHelper, InstrumentationHelper>();

            ServiceProvider = serviceCollection.BuildServiceProvider();

            try
            {
                var includeFilters      = _include?.Split(',');
                var includeDirectories  = _includeDirectory?.Split(',');
                var excludeFilters      = _exclude?.Split(',');
                var excludedSourceFiles = _excludeByFile?.Split(',');
                var excludeAttributes   = _excludeByAttribute?.Split(',');
                var fileSystem          = ServiceProvider.GetService <IFileSystem>();

                Coverage coverage = new Coverage(_path,
                                                 includeFilters,
                                                 includeDirectories,
                                                 excludeFilters,
                                                 excludedSourceFiles,
                                                 excludeAttributes,
                                                 _includeTestAssembly,
                                                 _singleHit,
                                                 _mergeWith,
                                                 _useSourceLink,
                                                 _logger,
                                                 ServiceProvider.GetService <IInstrumentationHelper>(),
                                                 fileSystem,
                                                 ServiceProvider.GetService <ISourceRootTranslator>());

                CoveragePrepareResult prepareResult = coverage.PrepareModules();
                InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName());
                using (var instrumentedStateFile = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write))
                {
                    using (Stream serializedState = CoveragePrepareResult.Serialize(prepareResult))
                    {
                        serializedState.CopyTo(instrumentedStateFile);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex);
                return(false);
            }

            return(true);
        }
        public void TestCoverageSkipModule__AssemblyMarkedAsExcludeFromCodeCoverage()
        {
            Mock <FileSystem> partialMockFileSystem = new Mock <FileSystem>();

            partialMockFileSystem.CallBase = true;
            partialMockFileSystem.Setup(fs => fs.NewFileStream(It.IsAny <string>(), It.IsAny <FileMode>(), It.IsAny <FileAccess>())).Returns((string path, FileMode mode, FileAccess access) =>
            {
                return(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read));
            });
            var loggerMock = new Mock <ILogger>();

            string excludedbyattributeDll = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), "coverlet.tests.projectsample.excludedbyattribute.dll").First();

            InstrumentationHelper instrumentationHelper =
                new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock <ILogger>().Object,
                                          new SourceRootTranslator(excludedbyattributeDll, new Mock <ILogger>().Object, new FileSystem()));

            // test skip module include test assembly feature
            var coverage = new Coverage(excludedbyattributeDll, new string[] { "[coverlet.tests.projectsample.excludedbyattribute*]*" }, Array.Empty <string>(), Array.Empty <string>(),
                                        Array.Empty <string>(), Array.Empty <string>(), true, false, string.Empty, false, loggerMock.Object, instrumentationHelper, partialMockFileSystem.Object,
                                        new SourceRootTranslator(loggerMock.Object, new FileSystem()));
            CoveragePrepareResult result = coverage.PrepareModules();

            Assert.Empty(result.Results);
            loggerMock.Verify(l => l.LogVerbose(It.IsAny <string>()));
        }
예제 #3
0
        public override bool Execute()
        {
            try
            {
                var includeFilters      = _include?.Split(',');
                var includeDirectories  = _includeDirectory?.Split(',');
                var excludeFilters      = _exclude?.Split(',');
                var excludedSourceFiles = _excludeByFile?.Split(',');
                var excludeAttributes   = _excludeByAttribute?.Split(',');

                // We add default exclusion filter if no specified
                if (excludeFilters is null || excludeFilters.Length == 0)
                {
                    excludeFilters = new string[] { "[xunit*]*" };
                }

                Coverage coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger);
                CoveragePrepareResult prepareResult = coverage.PrepareModules();
                InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName());
                using (var instrumentedStateFile = new FileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write))
                {
                    using (Stream serializedState = CoveragePrepareResult.Serialize(prepareResult))
                    {
                        serializedState.CopyTo(instrumentedStateFile);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex);
                return(false);
            }

            return(true);
        }
예제 #4
0
        public void TestCoverage()
        {
            string module = GetType().Assembly.Location;
            string pdb    = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");

            var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));

            File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
            File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);

            // TODO: Mimic hits by calling ModuleTrackerTemplate.RecordHit before Unload

            // Since Coverage only instruments dependancies, we need a fake module here
            var testModule = Path.Combine(directory.FullName, "test.module.dll");

            var coverage = new Coverage(testModule, Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), string.Empty, false);

            coverage.PrepareModules();

            // The module hit tracker must signal to Coverage that it has done its job, so call it manually
            var instrumenterResult = coverage.Results.Single();

            ModuleTrackerTemplate.HitsArray         = new int[instrumenterResult.HitCandidates.Count + ModuleTrackerTemplate.HitsResultHeaderSize];
            ModuleTrackerTemplate.HitsFilePath      = instrumenterResult.HitsFilePath;
            ModuleTrackerTemplate.HitsMemoryMapName = instrumenterResult.HitsResultGuid;
            ModuleTrackerTemplate.UnloadModule(null, null);

            var result = coverage.GetCoverageResult();

            Assert.NotEmpty(result.Modules);

            directory.Delete(true);
        }
예제 #5
0
        public void TestCoverageWithTestAssembly()
        {
            string module = GetType().Assembly.Location;
            string pdb    = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");

            var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));

            File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
            File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);

            InstrumentationHelper instrumentationHelper =
                new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock <ILogger>().Object,
                                          new SourceRootTranslator(module, new Mock <ILogger>().Object, new FileSystem()));

            var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(),
                                        Array.Empty <string>(), true, false, string.Empty, false, _mockLogger.Object, instrumentationHelper, new FileSystem(),
                                        new SourceRootTranslator(module, _mockLogger.Object, new FileSystem()));

            coverage.PrepareModules();

            var result = coverage.GetCoverageResult();

            Assert.NotEmpty(result.Modules);

            directory.Delete(true);
        }
예제 #6
0
        public void TestCoverage()
        {
            string module = GetType().Assembly.Location;
            string pdb    = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");

            var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));

            File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
            File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);

            // TODO: Find a way to mimick hits

            // Since Coverage only instruments dependancies, we need a fake module here
            var testModule = Path.Combine(directory.FullName, "test.module.dll");

            var coverage = new Coverage(testModule, Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), string.Empty);

            coverage.PrepareModules();

            var result = coverage.GetCoverageResult();

            Assert.NotEmpty(result.Modules);

            directory.Delete(true);
        }
예제 #7
0
        public override bool Execute()
        {
            try
            {
                var includeFilters      = _include?.Split(',');
                var includeDirectories  = _includeDirectory?.Split(',');
                var excludeFilters      = _exclude?.Split(',');
                var excludedSourceFiles = _excludeByFile?.Split(',');
                var excludeAttributes   = _excludeByAttribute?.Split(',');

                Coverage coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _includeTestAssembly, _singleHit, _mergeWith, _useSourceLink, _logger, (IInstrumentationHelper)DependencyInjection.Current.GetService(typeof(IInstrumentationHelper)));
                CoveragePrepareResult prepareResult = coverage.PrepareModules();
                InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName());
                using (var instrumentedStateFile = new FileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write))
                {
                    using (Stream serializedState = CoveragePrepareResult.Serialize(prepareResult))
                    {
                        serializedState.CopyTo(instrumentedStateFile);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex);
                return(false);
            }

            return(true);
        }
예제 #8
0
        async public static Task <CoveragePrepareResult> Run <T>(Func <dynamic, Task> callMethod, string persistPrepareResultToFile)
        {
            // Setup correct retry helper to avoid exception in InstrumentationHelper.RestoreOriginalModules on remote process exit
            DependencyInjection.Set(new ServiceCollection()
                                    .AddTransient <IRetryHelper, CustomRetryHelper>()
                                    .AddTransient <IProcessExitHandler, CustomProcessExitHandler>()
                                    .BuildServiceProvider());

            // Rename test file to avoid locks
            string location = typeof(T).Assembly.Location;
            string fileName = Path.ChangeExtension(Path.GetRandomFileName(), ".dll");
            string logFile  = Path.ChangeExtension(fileName, ".log");
            string newPath  = Path.Combine(Path.GetDirectoryName(location), fileName);

            File.Copy(location, newPath);
            File.Copy(Path.ChangeExtension(location, ".pdb"), Path.ChangeExtension(newPath, ".pdb"));

            // Instrument module
            Coverage coverage = new Coverage(newPath,
                                             new string[]
            {
                $"[{Path.GetFileNameWithoutExtension(fileName)}*]*"
            },
                                             Array.Empty <string>(),
                                             new string[]
            {
                "[xunit.*]*",
                "[coverlet.*]*"
            }, Array.Empty <string>(), Array.Empty <string>(), true, false, "", false, new Logger(logFile));
            CoveragePrepareResult prepareResult = coverage.PrepareModules();

            // Load new assembly
            Assembly asm = Assembly.LoadFile(newPath);

            // Instance type and call method
            await callMethod(Activator.CreateInstance(asm.GetType(typeof(T).FullName)));

            // Flush tracker
            Type tracker = asm.GetTypes().Single(n => n.FullName.Contains("Coverlet.Core.Instrumentation.Tracker"));

            // Void UnloadModule(System.Object, System.EventArgs)
            tracker.GetTypeInfo().GetMethod("UnloadModule").Invoke(null, new object[2] {
                null, null
            });

            // Persist CoveragePrepareResult
            using (FileStream fs = new FileStream(persistPrepareResultToFile, FileMode.Open))
            {
                CoveragePrepareResult.Serialize(prepareResult).CopyTo(fs);
            }

            return(prepareResult);
        }
예제 #9
0
        public override bool Execute()
        {
            try
            {
                _coverage = new Coverage(_path, Guid.NewGuid().ToString());
                _coverage.PrepareModules();
            }
            catch (Exception ex)
            {
                Log.LogErrorFromException(ex);
                return(false);
            }

            return(true);
        }
예제 #10
0
        public override bool Execute()
        {
            try
            {
                var excludeRules = Exclude?.Split(',');
                Coverage = new Coverage(Path, Guid.NewGuid().ToString(), excludeRules);
                Coverage.PrepareModules();
            }
            catch (Exception ex)
            {
                Log.LogErrorFromException(ex);
                return(false);
            }

            return(true);
        }
예제 #11
0
        public override bool Execute()
        {
            try
            {
                var rules   = _excludeByFile?.Split(',');
                var filters = _exclude?.Split(',');

                _coverage = new Coverage(_path, Guid.NewGuid().ToString(), filters, rules);
                _coverage.PrepareModules();
            }
            catch (Exception ex)
            {
                Log.LogErrorFromException(ex);
                return(false);
            }

            return(true);
        }
예제 #12
0
        public void TestCoverage()
        {
            string module     = typeof(CoverageTests).Assembly.Location;
            string identifier = Guid.NewGuid().ToString();

            var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), identifier));

            File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);

            var coverage = new Coverage(module, identifier);

            coverage.PrepareModules();

            var result = coverage.GetCoverageResult();

            Assert.Empty(result.Modules);

            directory.Delete(true);
        }
예제 #13
0
        public override bool Execute()
        {
            try
            {
                var excludedSourceFiles = _excludeByFile?.Split(',');
                var excludeFilters      = _exclude?.Split(',');
                var includeFilters      = _include?.Split(',');

                _coverage = new Coverage(_path, excludeFilters, includeFilters, excludedSourceFiles, true);
                _coverage.PrepareModules();
            }
            catch (Exception ex)
            {
                Log.LogErrorFromException(ex);
                return(false);
            }

            return(true);
        }
예제 #14
0
        public override bool Execute()
        {
            try
            {
                var includeFilters      = _include?.Split(',');
                var includeDirectories  = _includeDirectory?.Split(',');
                var excludeFilters      = _exclude?.Split(',');
                var excludedSourceFiles = _excludeByFile?.Split(',');
                var excludeAttributes   = _excludeByAttribute?.Split(',');

                _coverage = new Coverage(_path, includeFilters, includeDirectories, excludeFilters, excludedSourceFiles, excludeAttributes, _singleHit, _mergeWith, _useSourceLink);
                _coverage.PrepareModules();
            }
            catch (Exception ex)
            {
                Log.LogErrorFromException(ex);
                return(false);
            }

            return(true);
        }
예제 #15
0
        public void TestCoverage()
        {
            string module = GetType().Assembly.Location;
            string pdb    = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");

            var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));

            File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
            File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);

            // TODO: Find a way to mimick hits

            var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), Array.Empty <string>(), false, string.Empty, false, new Mock <ILogger>().Object);

            coverage.PrepareModules();

            var result = coverage.GetCoverageResult();

            Assert.NotEmpty(result.Modules);

            directory.Delete(true);
        }
예제 #16
0
        public void TestCoverageWithTestAssembly()
        {
            string module = GetType().Assembly.Location;
            string pdb    = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb");

            var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()));

            File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true);
            File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true);

            InstrumentationHelper instrumentationHelper =
                new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock <ILogger>().Object,
                                          new SourceRootTranslator(module, new Mock <ILogger>().Object, new FileSystem()));

            CoverageParameters parameters = new CoverageParameters
            {
                IncludeFilters      = Array.Empty <string>(),
                IncludeDirectories  = Array.Empty <string>(),
                ExcludeFilters      = Array.Empty <string>(),
                ExcludedSourceFiles = Array.Empty <string>(),
                ExcludeAttributes   = Array.Empty <string>(),
                IncludeTestAssembly = true,
                SingleHit           = false,
                MergeWith           = string.Empty,
                UseSourceLink       = false
            };

            var coverage = new Coverage(Path.Combine(directory.FullName, Path.GetFileName(module)), parameters, _mockLogger.Object, instrumentationHelper, new FileSystem(),
                                        new SourceRootTranslator(module, _mockLogger.Object, new FileSystem()), new CecilSymbolHelper());

            coverage.PrepareModules();

            var result = coverage.GetCoverageResult();

            Assert.NotEmpty(result.Modules);

            directory.Delete(true);
        }
예제 #17
0
        static int Main(string[] args)
        {
            var logger = new ConsoleLogger();
            var app    = new CommandLineApplication();

            app.Name     = "coverlet";
            app.FullName = "Cross platform .NET Core code coverage tool";
            app.HelpOption("-h|--help");
            app.VersionOption("-v|--version", GetAssemblyVersion());

            CommandArgument module              = app.Argument("<ASSEMBLY>", "Path to the test assembly.");
            CommandOption   target              = app.Option("-t|--target", "Path to the test runner application.", CommandOptionType.SingleValue);
            CommandOption   targs               = app.Option("-a|--targetargs", "Arguments to be passed to the test runner.", CommandOptionType.SingleValue);
            CommandOption   output              = app.Option("-o|--output", "Output of the generated coverage report", CommandOptionType.SingleValue);
            CommandOption   formats             = app.Option("-f|--format", "Format of the generated coverage report.", CommandOptionType.MultipleValue);
            CommandOption   threshold           = app.Option("--threshold", "Exits with error if the coverage % is below value.", CommandOptionType.SingleValue);
            CommandOption   thresholdTypes      = app.Option("--threshold-type", "Coverage type to apply the threshold to.", CommandOptionType.MultipleValue);
            CommandOption   excludeFilters      = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue);
            CommandOption   includeFilters      = app.Option("--include", "Filter expressions to include only specific modules and types.", CommandOptionType.MultipleValue);
            CommandOption   excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue);
            CommandOption   mergeWith           = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue);

            app.OnExecute(() =>
            {
                if (string.IsNullOrEmpty(module.Value) || string.IsNullOrWhiteSpace(module.Value))
                {
                    throw new CommandParsingException(app, "No test assembly specified.");
                }

                if (!target.HasValue())
                {
                    throw new CommandParsingException(app, "Target must be specified.");
                }

                Coverage coverage = new Coverage(module.Value, excludeFilters.Values.ToArray(), includeFilters.Values.ToArray(), excludedSourceFiles.Values.ToArray(), mergeWith.Value());
                coverage.PrepareModules();

                Process process                          = new Process();
                process.StartInfo.FileName               = target.Value();
                process.StartInfo.Arguments              = targs.HasValue() ? targs.Value() : string.Empty;
                process.StartInfo.CreateNoWindow         = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.Start();
                logger.LogInformation(process.StandardOutput.ReadToEnd());
                process.WaitForExit();

                var dOutput         = output.HasValue() ? output.Value() : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString();
                var dThreshold      = threshold.HasValue() ? int.Parse(threshold.Value()) : 0;
                var dThresholdTypes = thresholdTypes.HasValue() ? thresholdTypes.Values : new List <string>(new string[] { "line", "branch", "method" });

                logger.LogInformation("\nCalculating coverage result...");

                var result    = coverage.GetCoverageResult();
                var directory = Path.GetDirectoryName(dOutput);
                if (!Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }

                foreach (var format in (formats.HasValue() ? formats.Values : new List <string>(new string[] { "json" })))
                {
                    var reporter = new ReporterFactory(format).CreateReporter();
                    if (reporter == null)
                    {
                        throw new Exception($"Specified output format '{format}' is not supported");
                    }

                    if (reporter.OutputType == ReporterOutputType.Console)
                    {
                        // Output to console
                        logger.LogInformation("  Outputting results to console");
                        logger.LogInformation(reporter.Report(result));
                    }
                    else
                    {
                        // Output to file
                        var filename = Path.GetFileName(dOutput);
                        filename     = (filename == string.Empty) ? $"coverage.{reporter.Extension}" : filename;
                        filename     = Path.HasExtension(filename) ? filename : $"{filename}.{reporter.Extension}";

                        var report = Path.Combine(directory, filename);
                        logger.LogInformation($"  Generating report '{report}'");
                        File.WriteAllText(report, reporter.Report(result));
                    }
                }

                var summary               = new CoverageSummary();
                var exceptionBuilder      = new StringBuilder();
                var coverageTable         = new ConsoleTable("Module", "Line", "Branch", "Method");
                var thresholdFailed       = false;
                var overallLineCoverage   = summary.CalculateLineCoverage(result.Modules);
                var overallBranchCoverage = summary.CalculateBranchCoverage(result.Modules);
                var overallMethodCoverage = summary.CalculateMethodCoverage(result.Modules);

                foreach (var _module in result.Modules)
                {
                    var linePercent   = summary.CalculateLineCoverage(_module.Value).Percent * 100;
                    var branchPercent = summary.CalculateBranchCoverage(_module.Value).Percent * 100;
                    var methodPercent = summary.CalculateMethodCoverage(_module.Value).Percent * 100;

                    coverageTable.AddRow(Path.GetFileNameWithoutExtension(_module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%");

                    if (dThreshold > 0)
                    {
                        if (linePercent < dThreshold && dThresholdTypes.Contains("line"))
                        {
                            exceptionBuilder.AppendLine($"'{Path.GetFileNameWithoutExtension(_module.Key)}' has a line coverage '{linePercent}%' below specified threshold '{dThreshold}%'");
                            thresholdFailed = true;
                        }

                        if (branchPercent < dThreshold && dThresholdTypes.Contains("branch"))
                        {
                            exceptionBuilder.AppendLine($"'{Path.GetFileNameWithoutExtension(_module.Key)}' has a branch coverage '{branchPercent}%' below specified threshold '{dThreshold}%'");
                            thresholdFailed = true;
                        }

                        if (methodPercent < dThreshold && dThresholdTypes.Contains("method"))
                        {
                            exceptionBuilder.AppendLine($"'{Path.GetFileNameWithoutExtension(_module.Key)}' has a method coverage '{methodPercent}%' below specified threshold '{dThreshold}%'");
                            thresholdFailed = true;
                        }
                    }
                }

                logger.LogInformation(string.Empty);
                logger.LogInformation(coverageTable.ToStringAlternative());
                logger.LogInformation($"Total Line: {overallLineCoverage.Percent * 100}%");
                logger.LogInformation($"Total Branch: {overallBranchCoverage.Percent * 100}%");
                logger.LogInformation($"Total Method: {overallMethodCoverage.Percent * 100}%");

                if (thresholdFailed)
                {
                    throw new Exception(exceptionBuilder.ToString().TrimEnd(Environment.NewLine.ToCharArray()));
                }

                return(process.ExitCode == 0 ? 0 : process.ExitCode);
            });

            try
            {
                return(app.Execute(args));
            }
            catch (CommandParsingException ex)
            {
                logger.LogError(ex.Message);
                app.ShowHelp();
                return(1);
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                return(1);
            }
        }
예제 #18
0
        static int Main(string[] args)
        {
            var logger = new ConsoleLogger();
            var app    = new CommandLineApplication();

            app.Name     = "coverlet";
            app.FullName = "Cross platform .NET Core code coverage tool";
            app.HelpOption("-h|--help");
            app.VersionOption("-v|--version", GetAssemblyVersion());
            int exitCode = (int)CommandExitCodes.Success;

            CommandArgument          module              = app.Argument("<ASSEMBLY>", "Path to the test assembly.");
            CommandOption            target              = app.Option("-t|--target", "Path to the test runner application.", CommandOptionType.SingleValue);
            CommandOption            targs               = app.Option("-a|--targetargs", "Arguments to be passed to the test runner.", CommandOptionType.SingleValue);
            CommandOption            output              = app.Option("-o|--output", "Output of the generated coverage report", CommandOptionType.SingleValue);
            CommandOption <LogLevel> verbosity           = app.Option <LogLevel>("-v|--verbosity", "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.", CommandOptionType.SingleValue);
            CommandOption            formats             = app.Option("-f|--format", "Format of the generated coverage report.", CommandOptionType.MultipleValue);
            CommandOption            threshold           = app.Option("--threshold", "Exits with error if the coverage % is below value.", CommandOptionType.SingleValue);
            CommandOption            thresholdTypes      = app.Option("--threshold-type", "Coverage type to apply the threshold to.", CommandOptionType.MultipleValue);
            CommandOption            thresholdStat       = app.Option("--threshold-stat", "Coverage statistic used to enforce the threshold value.", CommandOptionType.SingleValue);
            CommandOption            excludeFilters      = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue);
            CommandOption            includeFilters      = app.Option("--include", "Filter expressions to include only specific modules and types.", CommandOptionType.MultipleValue);
            CommandOption            excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue);
            CommandOption            includeDirectories  = app.Option("--include-directory", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue);
            CommandOption            excludeAttributes   = app.Option("--exclude-by-attribute", "Attributes to exclude from code coverage.", CommandOptionType.MultipleValue);
            CommandOption            includeTestAssembly = app.Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.", CommandOptionType.NoValue);
            CommandOption            singleHit           = app.Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location", CommandOptionType.NoValue);
            CommandOption            mergeWith           = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue);
            CommandOption            useSourceLink       = app.Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.", CommandOptionType.NoValue);

            app.OnExecute(() =>
            {
                if (string.IsNullOrEmpty(module.Value) || string.IsNullOrWhiteSpace(module.Value))
                {
                    throw new CommandParsingException(app, "No test assembly specified.");
                }

                if (!target.HasValue())
                {
                    throw new CommandParsingException(app, "Target must be specified.");
                }

                if (verbosity.HasValue())
                {
                    // Adjust log level based on user input.
                    logger.Level = verbosity.ParsedValue;
                }

                // We add default exclusion filter if no specified
                if (excludeFilters.Values.Count == 0)
                {
                    excludeFilters.Values.Add("[xunit*]*");
                }

                Coverage coverage = new Coverage(module.Value,
                                                 includeFilters.Values.ToArray(),
                                                 includeDirectories.Values.ToArray(),
                                                 excludeFilters.Values.ToArray(),
                                                 excludedSourceFiles.Values.ToArray(),
                                                 excludeAttributes.Values.ToArray(),
                                                 includeTestAssembly.HasValue(),
                                                 singleHit.HasValue(),
                                                 mergeWith.Value(),
                                                 useSourceLink.HasValue(),
                                                 logger);
                coverage.PrepareModules();

                Process process                          = new Process();
                process.StartInfo.FileName               = target.Value();
                process.StartInfo.Arguments              = targs.HasValue() ? targs.Value() : string.Empty;
                process.StartInfo.CreateNoWindow         = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError  = true;
                process.OutputDataReceived              += (sender, eventArgs) =>
                {
                    if (!string.IsNullOrEmpty(eventArgs.Data))
                    {
                        logger.LogInformation(eventArgs.Data, important: true);
                    }
                };

                process.ErrorDataReceived += (sender, eventArgs) =>
                {
                    if (!string.IsNullOrEmpty(eventArgs.Data))
                    {
                        logger.LogError(eventArgs.Data);
                    }
                };

                process.Start();

                process.BeginErrorReadLine();
                process.BeginOutputReadLine();

                process.WaitForExit();

                var dOutput         = output.HasValue() ? output.Value() : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString();
                var dThreshold      = threshold.HasValue() ? double.Parse(threshold.Value()) : 0;
                var dThresholdTypes = thresholdTypes.HasValue() ? thresholdTypes.Values : new List <string>(new string[] { "line", "branch", "method" });
                var dThresholdStat  = thresholdStat.HasValue() ? Enum.Parse <ThresholdStatistic>(thresholdStat.Value(), true) : Enum.Parse <ThresholdStatistic>("minimum", true);

                logger.LogInformation("\nCalculating coverage result...");

                var result    = coverage.GetCoverageResult();
                var directory = Path.GetDirectoryName(dOutput);
                if (directory == string.Empty)
                {
                    directory = Directory.GetCurrentDirectory();
                }
                else if (!Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }

                foreach (var format in (formats.HasValue() ? formats.Values : new List <string>(new string[] { "json" })))
                {
                    var reporter = new ReporterFactory(format).CreateReporter();
                    if (reporter == null)
                    {
                        throw new Exception($"Specified output format '{format}' is not supported");
                    }

                    if (reporter.OutputType == ReporterOutputType.Console)
                    {
                        // Output to console
                        logger.LogInformation("  Outputting results to console", important: true);
                        logger.LogInformation(reporter.Report(result), important: true);
                    }
                    else
                    {
                        // Output to file
                        var filename = Path.GetFileName(dOutput);
                        filename     = (filename == string.Empty) ? $"coverage.{reporter.Extension}" : filename;
                        filename     = Path.HasExtension(filename) ? filename : $"{filename}.{reporter.Extension}";

                        var report = Path.Combine(directory, filename);
                        logger.LogInformation($"  Generating report '{report}'", important: true);
                        File.WriteAllText(report, reporter.Report(result));
                    }
                }

                var thresholdTypeFlags = ThresholdTypeFlags.None;

                foreach (var thresholdType in dThresholdTypes)
                {
                    if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Line;
                    }
                    else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Branch;
                    }
                    else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase))
                    {
                        thresholdTypeFlags |= ThresholdTypeFlags.Method;
                    }
                }

                var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method");
                var summary       = new CoverageSummary();
                int numModules    = result.Modules.Count;

                var totalLinePercent   = summary.CalculateLineCoverage(result.Modules).Percent;
                var totalBranchPercent = summary.CalculateBranchCoverage(result.Modules).Percent;
                var totalMethodPercent = summary.CalculateMethodCoverage(result.Modules).Percent;

                foreach (var _module in result.Modules)
                {
                    var linePercent   = summary.CalculateLineCoverage(_module.Value).Percent;
                    var branchPercent = summary.CalculateBranchCoverage(_module.Value).Percent;
                    var methodPercent = summary.CalculateMethodCoverage(_module.Value).Percent;

                    coverageTable.AddRow(Path.GetFileNameWithoutExtension(_module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%");
                }

                logger.LogInformation(coverageTable.ToStringAlternative());

                coverageTable.Columns.Clear();
                coverageTable.Rows.Clear();

                coverageTable.AddColumn(new[] { "", "Line", "Branch", "Method" });
                coverageTable.AddRow("Total", $"{totalLinePercent}%", $"{totalBranchPercent}%", $"{totalMethodPercent}%");
                coverageTable.AddRow("Average", $"{totalLinePercent / numModules}%", $"{totalBranchPercent / numModules}%", $"{totalMethodPercent / numModules}%");

                logger.LogInformation(coverageTable.ToStringAlternative());
                if (process.ExitCode > 0)
                {
                    exitCode += (int)CommandExitCodes.TestFailed;
                }
                thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, dThreshold, thresholdTypeFlags, dThresholdStat);
                if (thresholdTypeFlags != ThresholdTypeFlags.None)
                {
                    exitCode += (int)CommandExitCodes.CoverageBelowThreshold;
                    var exceptionMessageBuilder = new StringBuilder();
                    if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {dThreshold}");
                    }

                    if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} branch coverage is below the specified {dThreshold}");
                    }

                    if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None)
                    {
                        exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {dThreshold}");
                    }

                    throw new Exception(exceptionMessageBuilder.ToString());
                }

                return(exitCode);
            });

            try
            {
                return(app.Execute(args));
            }
            catch (CommandParsingException ex)
            {
                logger.LogError(ex.Message);
                app.ShowHelp();
                return((int)CommandExitCodes.CommandParsingException);
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                return(exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception);
            }
        }
예제 #19
0
 /// <summary>
 /// Prepares modules for getting coverage.
 /// Wrapper over coverage.PrepareModules
 /// </summary>
 /// <param name="coverage"></param>
 public void PrepareModules(Coverage coverage)
 {
     coverage.PrepareModules();
 }
예제 #20
0
        public override bool Execute()
        {
            AttachDebugger();

            IServiceCollection serviceCollection = new ServiceCollection();

            serviceCollection.AddTransient <IProcessExitHandler, ProcessExitHandler>();
            serviceCollection.AddTransient <IFileSystem, FileSystem>();
            serviceCollection.AddTransient <IConsole, SystemConsole>();
            serviceCollection.AddTransient <ILogger, MSBuildLogger>(_ => _logger);
            serviceCollection.AddTransient <IRetryHelper, RetryHelper>();
            // We cache resolutions
            serviceCollection.AddSingleton <ISourceRootTranslator, SourceRootTranslator>(serviceProvider => new SourceRootTranslator(Path, serviceProvider.GetRequiredService <ILogger>(), serviceProvider.GetRequiredService <IFileSystem>()));
            // We need to keep singleton/static semantics
            serviceCollection.AddSingleton <IInstrumentationHelper, InstrumentationHelper>();
            serviceCollection.AddSingleton <ICecilSymbolHelper, CecilSymbolHelper>();

            ServiceProvider = serviceCollection.BuildServiceProvider();

            try
            {
                IFileSystem fileSystem = ServiceProvider.GetService <IFileSystem>();

                var parameters = new CoverageParameters
                {
                    IncludeFilters          = Include?.Split(','),
                    IncludeDirectories      = IncludeDirectory?.Split(','),
                    ExcludeFilters          = Exclude?.Split(','),
                    ExcludedSourceFiles     = ExcludeByFile?.Split(','),
                    ExcludeAttributes       = ExcludeByAttribute?.Split(','),
                    IncludeTestAssembly     = IncludeTestAssembly,
                    SingleHit               = SingleHit,
                    MergeWith               = MergeWith,
                    UseSourceLink           = UseSourceLink,
                    SkipAutoProps           = SkipAutoProps,
                    DeterministicReport     = DeterministicReport,
                    DoesNotReturnAttributes = DoesNotReturnAttribute?.Split(',')
                };

                var coverage = new Coverage(Path,
                                            parameters,
                                            _logger,
                                            ServiceProvider.GetService <IInstrumentationHelper>(),
                                            ServiceProvider.GetService <IFileSystem>(),
                                            ServiceProvider.GetService <ISourceRootTranslator>(),
                                            ServiceProvider.GetService <ICecilSymbolHelper>());

                CoveragePrepareResult prepareResult = coverage.PrepareModules();
                InstrumenterState = new TaskItem(System.IO.Path.GetTempFileName());
                using Stream instrumentedStateFile = fileSystem.NewFileStream(InstrumenterState.ItemSpec, FileMode.Open, FileAccess.Write);
                using Stream serializedState       = CoveragePrepareResult.Serialize(prepareResult);
                serializedState.CopyTo(instrumentedStateFile);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex);
                return(false);
            }

            return(true);
        }