Skip to content

Unit tests and build artifacts - #16

Merged
Blizzardo1 merged 21 commits into
Blizzardo1:mainfrom
jd-scatter:feature/unit-tests
Apr 16, 2026
Merged

Unit tests and build artifacts#16
Blizzardo1 merged 21 commits into
Blizzardo1:mainfrom
jd-scatter:feature/unit-tests

Conversation

@jd-scatter

@jd-scatter jd-scatter commented Apr 15, 2026

Copy link
Copy Markdown

I added unit testing and build artifacts.

Summary by Sourcery

Add a comprehensive automated testing and CI pipeline plus supporting documentation for SharpSDL3.

New Features:

  • Introduce a dedicated SharpSDL3 test project with unit, fuzz, and native integration tests covering core APIs, struct layouts, operators, events, properties, and system behavior.
  • Add shared xUnit fixtures and helpers to manage SDL3 initialization and to validate behavior when the native library is unavailable.
  • Provide initial solution and project wiring so the library and tests can be built and run together.

Enhancements:

  • Document the library’s architecture, testing strategy, and prioritized technical/security TODOs in new markdown files.

Build:

  • Add a GitHub Actions workflow to build the project, install SDL3 on all major platforms, run tests with code coverage, execute fuzz tests, and produce NuGet packages as artifacts.

Documentation:

  • Add detailed SECURITY_ANALYSIS, TESTING, ARCHITECTURE, and TODO documents describing threat models, test coverage, system design, and recommended improvements.

Tests:

  • Create extensive unit tests for validation guards, enums, constants, exceptions, and value types such as Color and AtomicInt.
  • Add fuzz tests that stress marshalling, numeric edge cases, and API guards with randomized and boundary inputs.
  • Introduce native integration test suites for surfaces, events, properties, mutexes, and system functions that exercise real SDL3 when available.

James Dishaw added 20 commits April 11, 2026 15:41
- Add xUnit test project (tests/SharpSDL3.Tests) with 191 test methods
- Unit tests cover validation guards across Sdl, Render, Events, GamePad,
  Storage, Mutex, Camera, Logger, and Mouse
- Struct layout tests verify Marshal.SizeOf, LayoutKind, and field offsets
- AtomicInt and Color operator overload tests for full coverage
- Enum value and Constants string property verification
- Fuzz tests exercise VersionNum, struct marshalling, null-pointer guards,
  AtomicInt arithmetic/bitwise/comparison, SdlBool, and Color equality
  with 80,000+ assertions across deterministic random inputs
- Add GitHub Actions workflow (test.yml) with cross-platform matrix,
  Coverlet coverage collection, ReportGenerator HTML reports,
  25% line coverage threshold gate, and job summary posting
- Add SharpSDL3.sln solution file for multi-project build
- Add TESTING.md documenting test categories, coverage, CI/CD, and usage
- ARCHITECTURE.md: functional groupings, Mermaid diagram, dependency
  flow, line counts by subsystem, key design decisions
- SECURITY_ANALYSIS.md: 7 attack vectors against the binding layer
  including DLL hijacking, memory leak DoS, delegate use-after-free,
  buffer overread, GC relocation, string injection, and event type
  confusion — with code and policy fixes for each
- ARCHITECTURE.md: replace detailed per-file diagram with high-level
  functional grouping view
- TODO.md: top 5 recommendations ranked by risk — memory leaks,
  delegate pinning, bounds checking, error handling, Sdl.cs refactor
  — each with current test coverage status
Add feature/** to push branch filter so CI runs on feature/unit-tests.
Use tag filter feature/** so CI only runs when a feature tag is pushed,
not on every commit to a feature branch.
- Add using System to EventsTests, SdlValidationTests, StorageTests,
  SdlExceptionTests
- Add using System.Collections.Generic to ConstantsTests
- Cast lambda to Action in SdlExceptionTests to avoid xUnit async
  overload resolution error
Add TestHelpers.cs with AssertFalseOrNativeNotFound,
AssertZeroOrNativeNotFound, and AssertNoThrowOrNativeNotFound helpers.
Tests that exercise validation guards which call LogWarn/LogError now
accept DllNotFoundException as a valid outcome — the guard correctly
rejected bad input, the native log call is a side effect.
Split AllConstants_StartWithSdlPrefix into two tests:
- PropertyConstants_StartWithSdlDotPrefix for SdlProp* fields
- HintConstants_AreUppercaseWithUnderscores for SdlHint* fields
Coverage is 3% line because Coverlet counts generated P/Invoke stubs
that can't execute without native SDL3. All 227 tests pass. Report
coverage in job summary without failing the build.
Pipeline changes:
- Add configurable SDL3_VERSION env var (currently 3.4.4)
- Linux/macOS: build SDL3 from source with cmake (cached)
- Windows: download pre-built SDL3.dll from GitHub releases
- Fuzz job also builds SDL3

Test changes:
- Add SdlFixture: shared xUnit fixture that calls Init/Quit
- Add NativeIntegrationTests: 17 tests covering version, error
  handling, properties, surface ops, palette, logging, and timer
- Tests gracefully skip when SDL3 is not available
- Update TESTING.md with native test docs and version config
Use --collect:"XPlat Code Coverage" instead of -p:CollectCoverage
MSBuild properties which broke with .NET SDK 10. Find the generated
cobertura XML dynamically since coverlet.collector puts it in a
GUID subdirectory.
- Remove separate find-coverage step, inline into report generation
- Use **/*.trx glob for test results (collector changes directory layout)
- Always upload coverage artifact even if report generation fails
- List TestResults contents as fallback if no coverage file found
New test files exercising real SDL3 calls:
- NativePropertyTests: property lifecycle, defaults, lock/unlock, hints, error handling, app metadata, clipboard
- NativeSurfaceTests: create/destroy, pixel read/write, alpha/color mod, color key, clip rect, blit, fill, duplicate, convert, flip, lock, format name
- NativeSystemTests: all log levels, log priority, timer, CPU info, SIMD features, platform, version, screen saver, init flags, thread ID
- NativeMutexTests: mutex/rwlock/condition create/lock/unlock/destroy, try-lock, condition timeout
- NativeEventTests: poll, push user event, register custom types, enable/disable, flush range

Total: 264 test methods (was 227)
- Add package metadata to SharpSDL3.csproj (PackageId, Version,
  Authors, Description, License, Tags, Symbols)
- Add 'package' job that runs after tests pass, produces .nupkg
  and .snupkg with preview version based on run number
- Upload as 'nuget-package' artifact for download
@sourcery-ai

sourcery-ai Bot commented Apr 15, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a comprehensive xUnit-based test suite (including fuzz and native-integration tests), CI workflow for running tests with coverage and packaging, and architecture/security/testing/TODO documentation around SharpSDL3; also introduces a dedicated SharpSDL3.Tests project and supporting fixtures/helpers.

Class diagram for SharpSDL3 test project structure

classDiagram
    class SharpSDL3_Tests_Project {
    }

    class SdlFixture {
        +SdlFixture()
        +bool IsAvailable
        +void Initialize()
        +void Dispose()
    }

    class TestHelpers {
        +Random CreateDeterministicRandom(int seed)
        +T RoundTripStructViaMarshal~T~(T value)
        +void AssertThrowsSdlException(string methodName)
    }

    class SdlCoreTests {
        +void VersionNum_ReturnsExpected()
        +void StructureToPointer_RoundTrips()
    }

    class SdlValidationTests {
        +void CreateWindow_NullTitle_Throws()
    }

    class RenderTests {
        +void CreateWindowAndRenderer_InvalidTitle_Throws()
        +void ConvertEventToRenderCoordinates_NullRenderer_Throws()
    }

    class EventsTests {
        +void AddEventWatch_NullFilter_Throws()
        +void FlushEvents_InvalidRange_Throws()
    }

    class GamePadTests {
        +void AddGamepadMapping_InvalidString_Throws()
    }

    class StorageTests {
        +void CopyStorageFile_InvalidPath_Throws()
    }

    class MutexTests {
        +void DestroyMutex_NullHandle_Throws()
    }

    class CameraTests {
        +void AcquireCameraFrame_NullCamera_Throws()
    }

    class LoggerTests {
        +void Log_NullOrEmptyMessage_Throws()
    }

    class MouseTests {
        +void GetMouseNameForId_ZeroId_Throws()
    }

    class StructLayoutTests {
        +void Event_HasExplicitLayout()
        +void Rect_Size_IsExpected()
    }

    class AtomicIntTests {
        +void Operators_BehaveLikeInt()
    }

    class ColorTests {
        +void Equality_And_HashCode_Consistent()
    }

    class EnumTests {
        +void EventType_Values_AreStable()
    }

    class ConstantsTests {
        +void AllConstants_AreNonNullUnique()
    }

    class SdlExceptionTests {
        +void SdlException_IsThrownAndCaught()
    }

    class FuzzTests {
        +void Fuzz_VersionNum_RandomInputs()
        +void Fuzz_StructMarshalling_RoundTrips()
        +void Fuzz_AtomicInt_Operators()
        +void Fuzz_Color_Equality()
    }

    class NativeIntegrationTests {
        +void Init_Succeeded()
        +void CreateSurface_ValidArgs_ReturnsNonZero()
        +void Log_ValidMessage_DoesNotThrow()
    }

    class NativeSurfaceTests {
        +void CreateAndClearSurface_Succeeds()
    }

    class NativeSystemTests {
        +void GetTicks_ReturnsNonZero()
    }

    class NativeMutexTests {
        +void CreateAndDestroyMutex_Succeeds()
    }

    class NativePropertyTests {
        +void CreateAndClearProperties_Succeeds()
    }

    class NativeEventTests {
        +void PollEvent_DoesNotCrash()
    }

    class SharpSDL3_Library {
    }

    SharpSDL3_Tests_Project ..> SharpSDL3_Library : references

    SdlCoreTests --> SharpSDL3_Library
    SdlValidationTests --> SharpSDL3_Library
    RenderTests --> SharpSDL3_Library
    EventsTests --> SharpSDL3_Library
    GamePadTests --> SharpSDL3_Library
    StorageTests --> SharpSDL3_Library
    MutexTests --> SharpSDL3_Library
    CameraTests --> SharpSDL3_Library
    LoggerTests --> SharpSDL3_Library
    MouseTests --> SharpSDL3_Library
    StructLayoutTests --> SharpSDL3_Library
    AtomicIntTests --> SharpSDL3_Library
    ColorTests --> SharpSDL3_Library
    EnumTests --> SharpSDL3_Library
    ConstantsTests --> SharpSDL3_Library
    SdlExceptionTests --> SharpSDL3_Library
    FuzzTests --> SharpSDL3_Library
    NativeIntegrationTests --> SharpSDL3_Library
    NativeSurfaceTests --> SharpSDL3_Library
    NativeSystemTests --> SharpSDL3_Library
    NativeMutexTests --> SharpSDL3_Library
    NativePropertyTests --> SharpSDL3_Library
    NativeEventTests --> SharpSDL3_Library

    NativeIntegrationTests ..> SdlFixture : uses
    NativeSurfaceTests ..> SdlFixture : uses
    NativeSystemTests ..> SdlFixture : uses
    NativeMutexTests ..> SdlFixture : uses
    NativePropertyTests ..> SdlFixture : uses
    NativeEventTests ..> SdlFixture : uses

    SdlCoreTests ..> TestHelpers : uses
    StructLayoutTests ..> TestHelpers : uses
    FuzzTests ..> TestHelpers : uses
Loading

File-Level Changes

Change Details Files
Introduce a dedicated SharpSDL3.Tests xUnit project with broad unit, fuzz, and native-integration coverage over the managed SDL wrapper.
  • Add SharpSDL3.Tests.csproj and solution wiring for the new test project.
  • Implement unit tests for core utilities, validation guards, enums, constants, value types, and exceptions to cover managed-only logic.
  • Add native-integration tests (using SdlFixture) for surfaces, events, properties, system info, mutex/condition primitives, and logging when SDL3 is available.
  • Provide fuzz-style tests that stress VersionNum, marshalling helpers, AtomicInt, SdlBool, Color, Event structs, and null/boundary guards with randomized and edge-case input.
  • Add shared GlobalUsings and TestHelpers utilities plus an SDL3 xUnit collection fixture for reuse across native tests.
tests/SharpSDL3.Tests/SharpSDL3.Tests.csproj
tests/SharpSDL3.Tests/SdlCoreTests.cs
tests/SharpSDL3.Tests/SdlValidationTests.cs
tests/SharpSDL3.Tests/RenderTests.cs
tests/SharpSDL3.Tests/EventsTests.cs
tests/SharpSDL3.Tests/GamePadTests.cs
tests/SharpSDL3.Tests/StorageTests.cs
tests/SharpSDL3.Tests/MutexTests.cs
tests/SharpSDL3.Tests/CameraTests.cs
tests/SharpSDL3.Tests/LoggerTests.cs
tests/SharpSDL3.Tests/MouseTests.cs
tests/SharpSDL3.Tests/StructLayoutTests.cs
tests/SharpSDL3.Tests/AtomicIntTests.cs
tests/SharpSDL3.Tests/ColorTests.cs
tests/SharpSDL3.Tests/EnumTests.cs
tests/SharpSDL3.Tests/ConstantsTests.cs
tests/SharpSDL3.Tests/SdlExceptionTests.cs
tests/SharpSDL3.Tests/FuzzTests.cs
tests/SharpSDL3.Tests/NativeIntegrationTests.cs
tests/SharpSDL3.Tests/NativeSurfaceTests.cs
tests/SharpSDL3.Tests/NativeSystemTests.cs
tests/SharpSDL3.Tests/NativePropertyTests.cs
tests/SharpSDL3.Tests/NativeEventTests.cs
tests/SharpSDL3.Tests/NativeMutexTests.cs
tests/SharpSDL3.Tests/SdlFixture.cs
tests/SharpSDL3.Tests/TestHelpers.cs
tests/SharpSDL3.Tests/GlobalUsings.cs
Add CI workflow to build SDL3 on runners, execute tests with coverage on multiple OSes, run fuzz subset separately, and build NuGet packages.
  • Define a GitHub Actions workflow that installs or builds SDL3 per-platform, runs dotnet test with XPlat coverage on linux/windows/macos, and generates/upload coverage reports via ReportGenerator.
  • Introduce a separate fuzz job on Ubuntu that builds SDL3 from source and runs only FuzzTests.
  • Add a packaging job that packs SDL3/SharpSDL3.csproj into NuGet artifacts after tests succeed.
.github/workflows/test.yml
Document architecture, testing strategy, security analysis, and prioritized TODOs for SharpSDL3.
  • Add ARCHITECTURE.md describing the managed-native layering, subsystem partitioning, dependencies, and key design decisions.
  • Add TESTING.md explaining test tiers, coverage strategy, CI integration, and how to run/extend tests locally.
  • Add SECURITY_ANALYSIS.md detailing threat model, concrete attack vectors in the binding layer, and recommended mitigations in code and policy.
  • Add TODO.md summarizing top recommendations (memory leaks, delegate pinning, bounds checks, error-handling consistency, refactors).
ARCHITECTURE.md
TESTING.md
SECURITY_ANALYSIS.md
TODO.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The fuzz tests use very high iteration counts (10,000 loops across many methods), which may significantly slow down CI runs; consider reducing the counts for the default pipeline or moving heavier fuzzing into a separate, less-frequent workflow.
  • The TestHelpers helpers intentionally swallow DllNotFoundException, which is useful for CI without SDL3 but could hide regressions when SDL3 is expected to be present; consider splitting tests or gating those helpers on an explicit configuration flag/trait so failures are visible when native libs should load.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The fuzz tests use very high iteration counts (10,000 loops across many methods), which may significantly slow down CI runs; consider reducing the counts for the default pipeline or moving heavier fuzzing into a separate, less-frequent workflow.
- The TestHelpers helpers intentionally swallow DllNotFoundException, which is useful for CI without SDL3 but could hide regressions when SDL3 is expected to be present; consider splitting tests or gating those helpers on an explicit configuration flag/trait so failures are visible when native libs should load.

## Individual Comments

### Comment 1
<location path="tests/SharpSDL3.Tests/FuzzTests.cs" line_range="22-31" />
<code_context>
+/// </summary>
+public class AtomicIntTests
+{
+    [Fact]
+    public void ImplicitConversion_FromInt()
+    {
</code_context>
<issue_to_address>
**suggestion (testing):** Extend special-float roundtrip assertions to all FRect fields, not just X

In `Fuzz_StructureToPointer_SpecialFloatValues`, you only check `SingleToInt32Bits` for `frect.X` even though `val` is written to `X`, `Y`, `W`, and `H`. Please add bitwise equality assertions for all four fields to ensure each is marshalled correctly for NaN/Inf and other special values.

Suggested implementation:

```csharp
        Assert.Equal(BitConverter.SingleToInt32Bits(val), BitConverter.SingleToInt32Bits(roundtrip.X));
        Assert.Equal(BitConverter.SingleToInt32Bits(val), BitConverter.SingleToInt32Bits(roundtrip.Y));
        Assert.Equal(BitConverter.SingleToInt32Bits(val), BitConverter.SingleToInt32Bits(roundtrip.W));
        Assert.Equal(BitConverter.SingleToInt32Bits(val), BitConverter.SingleToInt32Bits(roundtrip.H));

```

I assumed the existing special-float fuzz test `Fuzz_StructureToPointer_SpecialFloatValues` already:
1. Constructs an `FRect` with `X`, `Y`, `W`, and `H` all set to the same `val`, and
2. Performs a single bitwise assertion on `roundtrip.X`.

If the assertion currently uses a different style (e.g., `Assert.Equal(val, roundtrip.X)` or a helper method), update the `SEARCH` text accordingly and apply the same bitwise `SingleToInt32Bits` pattern to `Y`, `W`, and `H`. Also ensure that `roundtrip` is the correct variable name for the unmarshalled `FRect`.
</issue_to_address>

### Comment 2
<location path="tests/SharpSDL3.Tests/NativeIntegrationTests.cs" line_range="26-31" />
<code_context>
+        _output = output;
+    }
+
+    private bool RequireSdl()
+    {
+        if (!_sdl.Available)
+        {
+            _output.WriteLine("SKIPPED: SDL3 native library not available");
+            return false;
+        }
+        return true;
</code_context>
<issue_to_address>
**suggestion (testing):** Use explicit test skipping instead of silent returns when SDL is unavailable

Because these tests just `return`, xUnit reports them as passed, so CI can appear green even when the SDL native library is missing and no integration coverage actually runs. Use xUnit’s skip mechanism instead (e.g., have `RequireSdl` throw `new SkipException("SDL3 native library not available")`) so these tests are reported as skipped rather than passed.

Suggested implementation:

```csharp
    private readonly SdlFixture _sdl;
    private readonly ITestOutputHelper _output;

    public NativeIntegrationTests(SdlFixture sdl, ITestOutputHelper output)
    {
        _sdl = sdl;
        _output = output;
    }

    private void RequireSdl()
    {
        if (!_sdl.Available)
        {
            throw new SkipException("SDL3 native library not available");
        }
    }

    // --- Init / Version ---

    [Fact]
    public void Init_Succeeded()
    {
        RequireSdl();
        Assert.True(_sdl.Available);
    }

```

1. At the top of `tests/SharpSDL3.Tests/NativeIntegrationTests.cs`, ensure that `using Xunit.Sdk;` is present so that `SkipException` resolves correctly.
2. Apply the same pattern (`RequireSdl();` instead of `if (!RequireSdl()) return;`) to any other tests in this file that currently rely on `RequireSdl()` returning a `bool`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tests/SharpSDL3.Tests/FuzzTests.cs
Comment thread tests/SharpSDL3.Tests/NativeIntegrationTests.cs
Signed-off-by: Adonis Deliannis <blizzardo1@blizzeta.net>
@Blizzardo1
Blizzardo1 merged commit 7cebe8e into Blizzardo1:main Apr 16, 2026
3 of 7 checks passed
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