├── .editorconfig ├── .gitignore ├── LICENSE ├── MiniCover.sln ├── NuGet.config ├── README.md ├── azure-pipelines.yml ├── build-sample.sh ├── build.sh ├── global.json ├── minicover.sh ├── sample ├── .config │ └── dotnet-tools.json ├── NuGet.config ├── Sample.sln ├── src │ └── Sample │ │ ├── AnotherClass.cs │ │ ├── ClassWithGeneratedRegex.cs │ │ ├── ClassWithYield.cs │ │ ├── DemoEnum.cs │ │ ├── PartialClass.cs │ │ ├── PartialClassB.cs │ │ ├── Sample.csproj │ │ ├── TryFinally │ │ ├── AClassWithATryFinallyInConstructor.cs │ │ ├── AClassWithFieldInitializedOutsideConstructor.cs │ │ ├── AClassWithSomeTryFinally.cs │ │ ├── AnAbstractClass.cs │ │ ├── AnotherClassWithoutTryFinally.cs │ │ ├── ClassWithComplicatedLambda.cs │ │ ├── ClassWithMultipleConstructors.cs │ │ ├── ClassWithSimpleLambda.cs │ │ ├── ClassWithTryFinallyInLambda.cs │ │ └── HeritingClass.cs │ │ └── WorkerThread.cs └── tests │ └── Sample.UnitTests │ ├── ClassWithGeneratedRegexTest.cs │ ├── Sample.UnitTests.csproj │ ├── UnitTest1.cs │ └── WorkerThreadTests.cs ├── src ├── MiniCover.Core │ ├── Extensions │ │ ├── AssemblyDefinitionExtensions.cs │ │ ├── CodeExtensions.cs │ │ ├── CustomAttributeProviderExtensions.cs │ │ ├── DocumentExtensions.cs │ │ ├── EnumerableExtensions.cs │ │ ├── GraphNodeExtensions.cs │ │ ├── ILProcessorExtensions.cs │ │ ├── InstructionExtensions.cs │ │ ├── MethodDefinitionExtensions.cs │ │ ├── ModuleDefinitionExtensions.cs │ │ ├── ObjectExtensions.cs │ │ └── TypeDefinitionExtensions.cs │ ├── FileSystem │ │ ├── CachedFileReader.cs │ │ └── IFileReader.cs │ ├── Hits │ │ ├── HitsInfo.cs │ │ ├── HitsReader.cs │ │ ├── HitsResetter.cs │ │ ├── IHitsReader.cs │ │ └── IHitsResetter.cs │ ├── Instrumentation │ │ ├── AssemblyInstrumenter.cs │ │ ├── CustomAssemblyResolver.cs │ │ ├── FileBasedInstrumentationContext.cs │ │ ├── IAssemblyInstrumenter.cs │ │ ├── IInstrumentationContext.cs │ │ ├── IInstrumenter.cs │ │ ├── IMethodInstrumenter.cs │ │ ├── ITypeInstrumenter.cs │ │ ├── IUninstrumenter.cs │ │ ├── Instrumenter.cs │ │ ├── MethodInstrumenter.cs │ │ ├── Patterns │ │ │ └── LambdaInitPattern.cs │ │ ├── TypeInstrumenter.cs │ │ └── Uninstrumenter.cs │ ├── MiniCover.Core.csproj │ ├── MiniCoverCoreServiceCollectionExtensions.cs │ ├── Model │ │ ├── AssemblyLocation.cs │ │ ├── GraphNode.cs │ │ ├── InstrumentationResult.cs │ │ ├── InstrumentedAssembly.cs │ │ ├── InstrumentedBranch.cs │ │ ├── InstrumentedCondition.cs │ │ ├── InstrumentedMethod.cs │ │ ├── InstrumentedSequence.cs │ │ └── SourceFile.cs │ └── Utils │ │ ├── DepsJsonUtils.cs │ │ ├── FileUtils.cs │ │ └── PathUtils.cs ├── MiniCover.HitServices │ ├── HitContext.cs │ ├── HitService.cs │ ├── InstrumentedAttribute.cs │ ├── MethodScope.cs │ └── MiniCover.HitServices.csproj ├── MiniCover.Reports │ ├── Clover │ │ ├── CloverCounter.cs │ │ ├── CloverReport.cs │ │ └── ICloverReport.cs │ ├── Cobertura │ │ ├── CoberturaReport.cs │ │ └── ICoberturaReport.cs │ ├── Console │ │ ├── ConsoleReport.cs │ │ └── IConsoleReport.cs │ ├── Coveralls │ │ ├── CoverallsReport.cs │ │ ├── ICoverallsReport.cs │ │ └── Models │ │ │ ├── CoverallsCommitModel.cs │ │ │ ├── CoverallsGitModel.cs │ │ │ ├── CoverallsJobModel.cs │ │ │ ├── CoverallsRemoteModel.cs │ │ │ └── CoverallsSourceFileModel.cs │ ├── Helpers │ │ ├── ConsoleBox.cs │ │ ├── ConsoleTable.cs │ │ ├── ISummaryFactory.cs │ │ ├── Summary.cs │ │ ├── SummaryFactory.cs │ │ └── SummaryRow.cs │ ├── Html │ │ ├── HtmlReport.cs │ │ ├── HtmlSourceFileReport.cs │ │ ├── IHtmlReport.cs │ │ ├── IHtmlSourceFileReport.cs │ │ ├── Shared.css │ │ ├── Shared.js │ │ ├── SourceFile.css │ │ └── Summary.css │ ├── MiniCover.Reports.csproj │ ├── MiniCoverReportsServiceCollectionExtensions.cs │ ├── NCover │ │ ├── INCoverReport.cs │ │ └── NCoverReport.cs │ ├── OpenCover │ │ ├── IOpenCoverReport.cs │ │ └── OpenCoverReport.cs │ └── Utils │ │ └── ResourceUtils.cs └── MiniCover │ ├── CommandLine │ ├── Commands │ │ ├── CloverReportCommand.cs │ │ ├── CoberturaReportCommand.cs │ │ ├── ConsoleReportCommand.cs │ │ ├── CoverallsReportCommand.cs │ │ ├── HtmlReportCommand.cs │ │ ├── InstrumentCommand.cs │ │ ├── NCoverReportCommand.cs │ │ ├── OpenCoverReportCommand.cs │ │ ├── ResetCommand.cs │ │ └── UninstrumentCommand.cs │ ├── DirectoryOption.cs │ ├── FileOption.cs │ ├── FilesPatternOption.cs │ ├── ICommand.cs │ ├── IDirectoryOption.cs │ ├── IFileOption.cs │ ├── IOption.cs │ ├── Options │ │ ├── CloverOutputOption.cs │ │ ├── CoberturaOutputOption.cs │ │ ├── CoverageFileOption.cs │ │ ├── CoverageLoadedFileOption.cs │ │ ├── ExcludeAssembliesPatternOption.cs │ │ ├── ExcludeSourcesPatternOption.cs │ │ ├── ExcludeTestsPatternOption.cs │ │ ├── HitsDirectoryOption.cs │ │ ├── HtmlOutputDirectoryOption.cs │ │ ├── ICloverOutputOption.cs │ │ ├── ICoberturaOutputOption.cs │ │ ├── ICoverageLoadedFileOption.cs │ │ ├── IHtmlOutputDirectoryOption.cs │ │ ├── INCoverOutputOption.cs │ │ ├── INoFailOption.cs │ │ ├── IOpenCoverOutputOption.cs │ │ ├── IThresholdOption.cs │ │ ├── IVerbosityOption.cs │ │ ├── IWorkingDirectoryOption.cs │ │ ├── IncludeAssembliesPatternOption.cs │ │ ├── IncludeSourcesPatternOption.cs │ │ ├── IncludeTestsPatternOption.cs │ │ ├── NCoverOutputOption.cs │ │ ├── NoFailOption.cs │ │ ├── OpenCoverOutputOption.cs │ │ ├── ParentDirectoryOption.cs │ │ ├── ThresholdOption.cs │ │ ├── VerbosityOption.cs │ │ └── WorkingDirectoryOption.cs │ └── StringOption.cs │ ├── Exceptions │ └── ValidationException.cs │ ├── IO │ ├── ConsoleOutput.cs │ ├── IOutput.cs │ ├── OutputLogger.cs │ └── OutputLoggerProvider.cs │ ├── MiniCover.csproj │ ├── Program.cs │ └── Properties │ └── launchSettings.json └── tests ├── MiniCover.Core.UnitTests ├── Extensions │ └── GraphNodeExtensionsTests.cs ├── Hits │ └── HitsInfoTests.cs ├── Instrumentation │ ├── AsyncMethod.cs │ ├── BaseTest.cs │ ├── ConstructorGenericCallThis.cs │ ├── ConstructorWithCallBase.cs │ ├── ConstructorWithTryFinally.cs │ ├── EnumerableMethod.cs │ ├── ExcludedFromCodeCoverageClass.cs │ ├── ExcludedFromCodeCoverageMethod.cs │ ├── FieldInitialization.cs │ ├── For.cs │ ├── If.cs │ ├── IfInline.cs │ ├── IfInlineNested.cs │ ├── IfWithAnd.cs │ ├── Lambda.cs │ ├── LongMethod.cs │ ├── Or.cs │ ├── OrWithEquals.cs │ ├── PropInit.cs │ ├── PropSet.cs │ ├── StaticMethod.cs │ ├── Switch.cs │ ├── ThrowException.cs │ ├── Using.cs │ └── WorkerThread.cs ├── MiniCover.Core.UnitTests.csproj ├── TestHelpers │ ├── ILFormatter.cs │ ├── InstrumentationExtensions.cs │ └── TestBase.cs └── Utils │ ├── DepsJsonUtilsTests.cs │ └── FileUtilsTests.cs ├── MiniCover.HitServices.UnitTests ├── HitContextTests.cs └── MiniCover.HitServices.UnitTests.csproj ├── MiniCover.Reports.UnitTests ├── MiniCover.Reports.UnitTests.csproj ├── TestHelpers │ └── TestBase.cs └── Utils │ └── ResourceUtilsTests.cs ├── MiniCover.TestHelpers ├── MiniCover.TestHelpers.csproj ├── StringExtensions.cs └── TestBase.cs └── MiniCover.UnitTests ├── CommandLine ├── CommandTests.cs ├── Commands │ ├── CloverReportCommandTests.cs │ ├── CoberturaReportCommandTests.cs │ ├── ConsoleReportCommandTests.cs │ ├── CoverallsReportCommandTests.cs │ ├── HtmlReportCommandTests.cs │ ├── NCoverReportCommandTests.cs │ ├── OpenCoverReportCommandTests.cs │ └── UninstrumentCommandTests.cs ├── DirectoryOptionTests.cs ├── FileOptionTests.cs ├── FilesPatternOptionTests.cs ├── Options │ ├── CloverOutputOptionTests.cs │ ├── CoberturaOutputOptionTests.cs │ ├── CoverageFileOptionTests.cs │ ├── CoverageLoadedFileOptionTests.cs │ ├── ExcludeAssembliesPatternOptionTests.cs │ ├── ExcludeSourcesPatternOptionTests.cs │ ├── ExcludeTestsPatternOptionTests.cs │ ├── HitsDirectoryOptionTests.cs │ ├── HtmlOutputDirectoryOptionTests.cs │ ├── IncludeAssembliesPatternOptionTests.cs │ ├── IncludeSourcesPatternOptionTests.cs │ ├── IncludeTestsPatternOptionTests.cs │ ├── NCoverOutputOptionTests.cs │ ├── OpenCoverOutputOptionTests.cs │ ├── ParentDirectoryOptionTests.cs │ ├── ThresholdOptionTests.cs │ ├── VerbosityOptionTests.cs │ └── WorkingDirectoryOptionTests.cs └── StringOptionTests.cs └── MiniCover.UnitTests.csproj /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Lucas Lorentz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - '*' 5 | - refs/tags/* 6 | 7 | jobs: 8 | 9 | - job: Build_Windows 10 | pool: 11 | vmImage: 'windows-latest' 12 | steps: 13 | - task: UseDotNet@2 14 | inputs: 15 | useGlobalJson: true 16 | - bash: | 17 | ./build.sh 18 | displayName: 'Build' 19 | env: 20 | COVERALLS_REPO_TOKEN: '$(COVERALLS_REPO_TOKEN)' 21 | - bash: | 22 | ./build-sample.sh 23 | displayName: 'Build Sample' 24 | - task: PublishCodeCoverageResults@1 25 | displayName: 'Publish code coverage' 26 | inputs: 27 | codeCoverageTool: Cobertura 28 | summaryFileLocation: '$(Build.SourcesDirectory)/cobertura.xml' 29 | 30 | - job: Build_Linux 31 | pool: 32 | vmImage: 'ubuntu-latest' 33 | steps: 34 | - task: UseDotNet@2 35 | inputs: 36 | useGlobalJson: true 37 | - bash: | 38 | ./build.sh 39 | displayName: 'Build' 40 | 41 | - job: Push_Package 42 | dependsOn: 43 | - Build_Windows 44 | - Build_Linux 45 | condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v')) 46 | pool: 47 | vmImage: 'ubuntu-latest' 48 | steps: 49 | - task: UseDotNet@2 50 | inputs: 51 | useGlobalJson: true 52 | - bash: | 53 | export Version=${TagName:1} 54 | dotnet pack -c Release -o nupkg 55 | dotnet nuget push "nupkg/*.nupkg" -k $NUGET_KEY -s https://api.nuget.org/v3/index.json --skip-duplicate 56 | env: 57 | TagName: '$(Build.SourceBranchName)' 58 | NUGET_KEY: '$(NUGET_KEY)' 59 | displayName: 'Release' 60 | 61 | -------------------------------------------------------------------------------- /build-sample.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | rm -rd ~/.nuget/packages/minicover/1.0.0 || true 6 | dotnet pack -c Release --output $PWD/sample/nupkgs 7 | cd sample 8 | rm -rf ./coverage 9 | dotnet build 10 | dotnet tool restore -v q 11 | dotnet minicover reset 12 | echo "# Start Instrument" 13 | dotnet minicover instrument 14 | echo "# End Instrument" 15 | dotnet test --no-build 16 | echo "# Start Uninstrument" 17 | dotnet minicover uninstrument 18 | echo "# End Uninstrument" 19 | echo "# Start Report" 20 | dotnet minicover report --threshold 60 21 | echo "# End Report" 22 | echo "# Start HtmlReport" 23 | dotnet minicover htmlreport --threshold 60 24 | echo "# End HtmlReport" 25 | echo "# Start XmlReport" 26 | dotnet minicover xmlreport 27 | echo "# End XmlReport" 28 | echo "# Start OpenCoverReport" 29 | dotnet minicover opencoverreport 30 | echo "# End OpenCoverReport" 31 | echo "# Start CloverReport" 32 | dotnet minicover cloverreport 33 | echo "# End CloverReport" 34 | echo "# Start CoberturaReport" 35 | dotnet minicover coberturareport 36 | echo "# End CoberturaReport" 37 | cd .. 38 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | rm -r artifacts || true 6 | rm -r coverage || true 7 | 8 | dotnet build 9 | 10 | echo "# Start Instrument" 11 | ./minicover.sh instrument --assemblies "**/net9.0/**/*.dll" --tests "" 12 | echo "# End Instrument" 13 | 14 | ./minicover.sh reset 15 | 16 | dotnet test --no-build 17 | 18 | echo "# Start Uninstrument" 19 | ./minicover.sh uninstrument 20 | echo "# End Uninstrument" 21 | 22 | echo "# Start Report" 23 | ./minicover.sh report --threshold 34 24 | echo "# End Report" 25 | 26 | echo "# Start HtmlReport" 27 | ./minicover.sh htmlreport --threshold 34 28 | echo "# End HtmlReport" 29 | 30 | echo "# Start CoberturaReport" 31 | ./minicover.sh coberturareport 32 | echo "# End CoberturaReport" 33 | 34 | if [ -n "${BUILD_BUILDID}" ] && [ -n "${COVERALLS_REPO_TOKEN}" ]; then 35 | echo "# Start Coveralls Report" 36 | ./minicover.sh coverallsreport \ 37 | --service-name "azure-devops" \ 38 | --repo-token "$COVERALLS_REPO_TOKEN" \ 39 | --commit "$BUILD_SOURCEVERSION" \ 40 | --commit-message "$BUILD_SOURCEVERSIONMESSAGE" \ 41 | --branch "$BUILD_SOURCEBRANCHNAME" \ 42 | --remote "origin" \ 43 | --remote-url "https://github.com/lucaslorentz/minicover" || echo "" 44 | echo "# End Coveralls Report" 45 | fi 46 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.100", 4 | "rollForward": "latestMinor" 5 | } 6 | } -------------------------------------------------------------------------------- /minicover.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dotnet run --project src/MiniCover/MiniCover.csproj -f net9.0 -- "$@" -------------------------------------------------------------------------------- /sample/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "minicover": { 6 | "version": "1.0.0", 7 | "commands": [ 8 | "minicover" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /sample/NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample/Sample.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "src\Sample\Sample.csproj", "{FEE4BFB0-6960-4B13-9AF2-E3A5F6B90920}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.UnitTests", "tests\Sample.UnitTests\Sample.UnitTests.csproj", "{E7B2DD61-9046-467B-BADA-1A920B4C0DDB}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FEE4BFB0-6960-4B13-9AF2-E3A5F6B90920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {FEE4BFB0-6960-4B13-9AF2-E3A5F6B90920}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {FEE4BFB0-6960-4B13-9AF2-E3A5F6B90920}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {FEE4BFB0-6960-4B13-9AF2-E3A5F6B90920}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {E7B2DD61-9046-467B-BADA-1A920B4C0DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E7B2DD61-9046-467B-BADA-1A920B4C0DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E7B2DD61-9046-467B-BADA-1A920B4C0DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E7B2DD61-9046-467B-BADA-1A920B4C0DDB}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {520D3324-EBAA-45FF-B9A6-977555C477DA} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /sample/src/Sample/AnotherClass.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Sample 4 | { 5 | public class AnotherClass 6 | { 7 | private int _someProperty = 7; 8 | 9 | public int SomeProperty 10 | { 11 | get 12 | { 13 | return _someProperty; 14 | } 15 | set 16 | { 17 | _someProperty = value; 18 | } 19 | } 20 | 21 | public int SomeMethod() 22 | { 23 | return SomeProperty * 2; 24 | } 25 | 26 | public void AnotherMethod() 27 | { 28 | for (var i = 0; i < 50000; i++) 29 | { 30 | SomeMethod(); 31 | } 32 | } 33 | 34 | public Task AMethodAsync() 35 | { 36 | return Task.FromResult(1); 37 | } 38 | 39 | public void AMethodNotAsync() 40 | { 41 | SomeProperty++; 42 | } 43 | 44 | public dynamic AMethodWithMultipleReturn(int value) 45 | { 46 | if (value % 2 == 0) 47 | return 2; 48 | if (value % 3 == 0) return "3"; 49 | return 1; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sample/src/Sample/ClassWithGeneratedRegex.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace Sample 4 | { 5 | // This class is used to validate that Minicover can instrument assemblies containing code generated by a source generator. 6 | public static partial class ClassWithGeneratedRegex 7 | { 8 | [GeneratedRegex($@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase)] 9 | private static partial Regex EmailRegex(); 10 | 11 | public static bool IsEmail(string text) 12 | { 13 | return EmailRegex().IsMatch(text); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sample/src/Sample/ClassWithYield.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Sample 5 | { 6 | public class ClassWithYield 7 | { 8 | public void Execute() { 9 | foreach(var n in Enumerate1To10()) { 10 | 11 | } 12 | } 13 | 14 | public IEnumerable Enumerate1To10() { 15 | for (var i = 1; i <= 10; i++) { 16 | yield return i; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/src/Sample/DemoEnum.cs: -------------------------------------------------------------------------------- 1 | namespace Sample 2 | { 3 | public enum DemoEnum 4 | { 5 | A, 6 | B, 7 | C 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/src/Sample/PartialClass.cs: -------------------------------------------------------------------------------- 1 | namespace Sample 2 | { 3 | public partial class PartialClass 4 | { 5 | private readonly int value; 6 | 7 | public PartialClass(int value) 8 | { 9 | this.value = value; 10 | } 11 | 12 | partial void APartialMethod(); 13 | 14 | public void CallPartialMethod() 15 | { 16 | APartialMethod(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /sample/src/Sample/PartialClassB.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Sample 4 | { 5 | public partial class PartialClass 6 | { 7 | partial void APartialMethod() 8 | { 9 | throw new Exception(message: value.ToString()); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /sample/src/Sample/Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | false 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/AClassWithATryFinallyInConstructor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Sample.TryFinally 4 | { 5 | public class AClassWithATryFinallyInConstructor 6 | { 7 | private readonly int value; 8 | public AClassWithATryFinallyInConstructor() 9 | { 10 | try 11 | { 12 | value = 5; 13 | } 14 | finally 15 | { 16 | Exit(); 17 | } 18 | } 19 | 20 | public void Exit() 21 | { 22 | try 23 | { 24 | Console.WriteLine("test"); 25 | } 26 | catch (Exception) 27 | { 28 | Console.WriteLine("erro"); 29 | throw; 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/AClassWithFieldInitializedOutsideConstructor.cs: -------------------------------------------------------------------------------- 1 | namespace Sample.TryFinally 2 | { 3 | public class AClassWithFieldInitializedOutsideConstructor 4 | { 5 | private readonly int value = 5; 6 | } 7 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/AClassWithSomeTryFinally.cs: -------------------------------------------------------------------------------- 1 | namespace Sample.TryFinally 2 | { 3 | public class AClassWithSomeTryFinally 4 | { 5 | public int MultiplyByTwo(int value) 6 | { 7 | var test = new AClassWithATryFinallyInConstructor(); 8 | try 9 | { 10 | return value * 2; 11 | } 12 | finally 13 | { 14 | test.Exit(); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/AnAbstractClass.cs: -------------------------------------------------------------------------------- 1 | namespace Sample.TryFinally 2 | { 3 | public abstract class AnAbstractClass 4 | { 5 | public int AnotherValue { get; } 6 | private readonly int value = 5; 7 | 8 | protected AnAbstractClass(int anotherValue) 9 | { 10 | AnotherValue = anotherValue; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/AnotherClassWithoutTryFinally.cs: -------------------------------------------------------------------------------- 1 | namespace Sample.TryFinally 2 | { 3 | public class AnotherClassWithoutTryFinally 4 | { 5 | public int MultiplyByTwo(int value) 6 | { 7 | return value * 2; 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/ClassWithComplicatedLambda.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Sample.TryFinally 5 | { 6 | public class ClassWithComplicatedLambda 7 | { 8 | public int Add2ToEachValueAndSumThem(params int[] values) 9 | { 10 | return values.Select(a => 11 | { 12 | var value = a + 2; 13 | return value; 14 | }).Sum(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/ClassWithMultipleConstructors.cs: -------------------------------------------------------------------------------- 1 | namespace Sample.TryFinally 2 | { 3 | public class ClassWithMultipleConstructors 4 | { 5 | private static readonly int staticValue; 6 | private readonly int value; 7 | private ClassWithMultipleConstructors() : this(15) 8 | { 9 | } 10 | 11 | private ClassWithMultipleConstructors(int value) 12 | { 13 | this.value = value * staticValue; 14 | } 15 | 16 | static ClassWithMultipleConstructors() 17 | { 18 | staticValue = 15; 19 | } 20 | 21 | public static ClassWithMultipleConstructors BuildFor(int value) => new ClassWithMultipleConstructors(value); 22 | 23 | public static ClassWithMultipleConstructors Default() => new ClassWithMultipleConstructors(); 24 | } 25 | 26 | public class AnotherClassWithMultipleConstructors 27 | { 28 | public string Text { get; } 29 | private readonly int value; 30 | 31 | public AnotherClassWithMultipleConstructors(int value, string text) 32 | { 33 | Text = text; 34 | this.value = value; 35 | } 36 | 37 | public AnotherClassWithMultipleConstructors() 38 | : this("test") 39 | { 40 | } 41 | 42 | public AnotherClassWithMultipleConstructors(string text) 43 | : this(12, text) 44 | { 45 | 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/ClassWithSimpleLambda.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Sample.TryFinally 4 | { 5 | public class ClassWithSimpleLambda 6 | { 7 | public int Add2ToEachValueAndSumThem(params int[] values) 8 | { 9 | return values.Select(a => a + 2).Sum(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/ClassWithTryFinallyInLambda.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Sample.TryFinally 5 | { 6 | public class ClassWithTryFinallyInLambda 7 | { 8 | public int Add2ToEachValueAndSumThem(params int[] values) 9 | { 10 | try 11 | { 12 | return values.Select(a => 13 | { 14 | try 15 | { 16 | return a + 2; 17 | } 18 | finally 19 | { 20 | SomeMethod(); 21 | } 22 | }).Sum(); 23 | } 24 | finally 25 | { 26 | SomeMethod(); 27 | } 28 | } 29 | 30 | public void SomeMethod() { 31 | 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /sample/src/Sample/TryFinally/HeritingClass.cs: -------------------------------------------------------------------------------- 1 | namespace Sample.TryFinally 2 | { 3 | public sealed class HeritingClass : AnAbstractClass 4 | { 5 | public HeritingClass(int anotherValue) 6 | : base(anotherValue) 7 | { 8 | this.Value = 15 * anotherValue; 9 | } 10 | 11 | public AnotherClass Instance { get; } = new AnotherClass(); 12 | public int Value { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /sample/src/Sample/WorkerThread.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Sample 5 | { 6 | public static class WorkerThread 7 | { 8 | public static Task RunWorkerThread() 9 | { 10 | var tcs = new TaskCompletionSource(); 11 | var thread = new Thread(() => DoWork(tcs)); 12 | thread.Start(); 13 | return tcs.Task; 14 | } 15 | 16 | private static void DoWork(TaskCompletionSource tcs) 17 | { 18 | for (int i = 0; i < 3; i++) 19 | { 20 | Thread.Sleep(100); 21 | } 22 | 23 | tcs.SetResult(true); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/tests/Sample.UnitTests/ClassWithGeneratedRegexTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Sample.TryFinally; 5 | using Xunit; 6 | 7 | namespace Sample.UnitTests 8 | { 9 | public class ClassWithGeneratedRegexTest 10 | { 11 | [Fact] 12 | public void Test() 13 | { 14 | var isEmail = ClassWithGeneratedRegex.IsEmail("test@test.com"); 15 | Assert.True(isEmail); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sample/tests/Sample.UnitTests/Sample.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | all 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sample/tests/Sample.UnitTests/WorkerThreadTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Sample.TryFinally; 5 | using Xunit; 6 | 7 | namespace Sample.UnitTests 8 | { 9 | public class WorkerThreadTests 10 | { 11 | [Fact] 12 | public async Task TestWorkerThread() 13 | { 14 | await WorkerThread.RunWorkerThread(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/AssemblyDefinitionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Mono.Cecil; 4 | using Mono.Cecil.Cil; 5 | 6 | namespace MiniCover.Core.Extensions 7 | { 8 | public static class AssemblyDefinitionExtensions 9 | { 10 | public static IList GetAllDocuments(this AssemblyDefinition assemblyDefinition) 11 | { 12 | return assemblyDefinition 13 | .MainModule.GetTypes() 14 | .SelectMany(t => t.GetAllDocuments(false)) 15 | .Distinct() 16 | .ToArray(); 17 | } 18 | 19 | public static bool IsExcludedFromCodeCoverage(this AssemblyDefinition assemblyDefinition) 20 | { 21 | return assemblyDefinition.HasExcludeFromCodeCoverageAttribute(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/CodeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MiniCover.Core.Extensions 5 | { 6 | public static class CodeExtensions 7 | { 8 | public static string ExtractCode(this string[] fileLines, int startLine, int endLine, int startColumn, int endColumn) 9 | { 10 | if (startLine == endLine) 11 | { 12 | var lineIndex = startLine - 1; 13 | if (lineIndex < 0 || lineIndex >= fileLines.Length) 14 | return null; 15 | 16 | var startIndex = startColumn - 1; 17 | if (startIndex < 0 || startIndex >= fileLines[lineIndex].Length) 18 | return null; 19 | 20 | var length = endColumn - startColumn; 21 | if (length <= 0 || startIndex + length > fileLines[lineIndex].Length) 22 | return null; 23 | 24 | return fileLines[lineIndex].Substring(startIndex, length); 25 | } 26 | else 27 | { 28 | var result = new List(); 29 | 30 | var firstLineIndex = startLine - 1; 31 | if (firstLineIndex < 0 || firstLineIndex >= fileLines.Length) 32 | return null; 33 | 34 | var startColumnIndex = startColumn - 1; 35 | if (startColumnIndex < 0 || startColumnIndex >= fileLines[firstLineIndex].Length) 36 | return null; 37 | 38 | var lastLineIndex = endLine - 1; 39 | if (lastLineIndex < firstLineIndex || lastLineIndex >= fileLines.Length) 40 | return null; 41 | 42 | var endLineLength = endColumn - 1; 43 | if (endLineLength <= 0 || endLineLength > fileLines[lastLineIndex].Length) 44 | return null; 45 | 46 | result.Add(fileLines[firstLineIndex].Substring(startColumnIndex)); 47 | 48 | for (var l = firstLineIndex + 1; l < lastLineIndex; l++) 49 | result.Add(fileLines[l]); 50 | 51 | result.Add(fileLines[lastLineIndex].Substring(0, endLineLength)); 52 | 53 | return string.Join(Environment.NewLine, result); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/CustomAttributeProviderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Mono.Cecil; 3 | 4 | namespace MiniCover.Core.Extensions 5 | { 6 | public static class CustomAttributeProviderExtensions 7 | { 8 | public static bool HasCompilerGeneratedAttribute(this ICustomAttributeProvider definition) 9 | { 10 | return definition.CustomAttributes 11 | .Any(c => c.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute"); 12 | } 13 | 14 | public static bool HasExcludeFromCodeCoverageAttribute(this ICustomAttributeProvider definition) 15 | { 16 | return definition.CustomAttributes 17 | .Any(a => a.AttributeType.FullName == "System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/DocumentExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Security.Cryptography; 4 | using Mono.Cecil.Cil; 5 | 6 | namespace MiniCover.Core.Extensions 7 | { 8 | public static class DocumentExtensions 9 | { 10 | public static bool FileHasChanged(this Document document) 11 | { 12 | if (!File.Exists(document.Url)) 13 | { 14 | // Ignore not found source generated files because they might be in memory 15 | if (document.Url.EndsWith(".g.cs")) 16 | return false; 17 | 18 | return true; 19 | } 20 | 21 | using (var stream = File.OpenRead(document.Url)) 22 | { 23 | var hasher = CreateHashAlgorithm(document.HashAlgorithm); 24 | 25 | if (hasher == null) 26 | return false; 27 | 28 | using (hasher) 29 | { 30 | var newHash = hasher.ComputeHash(stream); 31 | return !newHash.SequenceEqual(document.Hash); 32 | } 33 | } 34 | } 35 | 36 | private static HashAlgorithm CreateHashAlgorithm(DocumentHashAlgorithm algorithm) 37 | { 38 | switch (algorithm) 39 | { 40 | case DocumentHashAlgorithm.SHA1: return SHA1.Create(); 41 | case DocumentHashAlgorithm.SHA256: return SHA256.Create(); 42 | case DocumentHashAlgorithm.MD5: return MD5.Create(); 43 | default: return null; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace MiniCover.Core.Extensions 6 | { 7 | public static class EnumerableExtensions 8 | { 9 | public static IEnumerable> GroupByMany(this IEnumerable source, Func> keysSelector) 10 | { 11 | return source.SelectMany(x => keysSelector(x), (x, k) => new { x, k }) 12 | .GroupBy(j => j.k, j => j.x); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/GraphNodeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Core.Extensions 5 | { 6 | public static class GraphNodeExtensions 7 | { 8 | public static HashSet> Filter( 9 | this GraphNode rootNode, 10 | HashSet allowedValues) 11 | { 12 | var rootList = new HashSet> { rootNode }; 13 | 14 | var pending = new Queue<(HashSet> parentList, GraphNode node)>(); 15 | pending.Enqueue((rootList, rootNode)); 16 | 17 | var visitedNodes = new HashSet<(HashSet> parentList, GraphNode node)>(); 18 | 19 | while (pending.Count > 0) 20 | { 21 | var current = pending.Dequeue(); 22 | 23 | if (allowedValues.Contains(current.node.Value)) 24 | { 25 | if (!visitedNodes.Add(current)) 26 | continue; 27 | 28 | foreach (var child in current.node.Children) 29 | pending.Enqueue((current.node.Children, child)); 30 | } 31 | else if (current.parentList.Remove(current.node)) 32 | { 33 | if (!visitedNodes.Add(current)) 34 | continue; 35 | 36 | foreach (var child in current.node.Children) 37 | { 38 | current.parentList.Add(child); 39 | pending.Enqueue((current.parentList, child)); 40 | } 41 | } 42 | } 43 | 44 | return rootList; 45 | } 46 | 47 | public static HashSet> GetBranches(this HashSet> nodes) 48 | { 49 | var pending = new Queue>(nodes); 50 | var branches = new HashSet>(); 51 | var visitedNodes = new HashSet>(); 52 | 53 | while (pending.Count > 0) 54 | { 55 | var node = pending.Dequeue(); 56 | 57 | if (!visitedNodes.Add(node)) 58 | continue; 59 | 60 | if (node.Children.Count > 1) 61 | { 62 | branches.Add(node); 63 | } 64 | 65 | foreach (var child in node.Children) 66 | pending.Enqueue(child); 67 | } 68 | 69 | return branches; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/InstructionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MiniCover.Core.Model; 3 | using Mono.Cecil.Cil; 4 | 5 | namespace MiniCover.Core.Extensions 6 | { 7 | public static class InstructionExtensions 8 | { 9 | public static GraphNode ToGraph(this Instruction rootInstruction) 10 | { 11 | var cache = new Dictionary>(); 12 | 13 | var rootNode = new GraphNode(rootInstruction); 14 | cache[rootInstruction] = rootNode; 15 | 16 | var pending = new Queue>(); 17 | pending.Enqueue(rootNode); 18 | 19 | while (pending.Count > 0) 20 | { 21 | var node = pending.Dequeue(); 22 | foreach (var childInstruction in node.Value.GetChildren()) 23 | { 24 | if (!cache.TryGetValue(childInstruction, out var childNode)) 25 | { 26 | childNode = new GraphNode(childInstruction); 27 | cache[childInstruction] = childNode; 28 | pending.Enqueue(childNode); 29 | } 30 | 31 | node.Children.Add(childNode); 32 | } 33 | } 34 | 35 | return rootNode; 36 | } 37 | 38 | public static IEnumerable GetChildren(this Instruction instruction) 39 | { 40 | switch (instruction.OpCode.FlowControl) 41 | { 42 | case FlowControl.Cond_Branch: 43 | yield return instruction.Next; 44 | 45 | if (instruction.Operand is Instruction operand) 46 | yield return operand; 47 | else if (instruction.Operand is Instruction[] operands) 48 | foreach (var i in operands) 49 | yield return i; 50 | 51 | break; 52 | case FlowControl.Branch: 53 | yield return instruction.Operand as Instruction; 54 | break; 55 | default: 56 | if (instruction.Next != null) 57 | yield return instruction.Next; 58 | 59 | break; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/ModuleDefinitionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Reflection; 4 | using Mono.Cecil; 5 | 6 | namespace MiniCover.Core.Extensions 7 | { 8 | public static class ModuleDefinitionExtensions 9 | { 10 | public static TypeReference GetOrImportReference(this ModuleDefinition moduleDefinition, Type type) 11 | { 12 | var references = moduleDefinition.GetOrAddExtension("TypeReferences", () => new ConcurrentDictionary()); 13 | return references.GetOrAdd(type, (t) => moduleDefinition.ImportReference(type)); 14 | } 15 | 16 | public static MethodReference GetOrImportReference(this ModuleDefinition moduleDefinition, MethodInfo method) 17 | { 18 | var references = moduleDefinition.GetOrAddExtension("MethodReferences", () => new ConcurrentDictionary()); 19 | return references.GetOrAdd(method, (t) => moduleDefinition.ImportReference(method)); 20 | } 21 | 22 | public static bool IsExcludedFromCodeCoverage(this ModuleDefinition moduleDefinition) 23 | { 24 | return moduleDefinition.HasExcludeFromCodeCoverageAttribute() 25 | || moduleDefinition.Assembly.IsExcludedFromCodeCoverage(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace MiniCover.Core.Extensions 6 | { 7 | public static class ObjectExtensions 8 | { 9 | private static ConditionalWeakTable> _extendedDataTable 10 | = new ConditionalWeakTable>(); 11 | 12 | public static T GetExtension(this object obj, string key, T defaultValue = default) 13 | { 14 | var extendedData = _extendedDataTable.GetOrCreateValue(obj); 15 | if (!extendedData.TryGetValue(key, out var value)) 16 | return defaultValue; 17 | return (T)value; 18 | } 19 | 20 | public static T GetOrAddExtension(this object obj, string key, Func factory) 21 | { 22 | var extendedData = _extendedDataTable.GetOrCreateValue(obj); 23 | return (T)extendedData.GetOrAdd(key, k => factory()); 24 | } 25 | 26 | public static void SetExtension(this object obj, string key, object value) 27 | { 28 | var extendedData = _extendedDataTable.GetOrCreateValue(obj); 29 | extendedData[key] = value; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Extensions/TypeDefinitionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Mono.Cecil; 4 | using Mono.Cecil.Cil; 5 | 6 | namespace MiniCover.Core.Extensions 7 | { 8 | public static class TypeDefinitionExtensions 9 | { 10 | public static IList GetAllDocuments(this TypeDefinition typeDefinition, bool includeNestedTypes) 11 | { 12 | return typeDefinition.GetAllMethods(includeNestedTypes) 13 | .SelectMany(m => m.GetAllDocuments()) 14 | .Distinct() 15 | .ToArray(); 16 | } 17 | 18 | public static IEnumerable GetAllMethods(this TypeDefinition typeDefinition, bool includeNestedTypes) 19 | { 20 | foreach (var method in typeDefinition.Methods.Where(m => m.HasBody && m.DebugInformation.HasSequencePoints)) 21 | yield return method; 22 | 23 | if (includeNestedTypes) 24 | { 25 | foreach (var subType in typeDefinition.NestedTypes) 26 | { 27 | foreach (var method in GetAllMethods(subType, includeNestedTypes)) 28 | yield return method; 29 | } 30 | } 31 | } 32 | 33 | public static bool IsCompilerGenerated(this TypeDefinition typeDefinition) 34 | { 35 | return typeDefinition.HasCompilerGeneratedAttribute() 36 | || (typeDefinition.DeclaringType?.IsCompilerGenerated() ?? false); 37 | } 38 | 39 | public static bool IsExcludedFromCodeCoverage(this TypeDefinition typeDefinition) 40 | { 41 | if (typeDefinition.HasExcludeFromCodeCoverageAttribute()) 42 | return true; 43 | 44 | if (typeDefinition.DeclaringType != null) 45 | return typeDefinition.DeclaringType.IsExcludedFromCodeCoverage(); 46 | 47 | return typeDefinition.Module.IsExcludedFromCodeCoverage(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/MiniCover.Core/FileSystem/CachedFileReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.IO.Abstractions; 3 | using Microsoft.Extensions.Caching.Memory; 4 | 5 | namespace MiniCover.Core.FileSystem 6 | { 7 | public class CachedFileReader : IFileReader 8 | { 9 | private readonly IMemoryCache _memoryCache; 10 | private readonly IFileSystem _fileSystem; 11 | 12 | public CachedFileReader( 13 | IMemoryCache memoryCache, 14 | IFileSystem fileSystem) 15 | { 16 | _memoryCache = memoryCache; 17 | _fileSystem = fileSystem; 18 | } 19 | 20 | public string[] ReadAllLines(FileInfo file) 21 | { 22 | return _memoryCache.GetOrCreate(file.FullName, entry => 23 | { 24 | entry.Priority = CacheItemPriority.Low; 25 | return _fileSystem.File.ReadAllLines(file.FullName); 26 | }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/MiniCover.Core/FileSystem/IFileReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace MiniCover.Core.FileSystem 4 | { 5 | public interface IFileReader 6 | { 7 | string[] ReadAllLines(FileInfo file); 8 | } 9 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Hits/HitsInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using MiniCover.HitServices; 5 | 6 | namespace MiniCover.Core.Hits 7 | { 8 | public class HitsInfo 9 | { 10 | private readonly Dictionary _valuesById; 11 | 12 | public HitsInfo(IEnumerable contexts) 13 | { 14 | _valuesById = HitContext.MergeDuplicates(contexts) 15 | .SelectMany(c => c.Hits.Keys, (context, id) => new { 16 | context, 17 | id, 18 | hitCount = context.GetHitCount(id) 19 | }) 20 | .GroupBy(g => g.id) 21 | .ToDictionary(g => g.Key, g => new HitValues 22 | { 23 | HitCount = g.Sum(d => d.hitCount), 24 | Contexts = g.Select(d => d.context).ToArray() 25 | }); 26 | } 27 | 28 | public bool WasHit(int id) 29 | { 30 | return _valuesById.ContainsKey(id); 31 | } 32 | 33 | public int GetHitCount(int id) 34 | { 35 | if (!_valuesById.TryGetValue(id, out var values)) 36 | return 0; 37 | 38 | return values.HitCount; 39 | } 40 | 41 | public IEnumerable GetHitContexts(int id) 42 | { 43 | if (!_valuesById.TryGetValue(id, out var values)) 44 | return Enumerable.Empty(); 45 | 46 | return values.Contexts; 47 | } 48 | 49 | class HitValues 50 | { 51 | public int HitCount { get; set; } 52 | public HitContext[] Contexts { get; set; } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Hits/HitsReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.IO.Abstractions; 4 | using MiniCover.HitServices; 5 | 6 | namespace MiniCover.Core.Hits 7 | { 8 | public class HitsReader : IHitsReader 9 | { 10 | private readonly IFileSystem _fileSystem; 11 | 12 | public HitsReader(IFileSystem fileSystem) 13 | { 14 | _fileSystem = fileSystem; 15 | } 16 | 17 | public HitsInfo TryReadFromDirectory(string path) 18 | { 19 | var contexts = new List(); 20 | 21 | if (_fileSystem.Directory.Exists(path)) 22 | { 23 | foreach (var hitFile in _fileSystem.Directory.GetFiles(path, "*.hits")) 24 | { 25 | using (var fileStream = _fileSystem.File.Open(hitFile, FileMode.Open, FileAccess.Read)) 26 | { 27 | contexts.AddRange(HitContext.Deserialize(fileStream)); 28 | } 29 | } 30 | } 31 | 32 | return new HitsInfo(contexts); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Hits/HitsResetter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO.Abstractions; 3 | using System.Linq; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace MiniCover.Core.Hits 7 | { 8 | public class HitsResetter : IHitsResetter 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public HitsResetter( 13 | ILogger logger) 14 | { 15 | _logger = logger; 16 | } 17 | 18 | public bool ResetHits(IDirectoryInfo hitsDirectory) 19 | { 20 | _logger.LogInformation("Resetting hits directory '{directory}'", hitsDirectory.FullName); 21 | 22 | var hitsFiles = hitsDirectory.Exists 23 | ? hitsDirectory.GetFiles("*.hits") 24 | : new IFileInfo[0]; 25 | 26 | if (!hitsFiles.Any()) 27 | { 28 | _logger.LogInformation("Directory is already cleared"); 29 | return true; 30 | } 31 | 32 | _logger.LogInformation("Found {count} files to clean", hitsFiles.Length); 33 | 34 | var errorsCount = 0; 35 | foreach (var hitsFile in hitsFiles) 36 | { 37 | try 38 | { 39 | hitsFile.Delete(); 40 | _logger.LogTrace("{fileName} - removed", hitsFile.FullName); 41 | } 42 | catch (Exception e) 43 | { 44 | errorsCount++; 45 | _logger.LogError("{fileName} - error: {error}", hitsFile.FullName, e.Message); 46 | } 47 | } 48 | 49 | if (errorsCount != 0) 50 | { 51 | _logger.LogError("Reset operation completed with {errorsCount} errors", errorsCount); 52 | return false; 53 | } 54 | 55 | _logger.LogInformation("Reset operation completed without errors"); 56 | return true; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Hits/IHitsReader.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.Core.Hits 2 | { 3 | public interface IHitsReader 4 | { 5 | HitsInfo TryReadFromDirectory(string path); 6 | } 7 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Hits/IHitsResetter.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.Core.Hits 4 | { 5 | public interface IHitsResetter 6 | { 7 | bool ResetHits(IDirectoryInfo hitsDirectory); 8 | } 9 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/FileBasedInstrumentationContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO.Abstractions; 3 | using System.Linq; 4 | using MiniCover.Core.Extensions; 5 | using Mono.Cecil; 6 | 7 | namespace MiniCover.Core.Instrumentation 8 | { 9 | public class FileBasedInstrumentationContext : IInstrumentationContext 10 | { 11 | private int _uniqueId; 12 | 13 | public virtual IList Assemblies { get; set; } 14 | public virtual IList Sources { get; set; } 15 | public virtual IList Tests { get; set; } 16 | public virtual string HitsPath { get; set; } 17 | public virtual IDirectoryInfo Workdir { get; set; } 18 | 19 | public virtual int NewInstructionId() 20 | { 21 | return ++_uniqueId; 22 | } 23 | 24 | public virtual bool IsSource(MethodDefinition methodDefinition) 25 | { 26 | return methodDefinition.GetAllDocuments() 27 | .Any(d => Sources.Any(s => s.FullName == d.Url)); 28 | } 29 | 30 | public virtual bool IsTest(MethodDefinition methodDefinition) 31 | { 32 | return methodDefinition.GetAllDocuments() 33 | .Any(d => Tests.Any(s => s.FullName == d.Url)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/IAssemblyInstrumenter.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Core.Instrumentation 5 | { 6 | public interface IAssemblyInstrumenter 7 | { 8 | InstrumentedAssembly InstrumentAssemblyFile(IInstrumentationContext context, IFileInfo assemblyFile); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/IInstrumentationContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO.Abstractions; 3 | using Mono.Cecil; 4 | 5 | namespace MiniCover.Core.Instrumentation 6 | { 7 | public interface IInstrumentationContext 8 | { 9 | IList Assemblies { get; } 10 | string HitsPath { get; } 11 | IDirectoryInfo Workdir { get; } 12 | int NewInstructionId(); 13 | bool IsSource(MethodDefinition methodDefinition); 14 | bool IsTest(MethodDefinition methodDefinition); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/IInstrumenter.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Model; 2 | 3 | namespace MiniCover.Core.Instrumentation 4 | { 5 | public interface IInstrumenter 6 | { 7 | InstrumentationResult Instrument(IInstrumentationContext context); 8 | } 9 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/IMethodInstrumenter.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Model; 2 | using Mono.Cecil; 3 | 4 | namespace MiniCover.Core.Instrumentation 5 | { 6 | public interface IMethodInstrumenter 7 | { 8 | void InstrumentMethod(IInstrumentationContext context, bool instrumentInstructions, MethodDefinition methodDefinition, InstrumentedAssembly instrumentedAssembly); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/ITypeInstrumenter.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Model; 2 | using Mono.Cecil; 3 | 4 | namespace MiniCover.Core.Instrumentation 5 | { 6 | public interface ITypeInstrumenter 7 | { 8 | void InstrumentType(IInstrumentationContext context, TypeDefinition typeDefinition, InstrumentedAssembly instrumentedAssembly); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/IUninstrumenter.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Model; 2 | 3 | namespace MiniCover.Core.Instrumentation 4 | { 5 | public interface IUninstrumenter 6 | { 7 | void Uninstrument(InstrumentationResult result); 8 | } 9 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/Patterns/LambdaInitPattern.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MiniCover.Core.Extensions; 3 | using Mono.Cecil; 4 | using Mono.Cecil.Cil; 5 | 6 | namespace MiniCover.Core.Instrumentation.Patterns 7 | { 8 | public static class LambdaInitPattern 9 | { 10 | public static IEnumerable FindInstructions(IList instructions) 11 | { 12 | for (var i = 0; i < instructions.Count; i++) 13 | { 14 | var openInstruction = instructions[i]; 15 | 16 | if (openInstruction.OpCode.Code == Code.Ldsfld 17 | && openInstruction.Operand is FieldDefinition fieldDefinitionI 18 | && fieldDefinitionI.DeclaringType.IsCompilerGenerated()) 19 | { 20 | for (; i < instructions.Count; i++) 21 | { 22 | var currentInstruction = instructions[i]; 23 | 24 | yield return currentInstruction; 25 | 26 | if (currentInstruction.OpCode.Code == Code.Stsfld 27 | && currentInstruction.Operand is FieldDefinition fieldDefinitionJ 28 | && fieldDefinitionJ == fieldDefinitionI) 29 | { 30 | break; 31 | } 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/TypeInstrumenter.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Model; 2 | using Mono.Cecil; 3 | 4 | namespace MiniCover.Core.Instrumentation 5 | { 6 | public class TypeInstrumenter : ITypeInstrumenter 7 | { 8 | private readonly IMethodInstrumenter _methodInstrumenter; 9 | 10 | public TypeInstrumenter(IMethodInstrumenter methodInstrumenter) 11 | { 12 | _methodInstrumenter = methodInstrumenter; 13 | } 14 | 15 | public void InstrumentType( 16 | IInstrumentationContext context, 17 | TypeDefinition typeDefinition, 18 | InstrumentedAssembly instrumentedAssembly) 19 | { 20 | foreach (var methodDefinition in typeDefinition.Methods) 21 | { 22 | if (!methodDefinition.HasBody || !methodDefinition.DebugInformation.HasSequencePoints) 23 | continue; 24 | 25 | var isTest = context.IsTest(methodDefinition); 26 | var isSource = context.IsSource(methodDefinition); 27 | if (!isSource && !isTest) 28 | continue; 29 | 30 | _methodInstrumenter.InstrumentMethod( 31 | context, 32 | isSource, 33 | methodDefinition, 34 | instrumentedAssembly); 35 | } 36 | 37 | foreach (var nestedType in typeDefinition.NestedTypes) 38 | InstrumentType(context, nestedType, instrumentedAssembly); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Instrumentation/Uninstrumenter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.IO.Abstractions; 3 | using MiniCover.Core.Model; 4 | using MiniCover.Core.Utils; 5 | 6 | namespace MiniCover.Core.Instrumentation 7 | { 8 | public class Uninstrumenter : IUninstrumenter 9 | { 10 | private readonly DepsJsonUtils _depsJsonUtils; 11 | private readonly IFileSystem _fileSystem; 12 | 13 | public Uninstrumenter( 14 | DepsJsonUtils depsJsonUtils, 15 | IFileSystem fileSystem) 16 | { 17 | _depsJsonUtils = depsJsonUtils; 18 | _fileSystem = fileSystem; 19 | } 20 | 21 | public void Uninstrument(InstrumentationResult result) 22 | { 23 | foreach (var assembly in result.Assemblies) 24 | { 25 | foreach (var assemblyLocation in assembly.Locations) 26 | { 27 | if (_fileSystem.File.Exists(assemblyLocation.BackupFile)) 28 | { 29 | _fileSystem.File.Copy(assemblyLocation.BackupFile, assemblyLocation.File, true); 30 | _fileSystem.File.Delete(assemblyLocation.BackupFile); 31 | } 32 | 33 | if (_fileSystem.File.Exists(assemblyLocation.BackupPdbFile)) 34 | { 35 | _fileSystem.File.Copy(assemblyLocation.BackupPdbFile, assemblyLocation.PdbFile, true); 36 | _fileSystem.File.Delete(assemblyLocation.BackupPdbFile); 37 | } 38 | 39 | var assemblyDirectory = _fileSystem.FileInfo.New(assemblyLocation.File).Directory; 40 | foreach (var depsJsonFile in assemblyDirectory.GetFiles("*.deps.json")) 41 | { 42 | _depsJsonUtils.UnpatchDepsJson(depsJsonFile); 43 | } 44 | } 45 | } 46 | 47 | foreach (var extraAssembly in result.ExtraAssemblies) 48 | { 49 | if (_fileSystem.File.Exists(extraAssembly)) 50 | { 51 | _fileSystem.File.Delete(extraAssembly); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/MiniCover.Core/MiniCover.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0;net8.0 5 | 6 | 7 | 8 | Lucas Lorentz 9 | MiniCover code coverage measurement core implementation 10 | https://github.com/lucaslorentz/minicover 11 | coverage,cover,minicover 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/MiniCover.Core/MiniCoverCoreServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using Microsoft.Extensions.DependencyInjection.Extensions; 3 | using MiniCover.Core.FileSystem; 4 | using MiniCover.Core.Hits; 5 | using MiniCover.Core.Instrumentation; 6 | using MiniCover.Core.Utils; 7 | 8 | namespace Microsoft.Extensions.DependencyInjection 9 | { 10 | public static class ServiceCollectionExtensions 11 | { 12 | public static IServiceCollection AddMiniCoverCore(this IServiceCollection services) 13 | { 14 | services.AddMemoryCache(); 15 | 16 | services.TryAddSingleton(); 17 | services.TryAddSingleton(); 18 | services.TryAddSingleton(); 19 | services.TryAddSingleton(); 20 | services.TryAddSingleton(); 21 | services.TryAddSingleton(); 22 | services.TryAddSingleton(); 23 | services.TryAddSingleton(); 24 | services.TryAddSingleton(); 25 | services.TryAddSingleton(); 26 | 27 | return services; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/AssemblyLocation.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.Core.Model 2 | { 3 | public class AssemblyLocation 4 | { 5 | public string File { get; set; } 6 | public string BackupFile { get; set; } 7 | public string PdbFile { get; set; } 8 | public string BackupPdbFile { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/GraphNode.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniCover.Core.Model 4 | { 5 | public class GraphNode 6 | { 7 | public T Value { get; } 8 | 9 | public HashSet> Children { get; } 10 | 11 | public GraphNode(T value, params GraphNode[] children) 12 | { 13 | Value = value; 14 | Children = new HashSet>(children); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/InstrumentationResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Newtonsoft.Json; 4 | 5 | namespace MiniCover.Core.Model 6 | { 7 | public class InstrumentationResult 8 | { 9 | private readonly List _assemblies; 10 | private readonly HashSet _extraAssemblies; 11 | 12 | public InstrumentationResult() 13 | { 14 | _assemblies = new List(); 15 | _extraAssemblies = new HashSet(); 16 | } 17 | 18 | [JsonConstructor] 19 | protected InstrumentationResult( 20 | InstrumentedAssembly[] assemblies, 21 | string[] extraAssemblies) 22 | { 23 | _assemblies = assemblies?.ToList() ?? new List(); 24 | _extraAssemblies = extraAssemblies != null 25 | ? new HashSet(extraAssemblies) 26 | : new HashSet(); 27 | } 28 | 29 | [JsonProperty(Order = -2)] 30 | public string SourcePath { get; set; } 31 | 32 | [JsonProperty(Order = -2)] 33 | public string HitsPath { get; set; } 34 | 35 | public IEnumerable Assemblies => _assemblies; 36 | public IEnumerable ExtraAssemblies => _extraAssemblies; 37 | 38 | public void AddInstrumentedAssembly(InstrumentedAssembly instrumentedAssembly) 39 | { 40 | _assemblies.Add(instrumentedAssembly); 41 | } 42 | 43 | public void AddExtraAssembly(string file) 44 | { 45 | _extraAssemblies.Add(file); 46 | } 47 | 48 | public SourceFile[] GetSourceFiles() 49 | { 50 | return Assemblies 51 | .SelectMany(a => a.SourceFiles) 52 | .GroupBy(sf => sf.Path) 53 | .Select(g => SourceFile.Merge(g)) 54 | .ToArray(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/InstrumentedAssembly.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Newtonsoft.Json; 4 | 5 | namespace MiniCover.Core.Model 6 | { 7 | public class InstrumentedAssembly 8 | { 9 | private readonly Dictionary _methods; 10 | private readonly List _locations; 11 | private readonly SortedDictionary _sourceFiles; 12 | 13 | public InstrumentedAssembly(string name) 14 | { 15 | Name = name; 16 | _methods = new Dictionary(); 17 | _locations = new List(); 18 | _sourceFiles = new SortedDictionary(); 19 | } 20 | 21 | [JsonConstructor] 22 | protected InstrumentedAssembly( 23 | string name, 24 | InstrumentedMethod[] methods, 25 | AssemblyLocation[] locations, 26 | SourceFile[] sourceFiles) 27 | { 28 | Name = name; 29 | _methods = methods.ToDictionary(m => m.FullName, m => m); 30 | _locations = locations.ToList(); 31 | _sourceFiles = new SortedDictionary( 32 | sourceFiles.ToDictionary(sf => sf.Path, sf => sf)); 33 | } 34 | 35 | [JsonProperty(Order = -2)] 36 | public string Name { get; } 37 | 38 | public IEnumerable Methods => _methods.Values; 39 | public IEnumerable Locations => _locations; 40 | public IEnumerable SourceFiles => _sourceFiles.Values; 41 | 42 | [JsonIgnore] 43 | public string TempAssemblyFile { get; set; } 44 | 45 | [JsonIgnore] 46 | public string TempPdbFile { get; set; } 47 | 48 | public InstrumentedMethod GetOrAddMethod(string @class, string name, string fullName) 49 | { 50 | if (_methods.TryGetValue(fullName, out var existingValue)) 51 | return existingValue; 52 | 53 | var method = new InstrumentedMethod 54 | { 55 | Class = @class, 56 | Name = name, 57 | FullName = fullName 58 | }; 59 | 60 | _methods.Add(fullName, method); 61 | 62 | return method; 63 | } 64 | 65 | public void AddSequence(string file, InstrumentedSequence instruction) 66 | { 67 | if (!_sourceFiles.ContainsKey(file)) 68 | { 69 | _sourceFiles[file] = new SourceFile(file); 70 | } 71 | 72 | _sourceFiles[file].Sequences.Add(instruction); 73 | } 74 | 75 | public void AddLocation(string file, string backupFile, string pdbFile, string backupPdbFile) 76 | { 77 | var assemblyLocation = new AssemblyLocation 78 | { 79 | File = file, 80 | BackupFile = backupFile, 81 | PdbFile = pdbFile, 82 | BackupPdbFile = backupPdbFile 83 | }; 84 | 85 | _locations.Add(assemblyLocation); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/InstrumentedBranch.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace MiniCover.Core.Model 4 | { 5 | public class InstrumentedBranch 6 | { 7 | public int HitId { get; set; } 8 | public bool External { get; set; } 9 | 10 | [JsonIgnore] 11 | public string Instruction { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/InstrumentedCondition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace MiniCover.Core.Model 5 | { 6 | public class InstrumentedCondition 7 | { 8 | public InstrumentedBranch[] Branches { get; set; } = new InstrumentedBranch[0]; 9 | 10 | [JsonIgnore] 11 | public string Instruction { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/InstrumentedMethod.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.Core.Model 2 | { 3 | public class InstrumentedMethod 4 | { 5 | public string Class { get; set; } 6 | public string Name { get; set; } 7 | public string FullName { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/InstrumentedSequence.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace MiniCover.Core.Model 5 | { 6 | public class InstrumentedSequence 7 | { 8 | public int HitId { get; set; } 9 | public int StartLine { get; set; } 10 | public int EndLine { get; set; } 11 | public int StartColumn { get; set; } 12 | public int EndColumn { get; set; } 13 | public InstrumentedMethod Method { get; set; } 14 | public InstrumentedCondition[] Conditions { get; set; } = new InstrumentedCondition[0]; 15 | 16 | [JsonIgnore] 17 | public string Instruction { get; set; } 18 | 19 | [JsonIgnore] 20 | public string Code { get; set; } 21 | 22 | public IEnumerable GetLines() 23 | { 24 | for (var lineIndex = StartLine; lineIndex <= EndLine; lineIndex++) 25 | { 26 | yield return lineIndex; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Model/SourceFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Newtonsoft.Json; 4 | 5 | namespace MiniCover.Core.Model 6 | { 7 | public class SourceFile 8 | { 9 | public static SourceFile Merge(IEnumerable sources) 10 | { 11 | var path = sources.Select(s => s.Path).Distinct().Single(); 12 | 13 | return new SourceFile(path) 14 | { 15 | Sequences = sources.SelectMany(s => s.Sequences).ToList() 16 | }; 17 | } 18 | 19 | [JsonConstructor] 20 | public SourceFile(string path) 21 | { 22 | Path = path; 23 | } 24 | 25 | [JsonProperty(Order = -2)] 26 | public string Path { get; } 27 | 28 | public List Sequences = new List(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MiniCover.Core/Utils/FileUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Abstractions; 4 | using System.Security.Cryptography; 5 | 6 | namespace MiniCover.Core.Utils 7 | { 8 | public static class FileUtils 9 | { 10 | public static string GetFileHash(IFileInfo file) 11 | { 12 | using var stream = file.OpenRead(); 13 | using var hasher = MD5.Create(); 14 | var hashBytes = hasher.ComputeHash(stream); 15 | return BitConverter.ToString(hashBytes).Replace("-", string.Empty); 16 | } 17 | 18 | public static IFileInfo GetPdbFile(IFileInfo assemblyFile) 19 | { 20 | return assemblyFile.FileSystem.FileInfo.New(Path.ChangeExtension(assemblyFile.FullName, "pdb")); 21 | } 22 | 23 | public static IFileInfo GetBackupFile(IFileInfo file) 24 | { 25 | return file.FileSystem.FileInfo.New(Path.ChangeExtension(file.FullName, $"uninstrumented{file.Extension}")); 26 | } 27 | 28 | public static bool IsBackupFile(IFileInfo file) 29 | { 30 | return file.Name.Contains(".uninstrumented"); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/MiniCover.Core/Utils/PathUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace MiniCover.Core.Utils 5 | { 6 | public static class PathUtils 7 | { 8 | public static string GetRelativePath(string folder, string file) 9 | { 10 | if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString()) 11 | && !folder.EndsWith(Path.AltDirectorySeparatorChar.ToString())) 12 | folder += Path.DirectorySeparatorChar; 13 | 14 | var baseUri = new Uri(folder); 15 | var fullUri = new Uri(file); 16 | 17 | return Uri.UnescapeDataString( 18 | baseUri.MakeRelativeUri(fullUri) 19 | .ToString() 20 | .Replace('/', Path.DirectorySeparatorChar)); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/MiniCover.HitServices/HitService.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.HitServices 2 | { 3 | public static class HitService 4 | { 5 | public static MethodScope EnterMethod( 6 | string hitsPath, 7 | string assemblyName, 8 | string className, 9 | string methodName) 10 | { 11 | return new MethodScope(hitsPath, assemblyName, className, methodName); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/MiniCover.HitServices/InstrumentedAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiniCover.HitServices 4 | { 5 | public class InstrumentedAttribute : Attribute 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/MiniCover.HitServices/MethodScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace MiniCover.HitServices 5 | { 6 | public class MethodScope : IDisposable 7 | { 8 | private readonly HitContext _hitContext; 9 | private readonly string _hitsPath; 10 | private readonly bool _isEntryMethod; 11 | 12 | public MethodScope( 13 | string hitsPath, 14 | string assemblyName, 15 | string className, 16 | string methodName) 17 | { 18 | _hitContext = HitContext.Current; 19 | 20 | if (_hitContext == null) 21 | { 22 | _hitContext = new HitContext(assemblyName, className, methodName); 23 | _isEntryMethod = true; 24 | HitContext.Current = _hitContext; 25 | } 26 | 27 | _hitsPath = hitsPath; 28 | 29 | lock (_hitContext) 30 | { 31 | _hitContext.EnterMethod(); 32 | } 33 | } 34 | 35 | public void Hit(int id) 36 | { 37 | lock (_hitContext) 38 | { 39 | _hitContext.RecordHit(id); 40 | } 41 | } 42 | 43 | public void Dispose() 44 | { 45 | if (_isEntryMethod) 46 | HitContext.Current = null; 47 | 48 | lock (_hitContext) 49 | { 50 | if (_hitContext.ExitMethod() == 0) 51 | SaveHitContext(); 52 | } 53 | } 54 | 55 | private void SaveHitContext() 56 | { 57 | lock (_hitContext) 58 | { 59 | if (_hitContext.Hits.Count == 0) 60 | return; 61 | 62 | Directory.CreateDirectory(_hitsPath); 63 | 64 | var fileName = Path.Combine(_hitsPath, $"{_hitContext.Id}.hits"); 65 | 66 | using (var fileStream = File.Open(fileName, FileMode.Create)) 67 | { 68 | _hitContext.Serialize(fileStream); 69 | fileStream.Flush(); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/MiniCover.HitServices/MiniCover.HitServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | Lucas Lorentz 6 | MiniCover code coverage measurement core implementation 7 | https://github.com/lucaslorentz/minicover 8 | coverage,cover,minicover 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/Clover/CloverCounter.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.Reports.Clover 2 | { 3 | public class CloverCounter 4 | { 5 | public int Statements { get; set; } 6 | 7 | public int CoveredStatements { get; set; } 8 | 9 | public int Conditionals { get; set; } 10 | 11 | public int CoveredConditionals { get; set; } 12 | 13 | public int Methods { get; set; } 14 | 15 | public int CoveredMethods { get; set; } 16 | 17 | public int Lines { get; set; } 18 | 19 | public int Classes { get; set; } 20 | 21 | public int Files { get; set; } 22 | 23 | public int Packages { get; set; } 24 | 25 | public int Elements => Statements + Conditionals + Methods; 26 | 27 | public int CoveredElements => CoveredStatements + CoveredConditionals + CoveredMethods; 28 | 29 | public void Add(CloverCounter counter) 30 | { 31 | Statements += counter.Statements; 32 | CoveredStatements += counter.CoveredStatements; 33 | Conditionals += counter.Conditionals; 34 | CoveredConditionals += counter.CoveredConditionals; 35 | Methods += counter.Methods; 36 | CoveredMethods += counter.CoveredMethods; 37 | Lines += counter.Lines; 38 | Classes += counter.Classes; 39 | Files += counter.Files; 40 | Packages += counter.Packages; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Clover/ICloverReport.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Reports.Clover 5 | { 6 | public interface ICloverReport 7 | { 8 | void Execute(InstrumentationResult result, IFileInfo output); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Cobertura/ICoberturaReport.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Reports.Cobertura 5 | { 6 | public interface ICoberturaReport 7 | { 8 | void Execute(InstrumentationResult result, IFileInfo output); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Console/IConsoleReport.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Model; 2 | 3 | namespace MiniCover.Reports.Console 4 | { 5 | public interface IConsoleReport 6 | { 7 | int Execute(InstrumentationResult result, float threshold, bool noFail); 8 | } 9 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Coveralls/ICoverallsReport.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Reports.Coveralls 5 | { 6 | public interface ICoverallsReport 7 | { 8 | Task Execute(InstrumentationResult result, 9 | string output, 10 | string repoToken, 11 | string serviceJobId, 12 | string serviceName, 13 | string commitMessage, 14 | string rootfolder, 15 | string commit, 16 | string commitAuthorName, 17 | string commitAuthorEmail, 18 | string commitCommitterName, 19 | string commitCommitterEmail, 20 | string branch, 21 | string remoteName, 22 | string remoteUrl); 23 | } 24 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Coveralls/Models/CoverallsCommitModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace MiniCover.Reports.Coveralls.Models 4 | { 5 | public class CoverallsCommitModel 6 | { 7 | [JsonProperty("id")] 8 | public string Id { get; set; } 9 | 10 | [JsonProperty("author_name")] 11 | public string AuthorName { get; set; } 12 | 13 | [JsonProperty("author_email")] 14 | public string AuthorEmail { get; set; } 15 | 16 | [JsonProperty("committer_name")] 17 | public string CommitterName { get; set; } 18 | 19 | [JsonProperty("committer_email")] 20 | public string CommitterEmail { get; set; } 21 | 22 | [JsonProperty("message")] 23 | public string Message { get; set; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Coveralls/Models/CoverallsGitModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace MiniCover.Reports.Coveralls.Models 5 | { 6 | public class CoverallsGitModel 7 | { 8 | [JsonProperty("head")] 9 | public CoverallsCommitModel Head { get; set; } 10 | 11 | [JsonProperty("branch")] 12 | public string Branch { get; set; } 13 | 14 | [JsonProperty("remotes")] 15 | public List Remotes { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Coveralls/Models/CoverallsJobModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace MiniCover.Reports.Coveralls.Models 5 | { 6 | public class CoverallsJobModel 7 | { 8 | [JsonProperty("service_job_id")] 9 | public string ServiceJobId { get; set; } 10 | 11 | [JsonProperty("service_name")] 12 | public string ServiceName { get; set; } 13 | 14 | [JsonProperty("repo_token")] 15 | public string RepoToken { get; set; } 16 | 17 | [JsonProperty("git")] 18 | public CoverallsGitModel CoverallsGitModel { get; set; } 19 | 20 | [JsonProperty("source_files")] 21 | public List SourceFiles { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Coveralls/Models/CoverallsRemoteModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace MiniCover.Reports.Coveralls.Models 4 | { 5 | public class CoverallsRemote 6 | { 7 | [JsonProperty("name")] 8 | public string Name { get; set; } 9 | 10 | [JsonProperty("url")] 11 | public string Url { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/Coveralls/Models/CoverallsSourceFileModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace MiniCover.Reports.Coveralls.Models 4 | { 5 | public class CoverallsSourceFileModel 6 | { 7 | [JsonProperty("name")] 8 | public string Name { get; set; } 9 | 10 | [JsonProperty("source_digest")] 11 | public string SourceDigest { get; set; } 12 | 13 | [JsonProperty("coverage")] 14 | public int?[] Coverage { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Helpers/ConsoleBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MiniCover.Reports.Helpers 5 | { 6 | public static class ConsoleBox 7 | { 8 | private static readonly Dictionary _boxCharacters = new Dictionary 9 | { 10 | [BoxPart.All] = '┼', 11 | [BoxPart.Bottom | BoxPart.Right] = '┌', 12 | [BoxPart.Bottom | BoxPart.Left] = '┐', 13 | [BoxPart.Top | BoxPart.Right] = '└', 14 | [BoxPart.Top | BoxPart.Left] = '┘', 15 | [BoxPart.Left | BoxPart.Right] = '─', 16 | [BoxPart.Top | BoxPart.Bottom] = '│', 17 | [BoxPart.Top | BoxPart.Left | BoxPart.Right] = '┴', 18 | [BoxPart.Bottom | BoxPart.Left | BoxPart.Right] = '┬', 19 | [BoxPart.Left | BoxPart.Top | BoxPart.Bottom] = '┤', 20 | [BoxPart.Right | BoxPart.Top | BoxPart.Bottom] = '├' 21 | }; 22 | 23 | public static char ToChar(this BoxPart parts) 24 | { 25 | return _boxCharacters[parts]; 26 | } 27 | } 28 | 29 | [Flags] 30 | public enum BoxPart 31 | { 32 | None = 0, 33 | Top = 1, 34 | Right = 2, 35 | Bottom = 4, 36 | Left = 8, 37 | All = Top | Right | Bottom | Left, 38 | Vertical = Top | Bottom, 39 | Horizontal = Left | Right 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/Helpers/ISummaryFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MiniCover.Core.Hits; 3 | using MiniCover.Core.Model; 4 | 5 | namespace MiniCover.Reports.Helpers 6 | { 7 | public interface ISummaryFactory 8 | { 9 | Summary CalculateSummary(InstrumentationResult result, float threshold); 10 | 11 | Summary CalculateFilesSummary(IEnumerable sourceFiles, HitsInfo hitsInfo, float threshold); 12 | 13 | List GetSummaryGrid(SourceFile[] sourceFiles, HitsInfo hitsInfo, float threshold); 14 | } 15 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Helpers/Summary.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.Reports.Helpers 2 | { 3 | 4 | public class Summary 5 | { 6 | public int Statements { get; set; } 7 | public int CoveredStatements { get; set; } 8 | public float StatementsPercentage { get; set; } 9 | public bool StatementsCoveragePass { get; set; } 10 | public int Lines { get; set; } 11 | public int CoveredLines { get; set; } 12 | public float LinesPercentage { get; set; } 13 | public bool LinesCoveragePass { get; set; } 14 | public int Branches { get; set; } 15 | public int CoveredBranches { get; set; } 16 | public float BranchesPercentage { get; set; } 17 | public bool BranchesCoveragePass { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/Helpers/SummaryRow.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Model; 2 | 3 | namespace MiniCover.Reports.Helpers 4 | { 5 | public class SummaryRow 6 | { 7 | public int Level { get; set; } 8 | public string Name { get; set; } 9 | public string FullName { get; set; } 10 | public bool Root { get; set; } 11 | public bool Folder { get; set; } 12 | public bool File { get; set; } 13 | public SourceFile[] SourceFiles { get; set; } 14 | public Summary Summary { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/Html/IHtmlReport.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Reports.Html 5 | { 6 | public interface IHtmlReport 7 | { 8 | int Execute(InstrumentationResult result, IDirectoryInfo output, float threshold, bool noFail); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Html/IHtmlSourceFileReport.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Hits; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Reports.Html 5 | { 6 | public interface IHtmlSourceFileReport 7 | { 8 | void Generate(InstrumentationResult result, SourceFile sourceFile, HitsInfo hitsInfo, float threshold, string outputFile); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Html/Shared.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; 7 | font-size: 14px; 8 | color: #24292e; 9 | } 10 | 11 | table { 12 | border-collapse: collapse; 13 | font-size: 14px; 14 | } 15 | 16 | th { 17 | font-weight: 600; 18 | background-color: #fafafa; 19 | } 20 | 21 | th, td { 22 | text-align: left; 23 | padding: 5px; 24 | border: 1px solid #aaa; 25 | } 26 | 27 | tr.total { 28 | font-weight: bold; 29 | } 30 | 31 | th.value { 32 | text-align: center; 33 | } 34 | 35 | td.value { 36 | text-align: right; 37 | } 38 | 39 | .green { 40 | background-color: #e8f5e9; 41 | } 42 | 43 | .red { 44 | background-color: #ffebee; 45 | } 46 | 47 | h2 { 48 | font-weight: 600; 49 | } 50 | 51 | a, a:visited, a:active { 52 | color: #0078D4; 53 | text-decoration: none; 54 | } 55 | 56 | a:hover { 57 | text-decoration: underline; 58 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Html/Shared.js: -------------------------------------------------------------------------------- 1 | function hoverTargetHandler(e) { 2 | if (e.target.dataset.hoverTarget) { 3 | let elements = document.querySelectorAll(e.target.dataset.hoverTarget); 4 | if (e.type == 'mouseover') 5 | elements.forEach(x => x.classList.add('hover')); 6 | else if (e.type == 'mouseout') 7 | elements.forEach(x => x.classList.remove('hover')); 8 | } 9 | } 10 | document.addEventListener('mouseover', hoverTargetHandler); 11 | document.addEventListener('mouseout', hoverTargetHandler); 12 | 13 | function activateTargetHandler(e) { 14 | document.querySelectorAll('.active') 15 | .forEach(x => x.classList.remove('active')); 16 | 17 | if (e.target.dataset.activateTarget) { 18 | let elements = document.querySelectorAll(e.target.dataset.activateTarget); 19 | elements.forEach(x => x.classList.add('active')); 20 | } 21 | } 22 | document.addEventListener('click', activateTargetHandler); 23 | 24 | function toggleTargetHandler(e) { 25 | if (e.target.dataset.toggleTarget) { 26 | let elements = document.querySelectorAll(e.target.dataset.toggleTarget); 27 | elements.forEach(x => { 28 | if (x.classList.contains(show)) { 29 | x.classList.remove('show'); 30 | x.classList.add('hide'); 31 | } else { 32 | x.classList.remove('hide'); 33 | x.classList.add('show'); 34 | } 35 | }); 36 | } 37 | } 38 | document.addEventListener('click', toggleTargetHandler); 39 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/Html/SourceFile.css: -------------------------------------------------------------------------------- 1 | .legend { 2 | margin-bottom: 10px; 3 | } 4 | 5 | .legend label { 6 | vertical-align: middle; 7 | } 8 | 9 | .legend div { 10 | margin-left: 10px; 11 | padding: 0 4px; 12 | display: inline-block; 13 | vertical-align: middle; 14 | } 15 | 16 | .legend div.hit { 17 | background-color: #c8e6c9; 18 | } 19 | 20 | .legend div.not-hit { 21 | background-color: #ffcdd2; 22 | } 23 | 24 | .legend div.partial { 25 | background-color: #ffecb3; 26 | } 27 | 28 | .legend div, .code { 29 | font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace; 30 | font-size: 12px; 31 | line-height: 20px; 32 | } 33 | 34 | .code { 35 | border: 1px solid #ccc; 36 | background-color: #fafafa; 37 | } 38 | 39 | .line { 40 | position: relative; 41 | white-space: pre; 42 | padding-left: 40px; 43 | } 44 | 45 | .line-number { 46 | position: absolute; 47 | top: 0; 48 | left: 0; 49 | bottom: 0; 50 | width: 40px; 51 | font-size: 10px; 52 | text-overflow: ellipsis; 53 | text-align: right; 54 | background: #fff; 55 | padding-right: 5px; 56 | color: #555; 57 | } 58 | 59 | .hit-count { 60 | display: inline-block; 61 | width: 30px; 62 | font-size: 10px; 63 | } 64 | 65 | .line-content { 66 | padding-left: 5px; 67 | } 68 | 69 | .line.hit { 70 | background-color: #e8f5e9; 71 | } 72 | 73 | .line.partial { 74 | background-color: #fff8e1; 75 | } 76 | 77 | .line.not-hit { 78 | background-color: #ffebee; 79 | } 80 | 81 | 82 | .statement { 83 | position: relative; 84 | display: inline-block; 85 | color: #000; 86 | cursor: pointer; 87 | } 88 | 89 | .statement.hit { 90 | background-color: #c8e6c9; 91 | } 92 | 93 | .statement.partial { 94 | background-color: #ffecb3; 95 | } 96 | 97 | .statement.not-hit { 98 | background-color: #ffcdd2; 99 | } 100 | 101 | .statement.hit.hover, .statement.hit.active { 102 | background-color: #a5d6a7; 103 | } 104 | 105 | .statement.not-hit.hover, .statement.not-hit.active { 106 | background-color: #ef9a9a; 107 | } 108 | 109 | .statement.partial.hover, .statement.partial.active { 110 | background-color: #ffe082; 111 | } 112 | 113 | .statement-info { 114 | display: none; 115 | position: absolute; 116 | background: #fff; 117 | box-shadow: 0 3px 4px 0 rgba(0,0,0,0.14), 0 3px 3px -2px rgba(0,0,0,0.12), 0 1px 8px 0 rgba(0,0,0,0.20); 118 | z-index: 1; 119 | top: 100%; 120 | left: 0; 121 | padding: 15px; 122 | cursor: initial; 123 | } 124 | 125 | .statement-info ul { 126 | margin: 0; 127 | } 128 | 129 | .statement-info.active { 130 | display: block; 131 | } 132 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/Html/Summary.css: -------------------------------------------------------------------------------- 1 | tr.root { 2 | font-weight: bold; 3 | } 4 | 5 | tr.folder .name::before, tr.file .name::before { 6 | content: ' '; 7 | width: 16px; 8 | height: 14px; 9 | display: inline-block; 10 | background-repeat: no-repeat; 11 | background-position: left center; 12 | background-size: contain; 13 | margin-right: 5px; 14 | margin-bottom: 3px; 15 | vertical-align: middle; 16 | } 17 | 18 | tr.folder .name::before { 19 | background-image: url('data:image/svg+xml;utf8,'); 20 | } 21 | 22 | tr.file .name::before { 23 | background-image: url('data:image/svg+xml;utf8,'); 24 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/MiniCover.Reports.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0;net8.0 5 | 6 | 7 | 8 | Lucas Lorentz 9 | Minicover reports implementation 10 | https://github.com/lucaslorentz/minicover 11 | coverage,cover,minicover 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/MiniCoverReportsServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using Microsoft.Extensions.DependencyInjection.Extensions; 3 | using MiniCover.Reports.Clover; 4 | using MiniCover.Reports.Cobertura; 5 | using MiniCover.Reports.Console; 6 | using MiniCover.Reports.Coveralls; 7 | using MiniCover.Reports.Helpers; 8 | using MiniCover.Reports.Html; 9 | using MiniCover.Reports.NCover; 10 | using MiniCover.Reports.OpenCover; 11 | 12 | namespace Microsoft.Extensions.DependencyInjection 13 | { 14 | public static class ServiceCollectionExtensions 15 | { 16 | public static IServiceCollection AddMiniCoverReports(this IServiceCollection services) 17 | { 18 | services.TryAddSingleton(); 19 | 20 | services.TryAddSingleton(); 21 | 22 | services.TryAddSingleton(); 23 | services.TryAddSingleton(); 24 | services.TryAddSingleton(); 25 | services.TryAddSingleton(); 26 | services.TryAddSingleton(); 27 | services.TryAddSingleton(); 28 | services.TryAddSingleton(); 29 | services.TryAddSingleton(); 30 | 31 | return services; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/MiniCover.Reports/NCover/INCoverReport.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Reports.NCover 5 | { 6 | public interface INCoverReport 7 | { 8 | void Execute(InstrumentationResult result, IFileInfo output); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/OpenCover/IOpenCoverReport.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using MiniCover.Core.Model; 3 | 4 | namespace MiniCover.Reports.OpenCover 5 | { 6 | public interface IOpenCoverReport 7 | { 8 | void Execute(InstrumentationResult result, IFileInfo output); 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniCover.Reports/Utils/ResourceUtils.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace MiniCover.Reports.Utils 4 | { 5 | public static class ResourceUtils 6 | { 7 | public static string GetContent(string resourceName) 8 | { 9 | var assembly = typeof(ResourceUtils).Assembly; 10 | 11 | using (var stream = assembly.GetManifestResourceStream(resourceName)) 12 | using (var reader = new StreamReader(stream)) 13 | { 14 | return reader.ReadToEnd(); 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Commands/CloverReportCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MiniCover.CommandLine.Options; 3 | using MiniCover.Reports.Clover; 4 | 5 | namespace MiniCover.CommandLine.Commands 6 | { 7 | public class CloverReportCommand : ICommand 8 | { 9 | private readonly IWorkingDirectoryOption _workingDirectoryOption; 10 | private readonly ICloverOutputOption _cloverOutputOption; 11 | private readonly ICoverageLoadedFileOption _coverageLoadedFileOption; 12 | private readonly ICloverReport _cloverReport; 13 | 14 | public CloverReportCommand( 15 | IWorkingDirectoryOption workingDirectoryOption, 16 | ICoverageLoadedFileOption coverageLoadedFileOption, 17 | ICloverOutputOption cloverOutputOption, 18 | ICloverReport cloverReport) 19 | { 20 | _workingDirectoryOption = workingDirectoryOption; 21 | _coverageLoadedFileOption = coverageLoadedFileOption; 22 | _cloverOutputOption = cloverOutputOption; 23 | _cloverReport = cloverReport; 24 | } 25 | 26 | public string CommandName => "cloverreport"; 27 | public string CommandDescription => "Write an Clover-formatted XML report to file"; 28 | 29 | public IOption[] Options => new IOption[] 30 | { 31 | _workingDirectoryOption, 32 | _coverageLoadedFileOption, 33 | _cloverOutputOption 34 | }; 35 | 36 | public Task Execute() 37 | { 38 | _cloverReport.Execute(_coverageLoadedFileOption.Result, _cloverOutputOption.FileInfo); 39 | return Task.FromResult(0); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Commands/CoberturaReportCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MiniCover.CommandLine.Options; 3 | using MiniCover.Reports.Cobertura; 4 | 5 | namespace MiniCover.CommandLine.Commands 6 | { 7 | public class CoberturaReportCommand : ICommand 8 | { 9 | private readonly IWorkingDirectoryOption _workingDirectoryOption; 10 | private readonly ICoverageLoadedFileOption _coverageLoadedFileOption; 11 | private readonly ICoberturaOutputOption _coberturaOutputOption; 12 | private readonly ICoberturaReport _coberturaReport; 13 | 14 | public CoberturaReportCommand( 15 | IWorkingDirectoryOption workingDirectoryOption, 16 | ICoverageLoadedFileOption coverageLoadedFileOption, 17 | ICoberturaOutputOption coberturaOutputOption, 18 | ICoberturaReport coberturaReport) 19 | { 20 | _workingDirectoryOption = workingDirectoryOption; 21 | _coverageLoadedFileOption = coverageLoadedFileOption; 22 | _coberturaOutputOption = coberturaOutputOption; 23 | _coberturaReport = coberturaReport; 24 | } 25 | 26 | public string CommandName => "coberturareport"; 27 | public string CommandDescription => "Write a Cobertura-formatted XML report to file"; 28 | public IOption[] Options => new IOption[] 29 | { 30 | _workingDirectoryOption, 31 | _coverageLoadedFileOption, 32 | _coberturaOutputOption 33 | }; 34 | 35 | public Task Execute() 36 | { 37 | _coberturaReport.Execute(_coverageLoadedFileOption.Result, _coberturaOutputOption.FileInfo); 38 | return Task.FromResult(0); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Commands/ConsoleReportCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MiniCover.CommandLine.Options; 3 | using MiniCover.Reports.Console; 4 | 5 | namespace MiniCover.CommandLine.Commands 6 | { 7 | public class ConsoleReportCommand : ICommand 8 | { 9 | private readonly IWorkingDirectoryOption _workingDirectoryOption; 10 | private readonly ICoverageLoadedFileOption _coverageLoadedFileOption; 11 | private readonly IThresholdOption _thresholdOption; 12 | private readonly INoFailOption _noFailOption; 13 | private readonly IConsoleReport _consoleReport; 14 | 15 | public ConsoleReportCommand( 16 | IWorkingDirectoryOption workingDirectoryOption, 17 | ICoverageLoadedFileOption coverageLoadedFileOption, 18 | IThresholdOption thresholdOption, 19 | INoFailOption noFailOption, 20 | IConsoleReport consoleReport) 21 | { 22 | _workingDirectoryOption = workingDirectoryOption; 23 | _coverageLoadedFileOption = coverageLoadedFileOption; 24 | _thresholdOption = thresholdOption; 25 | _noFailOption = noFailOption; 26 | _consoleReport = consoleReport; 27 | } 28 | 29 | public string CommandName => "report"; 30 | public string CommandDescription => "Outputs coverage report"; 31 | public IOption[] Options => new IOption[] 32 | { 33 | _workingDirectoryOption, 34 | _coverageLoadedFileOption, 35 | _thresholdOption, 36 | _noFailOption 37 | }; 38 | 39 | public Task Execute() 40 | { 41 | var result = _consoleReport.Execute( 42 | _coverageLoadedFileOption.Result, 43 | _thresholdOption.Value, 44 | _noFailOption.Value); 45 | 46 | return Task.FromResult(result); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Commands/HtmlReportCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MiniCover.CommandLine.Options; 3 | using MiniCover.Reports.Html; 4 | 5 | namespace MiniCover.CommandLine.Commands 6 | { 7 | public class HtmlReportCommand : ICommand 8 | { 9 | private readonly IWorkingDirectoryOption _workingDirectoryOption; 10 | private readonly ICoverageLoadedFileOption _coverageLoadedFileOption; 11 | private readonly IHtmlOutputDirectoryOption _htmlOutputFolderOption; 12 | private readonly IThresholdOption _thresholdOption; 13 | private readonly INoFailOption _noFailOption; 14 | private readonly IHtmlReport _htmlReport; 15 | 16 | public HtmlReportCommand( 17 | IWorkingDirectoryOption workingDirectoryOption, 18 | ICoverageLoadedFileOption coverageLoadedFileOption, 19 | IHtmlOutputDirectoryOption htmlOutputFolderOption, 20 | IThresholdOption thresholdOption, 21 | INoFailOption noFailOption, 22 | IHtmlReport htmlReport) 23 | { 24 | _workingDirectoryOption = workingDirectoryOption; 25 | _coverageLoadedFileOption = coverageLoadedFileOption; 26 | _thresholdOption = thresholdOption; 27 | _noFailOption = noFailOption; 28 | _htmlReport = htmlReport; 29 | _htmlOutputFolderOption = htmlOutputFolderOption; 30 | } 31 | 32 | public string CommandName => "htmlreport"; 33 | public string CommandDescription => "Write html report to folder"; 34 | 35 | public IOption[] Options => new IOption[] 36 | { 37 | _workingDirectoryOption, 38 | _coverageLoadedFileOption, 39 | _thresholdOption, 40 | _noFailOption, 41 | _htmlOutputFolderOption 42 | }; 43 | 44 | public Task Execute() 45 | { 46 | var result = _htmlReport.Execute( 47 | _coverageLoadedFileOption.Result, 48 | _htmlOutputFolderOption.DirectoryInfo, 49 | _thresholdOption.Value, 50 | _noFailOption.Value); 51 | 52 | return Task.FromResult(result); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Commands/NCoverReportCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MiniCover.CommandLine.Options; 3 | using MiniCover.Reports.NCover; 4 | 5 | namespace MiniCover.CommandLine.Commands 6 | { 7 | public class NCoverReportCommand : ICommand 8 | { 9 | private readonly IWorkingDirectoryOption _workingDirectoryOption; 10 | private readonly ICoverageLoadedFileOption _coverageLoadedFileOption; 11 | private readonly INCoverOutputOption _nCoverOutputOption; 12 | private readonly INCoverReport _nCoverReport; 13 | 14 | public NCoverReportCommand( 15 | IWorkingDirectoryOption workingDirectoryOption, 16 | ICoverageLoadedFileOption coverageLoadedFileOption, 17 | INCoverOutputOption nCoverOutputOption, 18 | INCoverReport nCoverReport) 19 | { 20 | _workingDirectoryOption = workingDirectoryOption; 21 | _coverageLoadedFileOption = coverageLoadedFileOption; 22 | _nCoverOutputOption = nCoverOutputOption; 23 | _nCoverReport = nCoverReport; 24 | } 25 | 26 | public string CommandName => "xmlreport"; 27 | public string CommandDescription => "Write an NCover-formatted XML report to file"; 28 | 29 | public IOption[] Options => new IOption[] 30 | { 31 | _workingDirectoryOption, 32 | _coverageLoadedFileOption, 33 | _nCoverOutputOption 34 | }; 35 | 36 | public Task Execute() 37 | { 38 | _nCoverReport.Execute(_coverageLoadedFileOption.Result, _nCoverOutputOption.FileInfo); 39 | return Task.FromResult(0); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Commands/OpenCoverReportCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MiniCover.CommandLine.Options; 3 | using MiniCover.Reports.OpenCover; 4 | 5 | namespace MiniCover.CommandLine.Commands 6 | { 7 | public class OpenCoverReportCommand : ICommand 8 | { 9 | private readonly IWorkingDirectoryOption _workingDirectoryOption; 10 | private readonly ICoverageLoadedFileOption _coverageLoadedFileOption; 11 | private readonly IOpenCoverOutputOption _openCoverOutputOption; 12 | private readonly IOpenCoverReport _openCoverReport; 13 | 14 | public OpenCoverReportCommand( 15 | IWorkingDirectoryOption workingDirectoryOption, 16 | ICoverageLoadedFileOption coverageLoadedFileOption, 17 | IOpenCoverOutputOption openCoverOutputOption, 18 | IOpenCoverReport openCoverReport) 19 | { 20 | _workingDirectoryOption = workingDirectoryOption; 21 | _coverageLoadedFileOption = coverageLoadedFileOption; 22 | _openCoverOutputOption = openCoverOutputOption; 23 | _openCoverReport = openCoverReport; 24 | } 25 | 26 | public string CommandName => "opencoverreport"; 27 | public string CommandDescription => "Write an OpenCover-formatted XML report to file"; 28 | public IOption[] Options => new IOption[] 29 | { 30 | _workingDirectoryOption, 31 | _coverageLoadedFileOption, 32 | _openCoverOutputOption 33 | }; 34 | 35 | public Task Execute() 36 | { 37 | _openCoverReport.Execute(_coverageLoadedFileOption.Result, _openCoverOutputOption.FileInfo); 38 | return Task.FromResult(0); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Commands/ResetCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO.Abstractions; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.Logging; 6 | using MiniCover.CommandLine; 7 | using MiniCover.CommandLine.Options; 8 | using MiniCover.Core.Hits; 9 | using MiniCover.IO; 10 | 11 | namespace MiniCover.Commands 12 | { 13 | public class ResetCommand : ICommand 14 | { 15 | private readonly HitsDirectoryOption _hitsDirectoryOption; 16 | 17 | private readonly IHitsResetter _hitResetService; 18 | private readonly VerbosityOption _verbosityOption; 19 | private readonly WorkingDirectoryOption _workingDirectoryOption; 20 | 21 | public ResetCommand( 22 | IHitsResetter hitResetService, 23 | VerbosityOption verbosityOption, 24 | WorkingDirectoryOption workingDirectoryOption, 25 | HitsDirectoryOption hitsDirectoryOption) 26 | { 27 | _hitResetService = hitResetService; 28 | _verbosityOption = verbosityOption; 29 | _workingDirectoryOption = workingDirectoryOption; 30 | _hitsDirectoryOption = hitsDirectoryOption; 31 | } 32 | 33 | public string CommandName => "reset"; 34 | public string CommandDescription => "Reset hits count"; 35 | 36 | public IOption[] Options => new IOption[] 37 | { 38 | _verbosityOption, 39 | _workingDirectoryOption, 40 | _hitsDirectoryOption 41 | }; 42 | 43 | public Task Execute() 44 | { 45 | var hitsDirectory = _hitsDirectoryOption.DirectoryInfo; 46 | 47 | if (!_hitResetService.ResetHits(hitsDirectory)) 48 | return Task.FromResult(1); 49 | 50 | return Task.FromResult(0); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Commands/UninstrumentCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using MiniCover.CommandLine; 3 | using MiniCover.CommandLine.Options; 4 | using MiniCover.Core.Instrumentation; 5 | 6 | namespace MiniCover.Commands 7 | { 8 | public class UninstrumentCommand : ICommand 9 | { 10 | private readonly IVerbosityOption _verbosityOption; 11 | private readonly IWorkingDirectoryOption _workingDirectoryOption; 12 | private readonly ICoverageLoadedFileOption _coverageLoadedFileOption; 13 | private readonly IUninstrumenter _uninstrumenter; 14 | 15 | public UninstrumentCommand( 16 | IVerbosityOption verbosityOption, 17 | IWorkingDirectoryOption workingDirectoryOption, 18 | ICoverageLoadedFileOption coverageLoadedFileOption, 19 | IUninstrumenter uninstrumenter) 20 | { 21 | _verbosityOption = verbosityOption; 22 | _workingDirectoryOption = workingDirectoryOption; 23 | _coverageLoadedFileOption = coverageLoadedFileOption; 24 | _uninstrumenter = uninstrumenter; 25 | } 26 | 27 | public string CommandName => "uninstrument"; 28 | public string CommandDescription => "Uninstrument assemblies"; 29 | public IOption[] Options => new IOption[] 30 | { 31 | _verbosityOption, 32 | _workingDirectoryOption, 33 | _coverageLoadedFileOption 34 | }; 35 | 36 | public Task Execute() 37 | { 38 | var result = _coverageLoadedFileOption.Result; 39 | _uninstrumenter.Uninstrument(result); 40 | return Task.FromResult(0); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/DirectoryOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public abstract class DirectoryOption : ISingleValueOption, IDirectoryOption 6 | { 7 | private readonly IFileSystem _fileSystem; 8 | 9 | protected DirectoryOption(IFileSystem fileSystem) 10 | { 11 | _fileSystem = fileSystem; 12 | } 13 | 14 | public IDirectoryInfo DirectoryInfo { get; private set; } 15 | public abstract string Name { get; } 16 | public string ShortName => null; 17 | public abstract string Description { get; } 18 | protected abstract string DefaultValue { get; } 19 | 20 | public virtual void ReceiveValue(string value) 21 | { 22 | DirectoryInfo = _fileSystem.DirectoryInfo.New(value ?? DefaultValue); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/FileOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public abstract class FileOption : ISingleValueOption, IFileOption 6 | { 7 | private readonly IFileSystem _fileSystem; 8 | 9 | protected FileOption(IFileSystem fileSystem) 10 | { 11 | _fileSystem = fileSystem; 12 | } 13 | 14 | public IFileInfo FileInfo { get; private set; } 15 | public abstract string Name { get; } 16 | public virtual string ShortName => null; 17 | public abstract string Description { get; } 18 | protected abstract string DefaultValue { get; } 19 | 20 | public virtual void ReceiveValue(string value) 21 | { 22 | FileInfo = _fileSystem.FileInfo.New(value ?? DefaultValue); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/FilesPatternOption.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace MiniCover.CommandLine.Options 5 | { 6 | public abstract class FilesPatternOption : IMultiValueOption 7 | { 8 | public IList Value { get; private set; } 9 | public abstract string Name { get; } 10 | public virtual string ShortName => null; 11 | public abstract string Description { get; } 12 | protected abstract IList DefaultValue { get; } 13 | 14 | public void ReceiveValue(IList values) 15 | { 16 | if (values == null || values.Count == 0) 17 | { 18 | Value = DefaultValue; 19 | return; 20 | } 21 | 22 | Value = values.Where(v => !string.IsNullOrEmpty(v)).ToArray(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/ICommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace MiniCover.CommandLine 4 | { 5 | public interface ICommand 6 | { 7 | string CommandName { get; } 8 | string CommandDescription { get; } 9 | IOption[] Options { get; } 10 | Task Execute(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/IDirectoryOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public interface IDirectoryOption : IOption 6 | { 7 | IDirectoryInfo DirectoryInfo { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/IFileOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public interface IFileOption : IOption 6 | { 7 | IFileInfo FileInfo { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/IOption.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniCover.CommandLine 4 | { 5 | public interface IOption 6 | { 7 | string Name { get; } 8 | string ShortName => null; 9 | string Description { get; } 10 | } 11 | 12 | public interface INoValueOption : IOption 13 | { 14 | void ReceiveValue(bool value); 15 | } 16 | 17 | public interface ISingleValueOption : IOption 18 | { 19 | void ReceiveValue(string value); 20 | } 21 | 22 | public interface IMultiValueOption : IOption 23 | { 24 | void ReceiveValue(IList values); 25 | } 26 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/CloverOutputOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class CloverOutputOption : FileOption, ICloverOutputOption 6 | { 7 | public CloverOutputOption(IFileSystem fileSystem) 8 | : base(fileSystem) 9 | { 10 | } 11 | 12 | public override string Name => "--output"; 13 | public override string Description => $"Output file for Clover report [default: {DefaultValue}]"; 14 | protected override string DefaultValue => "./clover.xml"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/CoberturaOutputOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class CoberturaOutputOption : FileOption, ICoberturaOutputOption 6 | { 7 | public CoberturaOutputOption(IFileSystem fileSystem) 8 | : base(fileSystem) 9 | { 10 | } 11 | 12 | public override string Name => "--output"; 13 | public override string Description => $"Output file for cobertura report [default: {DefaultValue}]"; 14 | protected override string DefaultValue => "./cobertura.xml"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/CoverageFileOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class CoverageFileOption : FileOption 6 | { 7 | public CoverageFileOption(IFileSystem fileSystem) 8 | : base(fileSystem) 9 | { 10 | } 11 | 12 | public override string Name => "--coverage-file"; 13 | public override string Description => $"Coverage file name [default: {DefaultValue}]"; 14 | protected override string DefaultValue => "./coverage.json"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/CoverageLoadedFileOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using MiniCover.Core.Model; 3 | using MiniCover.Exceptions; 4 | using Newtonsoft.Json; 5 | 6 | namespace MiniCover.CommandLine.Options 7 | { 8 | public class CoverageLoadedFileOption : CoverageFileOption, ICoverageLoadedFileOption 9 | { 10 | private readonly IFileSystem _fileSystem; 11 | 12 | public CoverageLoadedFileOption(IFileSystem fileSystem) 13 | : base(fileSystem) 14 | { 15 | _fileSystem = fileSystem; 16 | } 17 | 18 | public InstrumentationResult Result { get; private set; } 19 | 20 | public override void ReceiveValue(string value) 21 | { 22 | base.ReceiveValue(value); 23 | 24 | if (!FileInfo.Exists) 25 | throw new ValidationException($"Coverage file does not exist '{FileInfo.FullName}'"); 26 | 27 | var coverageFileString = _fileSystem.File.ReadAllText(FileInfo.FullName); 28 | Result = JsonConvert.DeserializeObject(coverageFileString); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/ExcludeAssembliesPatternOption.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class ExcludeAssembliesPatternOption : FilesPatternOption 6 | { 7 | public override string Name => "--exclude-assemblies"; 8 | public override string Description => $"Pattern to exclude assemblies [default: {string.Join(" ", DefaultValue)}]"; 9 | protected override IList DefaultValue => new string[] { "**/obj/**/*.dll" }; 10 | } 11 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/ExcludeSourcesPatternOption.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class ExcludeSourcesPatternOption : FilesPatternOption 6 | { 7 | public override string Name => "--exclude-sources"; 8 | public override string Description => $"Pattern to exclude source files [default: {string.Join(" ", DefaultValue)}]"; 9 | protected override IList DefaultValue => new string[] { "**/bin/**/*.cs", "**/obj/**/*.cs" }; 10 | } 11 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/ExcludeTestsPatternOption.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class ExcludeTestsPatternOption : FilesPatternOption 6 | { 7 | public override string Name => "--exclude-tests"; 8 | public override string Description => $"Pattern to exclude source files [default: {string.Join(" ", DefaultValue)}]"; 9 | protected override IList DefaultValue => new string[] { "**/bin/**/*.cs", "**/obj/**/*.cs" }; 10 | } 11 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/HitsDirectoryOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class HitsDirectoryOption : DirectoryOption 6 | { 7 | public HitsDirectoryOption(IFileSystem fileSystem) 8 | : base(fileSystem) 9 | { 10 | } 11 | 12 | public override string Name => "--hits-directory"; 13 | public override string Description => $"Directory to store hits files [default: {DefaultValue}]"; 14 | protected override string DefaultValue => "./coverage-hits"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/HtmlOutputDirectoryOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class HtmlOutputDirectoryOption : DirectoryOption, IHtmlOutputDirectoryOption 6 | { 7 | public HtmlOutputDirectoryOption(IFileSystem fileSystem) 8 | : base(fileSystem) 9 | { 10 | } 11 | 12 | public override string Name => "--output"; 13 | public override string Description => $"Output folder for html report [default: {DefaultValue}]"; 14 | protected override string DefaultValue => "./coverage-html"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/ICloverOutputOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface ICloverOutputOption : IFileOption 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/ICoberturaOutputOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface ICoberturaOutputOption : IFileOption 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/ICoverageLoadedFileOption.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.Core.Model; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public interface ICoverageLoadedFileOption : IOption 6 | { 7 | InstrumentationResult Result { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/IHtmlOutputDirectoryOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface IHtmlOutputDirectoryOption : IDirectoryOption 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/INCoverOutputOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface INCoverOutputOption : IFileOption 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/INoFailOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface INoFailOption : IOption 4 | { 5 | bool Value { get; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/IOpenCoverOutputOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface IOpenCoverOutputOption : IFileOption 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/IThresholdOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface IThresholdOption : IOption 4 | { 5 | float Value { get; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/IVerbosityOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface IVerbosityOption : IOption 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/IWorkingDirectoryOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public interface IWorkingDirectoryOption : IOption 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/IncludeAssembliesPatternOption.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class IncludeAssembliesPatternOption : FilesPatternOption 6 | { 7 | public override string Name => "--assemblies"; 8 | public override string Description => $"Pattern to include assemblies [default: {string.Join(" ", DefaultValue)}]"; 9 | protected override IList DefaultValue => new string[] { "**/*.dll" }; 10 | } 11 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/IncludeSourcesPatternOption.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class IncludeSourcesPatternOption : FilesPatternOption 6 | { 7 | public override string Name => "--sources"; 8 | public override string Description => $"Pattern to include source files [default: {string.Join(" ", DefaultValue)}]"; 9 | protected override IList DefaultValue => new string[] { "src/**/*.cs" }; 10 | } 11 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/IncludeTestsPatternOption.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class IncludeTestsPatternOption : FilesPatternOption 6 | { 7 | public override string Name => "--tests"; 8 | public override string Description => $"Pattern to include test files [default: {string.Join(" ", DefaultValue)}]"; 9 | protected override IList DefaultValue => new string[] { "tests/**/*.cs", "test/**/*.cs" }; 10 | } 11 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/NCoverOutputOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class NCoverOutputOption : FileOption, INCoverOutputOption 6 | { 7 | public NCoverOutputOption(IFileSystem fileSystem) 8 | : base(fileSystem) 9 | { 10 | } 11 | 12 | public override string Name => "--output"; 13 | public override string Description => $"Output file for NCover report [default: {DefaultValue}]"; 14 | protected override string DefaultValue => "./coverage.xml"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/NoFailOption.cs: -------------------------------------------------------------------------------- 1 | namespace MiniCover.CommandLine.Options 2 | { 3 | public class NoFailOption : INoValueOption, INoFailOption 4 | { 5 | public bool Value { get; private set; } 6 | public string Name => "--no-fail"; 7 | public string Description => $"Do not fail this command if threshold is not met"; 8 | 9 | public void ReceiveValue(bool value) 10 | { 11 | Value = value; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/OpenCoverOutputOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class OpenCoverOutputOption : FileOption, IOpenCoverOutputOption 6 | { 7 | public OpenCoverOutputOption(IFileSystem fileSystem) 8 | : base(fileSystem) 9 | { 10 | } 11 | 12 | public override string Name => "--output"; 13 | public override string Description => $"Output file for OpenCover report [default: {DefaultValue}]"; 14 | protected override string DefaultValue => "./opencovercoverage.xml"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/ParentDirectoryOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class ParentDirectoryOption : DirectoryOption 6 | { 7 | public ParentDirectoryOption(IFileSystem fileSystem) 8 | : base(fileSystem) 9 | { 10 | } 11 | 12 | public override string Name => "--parentdir"; 13 | public override string Description => "Set parent directory for assemblies and source directories (if not used, falls back to --workdir)"; 14 | protected override string DefaultValue => "./"; 15 | } 16 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/ThresholdOption.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace MiniCover.CommandLine.Options 4 | { 5 | public class ThresholdOption : ISingleValueOption, IThresholdOption 6 | { 7 | private const float _defaultValue = 90; 8 | 9 | public float Value { get; private set; } 10 | public string Name => "--threshold"; 11 | public string Description => $"Coverage percentage threshold [default: {_defaultValue}]"; 12 | 13 | public void ReceiveValue(string value) 14 | { 15 | if (!float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var threshold)) 16 | { 17 | threshold = _defaultValue; 18 | } 19 | 20 | Value = threshold / 100; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/VerbosityOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | using MiniCover.Exceptions; 4 | using MiniCover.IO; 5 | 6 | namespace MiniCover.CommandLine.Options 7 | { 8 | public class VerbosityOption : ISingleValueOption, IVerbosityOption 9 | { 10 | private readonly IOutput _output; 11 | 12 | public VerbosityOption(IOutput output) 13 | { 14 | _output = output; 15 | } 16 | 17 | public string Name => "--verbosity"; 18 | public string ShortName => "-v"; 19 | public string Description => $"Change verbosity level ({GetPossibleValues()}) [default: {_output.MinimumLevel}]"; 20 | 21 | private static string GetPossibleValues() 22 | { 23 | return string.Join(", ", new[] { 24 | LogLevel.Trace, 25 | LogLevel.Debug, 26 | LogLevel.Information, 27 | LogLevel.Warning, 28 | LogLevel.Error, 29 | LogLevel.Critical 30 | }); 31 | } 32 | 33 | public void ReceiveValue(string value) 34 | { 35 | if (value != null) 36 | { 37 | if (!Enum.TryParse(value, true, out var logLevel)) 38 | throw new ValidationException($"Invalid verbosity '{value}'"); 39 | 40 | _output.MinimumLevel = logLevel; 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/Options/WorkingDirectoryOption.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace MiniCover.CommandLine.Options 5 | { 6 | public class WorkingDirectoryOption : DirectoryOption, IWorkingDirectoryOption 7 | { 8 | private readonly ILogger _logger; 9 | private readonly IFileSystem _fileSystem; 10 | 11 | public WorkingDirectoryOption( 12 | ILogger logger, 13 | IFileSystem fileSystem) 14 | : base(fileSystem) 15 | { 16 | _logger = logger; 17 | _fileSystem = fileSystem; 18 | } 19 | 20 | public override string Name => "--workdir"; 21 | public override string Description => $"Change working directory [default: {DefaultValue}]"; 22 | protected override string DefaultValue => "./"; 23 | 24 | public override void ReceiveValue(string value) 25 | { 26 | base.ReceiveValue(value); 27 | 28 | var currentDirectory = _fileSystem.DirectoryInfo.New(_fileSystem.Directory.GetCurrentDirectory()); 29 | if (DirectoryInfo.FullName != currentDirectory.FullName) 30 | { 31 | DirectoryInfo.Create(); 32 | _logger.LogInformation("Changing working directory to {directory}", DirectoryInfo.FullName); 33 | _fileSystem.Directory.SetCurrentDirectory(DirectoryInfo.FullName); 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/MiniCover/CommandLine/StringOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace MiniCover.CommandLine 3 | { 4 | public class StringOption : ISingleValueOption 5 | { 6 | public StringOption(string name, string description) 7 | { 8 | Name = name; 9 | Description = description; 10 | } 11 | 12 | public string Value { get; private set; } 13 | public string Name { get; } 14 | public string ShortName { get; set; } 15 | public string Description { get; } 16 | 17 | public void ReceiveValue(string value) 18 | { 19 | Value = value; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MiniCover/Exceptions/ValidationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace MiniCover.Exceptions 3 | { 4 | public class ValidationException : Exception 5 | { 6 | public ValidationException(string message) : base(message) 7 | { 8 | } 9 | 10 | public ValidationException(string message, Exception innerException) : base(message, innerException) 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/MiniCover/IO/ConsoleOutput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace MiniCover.IO 6 | { 7 | public class ConsoleOutput : IOutput 8 | { 9 | public LogLevel MinimumLevel { get; set; } = LogLevel.Information; 10 | 11 | public int Identation { get; set; } = 0; 12 | 13 | public void WriteLine( 14 | string message, 15 | LogLevel level) 16 | { 17 | if (level < MinimumLevel) return; 18 | 19 | var identation = new string(' ', Identation * 2); 20 | 21 | var identedMessage = identation + message.Replace(Environment.NewLine, Environment.NewLine + identation); 22 | 23 | var levelColor = GetLevelColor(level); 24 | 25 | if (levelColor != null) 26 | System.Console.ForegroundColor = levelColor.Value; 27 | 28 | GetLevelWriter(level).WriteLine(identedMessage); 29 | 30 | if (levelColor != null) 31 | System.Console.ResetColor(); 32 | } 33 | 34 | private ConsoleColor? GetLevelColor(LogLevel level) 35 | { 36 | switch (level) 37 | { 38 | case LogLevel.Critical: 39 | case LogLevel.Error: 40 | return ConsoleColor.Red; 41 | case LogLevel.Warning: 42 | return ConsoleColor.Yellow; 43 | case LogLevel.Information: 44 | return ConsoleColor.Blue; 45 | default: 46 | return null; 47 | } 48 | } 49 | 50 | private TextWriter GetLevelWriter(LogLevel level) 51 | { 52 | switch (level) 53 | { 54 | case LogLevel.Critical: 55 | case LogLevel.Error: 56 | return System.Console.Error; 57 | default: 58 | return System.Console.Out; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/MiniCover/IO/IOutput.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace MiniCover.IO 4 | { 5 | public interface IOutput 6 | { 7 | LogLevel MinimumLevel { get; set; } 8 | 9 | int Identation { get; set; } 10 | 11 | void WriteLine(string message, LogLevel level); 12 | } 13 | } -------------------------------------------------------------------------------- /src/MiniCover/IO/OutputLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace MiniCover.IO 4 | { 5 | public class OutputLoggerProvider : ILoggerProvider 6 | { 7 | private readonly IOutput _output; 8 | 9 | public OutputLoggerProvider(IOutput output) 10 | { 11 | _output = output; 12 | } 13 | 14 | public ILogger CreateLogger(string categoryName) 15 | { 16 | return new OutputLogger(categoryName, _output); 17 | } 18 | 19 | public void Dispose() 20 | { 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/MiniCover/MiniCover.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0;net8.0 6 | true 7 | minicover 8 | 9 | 10 | 11 | MiniCover 12 | MiniCover 13 | Lucas Lorentz 14 | MiniCover 15 | Cross platform code coverage tool for .NET Core 16 | https://github.com/lucaslorentz/minicover 17 | coverage,cover,minicover 18 | 19 | 20 | 21 | Project 22 | instrument --workdir ../../../../../ 23 | false 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/MiniCover/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "MiniCover": { 4 | "commandName": "Project", 5 | "commandLineArgs": "instrument --workdir ../../../../../sample --parentdir ../" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/ConstructorGenericCallThis.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using FluentAssertions; 4 | 5 | namespace MiniCover.Core.UnitTests.Instrumentation 6 | { 7 | public class ConstructorGenericCallThis : BaseTest 8 | { 9 | public class Class 10 | { 11 | public readonly T Value; 12 | public readonly bool Other; 13 | 14 | public Class(T value) 15 | : this(value, true) 16 | { 17 | } 18 | 19 | public Class(T value, bool other) 20 | { 21 | Value = value; 22 | Other = other; 23 | } 24 | } 25 | 26 | public ConstructorGenericCallThis() : base(typeof(Class<>).GetConstructors().First()) 27 | { 28 | } 29 | 30 | public override void FunctionalTest() 31 | { 32 | var result = new Class(5); 33 | result.Value.Should().Be(5); 34 | result.Other.Should().BeTrue(); 35 | } 36 | 37 | public override string ExpectedIL => @".locals init (MiniCover.HitServices.MethodScope V_0) 38 | IL_0000: ldstr ""/tmp"" 39 | IL_0005: ldstr ""MiniCover.Core.UnitTests"" 40 | IL_000a: ldstr ""MiniCover.Core.UnitTests.Instrumentation.ConstructorGenericCallThis/Class`1"" 41 | IL_000f: ldstr "".ctor"" 42 | IL_0014: call MiniCover.HitServices.MethodScope MiniCover.HitServices.HitService::EnterMethod(System.String,System.String,System.String,System.String) 43 | IL_0019: stloc.0 44 | IL_001a: nop 45 | .try 46 | { 47 | IL_001b: ldloc.0 48 | IL_001c: ldc.i4.1 49 | IL_001d: callvirt System.Void MiniCover.HitServices.MethodScope::Hit(System.Int32) 50 | IL_0022: ldarg.0 // this 51 | IL_0023: ldarg.1 52 | IL_0024: ldc.i4.1 53 | IL_0025: call System.Void MiniCover.Core.UnitTests.Instrumentation.ConstructorGenericCallThis/Class`1::.ctor(T,System.Boolean) 54 | IL_002a: nop 55 | IL_002b: nop 56 | IL_002c: leave.s IL_0036 57 | } 58 | finally 59 | { 60 | IL_002e: nop 61 | IL_002f: ldloc.0 62 | IL_0030: callvirt System.Void MiniCover.HitServices.MethodScope::Dispose() 63 | IL_0035: endfinally 64 | } 65 | IL_0036: ret 66 | "; 67 | 68 | public override IDictionary ExpectedHits => new Dictionary 69 | { 70 | [1] = 1 71 | }; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/ConstructorWithCallBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using FluentAssertions; 4 | 5 | namespace MiniCover.Core.UnitTests.Instrumentation 6 | { 7 | public class ConstructorWithCallBase : BaseTest 8 | { 9 | public class Class : BaseClass 10 | { 11 | public readonly bool WasCalled; 12 | 13 | public Class(int x) : base(x) 14 | { 15 | WasCalled = true; 16 | } 17 | } 18 | 19 | public class BaseClass 20 | { 21 | public readonly bool BaseWasCalled; 22 | 23 | public BaseClass(int x) 24 | { 25 | BaseWasCalled = true; 26 | } 27 | } 28 | 29 | public ConstructorWithCallBase() : base(typeof(Class).GetConstructors().First()) 30 | { 31 | } 32 | 33 | public override void FunctionalTest() 34 | { 35 | var result = new Class(5); 36 | result.WasCalled.Should().BeTrue(); 37 | result.BaseWasCalled.Should().BeTrue(); 38 | } 39 | 40 | public override string ExpectedIL => @".locals init (MiniCover.HitServices.MethodScope V_0) 41 | IL_0000: ldstr ""/tmp"" 42 | IL_0005: ldstr ""MiniCover.Core.UnitTests"" 43 | IL_000a: ldstr ""MiniCover.Core.UnitTests.Instrumentation.ConstructorWithCallBase/Class"" 44 | IL_000f: ldstr "".ctor"" 45 | IL_0014: call MiniCover.HitServices.MethodScope MiniCover.HitServices.HitService::EnterMethod(System.String,System.String,System.String,System.String) 46 | IL_0019: stloc.0 47 | IL_001a: nop 48 | .try 49 | { 50 | IL_001b: ldloc.0 51 | IL_001c: ldc.i4.1 52 | IL_001d: callvirt System.Void MiniCover.HitServices.MethodScope::Hit(System.Int32) 53 | IL_0022: ldarg.0 // this 54 | IL_0023: ldarg.1 55 | IL_0024: call System.Void MiniCover.Core.UnitTests.Instrumentation.ConstructorWithCallBase/BaseClass::.ctor(System.Int32) 56 | IL_0029: nop 57 | IL_002a: nop 58 | IL_002b: ldloc.0 59 | IL_002c: ldc.i4.2 60 | IL_002d: callvirt System.Void MiniCover.HitServices.MethodScope::Hit(System.Int32) 61 | IL_0032: ldarg.0 // this 62 | IL_0033: ldc.i4.1 63 | IL_0034: stfld System.Boolean MiniCover.Core.UnitTests.Instrumentation.ConstructorWithCallBase/Class::WasCalled 64 | IL_0039: leave.s IL_0043 65 | } 66 | finally 67 | { 68 | IL_003b: nop 69 | IL_003c: ldloc.0 70 | IL_003d: callvirt System.Void MiniCover.HitServices.MethodScope::Dispose() 71 | IL_0042: endfinally 72 | } 73 | IL_0043: ret 74 | "; 75 | 76 | public override IDictionary ExpectedHits => new Dictionary 77 | { 78 | [1] = 1, 79 | [2] = 1 80 | }; 81 | } 82 | } -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/ExcludedFromCodeCoverageClass.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | using FluentAssertions; 4 | using MiniCover.Core.Model; 5 | 6 | namespace MiniCover.Core.UnitTests.Instrumentation 7 | { 8 | public class ExcludedFromCodeCoverageClass : BaseTest 9 | { 10 | [ExcludeFromCodeCoverage] 11 | public class Class 12 | { 13 | public int Method(int value) 14 | { 15 | return value * 2; 16 | } 17 | } 18 | 19 | public ExcludedFromCodeCoverageClass() : base(typeof(Class).GetMethod(nameof(Class.Method))) 20 | { 21 | } 22 | 23 | public override void FunctionalTest() 24 | { 25 | new Class().Method(5).Should().Be(10); 26 | } 27 | 28 | public override string ExpectedIL => @".locals init (System.Int32 V_0, MiniCover.HitServices.MethodScope V_1, System.Int32 V_2) 29 | IL_0000: ldstr ""/tmp"" 30 | IL_0005: ldstr ""MiniCover.Core.UnitTests"" 31 | IL_000a: ldstr ""MiniCover.Core.UnitTests.Instrumentation.ExcludedFromCodeCoverageClass/Class"" 32 | IL_000f: ldstr ""Method"" 33 | IL_0014: call MiniCover.HitServices.MethodScope MiniCover.HitServices.HitService::EnterMethod(System.String,System.String,System.String,System.String) 34 | IL_0019: stloc.1 35 | IL_001a: nop 36 | .try 37 | { 38 | IL_001b: nop 39 | IL_001c: ldarg.1 40 | IL_001d: ldc.i4.2 41 | IL_001e: mul 42 | IL_001f: stloc.0 43 | IL_0020: br.s IL_0022 44 | IL_0022: ldloc.0 45 | IL_0023: stloc.2 46 | IL_0024: leave.s IL_002e 47 | } 48 | finally 49 | { 50 | IL_0026: nop 51 | IL_0027: ldloc.1 52 | IL_0028: callvirt System.Void MiniCover.HitServices.MethodScope::Dispose() 53 | IL_002d: endfinally 54 | } 55 | IL_002e: ldloc.2 56 | IL_002f: ret 57 | "; 58 | 59 | public override IDictionary ExpectedHits => new Dictionary(); 60 | 61 | public override InstrumentedSequence[] ExpectedInstructions => new InstrumentedSequence[0]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/ExcludedFromCodeCoverageMethod.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | using FluentAssertions; 4 | using MiniCover.Core.Model; 5 | 6 | namespace MiniCover.Core.UnitTests.Instrumentation 7 | { 8 | public class ExcludedFromCodeCoverageMethod : BaseTest 9 | { 10 | public class Class 11 | { 12 | [ExcludeFromCodeCoverage] 13 | public int Method(int value) 14 | { 15 | return value * 2; 16 | } 17 | } 18 | 19 | public ExcludedFromCodeCoverageMethod() : base(typeof(Class).GetMethod(nameof(Class.Method))) 20 | { 21 | } 22 | 23 | public override void FunctionalTest() 24 | { 25 | new Class().Method(5).Should().Be(10); 26 | } 27 | 28 | public override string ExpectedIL => @".locals init (System.Int32 V_0, MiniCover.HitServices.MethodScope V_1, System.Int32 V_2) 29 | IL_0000: ldstr ""/tmp"" 30 | IL_0005: ldstr ""MiniCover.Core.UnitTests"" 31 | IL_000a: ldstr ""MiniCover.Core.UnitTests.Instrumentation.ExcludedFromCodeCoverageMethod/Class"" 32 | IL_000f: ldstr ""Method"" 33 | IL_0014: call MiniCover.HitServices.MethodScope MiniCover.HitServices.HitService::EnterMethod(System.String,System.String,System.String,System.String) 34 | IL_0019: stloc.1 35 | IL_001a: nop 36 | .try 37 | { 38 | IL_001b: nop 39 | IL_001c: ldarg.1 40 | IL_001d: ldc.i4.2 41 | IL_001e: mul 42 | IL_001f: stloc.0 43 | IL_0020: br.s IL_0022 44 | IL_0022: ldloc.0 45 | IL_0023: stloc.2 46 | IL_0024: leave.s IL_002e 47 | } 48 | finally 49 | { 50 | IL_0026: nop 51 | IL_0027: ldloc.1 52 | IL_0028: callvirt System.Void MiniCover.HitServices.MethodScope::Dispose() 53 | IL_002d: endfinally 54 | } 55 | IL_002e: ldloc.2 56 | IL_002f: ret 57 | "; 58 | 59 | public override IDictionary ExpectedHits => new Dictionary(); 60 | 61 | public override InstrumentedSequence[] ExpectedInstructions => new InstrumentedSequence[0]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/FieldInitialization.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using FluentAssertions; 4 | 5 | namespace MiniCover.Core.UnitTests.Instrumentation 6 | { 7 | public class FieldInitialization : BaseTest 8 | { 9 | public class Class 10 | { 11 | public readonly int Value = 5; 12 | } 13 | 14 | public FieldInitialization() : base(typeof(Class).GetConstructors().First()) 15 | { 16 | } 17 | 18 | public override void FunctionalTest() 19 | { 20 | var result = new Class(); 21 | result.Value.Should().Be(5); 22 | } 23 | 24 | public override string ExpectedIL => @".locals init (MiniCover.HitServices.MethodScope V_0) 25 | IL_0000: ldstr ""/tmp"" 26 | IL_0005: ldstr ""MiniCover.Core.UnitTests"" 27 | IL_000a: ldstr ""MiniCover.Core.UnitTests.Instrumentation.FieldInitialization/Class"" 28 | IL_000f: ldstr "".ctor"" 29 | IL_0014: call MiniCover.HitServices.MethodScope MiniCover.HitServices.HitService::EnterMethod(System.String,System.String,System.String,System.String) 30 | IL_0019: stloc.0 31 | IL_001a: nop 32 | .try 33 | { 34 | IL_001b: ldloc.0 35 | IL_001c: ldc.i4.1 36 | IL_001d: callvirt System.Void MiniCover.HitServices.MethodScope::Hit(System.Int32) 37 | IL_0022: ldarg.0 // this 38 | IL_0023: ldc.i4.5 39 | IL_0024: stfld System.Int32 MiniCover.Core.UnitTests.Instrumentation.FieldInitialization/Class::Value 40 | IL_0029: ldarg.0 // this 41 | IL_002a: call System.Void System.Object::.ctor() 42 | IL_002f: nop 43 | IL_0030: leave.s IL_003a 44 | } 45 | finally 46 | { 47 | IL_0032: nop 48 | IL_0033: ldloc.0 49 | IL_0034: callvirt System.Void MiniCover.HitServices.MethodScope::Dispose() 50 | IL_0039: endfinally 51 | } 52 | IL_003a: ret 53 | "; 54 | 55 | public override IDictionary ExpectedHits => new Dictionary 56 | { 57 | [1] = 1 58 | }; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/Or.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentAssertions; 3 | using MiniCover.Core.Model; 4 | 5 | namespace MiniCover.Core.UnitTests.Instrumentation 6 | { 7 | public class Or : BaseTest 8 | { 9 | public class Class 10 | { 11 | public bool Method(bool x, bool y) 12 | { 13 | return x || y; 14 | } 15 | } 16 | 17 | public Or() : base(typeof(Class).GetMethod(nameof(Class.Method))) 18 | { 19 | } 20 | 21 | public override void FunctionalTest() 22 | { 23 | new Class().Method(false, false).Should().Be(false); 24 | new Class().Method(true, false).Should().Be(true); 25 | } 26 | 27 | public override string ExpectedIL => @".locals init (System.Boolean V_0, MiniCover.HitServices.MethodScope V_1, System.Boolean V_2) 28 | IL_0000: ldstr ""/tmp"" 29 | IL_0005: ldstr ""MiniCover.Core.UnitTests"" 30 | IL_000a: ldstr ""MiniCover.Core.UnitTests.Instrumentation.Or/Class"" 31 | IL_000f: ldstr ""Method"" 32 | IL_0014: call MiniCover.HitServices.MethodScope MiniCover.HitServices.HitService::EnterMethod(System.String,System.String,System.String,System.String) 33 | IL_0019: stloc.1 34 | IL_001a: nop 35 | .try 36 | { 37 | IL_001b: nop 38 | IL_001c: ldloc.1 39 | IL_001d: ldc.i4.1 40 | IL_001e: callvirt System.Void MiniCover.HitServices.MethodScope::Hit(System.Int32) 41 | IL_0023: ldarg.1 42 | IL_0024: ldarg.2 43 | IL_0025: or 44 | IL_0026: stloc.0 45 | IL_0027: br.s IL_0029 46 | IL_0029: ldloc.0 47 | IL_002a: stloc.2 48 | IL_002b: leave.s IL_0035 49 | } 50 | finally 51 | { 52 | IL_002d: nop 53 | IL_002e: ldloc.1 54 | IL_002f: callvirt System.Void MiniCover.HitServices.MethodScope::Dispose() 55 | IL_0034: endfinally 56 | } 57 | IL_0035: ldloc.2 58 | IL_0036: ret 59 | "; 60 | 61 | public override IDictionary ExpectedHits => new Dictionary 62 | { 63 | [1] = 2 64 | }; 65 | 66 | public override InstrumentedSequence[] ExpectedInstructions => new InstrumentedSequence[] 67 | { 68 | new InstrumentedSequence 69 | { 70 | Code = "return x || y;", 71 | EndColumn = 31, 72 | EndLine = 13, 73 | HitId = 1, 74 | Instruction = "IL_0001: ldarg x", 75 | Method = new InstrumentedMethod 76 | { 77 | Class = "MiniCover.Core.UnitTests.Instrumentation.Or/Class", 78 | FullName = "System.Boolean MiniCover.Core.UnitTests.Instrumentation.Or/Class::Method(System.Boolean,System.Boolean)", 79 | Name = "Method" 80 | }, 81 | StartColumn = 17, 82 | StartLine = 13 83 | } 84 | }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/PropInit.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FluentAssertions; 3 | 4 | namespace MiniCover.Core.UnitTests.Instrumentation 5 | { 6 | public class PropInit : BaseTest 7 | { 8 | public class Class 9 | { 10 | public int Value { get; init; } 11 | } 12 | 13 | public PropInit() : base(typeof(Class).GetProperties().First().GetSetMethod()) 14 | { 15 | } 16 | 17 | public override void FunctionalTest() 18 | { 19 | var result = new Class { Value = 5 }; 20 | result.Value.Should().Be(5); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/PropSet.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FluentAssertions; 3 | 4 | namespace MiniCover.Core.UnitTests.Instrumentation 5 | { 6 | public class PropSet : BaseTest 7 | { 8 | public class Class 9 | { 10 | public int Value { get; set; } 11 | } 12 | 13 | public PropSet() : base(typeof(Class).GetProperties().First().GetSetMethod()) 14 | { 15 | } 16 | 17 | public override void FunctionalTest() 18 | { 19 | var result = new Class { Value = 5 }; 20 | result.Value.Should().Be(5); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/StaticMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FluentAssertions; 4 | using Xunit; 5 | 6 | namespace MiniCover.Core.UnitTests.Instrumentation 7 | { 8 | public class StaticMethod : BaseTest 9 | { 10 | public static class Class 11 | { 12 | public static int Method(int value) 13 | { 14 | return value * 2; 15 | } 16 | } 17 | 18 | public StaticMethod() : base(typeof(Class).GetMethod(nameof(Class.Method))) 19 | { 20 | } 21 | 22 | public override void FunctionalTest() 23 | { 24 | Class.Method(5).Should().Be(10); 25 | } 26 | 27 | public override string ExpectedIL => @".locals init (System.Int32 V_0, MiniCover.HitServices.MethodScope V_1, System.Int32 V_2) 28 | IL_0000: ldstr ""/tmp"" 29 | IL_0005: ldstr ""MiniCover.Core.UnitTests"" 30 | IL_000a: ldstr ""MiniCover.Core.UnitTests.Instrumentation.StaticMethod/Class"" 31 | IL_000f: ldstr ""Method"" 32 | IL_0014: call MiniCover.HitServices.MethodScope MiniCover.HitServices.HitService::EnterMethod(System.String,System.String,System.String,System.String) 33 | IL_0019: stloc.1 34 | IL_001a: nop 35 | .try 36 | { 37 | IL_001b: nop 38 | IL_001c: ldloc.1 39 | IL_001d: ldc.i4.1 40 | IL_001e: callvirt System.Void MiniCover.HitServices.MethodScope::Hit(System.Int32) 41 | IL_0023: ldarg.0 // this 42 | IL_0024: ldc.i4.2 43 | IL_0025: mul 44 | IL_0026: stloc.0 45 | IL_0027: br.s IL_0029 46 | IL_0029: ldloc.0 47 | IL_002a: stloc.2 48 | IL_002b: leave.s IL_0035 49 | } 50 | finally 51 | { 52 | IL_002d: nop 53 | IL_002e: ldloc.1 54 | IL_002f: callvirt System.Void MiniCover.HitServices.MethodScope::Dispose() 55 | IL_0034: endfinally 56 | } 57 | IL_0035: ldloc.2 58 | IL_0036: ret 59 | "; 60 | 61 | public override IDictionary ExpectedHits => new Dictionary 62 | { 63 | [1] = 1 64 | }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/ThrowException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FluentAssertions; 4 | 5 | namespace MiniCover.Core.UnitTests.Instrumentation 6 | { 7 | public class ThrowException : BaseTest 8 | { 9 | public class Class 10 | { 11 | public int Method(int value) 12 | { 13 | value = value * 2; 14 | throw new Exception("Some exception"); 15 | } 16 | } 17 | 18 | public ThrowException() : base(typeof(Class).GetMethod(nameof(Class.Method))) 19 | { 20 | } 21 | 22 | public override void FunctionalTest() 23 | { 24 | try 25 | { 26 | new Class().Method(5).Should().Be(10); 27 | } 28 | catch 29 | { 30 | } 31 | } 32 | 33 | public override string ExpectedIL => @".locals init (MiniCover.HitServices.MethodScope V_0, System.Int32 V_1) 34 | IL_0000: ldstr ""/tmp"" 35 | IL_0005: ldstr ""MiniCover.Core.UnitTests"" 36 | IL_000a: ldstr ""MiniCover.Core.UnitTests.Instrumentation.ThrowException/Class"" 37 | IL_000f: ldstr ""Method"" 38 | IL_0014: call MiniCover.HitServices.MethodScope MiniCover.HitServices.HitService::EnterMethod(System.String,System.String,System.String,System.String) 39 | IL_0019: stloc.0 40 | IL_001a: nop 41 | .try 42 | { 43 | IL_001b: nop 44 | IL_001c: ldloc.0 45 | IL_001d: ldc.i4.1 46 | IL_001e: callvirt System.Void MiniCover.HitServices.MethodScope::Hit(System.Int32) 47 | IL_0023: ldarg.1 48 | IL_0024: ldc.i4.2 49 | IL_0025: mul 50 | IL_0026: starg 1 51 | IL_002a: ldloc.0 52 | IL_002b: ldc.i4.2 53 | IL_002c: callvirt System.Void MiniCover.HitServices.MethodScope::Hit(System.Int32) 54 | IL_0031: ldstr ""Some exception"" 55 | IL_0036: newobj System.Void System.Exception::.ctor(System.String) 56 | IL_003b: throw 57 | } 58 | finally 59 | { 60 | IL_003c: nop 61 | IL_003d: ldloc.0 62 | IL_003e: callvirt System.Void MiniCover.HitServices.MethodScope::Dispose() 63 | IL_0043: endfinally 64 | } 65 | IL_0044: ldloc.1 66 | IL_0045: ret 67 | "; 68 | 69 | public override IDictionary ExpectedHits => new Dictionary 70 | { 71 | [1] = 1, 72 | [2] = 1 73 | }; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Instrumentation/WorkerThread.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using FluentAssertions; 5 | using MiniCover.Core.Model; 6 | 7 | namespace MiniCover.Core.UnitTests.Instrumentation 8 | { 9 | public class WorkerThread : BaseTest 10 | { 11 | public static class Class 12 | { 13 | public static Task RunWorkerThread() 14 | { 15 | var tcs = new TaskCompletionSource(); 16 | var thread = new Thread(() => DoWork(tcs)); 17 | thread.Start(); 18 | return tcs.Task; 19 | } 20 | 21 | private static void DoWork(TaskCompletionSource tcs) 22 | { 23 | Thread.Sleep(100); 24 | tcs.SetResult(true); 25 | } 26 | } 27 | 28 | public WorkerThread() : base(typeof(Class)) 29 | { 30 | } 31 | 32 | public override void FunctionalTest() 33 | { 34 | Class.RunWorkerThread().GetAwaiter().GetResult(); 35 | } 36 | 37 | public override int? ExpectedHitCount => 7; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/MiniCover.Core.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | all 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/TestHelpers/TestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Moq; 3 | 4 | namespace MiniCover.UnitTests.TestHelpers 5 | { 6 | public class TestBase : IDisposable 7 | { 8 | private readonly MockRepository _mockRepository; 9 | 10 | public TestBase() 11 | { 12 | _mockRepository = new MockRepository(MockBehavior.Strict); 13 | } 14 | 15 | public Mock MockFor() 16 | where T : class 17 | { 18 | return _mockRepository.Create(); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | _mockRepository.VerifyAll(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/MiniCover.Core.UnitTests/Utils/FileUtilsTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions.TestingHelpers; 2 | using FluentAssertions; 3 | using MiniCover.Core.Utils; 4 | using MiniCover.TestHelpers; 5 | using Xunit; 6 | 7 | namespace MiniCover.UnitTests.Utils 8 | { 9 | public class FileUtilsTests 10 | { 11 | [Fact] 12 | public void GetFileHash_ShouldReturnHash() 13 | { 14 | var fileName = @"/test/ACME.Something.dll".ToOSPath(); 15 | 16 | var mockFileSystem = new MockFileSystem(); 17 | mockFileSystem.AddFile(fileName, new MockFileData(new byte[] { 1, 2, 3, 4, 5 })); 18 | 19 | var fileInfo = mockFileSystem.FileInfo.New(fileName); 20 | var hash = FileUtils.GetFileHash(fileInfo); 21 | 22 | hash.Should().Be("7CFDD07889B3295D6A550914AB35E068"); 23 | } 24 | 25 | [Fact] 26 | public void GetPdbFile_ShouldReturnPDBFile() 27 | { 28 | var fileName = @"/test/ACME.Something.dll".ToOSPath(); 29 | 30 | var mockFileSystem = new MockFileSystem(); 31 | mockFileSystem.AddFile(fileName, new MockFileData(new byte[5])); 32 | 33 | var fileInfo = mockFileSystem.FileInfo.New(fileName); 34 | var pdbInfo = FileUtils.GetPdbFile(fileInfo); 35 | 36 | pdbInfo.FullName.Should().Be(@"/test/ACME.Something.pdb".ToOSPath()); 37 | } 38 | 39 | [Fact] 40 | public void GetBackupFile_ShouldReturnBackupFile() 41 | { 42 | var fileName = @"/test/ACME.Something.dll".ToOSPath(); 43 | 44 | var mockFileSystem = new MockFileSystem(); 45 | mockFileSystem.AddFile(fileName, new MockFileData(new byte[5])); 46 | 47 | var fileInfo = mockFileSystem.FileInfo.New(fileName); 48 | var backupInfo = FileUtils.GetBackupFile(fileInfo); 49 | 50 | backupInfo.FullName.Should().Be(@"/test/ACME.Something.uninstrumented.dll".ToOSPath()); 51 | } 52 | 53 | [InlineData(@"/test/ACME.Something.dll", false)] 54 | [InlineData(@"/test/ACME.Something.uninstrumented.dll", true)] 55 | [Theory] 56 | public void IsBackupFile_ShouldReturnBackupFile(string fileName, bool isBackup) 57 | { 58 | var mockFileSystem = new MockFileSystem(); 59 | mockFileSystem.AddFile(fileName.ToOSPath(), new MockFileData(new byte[5])); 60 | 61 | var fileInfo = mockFileSystem.FileInfo.New(fileName.ToOSPath()); 62 | var result = FileUtils.IsBackupFile(fileInfo); 63 | 64 | result.Should().Be(isBackup); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/MiniCover.HitServices.UnitTests/HitContextTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | namespace MiniCover.HitServices.UnitTests 8 | { 9 | public class HitContextTests 10 | { 11 | [Fact] 12 | public void BinarySerializationShouldWork() 13 | { 14 | var assembly = this.GetType().Assembly; 15 | 16 | var sut = new HitContext(assembly.FullName, this.GetType().FullName, nameof(BinarySerializationShouldWork), new Dictionary { { 1, 15 } }); 17 | 18 | byte[] data; 19 | using (var stream = new MemoryStream()) 20 | { 21 | sut.Serialize(stream); 22 | data = stream.ToArray(); 23 | } 24 | 25 | using (var stream = new MemoryStream(data)) 26 | { 27 | var results = HitContext.Deserialize(stream).ToArray(); 28 | results.Length.Should().Be(1); 29 | 30 | var result = results.First(); 31 | result.ClassName.Should().Be(sut.ClassName); 32 | result.MethodName.Should().Be(sut.MethodName); 33 | result.AssemblyName.Should().Be(sut.AssemblyName); 34 | result.Hits.Should().BeEquivalentTo(sut.Hits); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/MiniCover.HitServices.UnitTests/MiniCover.HitServices.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | all 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/MiniCover.Reports.UnitTests/MiniCover.Reports.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | all 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/MiniCover.Reports.UnitTests/TestHelpers/TestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Moq; 3 | 4 | namespace MiniCover.Reports.UnitTests.TestHelpers 5 | { 6 | public class TestBase : IDisposable 7 | { 8 | private readonly MockRepository _mockRepository; 9 | 10 | public TestBase() 11 | { 12 | _mockRepository = new MockRepository(MockBehavior.Strict); 13 | } 14 | 15 | public Mock MockFor() 16 | where T : class 17 | { 18 | return _mockRepository.Create(); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | _mockRepository.VerifyAll(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/MiniCover.Reports.UnitTests/Utils/ResourceUtilsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using MiniCover.Reports.Utils; 4 | using Xunit; 5 | 6 | namespace MiniCover.UnitTests.Reports.Utils 7 | { 8 | public class ResourceUtilsTests 9 | { 10 | [Fact] 11 | public void GetContent() 12 | { 13 | var content = ResourceUtils.GetContent("MiniCover.Reports.Html.Shared.css"); 14 | content.Should().NotBeEmpty(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/MiniCover.TestHelpers/MiniCover.TestHelpers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/MiniCover.TestHelpers/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MiniCover.TestHelpers 5 | { 6 | public static class StringExtensions 7 | { 8 | public static string ToOSLineEnding(this string text) 9 | { 10 | text = text.Replace("\r\n", "\n"); 11 | 12 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 13 | text = text.Replace("\n", "\r\n"); 14 | 15 | return text; 16 | } 17 | 18 | public static string ToOSPath(this string text) 19 | { 20 | if (text.StartsWith("/") && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 21 | text = $"c:{text}"; 22 | else if (text.StartsWith("c:") && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 23 | text = text.Substring(2); 24 | 25 | return text.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/MiniCover.TestHelpers/TestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Moq; 3 | 4 | namespace MiniCover.TestHelpers 5 | { 6 | public class TestBase : IDisposable 7 | { 8 | private readonly MockRepository _mockRepository; 9 | 10 | public TestBase() 11 | { 12 | _mockRepository = new MockRepository(MockBehavior.Strict); 13 | } 14 | 15 | public Mock MockFor() 16 | where T : class 17 | { 18 | return _mockRepository.Create(); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | _mockRepository.VerifyAll(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/CommandTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using MiniCover.CommandLine; 3 | using MiniCover.TestHelpers; 4 | using Xunit; 5 | 6 | namespace MiniCover.UnitTests.CommandLine 7 | { 8 | public abstract class CommandTests : TestBase 9 | where T : ICommand 10 | { 11 | protected T Sut { get; set; } 12 | 13 | [Fact] 14 | public void ShouldHaveProperties() 15 | { 16 | Sut.CommandName.Should().NotBeEmpty(); 17 | Sut.CommandDescription.Should().NotBeEmpty(); 18 | Sut.Options.Should().NotBeNull(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Commands/CloverReportCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using MiniCover.CommandLine.Commands; 5 | using MiniCover.CommandLine.Options; 6 | using MiniCover.Core.Model; 7 | using MiniCover.Reports.Clover; 8 | using Moq; 9 | using Xunit; 10 | 11 | namespace MiniCover.UnitTests.CommandLine.Commands 12 | { 13 | public class CloverReportCommandTests : CommandTests 14 | { 15 | private readonly Mock _workingDirectoryOption; 16 | private readonly Mock _coverageLoadedFileOption; 17 | private readonly Mock _cloverOutputOption; 18 | private readonly Mock _cloverReport; 19 | 20 | public CloverReportCommandTests() 21 | { 22 | _workingDirectoryOption = MockFor(); 23 | _coverageLoadedFileOption = MockFor(); 24 | _cloverOutputOption = MockFor(); 25 | _cloverReport = MockFor(); 26 | 27 | Sut = new CloverReportCommand( 28 | _workingDirectoryOption.Object, 29 | _coverageLoadedFileOption.Object, 30 | _cloverOutputOption.Object, 31 | _cloverReport.Object 32 | ); 33 | } 34 | 35 | [Fact] 36 | public async Task Execute() 37 | { 38 | var result = new InstrumentationResult(); 39 | var output = MockFor(); 40 | 41 | _coverageLoadedFileOption.SetupGet(x => x.Result).Returns(result); 42 | _cloverOutputOption.SetupGet(x => x.FileInfo).Returns(output.Object); 43 | _cloverReport.Setup(x => x.Execute(result, output.Object)); 44 | 45 | var exitCode = await Sut.Execute(); 46 | exitCode.Should().Be(0); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Commands/CoberturaReportCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using MiniCover.CommandLine.Commands; 5 | using MiniCover.CommandLine.Options; 6 | using MiniCover.Core.Model; 7 | using MiniCover.Reports.Cobertura; 8 | using Moq; 9 | using Xunit; 10 | 11 | namespace MiniCover.UnitTests.CommandLine.Commands 12 | { 13 | public class CoberturaReportCommandTests : CommandTests 14 | { 15 | private readonly Mock _workingDirectoryOption; 16 | private readonly Mock _coberturaOutputOption; 17 | private readonly Mock _coverageLoadedFileOption; 18 | private readonly Mock _coberturaReport; 19 | 20 | public CoberturaReportCommandTests() 21 | { 22 | _workingDirectoryOption = MockFor(); 23 | _coberturaOutputOption = MockFor(); 24 | _coverageLoadedFileOption = MockFor(); 25 | _coberturaReport = MockFor(); 26 | 27 | Sut = new CoberturaReportCommand( 28 | _workingDirectoryOption.Object, 29 | _coverageLoadedFileOption.Object, 30 | _coberturaOutputOption.Object, 31 | _coberturaReport.Object 32 | ); 33 | } 34 | 35 | [Fact] 36 | public async Task Execute() 37 | { 38 | var result = new InstrumentationResult(); 39 | var output = MockFor(); 40 | 41 | _coverageLoadedFileOption.SetupGet(x => x.Result).Returns(result); 42 | _coberturaOutputOption.SetupGet(x => x.FileInfo).Returns(output.Object); 43 | _coberturaReport.Setup(x => x.Execute(result, output.Object)); 44 | 45 | var exitCode = await Sut.Execute(); 46 | exitCode.Should().Be(0); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Commands/ConsoleReportCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using MiniCover.CommandLine.Commands; 5 | using MiniCover.CommandLine.Options; 6 | using MiniCover.Core.Model; 7 | using MiniCover.Reports.Console; 8 | using Moq; 9 | using Xunit; 10 | 11 | namespace MiniCover.UnitTests.CommandLine.Commands 12 | { 13 | public class ConsoleReportCommandTests : CommandTests 14 | { 15 | private readonly Mock _workingDirectoryOption; 16 | private readonly Mock _coverageLoadedFileOption; 17 | private readonly Mock _thresholdOption; 18 | private readonly Mock _noFailOption; 19 | private readonly Mock _consoleReport; 20 | 21 | public ConsoleReportCommandTests() 22 | { 23 | _workingDirectoryOption = MockFor(); 24 | _coverageLoadedFileOption = MockFor(); 25 | _thresholdOption = MockFor(); 26 | _noFailOption = MockFor(); 27 | _consoleReport = MockFor(); 28 | 29 | Sut = new ConsoleReportCommand( 30 | _workingDirectoryOption.Object, 31 | _coverageLoadedFileOption.Object, 32 | _thresholdOption.Object, 33 | _noFailOption.Object, 34 | _consoleReport.Object 35 | ); 36 | } 37 | 38 | [InlineData(50f, false)] 39 | [InlineData(75f, true)] 40 | [Theory] 41 | public async Task Execute(float threshold, bool noFail) 42 | { 43 | var result = new InstrumentationResult(); 44 | var output = MockFor(); 45 | 46 | _coverageLoadedFileOption.SetupGet(x => x.Result).Returns(result); 47 | _thresholdOption.SetupGet(x => x.Value).Returns(threshold); 48 | _noFailOption.SetupGet(x => x.Value).Returns(noFail); 49 | _consoleReport.Setup(x => x.Execute(result, threshold, noFail)) 50 | .Returns(0); 51 | 52 | var exitCode = await Sut.Execute(); 53 | exitCode.Should().Be(0); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Commands/HtmlReportCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using MiniCover.CommandLine.Commands; 5 | using MiniCover.CommandLine.Options; 6 | using MiniCover.Core.Model; 7 | using MiniCover.Reports.Html; 8 | using Moq; 9 | using Xunit; 10 | 11 | namespace MiniCover.UnitTests.CommandLine.Commands 12 | { 13 | public class HtmlReportCommandTests : CommandTests 14 | { 15 | private readonly Mock _workingDirectoryOption; 16 | private readonly Mock _coverageLoadedFileOption; 17 | private readonly Mock _htmlOutputDirectoryOption; 18 | private readonly Mock _thresholdOption; 19 | private readonly Mock _noFailOption; 20 | private readonly Mock _htmlReport; 21 | 22 | public HtmlReportCommandTests() 23 | { 24 | _workingDirectoryOption = MockFor(); 25 | _coverageLoadedFileOption = MockFor(); 26 | _htmlOutputDirectoryOption = MockFor(); 27 | _thresholdOption = MockFor(); 28 | _noFailOption = MockFor(); 29 | _htmlReport = MockFor(); 30 | 31 | Sut = new HtmlReportCommand( 32 | _workingDirectoryOption.Object, 33 | _coverageLoadedFileOption.Object, 34 | _htmlOutputDirectoryOption.Object, 35 | _thresholdOption.Object, 36 | _noFailOption.Object, 37 | _htmlReport.Object 38 | ); 39 | } 40 | 41 | [InlineData(50f, false)] 42 | [InlineData(75f, true)] 43 | [Theory] 44 | public async Task Execute(float threshold, bool noFail) 45 | { 46 | var result = new InstrumentationResult(); 47 | var output = MockFor(); 48 | 49 | _coverageLoadedFileOption.SetupGet(x => x.Result).Returns(result); 50 | _htmlOutputDirectoryOption.SetupGet(x => x.DirectoryInfo).Returns(output.Object); 51 | _thresholdOption.SetupGet(x => x.Value).Returns(threshold); 52 | _noFailOption.SetupGet(x => x.Value).Returns(noFail); 53 | _htmlReport.Setup(x => x.Execute(result, output.Object, threshold, noFail)) 54 | .Returns(0); 55 | 56 | var exitCode = await Sut.Execute(); 57 | exitCode.Should().Be(0); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Commands/NCoverReportCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using MiniCover.CommandLine.Commands; 5 | using MiniCover.CommandLine.Options; 6 | using MiniCover.Core.Model; 7 | using MiniCover.Reports.NCover; 8 | using Moq; 9 | using Xunit; 10 | 11 | namespace MiniCover.UnitTests.CommandLine.Commands 12 | { 13 | public class NCoverReportCommandTests : CommandTests 14 | { 15 | private readonly Mock _workingDirectoryOption; 16 | private readonly Mock _nCoverOutputOption; 17 | private readonly Mock _coverageLoadedFileOption; 18 | private readonly Mock _nCoverReport; 19 | 20 | public NCoverReportCommandTests() 21 | { 22 | _workingDirectoryOption = MockFor(); 23 | _nCoverOutputOption = MockFor(); 24 | _coverageLoadedFileOption = MockFor(); 25 | _nCoverReport = MockFor(); 26 | 27 | Sut = new NCoverReportCommand( 28 | _workingDirectoryOption.Object, 29 | _coverageLoadedFileOption.Object, 30 | _nCoverOutputOption.Object, 31 | _nCoverReport.Object 32 | ); 33 | } 34 | 35 | [Fact] 36 | public async Task Execute() 37 | { 38 | var result = new InstrumentationResult(); 39 | var output = MockFor(); 40 | 41 | _coverageLoadedFileOption.SetupGet(x => x.Result).Returns(result); 42 | _nCoverOutputOption.SetupGet(x => x.FileInfo).Returns(output.Object); 43 | _nCoverReport.Setup(x => x.Execute(result, output.Object)); 44 | 45 | var exitCode = await Sut.Execute(); 46 | exitCode.Should().Be(0); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Commands/OpenCoverReportCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using MiniCover.CommandLine.Commands; 5 | using MiniCover.CommandLine.Options; 6 | using MiniCover.Core.Model; 7 | using MiniCover.Reports.OpenCover; 8 | using Moq; 9 | using Xunit; 10 | 11 | namespace MiniCover.UnitTests.CommandLine.Commands 12 | { 13 | public class OpenCoverReportCommandTests : CommandTests 14 | { 15 | private readonly Mock _workingDirectoryOption; 16 | private readonly Mock _openCoverOutputOption; 17 | private readonly Mock _coverageLoadedFileOption; 18 | private readonly Mock _openCoverReport; 19 | 20 | public OpenCoverReportCommandTests() 21 | { 22 | _workingDirectoryOption = MockFor(); 23 | _openCoverOutputOption = MockFor(); 24 | _coverageLoadedFileOption = MockFor(); 25 | _openCoverReport = MockFor(); 26 | 27 | Sut = new OpenCoverReportCommand( 28 | _workingDirectoryOption.Object, 29 | _coverageLoadedFileOption.Object, 30 | _openCoverOutputOption.Object, 31 | _openCoverReport.Object 32 | ); 33 | } 34 | 35 | [Fact] 36 | public async Task Execute() 37 | { 38 | var result = new InstrumentationResult(); 39 | var output = MockFor(); 40 | 41 | _coverageLoadedFileOption.SetupGet(x => x.Result).Returns(result); 42 | _openCoverOutputOption.SetupGet(x => x.FileInfo).Returns(output.Object); 43 | _openCoverReport.Setup(x => x.Execute(result, output.Object)); 44 | 45 | var exitCode = await Sut.Execute(); 46 | exitCode.Should().Be(0); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Commands/UninstrumentCommandTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using MiniCover.CommandLine.Options; 4 | using MiniCover.Commands; 5 | using MiniCover.Core.Instrumentation; 6 | using MiniCover.Core.Model; 7 | using Moq; 8 | using Xunit; 9 | 10 | namespace MiniCover.UnitTests.CommandLine.Commands 11 | { 12 | public class UninstrumentCommandTests : CommandTests 13 | { 14 | private readonly Mock _verbosityOption; 15 | private readonly Mock _workingDirectoryOption; 16 | private readonly Mock _coverageLoadedFileOption; 17 | private readonly Mock _uninstrumenter; 18 | 19 | public UninstrumentCommandTests() 20 | { 21 | _verbosityOption = MockFor(); 22 | _workingDirectoryOption = MockFor(); 23 | _coverageLoadedFileOption = MockFor(); 24 | _uninstrumenter = MockFor(); 25 | 26 | Sut = new UninstrumentCommand( 27 | _verbosityOption.Object, 28 | _workingDirectoryOption.Object, 29 | _coverageLoadedFileOption.Object, 30 | _uninstrumenter.Object 31 | ); 32 | } 33 | 34 | [Fact] 35 | public async Task Execute() 36 | { 37 | var result = new InstrumentationResult(); 38 | 39 | _coverageLoadedFileOption.SetupGet(x => x.Result).Returns(result); 40 | _uninstrumenter.Setup(x => x.Uninstrument(result)); 41 | 42 | var exitCode = await Sut.Execute(); 43 | exitCode.Should().Be(0); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/DirectoryOptionTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions.TestingHelpers; 2 | using FluentAssertions; 3 | using MiniCover.CommandLine.Options; 4 | using Xunit; 5 | 6 | namespace MiniCover.UnitTests.CommandLine 7 | { 8 | public abstract class DirectoryOptionTests 9 | where T : DirectoryOption 10 | { 11 | protected abstract string CurrentDirectory { get; } 12 | protected abstract string ExpectedDefaultValue { get; } 13 | protected abstract string InputValue { get; } 14 | protected abstract string ExpectedValue { get; } 15 | 16 | protected MockFileSystem MockFileSystem { get; } 17 | 18 | public DirectoryOptionTests() 19 | { 20 | MockFileSystem = new MockFileSystem(); 21 | } 22 | 23 | protected abstract T Create(); 24 | 25 | [Fact] 26 | public void ShouldHaveProperties() 27 | { 28 | var sut = Create(); 29 | sut.Name.Should().NotBeEmpty(); 30 | sut.Description.Should().NotBeEmpty(); 31 | } 32 | 33 | [Fact] 34 | public void NoValue_ShouldReturnDefaultValue() 35 | { 36 | MockFileSystem.AddDirectory(CurrentDirectory); 37 | MockFileSystem.Directory.SetCurrentDirectory(CurrentDirectory); 38 | 39 | var sut = Create(); 40 | sut.ReceiveValue(null); 41 | VerifyDefaultValue(sut); 42 | } 43 | 44 | [Fact] 45 | public void Value_ShouldReturnValue() 46 | { 47 | MockFileSystem.AddDirectory(CurrentDirectory); 48 | MockFileSystem.Directory.SetCurrentDirectory(CurrentDirectory); 49 | 50 | var sut = Create(); 51 | sut.ReceiveValue(InputValue); 52 | VerifyInputValue(sut); 53 | } 54 | 55 | protected virtual void VerifyDefaultValue(T sut) 56 | { 57 | sut.DirectoryInfo.Should().NotBeNull(); 58 | sut.DirectoryInfo.FullName.Should().Be(ExpectedDefaultValue); 59 | } 60 | 61 | protected virtual void VerifyInputValue(T sut) 62 | { 63 | sut.DirectoryInfo.Should().NotBeNull(); 64 | sut.DirectoryInfo.FullName.Should().Be(ExpectedValue); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/FileOptionTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions.TestingHelpers; 2 | using FluentAssertions; 3 | using MiniCover.CommandLine.Options; 4 | using Xunit; 5 | 6 | namespace MiniCover.UnitTests.CommandLine 7 | { 8 | public abstract class FileOptionTests 9 | where T : FileOption 10 | { 11 | protected abstract string CurrentDirectory { get; } 12 | protected abstract string ExpectedDefaultValue { get; } 13 | protected abstract string InputValue { get; } 14 | protected abstract string ExpectedValue { get; } 15 | protected virtual string FileContent => "file-content"; 16 | 17 | protected MockFileSystem MockFileSystem { get; } 18 | 19 | public FileOptionTests() 20 | { 21 | MockFileSystem = new MockFileSystem(); 22 | } 23 | 24 | protected abstract T Create(); 25 | 26 | [Fact] 27 | public void ShouldHaveProperties() 28 | { 29 | var sut = Create(); 30 | sut.Name.Should().NotBeEmpty(); 31 | sut.Description.Should().NotBeEmpty(); 32 | } 33 | 34 | [Fact] 35 | public void NoValue_ShouldReturnDefaultValue() 36 | { 37 | MockFileSystem.AddFile(ExpectedDefaultValue, FileContent); 38 | MockFileSystem.Directory.SetCurrentDirectory(CurrentDirectory); 39 | 40 | var sut = Create(); 41 | sut.ReceiveValue(null); 42 | VerifyDefaultValue(sut); 43 | } 44 | 45 | [Fact] 46 | public void Value_ShouldReturnValue() 47 | { 48 | MockFileSystem.AddFile(ExpectedValue, FileContent); 49 | MockFileSystem.Directory.SetCurrentDirectory(CurrentDirectory); 50 | 51 | var sut = Create(); 52 | sut.ReceiveValue(InputValue); 53 | VerifyInputValue(sut); 54 | } 55 | 56 | protected virtual void VerifyDefaultValue(T sut) 57 | { 58 | sut.FileInfo.Should().NotBeNull(); 59 | sut.FileInfo.FullName.Should().Be(ExpectedDefaultValue); 60 | } 61 | 62 | protected virtual void VerifyInputValue(T sut) 63 | { 64 | sut.FileInfo.Should().NotBeNull(); 65 | sut.FileInfo.FullName.Should().Be(ExpectedValue); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/FilesPatternOptionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using MiniCover.CommandLine.Options; 3 | using Xunit; 4 | 5 | namespace MiniCover.UnitTests.CommandLine 6 | { 7 | public abstract class FilesPatternOptionTests 8 | where T : FilesPatternOption 9 | { 10 | protected abstract string[] ExpectedDefaultValue { get; } 11 | protected abstract string[] InputValue { get; } 12 | protected abstract string[] ExpectedValue { get; } 13 | 14 | protected abstract T Create(); 15 | 16 | [Fact] 17 | public void ShouldHaveProperties() 18 | { 19 | var sut = Create(); 20 | sut.Name.Should().NotBeEmpty(); 21 | sut.Description.Should().NotBeEmpty(); 22 | } 23 | 24 | [Fact] 25 | public void NullValue_ShouldReturnDefaultValue() 26 | { 27 | var sut = Create(); 28 | sut.ReceiveValue(null); 29 | sut.Value.Should().NotBeNull(); 30 | sut.Value.Should().BeEquivalentTo(ExpectedDefaultValue); 31 | } 32 | 33 | [Fact] 34 | public void EmptyValue_ShouldReturnDefaultValue() 35 | { 36 | var sut = Create(); 37 | sut.ReceiveValue(new string[0]); 38 | sut.Value.Should().NotBeNull(); 39 | sut.Value.Should().BeEquivalentTo(ExpectedDefaultValue); 40 | } 41 | 42 | [Fact] 43 | public void Value_ShouldReturnValue() 44 | { 45 | var sut = Create(); 46 | sut.ReceiveValue(InputValue); 47 | sut.Value.Should().NotBeNull(); 48 | sut.Value.Should().BeEquivalentTo(ExpectedValue); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/CloverOutputOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | using MiniCover.TestHelpers; 3 | 4 | namespace MiniCover.UnitTests.CommandLine.Options 5 | { 6 | public class CloverOutputOptionTests : FileOptionTests 7 | { 8 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 9 | protected override string ExpectedDefaultValue => "/current-directory/clover.xml".ToOSPath(); 10 | protected override string InputValue => "folder/file.xml".ToOSPath(); 11 | protected override string ExpectedValue => "/current-directory/folder/file.xml".ToOSPath(); 12 | 13 | protected override CloverOutputOption Create() 14 | { 15 | return new CloverOutputOption(MockFileSystem); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/CoberturaOutputOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | using MiniCover.TestHelpers; 3 | 4 | namespace MiniCover.UnitTests.CommandLine.Options 5 | { 6 | public class CoberturaOutputOptionTests : FileOptionTests 7 | { 8 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 9 | protected override string ExpectedDefaultValue => "/current-directory/cobertura.xml".ToOSPath(); 10 | protected override string InputValue => "folder/file.xml".ToOSPath(); 11 | protected override string ExpectedValue => "/current-directory/folder/file.xml".ToOSPath(); 12 | 13 | protected override CoberturaOutputOption Create() 14 | { 15 | return new CoberturaOutputOption(MockFileSystem); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/CoverageFileOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | using MiniCover.TestHelpers; 3 | 4 | namespace MiniCover.UnitTests.CommandLine.Options 5 | { 6 | public class CoverageFileOptionTests : FileOptionTests 7 | { 8 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 9 | protected override string ExpectedDefaultValue => "/current-directory/coverage.json".ToOSPath(); 10 | protected override string InputValue => "folder/file.json".ToOSPath(); 11 | protected override string ExpectedValue => "/current-directory/folder/file.json".ToOSPath(); 12 | 13 | protected override CoverageFileOption Create() 14 | { 15 | return new CoverageFileOption(MockFileSystem); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/CoverageLoadedFileOptionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using MiniCover.CommandLine.Options; 4 | using MiniCover.Exceptions; 5 | using MiniCover.TestHelpers; 6 | using Xunit; 7 | 8 | namespace MiniCover.UnitTests.CommandLine.Options 9 | { 10 | public class CoverageLoadedFileOptionTests : FileOptionTests 11 | { 12 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 13 | protected override string ExpectedDefaultValue => "/current-directory/coverage.json".ToOSPath(); 14 | protected override string InputValue => "folder/file.json".ToOSPath(); 15 | protected override string ExpectedValue => "/current-directory/folder/file.json".ToOSPath(); 16 | protected override string FileContent => "{\"SourcePath\": \"/source-path\"}"; 17 | 18 | protected override CoverageLoadedFileOption Create() 19 | { 20 | return new CoverageLoadedFileOption(MockFileSystem); 21 | } 22 | 23 | protected override void VerifyDefaultValue(CoverageLoadedFileOption sut) 24 | { 25 | base.VerifyDefaultValue(sut); 26 | sut.Result.SourcePath.Should().Be("/source-path"); 27 | } 28 | 29 | protected override void VerifyInputValue(CoverageLoadedFileOption sut) 30 | { 31 | base.VerifyInputValue(sut); 32 | sut.Result.SourcePath.Should().Be("/source-path"); 33 | } 34 | 35 | [Fact] 36 | public void FileDoesntExist_ShouldThrow() 37 | { 38 | MockFileSystem.Directory.SetCurrentDirectory(CurrentDirectory); 39 | 40 | var sut = Create(); 41 | Action act = () => sut.ReceiveValue(InputValue); 42 | act.Should().Throw(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/ExcludeAssembliesPatternOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | 3 | namespace MiniCover.UnitTests.CommandLine.Options 4 | { 5 | public class ExcludeAssembliesPatternOptionTests : FilesPatternOptionTests 6 | { 7 | protected override string[] ExpectedDefaultValue => new string[] { "**/obj/**/*.dll" }; 8 | protected override string[] InputValue => new string[] { "a.dll", "**/a.dll" }; 9 | protected override string[] ExpectedValue => new string[] { "a.dll", "**/a.dll" }; 10 | 11 | protected override ExcludeAssembliesPatternOption Create() 12 | { 13 | return new ExcludeAssembliesPatternOption(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/ExcludeSourcesPatternOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | 3 | namespace MiniCover.UnitTests.CommandLine.Options 4 | { 5 | public class ExcludeSourcesPatternOptionTests : FilesPatternOptionTests 6 | { 7 | protected override string[] ExpectedDefaultValue => new string[] { "**/bin/**/*.cs", "**/obj/**/*.cs" }; 8 | protected override string[] InputValue => new string[] { "a.cs", "**/a.cs" }; 9 | protected override string[] ExpectedValue => new string[] { "a.cs", "**/a.cs" }; 10 | 11 | protected override ExcludeSourcesPatternOption Create() 12 | { 13 | return new ExcludeSourcesPatternOption(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/ExcludeTestsPatternOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | 3 | namespace MiniCover.UnitTests.CommandLine.Options 4 | { 5 | public class ExcludeTestsPatternOptionTests : FilesPatternOptionTests 6 | { 7 | protected override string[] ExpectedDefaultValue => new string[] { "**/bin/**/*.cs", "**/obj/**/*.cs" }; 8 | protected override string[] InputValue => new string[] { "a.cs", "**/a.cs" }; 9 | protected override string[] ExpectedValue => new string[] { "a.cs", "**/a.cs" }; 10 | 11 | protected override ExcludeTestsPatternOption Create() 12 | { 13 | return new ExcludeTestsPatternOption(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/HitsDirectoryOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | using MiniCover.TestHelpers; 3 | 4 | namespace MiniCover.UnitTests.CommandLine.Options 5 | { 6 | public class HitsDirectoryOptionTests : DirectoryOptionTests 7 | { 8 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 9 | protected override string ExpectedDefaultValue => "/current-directory/coverage-hits".ToOSPath(); 10 | protected override string InputValue => "folder".ToOSPath(); 11 | protected override string ExpectedValue => "/current-directory/folder".ToOSPath(); 12 | 13 | protected override HitsDirectoryOption Create() 14 | { 15 | return new HitsDirectoryOption(MockFileSystem); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/HtmlOutputDirectoryOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | using MiniCover.TestHelpers; 3 | 4 | namespace MiniCover.UnitTests.CommandLine.Options 5 | { 6 | public class HtmlOutputDirectoryOptionTests : DirectoryOptionTests 7 | { 8 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 9 | protected override string ExpectedDefaultValue => "/current-directory/coverage-html".ToOSPath(); 10 | protected override string InputValue => "folder".ToOSPath(); 11 | protected override string ExpectedValue => "/current-directory/folder".ToOSPath(); 12 | 13 | protected override HtmlOutputDirectoryOption Create() 14 | { 15 | return new HtmlOutputDirectoryOption(MockFileSystem); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/IncludeAssembliesPatternOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | 3 | namespace MiniCover.UnitTests.CommandLine.Options 4 | { 5 | public class IncludeAssembliesPatternOptionTests : FilesPatternOptionTests 6 | { 7 | protected override string[] ExpectedDefaultValue => new string[] { "**/*.dll" }; 8 | protected override string[] InputValue => new string[] { "a.dll", "**/a.dll" }; 9 | protected override string[] ExpectedValue => new string[] { "a.dll", "**/a.dll" }; 10 | 11 | protected override IncludeAssembliesPatternOption Create() 12 | { 13 | return new IncludeAssembliesPatternOption(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/IncludeSourcesPatternOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | 3 | namespace MiniCover.UnitTests.CommandLine.Options 4 | { 5 | public class IncludeSourcesPatternOptionTests : FilesPatternOptionTests 6 | { 7 | protected override string[] ExpectedDefaultValue => new string[] { "src/**/*.cs" }; 8 | protected override string[] InputValue => new string[] { "a.cs", "**/a.cs" }; 9 | protected override string[] ExpectedValue => new string[] { "a.cs", "**/a.cs" }; 10 | 11 | protected override IncludeSourcesPatternOption Create() 12 | { 13 | return new IncludeSourcesPatternOption(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/IncludeTestsPatternOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | 3 | namespace MiniCover.UnitTests.CommandLine.Options 4 | { 5 | public class IncludeTestsPatternOptionTests : FilesPatternOptionTests 6 | { 7 | protected override string[] ExpectedDefaultValue => new string[] { "tests/**/*.cs", "test/**/*.cs" }; 8 | protected override string[] InputValue => new string[] { "a.cs", "**/a.cs" }; 9 | protected override string[] ExpectedValue => new string[] { "a.cs", "**/a.cs" }; 10 | 11 | protected override IncludeTestsPatternOption Create() 12 | { 13 | return new IncludeTestsPatternOption(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/NCoverOutputOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | using MiniCover.TestHelpers; 3 | 4 | namespace MiniCover.UnitTests.CommandLine.Options 5 | { 6 | public class OpenCoverOutputOptionTests : FileOptionTests 7 | { 8 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 9 | protected override string ExpectedDefaultValue => "/current-directory/coverage.xml".ToOSPath(); 10 | protected override string InputValue => "folder/file.xml".ToOSPath(); 11 | protected override string ExpectedValue => "/current-directory/folder/file.xml".ToOSPath(); 12 | 13 | protected override NCoverOutputOption Create() 14 | { 15 | return new NCoverOutputOption(MockFileSystem); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/OpenCoverOutputOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | using MiniCover.TestHelpers; 3 | 4 | namespace MiniCover.UnitTests.CommandLine.Options 5 | { 6 | public class NCoverOutputOptionTests : FileOptionTests 7 | { 8 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 9 | protected override string ExpectedDefaultValue => "/current-directory/opencovercoverage.xml".ToOSPath(); 10 | protected override string InputValue => "folder/file.xml".ToOSPath(); 11 | protected override string ExpectedValue => "/current-directory/folder/file.xml".ToOSPath(); 12 | 13 | protected override OpenCoverOutputOption Create() 14 | { 15 | return new OpenCoverOutputOption(MockFileSystem); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/ParentDirectoryOptionTests.cs: -------------------------------------------------------------------------------- 1 | using MiniCover.CommandLine.Options; 2 | using MiniCover.TestHelpers; 3 | 4 | namespace MiniCover.UnitTests.CommandLine.Options 5 | { 6 | public class ParentDirectoryOptionTests : DirectoryOptionTests 7 | { 8 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 9 | protected override string ExpectedDefaultValue => "/current-directory".ToOSPath(); 10 | protected override string InputValue => "folder".ToOSPath(); 11 | protected override string ExpectedValue => "/current-directory/folder".ToOSPath(); 12 | 13 | protected override ParentDirectoryOption Create() 14 | { 15 | return new ParentDirectoryOption(MockFileSystem); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/ThresholdOptionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using MiniCover.CommandLine.Options; 3 | using Xunit; 4 | 5 | namespace MiniCover.UnitTests.CommandLine.Options 6 | { 7 | public class ThresholdOptionTests 8 | { 9 | [Fact] 10 | public void ShouldHaveProperties() 11 | { 12 | var sut = new ThresholdOption(); 13 | sut.Name.Should().NotBeEmpty(); 14 | sut.Description.Should().NotBeEmpty(); 15 | } 16 | 17 | [Fact] 18 | public void NoValue_ShouldReturnDefaultValue() 19 | { 20 | var sut = new ThresholdOption(); 21 | sut.ReceiveValue(null); 22 | sut.Value.Should().Be(0.9f); 23 | } 24 | 25 | [Fact] 26 | public void Value_ShouldReturnValue() 27 | { 28 | var sut = new ThresholdOption(); 29 | sut.ReceiveValue("80.51"); 30 | sut.Value.Should().Be(0.8051f); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/VerbosityOptionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Microsoft.Extensions.Logging; 4 | using MiniCover.CommandLine.Options; 5 | using MiniCover.Exceptions; 6 | using MiniCover.IO; 7 | using Moq; 8 | using Xunit; 9 | 10 | namespace MiniCover.UnitTests.CommandLine.Options 11 | { 12 | public class VerbosityOptionTests 13 | { 14 | [Fact] 15 | public void ShouldHaveProperties() 16 | { 17 | var outputMock = new Mock(); 18 | var sut = new VerbosityOption(outputMock.Object); 19 | sut.Name.Should().NotBeEmpty(); 20 | sut.Description.Should().NotBeEmpty(); 21 | } 22 | 23 | [Fact] 24 | public void NoValue_ShouldNotCHangeOutput() 25 | { 26 | var outputMock = new Mock(); 27 | outputMock.SetupProperty(o => o.MinimumLevel, LogLevel.Information); 28 | 29 | var sut = new VerbosityOption(outputMock.Object); 30 | sut.ReceiveValue(null); 31 | outputMock.Object.MinimumLevel.Should().Be(LogLevel.Information); 32 | } 33 | 34 | [InlineData("debug")] 35 | [InlineData("Debug")] 36 | [Theory] 37 | public void Value_ShouldSetOutputVerbose(string input) 38 | { 39 | var outputMock = new Mock(); 40 | outputMock.SetupProperty(o => o.MinimumLevel, LogLevel.Information); 41 | 42 | var sut = new VerbosityOption(outputMock.Object); 43 | sut.ReceiveValue(input); 44 | outputMock.Object.MinimumLevel.Should().Be(LogLevel.Debug); 45 | } 46 | 47 | [Fact] 48 | public void InvalidValue_ShouldThrow() 49 | { 50 | var outputMock = new Mock(); 51 | outputMock.SetupProperty(o => o.MinimumLevel, LogLevel.Information); 52 | 53 | var sut = new VerbosityOption(outputMock.Object); 54 | 55 | Action act = () => sut.ReceiveValue("InvalidLevel"); 56 | 57 | act.Should().Throw(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/Options/WorkingDirectoryOptionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.Extensions.Logging.Abstractions; 3 | using MiniCover.CommandLine.Options; 4 | using MiniCover.TestHelpers; 5 | 6 | namespace MiniCover.UnitTests.CommandLine.Options 7 | { 8 | public class WorkingDirectoryOptionTests : DirectoryOptionTests 9 | { 10 | protected override string CurrentDirectory => "/current-directory".ToOSPath(); 11 | protected override string ExpectedDefaultValue => "/current-directory".ToOSPath(); 12 | protected override string InputValue => "folder".ToOSPath(); 13 | protected override string ExpectedValue => "/current-directory/folder".ToOSPath(); 14 | 15 | protected override WorkingDirectoryOption Create() 16 | { 17 | return new WorkingDirectoryOption( 18 | NullLogger.Instance, 19 | MockFileSystem); 20 | } 21 | 22 | protected override void VerifyDefaultValue(WorkingDirectoryOption sut) 23 | { 24 | base.VerifyDefaultValue(sut); 25 | MockFileSystem.Directory.GetCurrentDirectory().Should().Be(CurrentDirectory); 26 | } 27 | 28 | protected override void VerifyInputValue(WorkingDirectoryOption sut) 29 | { 30 | base.VerifyInputValue(sut); 31 | MockFileSystem.Directory.GetCurrentDirectory().Should().Be(ExpectedValue); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/CommandLine/StringOptionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using MiniCover.CommandLine; 3 | using Xunit; 4 | 5 | namespace MiniCover.UnitTests.CommandLine 6 | { 7 | public class StringOptionTests 8 | { 9 | [Fact] 10 | public void ShouldHaveProperties() 11 | { 12 | var sut = Create(); 13 | sut.Name.Should().Be("--template"); 14 | sut.ShortName.Should().Be("-t"); 15 | sut.Description.Should().Be("Description"); 16 | } 17 | 18 | [Fact] 19 | public void NullValue_ShouldReturnDefaultValue() 20 | { 21 | var sut = Create(); 22 | sut.ReceiveValue(null); 23 | sut.Value.Should().BeNull(); 24 | } 25 | 26 | [Fact] 27 | public void Value_ShouldReturnValue() 28 | { 29 | var sut = Create(); 30 | sut.ReceiveValue("Something"); 31 | sut.Value.Should().Be("Something"); 32 | } 33 | 34 | private static StringOption Create() 35 | { 36 | return new StringOption("--template", "Description") 37 | { 38 | ShortName = "-t" 39 | }; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/MiniCover.UnitTests/MiniCover.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | all 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | --------------------------------------------------------------------------------