Skip to content

[codex] Increase coverage tests and fail CI on skipped tests - #253

Merged
fernandotonon merged 12 commits into
masterfrom
codex/coverage-close-90
Apr 7, 2026
Merged

[codex] Increase coverage tests and fail CI on skipped tests#253
fernandotonon merged 12 commits into
masterfrom
codex/coverage-close-90

Conversation

@fernandotonon

@fernandotonon fernandotonon commented Apr 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • add targeted CLIPipeline tests for unknown command handling and rename-to-existing-animation error flow
  • add MeshImporterExporter tests for glb short alias, Ogre XML export/import round-trip, and duplicate node-name handling on scene import
  • harden CI test execution in deploy.yml to fail the workflow when any test is skipped

Why

  • improve coverage in low-covered CLI and importer/exporter paths
  • enforce no-skips policy in CI so regressions are not silently ignored

Validation

  • built UnitTests locally
  • ran QT_QPA_PLATFORM=offscreen build_cov_local/debug/UnitTests --gtest_filter='CLIPipelineRun.*:MeshImporterExporterStandaloneTest.FormatFileURI_GlbShortAlias' --gtest_death_test_style=threadsafe
  • note: OGRE/GL-dependent tests compile but are environment-dependent locally; CI is the source of truth for those suites

Summary by CodeRabbit

  • Tests

    • Extended CLI error-condition coverage with additional negative cases and a rename-conflict check
    • Added importer/exporter round-trip and edge-case tests for missing/invalid inputs, duplicate node names, and a format-URI unit test
    • Made viewport tests retry main-window construction instead of skipping on first failure
  • Bug Fixes

    • Prevent creation of entities when mesh loading fails, avoiding empty/invalid entity imports
  • Chores

    • CI now detects and reports skipped unit tests and treats non-zero skipped counts as test-run failures

@coderabbitai

coderabbitai Bot commented Apr 7, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds CI skipped-test detection and failing gating to the Linux unit-test runner, and expands unit tests across CLI/pipeline and MeshImporterExporter; also adds a defensive null-check when preloading Ogre meshes to avoid creating entities from failed loads.

Changes

Cohort / File(s) Summary
CI Workflow Skip Tracking
\.github/workflows/deploy.yml
Add count_skipped_in_xml() and SKIPPED_TESTS tracking: parse per-suite gtest XML for result="skipped", log errors for non-zero skips, increment FAILED_SUITES and SKIPPED_TESTS, include skipped totals in summaries, and fail the job when SKIPPED_TESTS > 0. Adjust XML path handling via suite_xml.
CLI Pipeline Error Tests
src/CLIPipeline_test.cpp
Add tests for unknown CLI commands (with/without --no-telemetry), expand negative-case in-process tests for cmdInfo, cmdConvert, cmdFix, cmdAnim, cmdValidate, cmdLod combining missing inputs with problematic flags/options, and add a cmdAnim rename-conflict test.
Mesh Importer/Exporter Tests
src/MeshImporterExporter_test.cpp
Add tests for missing .mesh/.mesh.xml/generic inputs (including animation-only cases), Ogre XML exporter/importer round-trip, glTF duplicate-node-name normalization, formatFileURI glb case, and add required <QVector>/<set> includes.
Mesh Importer Runtime Guard
src/MeshImporterExporter.cpp
Preload Ogre::MeshPtr for *.mesh inputs via MeshManager::getSingleton().load(...); skip when mesh is null and pass the loaded mesh into createEntity(...) to avoid creating entities from failed mesh loads.
Test Harness Stability
src/EditorViewport_test.cpp
Replace single-shot MainWindow construction that skipped on exception with a bounded retry loop (3 attempts), resetting state between attempts and asserting construction success rather than skipping.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Actions as GitHub Actions
participant Runner as Test Runner (gtest)
participant XML as Suite XML File
participant Script as Workflow Script (count_skipped_in_xml)
participant Reporter as Actions Summary/Exit
Actions->>Runner: run isolated gtest suite with --gtest_output=xml
Runner->>XML: write suite XML
Script->>XML: parse result="skipped" counts
Script-->>Actions: report skipped count; increment SKIPPED_TESTS and FAILED_SUITES if >0
Actions->>Reporter: include skipped totals in summary
alt SKIPPED_TESTS > 0
Reporter->>Actions: exit job with failure
else
Reporter->>Actions: proceed based on failures
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through XML leaves, counted every skip with glee,

nudged the runner, raised a flag, "these skips are known to me."
Meshes woke careful, nodes renamed to play nice,
tests retried, pipelines sighed, and carrots fixed the spice.
— a rabbit hums a tiny CI tune 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% 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 clearly summarizes the two main changes: increasing test coverage and enforcing CI failure on skipped tests, which directly aligns with the file changes.
Description check ✅ Passed The description follows the template structure with Summary, Technical Details (Why/Validation), but lacks explicit Features/Bugfixes subsections and some technical change bullets could be more detailed.

✏️ 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 codex/coverage-close-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: 4

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

Inline comments:
In @.github/workflows/deploy.yml:
- Around line 917-925: The current block treats any skipped test as a hard
failure (it calls return 1), which breaks legitimate GTEST_SKIP() use; modify
the logic in the snippet that uses count_skipped_in_xml, skipped_count,
SKIPPED_TESTS, test_name and output_file so skipped tests are not converted into
suite failures: remove the return 1 and instead echo a warning and increment
SKIPPED_TESTS (or, if you need stricter behavior, apply an allowlist check
similar to GL_CRASH_ALLOWLIST before returning non‑zero). Ensure the handler
only fails the job for non‑skipped errors while preserving the existing
SKIPPED_TESTS aggregation.

In `@src/CLIPipeline_test.cpp`:
- Around line 521-529: The test mutates persistent telemetry settings by calling
CLIPipeline::run which triggers SentryReporter::setEnabled(false); fix the test
by saving the original Sentry/enabled value from QSettings before invoking
CLIPipeline::run and restoring it after the EXPECT_EXIT (or explicitly call
SentryReporter::setEnabled(original) in test teardown), ensuring the persistent
QSettings key is returned to its prior state so subsequent tests are not
affected.
- Around line 513-514: The death tests set process-global state via
qputenv("QT_QPA_PLATFORM", ...) and GTEST_FLAG_SET(death_test_style, ...) before
EXPECT_EXIT and never restore them, which breaks later Ogre tests (tryInitOgre
used by CLIPipelineInitTest and CLIPipelineCmdTest); fix by saving the original
QT_QPA_PLATFORM value and the original GTEST_FLAG(death_test_style) before
setting, run the EXPECT_EXIT, and then restore the saved environment variable
and GTest flag immediately after the death test assertion so the parent process
state is unchanged for subsequent tests.

In `@src/MeshImporterExporter_test.cpp`:
- Around line 308-312: The test Importer_MissingMeshFile_IsIgnored uses a lax
assertion EXPECT_LE(Manager::getSingleton()->getSceneNodes().size(), 1) which
can hide a dangling scene node; update the test to assert strictly that no scene
nodes remain after MeshImporterExporter::importer is called with a missing file
by replacing the <=1 check with an exact zero check (use
Manager::getSingleton()->getSceneNodes().size() == 0 or the appropriate
EXPECT_EQ/ASSERT_EQ), keeping the existing call to
MeshImporterExporter::importer and the entities emptiness assertion.
🪄 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: 7fa3b4aa-46c4-4a2d-bf8f-2a4d016b25c9

📥 Commits

Reviewing files that changed from the base of the PR and between d9fb722 and a09cb02.

📒 Files selected for processing (3)
  • .github/workflows/deploy.yml
  • src/CLIPipeline_test.cpp
  • src/MeshImporterExporter_test.cpp

Comment on lines +917 to +925
local skipped_count
skipped_count=$(count_skipped_in_xml "$output_file")
if [ "$skipped_count" -gt 0 ]; then
echo "ERROR: $test_name produced $skipped_count skipped test(s)."
SKIPPED_TESTS=$((SKIPPED_TESTS + skipped_count))
return 1
fi

return 0

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List tests that intentionally use GTEST_SKIP() today.
rg -n --glob 'src/**/*_test.cpp' '\bGTEST_SKIP\s*\(' | sed 's/:.*//' | sort | uniq -c | sort -nr

Repository: fernandotonon/QtMeshEditor

Length of output: 1528


This turns intentional GTEST_SKIP() paths into hard CI failures.

The repo extensively uses GTEST_SKIP() for graceful degradation when Ogre/GL or other test prerequisites are unavailable — 40+ test files contain over 400 instances across the codebase (MCPServer_test.cpp alone has 92 skip calls). Treating every skipped testcase as a failed suite will make the job red for these valid graceful-degradation paths, not just broken tests. Per the coding guidelines, "Features depending on optional components (e.g., local LLM / llama.cpp) may not be available in the test environment — skip gracefully."

If you want a no-skip policy, scope it to suites that are mandatory in CI or maintain an allowlist similar to GL_CRASH_ALLOWLIST.

Also applies to: 976-983, 1021-1028, 1162-1165

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

In @.github/workflows/deploy.yml around lines 917 - 925, The current block
treats any skipped test as a hard failure (it calls return 1), which breaks
legitimate GTEST_SKIP() use; modify the logic in the snippet that uses
count_skipped_in_xml, skipped_count, SKIPPED_TESTS, test_name and output_file so
skipped tests are not converted into suite failures: remove the return 1 and
instead echo a warning and increment SKIPPED_TESTS (or, if you need stricter
behavior, apply an allowlist check similar to GL_CRASH_ALLOWLIST before
returning non‑zero). Ensure the handler only fails the job for non‑skipped
errors while preserving the existing SKIPPED_TESTS aggregation.

Comment thread src/CLIPipeline_test.cpp Outdated
Comment thread src/CLIPipeline_test.cpp
Comment thread src/MeshImporterExporter_test.cpp

@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.

♻️ Duplicate comments (1)
.github/workflows/deploy.yml (1)

919-923: ⚠️ Potential issue | 🟠 Major

Don’t turn repo-wide GTEST_SKIP() paths into CI failures.

src/CLIPipeline_test.cpp and src/MeshImporterExporter_test.cpp still have intentional skips for missing Ogre/GL or test data. Converting any skip into return 1, FAILED_SUITES++, and a final exit 1 will make this job red for expected environment-dependent degradation paths instead of real regressions. Keep SKIPPED_TESTS for reporting, or scope the hard-fail rule to an explicit allowlist of suites that must never skip.

Based on learnings: Features depending on optional components (e.g., local LLM / llama.cpp) may not be available in the test environment — guard with #ifdef ENABLE_LOCAL_LLM or skip gracefully.

Also applies to: 977-980, 1162-1165

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

In @.github/workflows/deploy.yml around lines 919 - 923, The workflow currently
treats any skipped GoogleTest suite as a failure by converting skipped_count > 0
into return 1 / FAILED_SUITES++ / exit 1; update the logic in the blocks around
the SKIPPED_TESTS handling so that skipped tests are only counted (increment
SKIPPED_TESTS and echo a message) and do not cause return 1 or increment
FAILED_SUITES, or alternatively implement an explicit allowlist of suites (e.g.,
"CLIPipeline_test.cpp" and "MeshImporterExporter_test.cpp") that are permitted
to skip and only hard-fail skips outside that allowlist; locate the affected
shell logic referencing SKIPPED_TESTS, skipped_count, return 1, FAILED_SUITES
and adjust to preserve SKIPPED_TESTS reporting while avoiding exit 1 for
expected environment-dependent skips.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.github/workflows/deploy.yml:
- Around line 919-923: The workflow currently treats any skipped GoogleTest
suite as a failure by converting skipped_count > 0 into return 1 /
FAILED_SUITES++ / exit 1; update the logic in the blocks around the
SKIPPED_TESTS handling so that skipped tests are only counted (increment
SKIPPED_TESTS and echo a message) and do not cause return 1 or increment
FAILED_SUITES, or alternatively implement an explicit allowlist of suites (e.g.,
"CLIPipeline_test.cpp" and "MeshImporterExporter_test.cpp") that are permitted
to skip and only hard-fail skips outside that allowlist; locate the affected
shell logic referencing SKIPPED_TESTS, skipped_count, return 1, FAILED_SUITES
and adjust to preserve SKIPPED_TESTS reporting while avoiding exit 1 for
expected environment-dependent skips.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 154c2226-9d36-44b5-85f7-fde1080bbdb6

📥 Commits

Reviewing files that changed from the base of the PR and between a09cb02 and f24b1e3.

📒 Files selected for processing (1)
  • .github/workflows/deploy.yml

@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.

♻️ Duplicate comments (1)
src/MeshImporterExporter_test.cpp (1)

309-313: ⚠️ Potential issue | 🟡 Minor

Tighten the missing .mesh assertion to catch leaked scene nodes.

Line 312 still allows one leftover node via EXPECT_LE(..., 1), which can hide partial-import leakage. Make this strict (isEmpty() or EXPECT_EQ(..., 0)).

Suggested fix
 TEST_F(MeshImporterExporterTest, Importer_MissingMeshFile_IsIgnored) {
     MeshImporterExporter::importer(QStringList{"/tmp/nonexistent_mesh_importer_12345.mesh"});
     EXPECT_TRUE(Manager::getSingleton()->getEntities().isEmpty());
-    EXPECT_LE(Manager::getSingleton()->getSceneNodes().size(), 1);
+    EXPECT_TRUE(Manager::getSingleton()->getSceneNodes().isEmpty());
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/MeshImporterExporter_test.cpp` around lines 309 - 313, The test
Importer_MissingMeshFile_IsIgnored currently allows one leftover scene node
which can mask leaks; update the assertion on
Manager::getSingleton()->getSceneNodes() in the TEST_F to require zero nodes
(e.g., replace EXPECT_LE(..., 1) with a strict check such as
EXPECT_TRUE(...isEmpty()) or EXPECT_EQ(..., 0)) so
MeshImporterExporter::importer failing on a missing .mesh file cannot leave any
scene nodes behind.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/MeshImporterExporter_test.cpp`:
- Around line 309-313: The test Importer_MissingMeshFile_IsIgnored currently
allows one leftover scene node which can mask leaks; update the assertion on
Manager::getSingleton()->getSceneNodes() in the TEST_F to require zero nodes
(e.g., replace EXPECT_LE(..., 1) with a strict check such as
EXPECT_TRUE(...isEmpty()) or EXPECT_EQ(..., 0)) so
MeshImporterExporter::importer failing on a missing .mesh file cannot leave any
scene nodes behind.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fa0bbbc9-15e1-44ca-bee7-747a962c5d27

📥 Commits

Reviewing files that changed from the base of the PR and between f24b1e3 and 3eb129c.

📒 Files selected for processing (1)
  • src/MeshImporterExporter_test.cpp

@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.

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

309-328: Use per-test unique missing paths to avoid rare /tmp collisions.

These tests are good, but fixed names under /tmp can become flaky if a file happens to exist. Prefer generating non-existent paths from QTemporaryDir + unique suffix.

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

In `@src/MeshImporterExporter_test.cpp` around lines 309 - 328, Tests use
hardcoded /tmp paths that can collide with real files; update each test
(Importer_MissingMeshFile_IsIgnored, Importer_MissingMeshXmlFile_IsIgnored,
Importer_MissingGenericFileWithAnimOutputs_IsIgnored) to create per-test unique
non-existent paths by using QTemporaryDir (or
QTemporaryFile::createUniqueFilename) and appending a unique suffix, then pass
that generated path to MeshImporterExporter::importer and keep the same
expectations; ensure the generated path is removed or guaranteed non-existent
before calling importer so the test reliably exercises the "missing file is
ignored" behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/MeshImporterExporter_test.cpp`:
- Around line 309-328: Tests use hardcoded /tmp paths that can collide with real
files; update each test (Importer_MissingMeshFile_IsIgnored,
Importer_MissingMeshXmlFile_IsIgnored,
Importer_MissingGenericFileWithAnimOutputs_IsIgnored) to create per-test unique
non-existent paths by using QTemporaryDir (or
QTemporaryFile::createUniqueFilename) and appending a unique suffix, then pass
that generated path to MeshImporterExporter::importer and keep the same
expectations; ensure the generated path is removed or guaranteed non-existent
before calling importer so the test reliably exercises the "missing file is
ignored" behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f87a9726-3125-4518-8d4c-9c0b3c656f73

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb129c and 1276b55.

📒 Files selected for processing (2)
  • src/MeshImporterExporter.cpp
  • src/MeshImporterExporter_test.cpp

@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.

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

24-34: Retry mechanism is sound for test stabilization.

The MainWindow initialization retry (lines 24-34) is a solid approach. The Manager::~Manager() destructor properly cleans up Ogre state with explicit mRoot->shutdown() and delete mRoot calls, so subsequent retry attempts will have a clean Ogre environment. The bounded retry (3 attempts) with Manager::kill() between attempts should effectively handle transient initialization failures in headless test environments.

For improved diagnostics, consider logging the exception reason on failed attempts to help identify persistent issues:

Optional: Log exception info on retry
        constexpr int kMaxMainWindowInitAttempts = 3;
        for (int attempt = 1; attempt <= kMaxMainWindowInitAttempts && !mainWindow; ++attempt) {
            try {
                mainWindow = new MainWindow();
-            } catch (...) {
+            } catch (const std::exception& e) {
+                GTEST_LOG_(WARNING) << "MainWindow init attempt " << attempt << " failed: " << e.what();
+                mainWindow = nullptr;
+                Manager::kill();
+                QThread::msleep(150);
+            } catch (...) {
+                GTEST_LOG_(WARNING) << "MainWindow init attempt " << attempt << " failed with unknown exception";
                 mainWindow = nullptr;
                 Manager::kill();
                 QThread::msleep(150);
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/EditorViewport_test.cpp` around lines 24 - 34, The retry loop creating
MainWindow swallows exceptions making diagnostics hard; modify the try/catch
around new MainWindow() to capture the thrown exception (e.g., std::exception or
std::current_exception()) and log its what()/message or reconstituted info
before calling Manager::kill() and QThread::msleep(150), keeping the bounded
attempts (kMaxMainWindowInitAttempts) and existing cleanup behavior intact so
you still assert that mainWindow is non-null afterward.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/EditorViewport_test.cpp`:
- Around line 24-34: The retry loop creating MainWindow swallows exceptions
making diagnostics hard; modify the try/catch around new MainWindow() to capture
the thrown exception (e.g., std::exception or std::current_exception()) and log
its what()/message or reconstituted info before calling Manager::kill() and
QThread::msleep(150), keeping the bounded attempts (kMaxMainWindowInitAttempts)
and existing cleanup behavior intact so you still assert that mainWindow is
non-null afterward.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99eceee9-f92c-453e-96e7-0227147903a9

📥 Commits

Reviewing files that changed from the base of the PR and between 1276b55 and f8c6650.

📒 Files selected for processing (1)
  • src/EditorViewport_test.cpp

@sonarqubecloud

sonarqubecloud Bot commented Apr 7, 2026

Copy link
Copy Markdown

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