├── .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