diff --git a/.clang-tidy b/.clang-tidy
index 85b5c39..19cb257 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,5 +1,5 @@
Checks: 'bugprone-*,cppcoreguidelines-*,performance-*,readability-*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-array-to-pointer-decay'
-WarningsAsErrors: ''
+WarningsAsErrors: '*'
HeaderFilterRegex: '.*'
ExcludeHeaderFilterRegex: '(_deps|\.pb\.h|\.pb\.cc|/usr/)'
FormatStyle: 'file'
\ No newline at end of file
diff --git a/README.md b/README.md
index 1c3dc88..88c9546 100644
--- a/README.md
+++ b/README.md
@@ -1,69 +1,309 @@
-# Neuron-IDE-runtime
+# NeuronIDE Runtime
-## Setup and Build Instructions
+> Single source of project context. This document is intentionally written to be
+> self-contained so that a person *or a language model* can understand the goal,
+> the architecture, the current implementation status, and how to build/test the
+> project without reading the source first.
-This project uses CMake and requires a modern C++17 compiler.
+## 1. What this project is
-### 1. Clone repo and install required dependencies
-This project uses GTest, LSL, SDL2, Clang-format, Clang-tidy, protobuf-compiler and cmake, but you don't need to install GTest and LSL because cmake will install it for you. Below is minimal linux setup.
+**NeuronIDE** is a tool for designing and running **EEG experiments**. It has two parts:
-```bash
-git clone git@github.com:KN-Neuron/Neuron-IDE-runtime.git
+- an **editor** (out of scope for this repository) that lets a researcher visually
+ design an experiment and serializes it to a file, and
+- a **runtime** (this repository, `Neuron-IDE-runtime`) that reads that file,
+ reconstructs the experiment as a scene, runs it, and records the data.
-sudo apt update
-sudo apt install cmake clang-format clang-tidy libsdl2-dev protobuf-compiler
+Running an experiment means:
+
+1. **Reading EEG data** from the participant's headset (the "cap") in real time.
+2. **Rendering a graphical interface** that drives the participant — e.g. telling
+ them what to focus on, or, for **SSVEP** experiments, flickering on-screen
+ objects at controlled frequencies.
+3. **Emitting time markers** for experiment events (e.g. "stimulus shown"),
+ stamped at the exact moment the corresponding frame hits the screen.
+4. **Persisting** the EEG samples and the markers, with timestamps, to an output
+ file for later offline analysis.
+
+The hard requirement that shapes the whole design is **temporal alignment**: the
+EEG samples and the stimulus markers must share a **common clock domain** so that
+"this brain response happened N ms after that stimulus" is meaningful. See
+[§4 Clock synchronization](#4-clock-synchronization-critical).
+
+## 2. Tech stack
+
+| Concern | Choice |
+| ------------------------ | ---------------------------------------------------------------------- |
+| Language | C++20 |
+| Build system | CMake (≥ 3.25), Ninja-friendly |
+| Experiment file format | Protocol Buffers (proto3) — see `protoFiles/neuronide.proto` |
+| EEG acquisition | [LSL — Lab Streaming Layer](https://github.com/sccn/liblsl) (`liblsl`) |
+| Rendering / windowing | SDL2 (+ SDL2_image), with vsync |
+| Inter-thread queues | [moodycamel ConcurrentQueue](https://github.com/cameron314/concurrentqueue) (lock-free) |
+| Python scripting (planned)| pybind11 (`ScriptComponent`) |
+| Testing | GoogleTest + CTest |
+| Tooling | clang-format, clang-tidy, gcovr (coverage) |
+
+`liblsl`, `concurrentqueue`, and `googletest` are fetched automatically by CMake
+(`FetchContent`). SDL2 and Protobuf are expected to be installed on the system.
+
+## 3. Architecture
+
+The runtime starts by parsing the experiment file into a **Scene**, then runs the
+experiment across cooperating threads coordinated by a top-level `Runtime` object.
+The design follows a **game-engine-like component model**: a `Scene` owns
+`SceneObject`s, and each `SceneObject` owns `Component`s that implement behavior.
+
+### Data flow
+
+```mermaid
+flowchart LR
+ File[Experiment file
protobuf] --> Parser
+ Parser -->|builds| Scene
+
+ subgraph Threads
+ Renderer
+ LSLReader
+ DataWriter
+ end
+
+ Scene --> Renderer
+ Cap[EEG headset
LSL stream] --> LSLReader
+
+ Renderer -->|Marker| MQ[(markerQueue)]
+ LSLReader -->|EEGData| EQ[(eegQueue)]
+
+ EQ --> DataWriter
+ MQ --> DataWriter
+ DataWriter --> Out[Output file
CSV]
```
-### 2. Build the Project
-To configure and compile the project, open your terminal in the root directory and run:
+- **Parser** reads the protobuf experiment file and produces a `Scene`
+ (objects + components). Stateless entry point: `Parser::parse(path)`.
+- **Renderer** owns the SDL window, runs the update→render loop, and uses **vsync**
+ so it can timestamp markers at the *actual* moment of screen refresh. Components
+ emit marker names during `update`; the Renderer stamps them with
+ `lsl::local_clock()` right after `SDL_RenderPresent` and pushes `Marker`s onto
+ `markerQueue`.
+- **LSLReader** resolves and subscribes to the EEG LSL stream and continuously
+ pushes `EEGData` samples onto `eegQueue`.
+- **DataWriter** drains both queues and writes them to disk via a pluggable
+ formatting strategy (currently CSV).
-```bash
-# Generate the build system in the "build" directory
-cmake -B build
+The two queues (`eegQueue`, `markerQueue`) are the only shared state between
+threads and are **lock-free** (`moodycamel::ConcurrentQueue`). Producers
+(Renderer, LSLReader) never block on consumers (DataWriter).
+
+### Component model
+
+```mermaid
+classDiagram
+ class Scene {
+ -string experimentName
+ -vector~shared_ptr~SceneObject~~ objects
+ +update(Context&) void
+ +render(SDL_Renderer*) void
+ }
+ class SceneObject {
+ +string name
+ +bool isVisible
+ +Transform transform
+ -vector~unique_ptr~Component~~ components
+ +update(Context&) void
+ +render(SDL_Renderer*) void
+ }
+ class Component {
+ <>
+ #weak_ptr~SceneObject~ owner
+ +update(Context&)* void
+ +render(SDL_Renderer*)* void
+ }
+ class BlinkComponent
+ class SpriteRenderer
+ class TextRenderer
+ class ScriptComponent
+
+ Scene *-- SceneObject
+ SceneObject *-- Component
+ Component <|-- BlinkComponent
+ Component <|-- SpriteRenderer
+ Component <|-- TextRenderer
+ Component <|-- ScriptComponent
+```
+
+Components are built from protobuf messages by a **`ComponentRegistry`** (a
+singleton mapping a proto component type id → factory function). New component
+types self-register via the `REGISTER_COMPONENT(typeId, creatorFunc)` macro, so
+the parser does not need to know about concrete component classes.
+
+### Core data structures
+
+```cpp
+struct EEGData { // one EEG sample
+ double timestamp; // in the local_clock() domain (see §4)
+ std::vector channels;
+};
+
+struct Marker { // one experiment event
+ std::string eventName;
+ double timestamp; // local_clock() at vsync
+};
+
+struct Context { // passed to Component::update each frame
+ double timestamp; // per-frame delta time
+ std::vector* markers; // sink: components append marker names here
+};
+```
+
+## 4. Clock synchronization (critical)
+
+EEG analysis depends on EEG samples and stimulus markers being **comparable in
+time**. The Renderer stamps markers with `lsl::local_clock()` (this machine's
+clock). LSL `pull_sample()`, however, returns timestamps in the **sender's** clock
+domain. If left unconverted, samples and markers would live in different clocks
+and could not be aligned.
+
+`LSLReader` therefore enables LSL inlet **post-processing**
+(`post_clocksync | post_dejitter | post_monotonize`) so the timestamps it stores
+are already mapped into the local `lsl::local_clock()` domain — the same clock the
+Renderer uses. This is the single most important correctness property of the data
+path.
+
+## 5. Thread lifecycle conventions
+
+Threads use C++20 `std::jthread` + `std::stop_token` for cooperative cancellation.
+Two ownership patterns are in use:
+
+- **Self-owned thread** (`LSLReader`, `DataWriter`): the class owns its `jthread`.
+ `start(...)` spawns the worker (and first calls `stop()` so it is restartable);
+ `stop()` requests stop, joins, and releases resources; the destructor calls
+ `stop()`. The worker loop body takes the `stop_token`.
+- **Caller-owned thread** (`Renderer`): the class exposes `render(stop_token)` and
+ the *caller* owns the thread driving it. This makes the loop trivially testable
+ by passing an injected `std::stop_source`.
+
+`LSLReader` additionally resolves its stream **lazily on the worker thread** (so
+`start()` returns immediately instead of blocking the runtime while waiting for the
+cap), uses a **blocking pull with a finite timeout** (no busy-wait, low latency,
+periodic stop-token checks), and catches `lsl::lost_error` to re-resolve a dropped
+stream rather than letting an exception terminate the process.
+
+## 6. Implementation status
+
+| Area / class | Status | Notes |
+| --------------------------- | ------------- | ------------------------------------------------------------ |
+| `Parser` | Implemented | protobuf → `Scene` |
+| `Scene` / `SceneObject` | Implemented | component containers |
+| `Component` (base) | Implemented | abstract `update` / `render` |
+| `ComponentRegistry` | Implemented | proto-type → factory, macro-based self-registration |
+| `specifiic components` | **Planned** | defined in `neuronide.proto`, not yet implemented in C++ |
+| `Renderer` | Implemented | SDL + vsync, marker timestamping |
+| `LSLReader` | Implemented | LSL inlet → `eegQueue`, clock-synced (see §4) |
+| `DataWriter` | Implemented | strategy-based; `CSVFormatStrategy` |
+| `Runtime` orchestration | **Stub** | `Runtime::start()` currently only prints; wiring of Parser + the three threads is the next integration step |
+
+The class diagram in older docs is partly aspirational; the table above reflects
+the actual code.
+
+## 7. Repository layout
-# Compile the project
-cmake --build build
```
+Neuron-IDE-runtime/ # the C++ runtime (git repo)
+ ├── README.md # this file, code context
+ ├── CMakeLists.txt # top-level: deps, warnings, static analysis, coverage
+ ├── cmake/ # Dependencies / CompilerWarnings / StaticAnalysis / Coverage
+ ├── protoFiles/
+ │ ├── neuronide.proto # experiment file schema
+ │ └── tests/ # .pbtxt fixtures + compiled .pb
+ ├── include/ # public headers, mirrored by src/
+ │ ├── data_structures/ # EEGData, Marker, Context
+ │ ├── parser/ # Parser
+ │ ├── scene/ # Scene, SceneObject, components/
+ │ ├── renderer/ # Renderer
+ │ ├── lslreader/ # LSLReader
+ │ ├── datawriter/ # DataWriter, IDataFormatStrategy, CSVFormatStrategy
+ │ └── Runtime.hpp
+ ├── src/ # one CMake subdirectory (static lib) per module
+ └── tests/
+ ├── unit_tests/ # CTest label: "unit"
+ └── component_tests/ # CTest label: "component"
+```
+
+Each `src//` builds a static library; `runtime_core` links them together
+and the `NeuronIDE` executable links `runtime_core`.
-Once built, the main executable will be available inside the `build` directory.
+## 8. Build, test, and tooling
-### 3. Running Tests
-This project uses Google Test (GTest) and CTest for testing. The tests are categorized by labels so you can run them selectively.
+All commands are run from the `Neuron-IDE-runtime/` directory.
+
+### Dependencies (Linux)
-**To run all tests:**
```bash
-cd build
-ctest --output-on-failure
+sudo apt update
+sudo apt install cmake clang-format clang-tidy libsdl2-dev protobuf-compiler gcovr
+# GTest, LSL and concurrentqueue are fetched automatically by CMake.
```
-**To run only unit tests:**
+### Configure & build
+
```bash
-cd build
-ctest -L unit --output-on-failure
+cmake -B build
+cmake --build build
+./build/src/NeuronIDE # run the (currently stub) executable
```
-**To run only component tests:**
+### Tests
+
```bash
cd build
+ctest --output-on-failure # all tests
+ctest -L unit --output-on-failure # unit tests only
ctest -L component --output-on-failure
```
-### 4. Code Formatting
+> Note: `LSLReader` unit tests open a local LSL stream and exercise a real
+> outlet→inlet round-trip over loopback; they need loopback multicast to be
+> available.
+
+### Formatting & static analysis
+
+- Style: `.clang-format` (Google base, 4-space indent, 100 col, aligned
+ declarations/assignments). Checks: `.clang-tidy`
+ (`bugprone-*`, `cppcoreguidelines-*`, `performance-*`, `readability-*`).
+- Auto-format every tracked source/header:
+ ```bash
+ cmake --build build --target format
+ ```
+
+### Coverage
+
+```bash
+cmake -B build -DNEURON_IDE_ENABLE_COVERAGE=ON
+cmake --build build
+cmake --build build --target coverage # runs tests + gcovr
+xdg-open build/coverage/index.html
+```
+
+### Working with protobuf fixtures
-This project includes a custom CMake target to automatically format all C++ source files using `clang-format`.
+Author a `.pbtxt` (see `protoFiles/tests/test_scene.pbtxt`), then encode it:
-**To format your code:**
```bash
-# Assuming you have already configured the build directory
-cmake --build build --target format
+protoc --encode=NeuronIDE.Scene protoFiles/neuronide.proto \
+ < protoFiles/tests/test_scene.pbtxt > protoFiles/tests/test_scene.pb
```
-This will run `clang-format -i` over all trackable source and header files, applying the style configuration specified in the project.
+## 9. Contribution conventions
-### 5. Working with protobuf files
+- **Branches:** `/`, e.g. `feat/setup-project`.
+- **Commits:** `(optional scope): description`, e.g.
+ `feat(parser): create Parser class`.
+- **Types:** `feat`, `fix`, `style` (clang config), `test`, `ci` (`.github`).
-In case you want to create an example .pb file for testing or some other purpose, you have to first create a .pbtxt file (check out protoFiles/tests/test_scene.pbtxt for reference) which you then compile with this command:
+## 10. Roadmap (next steps)
-```bash
-protoc --encode=NeuronIDE.Scene protoFiles/neuronide.proto < protoFiles/tests/test_scene.pbtxt > protoFiles/tests/test_scene.pb
-```
\ No newline at end of file
+1. Implement `Runtime` orchestration: `Parser → Scene`, then run `Renderer`,
+ `LSLReader`, and `DataWriter` concurrently and shut them down cleanly.
+2. Implement the remaining components: `SpriteRenderer`, `TextRenderer`,
+ `ScriptComponent` (pybind11), each self-registering with `ComponentRegistry`.
+3. Validate `LSLReader` end-to-end against a real EEG headset.
diff --git a/include/Runtime.hpp b/include/Runtime.hpp
index e3ee994..838b4a6 100644
--- a/include/Runtime.hpp
+++ b/include/Runtime.hpp
@@ -7,6 +7,11 @@ class Runtime {
public:
Runtime() = default;
~Runtime() = default;
+
+ Runtime(const Runtime&) = delete;
+ Runtime& operator=(const Runtime&) = delete;
+ Runtime(Runtime&&) = delete;
+ Runtime& operator=(Runtime&&) = delete;
static void start();
};
diff --git a/include/data_structures/Context.hpp b/include/data_structures/Context.hpp
index 40f90d0..74c5a04 100644
--- a/include/data_structures/Context.hpp
+++ b/include/data_structures/Context.hpp
@@ -6,7 +6,7 @@
struct Context {
double timestamp = 0.0;
- std::vector& markers;
+ std::vector* markers = nullptr;
};
#endif // CONTEXT_HPP
\ No newline at end of file
diff --git a/include/datawriter/CSVFormatStrategy.hpp b/include/datawriter/CSVFormatStrategy.hpp
index 6043c49..fb76dbe 100644
--- a/include/datawriter/CSVFormatStrategy.hpp
+++ b/include/datawriter/CSVFormatStrategy.hpp
@@ -10,6 +10,11 @@ class CSVFormatStrategy : public IDataFormatStrategy {
CSVFormatStrategy() = default;
~CSVFormatStrategy() override;
+ CSVFormatStrategy(const CSVFormatStrategy&) = delete;
+ CSVFormatStrategy& operator=(const CSVFormatStrategy&) = delete;
+ CSVFormatStrategy(CSVFormatStrategy&&) = delete;
+ CSVFormatStrategy& operator=(CSVFormatStrategy&&) = delete;
+
void open(const std::string& filepath) override;
void close() override;
diff --git a/include/datawriter/DataWriter.hpp b/include/datawriter/DataWriter.hpp
index 59782c2..ab5a2a1 100644
--- a/include/datawriter/DataWriter.hpp
+++ b/include/datawriter/DataWriter.hpp
@@ -5,8 +5,8 @@
#include
-class EEGData;
-class Marker;
+struct EEGData;
+struct Marker;
#include
#include
diff --git a/include/datawriter/IDataFormatStrategy.hpp b/include/datawriter/IDataFormatStrategy.hpp
index cbed789..c178e43 100644
--- a/include/datawriter/IDataFormatStrategy.hpp
+++ b/include/datawriter/IDataFormatStrategy.hpp
@@ -3,7 +3,7 @@
#include
-class EEGData;
+struct EEGData;
struct Marker;
class IDataFormatStrategy {
diff --git a/include/parser/Parser.hpp b/include/parser/Parser.hpp
index 26bf0dd..09c2e1f 100644
--- a/include/parser/Parser.hpp
+++ b/include/parser/Parser.hpp
@@ -18,7 +18,7 @@ class Parser {
public:
Parser() = default;
- std::shared_ptr parse(const std::string& filePath);
+ static std::shared_ptr parse(const std::string& filePath);
private:
static std::shared_ptr buildSceneObject(const NeuronIDE::SceneObject& protoObj);
diff --git a/include/renderer/Renderer.hpp b/include/renderer/Renderer.hpp
index 267c810..3f551b0 100644
--- a/include/renderer/Renderer.hpp
+++ b/include/renderer/Renderer.hpp
@@ -17,6 +17,11 @@ class Renderer {
std::shared_ptr> markerQueue);
~Renderer() = default;
+ Renderer(const Renderer&) = delete;
+ Renderer& operator=(const Renderer&) = delete;
+ Renderer(Renderer&&) = delete;
+ Renderer& operator=(Renderer&&) = delete;
+
void render(const std::stop_token& stoken);
private:
diff --git a/include/scene/components/BlinkComponent.hpp b/include/scene/components/BlinkComponent.hpp
index b4814e9..dd68a4a 100644
--- a/include/scene/components/BlinkComponent.hpp
+++ b/include/scene/components/BlinkComponent.hpp
@@ -11,7 +11,7 @@ class Component;
class BlinkComponent : public Component {
public:
- BlinkComponent(std::shared_ptr owner, double freq)
+ BlinkComponent(const std::shared_ptr& owner, double freq)
: Component(owner), blinkFrequencyHz(freq) {}
void setFrequency(double freq);
diff --git a/include/scene/components/Component.hpp b/include/scene/components/Component.hpp
index 4000717..4e8d467 100644
--- a/include/scene/components/Component.hpp
+++ b/include/scene/components/Component.hpp
@@ -10,7 +10,7 @@ struct Context;
class Component {
public:
Component() = delete;
- Component(std::shared_ptr owner) : owner(owner) {}
+ Component(const std::shared_ptr& owner) : owner(owner) {}
virtual ~Component() = default;
Component(const Component&) = delete;
@@ -22,6 +22,7 @@ class Component {
virtual void render(SDL_Renderer* renderer) = 0;
protected:
+ // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
std::weak_ptr owner;
};
diff --git a/include/scene/components/ComponentRegistry.hpp b/include/scene/components/ComponentRegistry.hpp
index 668dc4a..1ef388e 100644
--- a/include/scene/components/ComponentRegistry.hpp
+++ b/include/scene/components/ComponentRegistry.hpp
@@ -21,6 +21,7 @@ class ComponentRegistry {
ComponentRegistry(ComponentRegistry&&) = delete;
ComponentRegistry& operator=(ComponentRegistry&&) = delete;
+ ~ComponentRegistry() = default;
static ComponentRegistry& instance() {
static ComponentRegistry instance;
@@ -39,8 +40,10 @@ class ComponentRegistry {
};
#define COMPONENT_REGISTRATION_CONCAT_IMPL(x, y) x##y
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define COMPONENT_REGISTRATION_CONCAT(x, y) COMPONENT_REGISTRATION_CONCAT_IMPL(x, y)
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define REGISTER_COMPONENT(typeId, creatorFunc) \
namespace { \
struct COMPONENT_REGISTRATION_CONCAT(ComponentRegistrar_, __LINE__) { \
@@ -48,7 +51,7 @@ class ComponentRegistry {
ComponentRegistry::instance().registerCreator(static_cast(typeId), creatorFunc); \
} \
}; \
- static COMPONENT_REGISTRATION_CONCAT(ComponentRegistrar_, __LINE__) \
+ static const COMPONENT_REGISTRATION_CONCAT(ComponentRegistrar_, __LINE__) \
COMPONENT_REGISTRATION_CONCAT(global_registrar_, __LINE__); \
}
diff --git a/src/datawriter/CSVFormatStrategy.cpp b/src/datawriter/CSVFormatStrategy.cpp
index e70e4a6..69e9be1 100644
--- a/src/datawriter/CSVFormatStrategy.cpp
+++ b/src/datawriter/CSVFormatStrategy.cpp
@@ -3,7 +3,7 @@
#include
#include
-CSVFormatStrategy::~CSVFormatStrategy() { close(); }
+CSVFormatStrategy::~CSVFormatStrategy() { CSVFormatStrategy::close(); }
void CSVFormatStrategy::open(const std::string& filepath) {
outputFile.open(filepath, std::ios::out | std::ios::trunc);
diff --git a/src/datawriter/DataWriter.cpp b/src/datawriter/DataWriter.cpp
index 5175cb5..cfd0880 100644
--- a/src/datawriter/DataWriter.cpp
+++ b/src/datawriter/DataWriter.cpp
@@ -9,7 +9,7 @@
constexpr auto kWriteLoopSleep = std::chrono::milliseconds(10);
template
-bool drainQueue(const std::shared_ptr& queue, WriteFn&& writeFn) {
+bool drainQueue(const std::shared_ptr& queue, WriteFn writeFn) {
bool wroteData = false;
if (queue) {
diff --git a/src/main.cpp b/src/main.cpp
index 1b85885..824bdc3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,8 @@
#include
int main(int argc, char* argv[]) {
+ (void)argc;
+ (void)argv;
Runtime::start();
return 0;
}
\ No newline at end of file
diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp
index 235dba1..f11a383 100644
--- a/src/parser/Parser.cpp
+++ b/src/parser/Parser.cpp
@@ -46,7 +46,7 @@ std::shared_ptr Parser::buildSceneObject(const NeuronIDE::SceneObje
for (const auto& protoComp : protoObj.components()) {
int typeId = static_cast(protoComp.component_type_case());
- if (seenComponentTypes.find(typeId) != seenComponentTypes.end()) {
+ if (seenComponentTypes.contains(typeId)) {
throw std::runtime_error("Parser: duplicate component type in object '" +
protoObj.name() + "'.");
}
diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp
index f57b239..142e969 100644
--- a/src/renderer/Renderer.cpp
+++ b/src/renderer/Renderer.cpp
@@ -6,7 +6,7 @@
#include "scene/Scene.hpp"
void Renderer::SDLWindowDeleter::operator()(SDL_Window* window) const {
- if (window) {
+ if (window != nullptr) {
SDL_DestroyWindow(window);
}
}
@@ -28,7 +28,7 @@ void Renderer::render(const std::stop_token& stoken) {
lastTime = currentTime;
currentFrameMarkers.clear();
- Context ctx{deltaTime, currentFrameMarkers};
+ Context ctx{deltaTime, ¤tFrameMarkers};
SDL_Event event;
while (SDL_PollEvent(&event) == 1) {
diff --git a/src/scene/components/BlinkComponent.cpp b/src/scene/components/BlinkComponent.cpp
index dbd9503..7911f0f 100644
--- a/src/scene/components/BlinkComponent.cpp
+++ b/src/scene/components/BlinkComponent.cpp
@@ -11,10 +11,12 @@ std::unique_ptr BlinkComponent::createBlinker(
}
void BlinkComponent::update(const Context& context) {
+ (void)context;
// TODO: implement blinking logic based on blinkFrequencyHz and context.timestamp
}
void BlinkComponent::render(SDL_Renderer* renderer) {
+ (void)renderer;
// This component does not render anything itself, it only controls visibility of the owner
// object.
}
diff --git a/src/scene/components/ComponentRegistry.cpp b/src/scene/components/ComponentRegistry.cpp
index b3419fc..f8c67c8 100644
--- a/src/scene/components/ComponentRegistry.cpp
+++ b/src/scene/components/ComponentRegistry.cpp
@@ -7,7 +7,7 @@
#include "scene/components/Component.hpp"
void ComponentRegistry::registerCreator(int typeId, ComponentCreatorFunc creator) {
- if (creators.find(typeId) != creators.end()) {
+ if (creators.contains(typeId)) {
throw std::runtime_error("Creator for this typeId is already registered.");
}
creators[typeId] = std::move(creator);
@@ -19,9 +19,9 @@ std::unique_ptr ComponentRegistry::build(const NeuronIDE::Component&
int typeId = static_cast(activeCase);
- auto it = creators.find(typeId);
- if (it != creators.end()) {
- return it->second(protoComp, owner);
+ auto iter = creators.find(typeId);
+ if (iter != creators.end()) {
+ return iter->second(protoComp, owner);
}
return nullptr;
diff --git a/tests/unit_tests/RendererTest.cpp b/tests/unit_tests/RendererTest.cpp
index fa00645..88da249 100644
--- a/tests/unit_tests/RendererTest.cpp
+++ b/tests/unit_tests/RendererTest.cpp
@@ -19,10 +19,11 @@ constexpr uint32_t kDummySurfaceFlags = 0;
class CustomComponent : public Component {
public:
- CustomComponent(std::shared_ptr owner, std::shared_ptr> updates,
- std::shared_ptr> renders,
- std::shared_ptr stopSource)
- : Component(std::move(owner)),
+ CustomComponent(const std::shared_ptr& owner,
+ std::shared_ptr> updates,
+ std::shared_ptr> renders,
+ std::shared_ptr stopSource)
+ : Component(owner),
updates(std::move(updates)),
renders(std::move(renders)),
stopSource(std::move(stopSource)) {}
@@ -47,12 +48,14 @@ class CustomComponent : public Component {
class MarkerComponent : public Component {
public:
- MarkerComponent(std::shared_ptr owner,
- std::shared_ptr stopSource)
- : Component(std::move(owner)), stopSource(std::move(stopSource)) {}
+ MarkerComponent(const std::shared_ptr& owner,
+ std::shared_ptr stopSource)
+ : Component(owner), stopSource(std::move(stopSource)) {}
void update(const Context& context) override {
- context.markers.push_back("test_marker");
+ if (context.markers != nullptr) {
+ context.markers->push_back("test_marker");
+ }
stopSource->request_stop();
}
@@ -81,14 +84,15 @@ TEST(RendererTest, RenderLoop_WhenComponentAdded_CallsUpdateExactlyOnceBeforeSto
SDL_CreateRGBSurfaceWithFormat(kDummySurfaceFlags, kDummySurfaceWidth, kDummySurfaceHeight,
kDummySurfaceDepth, SDL_PIXELFORMAT_RGBA32);
SDL_Renderer* sdlRenderer = SDL_CreateSoftwareRenderer(surface);
- auto sharedRenderer = std::shared_ptr(sdlRenderer, [surface](SDL_Renderer* r) {
- if (r) {
- SDL_DestroyRenderer(r);
- }
- if (surface) {
- SDL_FreeSurface(surface);
- }
- });
+ auto sharedRenderer =
+ std::shared_ptr(sdlRenderer, [surface](SDL_Renderer* renderer) {
+ if (renderer) {
+ SDL_DestroyRenderer(renderer);
+ }
+ if (surface) {
+ SDL_FreeSurface(surface);
+ }
+ });
auto markerQueue = std::make_shared>();
@@ -115,26 +119,27 @@ TEST(RendererTest, RenderLoop_QueuesMarkersFromComponents) {
SDL_CreateRGBSurfaceWithFormat(kDummySurfaceFlags, kDummySurfaceWidth, kDummySurfaceHeight,
kDummySurfaceDepth, SDL_PIXELFORMAT_RGBA32);
SDL_Renderer* sdlRenderer = SDL_CreateSoftwareRenderer(surface);
- auto sharedRenderer = std::shared_ptr(sdlRenderer, [surface](SDL_Renderer* r) {
- if (r) {
- SDL_DestroyRenderer(r);
- }
- if (surface) {
- SDL_FreeSurface(surface);
- }
- });
+ auto sharedRenderer =
+ std::shared_ptr(sdlRenderer, [surface](SDL_Renderer* renderer) {
+ if (renderer) {
+ SDL_DestroyRenderer(renderer);
+ }
+ if (surface) {
+ SDL_FreeSurface(surface);
+ }
+ });
auto markerQueue = std::make_shared>();
Renderer renderer(scene, sharedRenderer, markerQueue);
renderer.render(stop_source->get_token());
- Marker m;
- bool dequeued = markerQueue->try_dequeue(m);
+ Marker marker;
+ bool dequeued = markerQueue->try_dequeue(marker);
EXPECT_TRUE(dequeued);
if (dequeued) {
- EXPECT_EQ(m.eventName, "test_marker");
- EXPECT_FALSE(markerQueue->try_dequeue(m));
+ EXPECT_EQ(marker.eventName, "test_marker");
+ EXPECT_FALSE(markerQueue->try_dequeue(marker));
}
SDL_Quit();