Skip to content

Grow Ric*Feature command-feature test coverage - #1013

Open
magnesj wants to merge 22 commits into
devfrom
investigate-ric-testing
Open

Grow Ric*Feature command-feature test coverage#1013
magnesj wants to merge 22 commits into
devfrom
investigate-ric-testing

Conversation

@magnesj

@magnesj magnesj commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Builds on the command-feature test harness (ResInsight-featuretests) to add asserted, model-changing tests across the Ric*Feature command layer, plus the harness helpers needed to reach previously untestable features headlessly.

Harness additions

  • Active reservoir viewRiaFeatureTestModelBuilder now publishes the mock view via RiaApplication::setActiveReservoirView(). Roughly two thirds of features resolve their context from activeReservoirView() / activeGridView() rather than the selection, so this unlocks a large part of the command layer.
  • RiaFeatureTestTreeView — RAII helper that registers a headless caf::PdmUiTreeView through RiaFeatureCommandContext, letting tree-driven features (the RicToggleItems* family) resolve the children of the selected object without a RiuMainWindow or an OpenGL context.
  • Well path geometry and unit system — well paths are built with a real vertical trajectory and a metric unit system, so features needing a trajectory (laterals) or that would otherwise prompt for a unit system (completions) run headless.
  • Shared RimMockSummaryCase — moved to ApplicationLibCode/TestModels and put on the include path of both ResInsight-tests and ResInsight-featuretests, so summary cases can be built in memory without a .SMSPEC file. A summaryCase() builder is added on top of it.

Curated tests

New tests covering the main selection-context groups:

  • Eclipse case and view: close case, new view from a case and from a view, property filter, polygon cell filter, azimuth/dip and polyline intersections
  • Well path and completions: delete (single, one-of-several, empty selection), new lateral, new perforation interval
  • Summary and well log: default summary multi plot from a selected case, new well log plot
  • Tree-driven and view-agnostic: toggle items on/off/flip, create polygon

The suite runs offscreen with no OpenGL and is green.

Documentation

docs/agents/coding-style.md and docs/agents/core.md gain guidance to run cmake-format (from cmakelang) with the repository config on changed CMake files when the tool is available, so formatting lands in the commit instead of a follow-up bot pull request.

🤖 Generated with Claude Code

magnesj and others added 22 commits July 24, 2026 20:08
Add a new ResInsight-featuretests GoogleTest executable that boots a
headless (offscreen) RiaGuiApplication and exercises the object-selection
system through the Ric*Feature command layer:

- Enablement sweep: one parameterized test per registered feature that
  calls isCommandEnabled() with an empty selection, isolating any crash to
  the offending feature id.
- Execution sweep with an in-memory model builder, modal-dialog watchdog,
  and denylist. Disabled by default because features that assume a valid
  GUI selection context trip CVF_ASSERT/CAF_ASSERT that abort or hang and
  cannot be isolated per-feature without subprocess isolation.
- Curated deep tests for RicDeleteWellPathFeature and RicNewViewFeature
  asserting real model changes.

Fix a null-pointer dereference in
RicPlaceThermalFractureUsingTemplateDataFeature::selectedThermalFracture()
surfaced by the enablement sweep: it dereferenced the result of
selectedItemOfType<RimWellPathFracture>() without a null check.

Add RiaRegressionTestRunner::setRunningRegressionTests() so the test main
can enable the dialog-suppression code paths.
… well path

Add RicCloseCaseFeature-Test asserting a selected Eclipse case is removed
from the project. Extend RicNewViewFeature-Test to cover creating a view
from a selected view, and RicDeleteWellPathFeature-Test to cover deleting
one of several well paths and the empty-selection disabled state.
Add RiaFeatureTestTreeView, an RAII helper that builds a headless project
tree view and registers it as the active tree view via
RiaFeatureCommandContext. This lets tree-driven features resolve the
children of the selected object without a RiuMainWindow or OpenGL context.

Use it to add curated tests for the RicToggleItems feature family, toggling
the show state of a well path collection's child well paths off, on and
flipped.
…section tests

Many command features read RiaApplication::activeReservoirView() /
activeGridView() rather than the selection. Publish the mock view as the
active reservoir view in RiaFeatureTestModelBuilder so these features have a
valid context in the headless harness.

Add curated tests for RicNewAzimuthDipIntersectionFeature and
RicNewPolylineIntersectionFeature, which resolve the target view from the
active view and append an intersection to it.
Add tests for RicEclipsePropertyFilterNewInViewFeature and
RicNewPolygonFilter3dviewFeature, which read the active grid view and append
a property filter / polygon cell filter to the view's collections.
…tests

Build well paths with a real vertical trajectory and a metric unit system in
RiaFeatureTestModelBuilder, so features that need a trajectory (laterals) or
would otherwise prompt for a unit system (completions) run headless.

Add curated tests for RicNewWellPathLateralFeature (adds a lateral well path)
and RicNewPerforationIntervalFeature (adds a perforation interval to the
selected well path).
…ests

Move RimMockSummaryCase to a shared ApplicationLibCode/TestModels directory
and put it on the include path of both ResInsight-tests and
ResInsight-featuretests, so the feature tests can build in-memory summary
cases without a .SMSPEC file.

Add a summaryCase() builder and curated tests for
RicNewDefaultSummaryPlotFeature (creates a summary multi plot from a selected
case) and RicNewWellLogPlotFeature (creates a well log plot).
Add a test for RicCreatePolygonFeature, which appends a user-defined polygon
to the project polygon collection when nothing is selected.
Add a CMake Formatting section describing cmake-format from the cmakelang
package and the repository configuration cmake/cmake-format.py. Instruct
agents to run it on changed CMake files when cmakelang is available, so the
formatting fix is part of the commit instead of a follow-up bot pull request.
Add a geoMechCase() builder that loads the small VTK (.pvd) model from the
unit test data via RiaApplication::openOdbCaseFromFile, giving the feature
tests a GeoMech case and view without an Abaqus/ODB dependency.

Use it to test RicNewCellIndexFilterFeature, which is only enabled when the
active grid view belongs to a GeoMech case. Also assert the feature stays
disabled for an Eclipse view.
The in-process sweep cannot be a gate: features that assume a valid GUI
selection context trip CVF_ASSERT/CAF_ASSERT, which abort the process rather
than throw, so the first offender ends the run and hides every feature after
it. Features that block also stall the run indefinitely.

Add a --feature-exec switch that executes a single feature and exits, and a
parameterized sweep that re-runs this executable once per feature. A crash
or hang now fails only that feature's test case and is attributed to its
command id. Hangs are caught by a per-child timeout.

Extract the shared model, scenario and dialog-watchdog logic into
RicFeatureExecutionRunner so both sweeps use one implementation. The child
closes the project before the application is destroyed, and prints a marker
when feature execution is done, so a crash during harness teardown is not
reported as a feature defect.
…p in the sweep

Two harness defects made the subprocess sweep report crashes that were not
feature defects.

Calling actionTriggered() directly leaves QObject::sender() null, so every
feature reading caf::CmdFeature::userData() aborted on a failed assert.
Trigger the QAction instead, which is how the application invokes a feature.

RiaGuiApplication::initialize() creates and shows the main window, so Qt has
paint events queued for the 3D viewers. Offscreen there is no OpenGL context
and caf::Viewer::paintGL() dereferences it, which was attributed to whichever
feature happened to run. Drop the scheduled redraws and leave the event loop
alone; rendering belongs to a separate software-GL tier.

Together these take the sweep from 26 failures to 7. Also correct comments
that claimed the test executable has no RiuMainWindow.
RicTogglePerspectiveViewFeature changes the projection of the active viewer
and RicNewContourMapViewFeature creates and draws a new 3D view. The
offscreen platform plugin creates the viewer widget but no OpenGL context, so
drawing dereferences a null context in caf::Viewer::paintGL(). Both work in
the real application, so they belong in a software-OpenGL test tier rather
than being reported as feature defects.
The ResInsight-featuretests executable was built but never run in CI. Run it
alongside the unit tests so the curated command feature tests actually gate.

Also run the subprocess execution sweep, which starts one child process per
registered Ric*Feature and attributes a crash or hang to a single command id.
That is too slow for every push, so it is limited to the nightly schedule.
Split the model builders into helpers that do not close the project, so they
can be combined, and add richModel(): an Eclipse case and view, a well path,
a summary case, a GeoMech case and view, and a polygon in one project. The
execution sweep needs this because a feature is only executed under a
selection where it reports itself enabled, and Eclipse selections alone leave
every summary, GeoMech and polygon feature untouched.
Drive the execution sweep from richModel with one scenario per domain
(Eclipse case and view, well path, summary case, GeoMech case and view,
polygon), keeping the active view consistent with the selection. Features
actually executed rise from 223 to 244 of 470, and the wider selection
immediately exposed an abort in RicDeleteItemFeature on a GeoMech view.

Share allRegisteredFeatureIds from RicFeatureExecutionRunner. Both sweeps
defined it in an anonymous namespace, which breaks the unity build used on
Windows, where the two files end up in one translation unit.

Disable the RicNewWellLogPlotFeature test. It segfaults on Ubuntu clang-19
while passing on Windows and Ubuntu gcc, and the same feature was the one
that crashed in the sweep until the sweep stopped pumping the event loop.
Replace the hand picked scenarios with a walk of the project, keeping one
object per class keyword. Command features dispatch on the type of the
selected object, so one instance per class is enough, and it reaches the
collections and definition objects most features are written against, which
the hand picked list never did. Features actually executed rise from 244 to
315 of 470.

Derive the active view from the candidate instead of wiring it per scenario,
so selecting an object inside the GeoMech view no longer leaves the Eclipse
view active. Cap executions at three per feature and log when the cap is hit,
so a feature enabled for many types does not dominate the run and the
truncation is visible.

Ask for capabilities with capability<T>() rather than uiCapability() and
xmlCapability(). Those accessors assert when the capability is missing
instead of returning null, so they cannot be used to test for it, and not
every object in the project has both.
Each execution now starts from an identical model. Sharing one model let a
destructive feature leave debris that made a later execution fail for reasons
unrelated to the object then selected, and the existing guards could not
catch it because they only track the few objects they name. Such order
dependent failures are false reports, and they also make a run depend on the
order the candidates happen to be visited.
A failing CAF_ASSERT prints the file and line with std::printf and then calls
std::abort(), which does not flush. With buffered output the parent captured
everything except the message identifying where the child died.
The sweep starts one child process per registered Ric*Feature, which takes
several minutes when run serially. Register it as GoogleTest shards instead,
so ctest -j runs them in parallel while every feature still gets its own
child process and its own test case, keeping crash isolation and attribution.

Four shards complete in 103 seconds on a developer machine, and the work is
evenly spread with each shard taking between 96 and 103 seconds.

Note that enable_testing() is called in ApplicationLibCode, so the tests are
registered in that part of the build tree and ctest has to be pointed there.
Measured on a 32 core machine: four shards 103 s, eight shards 62 s, twenty
shards 61 s. Going past eight buys no wall clock time but doubles the CPU
spent, because the shards become unbalanced. GoogleTest splits by test index
while the cost per feature varies a lot, so one shard draws the expensive
features and takes 61 s while others finish in 31 s and wait.
@magnesj
magnesj force-pushed the investigate-ric-testing branch from b567977 to c7cf726 Compare July 24, 2026 18:09
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