Skip to content

Commit 909249c

Browse files
merge master
2 parents e39eca5 + 7feb550 commit 909249c

File tree

16 files changed

+391
-225
lines changed

16 files changed

+391
-225
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,12 +434,27 @@ These steps must be followed before you attempt to open the solution in an IDE (
434434
435435
### Performance testing
436436
437-
There is a performance test for the hit counting instrumentation in the test project `coverlet.core.performancetest`. Build the project with the msbuild step above and then run:
437+
There is a simple performance test for the hit counting instrumentation in the test project `coverlet.core.performancetest`. Build the project with the msbuild step above and then run:
438438
439439
dotnet test /p:CollectCoverage=true test/coverlet.core.performancetest/
440440
441441
The duration of the test can be tweaked by changing the number of iterations in the `[InlineData]` in the `PerformanceTest` class.
442442
443+
For more realistic testing it is recommended to try out any changes to the hit counting code paths on large, realistic projects. If you don't have any handy https://github.com/dotnet/corefx is an excellent candidate. [This page](https://github.com/dotnet/corefx/blob/master/Documentation/building/code-coverage.md) describes how to run code coverage tests for both the full solution and for individual projects with coverlet from nuget. Suitable projects (listed in order of escalating test durations):
444+
445+
* System.Collections.Concurrent.Tests
446+
* System.Collections.Tests
447+
* System.Reflection.Metadata.Tests
448+
* System.Xml.Linq.Events.Tests
449+
* System.Runtime.Serialization.Formatters.Tests
450+
451+
Change to the directory of the library and run the msbuild code coverage command:
452+
453+
dotnet msbuild /t:BuildAndTest /p:Coverage=true
454+
455+
To run with a development version of coverlet call `dotnet run` instead of the installed coverlet version, e.g.:
456+
457+
dotnet msbuild /t:BuildAndTest /p:Coverage=true /p:CoverageExecutablePath="dotnet run -p C:\...\coverlet\src\coverlet.console\coverlet.console.csproj"
443458
444459
## Code of Conduct
445460

codecov.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# https://docs.codecov.io/docs/codecov-yaml
2+
# https://github.com/codecov/support/wiki/Codecov-Yaml
3+
4+
coverage:
5+
status:
6+
project:
7+
default: false
8+
patch:
9+
default: false
10+
11+
comment:
12+
layout: "diff"
13+
14+
flags:
15+
production:
16+
paths:
17+
- src/
18+
test:
19+
paths:
20+
- test/

coverlet.sln

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test
1515
EndProject
1616
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}"
1717
EndProject
18-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.testsubject", "test\coverlet.testsubject\coverlet.testsubject.csproj", "{AE117FAA-C21D-4F23-917E-0C8050614750}"
18+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.testsubject", "test\coverlet.testsubject\coverlet.testsubject.csproj", "{AE117FAA-C21D-4F23-917E-0C8050614750}"
1919
EndProject
20-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}"
20+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}"
21+
EndProject
22+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.template", "src\coverlet.template\coverlet.template.csproj", "{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}"
2123
EndProject
2224
Global
2325
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -101,6 +103,18 @@ Global
101103
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x64.Build.0 = Release|Any CPU
102104
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x86.ActiveCfg = Release|Any CPU
103105
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x86.Build.0 = Release|Any CPU
106+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
107+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|Any CPU.Build.0 = Debug|Any CPU
108+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|x64.ActiveCfg = Debug|Any CPU
109+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|x64.Build.0 = Debug|Any CPU
110+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|x86.ActiveCfg = Debug|Any CPU
111+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Debug|x86.Build.0 = Debug|Any CPU
112+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|Any CPU.ActiveCfg = Release|Any CPU
113+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|Any CPU.Build.0 = Release|Any CPU
114+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|x64.ActiveCfg = Release|Any CPU
115+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|x64.Build.0 = Release|Any CPU
116+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|x86.ActiveCfg = Release|Any CPU
117+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254}.Release|x86.Build.0 = Release|Any CPU
104118
EndGlobalSection
105119
GlobalSection(SolutionProperties) = preSolution
106120
HideSolutionNode = FALSE
@@ -112,6 +126,7 @@ Global
112126
{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
113127
{AE117FAA-C21D-4F23-917E-0C8050614750} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
114128
{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
129+
{FF16BD00-4BE7-41F3-95AE-F69B56E5E254} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}
115130
EndGlobalSection
116131
GlobalSection(ExtensibilityGlobals) = postSolution
117132
SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10}

src/coverlet.console/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ static int Main(string[] args)
5656
process.StartInfo.Arguments = targs.HasValue() ? targs.Value() : string.Empty;
5757
process.StartInfo.CreateNoWindow = true;
5858
process.StartInfo.RedirectStandardOutput = true;
59+
process.StartInfo.RedirectStandardError = true;
5960
process.Start();
6061
logger.LogInformation(process.StandardOutput.ReadToEnd());
62+
logger.LogError(process.StandardError.ReadToEnd());
6163
process.WaitForExit();
6264

6365
var dOutput = output.HasValue() ? output.Value() : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString();

src/coverlet.core/Coverage.cs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.IO.MemoryMappedFiles;
45
using System.Linq;
56

67
using Coverlet.Core.Enums;
@@ -26,11 +27,15 @@ public class Coverage
2627
private bool _useSourceLink;
2728
private List<InstrumenterResult> _results;
2829

30+
private readonly Dictionary<string, MemoryMappedFile> _resultMemoryMaps = new Dictionary<string, MemoryMappedFile>();
31+
2932
public string Identifier
3033
{
3134
get { return _identifier; }
3235
}
3336

37+
internal IEnumerable<InstrumenterResult> Results => _results;
38+
3439
public Coverage(string module, string[] includeFilters, string[] includeDirectories, string[] excludeFilters, string[] excludedSourceFiles, string[] excludeAttributes, string mergeWith, bool useSourceLink)
3540
{
3641
_module = module;
@@ -77,6 +82,26 @@ public void PrepareModules()
7782
}
7883
}
7984
}
85+
86+
foreach (var result in _results)
87+
{
88+
var size = (result.HitCandidates.Count + ModuleTrackerTemplate.HitsResultHeaderSize) * sizeof(int);
89+
90+
MemoryMappedFile mmap;
91+
92+
try
93+
{
94+
// Try using a named memory map not backed by a file (currently only supported on Windows)
95+
mmap = MemoryMappedFile.CreateNew(result.HitsResultGuid, size);
96+
}
97+
catch (PlatformNotSupportedException)
98+
{
99+
// Fall back on a file-backed memory map
100+
mmap = MemoryMappedFile.CreateFromFile(result.HitsFilePath, FileMode.CreateNew, null, size);
101+
}
102+
103+
_resultMemoryMaps.Add(result.HitsResultGuid, mmap);
104+
}
80105
}
81106

82107
public CoverageResult GetCoverageResult()
@@ -183,12 +208,6 @@ private void CalculateCoverage()
183208
{
184209
foreach (var result in _results)
185210
{
186-
if (!File.Exists(result.HitsFilePath))
187-
{
188-
// File not instrumented, or nothing in it called. Warn about this?
189-
continue;
190-
}
191-
192211
List<Document> documents = result.Documents.Values.ToList();
193212
if (_useSourceLink && result.SourceLink != null)
194213
{
@@ -200,20 +219,26 @@ private void CalculateCoverage()
200219
}
201220
}
202221

203-
using (var fs = new FileStream(result.HitsFilePath, FileMode.Open))
204-
using (var br = new BinaryReader(fs))
222+
// Read hit counts from the memory mapped area, disposing it when done
223+
using (var mmapFile = _resultMemoryMaps[result.HitsResultGuid])
205224
{
206-
int hitCandidatesCount = br.ReadInt32();
225+
var mmapAccessor = mmapFile.CreateViewAccessor();
226+
227+
var unloadStarted = mmapAccessor.ReadInt32(ModuleTrackerTemplate.HitsResultUnloadStarted * sizeof(int));
228+
var unloadFinished = mmapAccessor.ReadInt32(ModuleTrackerTemplate.HitsResultUnloadFinished * sizeof(int));
207229

208-
// TODO: hitCandidatesCount should be verified against result.HitCandidates.Count
230+
if (unloadFinished < unloadStarted)
231+
{
232+
throw new Exception($"Hit counts only partially reported for {result.Module}");
233+
}
209234

210235
var documentsList = result.Documents.Values.ToList();
211236

212-
for (int i = 0; i < hitCandidatesCount; ++i)
237+
for (int i = 0; i < result.HitCandidates.Count; ++i)
213238
{
214239
var hitLocation = result.HitCandidates[i];
215240
var document = documentsList[hitLocation.docIndex];
216-
int hits = br.ReadInt32();
241+
var hits = mmapAccessor.ReadInt32((i + ModuleTrackerTemplate.HitsResultHeaderSize) * sizeof(int));
217242

218243
if (hitLocation.isBranch)
219244
{
@@ -256,6 +281,7 @@ private void CalculateCoverage()
256281
}
257282
}
258283

284+
// There's only a hits file on Linux, but if the file doesn't exist this is just a no-op
259285
InstrumentationHelper.DeleteHitsFile(result.HitsFilePath);
260286
}
261287
}

0 commit comments

Comments
 (0)