48 lines
3.4 KiB
Markdown
48 lines
3.4 KiB
Markdown
# azkoyen_technical_test
|
|
|
|
Azkoyen technical test implementation. Implemented (mostly) on standard c++ 17 framework, but with Qt wherever was necessary.
|
|
|
|
## Development approach
|
|
|
|
A Test-Driven Development (TDD) workflow was followed throughout the project. Every component — from the lowest-level file reader to the GUI window — has a corresponding Google Test suite that was written before (or alongside) the production code. This ensures each module behaves correctly in isolation and makes regressions immediately visible.
|
|
|
|
## SysfsRead class
|
|
|
|
`SysfsReader` ([include/SysfsRead.hpp](include/SysfsRead.hpp), [src/core/SysfsRead.cxx](src/core/SysfsRead.cxx)) is the lowest-level component. It opens a sysfs-like file and translates its raw text content into a `SysfsStatus` enum:
|
|
|
|
| File content | Status |
|
|
|--------------------------|---------------------|
|
|
| `"1"` | `Enabled` |
|
|
| `"error: temp too high"` | `ErrorTempTooHigh` |
|
|
| empty / whitespace-only | `Empty` |
|
|
| file missing | `Unreachable` |
|
|
| anything else | `UnexpectedValue` |
|
|
|
|
The reader never throws on I/O errors; every outcome is expressed through the enum so callers can react without exception handling. A helper `trim_in_place` strips trailing whitespace and newlines before comparison.
|
|
|
|
**Tests:** [tests/test_sysfs_read.cxx](tests/test_sysfs_read.cxx) — covers all five status branches by writing controlled content to a temporary file.
|
|
|
|
## Producer class / thread
|
|
|
|
`Producer` ([include/Producer.hpp](include/Producer.hpp), [src/core/Producer.cxx](src/core/Producer.cxx)) runs a worker `std::thread` that periodically polls the `SysfsReader` and, when the status is `Enabled`, generates a random integer and forwards it through an injected `send_fn` callback. The polling interval is 1 second under normal conditions and 7 seconds when the sysfs file reports `ErrorTempTooHigh` (cool-down).
|
|
|
|
## UnixIpcBridge
|
|
|
|
>[!note]
|
|
>why unix domain sockets? Because I have more experience with them under linux than with posix shared memmory and semaphore, and I find them easier to unit-test.
|
|
|
|
`UnixIpcBridge` ([include/UnixIpcBridge.hpp](include/UnixIpcBridge.hpp), [src/core/UnixIpcBridge.cxx](src/core/UnixIpcBridge.cxx)) is a small helper that connects to a UNIX domain socket and sends a single `int` per call. It opens a new connection for each value, which keeps the protocol stateless and simple.
|
|
|
|
**Tests:** [tests/test_unix_ipc.cxx](tests/test_unix_ipc.cxx) — spins up a fake socket server, sends values through the bridge, and asserts they arrive correctly.
|
|
|
|
## ConsumerThread
|
|
|
|
`ConsumerThread` ([include/ConsumerThread.hpp](include/ConsumerThread.hpp), [src/core/ConsumerThread.cxx](src/core/ConsumerThread.cxx)) is a `QObject` that listens on a UNIX domain socket in a background `std::thread`. On each received integer it:
|
|
|
|
1. Prints the value to `stdout`.
|
|
2. Emits the `valueReceived(int)` Qt signal.
|
|
|
|
The server socket is created and bound inside `start()` **before** the thread is spawned, so the socket is guaranteed to be ready by the time `start()` returns — eliminating race conditions with the producer. Graceful shutdown is handled by `stop()`, which shuts down the file descriptor to unblock the blocking `accept()` call.
|
|
|
|
**Tests:** [tests/test_consumer_thread.cxx](tests/test_consumer_thread.cxx) — uses `QSignalSpy` to verify single-value, multi-value, negative, and zero reception.
|