Skip to content

Commit ec2bc85

Browse files
MarcoRossignolidaveMueller
authored andcommitted
Fix resolving assemblies from frameworks not referenced by coverlet itself
1 parent 3ad4fa1 commit ec2bc85

File tree

23 files changed

+401
-45
lines changed

23 files changed

+401
-45
lines changed

Documentation/ReleasePlan.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ We release 3 components as NuGet packages:
2323

2424
| Package | Version |
2525
|:----------------------|:--------|
26-
|**coverlet.msbuild** | 3.2.0 |
27-
|**coverlet.console** | 3.2.0 |
28-
|**coverlet.collector** | 3.2.0 |
26+
|**coverlet.msbuild** | 6.0.0 |
27+
|**coverlet.console** | 6.0.0 |
28+
|**coverlet.collector** | 6.0.0 |
2929

3030

3131
| Release Date | coverlet.msbuild | coverlet.console | coverlet.collector| commit hash | notes |
3232
| :-----------------|:-----------------|:------------------|:------------------|:-----------------------------------------|:-------------------------------|
33+
| 05 May 2023 | 6.0.0 | 6.0.0 | 6.0.0 | 3ad4fa1d5cd7ffe206c0cb9dc805ee6ca5a7b550 | Version aligned with github one|
3334
| 29 Oct 2022 | 3.2.0 | 3.2.0 | 3.2.0 | e2c9d84a84a9d2d240ac15feb70f9198c6f8e173 | |
3435
| 06 Feb 2022 | 3.1.2 | 3.1.2 | 3.1.2 | e335b1a8025e49e2f2de6b40ef12ec9d3ed11ceb | Fix CoreLib coverage issues |
3536
| 30 Jan 2022 | 3.1.1 | 3.1.1 | 3.1.1 | e4278c06faba63122a870df15a1a1b934f6bc81d | |

coverlet.sln

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9A8B19D4
5454
test\Directory.Build.targets = test\Directory.Build.targets
5555
EndProjectSection
5656
EndProject
57-
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "coverlet.tests.projectsample.vbmynamespace", "test\coverlet.tests.projectsample.vbmynamespace\coverlet.tests.projectsample.vbmynamespace.vbproj", "{C9B7DC34-3E04-4F20-AED4-73791AF8020D}"
57+
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "coverlet.tests.projectsample.vbmynamespace", "test\coverlet.tests.projectsample.vbmynamespace\coverlet.tests.projectsample.vbmynamespace.vbproj", "{C9B7DC34-3E04-4F20-AED4-73791AF8020D}"
5858
EndProject
5959
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "coverlet.tests.projectsample.fsharp", "test\coverlet.tests.projectsample.fsharp\coverlet.tests.projectsample.fsharp.fsproj", "{1CBF6966-2A67-4D2C-8598-D174B83072F4}"
6060
EndProject
6161
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.netframework", "test\coverlet.tests.projectsample.netframework\coverlet.tests.projectsample.netframework.csproj", "{E69D68C9-78ED-4076-A14B-D07295A4B2A5}"
6262
EndProject
63+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.aspnet5", "test\coverlet.tests.projectsample.aspnet5\coverlet.tests.projectsample.aspnet5.csproj", "{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}"
64+
EndProject
65+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.aspnet5.tests", "test\coverlet.tests.projectsample.aspnet5.tests\coverlet.tests.projectsample.aspnet5.tests.csproj", "{8EC065A4-7700-45E6-8B90-0182E3649DEA}"
66+
EndProject
67+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.wpf", "test\coverlet.tests.projectsample.wpf\coverlet.tests.projectsample.wpf.csproj", "{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}"
68+
EndProject
6369
Global
6470
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6571
Debug|Any CPU = Debug|Any CPU
@@ -134,6 +140,18 @@ Global
134140
{E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
135141
{E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
136142
{E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.Build.0 = Release|Any CPU
143+
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
144+
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
145+
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
146+
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Release|Any CPU.Build.0 = Release|Any CPU
147+
{8EC065A4-7700-45E6-8B90-0182E3649DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
148+
{8EC065A4-7700-45E6-8B90-0182E3649DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
149+
{8EC065A4-7700-45E6-8B90-0182E3649DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
150+
{8EC065A4-7700-45E6-8B90-0182E3649DEA}.Release|Any CPU.Build.0 = Release|Any CPU
151+
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
152+
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}.Debug|Any CPU.Build.0 = Debug|Any CPU
153+
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}.Release|Any CPU.ActiveCfg = Release|Any CPU
154+
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}.Release|Any CPU.Build.0 = Release|Any CPU
137155
EndGlobalSection
138156
GlobalSection(SolutionProperties) = preSolution
139157
HideSolutionNode = FALSE
@@ -154,9 +172,12 @@ Global
154172
{5FF404AD-7C0B-465A-A1E9-558CDC642B0C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
155173
{F8199E19-FA9A-4559-9101-CAD7028121B4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
156174
{9A8B19D4-4A24-4217-AEFE-159B68F029A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
175+
{C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
157176
{1CBF6966-2A67-4D2C-8598-D174B83072F4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
158177
{E69D68C9-78ED-4076-A14B-D07295A4B2A5} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
159-
{C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
178+
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
179+
{8EC065A4-7700-45E6-8B90-0182E3649DEA} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
180+
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
160181
EndGlobalSection
161182
GlobalSection(ExtensibilityGlobals) = postSolution
162183
SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10}

src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using Microsoft.Extensions.DependencyModel;
1111
using Microsoft.Extensions.DependencyModel.Resolution;
1212
using Mono.Cecil;
13+
using Newtonsoft.Json;
14+
using Newtonsoft.Json.Linq;
1315

1416
namespace Coverlet.Core.Instrumentation
1517
{
@@ -70,14 +72,14 @@ public NetstandardAwareAssemblyResolver(string modulePath, ILogger logger)
7072
_modulePath = modulePath;
7173
_logger = logger;
7274

73-
// this is lazy because we cannot create AspNetCoreSharedFrameworkResolver if not on .NET Core runtime,
75+
// this is lazy because we cannot create NetCoreSharedFrameworkResolver if not on .NET Core runtime,
7476
// runtime folders are different
7577
_compositeResolver = new Lazy<CompositeCompilationAssemblyResolver>(() => new CompositeCompilationAssemblyResolver(new ICompilationAssemblyResolver[]
7678
{
7779
new AppBaseCompilationAssemblyResolver(),
78-
new ReferenceAssemblyPathResolver(),
7980
new PackageCompilationAssemblyResolver(),
80-
new AspNetCoreSharedFrameworkResolver(_logger)
81+
new NetCoreSharedFrameworkResolver(modulePath, _logger),
82+
new ReferenceAssemblyPathResolver(),
8183
}), true);
8284
}
8385

@@ -216,23 +218,37 @@ internal AssemblyDefinition TryWithCustomResolverOnDotNetCore(AssemblyNameRefere
216218
}
217219
}
218220

219-
internal class AspNetCoreSharedFrameworkResolver : ICompilationAssemblyResolver
221+
internal class NetCoreSharedFrameworkResolver : ICompilationAssemblyResolver
220222
{
221-
private readonly string[] _aspNetSharedFrameworkDirs;
223+
private readonly List<string> _aspNetSharedFrameworkDirs = new();
222224
private readonly ILogger _logger;
223225

224-
public AspNetCoreSharedFrameworkResolver(ILogger logger)
226+
public NetCoreSharedFrameworkResolver(string modulePath, ILogger logger)
225227
{
226228
_logger = logger;
227-
string runtimeRootPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
228-
string runtimeVersion = runtimeRootPath.Substring(runtimeRootPath.LastIndexOf(Path.DirectorySeparatorChar) + 1);
229-
_aspNetSharedFrameworkDirs = new string[]
229+
230+
string runtimeConfigFile = Path.Combine(
231+
Path.GetDirectoryName(modulePath)!,
232+
Path.GetFileNameWithoutExtension(modulePath) + ".runtimeconfig.json");
233+
if (!File.Exists(runtimeConfigFile))
234+
{
235+
return;
236+
}
237+
238+
var reader = new RuntimeConfigurationReader(runtimeConfigFile);
239+
IEnumerable<(string Name, string Version)> referencedFrameworks = reader.GetFrameworks();
240+
string runtimePath = Path.GetDirectoryName(typeof(object).Assembly.Location);
241+
string runtimeRootPath = Path.Combine(runtimePath!, "../..");
242+
foreach ((string frameworkName, string frameworkVersion) in referencedFrameworks)
230243
{
231-
Path.GetFullPath(Path.Combine(runtimeRootPath,"../../Microsoft.AspNetCore.All", runtimeVersion)),
232-
Path.GetFullPath(Path.Combine(runtimeRootPath, "../../Microsoft.AspNetCore.App", runtimeVersion))
233-
};
244+
var majorVersion = string.Join(".", frameworkVersion.Split('.').Take(2)) + ".";
245+
var directory = new DirectoryInfo(Path.Combine(runtimeRootPath, frameworkName));
246+
var latestVersion = directory.GetDirectories().Where(x => x.Name.StartsWith(majorVersion))
247+
.Select(x => Convert.ToUInt32(x.Name.Substring(majorVersion.Length))).Max();
248+
_aspNetSharedFrameworkDirs.Add(Path.Combine(directory.FullName, majorVersion + latestVersion));
249+
}
234250

235-
_logger.LogVerbose("AspNetCoreSharedFrameworkResolver search paths:");
251+
_logger.LogVerbose("NetCoreSharedFrameworkResolver search paths:");
236252
foreach (string searchPath in _aspNetSharedFrameworkDirs)
237253
{
238254
_logger.LogVerbose(searchPath);
@@ -250,7 +266,8 @@ public bool TryResolveAssemblyPaths(CompilationLibrary library, List<string> ass
250266
continue;
251267
}
252268

253-
foreach (string file in Directory.GetFiles(sharedFrameworkPath))
269+
string[] files = Directory.GetFiles(sharedFrameworkPath);
270+
foreach (string file in files)
254271
{
255272
if (Path.GetFileName(file).Equals(dllName, StringComparison.OrdinalIgnoreCase))
256273
{
@@ -264,4 +281,36 @@ public bool TryResolveAssemblyPaths(CompilationLibrary library, List<string> ass
264281
return false;
265282
}
266283
}
284+
285+
internal class RuntimeConfigurationReader
286+
{
287+
private readonly string _runtimeConfigFile;
288+
289+
public RuntimeConfigurationReader(string runtimeConfigFile)
290+
{
291+
_runtimeConfigFile = runtimeConfigFile;
292+
}
293+
294+
public IEnumerable<(string Name, string Version)> GetFrameworks()
295+
{
296+
JObject configuration =
297+
new JsonSerializer().Deserialize<JObject>(
298+
new JsonTextReader(new StringReader(File.ReadAllText(_runtimeConfigFile))));
299+
300+
JToken runtimeOptions = configuration["runtimeOptions"];
301+
JToken framework = runtimeOptions?["framework"];
302+
if (framework != null)
303+
{
304+
return new[] {(framework["name"].Value<string>(), framework["version"].Value<string>())};
305+
}
306+
307+
JToken frameworks = runtimeOptions?["frameworks"];
308+
if (frameworks != null)
309+
{
310+
return frameworks.Select(x => (x["name"].Value<string>(), x["version"].Value<string>()));
311+
}
312+
313+
throw new InvalidOperationException($"Unable to read runtime configuration from {_runtimeConfigFile}.");
314+
}
315+
}
267316
}

src/coverlet.core/Instrumentation/InstrumenterResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics;
7+
using System.Diagnostics.CodeAnalysis;
78
using System.Runtime.Serialization;
89

910
namespace Coverlet.Core.Instrumentation
@@ -79,6 +80,7 @@ public Document()
7980

8081
[DebuggerDisplay("isBranch = {isBranch} docIndex = {docIndex} start = {start} end = {end}")]
8182
[DataContract]
83+
[SuppressMessage("Style", "IDE1006", Justification = "suppress casing error for API compatibility")]
8284
internal class HitCandidate
8385
{
8486
public HitCandidate(bool isBranch, int docIndex, int start, int end) => (this.isBranch, this.docIndex, this.start, this.end) = (isBranch, docIndex, start, end);

src/coverlet.core/Properties/AssemblyInfo.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@
1313
[assembly: InternalsVisibleTo("coverlet.core.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")]
1414
[assembly: InternalsVisibleTo("coverlet.collector.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ed0ed6af9693182615b8dcadc83c918b8d36312f86cefc69539d67d4189cd1b89420e7c3871802ffef7f5ca7816c68ad856c77bf7c230cc07824d96aa5d1237eebd30e246b9a14e22695fb26b40c800f74ea96619092cbd3a5d430d6c003fc7a82e8ccd1e315b935105d9232fe9e99e8d7ff54bba6f191959338d4a3169df9b3")]
1515
[assembly: InternalsVisibleTo("coverlet.integration.tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010001d24efbe9cbc2dc49b7a3d2ae34ca37cfb69b4f450acd768a22ce5cd021c8a38ae7dc68b2809a1ac606ad531b578f192a5690b2986990cbda4dd84ec65a3a4c1c36f6d7bb18f08592b93091535eaee2f0c8e48763ed7f190db2008e1f9e0facd5c0df5aaab74febd3430e09a428a72e5e6b88357f92d78e47512d46ebdc3cbb")]
16+
[assembly: InternalsVisibleTo("coverlet.tests.projectsample.aspnet5.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")]
17+
[assembly: InternalsVisibleTo("coverlet.tests.projectsample.wpf5.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")]
18+
1619
// Needed to mock internal type https://github.com/Moq/moq4/wiki/Quickstart#advanced-features
17-
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
20+
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -619,24 +619,6 @@ public int SampleMethod()
619619
if (expectedExcludes) { loggerMock.Verify(l => l.LogVerbose(It.IsAny<string>())); }
620620
}
621621

622-
[Fact]
623-
public void TestInstrument_AspNetCoreSharedFrameworkResolver()
624-
{
625-
var resolver = new AspNetCoreSharedFrameworkResolver(_mockLogger.Object);
626-
var compilationLibrary = new CompilationLibrary(
627-
"package",
628-
"Microsoft.Extensions.Logging.Abstractions",
629-
"2.2.0",
630-
"sha512-B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==",
631-
Enumerable.Empty<string>(),
632-
Enumerable.Empty<Dependency>(),
633-
true);
634-
635-
var assemblies = new List<string>();
636-
Assert.True(resolver.TryResolveAssemblyPaths(compilationLibrary, assemblies));
637-
Assert.NotEmpty(assemblies);
638-
}
639-
640622
[Fact]
641623
public void TestInstrument_NetstandardAwareAssemblyResolver_PreserveCompilationContext()
642624
{
@@ -740,15 +722,15 @@ public void TestReachabilityHelper()
740722
new[]
741723
{
742724
// Throws
743-
7, 8,
725+
7, 8,
744726
// NoBranches
745-
12, 13, 14, 15, 16,
727+
12, 13, 14, 15, 16,
746728
// If
747-
19, 20, 22, 23, 24, 25, 26, 27, 29, 30,
729+
19, 20, 22, 23, 24, 25, 26, 27, 29, 30,
748730
// Switch
749-
33, 34, 36, 39, 40, 41, 42, 44, 45, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 64, 65, 68, 69,
731+
33, 34, 36, 39, 40, 41, 42, 44, 45, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 64, 65, 68, 69,
750732
// Subtle
751-
72, 73, 75, 78, 79, 80, 82, 83, 86, 87, 88, 91, 92, 95, 96, 98, 99, 101, 102, 103,
733+
72, 73, 75, 78, 79, 80, 82, 83, 86, 87, 88, 91, 92, 95, 96, 98, 99, 101, 102, 103,
752734
// UnreachableBranch
753735
106, 107, 108, 110, 111, 112, 113, 114,
754736
// ThrowsGeneric
@@ -774,7 +756,7 @@ public void TestReachabilityHelper()
774756
// Switch
775757
41, 42,
776758
// Subtle
777-
79, 80, 88, 96, 98, 99,
759+
79, 80, 88, 96, 98, 99,
778760
// UnreachableBranch
779761
110, 111, 112, 113, 114,
780762
// CallsGenericMethodDoesNotReturn
@@ -822,7 +804,7 @@ public void Instrumenter_MethodsWithoutReferenceToSource_AreSkipped()
822804

823805
var instrumenter = new Instrumenter(Path.Combine(directory.FullName, Path.GetFileName(module)), "_coverlet_tests_projectsample_vbmynamespace", parameters,
824806
loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(Path.Combine(directory.FullName, Path.GetFileName(module)), loggerMock.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper());
825-
807+
826808
instrumentationHelper.BackupOriginalModule(Path.Combine(directory.FullName, Path.GetFileName(module)), "_coverlet_tests_projectsample_vbmynamespace");
827809

828810
InstrumenterResult result = instrumenter.Instrument();

0 commit comments

Comments
 (0)