diff --git a/.github/workflows/host-tests.yml b/.github/workflows/host-tests.yml index a151b32..27fce3c 100644 --- a/.github/workflows/host-tests.yml +++ b/.github/workflows/host-tests.yml @@ -31,8 +31,24 @@ jobs: run: bash scripts/test-host.sh ui-layout: - name: Native UI layout - runs-on: ubuntu-24.04 + name: Native UI layout (${{ matrix.group }}) + runs-on: blacksmith-8vcpu-ubuntu-2404 + strategy: + fail-fast: false + matrix: + include: + - group: library + args: --screens-only library + - group: store + args: --screens-only store + - group: settings + args: --screens-only settings + - group: detail + args: --screens-only detail + - group: queue + args: --screens-only queue + - group: overlay and guest/empty + args: overlay steps: - name: Check out source uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 @@ -47,16 +63,18 @@ jobs: - name: Build native UI layout test run: | cmake -S tests/ui_layout -B build/ui-layout -G Ninja -DCMAKE_BUILD_TYPE=Debug - cmake --build build/ui-layout -j4 + cmake --build build/ui-layout -j"$(nproc)" - name: Check native UI layout - run: bash tests/ui_layout/run.sh build/ui-layout/ui_layout build/ui-evidence + env: + LP_NUM_THREADS: "1" + run: OPENNOW_UI_JOBS="$(nproc)" bash tests/ui_layout/run.sh build/ui-layout/ui_layout build/ui-evidence ${{ matrix.args }} - name: Upload native UI evidence if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: - name: native-ui-evidence + name: native-ui-evidence-${{ strategy.job-index }} path: build/ui-evidence if-no-files-found: ignore retention-days: 7 diff --git a/tests/ui_layout/README.md b/tests/ui_layout/README.md index 09185f8..8d3c7e5 100644 --- a/tests/ui_layout/README.md +++ b/tests/ui_layout/README.md @@ -44,6 +44,10 @@ For parallel isolated output directories, `--screens-only` before scenario names omits only separately assigned guest/empty cases. Run one group without that flag so all eight extra cases remain covered. Each attempted case gets a log; failures do not stop subsequent cases, and any failure makes the runner nonzero. +Each case renders on Mesa's software rasterizer, so set `OPENNOW_UI_JOBS` to run +that many cases at once on separate Xvfb displays. With `LP_NUM_THREADS=1`, one +case per CPU gives the best throughput. CI splits the matrix into six groups and +runs `OPENNOW_UI_JOBS=$(nproc)` on each 8-vCPU Blacksmith runner. ## Assertions diff --git a/tests/ui_layout/run.sh b/tests/ui_layout/run.sh index 3f11838..daf03c9 100644 --- a/tests/ui_layout/run.sh +++ b/tests/ui_layout/run.sh @@ -12,18 +12,21 @@ if [[ "${1:-}" == --screens-only ]]; then printf 'INFO Running named screens only; guest/empty cases run in the separate main matrix.\n' fi if (( $# )); then scenarios=("$@"); else scenarios=(library store settings detail queue overlay); fi -status=0 +parallel_cases="${OPENNOW_UI_JOBS:-1}" +case_number=0 +failures="$output/.failures" +rm -f "$failures" capture_case() { - local language="$1" width="$2" account="$3" scenario="$4" name="$5" + local display="$1" language="$2" width="$3" account="$4" scenario="$5" name="$6" rm -f "$output/$name.ppm" "$output/$name.png" "$output/$name.ppm.queue.ppm" "$output/$name.ppm.queue.png" \ "$output/$name.ppm.failure.ppm" "$output/$name.ppm.failure.png" "$output/$name.ppm.restored.ppm" "$output/$name.ppm.restored.png" rm -f "$output/$name.ppm".settings-*.ppm "$output/$name.ppm".settings-*.png - if xvfb-run -a -s '-screen 0 1920x1080x24' "$binary" "$language" "$account" \ + if xvfb-run -n "$display" -s '-screen 0 1920x1080x24' "$binary" "$language" "$account" \ "$output/$name.ppm" "$width" "$scenario" > "$output/$name.log" 2>&1; then printf 'PASS %s\n' "$name" else printf 'FAIL %s (see %s.log)\n' "$name" "$name" - status=1 + printf '%s\n' "$name" >> "$failures" fi if [[ -f "$output/$name.ppm" ]]; then convert "$output/$name.ppm" "$output/$name.png"; fi if [[ -f "$output/$name.ppm.queue.ppm" ]]; then convert "$output/$name.ppm.queue.ppm" "$output/$name.ppm.queue.png"; fi @@ -34,17 +37,23 @@ capture_case() { find "$output" -maxdepth 1 -name "$name.ppm.*-title-*.ppm" -print0 | while IFS= read -r -d '' path; do convert "$path" "${path%.ppm}.png"; done } +schedule_case() { + while (( $(jobs -rp | wc -l) >= parallel_cases )); do wait -n || true; done + case_number=$((case_number + 1)) + capture_case "$((100 + case_number))" "$@" & +} for width in 1280 1920; do for language in en zh-CN es ru it fr pl uk; do for scenario in "${scenarios[@]}"; do - capture_case "$language" "$width" 'A player with a very long account display name' "$scenario" "$language-$width-$scenario" + schedule_case "$language" "$width" 'A player with a very long account display name' "$scenario" "$language-$width-$scenario" done done if (( include_extra )); then - capture_case en "$width" guest library "guest-$width" - capture_case en "$width" Player empty "empty-$width" - capture_case en "$width" guest store "guest-store-$width" - capture_case en "$width" Player empty-store "empty-store-$width" + schedule_case en "$width" guest library "guest-$width" + schedule_case en "$width" Player empty "empty-$width" + schedule_case en "$width" guest store "guest-store-$width" + schedule_case en "$width" Player empty-store "empty-store-$width" fi done -exit "$status" +wait +if [[ -s "$failures" ]]; then exit 1; fi