Skip to content

test: expand coverage with 103 new test cases - #516

Merged
fernandotonon merged 2 commits into
masterfrom
improve/test-coverage-90
May 14, 2026
Merged

test: expand coverage with 103 new test cases#516
fernandotonon merged 2 commits into
masterfrom
improve/test-coverage-90

Conversation

@fernandotonon

@fernandotonon fernandotonon commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds 7 new _test.cpp files for previously untested pure-data and command-pattern classes (ScanConfig, SubMeshTransform, ApplyMaterialCommand, BoneTransformCommand, MeshDecimatorController, ViewportTitleBar, AppConsoleLog)
  • Expands 2 thin existing suites: Euler (5 → 30 cases) and QtMeshCloudClient (5 → 14 cases)
  • Net: 103 new test cases, ~1.7k LOC of test code

Target areas were chosen to maximize coverage gain without dragging in widget UI or live GL paths already excluded by LCOV_EXCL_* markers. Pure-data classes (ScanConfig YAML/JSON parsing, Euler math, QtMeshCloudClient validation) run anywhere; the Ogre-dependent suites (SubMeshTransform, ApplyMaterialCommand, BoneTransformCommand, MeshDecimatorController) follow the existing tryInitOgre() + canLoadMeshFiles() pattern so they exercise on Linux CI under Xvfb.

Test plan

  • cmake --build build_local --target UnitTests -j4 clean (0 errors, 0 new warnings)
  • strings build_local/bin/UnitTests | grep -E "(SubMeshTransform|ViewportTitleBar|...)" confirms new fixtures are registered
  • Linux CI passes all new suites under Xvfb
  • SonarCloud overall coverage advances toward the 90% target

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Expanded comprehensive test coverage across core components, including configuration parsing, mesh operations, viewport UI interactions, and command execution workflows. No user-facing changes.

Review Change Stack

Add unit-test suites for previously untested pure-data and command-pattern
classes, and expand the thin existing coverage on Euler / QtMeshCloudClient.

New test files:
- ScanConfig_test.cpp — YAML parser, variant-map loading, scope overrides
- SubMeshTransform_test.cpp — per-submesh translate/scale/rotate/read/write
- commands/ApplyMaterialCommand_test.cpp — redo/undo, multi-target, null
- commands/BoneTransformCommand_test.cpp — redo/undo, bind mode, missing entity
- MeshDecimatorController_test.cpp — singleton lifecycle, no-selection paths
- ViewportTitleBar_test.cpp — buttons, title sync, action propagation
- AppConsoleLog_test.cpp — install/attach/detach, stdio capture

Expanded suites:
- Euler_test.cpp: 5 → 30 cases (constructors, operators, normalize, limit,
  rotationTo)
- QtMeshCloudClient_test.cpp: 5 → 14 cases (validation edge cases, apiBaseUrl
  env handling, missing-token paths)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@fernandotonon has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 36 minutes and 26 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 48f1c343-1a55-45e5-a9d0-efbe3ace58c3

📥 Commits

Reviewing files that changed from the base of the PR and between e87124d and 5b93df7.

📒 Files selected for processing (3)
  • src/QtMeshCloudClient_test.cpp
  • src/ScanConfig_test.cpp
  • src/ViewportTitleBar_test.cpp
📝 Walkthrough

Walkthrough

This PR adds comprehensive GoogleTest coverage across nine modules: Euler rotations, submesh transforms, configuration parsing, viewport UI widgets, mesh decimation control, cloud API validation, console logging, and undo/redo command implementations. Total of ~1,880 lines of test code with Qt and Ogre test fixtures.

Changes

Comprehensive Test Suite Expansion

Layer / File(s) Summary
Euler math and submesh transforms
src/Euler_test.cpp, src/SubMeshTransform_test.cpp
Euler tests cover quaternion/matrix decomposition, operators, normalization, and clamping. SubMeshTransform tests validate vertex read/write, center computation, translation/scaling/rotation with centroid preservation and bounds recalculation across multi-submesh geometries.
Configuration parsing and cloud API
src/ScanConfig_test.cpp, src/QtMeshCloudClient_test.cpp
ScanConfig validates YAML parsing (scalars, lists, comment handling, nested scopes), variant/JSON deserialization, file loading, and rule/scope override precedence. QtMeshCloudClient expands structural validation and verifies environment-driven URL configuration and bearer-token error handling.
UI widget and mesh controller
src/ViewportTitleBar_test.cpp, src/MeshDecimatorController_test.cpp
ViewportTitleBar tests verify dock/action integration with two-way state sync, button click propagation, and tooltip behavior. MeshDecimatorController tests validate singleton invariants, signal emission on baseline changes, and preview state when selection is absent.
Logging and command patterns
src/AppConsoleLog_test.cpp, src/commands/ApplyMaterialCommand_test.cpp, src/commands/BoneTransformCommand_test.cpp
AppConsoleLog tests verify install safety, null handling, and stdio capture lifecycle. ApplyMaterialCommand and BoneTransformCommand tests validate redo/undo state preservation, multi-target independence, null-entity crash safety, and bind-mode semantics.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • fernandotonon/QtMeshEditor#386: Introduces BoneDragRelease behavior that is now covered by the new BoneTransformCommand_test.cpp test suite.
  • fernandotonon/QtMeshEditor#476: Adds AppConsoleLog implementation and integration into MainWindow, which is now comprehensively tested by the new AppConsoleLog_test.cpp.
  • fernandotonon/QtMeshEditor#438: Introduces ViewportTitleBar widget behavior that is directly covered by the new ViewportTitleBar_test.cpp test cases.

Poem

🐰 A rabbit hops through test suites new,
Checking transforms, configs too—
Euler spins, SubMesh glows,
Commands undo, UI flows.
Nine modules tested, bugs laid bare,
Quality blooms in the dev's care! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.20% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description covers the Summary section with clear details about new test files and expanded suites, but the Technical Details section is missing the requested bullets and structured areas (Features/Bugfixes) from the template. Add a Technical Details section with bullet points listing the main areas of change (test suites added/expanded, Ogre-dependent vs pure-data tests) and use the template structure for Features/Bugfixes sections.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the primary change: adding 103 new test cases to expand test coverage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch improve/test-coverage-90

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/QtMeshCloudClient_test.cpp (1)

76-105: 💤 Low value

Consider using a test fixture for environment variable tests.

The current approach of calling qputenv and qunsetenv within each test works but modifies global state without structured cleanup. If a test were to fail unexpectedly, subsequent tests could be affected by the lingering environment variable.

A test fixture with SetUp() and TearDown() methods would provide more robust cleanup:

class QtMeshCloudClientApiBaseUrlTest : public ::testing::Test {
protected:
    void SetUp() override {
        // Save original value
        originalValue = qgetenv("QTMESH_API_BASE");
        hadOriginal = qEnvironmentVariableIsSet("QTMESH_API_BASE");
    }
    
    void TearDown() override {
        // Restore original state
        if (hadOriginal)
            qputenv("QTMESH_API_BASE", originalValue);
        else
            qunsetenv("QTMESH_API_BASE");
    }
    
    QByteArray originalValue;
    bool hadOriginal;
};

That said, the current implementation is acceptable since EXPECT_EQ doesn't throw exceptions and cleanup will execute.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/QtMeshCloudClient_test.cpp` around lines 76 - 105, Wrap the
environment-dependent tests in a test fixture to save and restore
QTMESH_API_BASE so global state is always restored: create a class
QtMeshCloudClientApiBaseUrlTest : public ::testing::Test with protected members
QByteArray originalValue and bool hadOriginal, implement SetUp() to capture
originalValue = qgetenv("QTMESH_API_BASE") and hadOriginal =
qEnvironmentVariableIsSet("QTMESH_API_BASE"), and implement TearDown() to
restore with qputenv("QTMESH_API_BASE", originalValue) if hadOriginal or
qunsetenv("QTMESH_API_BASE") otherwise; then convert the TEST cases
(QtMeshCloudClientApiBaseUrl DefaultUrlWhenEnvUnset, EnvOverride,
TrailingSlashesStripped, WhitespaceTrimmed) to TEST_F using this fixture and
remove the manual qunsetenv calls from each test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/MeshDecimatorController_test.cpp`:
- Around line 25-26: The test currently uses ASSERT_TRUE(tryInitOgre()) and
ASSERT_TRUE(canLoadMeshFiles()) which cause hard failures; change these to
runtime checks that call GTEST_SKIP() with a clear message when prerequisites
are missing (e.g., if (!tryInitOgre()) GTEST_SKIP() << "OGRE not available"; and
similarly for canLoadMeshFiles()), so MeshDecimatorController_test.cpp skips
tests gracefully under headless/Xvfb environments rather than failing the suite.

In `@src/ViewportTitleBar_test.cpp`:
- Around line 97-117: The tests call ViewportTitleBar::closeButton() and
::floatButton() and then click them without asserting the pointers are non-null;
add null assertions before clicking to avoid segfaults: in CloseButtonClosesDock
add ASSERT_NE(nullptr, bar.closeButton()) (or ASSERT_TRUE(bar.closeButton()))
before bar.closeButton()->click(), and in FloatButtonTogglesFloating add
ASSERT_NE(nullptr, bar.floatButton()) before bar.floatButton()->click(); keep
the rest of the test behavior unchanged so failures report clear test assertions
instead of crashes.

---

Nitpick comments:
In `@src/QtMeshCloudClient_test.cpp`:
- Around line 76-105: Wrap the environment-dependent tests in a test fixture to
save and restore QTMESH_API_BASE so global state is always restored: create a
class QtMeshCloudClientApiBaseUrlTest : public ::testing::Test with protected
members QByteArray originalValue and bool hadOriginal, implement SetUp() to
capture originalValue = qgetenv("QTMESH_API_BASE") and hadOriginal =
qEnvironmentVariableIsSet("QTMESH_API_BASE"), and implement TearDown() to
restore with qputenv("QTMESH_API_BASE", originalValue) if hadOriginal or
qunsetenv("QTMESH_API_BASE") otherwise; then convert the TEST cases
(QtMeshCloudClientApiBaseUrl DefaultUrlWhenEnvUnset, EnvOverride,
TrailingSlashesStripped, WhitespaceTrimmed) to TEST_F using this fixture and
remove the manual qunsetenv calls from each test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: adec0690-7573-43c5-9f13-b286d71cd06e

📥 Commits

Reviewing files that changed from the base of the PR and between c0429b9 and e87124d.

📒 Files selected for processing (9)
  • src/AppConsoleLog_test.cpp
  • src/Euler_test.cpp
  • src/MeshDecimatorController_test.cpp
  • src/QtMeshCloudClient_test.cpp
  • src/ScanConfig_test.cpp
  • src/SubMeshTransform_test.cpp
  • src/ViewportTitleBar_test.cpp
  • src/commands/ApplyMaterialCommand_test.cpp
  • src/commands/BoneTransformCommand_test.cpp

Comment on lines +25 to +26
ASSERT_TRUE(tryInitOgre());
ASSERT_TRUE(canLoadMeshFiles());

@coderabbitai coderabbitai Bot May 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how Ogre-gated tests handle unavailable runtime prerequisites.
rg -n --type=cpp -C3 'tryInitOgre\(|canLoadMeshFiles\(|GTEST_SKIP\('

Repository: fernandotonon/QtMeshEditor

Length of output: 50383


🏁 Script executed:

# First, let's inspect MeshDecimatorController_test.cpp to see the context of lines 25-26
cat -n src/MeshDecimatorController_test.cpp | head -40

Repository: fernandotonon/QtMeshEditor

Length of output: 1573


🏁 Script executed:

cat -n src/MeshDecimatorController_test.cpp | head -40

Repository: fernandotonon/QtMeshEditor

Length of output: 1573


🏁 Script executed:

rg -n 'GTEST_SKIP' --type=cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 52


Replace hard failures with graceful skip for unavailable OGRE/mesh prerequisites.

Lines 25-26 currently fail the entire test suite when OGRE initialization or mesh loading is unavailable. Per coding guidelines, tests must work under Xvfb (headless X11) and skip gracefully when optional components are unavailable.

Suggested patch
-        ASSERT_TRUE(tryInitOgre());
-        ASSERT_TRUE(canLoadMeshFiles());
+        if (!tryInitOgre() || !canLoadMeshFiles()) {
+            GTEST_SKIP() << "Skipping MeshDecimatorController tests: OGRE/mesh prerequisites unavailable.";
+        }
📝 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
ASSERT_TRUE(tryInitOgre());
ASSERT_TRUE(canLoadMeshFiles());
if (!tryInitOgre() || !canLoadMeshFiles()) {
GTEST_SKIP() << "Skipping MeshDecimatorController tests: OGRE/mesh prerequisites unavailable.";
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MeshDecimatorController_test.cpp` around lines 25 - 26, The test
currently uses ASSERT_TRUE(tryInitOgre()) and ASSERT_TRUE(canLoadMeshFiles())
which cause hard failures; change these to runtime checks that call GTEST_SKIP()
with a clear message when prerequisites are missing (e.g., if (!tryInitOgre())
GTEST_SKIP() << "OGRE not available"; and similarly for canLoadMeshFiles()), so
MeshDecimatorController_test.cpp skips tests gracefully under headless/Xvfb
environments rather than failing the suite.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Declining this one — the project convention is ASSERT_TRUE(tryInitOgre()), never silent skip. From TestHelpers.h:182:

Test fixtures should use ASSERT_TRUE(tryInitOgre()) in CI — never skip silently.

Every existing Ogre-dependent suite follows this pattern (e.g. AddKeyframeCommand_test.cpp, TransformOperator_test.cpp, BoneDragRelease_test.cpp). The test_main also gates startup on a working tryInitOgre(), so a fail here only happens if something is wrong with the CI environment, which is what we want to catch.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@fernandotonon, understood — thank you for the clear explanation. The ASSERT_TRUE(tryInitOgre()) pattern is the right approach here per the project's documented convention, and I'll defer to that going forward.


✏️ Learnings added
Learnt from: fernandotonon
Repo: fernandotonon/QtMeshEditor PR: 516
File: src/MeshDecimatorController_test.cpp:25-26
Timestamp: 2026-05-14T03:23:16.101Z
Learning: In the QtMeshEditor project (fernandotonon/QtMeshEditor), the convention documented in `TestHelpers.h` line 182 is that Ogre-dependent test fixtures must use `ASSERT_TRUE(tryInitOgre())` and `ASSERT_TRUE(canLoadMeshFiles())` in SetUp — never `GTEST_SKIP()` or silent skipping. A failure here intentionally signals a broken CI environment. Existing suites (e.g. `AddKeyframeCommand_test.cpp`, `TransformOperator_test.cpp`, `BoneDragRelease_test.cpp`) all follow this pattern.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread src/ViewportTitleBar_test.cpp

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

Copy link
Copy Markdown

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: e87124d23c

ℹ️ 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".


TEST(QtMeshCloudClientApiBaseUrl, DefaultUrlWhenEnvUnset)
{
qunsetenv("QTMESH_API_BASE");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore QTMESH_API_BASE after mutating it in tests

These tests modify the process-wide QTMESH_API_BASE environment variable but never restore its prior value, so any later test in the same UnitTests process can see a different API base than the one it started with. This introduces order-dependent failures when the suite is run in environments where QTMESH_API_BASE is preconfigured (or when another test expects it preserved). Please snapshot and restore the original value (e.g., with a scoped env helper) instead of unsetting globally.

Useful? React with 👍 / 👎.

CI fixes:
- ScanConfig_test: PopulatesAllRuleFields was asserting the include list
  contained only the user's "**/*.fbx", but fromVariantMap auto-injects
  editor-only globs (tmd/rsd/ply) for non-Assimp formats. Switch to
  contains() checks so the test passes regardless of injection order.
- ViewportTitleBar_test: FloatButtonTogglesFloating used a parentless
  QDockWidget which is permanently floating — toggling has no observable
  effect. Now uses a QMainWindow parent and verifies the round trip.

Review feedback addressed:
- ViewportTitleBar_test: add ASSERT_NE on closeButton()/floatButton()
  before clicking, so a constructor regression fails the test cleanly
  instead of segfaulting.
- QtMeshCloudClient_test: snapshot QTMESH_API_BASE in SetUp and restore
  in TearDown so env-mutating tests don't leak state to later tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@fernandotonon
fernandotonon merged commit 96a1f57 into master May 14, 2026
20 checks passed
@fernandotonon
fernandotonon deleted the improve/test-coverage-90 branch May 14, 2026 04:47
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