From 42dacf08ac81db998723f75282c2bb2095009f7a Mon Sep 17 00:00:00 2001 From: Philipp Feigl <philipp.feigl@cyan-it.de> Date: Mon, 21 Oct 2024 10:23:40 +0200 Subject: [PATCH 1/3] Fix RuntimeConfigurationReader to support SelfContained builds SelfContained builds to not contain the `frameworks` node in the `runtimeconfig.json` file. They do however contain a `includedFrameworks` node. Details can be found here: https://github.com/dotnet/sdk/issues/3541 Running tests against a built assembly with the `SelfContained` option does result in the following often discussed error message > Unable to instrument module --- coverlet.sln | 7 +++++ .../Instrumentation/CecilAssemblyResolver.cs | 5 ++++ .../WpfResolverTests.cs | 28 +++++++++++++++++++ .../.editorconfig | 8 ++++++ .../Program.cs | 9 ++++++ .../TestClass.cs | 12 ++++++++ ...ts.projectsample.wpf8.selfcontained.csproj | 14 ++++++++++ .../Program.cs | 2 +- .../TestClass.cs | 2 +- 9 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 test/coverlet.tests.projectsample.wpf8.selfcontained/.editorconfig create mode 100644 test/coverlet.tests.projectsample.wpf8.selfcontained/Program.cs create mode 100644 test/coverlet.tests.projectsample.wpf8.selfcontained/TestClass.cs create mode 100644 test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj diff --git a/coverlet.sln b/coverlet.sln index 28fd22009..85241f5e4 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -88,6 +88,8 @@ Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "coverlet.tests.projectsampl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.utils", "test\coverlet.tests.utils\coverlet.tests.utils.csproj", "{0B109210-03CB-413F-888C-3023994AA384}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.wpf8.selfcontained", "test\coverlet.tests.projectsample.wpf8.selfcontained\coverlet.tests.projectsample.wpf8.selfcontained.csproj", "{71004336-9896-4AE5-8367-B29BB1680542}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -190,6 +192,10 @@ Global {0B109210-03CB-413F-888C-3023994AA384}.Debug|Any CPU.Build.0 = Debug|Any CPU {0B109210-03CB-413F-888C-3023994AA384}.Release|Any CPU.ActiveCfg = Release|Any CPU {0B109210-03CB-413F-888C-3023994AA384}.Release|Any CPU.Build.0 = Release|Any CPU + {71004336-9896-4AE5-8367-B29BB1680542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71004336-9896-4AE5-8367-B29BB1680542}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71004336-9896-4AE5-8367-B29BB1680542}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71004336-9896-4AE5-8367-B29BB1680542}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -220,6 +226,7 @@ Global {351A034E-E642-4DB9-A21D-F71C8151C243} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {03400776-1F9A-4326-B927-1CA9B64B42A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {0B109210-03CB-413F-888C-3023994AA384} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {71004336-9896-4AE5-8367-B29BB1680542} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} diff --git a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs index 7a6f1070a..0f384912b 100644 --- a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs +++ b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs @@ -318,6 +318,11 @@ public RuntimeConfigurationReader(string runtimeConfigFile) return runtimeOptionsElement["frameworks"].Select(x => (x["name"]?.Value<string>(), x["version"]?.Value<string>())).ToList(); } + if (runtimeOptionsElement?["includedFrameworks"] != null) + { + return runtimeOptionsElement["includedFrameworks"].Select(x => (x["name"]?.Value<string>(), x["version"]?.Value<string>())).ToList(); + } + throw new InvalidOperationException($"Unable to read runtime configuration from {_runtimeConfigFile}."); } } diff --git a/test/coverlet.integration.tests/WpfResolverTests.cs b/test/coverlet.integration.tests/WpfResolverTests.cs index 23e7dc48b..0c1165279 100644 --- a/test/coverlet.integration.tests/WpfResolverTests.cs +++ b/test/coverlet.integration.tests/WpfResolverTests.cs @@ -43,5 +43,33 @@ public void TestInstrument_NetCoreSharedFrameworkResolver() "sample assembly shall be resolved"); Assert.NotEmpty(assemblies); } + + [ConditionalFact] + [SkipOnOS(OS.Linux, "WPF only runs on Windows")] + [SkipOnOS(OS.MacOS, "WPF only runs on Windows")] + public void TestInstrument_NetCoreSharedFrameworkResolver_SelfContained() + { + string buildConfiguration = TestUtils.GetAssemblyBuildConfiguration().ToString().ToLowerInvariant(); + string wpfProjectPath = TestUtils.GetTestProjectPath("coverlet.tests.projectsample.wpf8.selfcontained"); + string testBinaryPath = Path.Combine(TestUtils.GetTestBinaryPath("coverlet.tests.projectsample.wpf8.selfcontained"), buildConfiguration); + Assert.True(DotnetCli($"build \"{wpfProjectPath}\"", out string output, out string error)); + string assemblyLocation = Directory.GetFiles(testBinaryPath, "coverlet.tests.projectsample.wpf8.selfcontained.dll", SearchOption.AllDirectories).First(); + + var mockLogger = new Mock<ILogger>(); + var resolver = new NetCoreSharedFrameworkResolver(assemblyLocation, mockLogger.Object); + var compilationLibrary = new CompilationLibrary( + "package", + "System.Drawing", + "0.0.0.0", + "sha512-not-relevant", + Enumerable.Empty<string>(), + Enumerable.Empty<Dependency>(), + true); + + var assemblies = new List<string>(); + Assert.True(resolver.TryResolveAssemblyPaths(compilationLibrary, assemblies), + "sample assembly shall be resolved"); + Assert.NotEmpty(assemblies); + } } } diff --git a/test/coverlet.tests.projectsample.wpf8.selfcontained/.editorconfig b/test/coverlet.tests.projectsample.wpf8.selfcontained/.editorconfig new file mode 100644 index 000000000..d66ee0772 --- /dev/null +++ b/test/coverlet.tests.projectsample.wpf8.selfcontained/.editorconfig @@ -0,0 +1,8 @@ +# top-most EditorConfig file +# We don't want to import other EditorConfig files and we want +# to ensure no rules are enabled for these asset source files. +root = true + +[*.cs] +# Default severity for all analyzer diagnostics +dotnet_analyzer_diagnostic.severity = none diff --git a/test/coverlet.tests.projectsample.wpf8.selfcontained/Program.cs b/test/coverlet.tests.projectsample.wpf8.selfcontained/Program.cs new file mode 100644 index 000000000..3eccb28c2 --- /dev/null +++ b/test/coverlet.tests.projectsample.wpf8.selfcontained/Program.cs @@ -0,0 +1,9 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace coverlet.tests.projectsample.wpf8.selfcontained; + +public static class Program +{ + public static void Main() { } +} diff --git a/test/coverlet.tests.projectsample.wpf8.selfcontained/TestClass.cs b/test/coverlet.tests.projectsample.wpf8.selfcontained/TestClass.cs new file mode 100644 index 000000000..e86381aa1 --- /dev/null +++ b/test/coverlet.tests.projectsample.wpf8.selfcontained/TestClass.cs @@ -0,0 +1,12 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Windows.Controls; + +namespace coverlet.tests.projectsample.wpf8.selfcontained +{ + public class TestClass + { + public UserControl? Control { get; set; } + } +} diff --git a/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj b/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj new file mode 100644 index 000000000..44e9580ce --- /dev/null +++ b/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj @@ -0,0 +1,14 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net8.0-windows</TargetFramework> + <Nullable>enable</Nullable> + <UseWpf>true</UseWpf> + <IsTestProject>false</IsTestProject> + <EnableWindowsTargeting>true</EnableWindowsTargeting> + <IsPackable>false</IsPackable> + <SelfContained>true</SelfContained> + </PropertyGroup> + +</Project> diff --git a/test/coverlet.tests.projectsample.wpf8/Program.cs b/test/coverlet.tests.projectsample.wpf8/Program.cs index 205a96743..ba9c1bcd7 100644 --- a/test/coverlet.tests.projectsample.wpf8/Program.cs +++ b/test/coverlet.tests.projectsample.wpf8/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Toni Solarin-Sodara // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace coverlet.tests.projectsample.wpf6; +namespace coverlet.tests.projectsample.wpf8; public static class Program { diff --git a/test/coverlet.tests.projectsample.wpf8/TestClass.cs b/test/coverlet.tests.projectsample.wpf8/TestClass.cs index 9299936c9..e38ac5467 100644 --- a/test/coverlet.tests.projectsample.wpf8/TestClass.cs +++ b/test/coverlet.tests.projectsample.wpf8/TestClass.cs @@ -3,7 +3,7 @@ using System.Windows.Controls; -namespace coverlet.tests.projectsample.wpf6 +namespace coverlet.tests.projectsample.wpf8 { public class TestClass { From bfbe67dd46e85fdc590e8a6df54c3c4816a7ae63 Mon Sep 17 00:00:00 2001 From: Philipp Feigl <philipp.feigl@cyan-it.de> Date: Mon, 21 Oct 2024 10:57:42 +0200 Subject: [PATCH 2/3] Add RuntimeIdentifier to possibly fix build under mac and linux --- test/coverlet.integration.tests/WpfResolverTests.cs | 2 +- .../coverlet.tests.projectsample.wpf8.selfcontained.csproj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/coverlet.integration.tests/WpfResolverTests.cs b/test/coverlet.integration.tests/WpfResolverTests.cs index 0c1165279..779cb1e1b 100644 --- a/test/coverlet.integration.tests/WpfResolverTests.cs +++ b/test/coverlet.integration.tests/WpfResolverTests.cs @@ -51,7 +51,7 @@ public void TestInstrument_NetCoreSharedFrameworkResolver_SelfContained() { string buildConfiguration = TestUtils.GetAssemblyBuildConfiguration().ToString().ToLowerInvariant(); string wpfProjectPath = TestUtils.GetTestProjectPath("coverlet.tests.projectsample.wpf8.selfcontained"); - string testBinaryPath = Path.Combine(TestUtils.GetTestBinaryPath("coverlet.tests.projectsample.wpf8.selfcontained"), buildConfiguration); + string testBinaryPath = Path.Combine(TestUtils.GetTestBinaryPath("coverlet.tests.projectsample.wpf8.selfcontained"), $"{buildConfiguration}_win-x64"); Assert.True(DotnetCli($"build \"{wpfProjectPath}\"", out string output, out string error)); string assemblyLocation = Directory.GetFiles(testBinaryPath, "coverlet.tests.projectsample.wpf8.selfcontained.dll", SearchOption.AllDirectories).First(); diff --git a/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj b/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj index 44e9580ce..1950b2244 100644 --- a/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj +++ b/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj @@ -9,6 +9,7 @@ <EnableWindowsTargeting>true</EnableWindowsTargeting> <IsPackable>false</IsPackable> <SelfContained>true</SelfContained> + <RuntimeIdentifier>win-x64</RuntimeIdentifier> </PropertyGroup> </Project> From 2e9eea487eb0211d89769f1e14d9d302056689b5 Mon Sep 17 00:00:00 2001 From: Philipp Feigl <philipp.feigl@cyan-it.de> Date: Mon, 21 Oct 2024 11:16:58 +0200 Subject: [PATCH 3/3] Tabs to spaces --- .../coverlet.tests.projectsample.wpf8.selfcontained.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj b/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj index 1950b2244..662e3e604 100644 --- a/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj +++ b/test/coverlet.tests.projectsample.wpf8.selfcontained/coverlet.tests.projectsample.wpf8.selfcontained.csproj @@ -8,8 +8,8 @@ <IsTestProject>false</IsTestProject> <EnableWindowsTargeting>true</EnableWindowsTargeting> <IsPackable>false</IsPackable> - <SelfContained>true</SelfContained> - <RuntimeIdentifier>win-x64</RuntimeIdentifier> + <SelfContained>true</SelfContained> + <RuntimeIdentifier>win-x64</RuntimeIdentifier> </PropertyGroup> </Project>