-
Notifications
You must be signed in to change notification settings - Fork 1
Integration tests #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
0462575
Unblock transitclockIntegration build
aaronbrethorst 1227f8c
Disable two fixture-rotten integration tests
aaronbrethorst a3847f0
Add tools/wmata_capture for refreshing integration-test fixtures
aaronbrethorst 12a33a6
Add run-all-tests profile for a single-command full-suite run
aaronbrethorst 40839a5
Make @Ignore actually take effect in the two disabled integration tests
aaronbrethorst 18ba5e5
Add integration tests as a CI step
aaronbrethorst a5224e8
Auto-load tools/wmata_capture/.env in the capture script
aaronbrethorst 9ad9170
Ignore __pycache__ directories
aaronbrethorst 6ff1424
Address PR #9 review nits
aaronbrethorst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Copy this file to `.env` and fill in your real key. `.env` is gitignored. | ||
| # | ||
| # Request a key at https://developer.wmata.com — the free tier covers a | ||
| # handful of consecutive hours of GTFS-RT polling, which is plenty to | ||
| # capture fixture-sized traces (~4h). | ||
| # | ||
| # DO NOT commit your real key. DO NOT pass it as a CLI flag. | ||
| WMATA_API_KEY=your-wmata-api-key-here |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| # tools/wmata_capture | ||
|
|
||
| Capture live WMATA bus data into the exact fixture format the | ||
| `transitclockIntegration` suite already ingests, so we can refresh the | ||
| aging 2016 fixtures with fresh, realistic traces. | ||
|
|
||
| The capture is a single-file Python script (`capture.py`) with | ||
| [PEP 723](https://peps.python.org/pep-0723/) inline dependency metadata. | ||
| It's designed to run for hours in the background and produce: | ||
|
|
||
| 1. `<output-dir>/gtfs/` — an unpacked static GTFS feed (the feed WMATA | ||
| publishes at the instant you start the capture). | ||
| 2. `<output-dir>/avl/<route_id>_<vehicle_id>.csv` — one CSV per vehicle, | ||
| formatted for `BatchCsvAvlFeedModule` with header | ||
| `vehicleId,time,assignmentId,assignmentType,heading,latitude,longitude`. | ||
| 3. `<output-dir>/capture.log` — run log (appended across repeated runs | ||
| against the same output dir). | ||
|
|
||
| ## Why this exists | ||
|
|
||
| Two integration tests in `transitclockIntegration` are currently | ||
| `@Ignore`'d because their 2016 WMATA fixtures have aged past the point | ||
| where the pinned assertions are meaningful — see | ||
| [OneBusAway/thetransitclock#7](https://github.com/OneBusAway/thetransitclock/issues/7) | ||
| and | ||
| [OneBusAway/thetransitclock#8](https://github.com/OneBusAway/thetransitclock/issues/8). | ||
| To re-enable them properly we need fresh AVL traces and a fresh baseline | ||
| prediction CSV. This tool produces the AVL side; the baseline CSV is then | ||
| regenerated by re-running the prediction test against the captured data. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - `uv` installed (`brew install uv` or see | ||
| [astral.sh/uv](https://docs.astral.sh/uv/)). | ||
| - A WMATA developer API key from | ||
| [developer.wmata.com](https://developer.wmata.com). The free tier is | ||
| sufficient. | ||
| - `WMATA_API_KEY` available in your environment. Copy `.env.example` to | ||
| `.env` and `source .env` before running — **never** commit the key, and | ||
| never pass it as a CLI flag (it would show up in shell history and in | ||
| the output of `ps`). | ||
|
|
||
| The script declares its own Python dependencies (`requests`, | ||
| `gtfs-realtime-bindings`) via PEP 723, so `uv run capture.py ...` will | ||
| resolve them into an ephemeral venv the first time. | ||
|
|
||
| ## Run recipes | ||
|
|
||
| ### Quick smoke test (5 minutes, one route) | ||
|
|
||
| ```sh | ||
| export WMATA_API_KEY=... | ||
| uv run capture.py \ | ||
| --output-dir ./output/smoke-$(date +%Y%m%d-%H%M%S) \ | ||
| --duration-hours 0.083 \ | ||
| --routes S2 | ||
| ``` | ||
|
|
||
| Expected output: | ||
|
|
||
| - `./output/smoke-.../gtfs/trips.txt` etc. present | ||
| - `./output/smoke-.../avl/S2_<vehicleId>.csv` for every S2 vehicle the | ||
| feed reported | ||
| - `./output/smoke-.../capture.log` with `polls ok: ~10` and a row count | ||
|
|
||
| ### Realistic fixture capture (AM rush, small route set) | ||
|
|
||
| ```sh | ||
| export WMATA_API_KEY=... | ||
| uv run capture.py \ | ||
| --output-dir ./output/fixtures-$(date +%Y%m%d) \ | ||
| --duration-hours 4 \ | ||
| --poll-interval 30 \ | ||
| --routes S2,3T,5A | ||
| ``` | ||
|
|
||
| Run against Eastern weekday AM peak (~06:00–10:00 ET) for the most data | ||
| per vehicle. Background it with `nohup ... &` or run under `tmux`. | ||
|
|
||
| ### Single-vehicle targeted capture | ||
|
|
||
| ```sh | ||
| export WMATA_API_KEY=... | ||
| uv run capture.py \ | ||
| --output-dir ./output/target-$(date +%Y%m%d) \ | ||
| --duration-hours 2 \ | ||
| --vehicles 2113 | ||
| ``` | ||
|
|
||
| ## Promoting captures to fixtures | ||
|
|
||
| Once a capture looks good: | ||
|
|
||
| 1. Replace the module's GTFS: | ||
| ```sh | ||
| rm -rf transitclockIntegration/src/test/resources/gtfs/S2 | ||
| cp -R tools/wmata_capture/output/<run>/gtfs transitclockIntegration/src/test/resources/gtfs/S2 | ||
| ``` | ||
| Adjust the target subdir name to the route the test uses. | ||
| 2. Replace the AVL CSV: | ||
| ```sh | ||
| cp tools/wmata_capture/output/<run>/avl/S2_<vehicleId>.csv \ | ||
| transitclockIntegration/src/test/resources/avl/S2_<vehicleId>.csv | ||
| ``` | ||
| Rename to match the test's hard-coded constant | ||
| (e.g. `S2_2113.csv`). | ||
| 3. **Regenerate the prediction baseline** (`pred/*.csv`). This is not | ||
| produced by this capture tool — it's the *output* of running the | ||
| current predictor against the new AVL. To rebuild: | ||
| - Temporarily remove the `@Ignore` from | ||
| `PredictionAccuracyIntegrationTest`. | ||
| - Run the test; dump the `new`-side predictions (see the test's | ||
| `setUp` — `session.createCriteria(Prediction.class).list()`) to a | ||
| CSV with the same columns as the existing | ||
| `pred/S2_2113.csv`. | ||
| - Save that CSV as the new baseline. | ||
| - Restore `@Ignore` removal into a real code change, not a test run. | ||
| 4. Commit the new fixtures + baseline in one PR; reference issues | ||
| OneBusAway/thetransitclock#7 and #8 in the description. | ||
|
|
||
| ## Known caveats | ||
|
|
||
| - **`block_id` in WMATA GTFS is not always populated.** The script falls | ||
| back to `assignmentType=TRIP_ID` when the GTFS-RT feed reports a | ||
| `trip_id` that has no corresponding `block_id` in `trips.txt`. | ||
| `BatchCsvAvlFeedModule` + `BlockAssigner` handle both types, but some | ||
| transitclock behavior paths depend on block assignment specifically — | ||
| prefer capturing a route/period where `trips.txt` does carry block_ids | ||
| (check via `awk -F, '{print $<block-col>}' trips.txt | sort -u | head`). | ||
| - **The GTFS-RT feed repeats observations.** The script de-duplicates on | ||
| `(vehicle_id, feed-reported timestamp)` within a single run, so a | ||
| 30-second poll interval against a feed that only updates every 60s | ||
| won't double up. De-dup state is not persisted across runs — don't | ||
| resume into the same output dir and expect continuity; start a new | ||
| output dir. | ||
| - **Clock.** Timestamps are emitted in `America/New_York` local time | ||
| formatted `MM-dd-yyyy HH:mm:ss` (the format `BatchCsvAvlFeedModule` | ||
| parses using the JVM default timezone). The capture host's TZ does | ||
| not matter. | ||
| - **Outages happen.** WMATA's API can return 5xx and can stall for | ||
| minutes. The script retries with a bounded exponential backoff (up to | ||
| ~2 minutes between attempts) rather than dying. | ||
|
|
||
| ## Files | ||
|
|
||
| - `capture.py` — the script (PEP 723, single file). | ||
| - `.env.example` — template for `WMATA_API_KEY`. Real `.env` is | ||
| gitignored. | ||
| - `output/` — default capture destination. Gitignored. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace the non-runnable awk placeholder with an executable command.
$<block-col>is a placeholder and will fail if copied verbatim.📘 Proposed doc fix
📝 Committable suggestion
🤖 Prompt for AI Agents