Skip to content

Expand core-engine unit test coverage (Tiers 1–4) - #2

Merged
aaronbrethorst merged 22 commits into
developfrom
tests2
Apr 22, 2026
Merged

Expand core-engine unit test coverage (Tiers 1–4)#2
aaronbrethorst merged 22 commits into
developfrom
tests2

Conversation

@aaronbrethorst

@aaronbrethorst aaronbrethorst commented Apr 22, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds Mockito 5, AssertJ 3, upgrades JUnit to 4.13.2 (pins byte-buddy to resolve a Hibernate classpath conflict).
  • Adds 20 new transitclock test classes covering pipeline, matching, prediction, headway/holding, and GTFS-ingest code.
  • Test count: 339 → 477 (all passing locally on mvn -pl transitclock test).

What's covered

Tier 1 (pipeline): TravelTimes, SpatialMatcher, TemporalMatcher, PredictionGeneratorDefaultImpl, ArrivalDepartureGeneratorDefaultImpl, AvlProcessor.
Tier 2 (state + math): SpatialMatch, core VehicleState, ServiceUtils, BlockAssigner, RealTimeSchedAdhProcessor.
Tier 3 (generators): scheduled + frequency KalmanPredictionGeneratorImpl, HoldingTimeGeneratorDefaultImpl, LastDepartureHeadwayGenerator, LastArrivalsHeadwayGenerator, Statistics.
Tier 4 (GTFS ingest): TitleFormatter, StopPathProcessor, TravelTimesProcessorForGtfsUpdates.

Approach + trade-offs

  • Low-coupling units (e.g. TravelTimes, Statistics, TitleFormatter, ServiceUtils, BlockAssigner, SpatialMatch) get thorough branch coverage.
  • Orchestrators tightly bound to singletons (AvlProcessor, ArrivalDepartureGeneratorDefaultImpl, Kalman generators) get lightweight "contract + early-return" coverage — the deep paths require Core / VehicleStateManager / DataDbLogger singletons and belong in integration tests. Each test file documents this explicitly.
  • Private pure-math helpers (e.g. HoldingTimeGeneratorDefaultImpl.calculateHoldingTime, headway generator statistics) are reached via reflection where they carry real algorithmic value.

Test plan

  • mvn -pl transitclock test → 477 passing, 0 failures, 0 errors.
  • Each new test file runs clean in isolation via mvn -pl transitclock test -Dtest=<ClassName>.

Summary by CodeRabbit

  • Chores

    • Updated test dependencies: JUnit → 4.13.2; added Mockito and AssertJ; pinned Byte Buddy.
  • Tests

    • Added extensive unit tests across prediction, vehicle state, scheduling, matching, travel-times, headway/holding generators, GTFS processing, utilities, and a test-framework smoke test.

Preparing for core-engine tests (matchers, AvlProcessor, prediction
generators) that need static-method mocking and richer assertions than
JUnit 4.11 + Hamcrest provide. Pins byte-buddy to 1.14.15 because
Hibernate 5.5 drags in 1.11.12 at compile scope and wins classpath
order over Mockito's, breaking Mockito initialization. Includes a
smoke test that exercises assertThrows, instance + static mocking,
and AssertJ fluent assertions end-to-end.
Covers the singleton, crow-flies travel-time math under all three distance
branches (below cutoff, exactly at cutoff, above cutoff), the layover
early-exits, and the Indices-driven stop-path lookups.
Covers day-of-week computation in the agency timezone, service-id
selection by weekday, the "all calendars expired" fallback to the
most recent one, calendar_date add/remove overrides, and the
serviceDate-keyed cache in getServiceIdsForDay.
Covers block assignment across all four AVL assignment types (block,
trip, tripShortName, route), the null-report / null-assignment guards,
the "no matching block" path, and the preference for an active block
over an inactive one when multiple service IDs match.
Covers the generate() decision tree: not-predictable short-circuit,
wait-stop before/after departure, regular scheduled stop, and the
fall-through to the next-scheduled-stop branch when there's no stop
info or no ScheduleTime at the current stop.
Covers mean (list, double[], int[]), toArray/toDoubleArray conversions
including null handling, sample-stddev including the single-value NaN
case, filteredMean (<=2 pass-through, no-outlier no-op, outlier drop),
and biasedFilteredMean under zero bias, non-zero bias, and the
<=2-after-filtering fallback.
Covers null-in/null-out, the no-regex-file pass-through, regex
replacements loaded from a temp file including ampersand-spacing
patterns, isReplaceTitle detection, comment/blank-line skipping,
malformed-line tolerance, the missing-file graceful failure, and
both modes of logRegexesThatDidNotMakeDifference.
Covers constructor-computed atStop state under layover, near-end-of-path,
just-after-previous-stop, and in-the-middle conditions; the ordering
comparators (lessThan/lessThanOrEqualTo) across trip/stopPath/segment/
distance; numberStopsBetweenMatches within a trip and across trips;
isLastTripOfBlock; isAtStop overloads; and the getScheduledWaitStop*
exception-to-minus-one fallback.
Covers the problemMatchDueToLackOfHeadingInfo decision tree
(null match, layover short-circuit, valid heading, and no-prev-AVL
problem), the null/empty tripsToInvestigate guards on the static
getSpatialMatches overload, and pins the MatchingType enum order.
Covers the singleton accessor, the matchToLayoverStopEvenIfOffRoute
decision path (empty potentialTrips, deadheadable trip, and a trip
whose start time has already passed), and the greedy-match shortcut
for a single no-schedule match in getBestTemporalMatchComparedToSchedule.
Covers the max-predictions-window default, getTravelTimeForPath
pass-through when storeTravelTimeStopPathPredictions is off, and
the delegate methods getStopTimeForPath and expectedTravelTimeFromMatchToEndOfStopPath.
Confirms it extends PredictionGeneratorDefaultImpl so the fallback path
stays intact, and that the un-overridden getStopTimeForPath still
delegates through to TravelTimes. Kalman's getTravelTimeForPath requires
live cache singletons to exercise, so is intentionally left to
integration tests.
Pins the four-layer inheritance chain (Kalman → HistoricalAverage →
LastVehicle → DefaultImpl) so a refactor that accidentally breaks the
fallback order trips a test. The override methods themselves require
the FrequencyBasedHistoricalAverageCache singleton to exercise, so
they're intentionally left to integration tests.
Exercises the private static calculateHoldingTime math via reflection:
the three-prediction case picks the worst-average slot, the
max_predictions cap truncates the search, and the negative-delta
clamp to zero works. Also pins the implements-HoldingTimeGenerator
contract. The public generate* methods aren't covered because they
require VehicleStateManager and HoldingTimeCache singletons.
Covers the HeadwayGenerator contract, the blanket try/catch that returns
null on bad input, and the private average/variance/coefficientOfVariance
statistics (via reflection) with a three-point dataset where the mean
and squared-deviations are trivial to verify by hand.
Matches the LastDepartureHeadwayGenerator test shape: contract check,
exception-swallowing generate(), and the private average/variance/
coefficientOfVariance statistics verified via reflection with a
hand-computable three-point dataset.
Covers vehicleId + sensible defaults, AVL-report history push, the
setMatch(null) → not-predictable + reset-bad-matches transition, match
set/get, headway/holdingTime/arrival round-trips, trip counter
increments, trip-start-time map storage, and the predictable-only
visibility guard on getRealTimeSchedAdh.

Named CoreVehicleStateTest to stay distinct from the existing
db/structs VehicleStateTest, which tests a different (entity) class
that happens to share the name.
Covers the ArrivalDepartureGenerator contract and the two early-return
guards in generate(): unpredictable vehicle and null current match.
The full stop-traversal logic requires a live DataDbLogger and
Core singleton, so is left to integration tests.
Minimal coverage only: singleton identity and the
lastAvlReportTime / getLastAvlReport invariant. The rest of
AvlProcessor (matchVehicleToAssignment, matchNewFixForPredictableVehicle,
processAvlReport) runs the full AVL pipeline through singletons
(VehicleStateManager, VehicleDataCache, Core, DataDbLogger) and is
intentionally left to integration tests.
Minimal coverage: empty-inputs processPathSegments no-op and the
constructor's shape-id grouping/sorting path. The real geometry
derivation requires a constructed TripPattern + real shapes, which
is GTFS-fixture territory and better covered by integration tests.
Covers the constructor's field wiring (via default counters), the
null-guard on setNumberOfTravelTimes, the round-trip setters/getters,
and the private static numberOfTravelTimes list-summing helper via
reflection. The full process(session, gtfsData) path requires a
Hibernate session and parsed GTFS data, so is left to integration
tests.
@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e356b5e9-ffd5-464a-a247-675beb93a8d4

📥 Commits

Reviewing files that changed from the base of the PR and between 6af332a and d368ef3.

📒 Files selected for processing (3)
  • transitclock/src/test/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImplTest.java
  • transitclock/src/test/java/org/transitclock/core/SpatialMatchTest.java
  • transitclock/src/test/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/FrequencyKalmanPredictionGeneratorImplTest.java
✅ Files skipped from review due to trivial changes (1)
  • transitclock/src/test/java/org/transitclock/core/SpatialMatchTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • transitclock/src/test/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/FrequencyKalmanPredictionGeneratorImplTest.java
  • transitclock/src/test/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImplTest.java

📝 Walkthrough

Walkthrough

Upgrades test dependencies (JUnit → 4.13.2; adds Mockito, AssertJ, ByteBuddy) and adds a comprehensive suite of new JUnit tests (≈24 classes) exercising core matching/prediction, vehicle state, GTFS processing, headway/holding generation, and statistics utilities.

Changes

Cohort / File(s) Summary
Dependency Updates
transitclock/pom.xml
Bumped junit:junit to 4.13.2 (test scope); added org.mockito:mockito-core:5.12.0 (test), org.assertj:assertj-core:3.26.3 (test), and net.bytebuddy:byte-buddy:1.14.15.
Test infra smoke
transitclock/src/test/java/org/transitclock/TestLibrariesSmokeTest.java
Smoke test validating JUnit assertThrows, Mockito instance/static mocking, and AssertJ wiring.
Core vehicle/state & processors
transitclock/src/test/java/org/transitclock/core/CoreVehicleStateTest.java, ArrivalDepartureGeneratorDefaultImplTest.java, AvlProcessorTest.java, RealTimeSchedAdhProcessorTest.java
Unit tests for VehicleState defaults/transitions, arrival/departure generation, AvlProcessor singleton/last-report-time, and real-time schedule adherence scenarios.
Matching: spatial & temporal & assignment
transitclock/src/test/java/org/transitclock/core/SpatialMatchTest.java, SpatialMatcherTest.java, TemporalMatcherTest.java, BlockAssignerTest.java
Tests for SpatialMatch distance/stop detection, SpatialMatcher heading logic, TemporalMatcher deadhead/layover matching, and BlockAssigner assignment paths (block/trip/short-name/route).
Prediction & travel-times
transitclock/src/test/java/org/transitclock/core/PredictionGeneratorDefaultImplTest.java, TravelTimesTest.java, core/predictiongenerator/.../kalman/*
Tests for default prediction limits, delegation to Indices/Trip for travel/stop times, travel-time computations, and Kalman predictor inheritance/behavior.
Headway & holding generators
transitclock/src/test/java/org/transitclock/core/headwaygenerator/*, .../holdingmethod/HoldingTimeGeneratorDefaultImplTest.java
Tests for headway/holding generator interfaces and internal statistical calculations (mean, variance, coefficient of variation, holding-time selection/clamping) via reflection.
GTFS utilities & processors
transitclock/src/test/java/org/transitclock/gtfs/StopPathProcessorTest.java, TitleFormatterTest.java, TravelTimesProcessorForGtfsUpdatesTest.java
Tests for StopPathProcessor no-op/duplicate-shape handling, TitleFormatter regex/ampersand rules and robustness to malformed files, and travel-times processor getters/summary logic.
Statistics utilities
transitclock/src/test/java/org/transitclock/statistics/StatisticsTest.java
Tests for mean/std-dev/filtered-mean/biased-filter behavior, array/list conversions, NaN edge cases, and fallback logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Tests #1 — Appears to add the same comprehensive test suite and the corresponding test-dependency updates to pom.xml.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the primary change: expanding unit test coverage for core-engine components across four defined tiers with explicit scope boundaries.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tests2

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🧹 Nitpick comments (1)
transitclock/src/test/java/org/transitclock/gtfs/StopPathProcessorTest.java (1)

30-41: Test intent and assertion are slightly mismatched.

The method name says sorting is verified, but the test only checks that construction does not throw. Consider either asserting observable sorted behavior (if accessible) or renaming the test to reflect no-throw semantics only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transitclock/src/test/java/org/transitclock/gtfs/StopPathProcessorTest.java`
around lines 30 - 41, The test named
constructor_handlesDuplicateShapeIdsAndSortsThem is misleading because it claims
to verify sorting but only asserts that the StopPathProcessor constructor
doesn't throw; update the test to either (1) assert observable sorted behavior
by accessing the processor's internal grouping or output (e.g., call a method or
getter on StopPathProcessor that returns the shape groups or ordered points and
assert the order of entries for shape-A), or (2) rename the test to
constructor_handlesDuplicateShapeIdsWithoutThrowing to reflect the current
no-throw check; modify the test body accordingly and keep references to the
existing GtfsShape instances (s1, s2) and the StopPathProcessor constructor so
the change is minimal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@transitclock/src/test/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImplTest.java`:
- Around line 19-30: The tests currently assert stubbed return values or use an
unlinked mock, making them vacuous; change both tests to verify interactions on
the VehicleState and the match mock instead: in
generate_unpredictableVehicleIsANoOp(), after calling new
ArrivalDepartureGeneratorDefaultImpl().generate(vs), verify that
vs.isPredictable() was invoked (verify(vs).isPredictable()) and that
vs.getMatch() was never called (verify(vs, never()).getMatch()); in
generate_doesNotTouchTrivialCollaboratorsWhenUnpredictable(), wire the match
into the VehicleState via when(vs.getMatch()).thenReturn(match) before calling
generate, then verify vs.isPredictable() was called and assert the match mock
had no interactions (verifyNoInteractions(match)) to ensure generate() respected
the interaction boundary.

In
`@transitclock/src/test/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/FrequencyKalmanPredictionGeneratorImplTest.java`:
- Around line 27-31: The test currently asserts the default max prediction time
by calling PredictionGeneratorDefaultImpl.getMaxPredictionsTimeSecs(), which
bypasses any static override on the Kalman implementation; change the assertion
to call the Kalman class's static accessor (e.g.,
FrequencyKalmanPredictionGeneratorImpl.getMaxPredictionsTimeSecs()) instead so
the test will fail if the Kalman class later defines its own static value and
thus truly verifies the Kalman class behavior.

In `@transitclock/src/test/java/org/transitclock/core/SpatialMatchTest.java`:
- Around line 311-320: The test currently never reaches the exception path
because buildSimpleBlock sets block.isWaitStop(0,0) to false, so
getScheduledWaitStopTimeSecs returns -1 before getScheduleTime is called; fix by
arranging the mock Block to report a wait stop for the tested indices (e.g.,
stub block.isWaitStop(0, 0) to return true) after creating the block returned by
buildSimpleBlock (or adjust buildSimpleBlock to allow specifying wait-stop
behavior), then keep the when(block.getScheduleTime(...)).thenThrow(...) and
assert that getScheduledWaitStopTimeSecs() and getScheduledWaitStopTime() return
-1 so the exception path is exercised for getScheduleTime in SpatialMatch.
- Around line 153-160: The test atStop_layoverStopAlwaysCountsAsAtStop currently
uses distanceAlongSegment = 5.0 which falls inside the configured
afterStopDistance and can pass without layover logic; change the test to use a
distance outside the after-stop window so only the layover flag forces atStop.
Update the matchAt invocation in that test (and any helper setup) to use a
distance greater than the configured afterStopDistance (for example replace
matchAt(block, 0, 5.0) with matchAt(block, 0, SpatialMatch.AFTER_STOP_DISTANCE +
1.0) or a hard-coded value larger than the afterStopDistance) so that
SpatialMatch.isAtStop() and atEndOfPathStop() only succeed because of the
layover flag.

---

Nitpick comments:
In `@transitclock/src/test/java/org/transitclock/gtfs/StopPathProcessorTest.java`:
- Around line 30-41: The test named
constructor_handlesDuplicateShapeIdsAndSortsThem is misleading because it claims
to verify sorting but only asserts that the StopPathProcessor constructor
doesn't throw; update the test to either (1) assert observable sorted behavior
by accessing the processor's internal grouping or output (e.g., call a method or
getter on StopPathProcessor that returns the shape groups or ordered points and
assert the order of entries for shape-A), or (2) rename the test to
constructor_handlesDuplicateShapeIdsWithoutThrowing to reflect the current
no-throw check; modify the test body accordingly and keep references to the
existing GtfsShape instances (s1, s2) and the StopPathProcessor constructor so
the change is minimal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9e543cb6-f19e-4682-b7ce-715b879d807e

📥 Commits

Reviewing files that changed from the base of the PR and between 9eb8642 and 6af332a.

📒 Files selected for processing (22)
  • transitclock/pom.xml
  • transitclock/src/test/java/org/transitclock/TestLibrariesSmokeTest.java
  • transitclock/src/test/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImplTest.java
  • transitclock/src/test/java/org/transitclock/core/AvlProcessorTest.java
  • transitclock/src/test/java/org/transitclock/core/BlockAssignerTest.java
  • transitclock/src/test/java/org/transitclock/core/CoreVehicleStateTest.java
  • transitclock/src/test/java/org/transitclock/core/PredictionGeneratorDefaultImplTest.java
  • transitclock/src/test/java/org/transitclock/core/RealTimeSchedAdhProcessorTest.java
  • transitclock/src/test/java/org/transitclock/core/ServiceUtilsTest.java
  • transitclock/src/test/java/org/transitclock/core/SpatialMatchTest.java
  • transitclock/src/test/java/org/transitclock/core/SpatialMatcherTest.java
  • transitclock/src/test/java/org/transitclock/core/TemporalMatcherTest.java
  • transitclock/src/test/java/org/transitclock/core/TravelTimesTest.java
  • transitclock/src/test/java/org/transitclock/core/headwaygenerator/LastArrivalsHeadwayGeneratorTest.java
  • transitclock/src/test/java/org/transitclock/core/headwaygenerator/LastDepartureHeadwayGeneratorTest.java
  • transitclock/src/test/java/org/transitclock/core/holdingmethod/HoldingTimeGeneratorDefaultImplTest.java
  • transitclock/src/test/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/FrequencyKalmanPredictionGeneratorImplTest.java
  • transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/ScheduledKalmanPredictionGeneratorImplTest.java
  • transitclock/src/test/java/org/transitclock/gtfs/StopPathProcessorTest.java
  • transitclock/src/test/java/org/transitclock/gtfs/TitleFormatterTest.java
  • transitclock/src/test/java/org/transitclock/gtfs/TravelTimesProcessorForGtfsUpdatesTest.java
  • transitclock/src/test/java/org/transitclock/statistics/StatisticsTest.java

Comment on lines +311 to +320
public void getScheduledWaitStopTimeSecs_returnsMinusOneOnException() {
Block block = buildSimpleBlock(2, null);
when(block.getScheduleTime(anyInt(), anyInt()))
.thenThrow(new RuntimeException("no schedule time"));

SpatialMatch match = matchAt(block, 0, 50.0);

assertThat(match.getScheduledWaitStopTimeSecs()).isEqualTo(-1);
assertThat(match.getScheduledWaitStopTime()).isEqualTo(-1);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Exception fallback path is likely not being exercised in this test.

buildSimpleBlock() sets block.isWaitStop(0, 0) to false, so the method can return -1 before invoking getScheduleTime(...) at all.

Proposed test fix
 `@Test`
 public void getScheduledWaitStopTimeSecs_returnsMinusOneOnException() {
 	Block block = buildSimpleBlock(2, null);
+	when(block.isWaitStop(0, 0)).thenReturn(true);
 	when(block.getScheduleTime(anyInt(), anyInt()))
 			.thenThrow(new RuntimeException("no schedule time"));
 
 	SpatialMatch match = matchAt(block, 0, 50.0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void getScheduledWaitStopTimeSecs_returnsMinusOneOnException() {
Block block = buildSimpleBlock(2, null);
when(block.getScheduleTime(anyInt(), anyInt()))
.thenThrow(new RuntimeException("no schedule time"));
SpatialMatch match = matchAt(block, 0, 50.0);
assertThat(match.getScheduledWaitStopTimeSecs()).isEqualTo(-1);
assertThat(match.getScheduledWaitStopTime()).isEqualTo(-1);
}
public void getScheduledWaitStopTimeSecs_returnsMinusOneOnException() {
Block block = buildSimpleBlock(2, null);
when(block.isWaitStop(0, 0)).thenReturn(true);
when(block.getScheduleTime(anyInt(), anyInt()))
.thenThrow(new RuntimeException("no schedule time"));
SpatialMatch match = matchAt(block, 0, 50.0);
assertThat(match.getScheduledWaitStopTimeSecs()).isEqualTo(-1);
assertThat(match.getScheduledWaitStopTime()).isEqualTo(-1);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transitclock/src/test/java/org/transitclock/core/SpatialMatchTest.java`
around lines 311 - 320, The test currently never reaches the exception path
because buildSimpleBlock sets block.isWaitStop(0,0) to false, so
getScheduledWaitStopTimeSecs returns -1 before getScheduleTime is called; fix by
arranging the mock Block to report a wait stop for the tested indices (e.g.,
stub block.isWaitStop(0, 0) to return true) after creating the block returned by
buildSimpleBlock (or adjust buildSimpleBlock to allow specifying wait-stop
behavior), then keep the when(block.getScheduleTime(...)).thenThrow(...) and
assert that getScheduledWaitStopTimeSecs() and getScheduledWaitStopTime() return
-1 so the exception path is exercised for getScheduleTime in SpatialMatch.

ArrivalDepartureGeneratorDefaultImplTest: replace vacuous stub
re-assertions with verify()/verify(never()) interaction checks so the
tests fail if the short-circuit guards are removed.

FrequencyKalmanPredictionGeneratorImplTest: call
getMaxPredictionsTimeSecs() through KalmanPredictionGeneratorImpl
rather than PredictionGeneratorDefaultImpl so a future static
override on any intermediate layer will break the test.

SpatialMatchTest.atStop_layoverStopAlwaysCountsAsAtStop: move the
match to the segment midpoint, outside both beforeStopDistance and
afterStopDistance windows, so isAtStop can only be true via the
layover flag.
@aaronbrethorst

Copy link
Copy Markdown
Member Author

Pushed d368ef37 addressing the review feedback.

Applied (3):

  • ArrivalDepartureGeneratorDefaultImplTest — replaced vacuous stub re-assertions with verify(vs).isPredictable() + verify(vs, never()).getMatch() so the tests fail if the short-circuit guards are removed.
  • FrequencyKalmanPredictionGeneratorImplTest — assertion now calls KalmanPredictionGeneratorImpl.getMaxPredictionsTimeSecs() instead of going through the base class, so a future static override on any intermediate layer will break the test.
  • SpatialMatchTest#atStop_layoverStopAlwaysCountsAsAtStop — moved the match to the segment midpoint (outside both beforeStopDistance and afterStopDistance windows) so isAtStop can only be true via the layover flag.

Not applied (1):

  • SpatialMatchTest#getScheduledWaitStopTimeSecs_returnsMinusOneOnException — after re-reading the code, the suggestion appears to be based on a misread. SpatialMatch.getScheduledWaitStopTimeSecs() has no isWaitStop short-circuit — it enters the try block and calls block.getScheduleTime(tripIndex, stopPathIndex) directly. The existing when(block.getScheduleTime(anyInt(), anyInt())).thenThrow(...) stub does fire, and the -1 return comes from the catch. The exception path is already being exercised.

477 tests still passing (mvn -pl transitclock test).

@aaronbrethorst
aaronbrethorst merged commit e476ecf into develop Apr 22, 2026
3 checks passed
@aaronbrethorst
aaronbrethorst deleted the tests2 branch April 22, 2026 17:44
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