diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 49ba0d3..fbfe962 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -10,7 +10,7 @@ "rollForward": false }, "csharpier": { - "version": "1.2.6", + "version": "1.3.0", "commands": [ "csharpier" ], diff --git a/.github/workflows/build-test-core.yml b/.github/workflows/build-test-core.yml index c5a4acf..504c0d6 100644 --- a/.github/workflows/build-test-core.yml +++ b/.github/workflows/build-test-core.yml @@ -76,8 +76,47 @@ jobs: - name: Build run: dotnet build Scribe.slnx --configuration Release --no-restore + # Microsoft.Testing.Platform: tests are self-hosting executables driven by the MTP + # `dotnet test` (opted in via global.json `test.runner`). xUnit's built-in reporter + # emits CTRF test results (no extra package); the CodeCoverage extension emits + # Cobertura. Both are rendered below; the report steps run on always() so a test + # failure still publishes results while the Test step's exit code fails the job. - name: Test - run: dotnet test Scribe.slnx --configuration Release --no-build --verbosity normal --logger "trx;LogFileName=test-results.trx" + run: >- + dotnet test --solution Scribe.slnx --configuration Release --no-build --verbosity normal + -- + --report-ctrf --report-ctrf-filename test-results.ctrf.json + --coverage --coverage-output-format cobertura --coverage-output coverage.cobertura.xml + + - name: Publish test report + if: always() + continue-on-error: true + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: '**/test-results.ctrf.json' + + - name: Generate coverage report + if: always() + continue-on-error: true + uses: danielpalme/ReportGenerator-GitHub-Action@5 + with: + reports: '**/coverage.cobertura.xml' + targetdir: coveragereport + reporttypes: MarkdownSummaryGithub;Html + + - name: Publish coverage summary + if: always() + run: | + if [ -f coveragereport/SummaryGithub.md ]; then + cat coveragereport/SummaryGithub.md >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Upload coverage report + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coveragereport - name: Pack core packages if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' @@ -86,13 +125,6 @@ jobs: dotnet pack Scribe.Ink/Scribe.Ink.csproj --configuration Release --no-build --output .artifacts/packages dotnet pack Scribe.Scriptorium/Scribe.Scriptorium.csproj --configuration Release --no-build --output .artifacts/packages - - name: Upload test results - if: always() - uses: actions/upload-artifact@v4 - with: - name: test-results-core - path: "**/test-results.trx" - - name: Upload packages if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' uses: actions/upload-artifact@v4 diff --git a/Directory.Build.props b/Directory.Build.props index dd819b1..36f6c8c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,6 +12,8 @@ true false + + Exe net10.0 preview enable diff --git a/Directory.Packages.props b/Directory.Packages.props index 37440cd..2e27cfa 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,20 +9,19 @@ - + - + - - + - - + - + \ No newline at end of file diff --git a/Scribe.Tests/OutputDirectoryAssemblyResolver.cs b/Scribe.Tests/OutputDirectoryAssemblyResolver.cs new file mode 100644 index 0000000..7bb7b6d --- /dev/null +++ b/Scribe.Tests/OutputDirectoryAssemblyResolver.cs @@ -0,0 +1,45 @@ +using System.Runtime.Loader; +using Xunit.v3; + +[assembly: TestPipelineStartup(typeof(Scribe.Tests.OutputDirectoryAssemblyResolver))] + +namespace Scribe.Tests; + +/// +/// Microsoft.Testing.Platform runs this test project as a self-hosted executable that +/// resolves runtime assemblies strictly from its .deps.json. Scribe is consumed +/// here via a ProjectReference to a Scribe.Sdk Library project, whose output +/// is intentionally kept out of consumers' runtime dependency graph — in production it +/// ships embedded as an analyzer, loaded by the Roslyn host, never as an application +/// runtime dependency. VSTest's testhost masked this by probing the output +/// directory; MTP does not, so a test that instantiates a generator touching Scribe throws +/// . +/// +/// Installed via xUnit's pipeline-startup hook rather than a [ModuleInitializer]: +/// the Scribe.Sdk injects polyfilled ModuleInitializerAttribute types into both +/// Scribe and Scribe.Ink, which collide here via InternalsVisibleTo. +/// The assemblies sit in the output directory regardless; this teaches the default load +/// context to probe there for anything missing from the deps.json graph. +/// +public sealed class OutputDirectoryAssemblyResolver : ITestPipelineStartup +{ + public ValueTask StartAsync(Xunit.Sdk.IMessageSink diagnosticMessageSink) + { + AssemblyLoadContext.Default.Resolving += static (context, assemblyName) => + { + if (assemblyName.Name is not { } name) + { + return null; + } + + var candidate = Path.Combine(AppContext.BaseDirectory, name + ".dll"); + return File.Exists(candidate) + ? context.LoadFromAssemblyPath(candidate) + : null; + }; + + return default; + } + + public ValueTask StopAsync() => default; +} diff --git a/Scribe.Tests/Scribe.Tests.csproj b/Scribe.Tests/Scribe.Tests.csproj index b138b25..8a851c8 100644 --- a/Scribe.Tests/Scribe.Tests.csproj +++ b/Scribe.Tests/Scribe.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -7,9 +7,30 @@ - + - - + + + + + <_AmbiguousNetStandardSystemRefs>;System.Buffers.dll;System.Collections.Immutable.dll;System.Memory.dll;System.Numerics.Vectors.dll;System.Reflection.Metadata.dll;System.Runtime.CompilerServices.Unsafe.dll;System.Text.Encoding.CodePages.dll;System.Threading.Tasks.Extensions.dll; + + + + + + diff --git a/Scribe.slnx b/Scribe.slnx index 2f4963b..41ae985 100644 --- a/Scribe.slnx +++ b/Scribe.slnx @@ -1,6 +1,11 @@ + + + + + diff --git a/global.json b/global.json index 77db41d..9655c62 100644 --- a/global.json +++ b/global.json @@ -5,6 +5,9 @@ "allowPrerelease": false }, "msbuild-sdks": { - "BulletsForHumanity.Scribe.Sdk": "0.6.4" + "BulletsForHumanity.Scribe.Sdk": "0.6.6" + }, + "test": { + "runner": "Microsoft.Testing.Platform" } }