Skip to content

Pipeline tests - #3

Merged
aaronbrethorst merged 10 commits into
developfrom
pipeline-tests
Apr 23, 2026
Merged

Pipeline tests#3
aaronbrethorst merged 10 commits into
developfrom
pipeline-tests

Conversation

@aaronbrethorst

@aaronbrethorst aaronbrethorst commented Apr 23, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added opt-in end-to-end pipeline test stage executed separately in CI.
    • Created a reusable test harness and fixtures to run realistic pipeline scenarios.
  • Documentation

    • Added explicit "Running tests" guidance and documented opt-in pipeline/integration test commands.
  • Tests

    • Added many unit tests covering core data structures and utilities.
    • Added integration/pipeline tests validating vehicle processing and end-to-end behavior.
  • Chores

    • CI updated to run pipeline tests as a separate post-verify stage.

Covers pure-logic utilities and thin wrappers that had no coverage:
MapKey, IntervalTimer, Encryption, OrderedCollection, ChinaGpsOffset
in utils; TripPatternKey, Arrival/Departure in db/structs; and
VehicleAtStopInfo in core. 73 tests total, all passing, no changes
to production code.
Introduces a TransitFixtures helper in the test tree that assembles
GtfsTrip, Trip, Block, and StopPath graphs via real constructors, then
uses it to cover the logic that the first round skipped: Trip's
addScheduleTimes inference, block-id fallback chain, and frequency
copy constructors; Block's route-id dedup and lookup methods; and
StopPath's setLocations pathLength calculation and flag getters.
30 new tests, all passing.
Adds a new Maven module that boots a real Core against an in-memory
HSQL database populated with the WMATA 5A GTFS fixture, opt-in via
an include-pipeline-tests profile so the default build is unchanged.
CoreHarness is a JUnit ClassRule exposing helpers for subsequent
behavior tests (dbConfig, setNow, clock). CoreHarnessSmokeTest proves
the boot works.

Two constraints surfaced and are worked around here:
- GtfsData.isCalendarActiveInTheFuture hard-codes System.currentTimeMillis
  and the WMATA calendars expired in 2018. The harness stages the GTFS
  into a temp dir with rewritten far-future end_dates.
- Hibernate's hbm2ddl.auto=create would drop+recreate tables every time
  Core rebuilds its SessionFactory (which it does during startup),
  wiping the imported data. Switched this module's hibernate config to
  hbm2ddl.auto=update.
Five behavior tests covering AvlProcessor#processAvlReport against the
WMATA 5A dataset via CoreHarness: lastAvlReport updates, off-route
unassigned vehicles are not predictable, invalid block assignments are
tolerated, sequential reports share VehicleState, and the happy-path
report does not throw.

Each test advances Core's clock via a per-JVM monotonic counter because
AvlProcessor#setLastAvlReport silently drops reports whose timestamp is
not strictly greater than the last stored report's — JUnit 4's default
(hash-based) method ordering would otherwise make the result dependent
on which test ran last.
- Rename MapKeyTest.twoArgAndThreeArgKeysAreNotEqualEvenWhenLeadingFieldsMatch
  to AreEqualWhenTrailingSlotsAreNull. The body asserts equality, so the old
  name was a direct contradiction.
- Fail loudly on malformed calendar.txt rows in CoreHarness. The old code
  silently passed through rows with <10 columns, which would leave the stale
  2016 end_date intact and defeat the whole point of the rewrite. Now throws
  IOException with line number and content. New CoreHarnessCalendarRewriteTest
  pins the new loud-failure contract plus the canonical/empty-line cases.
- Restore JVM system properties in CoreHarness.after() and assert Core is
  not already booted on entry. Belt-and-suspenders against surefire fork-
  config drift: reuseForks=false isolates classes today, but a change would
  otherwise leak configFiles/agencyId between sibling test classes silently.
- Add happy-path match test to AvlProcessorBehaviorTest. Picks trip 868588900
  on block SE-08 (service 8) at its first stop (Dulles Airport Main Terminal),
  anchors Core's clock to 2016-06-20 11:50 EDT — a Monday when service 8 runs
  with no calendar_dates exclusion — and asserts the vehicle becomes
  predictable with a concrete TemporalMatch. This replaces the prior
  processAvlReportDoesNotThrowForTypicalNonMatchingReport test, which just
  rethrew RuntimeExceptions as AssertionErrors and added no value.
- Route every clock advance through the nextTime AtomicLong. Adds
  advanceClockBy(deltaMs) and jumpClockTo(epochMs) helpers; the jump variant
  max-merges into nextTime so subsequent tests' @before hooks can't silently
  rewind the clock.
- Add assertThat(state.getMatch()).isNull() to the off-route unassigned test,
  so "spatial match failed" is distinguished from "short-circuited before
  matching" — same failure signal today, different regression cause tomorrow.
- Throw on non-regular entries in GTFS staging. The WMATA 5A fixture is flat
  but a fixture with a subdirectory or symlink would otherwise be silently
  skipped, producing confusing "file not found" errors far from the staging
  step.
- Correct the GTFS column-order comment in rewriteCalendarWithFutureEndDates.
  GTFS doesn't require column ordering; the rewriter relies on the canonical
  order used by WMATA fixtures and would need a header-aware parser for a
  different layout.
- Rename IntervalTimerTest.elapsedMsecStrHasThreeDecimalDigits to
  elapsedMsecStrParsesAsNonNegativeNumber — the old name promised a format
  check the body never performed.
- Delete unused CoreHarness.withGtfs. No caller, speculative comment.
- Soften AvlProcessorBehaviorTest class-javadoc: stop enumerating specific
  service_ids that would rot if the fixture is refreshed, point at the
  fixture path and the HAPPY_PATH_* constants instead.
- TripTest.exactTimesHeadwayCopyConstructorOffsetsTimes now asserts that
  each ScheduleTime in the copy has its arrival/departure offset by the
  same shift. Previously only start/end/blockId were checked, so a
  regression that left the schedule-time list at the base (unshifted)
  values would pass. Also added an explicit isExactTimesHeadway() check.
- TripPatternKeyTest.constructorRejectsNullStopPathsList asserts on the
  exception message fragment ("stopPaths") so a refactor that drops the
  explicit guard in favor of an implicit dereference NPE is noticed.
- EncryptionTest.decryptOfTamperedCiphertextThrows truncates 4 trailing
  chars instead of flipping a single char. The char-flip variant could
  land on a base64 padding char for some schemes and produce a still-
  decryptable value; truncation reliably destroys the ciphertext.
- Bump pipeline-tests module's logback-classic from 1.1.2 to 1.1.3 to
  match transitclockCore's declared version.
- Add trailing newline to root .gitignore.
- README gains a "Running tests" section that lists the default, pipeline,
  and integration test invocations, including the first-run cost note.
- CLAUDE.md gets (a) a bullet under "Build and test" that mirrors the
  existing integration-tests bullet, and (b) a transitclockPipelineTests
  entry in the module layout (bumping the module count from seven to eight).
- CI runs the pipeline suite as a separate step after the unit-test build
  so failures are attributed distinctly. The existing surefire-reports
  upload-artifact already uses a **/target/surefire-reports glob and picks
  up the new module's reports automatically.
@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 26c8076c-8c3c-4138-9ee9-26c07c206934

📥 Commits

Reviewing files that changed from the base of the PR and between 8751210 and 61f2401.

📒 Files selected for processing (6)
  • CLAUDE.md
  • transitclock/src/test/java/org/transitclock/testutil/TransitFixtures.java
  • transitclock/src/test/java/org/transitclock/utils/IntervalTimerTest.java
  • transitclockPipelineTests/pom.xml
  • transitclockPipelineTests/src/test/resources/hsql_pipeline_test_hibernate.cfg.xml
  • transitclockPipelineTests/src/test/resources/transitclockConfigHsql.xml

📝 Walkthrough

Walkthrough

Adds a new opt-in Maven pipeline test module (transitclockPipelineTests) with harness/resources and CI stage, numerous unit test classes and test utilities under transitclock/src/test, updates POM/profile, docs, and gitignore to support running these pipeline and integration tests.

Changes

Cohort / File(s) Summary
CI / Top-level build
\.github/workflows/ci.yml, pom.xml
Added CI step to run pipeline tests separately and a Maven profile include-pipeline-tests to opt-in the transitclockPipelineTests module.
Documentation & Gitignore
CLAUDE.md, README.md, \.gitignore
Documented new pipeline/integration test profiles and commands; added settings.local.json to .gitignore.
Test utilities / fixtures
transitclock/src/test/java/org/transitclock/testutil/TransitFixtures.java
New shared test fixture helpers to construct GTFS-derived entities (Trip, Block, StopPath, ScheduleTime, etc.).
Core data-structure unit tests
transitclock/src/test/java/org/transitclock/db/structs/*Test.java, transitclock/src/test/java/org/transitclock/core/VehicleAtStopInfoTest.java
Multiple new JUnit suites covering Block, Trip, StopPath, TripPatternKey, Arrival/Departure subclass behavior, and VehicleAtStopInfo equality/normalization.
Utility unit tests
transitclock/src/test/java/org/transitclock/utils/*Test.java
New tests for China GPS offsets, Encryption, IntervalTimer, MapKey, OrderedCollection.
Pipeline tests module (new)
transitclockPipelineTests/pom.xml, transitclockPipelineTests/.gitignore, transitclockPipelineTests/src/test/resources/*
New Maven module with HSQL test Hibernate config, Logback test config, pipeline transitclock config, and module-level .gitignore.
Pipeline test harness & tests
transitclockPipelineTests/src/test/java/org/transitclock/pipelinetests/CoreHarness.java, .../CoreHarness*.java, .../AvlProcessorBehaviorTest.java
New CoreHarness JUnit rule that stages WMATA 5A GTFS into in-memory HSQL, boots Core, controls clock; smoke, calendar-rewrite, and AvlProcessor integration tests exercising end-to-end AVL processing.

Sequence Diagram(s)

sequenceDiagram
  participant Test as Pipeline Test
  participant Harness as CoreHarness
  participant Core as Core
  participant DB as HSQL DB
  participant Avl as AvlProcessor

  rect rgba(200,200,255,0.5)
  Test->>Harness: request boot with WMATA 5A fixture & setNow(t)
  Harness->>DB: stage GTFS files and rewrite calendar.txt
  Harness->>Core: initialize Core (reads DB/config)
  Core->>DB: load persisted entities
  end

  rect rgba(200,255,200,0.5)
  Test->>Avl: build AvlReport stamped with harness clock
  Avl->>Core: submit report for processing
  Core->>Avl: update/return VehicleState (predictable or unpredictable)
  Avl->>Test: assertions on VehicleState and last report time
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Pipeline tests #3 — Adds the same transitclockPipelineTests module, POM, CI step, CoreHarness, and pipeline test classes (highly related).
  • Tests #1 — Related changes to CI/test infrastructure and addition of large test suites and pipeline test orchestration.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.76% 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 "Pipeline tests" directly reflects the main change: introduction of an end-to-end pipeline test module (transitclockPipelineTests) with supporting infrastructure, documentation updates, and CI integration.
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 pipeline-tests

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: 8

🧹 Nitpick comments (10)
transitclock/src/test/java/org/transitclock/db/structs/ArrivalDepartureSubclassesTest.java (3)

59-60: Prefer value assertions over identity assertions for Date fields.

Using assertSame here over-constrains implementation details and can block safe defensive-copy changes.

Suggested test adjustment
-		assertSame(avl, a.getAvlTime());
+		assertEquals(avl, a.getAvlTime());
...
-		assertSame(freqStart, a.getFreqStartTime());
+		assertEquals(freqStart, a.getFreqStartTime());
...
-		assertSame(freqStart, d.getFreqStartTime());
+		assertEquals(freqStart, d.getFreqStartTime());

Also applies to: 84-89

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

In
`@transitclock/src/test/java/org/transitclock/db/structs/ArrivalDepartureSubclassesTest.java`
around lines 59 - 60, The test currently uses identity assertion assertSame(avl,
a.getAvlTime()) which ties behavior to object identity; change this to a value
assertion (e.g., assertEquals(avl, a.getAvlTime())) so Date equality is checked
rather than reference equality, and apply the same change for the other Date
assertions in the test (those around the 84-89 block) to avoid preventing
defensive-copy implementations.

122-126: Make equality test use distinct Date instances with equal values.

Right now both objects share the same Date references, so this test can pass even if equality accidentally relies on reference identity.

Suggested test hardening
-		Date t = time(1_000_000);
-		Date avl = time(999_000);
-		Arrival a = new Arrival(CONFIG_REV, VEHICLE_ID, t, avl, null, 0, 1, null);
-		Arrival b = new Arrival(CONFIG_REV, VEHICLE_ID, t, avl, null, 0, 1, null);
+		Arrival a = new Arrival(CONFIG_REV, VEHICLE_ID, time(1_000_000), time(999_000), null, 0, 1, null);
+		Arrival b = new Arrival(CONFIG_REV, VEHICLE_ID, time(1_000_000), time(999_000), null, 0, 1, null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@transitclock/src/test/java/org/transitclock/db/structs/ArrivalDepartureSubclassesTest.java`
around lines 122 - 126, The test in ArrivalDepartureSubclassesTest creates
Arrival instances using the same Date object references (t and avl) so equality
may pass via reference identity; instead instantiate distinct Date objects with
identical time values (e.g., new Date(time(...).getTime()) or use time(...)
twice into separate variables) when constructing Arrival a and Arrival b so both
have equal but not identical Date instances; update the lines that set Date t
and avl and/or the second Arrival constructor call to use those distinct Date
objects while keeping the same millisecond values.

14-17: Align class-level test description with actual coverage.

The Javadoc says withUpdatedTime is verified, but this suite currently does not test it. Either add that test or trim the statement.

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

In
`@transitclock/src/test/java/org/transitclock/db/structs/ArrivalDepartureSubclassesTest.java`
around lines 14 - 17, The class Javadoc claims withUpdatedTime is tested but no
such test exists; add a unit test in ArrivalDepartureSubclassesTest that
constructs an Arrival (and optionally a Departure), calls
Arrival.withUpdatedTime(newTime) and asserts the returned object has the updated
time (via getTime/getTimestamp), retains other fields unchanged, and still
reports isArrival true (and similarly verify Departure behavior if applicable),
referencing the Arrival class and its withUpdatedTime method as well as
isArrival/isDeparture and getter methods to locate code to assert against.
transitclock/src/test/java/org/transitclock/utils/ChinaGpsOffsetTest.java (1)

18-38: Add boundary-value assertions for outOfChina() thresholds.

Nice coverage for obvious in/out cases, but Lines 19-38 don’t verify exact cutoff behavior (72.004, 137.8347, 0.8293, 55.8271). Since the implementation uses strict </>, explicit edge tests would protect against future off-by-one regressions.

Suggested test additions
 `@Test`
 public void insideChinaBoundingBox() {
+	// Exact threshold values should be considered inside.
+	assertFalse(ChinaGpsOffset.outOfChina(0.8293, 110.0));
+	assertFalse(ChinaGpsOffset.outOfChina(55.8271, 110.0));
+	assertFalse(ChinaGpsOffset.outOfChina(30.0, 72.004));
+	assertFalse(ChinaGpsOffset.outOfChina(30.0, 137.8347));
+
 	// Beijing
 	assertFalse(ChinaGpsOffset.outOfChina(39.9042, 116.4074));
 	// Zhengzhou (used in the class's own main())
 	assertFalse(ChinaGpsOffset.outOfChina(34.79521, 113.69259));
 	// Shanghai
 	assertFalse(ChinaGpsOffset.outOfChina(31.2304, 121.4737));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transitclock/src/test/java/org/transitclock/utils/ChinaGpsOffsetTest.java`
around lines 18 - 38, Add explicit boundary-value assertions for
ChinaGpsOffset.outOfChina to protect against off-by-one regressions: call
outOfChina with the exact longitude thresholds 72.004 and 137.8347 (e.g.,
outOfChina(<anyLatWithinRange>, 72.004) and 137.8347) and assertFalse, and call
outOfChina with the exact latitude thresholds 0.8293 and 55.8271 (e.g.,
outOfChina(0.8293, <anyLonWithinRange>) and 55.8271) and assertFalse; also
consider adding one test just inside and one just outside each threshold (e.g.,
72.0039 and 72.0041) to verify the strict comparison behavior in
ChinaGpsOffset.outOfChina.
transitclock/src/test/java/org/transitclock/utils/EncryptionTest.java (2)

54-79: Consider extracting repeated decrypt-failure assertion logic.

The two exception tests repeat the same try/catch pattern; a helper would reduce duplication and keep tests focused on inputs.

🧹 Possible refactor
 public class EncryptionTest {
+
+	private void assertDecryptThrows(String input, String message) {
+		try {
+			Encryption.decrypt(input);
+			fail(message);
+		} catch (EncryptionOperationNotPossibleException expected) {
+			// expected
+		}
+	}
 
 	`@Test`
 	public void decryptOfTamperedCiphertextThrows() {
 		String encrypted = Encryption.encrypt("original");
@@
-		try {
-			Encryption.decrypt(tampered);
-			fail("Expected EncryptionOperationNotPossibleException on tampered ciphertext");
-		} catch (EncryptionOperationNotPossibleException expected) {
-			// expected
-		}
+		assertDecryptThrows(
+				tampered,
+				"Expected EncryptionOperationNotPossibleException on tampered ciphertext");
 	}
@@
 	`@Test`
 	public void decryptOfGarbageThrows() {
-		try {
-			Encryption.decrypt("not-a-real-ciphertext");
-			fail("Expected EncryptionOperationNotPossibleException on garbage input");
-		} catch (EncryptionOperationNotPossibleException expected) {
-			// expected
-		}
+		assertDecryptThrows(
+				"not-a-real-ciphertext",
+				"Expected EncryptionOperationNotPossibleException on garbage input");
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transitclock/src/test/java/org/transitclock/utils/EncryptionTest.java` around
lines 54 - 79, Extract the repeated try/catch/fail pattern in
decryptOfTamperedCiphertextThrows and decryptOfGarbageThrows into a small test
helper (e.g., assertDecryptFails(String ciphertext)) that calls
Encryption.decrypt(ciphertext) and fails the test unless an
EncryptionOperationNotPossibleException is thrown; then replace the try/catch
blocks in both tests with calls to assertDecryptFails("not-a-real-ciphertext")
and assertDecryptFails(tampered) to remove duplication and keep tests focused on
inputs.

43-52: Strengthen Line 51 by asserting against the original plaintext directly.

At Line 51, comparing decrypt(a) to decrypt(b) is weaker than checking both against "sameInput".

♻️ Suggested tweak
 	String a = Encryption.encrypt("sameInput");
 	String b = Encryption.encrypt("sameInput");
 	assertNotEquals(a, b);
-	// But both decrypt back to the same plaintext.
-	assertEquals(Encryption.decrypt(a), Encryption.decrypt(b));
+	// But both decrypt back to the original plaintext.
+	assertEquals("sameInput", Encryption.decrypt(a));
+	assertEquals("sameInput", Encryption.decrypt(b));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transitclock/src/test/java/org/transitclock/utils/EncryptionTest.java` around
lines 43 - 52, Test currently only compares Encryption.decrypt(a) to
Encryption.decrypt(b); strengthen it by asserting both decryptions equal the
original plaintext "sameInput". In the test method
encryptionIsSaltedSoSameInputProducesDifferentCiphertexts, replace or supplement
the existing assertEquals(Encryption.decrypt(a), Encryption.decrypt(b)) with two
assertions that assertEquals("sameInput", Encryption.decrypt(a)) and
assertEquals("sameInput", Encryption.decrypt(b)) so both decrypted values are
explicitly validated against the original input while keeping the
assertNotEquals(a, b) check for differing ciphertexts.
transitclock/src/test/java/org/transitclock/db/structs/TripPatternKeyTest.java (1)

60-65: Use independent lists/instances when validating logical equality

Both equality/hash tests currently reuse the same paths list reference for both keys. Using separate list/object instances would better guard against accidental identity-based regressions and make the tests stronger.

Possible test hardening diff
- List<StopPath> paths = Arrays.asList(
-         stopPath(1, "p1", "s1", 0, "r1"),
-         stopPath(1, "p2", "s2", 1, "r1"));
- TripPatternKey a = new TripPatternKey("shapeA", paths);
- TripPatternKey b = new TripPatternKey("shapeA", paths);
+ TripPatternKey a = new TripPatternKey("shapeA", Arrays.asList(
+         stopPath(1, "p1", "s1", 0, "r1"),
+         stopPath(1, "p2", "s2", 1, "r1")));
+ TripPatternKey b = new TripPatternKey("shapeA", Arrays.asList(
+         stopPath(1, "p1", "s1", 0, "r1"),
+         stopPath(1, "p2", "s2", 1, "r1")));

Also applies to: 151-156

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

In
`@transitclock/src/test/java/org/transitclock/db/structs/TripPatternKeyTest.java`
around lines 60 - 65, The test currently reuses the same List instance `paths`
for both TripPatternKey objects which hides identity-based bugs; update the
test(s) to construct independent but equal lists/objects (e.g., create a second
List with newly instantiated StopPath objects that have the same values) before
creating the second TripPatternKey so equality/hashCode are validated logically;
apply the same change to the similar assertions around the other occurrence (the
test near the block that corresponds to lines 151-156) referencing the same
classes `TripPatternKey` and `StopPath`.
transitclock/src/test/java/org/transitclock/core/VehicleAtStopInfoTest.java (1)

45-86: Comprehensive equality testing with good coverage of edge cases.

The tests correctly verify:

  • getClass-based equality prevents VehicleAtStopInfo from equaling plain Indices
  • Both directions of inequality (lines 63-64)
  • Field-specific inequality conditions
♻️ Optional: Consider using assertEquals consistently for equality assertions

For consistency, you could use assertEquals throughout instead of mixing with assertTrue(x.equals(y)):

 	public void twoVehicleAtStopInfosWithSameFieldsAreEqual() {
 		VehicleAtStopInfo a = new VehicleAtStopInfo(null, 1, 2);
 		VehicleAtStopInfo b = new VehicleAtStopInfo(null, 1, 2);
-		assertTrue(a.equals(b));
+		assertEquals(a, b);
 	}

Both approaches are valid; this just improves consistency with line 52.

🤖 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/VehicleAtStopInfoTest.java`
around lines 45 - 86, Change the equality assertion in
twoVehicleAtStopInfosWithSameFieldsAreEqual to use assertEquals for consistency:
replace the assertTrue(a.equals(b)) call in that test with assertEquals(a, b)
(keeping the existing variable setup in VehicleAtStopInfo a and b) so all
equality checks in this class use the same assertEquals style.
transitclockPipelineTests/pom.xml (1)

71-74: Upgrade maven-surefire-plugin from 2.19.1 to 3.5.5 or later.

The 2.19.1 release is outdated for JDK 17. All surefire 3.x versions officially support Java 17, with 3.5.5 (latest stable) providing improved reliability, better test-platform compatibility, and full JUnit 4 support via the built-in surefire-junit4 provider.

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

In `@transitclockPipelineTests/pom.xml` around lines 71 - 74, Update the
maven-surefire-plugin version to a JDK17-compatible release by changing the
plugin declaration for org.apache.maven.plugins:maven-surefire-plugin (the
<version> element currently set to 2.19.1) to 3.5.5 (or later); ensure the
<configuration> remains valid for the 3.x provider and adjust any deprecated
configuration keys if build warnings appear after the upgrade (target the plugin
coordinates groupId org.apache.maven.plugins and artifactId
maven-surefire-plugin).
transitclockPipelineTests/src/test/java/org/transitclock/pipelinetests/CoreHarness.java (1)

210-212: Temp directory contents may not be cleaned up.

deleteOnExit() only deletes the directory itself (and only if empty). The GTFS files copied into staged will persist until JVM exit and won't be automatically removed, potentially leaving orphaned files in the temp directory if the JVM crashes or if many test runs accumulate.

Consider using a shutdown hook or try-with-resources pattern with explicit cleanup, or accept this as a minor tradeoff for test simplicity.

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

In
`@transitclockPipelineTests/src/test/java/org/transitclock/pipelinetests/CoreHarness.java`
around lines 210 - 212, The temp directory created via Files.createTempDirectory
and stored in the staged Path in CoreHarness is only registered with
staged.toFile().deleteOnExit(), which won't remove non-empty directories; change
to explicitly clean up the directory contents either by registering a JVM
shutdown hook that recursively deletes staged (including files) or by ensuring
tests use a try/finally (or try-with-resources style wrapper) that recursively
deletes staged after use; locate the staged variable and replace the single
deleteOnExit() call with a recursive delete routine invoked from a shutdown hook
or final cleanup block to ensure files copied into staged are removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLAUDE.md`:
- Line 37: The opening sentence "Eight Maven modules under the root aggregator
`pom.xml`" is incorrect relative to the bullets that enumerate nine modules;
update that sentence to match the list (e.g., change to "Nine Maven modules
under the root aggregator `pom.xml`" or rephrase to "Multiple Maven modules
across default and opt-in profiles under the root aggregator `pom.xml`") so the
summary aligns with the enumerated bullets. Ensure you edit the exact phrase
"Eight Maven modules under the root aggregator `pom.xml`" to the chosen
corrected wording.

In
`@transitclock/src/test/java/org/transitclock/db/structs/TripPatternKeyTest.java`:
- Around line 117-125: The test assertions contradict
TripPatternKey.equals/hashCode (which compares shapeId and ordered
StopPath#getStopPathIndex() values), so update the tests rather than production
code: in TripPatternKeyTest methods like differentStopIdBreaksEquality (and the
similar one at 128-134) either change assertNotEquals(a, b) to assertEquals(a,
b) to reflect that differing StopPath.stopId/configRev does not affect equality,
or alter the StopPath fixtures to produce different stopPathIndex values (via
stopPath(...) inputs) so equality should legitimately differ; reference
TripPatternKey.equals/hashCode and StopPath#getStopPathIndex() when making the
change.

In `@transitclock/src/test/java/org/transitclock/testutil/TransitFixtures.java`:
- Around line 115-129: In blockOf (TransitFixtures.java) the current
startTime/endTime computation can return Integer.MIN_VALUE or drop valid end
times; change the post-loop logic to handle partial times: after scanning
tripList (using startTime, endTime), if both bounds stayed at their sentinels
set both to 0; if only startTime is set (endTime still MIN_VALUE) set endTime =
startTime; if only endTime is set (startTime still MAX_VALUE) set startTime =
endTime; keep the existing sentinel initializations and ensure the final new
Block(DEFAULT_CONFIG_REV, blockId, serviceId, startTime, endTime, tripList)
receives valid bounds.

In `@transitclock/src/test/java/org/transitclock/utils/IntervalTimerTest.java`:
- Around line 63-81: The tests use Double.parseDouble on the locale-sensitive
string returned by IntervalTimer.elapsedMsecStr() (and
IntervalTimer.toString()), which fails for locales that use commas; update the
tests elapsedMsecStrParsesAsNonNegativeNumber and
toStringReturnsSameAsElapsedMsecStr to parse using
NumberFormat.getNumberInstance() (or NumberFormat.getInstance()) and
NumberFormat.parse(...) to obtain a Number and then compare its doubleValue(),
ensuring locale-aware parsing for both timer.elapsedMsecStr() and
timer.toString() and keeping the same non-negative and delta assertions.

In `@transitclockPipelineTests/pom.xml`:
- Around line 6-10: This module POM (artifactId transitclockPipelineTests) is
missing a <parent> declaration so it doesn't inherit shared build/plugins
(including JaCoCo/reporting); add a <parent> element that references the root
project's groupId, artifactId and version and set relativePath to ../pom.xml so
the module inherits the root POM's build and reporting configuration (thereby
enabling JaCoCo settings for transitclockPipelineTests).
- Around line 38-64: Update the test dependency versions in the pom.xml to
address known CVEs: bump org.hsqldb:hsqldb from 2.2.4 to at least 2.7.1,
ch.qos.logback:logback-classic from 1.1.3 to 1.2.0+, junit:junit from 4.11 to
4.13.1+, and org.assertj:assertj-core from 3.24.2 down/side-step to a safe
patched line (use 3.17.0+ as recommended); locate and modify the <dependency>
blocks for artifactIds hsqldb, logback-classic, junit and assertj-core in the
file and update the <version> elements accordingly, then run your build/CI to
verify tests still pass under Java 17.

In
`@transitclockPipelineTests/src/test/resources/hsql_pipeline_test_hibernate.cfg.xml`:
- Around line 79-84: Update the HSQLDB JDBC URLs for the properties
hibernate.connection.url and hibernate.ro.connection.url to use the correct
in-memory format by removing the "//localhost/" segment; change the values from
"jdbc:hsqldb:mem://localhost/xdb" to the standard "jdbc:hsqldb:mem:xdb" so the
database initializes correctly.

In `@transitclockPipelineTests/src/test/resources/transitclockConfigHsql.xml`:
- Around line 28-32: Fix the typo in the XML comment that currently reads
"VechiclesServer" — change it to "VehiclesServer"; locate the comment near the
<dwelltime> and <fuzzytime> entries (the comment referencing matching the last
stop of a vehicle to a time) and update the text only.

---

Nitpick comments:
In `@transitclock/src/test/java/org/transitclock/core/VehicleAtStopInfoTest.java`:
- Around line 45-86: Change the equality assertion in
twoVehicleAtStopInfosWithSameFieldsAreEqual to use assertEquals for consistency:
replace the assertTrue(a.equals(b)) call in that test with assertEquals(a, b)
(keeping the existing variable setup in VehicleAtStopInfo a and b) so all
equality checks in this class use the same assertEquals style.

In
`@transitclock/src/test/java/org/transitclock/db/structs/ArrivalDepartureSubclassesTest.java`:
- Around line 59-60: The test currently uses identity assertion assertSame(avl,
a.getAvlTime()) which ties behavior to object identity; change this to a value
assertion (e.g., assertEquals(avl, a.getAvlTime())) so Date equality is checked
rather than reference equality, and apply the same change for the other Date
assertions in the test (those around the 84-89 block) to avoid preventing
defensive-copy implementations.
- Around line 122-126: The test in ArrivalDepartureSubclassesTest creates
Arrival instances using the same Date object references (t and avl) so equality
may pass via reference identity; instead instantiate distinct Date objects with
identical time values (e.g., new Date(time(...).getTime()) or use time(...)
twice into separate variables) when constructing Arrival a and Arrival b so both
have equal but not identical Date instances; update the lines that set Date t
and avl and/or the second Arrival constructor call to use those distinct Date
objects while keeping the same millisecond values.
- Around line 14-17: The class Javadoc claims withUpdatedTime is tested but no
such test exists; add a unit test in ArrivalDepartureSubclassesTest that
constructs an Arrival (and optionally a Departure), calls
Arrival.withUpdatedTime(newTime) and asserts the returned object has the updated
time (via getTime/getTimestamp), retains other fields unchanged, and still
reports isArrival true (and similarly verify Departure behavior if applicable),
referencing the Arrival class and its withUpdatedTime method as well as
isArrival/isDeparture and getter methods to locate code to assert against.

In
`@transitclock/src/test/java/org/transitclock/db/structs/TripPatternKeyTest.java`:
- Around line 60-65: The test currently reuses the same List instance `paths`
for both TripPatternKey objects which hides identity-based bugs; update the
test(s) to construct independent but equal lists/objects (e.g., create a second
List with newly instantiated StopPath objects that have the same values) before
creating the second TripPatternKey so equality/hashCode are validated logically;
apply the same change to the similar assertions around the other occurrence (the
test near the block that corresponds to lines 151-156) referencing the same
classes `TripPatternKey` and `StopPath`.

In `@transitclock/src/test/java/org/transitclock/utils/ChinaGpsOffsetTest.java`:
- Around line 18-38: Add explicit boundary-value assertions for
ChinaGpsOffset.outOfChina to protect against off-by-one regressions: call
outOfChina with the exact longitude thresholds 72.004 and 137.8347 (e.g.,
outOfChina(<anyLatWithinRange>, 72.004) and 137.8347) and assertFalse, and call
outOfChina with the exact latitude thresholds 0.8293 and 55.8271 (e.g.,
outOfChina(0.8293, <anyLonWithinRange>) and 55.8271) and assertFalse; also
consider adding one test just inside and one just outside each threshold (e.g.,
72.0039 and 72.0041) to verify the strict comparison behavior in
ChinaGpsOffset.outOfChina.

In `@transitclock/src/test/java/org/transitclock/utils/EncryptionTest.java`:
- Around line 54-79: Extract the repeated try/catch/fail pattern in
decryptOfTamperedCiphertextThrows and decryptOfGarbageThrows into a small test
helper (e.g., assertDecryptFails(String ciphertext)) that calls
Encryption.decrypt(ciphertext) and fails the test unless an
EncryptionOperationNotPossibleException is thrown; then replace the try/catch
blocks in both tests with calls to assertDecryptFails("not-a-real-ciphertext")
and assertDecryptFails(tampered) to remove duplication and keep tests focused on
inputs.
- Around line 43-52: Test currently only compares Encryption.decrypt(a) to
Encryption.decrypt(b); strengthen it by asserting both decryptions equal the
original plaintext "sameInput". In the test method
encryptionIsSaltedSoSameInputProducesDifferentCiphertexts, replace or supplement
the existing assertEquals(Encryption.decrypt(a), Encryption.decrypt(b)) with two
assertions that assertEquals("sameInput", Encryption.decrypt(a)) and
assertEquals("sameInput", Encryption.decrypt(b)) so both decrypted values are
explicitly validated against the original input while keeping the
assertNotEquals(a, b) check for differing ciphertexts.

In `@transitclockPipelineTests/pom.xml`:
- Around line 71-74: Update the maven-surefire-plugin version to a
JDK17-compatible release by changing the plugin declaration for
org.apache.maven.plugins:maven-surefire-plugin (the <version> element currently
set to 2.19.1) to 3.5.5 (or later); ensure the <configuration> remains valid for
the 3.x provider and adjust any deprecated configuration keys if build warnings
appear after the upgrade (target the plugin coordinates groupId
org.apache.maven.plugins and artifactId maven-surefire-plugin).

In
`@transitclockPipelineTests/src/test/java/org/transitclock/pipelinetests/CoreHarness.java`:
- Around line 210-212: The temp directory created via Files.createTempDirectory
and stored in the staged Path in CoreHarness is only registered with
staged.toFile().deleteOnExit(), which won't remove non-empty directories; change
to explicitly clean up the directory contents either by registering a JVM
shutdown hook that recursively deletes staged (including files) or by ensuring
tests use a try/finally (or try-with-resources style wrapper) that recursively
deletes staged after use; locate the staged variable and replace the single
deleteOnExit() call with a recursive delete routine invoked from a shutdown hook
or final cleanup block to ensure files copied into staged are removed.
🪄 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: b80bb7f2-360c-4206-8427-151a1ba20535

📥 Commits

Reviewing files that changed from the base of the PR and between e476ecf and 8751210.

📒 Files selected for processing (26)
  • .github/workflows/ci.yml
  • .gitignore
  • CLAUDE.md
  • README.md
  • pom.xml
  • transitclock/src/test/java/org/transitclock/core/VehicleAtStopInfoTest.java
  • transitclock/src/test/java/org/transitclock/db/structs/ArrivalDepartureSubclassesTest.java
  • transitclock/src/test/java/org/transitclock/db/structs/BlockTest.java
  • transitclock/src/test/java/org/transitclock/db/structs/StopPathExtendedTest.java
  • transitclock/src/test/java/org/transitclock/db/structs/TripPatternKeyTest.java
  • transitclock/src/test/java/org/transitclock/db/structs/TripTest.java
  • transitclock/src/test/java/org/transitclock/testutil/TransitFixtures.java
  • transitclock/src/test/java/org/transitclock/utils/ChinaGpsOffsetTest.java
  • transitclock/src/test/java/org/transitclock/utils/EncryptionTest.java
  • transitclock/src/test/java/org/transitclock/utils/IntervalTimerTest.java
  • transitclock/src/test/java/org/transitclock/utils/MapKeyTest.java
  • transitclock/src/test/java/org/transitclock/utils/OrderedCollectionTest.java
  • transitclockPipelineTests/.gitignore
  • transitclockPipelineTests/pom.xml
  • transitclockPipelineTests/src/test/java/org/transitclock/pipelinetests/AvlProcessorBehaviorTest.java
  • transitclockPipelineTests/src/test/java/org/transitclock/pipelinetests/CoreHarness.java
  • transitclockPipelineTests/src/test/java/org/transitclock/pipelinetests/CoreHarnessCalendarRewriteTest.java
  • transitclockPipelineTests/src/test/java/org/transitclock/pipelinetests/CoreHarnessSmokeTest.java
  • transitclockPipelineTests/src/test/resources/hsql_pipeline_test_hibernate.cfg.xml
  • transitclockPipelineTests/src/test/resources/logback-test.xml
  • transitclockPipelineTests/src/test/resources/transitclockConfigHsql.xml

Comment thread CLAUDE.md Outdated
Comment on lines +117 to +125
public void differentStopIdBreaksEquality() {
StopPath p1 = stopPath(1, "p1", "s1", 0, "r1");
StopPath p1DiffStop = stopPath(1, "p1", "sX", 0, "r1");
TripPatternKey a = new TripPatternKey("shapeA",
Collections.singletonList(p1));
TripPatternKey b = new TripPatternKey("shapeA",
Collections.singletonList(p1DiffStop));
assertNotEquals(a, b);
}

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 | 🔴 Critical

Assertions here contradict TripPatternKey.equals/hashCode and will fail

These tests assume stopId and configRev affect key equality, but TripPatternKey currently compares shapeId plus ordered StopPath#getStopPathIndex() values (see transitclock/src/main/java/org/transitclock/db/structs/TripPatternKey.java, Lines 86-140).
Please align contract and tests: either update TripPatternKey semantics or change these assertions to match current behavior.

Also applies to: 128-134

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

In
`@transitclock/src/test/java/org/transitclock/db/structs/TripPatternKeyTest.java`
around lines 117 - 125, The test assertions contradict
TripPatternKey.equals/hashCode (which compares shapeId and ordered
StopPath#getStopPathIndex() values), so update the tests rather than production
code: in TripPatternKeyTest methods like differentStopIdBreaksEquality (and the
similar one at 128-134) either change assertNotEquals(a, b) to assertEquals(a,
b) to reflect that differing StopPath.stopId/configRev does not affect equality,
or alter the StopPath fixtures to produce different stopPathIndex values (via
stopPath(...) inputs) so equality should legitimately differ; reference
TripPatternKey.equals/hashCode and StopPath#getStopPathIndex() when making the
change.

Comment thread transitclock/src/test/java/org/transitclock/utils/IntervalTimerTest.java Outdated
Comment thread transitclockPipelineTests/pom.xml
Comment on lines +38 to +64
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.4</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<!-- Matches transitclockCore's logback-classic version to avoid
classpath surprises during test runs. -->
<version>1.1.3</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>

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 | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For Maven artifacts org.hsqldb:hsqldb, ch.qos.logback:logback-classic, org.assertj:assertj-core, and junit:junit, list current GHSA/CVE advisories and the first patched versions compatible with Java 17.

💡 Result:

org.hsqldb:hsqldb - GHSA/CVE: GHSA-77xx-rxvh-q682 / CVE-2022-41853 (Remote Code Execution via untrusted input in Statement/PreparedStatement) - First patched version compatible with Java 17: 2.7.1 (main jar requires JRE 11+, tested up to Java 17; current latest 2.7.4) ch.qos.logback:logback-classic - GHSA/CVE: GHSA-25qh-j22f-pwp8 / CVE-2025-11226 (Arbitrary Code Execution in conditional config file processing, requires Janino+Spring); other recent CVEs like CVE-2024-12798, CVE-2024-12801 backported to 1.3.x, CVE-2026-1225 fixed in 1.5.25 - First patched version compatible with Java 17: 1.2.0 (all modern versions 1.3+ require JDK8+, 1.5+ JDK11+ built/tested on JDK21 but run on 17; current latest 1.5.32) org.assertj:assertj-core - GHSA/CVE: GHSA-rqfh-9r24-8c9r / CVE-2026-24400 (XXE in isXmlEqualTo assertion via XmlStringPrettyFormatter with untrusted XML) - First patched version compatible with Java 17: 3.17.0 (dropped Java 8 support; current latest 3.27.7) junit:junit (JUnit 4) - GHSA/CVE: GHSA-269g-pwp5-87pp / CVE-2020-15250 (TemporaryFolder JUnit rule creates world-readable files on Unix-like systems) - First patched version compatible with Java 17: 4.13.1 (all JUnit 4 versions compatible with Java 17; current latest 4.13.2)

Citations:


🏁 Script executed:

#!/bin/bash
# First, locate and read the specific pom.xml file
fd -t f "pom.xml" | head -20

Repository: OneBusAway/thetransitclock

Length of output: 339


🏁 Script executed:

#!/bin/bash
# Read the transitclockPipelineTests/pom.xml file
cat -n transitclockPipelineTests/pom.xml | head -100

Repository: OneBusAway/thetransitclock

Length of output: 4085


🏁 Script executed:

#!/bin/bash
# Check the root pom for Java version target
cat -n pom.xml | grep -A 5 -B 5 "java\|Java\|source\|target" | head -50

Repository: OneBusAway/thetransitclock

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Broader search for Java version in root pom
cat pom.xml | grep -i "java\|source\|target\|release" | head -30

Repository: OneBusAway/thetransitclock

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check transitclock (core) pom for Java version and logback version
cat transitclock/pom.xml | grep -A 2 -B 2 "java\|Java\|logback" | head -80

Repository: OneBusAway/thetransitclock

Length of output: 2463


🏁 Script executed:

#!/bin/bash
# Search for maven-compiler-plugin configuration in root pom
cat pom.xml | grep -A 10 "maven-compiler-plugin"

Repository: OneBusAway/thetransitclock

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check if there's a parent pom reference and look for full <properties> section in root
cat pom.xml | head -150

Repository: OneBusAway/thetransitclock

Length of output: 2604


🏁 Script executed:

#!/bin/bash
# Read the full transitclock/pom.xml to check for Java version and compiler settings
wc -l transitclock/pom.xml

Repository: OneBusAway/thetransitclock

Length of output: 94


🏁 Script executed:

#!/bin/bash
# Get sections with build, properties, and compiler from transitclock/pom.xml
cat transitclock/pom.xml | grep -A 20 "<build>" | head -40

Repository: OneBusAway/thetransitclock

Length of output: 1200


Upgrade vulnerable test dependencies before merge.

The pinned versions are affected by known security advisories that impact CI and developer environments despite test scope:

  • hsqldb 2.2.4 → upgrade to 2.7.1+ (RCE via CVE-2022-41853)
  • logback-classic 1.1.3 → upgrade to 1.2.0+ (Arbitrary Code Execution via CVE-2025-11226)
  • junit 4.11 → upgrade to 4.13.1+ (file permission exposure via CVE-2020-15250)
  • assertj-core 3.24.2 → upgrade to 3.17.0+ (XXE via CVE-2026-24400)

All patched versions are compatible with Java 17 (the project's target).

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

In `@transitclockPipelineTests/pom.xml` around lines 38 - 64, Update the test
dependency versions in the pom.xml to address known CVEs: bump org.hsqldb:hsqldb
from 2.2.4 to at least 2.7.1, ch.qos.logback:logback-classic from 1.1.3 to
1.2.0+, junit:junit from 4.11 to 4.13.1+, and org.assertj:assertj-core from
3.24.2 down/side-step to a safe patched line (use 3.17.0+ as recommended);
locate and modify the <dependency> blocks for artifactIds hsqldb,
logback-classic, junit and assertj-core in the file and update the <version>
elements accordingly, then run your build/CI to verify tests still pass under
Java 17.

- Fix module count in CLAUDE.md (nine, not eight).
- Fix HSQLDB JDBC URL format to the standard jdbc:hsqldb:mem:<name>.
- Fix VechiclesServer typo in transitclockConfigHsql.xml.
- Inherit root parent pom in transitclockPipelineTests so JaCoCo and other
  shared plugins apply. Preserve the jacoco-set argLine via @{argLine} so
  the agent isn't dropped when we add -Dtransitclock.logging.dir.
- Bump junit (4.11 -> 4.13.2) and assertj-core (3.24.2 -> 3.26.3) in
  transitclockPipelineTests to match transitclockCore. Left hsqldb and
  logback-classic deliberately aligned with core per the explicit comment,
  since diverging risks classpath surprises when the pipeline tests boot a
  real Core.
- Handle partial trip times in TransitFixtures.blockOf — previously a trip
  with only a start or only an end could leave startTime or endTime at
  Integer.MAX_VALUE / Integer.MIN_VALUE, or a present end could be reset
  to zero.
- Parse IntervalTimer.elapsedMsecStr()/toString() via NumberFormat so the
  tests don't fail on JVMs whose default locale uses a comma decimal
  separator.
@aaronbrethorst
aaronbrethorst merged commit afb8792 into develop Apr 23, 2026
2 of 3 checks passed
@aaronbrethorst
aaronbrethorst deleted the pipeline-tests branch April 23, 2026 02:47
This was referenced Apr 23, 2026
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