Skip to content

Convert test suite to Swift Testing with BDD scenarios - #7

Open
ARamy23 wants to merge 1 commit into
Abdo-codes:mainfrom
ARamy23:tests/bdd-characterization-suite
Open

Convert test suite to Swift Testing with BDD scenarios#7
ARamy23 wants to merge 1 commit into
Abdo-codes:mainfrom
ARamy23:tests/bdd-characterization-suite

Conversation

@ARamy23

@ARamy23 ARamy23 commented Jul 28, 2026

Copy link
Copy Markdown

Rewrites NightcapAppTests from XCTest to Swift Testing, organised as one @Suite per feature with numbered Given/When/Then scenarios. No production code is touched — this characterises current behaviour rather than changing it.

Gaps this found

  • removeAppRequested had zero coverage. Three scenarios now cover removing a running app, removing an idle one, and removing one of two running apps (the assertion must survive for the other).
  • test_terminate_event_keeps_assertion_when_another_instance_still_running asserted nothing — it only checked the store didn't mutate. It now verifies release() is never called.
  • No scenario covered IOKit refusing the assertion. Added: the app must not report the Mac as being kept awake when IOPMAssertionCreateWithName fails.
  • The assertion reason string is now asserted when a second watched app launches, so what shows up in pmset -g assertions stays truthful.

Parallelism fix

Swift Testing runs suites in parallel where XCTest ran serially. AppFeature.State uses @Shared(.fileStorage(...)) against a real container JSON, which would be shared mutable state across scenarios. Each test store now gets defaultFileStorage = .inMemory — isolation rather than forcing .serialized, so the suite stays parallel.

Coverage

scripts/check-domain-coverage.sh enforces a floor on pure-domain coverage from an .xcresult. Live adapters (NSWorkspace, IOKit, SMAppService, StoreKit) and SwiftUI views are excluded by design — covering those means integration tests, not characterisation.

before after
Scenarios 15 20
WatchedApp.swift 100% 100%
LaunchAtLoginStatus.swift 100% 100%
AppFeature.swift 93.14% 95.67%
Domain total 93.70% 96.04% (291/303)

Baseline measurements are recorded in docs/roadmap/S1-coverage-baseline.md.

One behaviour worth a look

syncAssertion calls assertion.release() unconditionally whenever no watched app is running — including on .onAppear when nothing was ever held. AssertionHolder guards on held, so it's a harmless no-op at the IOKit layer, but it's visible to any test counting release calls. Characterised as-is rather than changed; happy to tighten it if you'd prefer.

Notes

  • .gitignore gains project.local.yml, a local xcodegen spec override. project.yml pins DEVELOPMENT_TEAM, so contributors without that team can't build; a local override spec lets them use ad-hoc signing without dirtying the repo.
  • All 20 scenarios verified passing on Xcode 27.

https://claude.ai/code/session_01WGsVLm7Vs83QN4o1tCaCLw

Rewrites NightcapAppTests from XCTest to Swift Testing, organised as one
@suite per feature with numbered Given/When/Then scenarios. Behaviour is
characterised rather than changed: no production code is touched.

Notable additions beyond a straight translation:

- removeAppRequested had no coverage at all. Three scenarios now cover
  removing a running app, removing an idle one, and removing one of two
  running apps (the assertion must survive for the other).
- The assertion reason string is asserted when a second watched app
  launches, so the pmset-visible reason stays truthful.
- A scenario covers IOKit refusing the assertion, where the app must not
  claim the Mac is being kept awake.
- test_terminate_event_keeps_assertion_when_another_instance_still_running
  previously asserted nothing. It now checks that release() is not called.

Tests are given an in-memory file storage dependency. Swift Testing runs
suites in parallel, and @shared(.fileStorage) would otherwise be shared
mutable state across scenarios.

scripts/check-domain-coverage.sh enforces a floor on pure-domain coverage
from an .xcresult bundle. Live adapters (NSWorkspace, IOKit, SMAppService,
StoreKit) and SwiftUI views are excluded by design; covering those means
integration tests, not characterisation.

20 scenarios, all passing. Domain coverage 96.04% (291/303), up from
93.70% (284/303).

Claude-Session: https://claude.ai/code/session_01WGsVLm7Vs83QN4o1tCaCLw
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.

1 participant