From af251584acb3330f77898f3b855e75cba190a394 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 28 Oct 2026 10:42:26 -0700 Subject: [PATCH] devops(ci): run Windows tests at normal I/O priority The Windows runner starts steps at I/O priority Low. The driver and the browsers inherit it, and Firefox storage flushes stall until tests time out. Reset the test steps to Normal, as microsoft/playwright#43707 does upstream. --- .github/workflows/ci.yml | 32 ++++++++++++++++++------- scripts/windows-normal-io.ps1 | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 scripts/windows-normal-io.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3aff2aed..e48cb0749 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,21 +127,31 @@ jobs: id: install run: python -m playwright install --with-deps ${{ matrix.browser }} - name: Common Tests - run: pytest tests/common --browser=${{ matrix.browser }} + run: | + ${{ runner.os == 'Windows' && '. ./scripts/windows-normal-io.ps1' || '' }} + pytest tests/common --browser=${{ matrix.browser }} - name: Test Reference count - run: pytest tests/test_reference_count_async.py --browser=${{ matrix.browser }} + run: | + ${{ runner.os == 'Windows' && '. ./scripts/windows-normal-io.ps1' || '' }} + pytest tests/test_reference_count_async.py --browser=${{ matrix.browser }} - name: Test Wheel Installation - run: pytest tests/test_installation.py --browser=${{ matrix.browser }} + run: | + ${{ runner.os == 'Windows' && '. ./scripts/windows-normal-io.ps1' || '' }} + pytest tests/test_installation.py --browser=${{ matrix.browser }} - name: Test Sync API if: runner.os != 'Linux' - run: pytest tests/sync --browser=${{ matrix.browser }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes + run: | + ${{ runner.os == 'Windows' && '. ./scripts/windows-normal-io.ps1' || '' }} + pytest tests/sync --browser=${{ matrix.browser }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes - name: Test Sync API if: runner.os == 'Linux' run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" pytest tests/sync --browser=${{ matrix.browser }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes - name: Test Async API # Also after a failed sync step: the two suites report on their own. if: ${{ !cancelled() && steps.install.outcome == 'success' && runner.os != 'Linux' }} - run: pytest tests/async --browser=${{ matrix.browser }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes + run: | + ${{ runner.os == 'Windows' && '. ./scripts/windows-normal-io.ps1' || '' }} + pytest tests/async --browser=${{ matrix.browser }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes - name: Test Async API if: ${{ !cancelled() && steps.install.outcome == 'success' && runner.os == 'Linux' }} run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" pytest tests/async --browser=${{ matrix.browser }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes @@ -187,17 +197,23 @@ jobs: id: install run: python -m playwright install ${{ matrix.browser-channel }} --with-deps - name: Common Tests - run: pytest tests/common --browser=chromium --browser-channel=${{ matrix.browser-channel }} + run: | + ${{ runner.os == 'Windows' && '. ./scripts/windows-normal-io.ps1' || '' }} + pytest tests/common --browser=chromium --browser-channel=${{ matrix.browser-channel }} - name: Test Sync API if: runner.os != 'Linux' - run: pytest tests/sync --browser=chromium --browser-channel=${{ matrix.browser-channel }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes + run: | + ${{ runner.os == 'Windows' && '. ./scripts/windows-normal-io.ps1' || '' }} + pytest tests/sync --browser=chromium --browser-channel=${{ matrix.browser-channel }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes - name: Test Sync API if: runner.os == 'Linux' run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" pytest tests/sync --browser=chromium --browser-channel=${{ matrix.browser-channel }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes - name: Test Async API # Also after a failed sync step: the two suites report on their own. if: ${{ !cancelled() && steps.install.outcome == 'success' && runner.os != 'Linux' }} - run: pytest tests/async --browser=chromium --browser-channel=${{ matrix.browser-channel }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes + run: | + ${{ runner.os == 'Windows' && '. ./scripts/windows-normal-io.ps1' || '' }} + pytest tests/async --browser=chromium --browser-channel=${{ matrix.browser-channel }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes - name: Test Async API if: ${{ !cancelled() && steps.install.outcome == 'success' && runner.os == 'Linux' }} run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" pytest tests/async --browser=chromium --browser-channel=${{ matrix.browser-channel }} -n ${{ env.WORKERS }} -p pytest_dot_reporter -rfE --color=yes diff --git a/scripts/windows-normal-io.ps1 b/scripts/windows-normal-io.ps1 new file mode 100644 index 000000000..f1308b638 --- /dev/null +++ b/scripts/windows-normal-io.ps1 @@ -0,0 +1,44 @@ +# Dot-source at the start of a Windows test step: the self-hosted runner starts steps at +# I/O priority Low, which the driver and the browsers inherit, and Firefox storage flushes +# then stall until tests time out. See https://github.com/microsoft/playwright/issues/43704. + +$ErrorActionPreference = 'Stop' + +Add-Type -TypeDefinition @' +using System; +using System.Runtime.InteropServices; + +public static class TestProcessIOPriority { + [DllImport("ntdll.dll")] + public static extern int NtSetInformationProcess( + IntPtr process, int informationClass, ref int information, int size); + + [DllImport("ntdll.dll")] + public static extern int NtQueryInformationProcess( + IntPtr process, int informationClass, out int information, int size, out int length); +} +'@ + +$priority = 0 +$length = 0 +$status = [TestProcessIOPriority]::NtQueryInformationProcess( + [IntPtr](-1), 33, [ref]$priority, 4, [ref]$length) +if ($status -ne 0) { + throw ('Reading I/O priority failed: NTSTATUS 0x{0:X8}' -f $status) +} +$inheritedPriority = $priority +$priority = 2 +$status = [TestProcessIOPriority]::NtSetInformationProcess( + [IntPtr](-1), 33, [ref]$priority, 4) +if ($status -ne 0) { + throw ('Setting normal I/O priority failed: NTSTATUS 0x{0:X8}' -f $status) +} +$status = [TestProcessIOPriority]::NtQueryInformationProcess( + [IntPtr](-1), 33, [ref]$priority, 4, [ref]$length) +if ($status -ne 0) { + throw ('Reading I/O priority failed: NTSTATUS 0x{0:X8}' -f $status) +} +if ($priority -ne 2) { + throw "Expected normal I/O priority (2), got $priority" +} +Write-Host "Test launcher I/O priority: $inheritedPriority -> $priority (Normal)"