Skip to content

Drop unused ChartAxis members and cover the empty-series guard - #962

Open
ericgriffin wants to merge 2 commits into
mainfrom
fix/chart-axis-coverage-cleanup
Open

Drop unused ChartAxis members and cover the empty-series guard#962
ericgriffin wants to merge 2 commits into
mainfrom
fix/chart-axis-coverage-cleanup

Conversation

@ericgriffin

@ericgriffin ericgriffin commented Aug 10, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #948, which merged before this landed on the branch. Addresses the
Codecov report on that PR: patch coverage 84.06%, with 11 lines uncovered in the
new chart_axis.dart.

Reading the gap rather than just filling it, only one of the three causes was
actually untested behaviour:

  • 8 lines were dead API. operator ==, hashCode and toString were added
    speculatively. ChartAxis is built fresh inside build() and is never
    compared, hashed or printed anywhere in the codebase. Removed.

  • 1 line looked structurally unreachable, and was not. The trailing
    return 10 * magnitude in _nextStepUp was removed on the grounds that
    normalized is always in [1, 10). Review on this PR caught that the claim
    is false, and the removal turned a graceful degradation into a crash. See the
    correction below.

  • The genuine gap was ChartAxis.forTrend handed an empty series, which
    still has to produce a drawable axis. That one gets a test.

Writing tests for the dead API would have been coverage theatre. chart_axis.dart
is now at 52/52 lines.

Correction: a crash introduced and fixed within this PR

The second bullet above was wrong, and Copilot's review caught it.
math.log(1000) / math.ln10 is 2.9999999999999996, so flooring it yields 2,
_magnitudeOf returns 100 instead of 1000, and normalized lands on exactly
10. The same holds at 1e6, 1e9 and 1e12. Both step ladders end at 10, so the
search for a wider rung matched nothing and threw Bad state: No element while
snapping the axis. A sweep over the public API found 22 reproducing inputs, for
instance ChartAxis.forTrend([1407.7040137891763, 5630.816055156705]).

The deleted fallback had been quietly absorbing this: it returned 10 * magnitude, which equalled the current interval, so the caller's
if (wider <= interval) break ended the loop cleanly.

Fixed at the source rather than by restoring the fallback: _magnitudeOf now
corrects the exponent in either direction, so value / magnitude genuinely sits
in [1, 10) and the firstWhere is safe. Restoring the fallback would have kept
a broken contract alive behind a plausible-looking default.

Changes

  • Remove unused operator ==, hashCode and toString from ChartAxis.
  • Simplify _nextStepUp to a single firstWhere expression.
  • Correct _magnitudeOf so value / magnitude really is in [1, 10), fixing
    the Bad state: No element crash that the firstWhere exposed.
  • Add a ChartAxis.forTrend(const []) test asserting the empty-series axis is
    still drawable and tick-aligned.
  • Add regression tests for the power-of-ten step: the two reproducing inputs
    plus a sweep over spans from 1 to 200000 asserting every axis is non-empty and
    tick-aligned. Both fail against the previous commit with that StateError.

Test Plan

  • flutter analyze passes
  • dart format . clean
  • Chart axis and stat chart tests pass (28)
  • chart_axis.dart line coverage 52/52
  • flutter test full suite passes
  • Manual testing: not applicable, no behaviour change

The removed members had no callers. The one behaviour change is the
_magnitudeOf correction, which fixes a crash.

Codecov flagged 11 uncovered lines in chart_axis.dart. Eight were
operator ==, hashCode and toString, which nothing calls: ChartAxis is
built fresh inside build() and never compared or printed. A ninth was
the trailing return in _nextStepUp, which is unreachable because
_magnitudeOf floors the exponent, so normalized is always in [1, 10) and
the 10 ending both ladders always matches. Testing either would have
been coverage theatre, so both are removed instead; _nextStepUp now uses
firstWhere without an orElse, which throws loudly if that invariant ever
breaks rather than silently returning a plausible wrong step.

The remaining gap was real behaviour: forTrend handed an empty series
still has to produce a drawable axis. That one gets a test.

chart_axis.dart is now at 48/48 lines.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR is a follow-up to #948 focused on eliminating dead ChartAxis API surface and closing the only meaningful unit-test gap: ensuring ChartAxis.forTrend still produces a valid/drawable axis when given an empty series.

Changes:

  • Added a unit test covering ChartAxis.forTrend(const []) to ensure bounds and tick alignment remain valid for empty input.
  • Simplified _nextStepUp by removing an unreachable fallback path and relying on firstWhere to enforce invariants.
  • Removed unused ChartAxis overrides (==, hashCode, toString) that have no callers in the codebase.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
test/features/statistics/presentation/widgets/chart_axis_test.dart Adds coverage for the empty-series guard to ensure the axis remains drawable and tick-aligned.
lib/features/statistics/presentation/widgets/chart_axis.dart Removes unused overrides and refactors _nextStepUp to rely on a stricter invariant check.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/features/statistics/presentation/widgets/chart_axis.dart
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

📦 Build artifacts for this PR · commit 7cbca56

Platform Download
Android (APK) android-apk
macOS build failed
Windows windows-build
Linux linux-build

Artifacts expire in 7 days. Downloading requires being signed in to GitHub. macOS needs two extractions: unzip the downloaded artifact, then unzip the submersion-macos.zip inside it to get a runnable submersion.app. The build is ad-hoc signed — right-click → Open on first launch.

Updated automatically on each push.

@ericgriffin ericgriffin self-assigned this Aug 10, 2026
@ericgriffin ericgriffin moved this from Backlog to In review in Submersion Release Tracker Aug 10, 2026
Review on this PR caught a real crash that the previous commit
introduced. Removing the trailing return in _nextStepUp was justified by
the claim that normalized is always in [1, 10), and that claim is false:
math.log(1000) / math.ln10 is 2.9999999999999996, so flooring it yields
2, the magnitude comes out as 100 instead of 1000, and normalized lands
on exactly 10. The same holds at 1e6, 1e9 and 1e12. Both step ladders
end at 10, so the search for a wider rung matched nothing and threw
"Bad state: No element" while snapping the axis. A sweep found 22
reproducing inputs through the public API, among them
ChartAxis.forTrend([1407.7040137891763, 5630.816055156705]).

Fix the invariant at its source rather than restoring a fallback that
would hide a broken contract: _magnitudeOf now corrects the exponent in
either direction, so value / magnitude genuinely sits in [1, 10) and
_nextStepUp's firstWhere is safe.

Covered by the two reproducing cases and a sweep over spans from 1 to
200000 asserting each axis is non-empty and tick-aligned; both fail
against the previous commit with the exact StateError.
Copilot AI review requested due to automatic review settings August 10, 2026 22:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

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

Labels

None yet

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

2 participants