diff --git a/ApplicationLibCode/ModelVisualization/RivSingleCellPartGenerator.cpp b/ApplicationLibCode/ModelVisualization/RivSingleCellPartGenerator.cpp index cd66e4b0ea..34686619c8 100644 --- a/ApplicationLibCode/ModelVisualization/RivSingleCellPartGenerator.cpp +++ b/ApplicationLibCode/ModelVisualization/RivSingleCellPartGenerator.cpp @@ -228,9 +228,17 @@ cvf::ref RivSingleCellPartGenerator::createMeshDrawable() { if ( m_rigCaseData && m_cellIndex != cvf::UNDEFINED_SIZE_T ) { + auto mainGrid = m_rigCaseData->mainGrid(); + if ( !mainGrid ) return nullptr; + + if ( m_gridIndex >= mainGrid->gridCount() ) return nullptr; + auto grid = m_rigCaseData->grid( m_gridIndex ); if ( !grid ) return nullptr; + // The selection can be stale after the case data has been reloaded, and the cell index can be out of bounds + if ( m_cellIndex >= grid->cellCount() ) return nullptr; + if ( m_showLgrMeshLines && RiaPreferencesGrid::current()->radialGridMode() == RiaGridDefines::RadialGridMode::CYLINDRICAL ) { return createMeshDrawableFromLgrGridCells(); diff --git a/ApplicationLibCode/UnitTests/CMakeLists.txt b/ApplicationLibCode/UnitTests/CMakeLists.txt index 9cfe6c40ed..6a9e74ea07 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists.txt +++ b/ApplicationLibCode/UnitTests/CMakeLists.txt @@ -37,6 +37,7 @@ set(SOURCE_UNITTEST_FILES ${CMAKE_CURRENT_LIST_DIR}/RivAnnotationTools-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RivPipeGeometryGenerator-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RivIjkIntersectionGeometryGenerator-Test.cpp + ${CMAKE_CURRENT_LIST_DIR}/RivSingleCellPartGenerator-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RivTernaryScalarMapper-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/ScalarMapper-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/WellPathAsciiFileReader-Test.cpp diff --git a/ApplicationLibCode/UnitTests/RivSingleCellPartGenerator-Test.cpp b/ApplicationLibCode/UnitTests/RivSingleCellPartGenerator-Test.cpp new file mode 100644 index 0000000000..d783789d2f --- /dev/null +++ b/ApplicationLibCode/UnitTests/RivSingleCellPartGenerator-Test.cpp @@ -0,0 +1,108 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "gtest/gtest.h" + +#include "RigEclipseCaseData.h" +#include "RigMainGrid.h" +#include "RigReservoirBuilder.h" + +#include "RivSingleCellPartGenerator.h" + +#include "cvfPart.h" +#include "cvfVector3.h" + +namespace +{ +//-------------------------------------------------------------------------------------------------- +/// Build a regular ni x nj x nk box grid in memory (no file, no view) +//-------------------------------------------------------------------------------------------------- +cvf::ref buildBoxGrid( int ni, int nj, int nk ) +{ + RigReservoirBuilder builder; + builder.setIJKCount( cvf::Vec3st( ni, nj, nk ) ); + builder.setWorldCoordinates( cvf::Vec3d( 0.0, 0.0, 0.0 ), cvf::Vec3d( ni, nj, -nk ) ); + + cvf::ref caseData = new RigEclipseCaseData( nullptr ); + builder.createGridsAndCells( caseData.p() ); + caseData->mainGrid()->computeCachedData(); + + return caseData; +} +} // namespace + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RivSingleCellPartGeneratorTest, ValidCellIndexCreatesDrawable ) +{ + cvf::ref caseData = buildBoxGrid( 2, 3, 4 ); + + RivSingleCellPartGenerator partGen( caseData.p(), 0, 5, cvf::Vec3d::ZERO ); + + cvf::ref part = partGen.createPart( cvf::Color3f::RED ); + ASSERT_TRUE( part.notNull() ); + EXPECT_TRUE( part->drawable() != nullptr ); +} + +//-------------------------------------------------------------------------------------------------- +/// A stale selection can reference a cell index no longer present in the grid +//-------------------------------------------------------------------------------------------------- +TEST( RivSingleCellPartGeneratorTest, CellIndexOutOfBoundsIsIgnored ) +{ + cvf::ref caseData = buildBoxGrid( 2, 3, 4 ); + + const size_t cellCount = caseData->mainGrid()->cellCount(); + + for ( size_t cellIndex : { cellCount, cellCount + 1000 } ) + { + RivSingleCellPartGenerator partGen( caseData.p(), 0, cellIndex, cvf::Vec3d::ZERO ); + + cvf::ref part = partGen.createPart( cvf::Color3f::RED ); + ASSERT_TRUE( part.notNull() ); + EXPECT_TRUE( part->drawable() == nullptr ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// A stale selection can reference a grid no longer present in the case +//-------------------------------------------------------------------------------------------------- +TEST( RivSingleCellPartGeneratorTest, GridIndexOutOfBoundsIsIgnored ) +{ + cvf::ref caseData = buildBoxGrid( 2, 3, 4 ); + + RivSingleCellPartGenerator partGen( caseData.p(), 1, 0, cvf::Vec3d::ZERO ); + + cvf::ref part = partGen.createPart( cvf::Color3f::RED ); + ASSERT_TRUE( part.notNull() ); + EXPECT_TRUE( part->drawable() == nullptr ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RivSingleCellPartGeneratorTest, UndefinedCellIndexIsIgnored ) +{ + cvf::ref caseData = buildBoxGrid( 2, 3, 4 ); + + RivSingleCellPartGenerator partGen( caseData.p(), 0, cvf::UNDEFINED_SIZE_T, cvf::Vec3d::ZERO ); + + cvf::ref part = partGen.createPart( cvf::Color3f::RED ); + ASSERT_TRUE( part.notNull() ); + EXPECT_TRUE( part->drawable() == nullptr ); +}