Skip to content

Build test apps without SDL, so tests that build them run in parallel - #724

Merged
stanoddly merged 2 commits into
mainfrom
test-app-without-sdl
Nov 14, 2026
Merged

stanoddly merged 2 commits into
mainfrom
test-app-without-sdl

Conversation

@botoddly

Copy link
Copy Markdown
Collaborator

TestApp.CreateBuilder() started SDL, which belongs to the whole process: one event queue, SDL_Quit for every subsystem, global hints and a main-thread rule. So only one test app could exist at a time, and every test that built one was [NonParallelizable]. A test app now never starts SDL, and tests that build apps run in parallel.

Windows

  • Window is platform-neutral and abstract. The new NativeWindow holds the SDL window, and SwapchainWindow and headless mode's OffscreenWindow derive from it.
  • StubWindow is a window without a platform, on one display of 1024 × 768 at scale 1, as SDL's dummy driver reports it. A new size raises ResolutionChanged in the next frame, before any update.
  • Text input moves into the window. WindowRegistry keeps windows only and matches SDL window IDs on native windows.

Services

  • A frame reads the platform through an internal IEventSource. EventService is the SDL one, and StubEventSource raises the stub windows' size changes.
  • PixelyAppBuilder.Build() registers PlatformInfo, the cursor, clipboard, platform settings and display services, and the event source only when nothing has registered them. AddGpuServices does the same for the font system, and RegisterAudio for the audio system.
  • A test app registers stubs for all of them: StubCursorService, StubClipboardService, StubPlatformSettings, StubDisplayService, StubFontSystem and StubAudioSystem.
  • The keyboard, mouse, text input and isolated gamepad services no longer start SDL.
  • Cursor has an internal owner interface, and the cursor checks are shared by both cursor services.

Audio

  • AudioSystem is an abstract base with internal *Core methods, as GpuDevice is. SdlAudioSystem and SdlAudioStream hold the SDL_mixer code.
  • StubAudioSystem reads no file and plays nothing. A source keeps the state SDL_mixer gives it, a sound plays until it is stopped or the test calls Finish, and LoadedPath tells which sound a clip is.
  • Behavior changes for real apps:
    • SDL audio starts after SDL itself, so the audio driver hint and app metadata are set first.
    • Swapping a playing source's clip no longer leaves the track without input between the detach and the attach.

Differences from SDL's dummy driver

In a test app:

  • always-on-top, dragging, the hit test and system cursors work
  • file dialogs fail, and locales are empty
  • a window created fullscreen restores to its configured size, where the dummy driver gives 1 × 1
  • text measures with fixed metrics, and font and audio paths are never read
  • a test app has an audio system even when the game registers none

docs/testing.md lists them, and DECISIONS.md has the entry.

Testing

  • New tests:
    • characterization tests of a test app's windows and services, written on the old implementation
    • characterization tests of the SDL_mixer audio system on the dummy driver, written before the seam
    • stub audio tests
    • a test that a test app never starts SDL (SDL_WasInit stays 0) through build, frames, input, fonts, audio and disposal
    • a test that builds apps on 8 threads at once
  • Pixely.Tests, Pixely.Testing.Tests and Pixely.Ui.Tests run fixtures in parallel. Three fixtures that use SDL_ttf or an environment variable stay [NonParallelizable].
  • On Linux:
    • the solution builds for both targets with no warnings
    • every test project with tests passes: Pixely.Tests 735, Pixely.Testing.Tests 142, Pixely.Ui.Tests 528
    • Pixely.Package.Tests is [Explicit] and did not run locally

@stanoddly
stanoddly merged commit aee3850 into main Nov 14, 2026
3 checks passed
@stanoddly
stanoddly deleted the test-app-without-sdl branch November 14, 2026 14:32
@github-actions

Copy link
Copy Markdown

📦 Development package Pixely 0.0.175 is available.

Workflow run (attempt 1)

@github-actions

Copy link
Copy Markdown

✅ Development package validation passed on all platforms.

Workflow run (attempt 1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants