From 5d4fdc57a827b854164a0b460e99c9651e9fda4d Mon Sep 17 00:00:00 2001 From: Jevan Saks Date: Tue, 16 Jun 2026 11:42:08 -0700 Subject: [PATCH] Run COM runtime tests on GitHub Actions windows-latest The COM/Direct2D/WMI/Shell runtime tests were tagged RequiresHardware (formerly FailsInCloudTest) and excluded from ALL CI, so we only ever verified that the generated projection compiled -- never that it actually works at runtime through the .NET / source-generated COM marshalers. The original failures were on the locked-down Azure DevOps 1ES build pool (microbuild agents) which lack an interactive desktop, GPU, and some WMI providers. GitHub-hosted windows-latest runners do NOT have those limitations: they run an interactive session, Defender's WMI provider, the firewall service, and can drive Direct2D via the WARP software rasterizer. Changes: - Add a gating `test-hardware-windows` job that runs the RequiresHardware tests on windows-latest so the real runtime code paths are exercised in PRs. Wire it into the `validate` required check. - Keep the RequiresHardware trait so the Azure DevOps 1ES pool (dotnet-test-cloud.ps1) continues to exclude these tests. - Direct2D ReturnValueMarshalsCorrectly tests now create a D2D1_RENDER_TARGET_TYPE_SOFTWARE (WARP) render target so they exercise the same render-target creation + HWND marshaling without a hardware GPU. - No graceful Assert.Skip fallbacks: if a test regresses or the environment changes, the job fails loudly rather than silently skipping. - Fix misleading trait comments (including a copy-paste "WMI" comment on a Shell-only test) to document the real ADO-1ES-vs-GitHub rationale. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build.yml | 41 ++++++++++++++++++- .../COMTests.cs | 8 ++-- .../ComRuntimeTests.cs | 10 +++-- .../COMTests.cs | 6 ++- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 31afc209..8516e126 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,6 +75,43 @@ jobs: path: TestResults/ retention-days: 5 + test-hardware-windows: + name: 🧪 Tests (Windows, Hardware) + needs: build + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: ⚙️ Install prerequisites + run: ./init.ps1 -UpgradePrerequisites -NoNuGetCredProvider + shell: pwsh + - name: 📦 Download build + uses: actions/download-artifact@v4 + with: + name: build-windows + path: bin/ + # GitHub-hosted windows-latest runners can execute these COM/Direct2D/WMI/Shell runtime + # tests (Direct2D uses the WARP software rasterizer, so no GPU is required). They remain + # tagged RequiresHardware so the locked-down Azure DevOps 1ES build pool keeps excluding + # them, but we exercise the real runtime code paths here rather than only on dev machines. + - name: 🧪 Run hardware-dependent tests + run: | + dotnet test --no-build -c ${{ env.BuildConfiguration }} ` + --filter "TestCategory=RequiresHardware" ` + --blame-hang-timeout 600s ` + --blame-crash ` + --logger trx ` + --results-directory TestResults + shell: pwsh + - name: 📢 Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-hardware-windows + path: TestResults/ + retention-days: 5 + test-fast-linux: name: 🧪 Tests (Linux) runs-on: ubuntu-latest @@ -242,18 +279,20 @@ jobs: validate: name: ✅ Validate if: always() - needs: [build, test-fast-windows, test-fast-linux, test-heavy, integration-test] + needs: [build, test-fast-windows, test-hardware-windows, test-fast-linux, test-heavy, integration-test] runs-on: ubuntu-latest steps: - name: Check results run: | echo "Build: ${{ needs.build.result }}" echo "Windows tests: ${{ needs.test-fast-windows.result }}" + echo "Windows hardware tests: ${{ needs.test-hardware-windows.result }}" echo "Linux tests: ${{ needs.test-fast-linux.result }}" echo "Heavy tests: ${{ needs.test-heavy.result }}" echo "Integration tests: ${{ needs.integration-test.result }}" if [[ "${{ needs.build.result }}" != "success" || "${{ needs.test-fast-windows.result }}" != "success" || + "${{ needs.test-hardware-windows.result }}" != "success" || "${{ needs.test-fast-linux.result }}" != "success" || "${{ needs.test-heavy.result }}" != "success" || "${{ needs.integration-test.result }}" != "success" ]]; then diff --git a/test/GenerationSandbox.BuildTask.Tests/COMTests.cs b/test/GenerationSandbox.BuildTask.Tests/COMTests.cs index 257ee393..d031a266 100644 --- a/test/GenerationSandbox.BuildTask.Tests/COMTests.cs +++ b/test/GenerationSandbox.BuildTask.Tests/COMTests.cs @@ -132,7 +132,7 @@ private static void CompileOnlyCreateCommittedResourceGenericOverload(ID3D12Devi [Fact] - [Trait("TestCategory", "RequiresHardware")] // D3D APIs fail in cloud VMs + [Trait("TestCategory", "RequiresHardware")] // Excluded from the locked-down ADO 1ES build pool; runs on GitHub Actions (Direct2D uses WARP). public void ReturnValueMarshalsCorrectly() { // Create an ID2D1HwndRenderTarget and verify GetHwnd returns the original HWND. @@ -188,7 +188,9 @@ static LRESULT WndProc(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) // 3. Prepare render target properties. D2D1_RENDER_TARGET_PROPERTIES rtProps = new() { - type = D2D1_RENDER_TARGET_TYPE.D2D1_RENDER_TARGET_TYPE_DEFAULT, + // Use WARP (software) rendering so this exercises the same D2D render-target + // creation and HWND marshaling code paths on GPU-less CI VMs without a hardware GPU. + type = D2D1_RENDER_TARGET_TYPE.D2D1_RENDER_TARGET_TYPE_SOFTWARE, pixelFormat = new D2D1_PIXEL_FORMAT { format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN, @@ -224,7 +226,7 @@ static LRESULT WndProc(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) } [Fact] - [Trait("TestCategory", "RequiresHardware")] // WMI APIs don't work in cloud VMs. + [Trait("TestCategory", "RequiresHardware")] // Excluded from the locked-down ADO 1ES build pool; runs on GitHub Actions. public void IWbemServices_GetObject_Works() { Assert.SkipUnless(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Test calls Windows-specific APIs"); diff --git a/test/GenerationSandbox.Tests/ComRuntimeTests.cs b/test/GenerationSandbox.Tests/ComRuntimeTests.cs index f2e25a4e..1277777a 100644 --- a/test/GenerationSandbox.Tests/ComRuntimeTests.cs +++ b/test/GenerationSandbox.Tests/ComRuntimeTests.cs @@ -35,7 +35,7 @@ public void RemotableInterface() } [Fact] - [Trait("TestCategory", "RequiresHardware")] // D3D APIs fail in cloud VMs + [Trait("TestCategory", "RequiresHardware")] // Excluded from the locked-down ADO 1ES build pool; runs on GitHub Actions (Direct2D uses WARP). public void ReturnValueMarshalsCorrectly() { // Create an ID2D1HwndRenderTarget and verify GetHwnd returns the original HWND. @@ -90,7 +90,9 @@ static LRESULT WndProc(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) // 3. Prepare render target properties. D2D1_RENDER_TARGET_PROPERTIES rtProps = new() { - type = D2D1_RENDER_TARGET_TYPE.D2D1_RENDER_TARGET_TYPE_DEFAULT, + // Use WARP (software) rendering so this exercises the same D2D render-target + // creation and HWND marshaling code paths on GPU-less CI VMs without a hardware GPU. + type = D2D1_RENDER_TARGET_TYPE.D2D1_RENDER_TARGET_TYPE_SOFTWARE, pixelFormat = new D2D1_PIXEL_FORMAT { format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN, @@ -126,7 +128,7 @@ static LRESULT WndProc(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) } [Fact] - [Trait("TestCategory", "RequiresHardware")] // WMI APIs don't work in cloud VMs. + [Trait("TestCategory", "RequiresHardware")] // Excluded from the locked-down ADO 1ES build pool; runs on GitHub Actions. public void IWbemServices_GetObject_Works() { Assert.SkipUnless(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Test calls Windows-specific APIs"); @@ -161,7 +163,7 @@ public void IWbemServices_GetObject_Works() } [Fact] - [Trait("TestCategory", "RequiresHardware")] // WMI APIs don't work in cloud VMs. + [Trait("TestCategory", "RequiresHardware")] // Needs Shell/Explorer COM; excluded from the locked-down ADO 1ES build pool, runs on GitHub Actions. public void CanCallIDispatchOnlyMethods() { Assert.SkipUnless(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Test calls Windows-specific APIs"); diff --git a/test/GenerationSandbox.Unmarshalled.Tests/COMTests.cs b/test/GenerationSandbox.Unmarshalled.Tests/COMTests.cs index fc6b0cd1..5e5776d6 100644 --- a/test/GenerationSandbox.Unmarshalled.Tests/COMTests.cs +++ b/test/GenerationSandbox.Unmarshalled.Tests/COMTests.cs @@ -43,7 +43,7 @@ public unsafe void CocreatableClassesWithImplicitInterfaces() #endif [Fact] - [Trait("TestCategory", "RequiresHardware")] // D3D APIs fail in cloud VMs + [Trait("TestCategory", "RequiresHardware")] // Excluded from the locked-down ADO 1ES build pool; runs on GitHub Actions (Direct2D uses WARP). public unsafe void ReturnValueMarshalsCorrectly() { // Create an ID2D1HwndRenderTarget and verify GetHwnd returns the original HWND. @@ -78,7 +78,9 @@ public unsafe void ReturnValueMarshalsCorrectly() // 3. Prepare render target properties. D2D1_RENDER_TARGET_PROPERTIES rtProps = new() { - type = D2D1_RENDER_TARGET_TYPE.D2D1_RENDER_TARGET_TYPE_DEFAULT, + // Use WARP (software) rendering so this exercises the same D2D render-target + // creation and HWND marshaling code paths on GPU-less CI VMs without a hardware GPU. + type = D2D1_RENDER_TARGET_TYPE.D2D1_RENDER_TARGET_TYPE_SOFTWARE, pixelFormat = new D2D1_PIXEL_FORMAT { format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN,