blob: 2006973ddfc018d690eb60061815fd1fdd15965a [file] [view] [edit]
# WLCS Development Guide
## Architecture Overview
**What is WLCS**: A protocol-conformance test suite for Wayland compositors. Unlike previous test suites that use protocol extensions, WLCS uses compositor-provided integration modules loaded as shared libraries (.so files). Tests run in-process with the compositor, enabling easier debugging and consistent timing.
**Core Components**:
- `include/wlcs/display_server.h`: Integration API that compositors must implement (`WlcsServerIntegration`, `WlcsDisplayServer` structs)
- `src/`: Test infrastructure (test runner, client/server helpers, protocol wrappers)
- `tests/`: Protocol conformance tests for wl_compositor, xdg_shell, layer_shell, input methods, etc.
- `example/mir_integration.cpp`: Reference integration showing how Mir implements the WLCS API
**Key Pattern**: Compositors provide a `wlcs_server_integration` symbol in their integration .so. WLCS loads this and calls hooks to start/stop the compositor, create client sockets, inject input, etc.
## Test Infrastructure
**Base Classes** (see `include/in_process_server.h`):
- `InProcessServer`: GTest fixture that creates a `Server` instance. Use `SetUp()/TearDown()` explicitly
- `StartedInProcessServer`: Auto-starts the server in constructor. Most tests inherit from this
**Creating Test Clients**:
```cpp
wlcs::Client client{the_server()}; // Creates Wayland client connection
wlcs::Surface surface{client}; // Creates wl_surface
client.roundtrip(); // Block until server processes requests
```
**Simulating Input** (see `include/wlcs/pointer.h`, `include/wlcs/touch.h`):
```cpp
auto pointer = the_server().create_pointer();
pointer.move_to(100, 200); // Absolute coordinates
pointer.left_click();
auto touch = the_server().create_touch();
touch.down_at(50, 50);
touch.move_to(60, 60);
touch.up();
```
**Protocol Wrappers**: Headers like `layer_shell_v1.h`, `xdg_shell_stable.h` provide RAII wrappers around Wayland protocols with helper methods (e.g., `dispatch_until_configure()`).
**Parameterized Tests**: Many tests use `TEST_P` to run across multiple shell types or input devices. See `tests/subsurfaces.cpp` for the pattern using `AbstractInputDevice` abstraction.
## Code Style
Follow the [Canonical Mir C++ Guide](https://canonical-mir.readthedocs-hosted.com/stable/contributing/reference/cppguide/).
**Requirements**:
- C++20 required (`CMAKE_CXX_STANDARD 20`)
- Strict warnings: `-Werror` enabled by default (toggle with `WLCS_FATAL_COMPILE_WARNINGS`)
**Key Conventions**:
- Headers use include guards (`#ifndef WLCS_*_H_`)
- Wayland objects wrapped in RAII types (see `WlHandle<T>` in `wl_handle.h`)
- Helper timeouts: `wlcs::helpers::a_short_time()` (for negative tests), `wlcs::helpers::a_long_time()` (for operation timeouts)
- Test structure: Arrange (create surfaces/clients), Act (inject input/commit), Assert (check window focus/position)
**Testing Protocol Errors**: Use `expect_protocol_error.h` to verify compositor rejects invalid client requests:
```cpp
EXPECT_PROTOCOL_ERROR(client, &interface_name, error_code, {
// code that should trigger error
});
```
## Common Tasks
**Adding Protocol Tests**:
1. Add `.xml` to `src/protocol/` if not present
2. Create wrapper class in `include/` (follow pattern from `layer_shell_v1.h`)
3. Implement wrapper in `src/` (generate protocol bindings in CMake)
4. Write tests in `tests/` inheriting from `StartedInProcessServer`
**Protocol Version Handling**: Use `VersionSpecifier` (see `version_specifier.h`) to bind interfaces:
```cpp
auto shell = client.bind_if_supported<xdg_wm_base>(wlcs::AtLeast(2));
```