Skip to content

Increase test coverage with 71 new tests and in-memory helpers#171

Merged
fernandotonon merged 3 commits intomasterfrom
increase-test-coverage-80
Mar 4, 2026
Merged

Increase test coverage with 71 new tests and in-memory helpers#171
fernandotonon merged 3 commits intomasterfrom
increase-test-coverage-80

Conversation

@fernandotonon
Copy link
Owner

@fernandotonon fernandotonon commented Mar 4, 2026

Summary

  • Add 3 reusable in-memory mesh creation helpers to TestHelpers.h (createInMemoryTriangleMesh, createInMemorySkeletonMesh, createAnimatedTestEntity) enabling tests to create entities without loading files from disk
  • Add 71 new tests across 7 test files covering AnimationControlWidget (23), MCPServer (15), MeshImporterExporter (8), TransformOperator (7), PrimitivesWidget (10), and Manager (6)
  • Fix bug in about_test.cpp and main_test.cpp where local QApplication creation destroyed the global one from test_main.cpp, breaking all subsequent test suites

Test plan

  • All 830 tests pass locally (261 PASSED, 569 SKIPPED on macOS due to no GL context)
  • New tests correctly skip on macOS via tryInitOgre()/canLoadMeshFiles() guards
  • New tests will run on Linux CI with Xvfb (GL context available)
  • Verify CI build passes on all platforms (Windows, macOS, Linux)
  • Verify SonarCloud coverage increases toward 80%

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded test coverage for primitives, in-memory meshes, skeletal animation and keyframe workflows, and scene/node management.
    • Added mesh import/export tests covering multiple formats and multi-file export scenarios.
    • Extended UI and headless tests for transform, selection, animation controls, and server error-edge cases; simplified test setup to rely on a shared application instance.

Add reusable in-memory mesh creation helpers (triangle, skeleton, animated entity)
to TestHelpers.h and expand test coverage across 7 test files:
- AnimationControlWidget: 23 new tests (was 3) for animation tree, keyframes, playback
- MCPServer: 15 new tests for tool success paths with in-memory entities
- MeshImporterExporter: 8 new tests for in-memory export (OBJ, STL, glTF, Ogre)
- TransformOperator: 7 new tests for translate/rotate/scale with entities
- PrimitivesWidget: 10 new tests verifying entity creation for all primitives
- Manager: 6 new tests for in-memory entity lifecycle and scene hierarchy
- Fix about_test/main_test creating local QApplication that destroyed the global one

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

📝 Walkthrough

Walkthrough

Adds a broad set of headless/in-memory tests and helpers: in-memory mesh/skeleton/entity builders, extensive tests for MCPServer, Manager, mesh import/export, primitives, transforms, and animation widget flows; replaces local QApplication mocks with runtime QCoreApplication existence checks.

Changes

Cohort / File(s) Summary
Test Utilities
src/TestHelpers.h
Added three static inline helpers: createInMemoryTriangleMesh(), createInMemorySkeletonMesh(), and createAnimatedTestEntity() to build meshes, skeletons, and animated entities in-memory (hardware buffers require GL context).
MCP Server Tests
src/MCPServer_test.cpp
Large suite of new tests covering in-memory entities, primitives, materials, transform/mesh flows, skeletal/animation edge cases, export/import scenarios, HTTP endpoints, and server start/stop/state edge cases.
Manager Tests
src/Manager_test.cpp
Headless tests for scene node/entity lifecycle, in-memory skeleton/entity creation/destruction, parenting, selection behavior, signals (SceneNodeCreated/Destroyed, EntityCreated), and scene/viewport/state checks.
Mesh Import/Export Tests
src/MeshImporterExporter_test.cpp
In-memory mesh export tests for OBJ/STL/glTF2/GLB/Ogre mesh/Ogre XML, multi-file export test, and supported formats string verification (guarded by headless mesh capability).
Primitive Widget Tests
src/PrimitivesWidget_test.cpp
Added per-primitive creation/verification tests (cube, sphere, torus, cone, tube, capsule, ico-sphere, rounded box, spring), mesh vertex-data checks, and a multi-primitive create/destroy scenario.
Transform Operator Tests
src/TransformOperator_test.cpp
Tests exercising TransformOperator on in-memory entities: translate/scale/orient operations, multi-selection, removal, selection-state edge cases, and no-crash behavior in headless/no-render contexts.
Animation Control Widget Tests
src/animationcontrolwidget_test.cpp
Added setupAnimatedEntity() helper and numerous tests for animation tree population, bone lists, slider/spinbox ranges, keyframe add/delete/prev/next, playback timer behavior, multiple animations, and UI synchronization with in-memory animated entities.
Application Init Tests
src/about_test.cpp, src/main_test.cpp
Removed local QApplication mocks and mock-based tests; replaced with checks that QCoreApplication::instance() exists and simplified related test flows. Deleted MockQApplication/MockMainWindow and associated test.
Misc / Test Integrations
src/*.cpp (multiple test files)
Many additional headless/in-memory test cases added across test files; no changes to public/exported API signatures.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I stitched small meshes in a cozy nest,
Hopped through bones and keyframes with zest,
Nodes and widgets, tests that gleam,
A headless night of developer dream,
The rabbit signs and snuggles the rest.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding 71 new tests and in-memory helpers to improve test coverage.
Description check ✅ Passed The description includes a comprehensive summary and technical details section covering the helpers, test counts, bug fixes, and test plan with verification status.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch increase-test-coverage-80

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 65ed9641aa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

EXPECT_EQ(entityItem->childCount(), 2);

// Switch to second animation
tree->setCurrentItem(entityItem->child(1));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Select animation item by name instead of child index

MultipleAnimationsInTree assumes entityItem->child(1) is the 0.5s animation, but AnimationControlWidget::updateAnimationTree() builds children by iterating getAnimationStates(), whose iteration order is not guaranteed to match creation order. In environments where the order differs (e.g., different STL/OGRE implementations), this test selects the wrong animation and fails despite correct behavior. Selecting the child by animation text (e.g., anim: Run) avoids this ordering-dependent failure.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
src/main_test.cpp (2)

17-22: Remove unused MockMainWindow class.

The MockMainWindow class is no longer used after removing the QApplicationAndMainWindowMock test. Consider removing this dead code to keep the test file clean.

🧹 Proposed removal
-// Mock class for MainWindow
-class MockMainWindow : public MainWindow
-{
-public:
-    MOCK_METHOD(void, show, ());
-};
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main_test.cpp` around lines 17 - 22, Remove the dead MockMainWindow class
definition (class MockMainWindow : public MainWindow { ... }) from the test
file: locate the MockMainWindow declaration and delete it, and then run/build
tests to ensure there are no remaining references to MockMainWindow or relies-on
symbols in other tests (if any, replace or update them accordingly).

50-50: Disabled test still creates a local QApplication.

The DISABLED_ImportMeshs test creates its own QApplication at line 50, which would cause the same destruction bug if this test is ever re-enabled. Consider refactoring this test to use the global QApplication from test_main.cpp when fixing the underlying Ogre issues.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main_test.cpp` at line 50, The DISABLED_ImportMeshs test currently
constructs a local QApplication (QApplication app(argc, mutable_argv)) which
will reproduce the destruction bug if re-enabled; change the test to use the
global QApplication provided by test_main.cpp instead of creating its own
instance—e.g., obtain the existing application via QApplication::instance() or
reference the global app object exported by test_main.cpp and remove the local
QApplication construction in the DISABLED_ImportMeshs test so the test uses the
shared application lifetime while you address the underlying Ogre destruction
issue.
src/animationcontrolwidget_test.cpp (1)

526-607: Consider extracting the multi-animation entity creation to a helper.

This test duplicates significant mesh/skeleton creation code that's similar to createAnimatedTestEntity() in TestHelpers.h. A parameterized helper or a variant that accepts multiple animations would reduce duplication and improve maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/animationcontrolwidget_test.cpp` around lines 526 - 607, The test
duplicates mesh/skeleton/entity setup that matches createAnimatedTestEntity() in
TestHelpers.h; extract the shared setup into a new helper (e.g.,
createAnimatedTestEntityWithAnimations or extend createAnimatedTestEntity) that
accepts a list of animation descriptors (name and length/keyframe info), move
skeleton/mesh/bone/vertex/index creation there, and return the created scene
node/entity; then simplify AnimationControlWidgetTest::MultipleAnimationsInTree
to call the new helper, attach/select the returned node, call
widget.updateAnimationTree(), and keep the existing assertions (tree lookup,
selecting child, checking horizontalSlider maximum). Ensure you reference and
reuse createAnimatedTestEntity()/createAnimatedTestEntityWithAnimations and
AnimationControlWidget::updateAnimationTree when updating the test.
src/TestHelpers.h (1)

312-405: Consider adding error handling for entity creation failure.

The function creates multiple Ogre resources (skeleton, animation, mesh) but if sceneMgr->createEntity() fails, these resources are leaked. Since callers check for nullptr return, a failure path should clean up.

💡 Proposed error handling
     // Create entity via Manager
     auto* sceneMgr = Manager::getSingleton()->getSceneMgr();
     auto* node = Manager::getSingleton()->addSceneNode(name.c_str());
-    auto* entity = sceneMgr->createEntity(name, mesh);
-    node->attachObject(entity);
+    Ogre::Entity* entity = nullptr;
+    try {
+        entity = sceneMgr->createEntity(name, mesh);
+        node->attachObject(entity);
+    } catch (...) {
+        Manager::getSingleton()->destroySceneNode(node);
+        Ogre::MeshManager::getSingleton().remove(mesh);
+        Ogre::SkeletonManager::getSingleton().remove(skel);
+        return nullptr;
+    }
 
     return entity;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/TestHelpers.h` around lines 312 - 405, If sceneMgr->createEntity(...)
fails in createAnimatedTestEntity, clean up all created resources before
returning nullptr: destroy/remove the Mesh (mesh), the Skeleton (skel), free any
HardwareBuffers (vbuf, ibuf) if you allocated them, and remove the SceneNode
(node) you created via Manager::getSingleton()->addSceneNode; then return
nullptr. Implement a failure check immediately after calling
sceneMgr->createEntity(name, mesh) that performs the above cleanup using the
corresponding Ogre managers (e.g.,
Ogre::MeshManager::getSingleton().remove(mesh->getName()),
Ogre::SkeletonManager::getSingleton().remove(skel->getName()),
HardwareBufferManager cleanup, and Manager::getSingleton()->removeSceneNode or
node destruction), ensuring no leaks when createEntity returns nullptr.
src/MCPServer_test.cpp (1)

2060-2084: Custom-parameter tests don’t verify parameter effects yet.

These tests currently pass if creation succeeds, even if radius, height, segments, or sizeX/Y/Z are ignored. Consider asserting resulting geometry/state (e.g., bounds, vertex counts, or queried primitive properties) so coverage matches the test intent.

Also applies to: 2170-2182

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Manager_test.cpp`:
- Around line 582-597: The test unsafely assumes objects from
Manager::getEntities() are Ogre::Entity*; before casting/use, check each
object's getMovableType() equals "Entity" (or use dynamic_cast) to avoid
ManualObject crashes—e.g., after retrieving the last movable from
Manager::getEntities(), verify movable->getMovableType() == "Entity" and only
then static_cast/Ogre::Entity* entity = static_cast<Ogre::Entity*>(movable) and
call Manager::hasAnimationName(entity,...); alternatively update
Manager::getEntities() to filter by getMovableType() == "Entity" so callers
always receive only entities.

In `@src/MCPServer_test.cpp`:
- Around line 2323-2330: The test hardcodes "/tmp/…" which fails on Windows;
replace the hardcoded path usage around args and cleanup with a cross-platform
temp path (e.g., use QDir::tempPath() or better QTemporaryDir) when preparing
args["path"] passed to server->callTool("export_mesh", args) and when calling
QFile::remove to delete the exported files; construct the full file names using
the chosen temp dir (or use QTemporaryDir to auto-cleanup) so the test runs on
Windows/macOS/Linux without `#ifdef` guards.

In `@src/PrimitivesWidget_test.cpp`:
- Around line 282-292: The test dereferences mesh->sharedVertexData which is
null for procedurally generated meshes; update CreateCubeVerifyEntity (and the
other primitive tests: Sphere, Torus, Cone, Tube, Capsule, IcoSphere, RBox,
Spring) to first get auto* mesh = entity->getMesh() and check mesh is non-null,
then inspect per-submesh vertex data instead of sharedVertexData (e.g., if
mesh->getNumSubMeshes() > 0 iterate submesh = mesh->getSubMesh(i) and ensure
submesh->vertexData && submesh->vertexData->vertexCount > 0u or sum vertex
counts), mirroring the safe pattern used in MeshTransform_test.cpp; keep the
same setup/teardown (PrimitiveObject::createCube, Manager::getSingleton,
sceneMgr->getEntity, mgr->destroySceneNode) but replace the direct
sharedVertexData access with these null-checked per-submesh checks.

---

Nitpick comments:
In `@src/animationcontrolwidget_test.cpp`:
- Around line 526-607: The test duplicates mesh/skeleton/entity setup that
matches createAnimatedTestEntity() in TestHelpers.h; extract the shared setup
into a new helper (e.g., createAnimatedTestEntityWithAnimations or extend
createAnimatedTestEntity) that accepts a list of animation descriptors (name and
length/keyframe info), move skeleton/mesh/bone/vertex/index creation there, and
return the created scene node/entity; then simplify
AnimationControlWidgetTest::MultipleAnimationsInTree to call the new helper,
attach/select the returned node, call widget.updateAnimationTree(), and keep the
existing assertions (tree lookup, selecting child, checking horizontalSlider
maximum). Ensure you reference and reuse
createAnimatedTestEntity()/createAnimatedTestEntityWithAnimations and
AnimationControlWidget::updateAnimationTree when updating the test.

In `@src/main_test.cpp`:
- Around line 17-22: Remove the dead MockMainWindow class definition (class
MockMainWindow : public MainWindow { ... }) from the test file: locate the
MockMainWindow declaration and delete it, and then run/build tests to ensure
there are no remaining references to MockMainWindow or relies-on symbols in
other tests (if any, replace or update them accordingly).
- Line 50: The DISABLED_ImportMeshs test currently constructs a local
QApplication (QApplication app(argc, mutable_argv)) which will reproduce the
destruction bug if re-enabled; change the test to use the global QApplication
provided by test_main.cpp instead of creating its own instance—e.g., obtain the
existing application via QApplication::instance() or reference the global app
object exported by test_main.cpp and remove the local QApplication construction
in the DISABLED_ImportMeshs test so the test uses the shared application
lifetime while you address the underlying Ogre destruction issue.

In `@src/TestHelpers.h`:
- Around line 312-405: If sceneMgr->createEntity(...) fails in
createAnimatedTestEntity, clean up all created resources before returning
nullptr: destroy/remove the Mesh (mesh), the Skeleton (skel), free any
HardwareBuffers (vbuf, ibuf) if you allocated them, and remove the SceneNode
(node) you created via Manager::getSingleton()->addSceneNode; then return
nullptr. Implement a failure check immediately after calling
sceneMgr->createEntity(name, mesh) that performs the above cleanup using the
corresponding Ogre managers (e.g.,
Ogre::MeshManager::getSingleton().remove(mesh->getName()),
Ogre::SkeletonManager::getSingleton().remove(skel->getName()),
HardwareBufferManager cleanup, and Manager::getSingleton()->removeSceneNode or
node destruction), ensuring no leaks when createEntity returns nullptr.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e5c69ca2-2144-4c58-8df0-7bff460262a1

📥 Commits

Reviewing files that changed from the base of the PR and between f67ebd8 and 65ed964.

📒 Files selected for processing (9)
  • src/MCPServer_test.cpp
  • src/Manager_test.cpp
  • src/MeshImporterExporter_test.cpp
  • src/PrimitivesWidget_test.cpp
  • src/TestHelpers.h
  • src/TransformOperator_test.cpp
  • src/about_test.cpp
  • src/animationcontrolwidget_test.cpp
  • src/main_test.cpp

fernandotonon and others added 2 commits March 4, 2026 13:24
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix null dereference on sharedVertexData in PrimitivesWidget tests: use
  per-submesh vertex data (procedural meshes don't use sharedVertexData)
- Add movable type check in Manager_test before casting to Ogre::Entity*
- Use QDir::tempPath() instead of hardcoded /tmp/ in MCPServer export test
- Select animation by name instead of child index in MultipleAnimationsInTree
  (iteration order of animation states is not guaranteed)
- Remove dead MockMainWindow class from main_test.cpp
- Fix DISABLED_ImportMeshs to use global QApplication instead of creating local

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
src/Manager_test.cpp (1)

592-596: Break after finding the first valid Ogre::Entity.

Once an entity is found, continuing iteration can accidentally pick a different one and make assertions less deterministic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Manager_test.cpp` around lines 592 - 596, In the loop over
mgr->getEntities() stop iterating once you find the first matching Ogre::Entity:
after detecting obj->getMovableType() == "Entity" and assigning entity =
static_cast<Ogre::Entity*>(obj) add an early break so subsequent items cannot
overwrite entity; update the loop that calls mgr->getEntities(), checks
getMovableType(), and assigns to entity to include this break to make the test
deterministic.
src/PrimitivesWidget_test.cpp (1)

282-424: Consider extracting a helper for primitive entity verification.

The 9 Create*VerifyEntity tests duplicate the same setup/verification/cleanup flow. A helper (or parameterized test) would reduce maintenance and keep assertions consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/PrimitivesWidget_test.cpp` around lines 282 - 424, Multiple tests
duplicate the same create/verify/destroy pattern; extract a reusable helper
(e.g., verifyPrimitiveEntity) that accepts the primitive name and a creator
callable (or use a parameterized test) to perform: call the creator (e.g.,
PrimitiveObject::createCube / createSphere / createTorus / createCone /
createTube / createCapsule / createIcoSphere / createRoundedBox / createSpring)
with the name, obtain Manager::getSingleton(), assert hasSceneNode(name),
getSceneMgr(), assert hasEntity(name), fetch entity via
sceneMgr->getEntity(name), check mesh/submesh and vertex data (use
submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData) and
EXPECT_GT(vdata->vertexCount, 0u), then call mgr->destroySceneNode(name);
replace the nine TEST_F blocks with calls to this helper or a parameterized test
feeding name + creator.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/animationcontrolwidget_test.cpp`:
- Around line 227-231: Tests dereference QTableWidgetItem returned by
table->item(...) without null checks (e.g., the expression table->item(0,
1)->text()), which can segfault if the cell is empty; before calling methods on
the item, capture it into a QTableWidgetItem* (e.g., auto* item =
table->item(row, col)) and ASSERT_NE(item, nullptr) (or ASSERT_TRUE(item)) to
fail the test early, then use item->text() in the EXPECT_* assertions; apply
this guard to every occurrence in animationcontrolwidget_test.cpp where
table->item(...) is used (including the instances flagged in the review) to
avoid nullptr dereference.
- Around line 532-556: The test uses fixed resource names "MultiAnimSkel" and
"MultiAnimMesh" when calling Ogre::SkeletonManager::getSingleton().create and
Ogre::MeshManager::getSingleton().createManual which causes resource collisions
on repeated runs; update the test to either generate unique names (e.g., append
a UUID/timestamp) for the skeleton and mesh or explicitly remove the created
resources at the end of the test by calling the managers' remove() for the
created skeleton and mesh (referencing skel and mesh) so resources are cleaned
up and subsequent runs won't hit "resource already exists" errors.

In `@src/Manager_test.cpp`:
- Around line 636-638: The test uses EXPECT_TRUE for preconditions that are
immediately followed by dereferences (entity->hasSkeleton() before
entity->getSkeleton()->getNumBones() and entity->hasAnimationState("TestAnim")
before entity->getAnimationState("TestAnim")), which can lead to null-pointer
dereference on failure; change those EXPECT_* checks to ASSERT_* (e.g.,
ASSERT_TRUE(entity->hasSkeleton()) or ASSERT_NE(entity->getSkeleton(), nullptr)
and ASSERT_TRUE(entity->hasAnimationState("TestAnim")) or
ASSERT_NE(entity->getAnimationState("TestAnim"), nullptr)) so the test aborts on
failure and avoids dereferencing invalid pointers for getSkeleton(),
getNumBones(), hasAnimationState(), and getAnimationState("TestAnim").

In `@src/MCPServer_test.cpp`:
- Around line 2325-2332: The test currently constructs a fixed exportBase in the
shared temp dir (exportBase =
QDir(QDir::tempPath()).filePath("mcp_inmemory_export")) and writes/cleans files
which can collide; replace this with a QTemporaryDir instance and use its path()
to build a unique per-test directory for args["path"] (used by
server->callTool("export_mesh", args)) and adjust cleanup to rely on the
QTemporaryDir lifetime (or explicitly remove within that temp dir) so the export
.obj/.material/.mtl filenames are created inside the temporary directory rather
than a shared basename.

In `@src/PrimitivesWidget_test.cpp`:
- Around line 290-293: Add null-check assertions for mesh and submesh before
dereferencing: after obtaining auto* mesh = entity->getMesh().get() add
ASSERT_NE(mesh, nullptr), and after auto* submesh = mesh->getSubMesh(0) add
ASSERT_NE(submesh, nullptr); then keep the existing vdata selection and
ASSERT_NE(vdata, nullptr). Apply the same pattern for the other test blocks that
use getMesh(), getSubMesh(), and vdata.

---

Nitpick comments:
In `@src/Manager_test.cpp`:
- Around line 592-596: In the loop over mgr->getEntities() stop iterating once
you find the first matching Ogre::Entity: after detecting obj->getMovableType()
== "Entity" and assigning entity = static_cast<Ogre::Entity*>(obj) add an early
break so subsequent items cannot overwrite entity; update the loop that calls
mgr->getEntities(), checks getMovableType(), and assigns to entity to include
this break to make the test deterministic.

In `@src/PrimitivesWidget_test.cpp`:
- Around line 282-424: Multiple tests duplicate the same create/verify/destroy
pattern; extract a reusable helper (e.g., verifyPrimitiveEntity) that accepts
the primitive name and a creator callable (or use a parameterized test) to
perform: call the creator (e.g., PrimitiveObject::createCube / createSphere /
createTorus / createCone / createTube / createCapsule / createIcoSphere /
createRoundedBox / createSpring) with the name, obtain Manager::getSingleton(),
assert hasSceneNode(name), getSceneMgr(), assert hasEntity(name), fetch entity
via sceneMgr->getEntity(name), check mesh/submesh and vertex data (use
submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData) and
EXPECT_GT(vdata->vertexCount, 0u), then call mgr->destroySceneNode(name);
replace the nine TEST_F blocks with calls to this helper or a parameterized test
feeding name + creator.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: efa1ca29-f7f4-4541-bb82-5c91aa7c5503

📥 Commits

Reviewing files that changed from the base of the PR and between cec73da and 086e83c.

📒 Files selected for processing (5)
  • src/MCPServer_test.cpp
  • src/Manager_test.cpp
  • src/PrimitivesWidget_test.cpp
  • src/animationcontrolwidget_test.cpp
  • src/main_test.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main_test.cpp

Comment on lines +227 to +231
auto* table = widget.findChild<QTableWidget*>("tableWidget");
ASSERT_NE(table, nullptr);
// Translation X should be near 0.5 (kf1 translate)
EXPECT_FALSE(table->item(0, 1)->text().isEmpty());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Read the relevant sections of the test file
wc -l src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 109


🏁 Script executed:

# Check lines around 227-231, 252-256, and 439-442
sed -n '225,235p' src/animationcontrolwidget_test.cpp
echo "---"
sed -n '250,260p' src/animationcontrolwidget_test.cpp
echo "---"
sed -n '437,445p' src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 1213


Guard QTableWidgetItem* before dereferencing.

At lines 230, 255, and 441, table->item(row, col) is dereferenced without a null check. Since QTableWidget::item() returns nullptr for out-of-bounds or unpopulated cells, these tests can segfault if the table is not populated as expected—particularly under Xvfb where UI state timing is unpredictable.

Apply null checks before dereferencing:

Fix pattern
-    EXPECT_FALSE(table->item(0, 1)->text().isEmpty());
+    auto* txItem = table->item(0, 1);
+    ASSERT_NE(txItem, nullptr);
+    EXPECT_FALSE(txItem->text().isEmpty());
-    table->item(0, 1)->setText("99.5");
+    auto* txItem = table->item(0, 1);
+    ASSERT_NE(txItem, nullptr);
+    txItem->setText("99.5");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
auto* table = widget.findChild<QTableWidget*>("tableWidget");
ASSERT_NE(table, nullptr);
// Translation X should be near 0.5 (kf1 translate)
EXPECT_FALSE(table->item(0, 1)->text().isEmpty());
}
auto* table = widget.findChild<QTableWidget*>("tableWidget");
ASSERT_NE(table, nullptr);
// Translation X should be near 0.5 (kf1 translate)
auto* txItem = table->item(0, 1);
ASSERT_NE(txItem, nullptr);
EXPECT_FALSE(txItem->text().isEmpty());
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/animationcontrolwidget_test.cpp` around lines 227 - 231, Tests
dereference QTableWidgetItem returned by table->item(...) without null checks
(e.g., the expression table->item(0, 1)->text()), which can segfault if the cell
is empty; before calling methods on the item, capture it into a
QTableWidgetItem* (e.g., auto* item = table->item(row, col)) and ASSERT_NE(item,
nullptr) (or ASSERT_TRUE(item)) to fail the test early, then use item->text() in
the EXPECT_* assertions; apply this guard to every occurrence in
animationcontrolwidget_test.cpp where table->item(...) is used (including the
instances flagged in the review) to avoid nullptr dereference.

Comment on lines +532 to +556
auto skel = Ogre::SkeletonManager::getSingleton().create(
"MultiAnimSkel", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
auto* rootBone = skel->createBone("Root", 0);
auto* childBone = skel->createBone("Child", 1);
childBone->setPosition(Ogre::Vector3(0, 1, 0));
rootBone->addChild(childBone);
skel->setBindingPose();

// Animation 1
auto* anim1 = skel->createAnimation("Walk", 1.0f);
auto* track1 = anim1->createNodeTrack(1);
track1->setAssociatedNode(childBone);
track1->createNodeKeyFrame(0.0f)->setTranslate(Ogre::Vector3::ZERO);
track1->createNodeKeyFrame(1.0f)->setTranslate(Ogre::Vector3(1, 0, 0));

// Animation 2
auto* anim2 = skel->createAnimation("Run", 0.5f);
auto* track2 = anim2->createNodeTrack(1);
track2->setAssociatedNode(childBone);
track2->createNodeKeyFrame(0.0f)->setTranslate(Ogre::Vector3::ZERO);
track2->createNodeKeyFrame(0.5f)->setTranslate(Ogre::Vector3(2, 0, 0));

auto mesh = Ogre::MeshManager::getSingleton().createManual(
"MultiAnimMesh", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
auto* sub = mesh->createSubMesh();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f "animationcontrolwidget_test.cpp"

Repository: fernandotonon/QtMeshEditor

Length of output: 105


🏁 Script executed:

wc -l src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 109


🏁 Script executed:

sed -n '532,556p' src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 1241


🏁 Script executed:

sed -n '510,570p' src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 2687


🏁 Script executed:

rg "MultiAnimSkel|MultiAnimMesh" src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 235


🏁 Script executed:

grep -n "class AnimationControlWidgetTest\|setUp\|TearDown\|TEST_F" src/animationcontrolwidget_test.cpp | head -30

Repository: fernandotonon/QtMeshEditor

Length of output: 1779


🏁 Script executed:

sed -n '42,58p' src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 654


🏁 Script executed:

grep -n "ResourceGroupManager\|destroy\|remove" src/animationcontrolwidget_test.cpp | head -20

Repository: fernandotonon/QtMeshEditor

Length of output: 243


🏁 Script executed:

sed -n '526,616p' src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 4020


🏁 Script executed:

rg "createManual|SkeletonManager.*create|MeshManager.*create" src/animationcontrolwidget_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 195


🏁 Script executed:

rg "QRandomGenerator" src/

Repository: fernandotonon/QtMeshEditor

Length of output: 539


🏁 Script executed:

rg "SkeletonManager\|MeshManager" src/ -A 3 -B 1 | head -60

Repository: fernandotonon/QtMeshEditor

Length of output: 52


🏁 Script executed:

grep -r "SkeletonManager\|MeshManager" src/ --include="*.cpp" --include="*.h"

Repository: fernandotonon/QtMeshEditor

Length of output: 8090


🏁 Script executed:

sed -n '1,60p' src/AnimationMerger_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 2267


🏁 Script executed:

grep -n "remove(" src/AnimationMerger_test.cpp | head -20

Repository: fernandotonon/QtMeshEditor

Length of output: 846


🏁 Script executed:

sed -n '85,140p' src/AnimationMerger_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 2046


Use unique Ogre resource names in tests to avoid collisions.

"MultiAnimSkel" and "MultiAnimMesh" are fixed names that persist in Ogre's resource managers. Repeated test runs in the same process will fail with "resource already exists" errors on the second execution. Other tests in the codebase (e.g., AnimationMerger_test.cpp) explicitly clean up resources via .remove(), but this test does not.

Proposed fix
+#include <QRandomGenerator>
...
-    auto skel = Ogre::SkeletonManager::getSingleton().create(
-        "MultiAnimSkel", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
+    const QString suffix = QString::number(QRandomGenerator::global()->generate64());
+    const std::string skelName = ("MultiAnimSkel_" + suffix).toStdString();
+    const std::string meshName = ("MultiAnimMesh_" + suffix).toStdString();
+    auto skel = Ogre::SkeletonManager::getSingleton().create(
+        skelName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
...
-    auto mesh = Ogre::MeshManager::getSingleton().createManual(
-        "MultiAnimMesh", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
+    auto mesh = Ogre::MeshManager::getSingleton().createManual(
+        meshName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/animationcontrolwidget_test.cpp` around lines 532 - 556, The test uses
fixed resource names "MultiAnimSkel" and "MultiAnimMesh" when calling
Ogre::SkeletonManager::getSingleton().create and
Ogre::MeshManager::getSingleton().createManual which causes resource collisions
on repeated runs; update the test to either generate unique names (e.g., append
a UUID/timestamp) for the skeleton and mesh or explicitly remove the created
resources at the end of the test by calling the managers' remove() for the
created skeleton and mesh (referencing skel and mesh) so resources are cleaned
up and subsequent runs won't hit "resource already exists" errors.

Comment on lines +636 to +638
EXPECT_TRUE(entity->hasSkeleton());
EXPECT_EQ(entity->getSkeleton()->getNumBones(), 2u);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

head -n 670 src/Manager_test.cpp | tail -n 45

Repository: fernandotonon/QtMeshEditor

Length of output: 1680


🏁 Script executed:

rg -n "EXPECT_TRUE\(entity->has(Skeleton|AnimationState)" src/Manager_test.cpp -A 2 -B 1

Repository: fernandotonon/QtMeshEditor

Length of output: 363


Use ASSERT_* for skeleton/animation preconditions before dereference.

At lines 636–637, EXPECT_TRUE(entity->hasSkeleton()) is immediately followed by EXPECT_EQ(entity->getSkeleton()->getNumBones(), 2u). Similarly, at lines 649–652, EXPECT_TRUE(entity->hasSkeleton()) and EXPECT_TRUE(entity->hasAnimationState("TestAnim")) precede a direct call to entity->getAnimationState("TestAnim"). If these expectations fail, the test continues and dereferences potentially invalid pointers, causing a crash rather than a clean assertion failure. Use ASSERT_TRUE or ASSERT_NE for preconditions that guard against null or invalid pointer dereference.

Proposed fix
-    EXPECT_TRUE(entity->hasSkeleton());
-    EXPECT_EQ(entity->getSkeleton()->getNumBones(), 2u);
+    ASSERT_TRUE(entity->hasSkeleton());
+    ASSERT_NE(entity->getSkeleton(), nullptr);
+    EXPECT_EQ(entity->getSkeleton()->getNumBones(), 2u);
...
-    EXPECT_TRUE(entity->hasSkeleton());
-    EXPECT_TRUE(entity->hasAnimationState("TestAnim"));
-
-    auto* state = entity->getAnimationState("TestAnim");
+    ASSERT_TRUE(entity->hasSkeleton());
+    ASSERT_TRUE(entity->hasAnimationState("TestAnim"));
+    auto* state = entity->getAnimationState("TestAnim");
+    ASSERT_NE(state, nullptr);
     EXPECT_NEAR(state->getLength(), 1.0f, 0.01f);

Also applies to: 649–653

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Manager_test.cpp` around lines 636 - 638, The test uses EXPECT_TRUE for
preconditions that are immediately followed by dereferences
(entity->hasSkeleton() before entity->getSkeleton()->getNumBones() and
entity->hasAnimationState("TestAnim") before
entity->getAnimationState("TestAnim")), which can lead to null-pointer
dereference on failure; change those EXPECT_* checks to ASSERT_* (e.g.,
ASSERT_TRUE(entity->hasSkeleton()) or ASSERT_NE(entity->getSkeleton(), nullptr)
and ASSERT_TRUE(entity->hasAnimationState("TestAnim")) or
ASSERT_NE(entity->getAnimationState("TestAnim"), nullptr)) so the test aborts on
failure and avoids dereferencing invalid pointers for getSkeleton(),
getNumBones(), hasAnimationState(), and getAnimationState("TestAnim").

Comment on lines +2325 to +2332
const QString exportBase = QDir(QDir::tempPath()).filePath("mcp_inmemory_export");
args["path"] = exportBase + ".obj";
QJsonObject result = server->callTool("export_mesh", args);
EXPECT_FALSE(isError(result));

QFile::remove(exportBase + ".obj");
QFile::remove(exportBase + ".material");
QFile::remove(exportBase + ".mtl");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check the exact code at lines 2325-2332 and surrounding context
head -2332 src/MCPServer_test.cpp | tail -20

Repository: fernandotonon/QtMeshEditor

Length of output: 912


🏁 Script executed:

# Check the include section to see what's already included
head -20 src/MCPServer_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 572


🏁 Script executed:

# Search for other temp file patterns and QTemporaryDir usage in the file
grep -n "QTemporaryDir\|QDir::tempPath\|tempPath\|mcp_inmemory" src/MCPServer_test.cpp | head -30

Repository: fernandotonon/QtMeshEditor

Length of output: 161


🏁 Script executed:

# Check if this test function is isolated or run in parallel
grep -n "^TEST\|^class.*Test" src/MCPServer_test.cpp | grep -B2 -A2 "2325\|export_mesh"

Repository: fernandotonon/QtMeshEditor

Length of output: 52


🏁 Script executed:

# Verify what files are cleaned up after the test
sed -n '2320,2340p' src/MCPServer_test.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 765


Use unique temporary directories to avoid test file collisions.

Line 2325 uses a fixed basename in the shared system temp directory. Concurrent or repeated test runs can cause file conflicts. Use QTemporaryDir to create a unique per-test directory instead.

Proposed fix
+#include <QTemporaryDir>
 ...
-    const QString exportBase = QDir(QDir::tempPath()).filePath("mcp_inmemory_export");
+    QTemporaryDir tempDir;
+    ASSERT_TRUE(tempDir.isValid());
+    const QString exportBase = QDir(tempDir.path()).filePath("mcp_inmemory_export");
     args["path"] = exportBase + ".obj";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const QString exportBase = QDir(QDir::tempPath()).filePath("mcp_inmemory_export");
args["path"] = exportBase + ".obj";
QJsonObject result = server->callTool("export_mesh", args);
EXPECT_FALSE(isError(result));
QFile::remove(exportBase + ".obj");
QFile::remove(exportBase + ".material");
QFile::remove(exportBase + ".mtl");
QTemporaryDir tempDir;
ASSERT_TRUE(tempDir.isValid());
const QString exportBase = QDir(tempDir.path()).filePath("mcp_inmemory_export");
args["path"] = exportBase + ".obj";
QJsonObject result = server->callTool("export_mesh", args);
EXPECT_FALSE(isError(result));
QFile::remove(exportBase + ".obj");
QFile::remove(exportBase + ".material");
QFile::remove(exportBase + ".mtl");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/MCPServer_test.cpp` around lines 2325 - 2332, The test currently
constructs a fixed exportBase in the shared temp dir (exportBase =
QDir(QDir::tempPath()).filePath("mcp_inmemory_export")) and writes/cleans files
which can collide; replace this with a QTemporaryDir instance and use its path()
to build a unique per-test directory for args["path"] (used by
server->callTool("export_mesh", args)) and adjust cleanup to rely on the
QTemporaryDir lifetime (or explicitly remove within that temp dir) so the export
.obj/.material/.mtl filenames are created inside the temporary directory rather
than a shared basename.

Comment on lines +290 to +293
auto* mesh = entity->getMesh().get();
auto* submesh = mesh->getSubMesh(0);
Ogre::VertexData* vdata = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
ASSERT_NE(vdata, nullptr);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if the test file exists and read the relevant section
if [ -f "src/PrimitivesWidget_test.cpp" ]; then
  echo "=== Lines 280-295 (around first flagged issue) ==="
  sed -n '280,295p' src/PrimitivesWidget_test.cpp | cat -n
  
  echo ""
  echo "=== Lines 305-315 (around second flagged issue) ==="
  sed -n '305,315p' src/PrimitivesWidget_test.cpp | cat -n
  
  echo ""
  echo "=== Check for TEST_F or fixture setup ==="
  head -n 50 src/PrimitivesWidget_test.cpp | grep -A 20 "TEST_F\|SetUp\|class.*Test"
else
  echo "File not found"
fi

Repository: fernandotonon/QtMeshEditor

Length of output: 2395


🏁 Script executed:

# Also check for patterns with mesh/submesh access across multiple test locations
rg -n "getEntity|getMesh|getSubMesh" src/PrimitivesWidget_test.cpp -A 3 -B 1

Repository: fernandotonon/QtMeshEditor

Length of output: 3647


Add mesh/submesh existence assertions before dereferencing.

The pattern across nine tests dereferences mesh and submesh without null guards. If a primitive fails to create or mesh retrieval breaks, the test will segfault instead of reporting a clean assertion failure.

Proposed fix pattern
     auto* entity = sceneMgr->getEntity("VerifyCube");
     auto* mesh = entity->getMesh().get();
+    ASSERT_NE(mesh, nullptr);
+    ASSERT_GT(mesh->getNumSubMeshes(), 0u);
     auto* submesh = mesh->getSubMesh(0);
+    ASSERT_NE(submesh, nullptr);
     Ogre::VertexData* vdata = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
     ASSERT_NE(vdata, nullptr);

Also applies to: 306–309, 322–325, 338–341, 354–357, 370–373, 386–389, 402–405, 418–421

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/PrimitivesWidget_test.cpp` around lines 290 - 293, Add null-check
assertions for mesh and submesh before dereferencing: after obtaining auto* mesh
= entity->getMesh().get() add ASSERT_NE(mesh, nullptr), and after auto* submesh
= mesh->getSubMesh(0) add ASSERT_NE(submesh, nullptr); then keep the existing
vdata selection and ASSERT_NE(vdata, nullptr). Apply the same pattern for the
other test blocks that use getMesh(), getSubMesh(), and vdata.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 4, 2026

@fernandotonon fernandotonon merged commit 7accb12 into master Mar 4, 2026
15 checks passed
@fernandotonon fernandotonon deleted the increase-test-coverage-80 branch March 4, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant