Files
ResInsight/ApplicationLibCode/UnitTests/RifVtkSurfaceImporter-Test.cpp
T
Magne Sjaastad ab96c520bd #14895: Improve stability when importing malformed input files
* #14895: Fix null grid dereference when importing mismatching GRDECL
* #14895: Fix out-of-bounds read in perforation interval import
* #14895: Validate connectivity indices in VTK import
* #14895: Fix heap overflow for odd PORV count in dual porosity cases
* #14895: Update roffcpp to validate array lengths when parsing ROFF files
2026-10-12 09:40:23 +02:00

199 lines
7.2 KiB
C++

#include "RifVtkImportUtil.h"
#include "RifVtkSurfaceImporter.h"
#include "RiaTestDataDirectory.h"
#include "Surface/RigTriangleMeshData.h"
#include <gtest/gtest.h>
#include <filesystem>
#include <fstream>
#include <memory>
#include <vector>
#include <QTemporaryDir>
// Test importing a VTU file
TEST( RifVtkSurfaceImporterTest, ImportFromFile )
{
// Setup
std::filesystem::path testDataDir = std::filesystem::path( TEST_DATA_DIR ) / "RifVtkSurfaceImporter";
std::filesystem::path vtuFilePath = testDataDir / "fracture-00000.vtu";
// Exercise
auto triangleMeshData = RifVtkSurfaceImporter::importFromFile( vtuFilePath );
// Verify
ASSERT_NE( triangleMeshData, nullptr ) << "Failed to import VTU file";
// Check vertex count (based on the sample data)
// Each triangle has 3 vertices and the sample file has 24 triangles (cells)
const size_t expectedVertexCount = 24 * 3;
auto [triangleVertices, triangleIndices] = triangleMeshData->geometry();
EXPECT_EQ( triangleVertices.size(), expectedVertexCount );
// Check connectivity size
EXPECT_EQ( triangleIndices.size(), expectedVertexCount );
// Check properties
auto propertyNames = triangleMeshData->propertyNames();
std::vector<QString> expectedProperties = { "ReservoirCell", "ReservoirPerm", "ReservoirDist" };
for ( const QString& expectedProperty : expectedProperties )
{
EXPECT_TRUE( std::any_of( propertyNames.begin(),
propertyNames.end(),
[expectedProperty]( const QString& name ) { return name == expectedProperty; } ) );
EXPECT_EQ( triangleMeshData->propertyValues( expectedProperty ).size(), expectedVertexCount );
}
// Check that we handled NaN values
auto values = triangleMeshData->propertyValues( "ReservoirPerm" );
for ( size_t i = 24; i < 30; i++ )
{
ASSERT_TRUE( std::isnan( triangleMeshData->propertyValues( "ReservoirPerm" )[i] ) );
}
}
// Connectivity referring to a non-existing vertex used to read outside the vertex array
TEST( RifVtkSurfaceImporterTest, ConnectivityOutOfRangeIsRejected )
{
const std::string xml = R"(<?xml version="1.0"?>
<VTKFile type="UnstructuredGrid" version="1.0">
<UnstructuredGrid>
<Piece NumberOfPoints="3" NumberOfCells="1">
<Points>
<DataArray type="Float64" Name="Coordinates" NumberOfComponents="3">0 0 0 1 0 0 0 1 0</DataArray>
</Points>
<Cells>
<DataArray type="UInt32" Name="connectivity">100000000 1 2</DataArray>
<DataArray type="UInt32" Name="offsets">3</DataArray>
<DataArray type="UInt8" Name="types">5</DataArray>
</Cells>
</Piece>
</UnstructuredGrid>
</VTKFile>)";
QTemporaryDir tempDir;
ASSERT_TRUE( tempDir.isValid() );
const auto filePath = std::filesystem::path( tempDir.path().toStdString() ) / "surface.vtu";
auto writeFile = [&filePath]( const std::string& content )
{
std::ofstream out( filePath );
out << content;
};
writeFile( xml );
EXPECT_EQ( nullptr, RifVtkSurfaceImporter::importFromFile( filePath ) );
// The same file with valid indices is accepted
auto validXml = xml;
validXml.replace( validXml.find( "100000000" ), 9, "0" );
writeFile( validXml );
EXPECT_NE( nullptr, RifVtkSurfaceImporter::importFromFile( filePath ) );
}
// Test parsing a PVD file
TEST( RifVtkSurfaceImporterTest, ParsePvdDatasets )
{
// Setup
std::filesystem::path testDataDir = std::filesystem::path( TEST_DATA_DIR ) / "RifVtkSurfaceImporter";
std::filesystem::path pvdFilePath = testDataDir / "dataset.pvd";
// Exercise
auto datasets = RifVtkImportUtil::parsePvdDatasets( pvdFilePath );
// Verify
ASSERT_EQ( datasets.size(), 2 ) << "Expected 2 datasets in the PVD file";
// Check the first dataset
EXPECT_DOUBLE_EQ( datasets[0].timestep, 1234567.123 );
EXPECT_EQ( datasets[0].filepath.filename(), "fracture-00000.vtu" );
// Check the second dataset
EXPECT_DOUBLE_EQ( datasets[1].timestep, 7654321.765 );
EXPECT_EQ( datasets[1].filepath.filename(), "fracture-00001.vtu" );
// Check that file paths are absolute
EXPECT_TRUE( datasets[0].filepath.is_absolute() );
EXPECT_TRUE( datasets[1].filepath.is_absolute() );
}
// Test importing multiple VTU files from PVD
TEST( RifVtkSurfaceImporterTest, ImportMultipleTimesteps )
{
// Setup
std::filesystem::path testDataDir = std::filesystem::path( TEST_DATA_DIR ) / "RifVtkSurfaceImporter";
std::filesystem::path pvdFilePath = testDataDir / "dataset.pvd";
// Exercise
auto datasets = RifVtkImportUtil::parsePvdDatasets( pvdFilePath );
ASSERT_FALSE( datasets.empty() ) << "Failed to parse PVD datasets";
// Import each VTU file
std::map<QString, std::vector<double>> expectedValues;
expectedValues["ReservoirPerm"] = { 1.2e-13, 2.2e-13 };
expectedValues["ReservoirDist"] = { 10.0, 110.0 };
expectedValues["ReservoirCell"] = { 83769, 73769 };
int index = 0;
for ( const auto& dataset : datasets )
{
auto triangleMeshData = RifVtkSurfaceImporter::importFromFile( dataset.filepath );
// Verify
ASSERT_NE( triangleMeshData, nullptr ) << "Failed to import VTU file: " << dataset.filepath;
for ( const auto& [name, values] : expectedValues )
{
// Check if ReservoirPerm values differ between timesteps
auto propValues = triangleMeshData->propertyValues( name );
EXPECT_NEAR( propValues[0], values[index], 1e-15 );
}
index++;
}
}
// Test handling of invalid files
TEST( RifVtkSurfaceImporterTest, HandleInvalidFiles )
{
std::filesystem::path testDataDir = std::filesystem::path( TEST_DATA_DIR ) / "RifVtkSurfaceImporter";
// Test with non-existent file
std::filesystem::path nonExistentFile = testDataDir / "non_existent_file.vtu";
auto result = RifVtkSurfaceImporter::importFromFile( nonExistentFile );
EXPECT_EQ( result, nullptr ) << "Expected nullptr for non-existent file";
// Test with invalid PVD file
std::filesystem::path invalidPvdFile = testDataDir / "invalid.pvd";
auto datasets = RifVtkImportUtil::parsePvdDatasets( invalidPvdFile );
EXPECT_TRUE( datasets.empty() ) << "Expected empty dataset list for invalid PVD file";
}
// Test file path resolution in PVD datasets
TEST( RifVtkSurfaceImporterTest, PvdPathResolution )
{
// Setup
std::filesystem::path testDataDir = std::filesystem::path( TEST_DATA_DIR ) / "RifVtkSurfaceImporter";
std::filesystem::path pvdFilePath = testDataDir / "dataset.pvd";
// Exercise
auto datasets = RifVtkImportUtil::parsePvdDatasets( pvdFilePath );
ASSERT_FALSE( datasets.empty() ) << "Failed to parse PVD datasets";
// Verify
for ( const auto& dataset : datasets )
{
// Check that file paths are absolute
EXPECT_TRUE( dataset.filepath.is_absolute() );
// Check that the resolved path is in the same directory as the PVD file
EXPECT_EQ( dataset.filepath.parent_path(), pvdFilePath.parent_path() );
// Check file existence
EXPECT_TRUE( std::filesystem::exists( dataset.filepath ) ) << "File does not exist: " << dataset.filepath;
}
}