Skip to content

Refactor window handling, improve theme support, and update dependencies #94

Refactor window handling, improve theme support, and update dependencies

Refactor window handling, improve theme support, and update dependencies #94

Workflow file for this run

name: A/V Sync Tests
# Verifies audio/video sync correctness on every platform:
# 1. Unit + property tests for the timestamp pipeline (encoders, drift
# trackers, muxers).
# 2. The synthetic device matrix: fake cameras/screens/microphones across
# frame rates, sample rates, channel counts and delivery pathologies
# (jitter, drops, static-screen gaps), driven through the real recording
# pipeline and verified at the container level. No capture hardware or
# GPU required, so results are deterministic on hosted runners.
#
# Findings are published as a job-summary table and a JSON artifact per OS.
on:
workflow_dispatch:
schedule:
- cron: "0 5 * * *"
pull_request:
paths:
- "crates/recording/**"
- "crates/enc-ffmpeg/**"
- "crates/enc-avfoundation/**"
- "crates/enc-mediafoundation/**"
- "crates/timestamp/**"
- "crates/rendering/**"
- "crates/media-info/**"
- ".github/workflows/sync-tests.yml"
concurrency:
group: sync-tests-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
permissions:
contents: read
jobs:
sync-tests:
strategy:
fail-fast: false
matrix:
runner:
- macos-latest
- windows-2022
- ubuntu-24.04
runs-on: ${{ matrix.runner }}
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Rust setup
uses: dtolnay/rust-toolchain@1.88.0
- name: Rust cache
uses: ./.github/actions/setup-rust-cache
with:
target: ${{ runner.os == 'Windows' && 'x86_64-pc-windows-msvc' || runner.os == 'macOS' && 'aarch64-apple-darwin' || 'x86_64-unknown-linux-gnu' }}
- name: Install desktop dependencies
uses: ./.github/actions/install-desktop-deps
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
- name: Native dependencies
env:
RUST_TARGET_TRIPLE: ${{ runner.os == 'Linux' && 'x86_64-unknown-linux-gnu' || runner.os == 'Windows' && 'x86_64-pc-windows-msvc' || 'aarch64-apple-darwin' }}
run: node scripts/setup.js
- name: Add FFmpeg DLLs to PATH
if: runner.os == 'Windows'
shell: pwsh
run: Add-Content -Path $env:GITHUB_PATH -Value "${{ github.workspace }}\\target\\ffmpeg\\bin"
- name: Install software Vulkan driver (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y mesa-vulkan-drivers libvulkan1
# Hosted Windows runners have no audio endpoint, so WASAPI loopback
# capture (system audio) cannot run at all without a virtual sound
# card. Scream provides a signed null render device; with it installed,
# the loopback keepalive, AUDCLNT_BUFFERFLAGS_SILENT zeroing and QPC
# capture timestamps are exercised against a real audio engine.
#
# Pinned to 3.6: later releases (3.8/4.0) were signed after the 2021
# kernel cross-signing deprecation and PnP blocks their install on an
# interactive consent prompt, which hangs headless runners. The step
# timeout turns any such hang into a fast failure.
- name: Install virtual audio device (Windows)
if: runner.os == 'Windows'
timeout-minutes: 10
shell: pwsh
run: |
Invoke-WebRequest -Uri https://github.com/duncanthrax/scream/releases/download/3.6/Scream3.6.zip -OutFile Scream3.6.zip
$expected = "25ea5e778b4e6995a98d448b9b5f6d321f681663f1aeeec69d8e63183d008b19"
$actual = (Get-FileHash -Algorithm SHA256 Scream3.6.zip).Hash.ToLowerInvariant()
if ($actual -ne $expected) { throw "Scream3.6.zip SHA-256 mismatch: got $actual, expected $expected" }
Expand-Archive -Path Scream3.6.zip -DestinationPath Scream
$cert = (Get-AuthenticodeSignature Scream\Install\driver\Scream.sys).SignerCertificate
$store = [System.Security.Cryptography.X509Certificates.X509Store]::new("TrustedPublisher", "LocalMachine")
$store.Open("ReadWrite")
$store.Add($cert)
$store.Close()
Scream\Install\helpers\devcon.exe install Scream\Install\driver\Scream.inf *Scream
if ($LASTEXITCODE -gt 1) { throw "devcon install failed with exit code $LASTEXITCODE" }
Start-Service -Name Audiosrv -ErrorAction SilentlyContinue
- name: Timestamp pipeline unit + property tests
shell: bash
run: |
cargo test --locked -p cap-timestamp -p cap-enc-ffmpeg
cargo test --locked -p cap-recording --lib
cargo test --locked -p cap-rendering
# The AVFoundation encoder (studio camera/display on macOS) has its own
# pts handling; its fps-matrix duration tests guard against re-timing
# sources that deliver at a different rate than configured.
- name: AVFoundation encoder tests (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
cargo test --locked -p cap-enc-avfoundation
# Runs against the Scream endpoint installed above; the env var turns
# a missing endpoint (driver install regression) into a hard failure
# instead of a silent skip.
- name: WASAPI loopback endpoint tests (Windows)
if: runner.os == 'Windows'
shell: bash
env:
CAP_REQUIRE_AUDIO_ENDPOINT: "1"
run: |
cargo test --locked -p cap-recording --test windows_loopback -- --nocapture
- name: Synthetic device matrix
id: matrix
continue-on-error: true
shell: bash
env:
CAP_SYNC_MATRIX_REPORT: ${{ github.workspace }}/sync-matrix-${{ matrix.runner }}.json
CAP_SYNC_MATRIX_RANDOM_CASES: ${{ github.event_name == 'schedule' && '40' || '6' }}
run: |
cargo test --locked -p cap-recording --test sync_matrix -- --nocapture
# Verifies the editor's playback machinery (decoders, frame scheduling,
# audio pipeline) preserves sync. The fixture recording is generated
# through the real recording pipeline, so no capture hardware is needed;
# rendering uses the platform's software adapter where no GPU exists.
# 30s of pattern stabilizes the drift slope against frame-quantization
# noise. Playback runs at the default 30 fps: lower rates trip the audio
# sync policy's drift-correction threshold every few frames and accrue
# real (policy-induced) drift that fails the gate.
#
# Linux-only: the Windows WARP adapter composites blank frames and the
# macOS runners' paravirtualized Metal collapses to ~2 fps presentation
# regardless of decoder, so neither can sustain a wall-clock playback
# measurement. The decoder logic under test (FFmpeg gap holds) is fully
# exercised here; the macOS AVAssetReader path is covered by running
# `cap selftest playback` locally on real hardware.
- name: Editor playback sync harness
if: runner.os == 'Linux'
shell: bash
run: |
cargo run --locked -p cap -- --log-level info selftest playback --duration 30 --json
- name: Report findings
if: always()
shell: bash
run: |
REPORT="${{ github.workspace }}/sync-matrix-${{ matrix.runner }}.json"
{
echo "## A/V sync matrix — ${{ matrix.runner }}"
echo ""
if [ -f "$REPORT" ]; then
PYTHONIOENCODING=utf-8 python3 - "$REPORT" << 'PYEOF'
import json, sys
report = json.load(open(sys.argv[1]))
print(f"Randomized seed: `{report.get('seed')}` (rerun with CAP_SYNC_MATRIX_SEED)")
print()
print("| Case | Result | Detail |")
print("| --- | --- | --- |")
for case in report.get("cases", []):
verdict = "PASS" if case["pass"] else "FAIL"
detail = case["detail"].replace("|", "\\|")
print(f"| {case['name']} | {verdict} | {detail} |")
PYEOF
else
echo "No report produced — the matrix crashed before writing results."
fi
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload findings
if: always()
uses: actions/upload-artifact@v4
with:
name: sync-matrix-${{ matrix.runner }}
path: ${{ github.workspace }}/sync-matrix-${{ matrix.runner }}.json
if-no-files-found: ignore
- name: Fail on matrix failures
if: steps.matrix.outcome == 'failure'
shell: bash
run: |
echo "Synthetic sync matrix reported failures; see the job summary." >&2
exit 1