# GTK Layer Shell tests
This directory is home to the gtk4-layer-shell test suite.

## To run tests
```bash
ninja -C build test
```
(where `build` is the path to your build directory).

__To run a single test:__
```bash
ninja -C build && meson test -C build --verbose [testname]
```

__To run all tests under valgrind:__
```bash
GTKLS_VALGRIND=1 ninja -C build test
```

__To run against the current Wayland compositor:__
```bash
ninja -C build && ./build/test/<testname> --auto
```
This can help with debugging since you get to see the output.

__Don't run the session lock tests this way__ unless you know what you're doing. Some of them may soft-brick your compositor. If this happens you may need to reboot.

You can drop the `--auto` flag to run the test interactively, so you can click through each section.

__To run against the current compositor under [wayland-debug](https://github.com/wmww/wayland-debug):__
```bash
ninja -C build && wayland-debug -f 'zwlr_*, xdg_*, ext_*' -r ./build/test/<testname> --auto
```

__To run under the mock server, but separately:__
This can be useful to run the tests under wayland-debug or GDB
```bash
# server:
ninja -C build && GTKLS_TEST_DIR=/tmp ./build/test/mock-server/mock-server
# client:
ninja -C build && GTKLS_TEST_DIR=/tmp ./build/test/[testname] --auto
```

__To run the test script without meson:__
This is mostly useful for debugging the test script
```bash
ninja -C build && test/run-integration-test.py build/test/<testname>
```

## To add a new integration test
1. Copy an existing integration test file
2. Implement your test as a series of one or more callbacks
3. Add its name to the list in `test/integration-tests/meson.build`

## Scripts
- `tests-not-enabled.py` is only run if tests are disabled, and explains to the user how to enable them
- `run-integration-test.py` runs a single integration test
- `check-all-tests-are-in-meson.py` fails if any test files exist that haven't been added to meson (an easy mistake to make)

## Integration tests
Most of the potential bugs in GTK Layer Shell arise from interactions between the library, GTK and the Wayland compositor, so unit tests aren't particularly useful. Instead, most of our tests are integration tests.

### Integration test app
Each integration test is a single unique GTK app that uses GTK Layer Shell. All test clients are located in an integration test subdirectory (eg `layer-tests/`). Anything common to multiple tests gets pulled into `integration-test-common` or `test-common`. Tests consist of a sequence of callbacks. At the start of each callback the app can state that specific Wayland messages should be sent during or after the callback is run (see expectations format below). Each meson test runs a single integration test.

Integration tests can be run directly on a normal Wayland compositor (this may be useful for debugging). When run without arguments, they open an additional layer shell window with a `Continue ->` button to manually advance the test. Pass `--auto` to run each test callback with a timeout the way they are run when automated.

### Expectations format
Integration tests emit protocol expectations by using the `EXPECT_MESSAGE` macro. Each expectation is a white-space-separated sequence of tokens written to a line of stdout. The first element must be `EXPECT:` (this is automatically inserted by `EXPECT_MESSAGE`). For an expectation to match a message, each following token must appear in order in the message line. The list of expected messages must match in the correct order. Messages are matched against the output of the app run with `WAYLAND_DEBUG=1`. Events and requests are not distinguished.

Tests can also use the `UNEXPECT_MESSAGE()` macro to emit `UNEXPECT:` lines. They're the same, except if a matching message is encountered the test fails.

When the script encounters `CHECK EXPECTATIONS COMPLETED` (emitted by the `CHECK_EXPECTATIONS()` macro), it will assert that all previous expectations have been met. This is emitted automatically at the start of each test callback, and implicitly exists at the end of the test.

### Test runner
`ninja -C build test` will run `run-integration-test.py` for each test defined in `test/meson.build`. This script:
- Creates a temporary directory in `/tmp` to serve as `XDG_RUNTIME_DIR` (this allows tests to run in parallel without interfering with each other)
- Spins up a mock Wayland server
- Runs the given integration test within it
- (Both are run with `WAYLAND_DEBUG=1` so protocol messages are written to stderr by libwayland)
- Ensures both the client and server exit successfully
- Parses the client's protocol message expectations
- Ensures they match the protocol messages generated by libwayland

### Mock server
Rather than running the integration tests in an external Wayland compositor, we implement our own mock Wayland compositor (located in `mock-server`). This doesn't show anything on-screen or get real user input, it simply gives the required responses to protocol messages. It's only dependency is libwayland. It implements most of the protocol with a single default dispatcher. This reads the message signature and takes whatever action appears to be required. The behavior of some messages is overridden in `overrides.c`.

## Container images
These images are used to run the tests in CI
- Generated a classic token [here](https://github.com/settings/tokens) with `write:packages`, `read:packages` and `delete:packages`
- `podman login ghcr.io -u wmww` and entered it on the prompt
- `podman build -f test/Containerfile -t ghcr.io/wmww/gtk4-layer-shell-ci:latest .`
- `podman push ghcr.io/wmww/gtk4-layer-shell-ci:latest`
- Create container and shell into it: `podman run -it --replace --name gtk4-layer-shell-ci-0 ghcr.io/wmww/gtk4-layer-shell-ci:latest bash`
- Open another shell: `podman exec -it gtk4-layer-shell-ci-0 bash`
- Get size of image `echo "$(("$(podman image inspect ghcr.io/wmww/gtk4-layer-shell-ci:latest --format '{{ .Size }}')" / 1024 / 1024)) MB"`
