Skip to content

ci: measure test coverage and upload as artifact#323

Merged
TaprootFreak merged 1 commit into
developfrom
ci/coverage-measurement
May 15, 2026
Merged

ci: measure test coverage and upload as artifact#323
TaprootFreak merged 1 commit into
developfrom
ci/coverage-measurement

Conversation

@TaprootFreak
Copy link
Copy Markdown
Contributor

Summary

Adds flutter test --coverage to the existing PR workflow and uploads the (filtered) lcov.info as a CI artifact.

  • flutter test --coverage replaces the plain flutter test invocation
  • coverage/lcov.info is filtered with lcov --remove to drop generated files (lib/generated/**) and lib/main.dart so the artifact reflects only the activated surface
  • The filtered report is uploaded as artifact coverage-lcov on every run (if: always())
  • lcov --summary prints to the CI log so contributors can eyeball the line/function/branch numbers without downloading the artifact

Why

Step 1 of the coverage infrastructure roadmap documented in PR #322. Without measurement, the 100% rule is unverifiable; with measurement but no threshold, this PR is safe to merge today and lays the foundation for the threshold gate in a follow-up.

What this PR is not

  • Does not enforce any threshold. The build still passes regardless of coverage %. Hard-gating below 100% would block every open PR today (current coverage ratio ≈ 10%).
  • Does not add any new tests.
  • Does not change which files exist in test/.

Test plan

  • CI run completes green
  • coverage-lcov artifact is present on the PR's checks page
  • lcov --summary line of the CI log shows non-zero coverage on lib/packages/**
  • Filtered report excludes lib/generated/**

flutter test now runs with --coverage. The generated coverage/lcov.info
is filtered to remove generated code (lib/generated/**) and the main
entrypoint, then uploaded as an artifact so coverage can be inspected
without re-running the suite locally.

No threshold enforcement yet — see the README 'Coverage infrastructure
roadmap'. This step only produces a baseline measurement; the gate
that fails the build below 100% lands in a follow-up.
TaprootFreak added a commit that referenced this pull request May 15, 2026
)

## Summary
Adds 38 unit tests across six previously-untested files in
`lib/packages/utils/` and `lib/packages/wallet/`. Each spec lives in the
mirror path under `test/` per the project convention.

| File under test | Test file | Cases |
| --- | --- | --- |
| `lib/packages/utils/fast_hash.dart` |
`test/packages/utils/fast_hash_test.dart` | 5 |
| `lib/packages/utils/jwt_decoder.dart` |
`test/packages/utils/jwt_decoder_test.dart` | 7 |
| `lib/packages/wallet/payment_uri.dart` |
`test/packages/wallet/payment_uri_test.dart` | 4 |
| `lib/packages/wallet/wallet_account.dart` |
`test/packages/wallet/wallet_account_test.dart` | 7 |
| `lib/packages/wallet/wallet.dart` |
`test/packages/wallet/wallet_test.dart` | 11 |
| `lib/packages/wallet/eip7702_signer.dart` |
`test/packages/wallet/eip7702_signer_test.dart` | 4 |

## What each file covers
- **fast_hash:** FNV-1a determinism, distinct inputs differ, ordering
matters, unicode safety, empty-string offset basis.
- **jwt_decoder:** well-formed payload parsing, segment-count errors,
non-map payload rejection, all three valid base64url padding lengths,
illegal length-mod-4 rejection.
- **payment_uri:** empty-amount short form, dotted and comma-locale
amounts, preservation of decimal precision.
- **wallet_account:** BIP-44 derivation path format, deterministic
Hardhat account #0 address from the standard test mnemonic, distinct
indices, signMessage shape + determinism + sensitivity to addressIndex.
- **wallet:** `SoftwareWallet` walletType + primary/current account
identity + `selectAccount` semantics + id/name mutability; `DebugWallet`
sign refusal.
- **eip7702_signer:** hardware-credential refusal (BitBox cannot sign
EIP-7702), software-credential signing, signature determinism,
nonce-sensitivity.

## Why
Stage 2 of the coverage push tracked in the README features matrix
([#322](#322)). These six
files are pure-Dart with no platform dependencies, so they were the
cheapest meaningful coverage win available — no service mocks, no widget
rendering, no merge-conflict risk with the three in-flight test PRs
([#319](#319),
[#320](#320),
[#321](#321)).

## Test plan
- [x] `flutter analyze` clean on all six new test files (locally on
Flutter 3.38.5)
- [x] `flutter test test/packages/utils/ test/packages/wallet/` — 38 /
38 passing
- [ ] CI green on this branch
- [ ] Spot-check: coverage artifact from #323 (once that lands) shows
non-zero coverage on the six files above
@TaprootFreak TaprootFreak marked this pull request as ready for review May 15, 2026 07:24
@TaprootFreak TaprootFreak merged commit 75f0f90 into develop May 15, 2026
1 check passed
@TaprootFreak TaprootFreak deleted the ci/coverage-measurement branch May 15, 2026 07:24
TaprootFreak added a commit that referenced this pull request May 23, 2026
- fake_async example: add bitbox_flutter.dart import and inline the
  pairedServiceSync idiom so the snippet is copy-paste compilable
- platform-coupled section: real-plugin counterpart is Tier 2/3, not
  Tier 1/2 (Tier 1 is FakeBitboxCredentials by definition); add the
  CONTRIBUTING.md footnote-165 caveat that the annotation is the
  documenting form today
- README: PRs #319/#320/#321/#322/#323 are all merged since
  2026-05-15; replace "in flight"/"landing in"/"extended in" with
  "added via" / "have closed" / "partially covered after"
TaprootFreak added a commit that referenced this pull request May 25, 2026
## Summary

Audited every claim in `docs/testing.md` against the current repo state.
Two stale entries fixed; everything else verified accurate and left
untouched.

## Findings & fixes

### Tier-table (L5-11)
- **Status:** accurate. 5 tiers, Tier 0/1 CI both shown as green
(`flutter test --coverage`) — matches
`.github/workflows/pull-request.yaml` reality.

### Tier 3 coverage-artifact claim (L369)
- **Before:** "Coverage is uploaded as an artifact (see #323)" under the
`tier3-handbook.yaml` sentence.
- **Finding:** `tier3-handbook.yaml` only uploads the
`handbook-captures` artifact (per-flow diagnostic recordings). The
coverage artifacts (`coverage-lcov`, `coverage-summary`) come from the
`Analyze & Test` job in `pull-request.yaml`, not from Tier 3. PR
[#323](#323) was about Tier
0/1 coverage upload.
- **Fix:** Reworded the paragraph to correctly attribute artifact
provenance and surface the floor protocol (`.coverage-floor-lines =
100`, `.coverage-floor-functions = 50`).

### Surface-needs-infra table (L375-378 in new layout)

**Drift wrappers row (was L377)** — removed.
- **Before:** "wrapper specs are not written yet"
- **Finding:** All 5 Drift-backed repos (asset, balance, cache,
transaction, wallet) have tests under `test/packages/repository/` that
already use `AppDatabase.forTesting(NativeDatabase.memory())` — exactly
the pattern the row prescribed. Even the 3 non-Drift repos (settings,
supported_fiat, supported_language) have specs.
- **Path correction:** doc said `database.dart`, actual path is
`lib/packages/storage/database.dart`.
- **Fix:** Row deleted; replaced with a short resolution note + correct
path + test-file pointer.

**Page-widgets `getIt<X>()` row (was L378)** — kept as-is.
- **Finding:** Still valid. `lib/screens/dashboard/dashboard_page.dart`,
`lib/screens/receive/receive_page.dart`,
`lib/screens/settings/settings_page.dart` still call `getIt<X>()`
directly in `build` / `BlocProvider.create`. Many sub-pages do as well.

**`chain_asset_icon` / `image_picker_sheet` row (was L379)** — kept
as-is.
- **Finding:** Both files still exist under `lib/widgets/`. No tests for
either. The recent `precacheImages` fix in
[#575](#575) only affects
golden tests (visual regression), not unit tests for these widgets.

### Sumsub paragraph (L381 / L382 in new layout)
- **Status:** accurate.
`lib/screens/kyc/steps/ident/cubits/kyc_ident/sumsub_ident_port.dart`
(interface), `lib/screens/kyc/steps/ident/sumsub_ident_sdk_adapter.dart`
(adapter, outside cubit folder), `@no-integration-test` annotation
present, test file at the claimed path.

### DocumentsDirectoryPort paragraph (L383 / L384 in new layout)
- **Status:** accurate. `lib/packages/io/documents_directory_port.dart`
+ `lib/packages/io/path_provider_adapter.dart` exist with the documented
`coverage:ignore` + `@no-integration-test` annotations.
`AppDatabase.getDatabasePath()` accepts a `DocumentsDirectoryPort` arg
as claimed.

### Adding tests for BitBox-related code (L388-)
- **Status:** accurate. All five referenced classes resolve in `lib/`.
`enum FakeBitboxBehavior` exists in
`test/helper/fake_bitbox_credentials.dart` with the documented variants.

### README coverage sections
- **Status:** accurate. The 100% rule is correctly framed as the target,
the floor protocol matches `.coverage-floor-lines = 100` /
`.coverage-floor-functions = 50` reality.

## Verification

- `flutter analyze test/` — 110 pre-existing issues (same as baseline
before the edits; all `generated/i18n.dart` related). No regression
introduced by docs edits.
- Every referenced file/symbol verified with `grep` / `find` against the
current `develop` HEAD.

## Test plan

- [ ] Reviewer spot-checks the two changed paragraphs against the
workflow files / test directory.
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