Part of #41. The top bar has Quiz and Statistics tabs instead of the Statistics button. The Statistics tab replaces the quiz below the top bar (the song keeps playing) and shows every soundtrack as a card with its plays, last played date and most and least played songs, loaded from the new GET /api/v1/statistics/soundtracks. The cards can be searched (with the Search popup's matching, now in SoundtrackSearch), filtered on played or never played, and sorted A-Z, Z-A, most or least played. Clicking a card opens a popup with all its songs. Screenshots of the other states only differ in the top bar, and the layout baseline is taken again with the new statistics states.
84 lines
3.6 KiB
Markdown
84 lines
3.6 KiB
Markdown
# Testing
|
|
|
|
Tests use [GdUnit4](https://github.com/godot-gdunit-labs/gdUnit4) 6.2.1, committed in
|
|
`addons/gdUnit4/`. The whole suite runs headless in about 10 seconds.
|
|
|
|
## Running the tests
|
|
|
|
```bash
|
|
./run_tests.sh # everything
|
|
./run_tests.sh res://tests/unit # only unit tests
|
|
./run_tests.sh res://tests/unit/test_search.gd # one suite
|
|
```
|
|
|
|
The script finds Godot through `$GODOT`, then `godot` on your PATH, then
|
|
`/Applications/Godot.app` or `/Applications/Godot_mono.app`. Reports are written to
|
|
`reports/` (ignored by git).
|
|
|
|
In the editor, open the GdUnit4 panel or right-click the `tests` folder and choose
|
|
**Run Tests**.
|
|
|
|
CI runs the suite in the `test` job of `.gitea/workflows/gitea-build.yaml` on every push
|
|
and pull request to `main` and `develop`. The `build` job only starts once the tests
|
|
pass.
|
|
|
|
## Layout
|
|
|
|
| Folder | Contents |
|
|
|---|---|
|
|
| `tests/unit/` | Logic without scenes: playlist, songs, player scoring, time formatting, search, the server client and the API classes |
|
|
| `tests/integration/` | Real scenes (`MainWindow.tscn`, `SyncPopup.tscn`) run against a fake server, including the point keybinds and the Statistics tab |
|
|
| `tests/fakes/` | `FakeHttpTransport`, `AutoloadSnapshot` and the `MainWindowTestSuite` base class |
|
|
| `tests/data/` | Files the fake server sends, e.g. a short MP3 |
|
|
|
|
## Faking the MusicServer
|
|
|
|
All server calls go through the `MusicServerClient` autoload, which sends requests with
|
|
a swappable `transport`. Tests install a `FakeHttpTransport` that answers from canned
|
|
responses and records what was requested:
|
|
|
|
```gdscript
|
|
var server: FakeHttpTransport = FakeHttpTransport.new().install()
|
|
server.respond_json("/music/list", [{"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Frog's Theme"}])
|
|
server.respond_bytes("/music/rand", mp3_bytes)
|
|
|
|
# ... run the code under test ...
|
|
|
|
assert_int(server.count("/music/rand")).is_equal(1)
|
|
server.uninstall()
|
|
```
|
|
|
|
Paths are relative to the API (`/music/list` is `<server>/api/v1/music/list`). Paths
|
|
without a response get a 404, so the code under test sees a failed request.
|
|
|
|
Like the real server, the fake hands out tokens on `POST /token` and answers 401 to
|
|
requests without a valid one, so the client's token handling runs in every test. Token
|
|
requests are counted in `server.token_requests`, not in `requested_paths`. Use
|
|
`server.expire_tokens()` to make the client renew its token, `server.hold("/token")` to
|
|
keep it waiting for one, and `server.respond_json("/token", {...}, 401)` for a refused
|
|
password.
|
|
|
|
## API classes
|
|
|
|
The classes in `server/generated/` (`SongInfo`, `StatisticsSummary`, ...) are generated
|
|
from the server's OpenAPI spec, committed as `server/openapi.json`. To update them, copy
|
|
a new spec there (`just openapi` in MusicServer, or `<server>/openapi.json`) and run:
|
|
|
|
```bash
|
|
godot --headless --path . -s tools/generate_api_types.gd
|
|
```
|
|
|
|
`tests/unit/test_api_types.gd` parses a sample response with each class.
|
|
|
|
## Writing tests
|
|
|
|
- Settings and Playlist are global. Tests that change them restore them afterwards, either
|
|
by hand or with `AutoloadSnapshot.take([Settings, Playlist])` and `restore()`.
|
|
- Main window tests extend `MainWindowTestSuite`, which starts the window against the fake
|
|
server and has helpers such as `_add_player()`, `_remove_player()` and
|
|
`_give_point_with_keybind()`.
|
|
- Player ids are UUID strings that never change. A player's position in
|
|
`Settings.player_array` only decides their keybind (Alt+1 for the first row and so on),
|
|
so tests check both: that points land on the right player id, and that keybinds follow
|
|
the rows when players are added or removed.
|