Skip to content

Local fix net6 wpf #1468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Documentation/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Fixed
-Fix problem with coverage for .net5 WPF application [#1221](https://github.com/coverlet-coverage/coverlet/issues/1221) by https://github.com/lg2de
-Fix unable to instrument module for Microsoft.AspNetCore.Mvc.Razor [#1459](https://github.com/coverlet-coverage/coverlet/issues/1459) by https://github.com/lg2de

## Release date 2023-05-21
### Packages
coverlet.msbuild 6.0.0
Expand Down
25 changes: 23 additions & 2 deletions coverlet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9A8B19D4
test\Directory.Build.targets = test\Directory.Build.targets
EndProjectSection
EndProject
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}"
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}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -134,6 +140,18 @@ Global
{E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.Build.0 = Release|Any CPU
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Release|Any CPU.Build.0 = Release|Any CPU
{8EC065A4-7700-45E6-8B90-0182E3649DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8EC065A4-7700-45E6-8B90-0182E3649DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8EC065A4-7700-45E6-8B90-0182E3649DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8EC065A4-7700-45E6-8B90-0182E3649DEA}.Release|Any CPU.Build.0 = Release|Any CPU
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -154,9 +172,12 @@ Global
{5FF404AD-7C0B-465A-A1E9-558CDC642B0C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{F8199E19-FA9A-4559-9101-CAD7028121B4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{9A8B19D4-4A24-4217-AEFE-159B68F029A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{1CBF6966-2A67-4D2C-8598-D174B83072F4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{E69D68C9-78ED-4076-A14B-D07295A4B2A5} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{8EC065A4-7700-45E6-8B90-0182E3649DEA} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{D2CB5A5E-2483-460B-A713-F5CBDD16D87C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10}
Expand Down
75 changes: 62 additions & 13 deletions src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.DependencyModel.Resolution;
using Mono.Cecil;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Coverlet.Core.Instrumentation
{
Expand Down Expand Up @@ -70,14 +72,14 @@ public NetstandardAwareAssemblyResolver(string modulePath, ILogger logger)
_modulePath = modulePath;
_logger = logger;

// this is lazy because we cannot create AspNetCoreSharedFrameworkResolver if not on .NET Core runtime,
// this is lazy because we cannot create NetCoreSharedFrameworkResolver if not on .NET Core runtime,
// runtime folders are different
_compositeResolver = new Lazy<CompositeCompilationAssemblyResolver>(() => new CompositeCompilationAssemblyResolver(new ICompilationAssemblyResolver[]
{
new AppBaseCompilationAssemblyResolver(),
new NetCoreSharedFrameworkResolver(modulePath, _logger),
new ReferenceAssemblyPathResolver(),
new PackageCompilationAssemblyResolver(),
new AspNetCoreSharedFrameworkResolver(_logger)
}), true);
}

Expand Down Expand Up @@ -216,23 +218,37 @@ internal AssemblyDefinition TryWithCustomResolverOnDotNetCore(AssemblyNameRefere
}
}

internal class AspNetCoreSharedFrameworkResolver : ICompilationAssemblyResolver
internal class NetCoreSharedFrameworkResolver : ICompilationAssemblyResolver
{
private readonly string[] _aspNetSharedFrameworkDirs;
private readonly List<string> _aspNetSharedFrameworkDirs = new();
private readonly ILogger _logger;

public AspNetCoreSharedFrameworkResolver(ILogger logger)
public NetCoreSharedFrameworkResolver(string modulePath, ILogger logger)
{
_logger = logger;
string runtimeRootPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
string runtimeVersion = runtimeRootPath.Substring(runtimeRootPath.LastIndexOf(Path.DirectorySeparatorChar) + 1);
_aspNetSharedFrameworkDirs = new string[]

string runtimeConfigFile = Path.Combine(
Path.GetDirectoryName(modulePath)!,
Path.GetFileNameWithoutExtension(modulePath) + ".runtimeconfig.json");
if (!File.Exists(runtimeConfigFile))
{
return;
}

var reader = new RuntimeConfigurationReader(runtimeConfigFile);
IEnumerable<(string Name, string Version)> referencedFrameworks = reader.GetFrameworks();
string runtimePath = Path.GetDirectoryName(typeof(object).Assembly.Location);
string runtimeRootPath = Path.Combine(runtimePath!, "../..");
foreach ((string frameworkName, string frameworkVersion) in referencedFrameworks)
{
Path.GetFullPath(Path.Combine(runtimeRootPath,"../../Microsoft.AspNetCore.All", runtimeVersion)),
Path.GetFullPath(Path.Combine(runtimeRootPath, "../../Microsoft.AspNetCore.App", runtimeVersion))
};
var majorVersion = string.Join(".", frameworkVersion.Split('.').Take(2)) + ".";
var directory = new DirectoryInfo(Path.Combine(runtimeRootPath, frameworkName));
var latestVersion = directory.GetDirectories().Where(x => x.Name.StartsWith(majorVersion))
.Select(x => Convert.ToUInt32(x.Name.Substring(majorVersion.Length))).Max();
_aspNetSharedFrameworkDirs.Add(Path.Combine(directory.FullName, majorVersion + latestVersion));
}

_logger.LogVerbose("AspNetCoreSharedFrameworkResolver search paths:");
_logger.LogVerbose("NetCoreSharedFrameworkResolver search paths:");
foreach (string searchPath in _aspNetSharedFrameworkDirs)
{
_logger.LogVerbose(searchPath);
Expand All @@ -250,7 +266,8 @@ public bool TryResolveAssemblyPaths(CompilationLibrary library, List<string> ass
continue;
}

foreach (string file in Directory.GetFiles(sharedFrameworkPath))
string[] files = Directory.GetFiles(sharedFrameworkPath);
foreach (string file in files)
{
if (Path.GetFileName(file).Equals(dllName, StringComparison.OrdinalIgnoreCase))
{
Expand All @@ -264,4 +281,36 @@ public bool TryResolveAssemblyPaths(CompilationLibrary library, List<string> ass
return false;
}
}

internal class RuntimeConfigurationReader
{
private readonly string _runtimeConfigFile;

public RuntimeConfigurationReader(string runtimeConfigFile)
{
_runtimeConfigFile = runtimeConfigFile;
}

public IEnumerable<(string Name, string Version)> GetFrameworks()
{
JObject configuration =
new JsonSerializer().Deserialize<JObject>(
new JsonTextReader(new StringReader(File.ReadAllText(_runtimeConfigFile))));

JToken runtimeOptions = configuration["runtimeOptions"];
JToken framework = runtimeOptions?["framework"];
if (framework != null)
{
return new[] {(framework["name"].Value<string>(), framework["version"].Value<string>())};
}

JToken frameworks = runtimeOptions?["frameworks"];
if (frameworks != null)
{
return frameworks.Select(x => (x["name"].Value<string>(), x["version"].Value<string>()));
}

throw new InvalidOperationException($"Unable to read runtime configuration from {_runtimeConfigFile}.");
}
}
}
2 changes: 2 additions & 0 deletions src/coverlet.core/Instrumentation/InstrumenterResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;

namespace Coverlet.Core.Instrumentation
Expand Down Expand Up @@ -79,6 +80,7 @@ public Document()

[DebuggerDisplay("isBranch = {isBranch} docIndex = {docIndex} start = {start} end = {end}")]
[DataContract]
[SuppressMessage("Style", "IDE1006", Justification = "suppress casing error for API compatibility")]
internal class HitCandidate
{
public HitCandidate(bool isBranch, int docIndex, int start, int end) => (this.isBranch, this.docIndex, this.start, this.end) = (isBranch, docIndex, start, end);
Expand Down
5 changes: 4 additions & 1 deletion src/coverlet.core/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@
[assembly: InternalsVisibleTo("coverlet.core.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")]
[assembly: InternalsVisibleTo("coverlet.collector.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ed0ed6af9693182615b8dcadc83c918b8d36312f86cefc69539d67d4189cd1b89420e7c3871802ffef7f5ca7816c68ad856c77bf7c230cc07824d96aa5d1237eebd30e246b9a14e22695fb26b40c800f74ea96619092cbd3a5d430d6c003fc7a82e8ccd1e315b935105d9232fe9e99e8d7ff54bba6f191959338d4a3169df9b3")]
[assembly: InternalsVisibleTo("coverlet.integration.tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010001d24efbe9cbc2dc49b7a3d2ae34ca37cfb69b4f450acd768a22ce5cd021c8a38ae7dc68b2809a1ac606ad531b578f192a5690b2986990cbda4dd84ec65a3a4c1c36f6d7bb18f08592b93091535eaee2f0c8e48763ed7f190db2008e1f9e0facd5c0df5aaab74febd3430e09a428a72e5e6b88357f92d78e47512d46ebdc3cbb")]
[assembly: InternalsVisibleTo("coverlet.tests.projectsample.aspnet5.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")]
[assembly: InternalsVisibleTo("coverlet.tests.projectsample.wpf5.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")]

// Needed to mock internal type https://github.com/Moq/moq4/wiki/Quickstart#advanced-features
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
32 changes: 7 additions & 25 deletions test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -619,24 +619,6 @@ public int SampleMethod()
if (expectedExcludes) { loggerMock.Verify(l => l.LogVerbose(It.IsAny<string>())); }
}

[Fact]
public void TestInstrument_AspNetCoreSharedFrameworkResolver()
{
var resolver = new AspNetCoreSharedFrameworkResolver(_mockLogger.Object);
var compilationLibrary = new CompilationLibrary(
"package",
"Microsoft.Extensions.Logging.Abstractions",
"2.2.0",
"sha512-B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==",
Enumerable.Empty<string>(),
Enumerable.Empty<Dependency>(),
true);

var assemblies = new List<string>();
Assert.True(resolver.TryResolveAssemblyPaths(compilationLibrary, assemblies));
Assert.NotEmpty(assemblies);
}

[Fact]
public void TestInstrument_NetstandardAwareAssemblyResolver_PreserveCompilationContext()
{
Expand Down Expand Up @@ -740,15 +722,15 @@ public void TestReachabilityHelper()
new[]
{
// Throws
7, 8,
7, 8,
// NoBranches
12, 13, 14, 15, 16,
12, 13, 14, 15, 16,
// If
19, 20, 22, 23, 24, 25, 26, 27, 29, 30,
19, 20, 22, 23, 24, 25, 26, 27, 29, 30,
// Switch
33, 34, 36, 39, 40, 41, 42, 44, 45, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 64, 65, 68, 69,
33, 34, 36, 39, 40, 41, 42, 44, 45, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 64, 65, 68, 69,
// Subtle
72, 73, 75, 78, 79, 80, 82, 83, 86, 87, 88, 91, 92, 95, 96, 98, 99, 101, 102, 103,
72, 73, 75, 78, 79, 80, 82, 83, 86, 87, 88, 91, 92, 95, 96, 98, 99, 101, 102, 103,
// UnreachableBranch
106, 107, 108, 110, 111, 112, 113, 114,
// ThrowsGeneric
Expand All @@ -774,7 +756,7 @@ public void TestReachabilityHelper()
// Switch
41, 42,
// Subtle
79, 80, 88, 96, 98, 99,
79, 80, 88, 96, 98, 99,
// UnreachableBranch
110, 111, 112, 113, 114,
// CallsGenericMethodDoesNotReturn
Expand Down Expand Up @@ -822,7 +804,7 @@ public void Instrumenter_MethodsWithoutReferenceToSource_AreSkipped()

var instrumenter = new Instrumenter(Path.Combine(directory.FullName, Path.GetFileName(module)), "_coverlet_tests_projectsample_vbmynamespace", parameters,
loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(Path.Combine(directory.FullName, Path.GetFileName(module)), loggerMock.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper());

instrumentationHelper.BackupOriginalModule(Path.Combine(directory.FullName, Path.GetFileName(module)), "_coverlet_tests_projectsample_vbmynamespace");

InstrumenterResult result = instrumenter.Instrument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,26 +123,24 @@ public void MutexBlocksMultipleWriters()
FunctionExecutor.Run(async () =>
{
using var ctx = new TrackerContext();
using (var mutex = new Mutex(
true, Path.GetFileNameWithoutExtension(ModuleTrackerTemplate.HitsFilePath) + "_Mutex", out bool createdNew))
{
Assert.True(createdNew);
using var mutex = new Mutex(
true, Path.GetFileNameWithoutExtension(ModuleTrackerTemplate.HitsFilePath) + "_Mutex", out bool createdNew);
Assert.True(createdNew);

ModuleTrackerTemplate.HitsArray = new[] { 0, 1, 2, 3 };
var unloadTask = Task.Run(() => ModuleTrackerTemplate.UnloadModule(null, null));
ModuleTrackerTemplate.HitsArray = new[] { 0, 1, 2, 3 };
var unloadTask = Task.Run(() => ModuleTrackerTemplate.UnloadModule(null, null));

Assert.False(unloadTask.Wait(5));
Assert.False(unloadTask.Wait(5));

WriteHitsFile(new[] { 0, 3, 2, 1 });
WriteHitsFile(new[] { 0, 3, 2, 1 });

Assert.False(unloadTask.Wait(5));
Assert.False(unloadTask.Wait(5));

mutex.ReleaseMutex();
await unloadTask;
mutex.ReleaseMutex();
await unloadTask;

int[] expectedHitsArray = new[] { 0, 4, 4, 4 };
Assert.Equal(expectedHitsArray, ReadHitsFile());
}
int[] expectedHitsArray = new[] { 0, 4, 4, 4 };
Assert.Equal(expectedHitsArray, ReadHitsFile());

return 0;
});
Expand Down
Loading