From b49b659fc957aee7c27fefdc8fda71e4ded69cd7 Mon Sep 17 00:00:00 2001 From: Michal Date: Fri, 19 Jun 2026 22:33:04 +0200 Subject: [PATCH 1/3] refactor: resolve clang-tidy warnings --- .clang-tidy | 2 +- include/Runtime.hpp | 5 +++ include/data_structures/Context.hpp | 2 +- include/datawriter/CSVFormatStrategy.hpp | 5 +++ include/datawriter/DataWriter.hpp | 4 +-- include/datawriter/IDataFormatStrategy.hpp | 2 +- include/parser/Parser.hpp | 2 +- include/renderer/Renderer.hpp | 5 +++ include/scene/components/BlinkComponent.hpp | 2 +- include/scene/components/Component.hpp | 3 +- .../scene/components/ComponentRegistry.hpp | 5 ++- src/datawriter/CSVFormatStrategy.cpp | 2 +- src/datawriter/DataWriter.cpp | 2 +- src/main.cpp | 2 ++ src/parser/Parser.cpp | 2 +- src/renderer/Renderer.cpp | 4 +-- src/scene/components/BlinkComponent.cpp | 2 ++ src/scene/components/ComponentRegistry.cpp | 8 ++--- tests/unit_tests/RendererTest.cpp | 32 ++++++++++--------- 19 files changed, 58 insertions(+), 33 deletions(-) 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/include/Runtime.hpp b/include/Runtime.hpp index e3ee994..bb01728 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..53b7d9b 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..1a58bbb 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..7044cd5 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..178b5e1 100644 --- a/tests/unit_tests/RendererTest.cpp +++ b/tests/unit_tests/RendererTest.cpp @@ -19,10 +19,10 @@ constexpr uint32_t kDummySurfaceFlags = 0; class CustomComponent : public Component { public: - CustomComponent(std::shared_ptr owner, std::shared_ptr> updates, + CustomComponent(const std::shared_ptr& owner, std::shared_ptr> updates, std::shared_ptr> renders, std::shared_ptr stopSource) - : Component(std::move(owner)), + : Component(owner), updates(std::move(updates)), renders(std::move(renders)), stopSource(std::move(stopSource)) {} @@ -47,12 +47,14 @@ class CustomComponent : public Component { class MarkerComponent : public Component { public: - MarkerComponent(std::shared_ptr owner, + MarkerComponent(const std::shared_ptr& owner, std::shared_ptr stopSource) - : Component(std::move(owner)), stopSource(std::move(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,9 +83,9 @@ 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); + auto sharedRenderer = std::shared_ptr(sdlRenderer, [surface](SDL_Renderer* renderer) { + if (renderer) { + SDL_DestroyRenderer(renderer); } if (surface) { SDL_FreeSurface(surface); @@ -115,9 +117,9 @@ 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); + auto sharedRenderer = std::shared_ptr(sdlRenderer, [surface](SDL_Renderer* renderer) { + if (renderer) { + SDL_DestroyRenderer(renderer); } if (surface) { SDL_FreeSurface(surface); @@ -129,12 +131,12 @@ TEST(RendererTest, RenderLoop_QueuesMarkersFromComponents) { 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(); From 654c2af4cb4f8b262e714ed1fecb30cad1364c8b Mon Sep 17 00:00:00 2001 From: Michal Date: Fri, 19 Jun 2026 22:36:40 +0200 Subject: [PATCH 2/3] fix: fix code formatting --- include/Runtime.hpp | 6 ++-- include/data_structures/Context.hpp | 2 +- include/datawriter/CSVFormatStrategy.hpp | 6 ++-- include/renderer/Renderer.hpp | 6 ++-- tests/unit_tests/RendererTest.cpp | 43 +++++++++++++----------- 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/include/Runtime.hpp b/include/Runtime.hpp index bb01728..838b4a6 100644 --- a/include/Runtime.hpp +++ b/include/Runtime.hpp @@ -8,10 +8,10 @@ class Runtime { Runtime() = default; ~Runtime() = default; - Runtime(const Runtime&) = delete; + Runtime(const Runtime&) = delete; Runtime& operator=(const Runtime&) = delete; - Runtime(Runtime&&) = delete; - Runtime& operator=(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 53b7d9b..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 = nullptr; + 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 1a58bbb..fb76dbe 100644 --- a/include/datawriter/CSVFormatStrategy.hpp +++ b/include/datawriter/CSVFormatStrategy.hpp @@ -10,10 +10,10 @@ class CSVFormatStrategy : public IDataFormatStrategy { CSVFormatStrategy() = default; ~CSVFormatStrategy() override; - CSVFormatStrategy(const CSVFormatStrategy&) = delete; + CSVFormatStrategy(const CSVFormatStrategy&) = delete; CSVFormatStrategy& operator=(const CSVFormatStrategy&) = delete; - CSVFormatStrategy(CSVFormatStrategy&&) = delete; - CSVFormatStrategy& operator=(CSVFormatStrategy&&) = delete; + CSVFormatStrategy(CSVFormatStrategy&&) = delete; + CSVFormatStrategy& operator=(CSVFormatStrategy&&) = delete; void open(const std::string& filepath) override; void close() override; diff --git a/include/renderer/Renderer.hpp b/include/renderer/Renderer.hpp index 7044cd5..3f551b0 100644 --- a/include/renderer/Renderer.hpp +++ b/include/renderer/Renderer.hpp @@ -17,10 +17,10 @@ class Renderer { std::shared_ptr> markerQueue); ~Renderer() = default; - Renderer(const Renderer&) = delete; + Renderer(const Renderer&) = delete; Renderer& operator=(const Renderer&) = delete; - Renderer(Renderer&&) = delete; - Renderer& operator=(Renderer&&) = delete; + Renderer(Renderer&&) = delete; + Renderer& operator=(Renderer&&) = delete; void render(const std::stop_token& stoken); diff --git a/tests/unit_tests/RendererTest.cpp b/tests/unit_tests/RendererTest.cpp index 178b5e1..88da249 100644 --- a/tests/unit_tests/RendererTest.cpp +++ b/tests/unit_tests/RendererTest.cpp @@ -19,9 +19,10 @@ constexpr uint32_t kDummySurfaceFlags = 0; class CustomComponent : public Component { public: - CustomComponent(const std::shared_ptr& owner, std::shared_ptr> updates, - std::shared_ptr> renders, - std::shared_ptr stopSource) + 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)), @@ -48,7 +49,7 @@ class CustomComponent : public Component { class MarkerComponent : public Component { public: MarkerComponent(const std::shared_ptr& owner, - std::shared_ptr stopSource) + std::shared_ptr stopSource) : Component(owner), stopSource(std::move(stopSource)) {} void update(const Context& context) override { @@ -83,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* renderer) { - if (renderer) { - SDL_DestroyRenderer(renderer); - } - 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>(); @@ -117,14 +119,15 @@ 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* renderer) { - if (renderer) { - SDL_DestroyRenderer(renderer); - } - 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>(); From e479f6787b7ba115ea9b6590d7a803725618fc30 Mon Sep 17 00:00:00 2001 From: Michal Date: Fri, 19 Jun 2026 22:52:21 +0200 Subject: [PATCH 3/3] docs: update README --- README.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1c3dc88..dd43762 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,16 @@ ## Setup and Build Instructions -This project uses CMake and requires a modern C++17 compiler. +This project uses CMake (minimum version 3.25) and requires a modern C++20 compiler. ### 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. +This project uses GTest, LSL, SDL2, Clang-format, Clang-tidy-20, protobuf-compiler, gcovr and cmake, but you don't need to install GTest and LSL because cmake will install it for you. Below is minimal linux setup. ```bash git clone git@github.com:KN-Neuron/Neuron-IDE-runtime.git sudo apt update -sudo apt install cmake clang-format clang-tidy libsdl2-dev protobuf-compiler +sudo apt install cmake clang-format clang-tidy-20 libsdl2-dev protobuf-compiler gcovr ``` ### 2. Build the Project @@ -66,4 +66,34 @@ In case you want to create an example .pb file for testing or some other purpose ```bash protoc --encode=NeuronIDE.Scene protoFiles/neuronide.proto < protoFiles/tests/test_scene.pbtxt > protoFiles/tests/test_scene.pb +``` + +### 6. Code Coverage + +This project supports generating code coverage reports using `gcovr` to ensure that our tests thoroughly exercise the codebase. + +**To build with coverage enabled:** +You need to pass the `NEURON_IDE_ENABLE_COVERAGE` flag during the CMake configuration step. + +```bash +# Generate the build system with coverage instrumentation enabled +cmake -B build -DNEURON_IDE_ENABLE_COVERAGE=ON + +# Compile the project +cmake --build build +``` + +**To run coverage and generate reports:** +Once built with the coverage flag, you can run the `coverage` target. This will automatically execute the test suite and then invoke `gcovr` to generate both a terminal summary and detailed HTML reports. + +```bash +cmake --build build --target coverage +``` + +**To check the coverage reports:** +After running the coverage target, the generated HTML files will be located in the `build/coverage` directory. You can open `index.html` in your favorite web browser to explore detailed line-by-line coverage for each file. + +```bash +# Open the main coverage report in your default web browser +xdg-open build/coverage/index.html ``` \ No newline at end of file