LCOV - code coverage report
Current view: top level - autotest/cpp - test_ogr.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2774 2791 99.4 %
Date: 2026-10-23 13:59:38 Functions: 385 385 100.0 %

          Line data    Source code
       1             : ///////////////////////////////////////////////////////////////////////////////
       2             : //
       3             : // Project:  C++ Test Suite for GDAL/OGR
       4             : // Purpose:  Test general OGR features.
       5             : // Author:   Mateusz Loskot <mateusz@loskot.net>
       6             : //
       7             : ///////////////////////////////////////////////////////////////////////////////
       8             : // Copyright (c) 2006, Mateusz Loskot <mateusz@loskot.net>
       9             : /*
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_unit_test.h"
      14             : 
      15             : #include "ogr_p.h"
      16             : #include "ogrsf_frmts.h"
      17             : #include "../../ogr/ogrsf_frmts/osm/gpb.h"
      18             : #include "ogr_recordbatch.h"
      19             : #include "ogrlayerarrow.h"
      20             : #include "ogr_geos.h"
      21             : 
      22             : #include <string>
      23             : #include <algorithm>
      24             : #include <cmath>
      25             : #include <fstream>
      26             : #include <limits>
      27             : 
      28             : #ifdef HAVE_SQLITE3
      29             : #include <sqlite3.h>
      30             : #endif
      31             : 
      32             : #include "gtest_include.h"
      33             : 
      34             : namespace
      35             : {
      36             : 
      37             : // Common fixture with test data
      38             : struct test_ogr : public ::testing::Test
      39             : {
      40             :     std::string data_{tut::common::data_basedir};
      41             :     std::string data_tmp_{tut::common::tmp_basedir};
      42             : };
      43             : 
      44             : // Test OGR driver registrar access
      45           4 : TEST_F(test_ogr, GetGDALDriverManager)
      46             : {
      47           1 :     ASSERT_TRUE(nullptr != GetGDALDriverManager());
      48             : }
      49             : 
      50             : template <class T>
      51          16 : void testSpatialReferenceLeakOnCopy(OGRSpatialReference *poSRS)
      52             : {
      53          16 :     ASSERT_EQ(1, poSRS->GetReferenceCount());
      54             :     {
      55             :         int nCurCount;
      56          16 :         int nLastCount = 1;
      57          16 :         T value;
      58          16 :         value.assignSpatialReference(poSRS);
      59          16 :         nCurCount = poSRS->GetReferenceCount();
      60          16 :         ASSERT_GT(nCurCount, nLastCount);
      61          16 :         nLastCount = nCurCount;
      62             : 
      63          16 :         T value2(value);
      64          16 :         nCurCount = poSRS->GetReferenceCount();
      65          16 :         ASSERT_GT(nCurCount, nLastCount);
      66          16 :         nLastCount = nCurCount;
      67             : 
      68          16 :         T value3;
      69          16 :         value3 = value;
      70          16 :         nCurCount = poSRS->GetReferenceCount();
      71          16 :         ASSERT_GT(nCurCount, nLastCount);
      72          16 :         nLastCount = nCurCount;
      73             : 
      74          16 :         value3 = value;
      75             :         // avoid Coverity Scan warning about above assignment being better
      76             :         // replaced with a move.
      77          16 :         EXPECT_NE(value.getSpatialReference(), nullptr);
      78          16 :         ASSERT_EQ(nLastCount, poSRS->GetReferenceCount());
      79             :     }
      80          16 :     ASSERT_EQ(1, poSRS->GetReferenceCount());
      81             : }
      82             : 
      83             : // Test if copy does not leak or double delete the spatial reference
      84           4 : TEST_F(test_ogr, SpatialReference_leak)
      85             : {
      86           1 :     OGRSpatialReference *poSRS = new OGRSpatialReference();
      87           1 :     ASSERT_TRUE(nullptr != poSRS);
      88             : 
      89           1 :     testSpatialReferenceLeakOnCopy<OGRPoint>(poSRS);
      90           1 :     testSpatialReferenceLeakOnCopy<OGRLineString>(poSRS);
      91           1 :     testSpatialReferenceLeakOnCopy<OGRLinearRing>(poSRS);
      92           1 :     testSpatialReferenceLeakOnCopy<OGRCircularString>(poSRS);
      93           1 :     testSpatialReferenceLeakOnCopy<OGRCompoundCurve>(poSRS);
      94           1 :     testSpatialReferenceLeakOnCopy<OGRCurvePolygon>(poSRS);
      95           1 :     testSpatialReferenceLeakOnCopy<OGRPolygon>(poSRS);
      96           1 :     testSpatialReferenceLeakOnCopy<OGRGeometryCollection>(poSRS);
      97           1 :     testSpatialReferenceLeakOnCopy<OGRMultiSurface>(poSRS);
      98           1 :     testSpatialReferenceLeakOnCopy<OGRMultiPolygon>(poSRS);
      99           1 :     testSpatialReferenceLeakOnCopy<OGRMultiPoint>(poSRS);
     100           1 :     testSpatialReferenceLeakOnCopy<OGRMultiCurve>(poSRS);
     101           1 :     testSpatialReferenceLeakOnCopy<OGRMultiLineString>(poSRS);
     102           1 :     testSpatialReferenceLeakOnCopy<OGRTriangle>(poSRS);
     103           1 :     testSpatialReferenceLeakOnCopy<OGRPolyhedralSurface>(poSRS);
     104           1 :     testSpatialReferenceLeakOnCopy<OGRTriangulatedSurface>(poSRS);
     105             : 
     106           1 :     delete poSRS;
     107             : 
     108             :     // Check that assignSpatialReference() works when passed the SRS
     109             :     // object it already owns and whose has a single reference.
     110           1 :     poSRS = new OGRSpatialReference();
     111           2 :     OGRPoint oPoint;
     112           1 :     oPoint.assignSpatialReference(poSRS);
     113           1 :     poSRS->Release();
     114           1 :     oPoint.assignSpatialReference(oPoint.getSpatialReference());
     115             : }
     116             : 
     117             : template <class T> T *make();
     118             : 
     119           6 : template <> OGRPoint *make()
     120             : {
     121           6 :     return new OGRPoint(1.0, 2.0, 3.0);
     122             : }
     123             : 
     124          18 : template <> OGRLineString *make()
     125             : {
     126          18 :     OGRLineString *poLineString = new OGRLineString();
     127             : 
     128          18 :     poLineString->addPoint(1.0, 2.0, 3.0);
     129          18 :     poLineString->addPoint(1.1, 2.1, 3.1);
     130          18 :     poLineString->addPoint(1.2, 2.2, 3.2);
     131             : 
     132          18 :     return poLineString;
     133             : }
     134             : 
     135          22 : template <> OGRLinearRing *make()
     136             : {
     137          22 :     OGRLinearRing *poLinearRing = new OGRLinearRing();
     138             : 
     139          22 :     poLinearRing->addPoint(1.0, 2.0, 3.0);
     140          22 :     poLinearRing->addPoint(1.1, 2.1, 3.1);
     141          22 :     poLinearRing->addPoint(1.2, 2.2, 3.2);
     142          22 :     poLinearRing->addPoint(1.0, 2.0, 3.0);
     143             : 
     144          22 :     return poLinearRing;
     145             : }
     146             : 
     147          14 : template <> OGRCircularString *make()
     148             : {
     149          14 :     OGRCircularString *poCircularString = new OGRCircularString();
     150             : 
     151          14 :     poCircularString->addPoint(1.0, 2.0, 3.0);
     152          14 :     poCircularString->addPoint(1.1, 2.1, 3.1);
     153          14 :     poCircularString->addPoint(1.2, 2.2, 3.2);
     154             : 
     155          14 :     return poCircularString;
     156             : }
     157             : 
     158          12 : template <> OGRCompoundCurve *make()
     159             : {
     160          12 :     OGRCompoundCurve *poCompoundCurve = new OGRCompoundCurve();
     161             : 
     162          12 :     poCompoundCurve->addCurveDirectly(make<OGRLineString>());
     163          12 :     OGRCircularString *poCircularString = make<OGRCircularString>();
     164          12 :     poCircularString->reversePoints();
     165          12 :     poCompoundCurve->addCurveDirectly(poCircularString);
     166             : 
     167          12 :     return poCompoundCurve;
     168             : }
     169             : 
     170           4 : template <> OGRCurvePolygon *make()
     171             : {
     172           4 :     OGRCurvePolygon *poCurvePolygon = new OGRCurvePolygon();
     173             : 
     174           4 :     poCurvePolygon->addRingDirectly(make<OGRCompoundCurve>());
     175           4 :     poCurvePolygon->addRingDirectly(make<OGRCompoundCurve>());
     176             : 
     177           4 :     return poCurvePolygon;
     178             : }
     179             : 
     180           8 : template <> OGRPolygon *make()
     181             : {
     182           8 :     OGRPolygon *poPolygon = new OGRPolygon();
     183             : 
     184           8 :     poPolygon->addRingDirectly(make<OGRLinearRing>());
     185           8 :     poPolygon->addRingDirectly(make<OGRLinearRing>());
     186             : 
     187           8 :     return poPolygon;
     188             : }
     189             : 
     190           2 : template <> OGRGeometryCollection *make()
     191             : {
     192           2 :     OGRGeometryCollection *poCollection = new OGRGeometryCollection();
     193             : 
     194           2 :     poCollection->addGeometryDirectly(make<OGRPoint>());
     195           2 :     poCollection->addGeometryDirectly(make<OGRLinearRing>());
     196             : 
     197           2 :     return poCollection;
     198             : }
     199             : 
     200           2 : template <> OGRMultiSurface *make()
     201             : {
     202           2 :     OGRMultiSurface *poCollection = new OGRMultiSurface();
     203             : 
     204           2 :     poCollection->addGeometryDirectly(make<OGRPolygon>());
     205           2 :     poCollection->addGeometryDirectly(make<OGRCurvePolygon>());
     206             : 
     207           2 :     return poCollection;
     208             : }
     209             : 
     210           2 : template <> OGRMultiPolygon *make()
     211             : {
     212           2 :     OGRMultiPolygon *poCollection = new OGRMultiPolygon();
     213             : 
     214           2 :     poCollection->addGeometryDirectly(make<OGRPolygon>());
     215             : 
     216           2 :     return poCollection;
     217             : }
     218             : 
     219           2 : template <> OGRMultiPoint *make()
     220             : {
     221           2 :     OGRMultiPoint *poCollection = new OGRMultiPoint();
     222             : 
     223           2 :     poCollection->addGeometryDirectly(make<OGRPoint>());
     224             : 
     225           2 :     return poCollection;
     226             : }
     227             : 
     228           2 : template <> OGRMultiCurve *make()
     229             : {
     230           2 :     OGRMultiCurve *poCollection = new OGRMultiCurve();
     231             : 
     232           2 :     poCollection->addGeometryDirectly(make<OGRLineString>());
     233           2 :     poCollection->addGeometryDirectly(make<OGRCompoundCurve>());
     234             : 
     235           2 :     return poCollection;
     236             : }
     237             : 
     238           2 : template <> OGRMultiLineString *make()
     239             : {
     240           2 :     OGRMultiLineString *poCollection = new OGRMultiLineString();
     241             : 
     242           2 :     poCollection->addGeometryDirectly(make<OGRLineString>());
     243           2 :     poCollection->addGeometryDirectly(make<OGRLinearRing>());
     244             : 
     245           2 :     return poCollection;
     246             : }
     247             : 
     248           4 : template <> OGRTriangle *make()
     249             : {
     250           8 :     OGRPoint p1(0, 0), p2(0, 1), p3(1, 1);
     251           8 :     return new OGRTriangle(p1, p2, p3);
     252             : }
     253             : 
     254           2 : template <> OGRTriangulatedSurface *make()
     255             : {
     256           2 :     OGRTriangulatedSurface *poTS = new OGRTriangulatedSurface();
     257           2 :     poTS->addGeometryDirectly(make<OGRTriangle>());
     258           2 :     return poTS;
     259             : }
     260             : 
     261           2 : template <> OGRPolyhedralSurface *make()
     262             : {
     263           2 :     OGRPolyhedralSurface *poPS = new OGRPolyhedralSurface();
     264           2 :     poPS->addGeometryDirectly(make<OGRPolygon>());
     265           2 :     return poPS;
     266             : }
     267             : 
     268          16 : template <class T> void testCopyEquals()
     269             : {
     270          16 :     auto poOrigin = std::unique_ptr<T>(make<T>());
     271          16 :     ASSERT_TRUE(nullptr != poOrigin);
     272             : 
     273          16 :     T value2(*poOrigin);
     274             : 
     275          16 :     ASSERT_TRUE(poOrigin->Equals(&value2))
     276           0 :         << poOrigin->getGeometryName() << ": copy constructor changed a value";
     277             : 
     278          16 :     T value3;
     279          16 :     value3 = *poOrigin;
     280          16 :     value3 = *poOrigin;
     281          16 :     auto &value3Ref(value3);
     282          16 :     value3 = value3Ref;
     283             : 
     284             : #ifdef DEBUG_VERBOSE
     285             :     char *wkt1 = NULL, *wkt2 = NULL;
     286             :     poOrigin->exportToWkt(&wkt1);
     287             :     value3.exportToWkt(&wkt2);
     288             :     printf("%s %s\n", wkt1, wkt2);
     289             :     CPLFree(wkt1);
     290             :     CPLFree(wkt2);
     291             : #endif
     292          16 :     ASSERT_TRUE(poOrigin->Equals(&value3))
     293           0 :         << poOrigin->getGeometryName()
     294           0 :         << ": assignment operator changed a value";
     295             : 
     296          16 :     value3 = T();
     297          16 :     ASSERT_TRUE(value3.IsEmpty());
     298             : }
     299             : 
     300             : // Test if copy constructor and assignment operators succeeds on copying the
     301             : // geometry data
     302           4 : TEST_F(test_ogr, SpatialReference_leak_copy_constructor)
     303             : {
     304           1 :     testCopyEquals<OGRPoint>();
     305           1 :     testCopyEquals<OGRLineString>();
     306           1 :     testCopyEquals<OGRLinearRing>();
     307           1 :     testCopyEquals<OGRCircularString>();
     308           1 :     testCopyEquals<OGRCompoundCurve>();
     309           1 :     testCopyEquals<OGRCurvePolygon>();
     310           1 :     testCopyEquals<OGRPolygon>();
     311           1 :     testCopyEquals<OGRGeometryCollection>();
     312           1 :     testCopyEquals<OGRMultiSurface>();
     313           1 :     testCopyEquals<OGRMultiPolygon>();
     314           1 :     testCopyEquals<OGRMultiPoint>();
     315           1 :     testCopyEquals<OGRMultiCurve>();
     316           1 :     testCopyEquals<OGRMultiLineString>();
     317           1 :     testCopyEquals<OGRTriangle>();
     318           1 :     testCopyEquals<OGRPolyhedralSurface>();
     319           1 :     testCopyEquals<OGRTriangulatedSurface>();
     320           1 : }
     321             : 
     322             : // Test crazy usage of OGRGeometryCollection copy constructor
     323           4 : TEST_F(test_ogr, OGRGeometryCollection_copy_constructor_illegal_use)
     324             : {
     325           2 :     OGRGeometryCollection gc;
     326           1 :     gc.addGeometryDirectly(new OGRPoint(1, 2));
     327             : 
     328           2 :     OGRMultiPolygon mp;
     329           1 :     mp.addGeometryDirectly(new OGRPolygon());
     330             : 
     331           1 :     OGRGeometryCollection *mp_as_gc = &mp;
     332           1 :     CPLErrorReset();
     333             :     {
     334           2 :         CPLErrorHandlerPusher oPusher(CPLQuietErrorHandler);
     335           1 :         *mp_as_gc = gc;
     336             :         // avoid Coverity Scan warning
     337           1 :         EXPECT_EQ(gc.getSpatialReference(), nullptr);
     338             :     }
     339           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(),
     340             :                  "Illegal use of OGRGeometryCollection::operator=(): trying to "
     341             :                  "assign an incompatible sub-geometry");
     342           1 :     EXPECT_TRUE(mp.IsEmpty());
     343           1 : }
     344             : 
     345             : // Test crazy usage of OGRCurvePolygon copy constructor
     346           4 : TEST_F(test_ogr, OGRCurvePolygon_copy_constructor_illegal_use)
     347             : {
     348           2 :     OGRCurvePolygon cp;
     349           1 :     auto poCC = new OGRCircularString();
     350           1 :     poCC->addPoint(0, 0);
     351           1 :     poCC->addPoint(1, 1);
     352           1 :     poCC->addPoint(2, 0);
     353           1 :     poCC->addPoint(1, -1);
     354           1 :     poCC->addPoint(0, 0);
     355           1 :     cp.addRingDirectly(poCC);
     356             : 
     357           2 :     OGRPolygon poly;
     358           1 :     auto poLR = new OGRLinearRing();
     359           1 :     poLR->addPoint(0, 0);
     360           1 :     poLR->addPoint(1, 1);
     361           1 :     poLR->addPoint(2, 0);
     362           1 :     poLR->addPoint(1, -1);
     363           1 :     poLR->addPoint(0, 0);
     364           1 :     poly.addRingDirectly(poLR);
     365             : 
     366           1 :     OGRCurvePolygon *poly_as_cp = &poly;
     367           1 :     CPLErrorReset();
     368             :     {
     369           2 :         CPLErrorHandlerPusher oPusher(CPLQuietErrorHandler);
     370           1 :         *poly_as_cp = cp;
     371             :         // avoid Coverity Scan warning
     372           1 :         EXPECT_EQ(cp.getSpatialReference(), nullptr);
     373             :     }
     374           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(),
     375             :                  "Illegal use of OGRCurvePolygon::operator=(): trying to "
     376             :                  "assign an incompatible sub-geometry");
     377           1 :     EXPECT_TRUE(poly.IsEmpty());
     378           1 : }
     379             : 
     380          16 : template <class T> void testMove()
     381             : {
     382          16 :     auto poSRS = new OGRSpatialReference();
     383             :     {
     384          16 :         auto poOrigin = std::unique_ptr<T>(make<T>());
     385          16 :         ASSERT_TRUE(nullptr != poOrigin);
     386          16 :         poOrigin->assignSpatialReference(poSRS);
     387             : 
     388          16 :         T valueCopy(*poOrigin);
     389          16 :         const int refCountBefore = poSRS->GetReferenceCount();
     390          16 :         T fromMoved(std::move(*poOrigin));
     391          16 :         EXPECT_EQ(poSRS->GetReferenceCount(), refCountBefore);
     392             : 
     393          16 :         ASSERT_TRUE(fromMoved.Equals(&valueCopy))
     394           0 :             << valueCopy.getGeometryName()
     395           0 :             << ": move constructor changed a value";
     396          16 :         EXPECT_EQ(fromMoved.getSpatialReference(), poSRS);
     397             : 
     398          16 :         T valueCopy2(valueCopy);
     399          16 :         EXPECT_EQ(valueCopy.getSpatialReference(), poSRS);
     400          16 :         T value3;
     401          16 :         const int refCountBefore2 = poSRS->GetReferenceCount();
     402          16 :         value3 = std::move(valueCopy);
     403          16 :         EXPECT_EQ(poSRS->GetReferenceCount(), refCountBefore2);
     404             : 
     405          16 :         ASSERT_TRUE(value3.Equals(&valueCopy2))
     406           0 :             << valueCopy2.getGeometryName()
     407           0 :             << ": move assignment operator changed a value";
     408          16 :         EXPECT_EQ(value3.getSpatialReference(), poSRS);
     409             :     }
     410          16 :     EXPECT_EQ(poSRS->GetReferenceCount(), 1);
     411          16 :     poSRS->Release();
     412             : }
     413             : 
     414           4 : TEST_F(test_ogr, geometry_move)
     415             : {
     416           1 :     testMove<OGRPoint>();
     417           1 :     testMove<OGRLineString>();
     418           1 :     testMove<OGRLinearRing>();
     419           1 :     testMove<OGRCircularString>();
     420           1 :     testMove<OGRCompoundCurve>();
     421           1 :     testMove<OGRCurvePolygon>();
     422           1 :     testMove<OGRPolygon>();
     423           1 :     testMove<OGRGeometryCollection>();
     424           1 :     testMove<OGRMultiSurface>();
     425           1 :     testMove<OGRMultiPolygon>();
     426           1 :     testMove<OGRMultiPoint>();
     427           1 :     testMove<OGRMultiCurve>();
     428           1 :     testMove<OGRMultiLineString>();
     429           1 :     testMove<OGRTriangle>();
     430           1 :     testMove<OGRPolyhedralSurface>();
     431           1 :     testMove<OGRTriangulatedSurface>();
     432           1 : }
     433             : 
     434           4 : TEST_F(test_ogr, geometry_get_point)
     435             : {
     436             :     {
     437           1 :         OGRPoint p;
     438           1 :         double x = 1, y = 2;
     439           1 :         OGR_G_SetPoints((OGRGeometryH)&p, 1, &x, 0, &y, 0, nullptr, 0);
     440           1 :         ASSERT_EQ(p.getCoordinateDimension(), 2);
     441           1 :         ASSERT_EQ(p.getX(), 1);
     442           1 :         ASSERT_EQ(p.getY(), 2);
     443           1 :         ASSERT_EQ(p.getZ(), 0);
     444             :     }
     445             : 
     446             :     {
     447           1 :         OGRPoint p;
     448           1 :         double x = 1, y = 2, z = 3;
     449           1 :         OGR_G_SetPoints((OGRGeometryH)&p, 1, &x, 0, &y, 0, &z, 0);
     450           1 :         ASSERT_EQ(p.getCoordinateDimension(), 3);
     451           1 :         ASSERT_EQ(p.getX(), 1);
     452           1 :         ASSERT_EQ(p.getY(), 2);
     453           1 :         ASSERT_EQ(p.getZ(), 3);
     454             :     }
     455             : 
     456             :     {
     457           2 :         OGRPoint p;
     458           1 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     459           1 :         OGR_G_SetPoints((OGRGeometryH)&p, 1, nullptr, 0, nullptr, 0, nullptr,
     460             :                         0);
     461           1 :         CPLPopErrorHandler();
     462             :     }
     463             : 
     464             :     {
     465           1 :         OGRLineString ls;
     466           1 :         double x = 1, y = 2;
     467           1 :         OGR_G_SetPoints((OGRGeometryH)&ls, 1, &x, 0, &y, 0, nullptr, 0);
     468           1 :         ASSERT_EQ(ls.getCoordinateDimension(), 2);
     469           1 :         ASSERT_EQ(ls.getX(0), 1);
     470           1 :         ASSERT_EQ(ls.getY(0), 2);
     471           1 :         ASSERT_EQ(ls.getZ(0), 0);
     472             :     }
     473             : 
     474             :     {
     475           1 :         OGRLineString ls;
     476           1 :         double x = 1, y = 2;
     477           1 :         OGR_G_SetPoints((OGRGeometryH)&ls, 1, &x, 0, &y, 0, nullptr, 0);
     478           1 :         ASSERT_EQ(ls.getCoordinateDimension(), 2);
     479           1 :         ASSERT_EQ(ls.getX(0), 1);
     480           1 :         ASSERT_EQ(ls.getY(0), 2);
     481           1 :         ASSERT_EQ(ls.getZ(0), 0);
     482             :     }
     483             : 
     484             :     {
     485           1 :         OGRLineString ls;
     486           1 :         double x = 1, y = 2;
     487           1 :         OGR_G_SetPoints((OGRGeometryH)&ls, 1, &x, 8, &y, 8, nullptr, 0);
     488           1 :         ASSERT_EQ(ls.getCoordinateDimension(), 2);
     489           1 :         ASSERT_EQ(ls.getX(0), 1);
     490           1 :         ASSERT_EQ(ls.getY(0), 2);
     491           1 :         ASSERT_EQ(ls.getZ(0), 0);
     492             :     }
     493             : 
     494             :     {
     495           1 :         OGRLineString ls;
     496           1 :         double x = 1, y = 2, z = 3;
     497           1 :         OGR_G_SetPoints((OGRGeometryH)&ls, 1, &x, 0, &y, 0, &z, 0);
     498           1 :         ASSERT_EQ(ls.getCoordinateDimension(), 3);
     499           1 :         ASSERT_EQ(ls.getX(0), 1);
     500           1 :         ASSERT_EQ(ls.getY(0), 2);
     501           1 :         ASSERT_EQ(ls.getZ(0), 3);
     502             :     }
     503             : 
     504             :     {
     505           1 :         OGRLineString ls;
     506           1 :         double x = 1, y = 2, z = 3;
     507           1 :         OGR_G_SetPoints((OGRGeometryH)&ls, 1, &x, 8, &y, 8, &z, 8);
     508           1 :         ASSERT_EQ(ls.getCoordinateDimension(), 3);
     509           1 :         ASSERT_EQ(ls.getX(0), 1);
     510           1 :         ASSERT_EQ(ls.getY(0), 2);
     511           1 :         ASSERT_EQ(ls.getZ(0), 3);
     512             :     }
     513             : 
     514             :     {
     515           2 :         OGRLineString ls;
     516           1 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     517           1 :         OGR_G_SetPoints((OGRGeometryH)&ls, 1, nullptr, 0, nullptr, 0, nullptr,
     518             :                         0);
     519           1 :         CPLPopErrorHandler();
     520             :     }
     521             : }
     522             : 
     523           4 : TEST_F(test_ogr, OGR_G_CreateGeometry_unknown)
     524             : {
     525           1 :     EXPECT_EQ(OGR_G_CreateGeometry(wkbUnknown), nullptr);
     526           1 : }
     527             : 
     528           4 : TEST_F(test_ogr, style_manager)
     529             : {
     530           1 :     OGRStyleMgrH hSM = OGR_SM_Create(nullptr);
     531           1 :     EXPECT_TRUE(OGR_SM_InitStyleString(
     532             :         hSM, "PEN(w:2px,c:#000000,id:\"mapinfo-pen-2,ogr-pen-0\")"));
     533           1 :     OGRStyleToolH hTool = OGR_SM_GetPart(hSM, 0, nullptr);
     534           1 :     EXPECT_TRUE(hTool != nullptr);
     535           1 :     if (hTool)
     536             :     {
     537             :         int bValueIsNull;
     538             : 
     539           1 :         EXPECT_NEAR(OGR_ST_GetParamDbl(hTool, OGRSTPenWidth, &bValueIsNull),
     540             :                     2.0 * (1.0 / (72.0 * 39.37)) * 1000, 1e-6);
     541           1 :         EXPECT_EQ(OGR_ST_GetUnit(hTool), OGRSTUMM);
     542             : 
     543           1 :         OGR_ST_SetUnit(hTool, OGRSTUPixel, 1.0);
     544           1 :         EXPECT_EQ(OGR_ST_GetParamDbl(hTool, OGRSTPenWidth, &bValueIsNull), 2.0);
     545           1 :         EXPECT_EQ(OGR_ST_GetUnit(hTool), OGRSTUPixel);
     546           1 :         OGR_ST_Destroy(hTool);
     547             :     }
     548             : 
     549           1 :     OGR_SM_Destroy(hSM);
     550           1 : }
     551             : 
     552           4 : TEST_F(test_ogr, OGRParseDate)
     553             : {
     554             :     const auto OGRParseDateWrapper =
     555          64 :         [](const char *str, OGRField *psField, int nFlags)
     556             :     {
     557             :         // Putting inside a std::string helps Valgrind and other checkers to
     558             :         // detect out-of-bounds access
     559          64 :         return OGRParseDate(std::string(str).c_str(), psField, nFlags);
     560             :     };
     561             : 
     562             :     OGRField sField;
     563           1 :     EXPECT_EQ(OGRParseDateWrapper("2017/11/31 12:34:56", &sField, 0), TRUE);
     564           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     565           1 :     EXPECT_EQ(sField.Date.Month, 11);
     566           1 :     EXPECT_EQ(sField.Date.Day, 31);
     567           1 :     EXPECT_EQ(sField.Date.Hour, 12);
     568           1 :     EXPECT_EQ(sField.Date.Minute, 34);
     569           1 :     EXPECT_EQ(sField.Date.Second, 56.0f);
     570           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     571             : 
     572           1 :     EXPECT_EQ(OGRParseDateWrapper("2017/11/31 12:34:56+00", &sField, 0), TRUE);
     573           1 :     EXPECT_EQ(sField.Date.TZFlag, 100);
     574             : 
     575           1 :     EXPECT_EQ(OGRParseDateWrapper("2017/11/31 12:34:56+12:00", &sField, 0),
     576             :               TRUE);
     577           1 :     EXPECT_EQ(sField.Date.TZFlag, 100 + 12 * 4);
     578             : 
     579           1 :     EXPECT_EQ(OGRParseDateWrapper("2017/11/31 12:34:56+1200", &sField, 0),
     580             :               TRUE);
     581           1 :     EXPECT_EQ(sField.Date.TZFlag, 100 + 12 * 4);
     582             : 
     583           1 :     EXPECT_EQ(OGRParseDateWrapper("2017/11/31 12:34:56+815", &sField, 0), TRUE);
     584           1 :     EXPECT_EQ(sField.Date.TZFlag, 100 + 8 * 4 + 1);
     585             : 
     586           1 :     EXPECT_EQ(OGRParseDateWrapper("2017/11/31 12:34:56-12:00", &sField, 0),
     587             :               TRUE);
     588           1 :     EXPECT_EQ(sField.Date.TZFlag, 100 - 12 * 4);
     589             : 
     590           1 :     EXPECT_EQ(OGRParseDateWrapper(" 2017/11/31 12:34:56", &sField, 0), TRUE);
     591           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     592             : 
     593           1 :     EXPECT_EQ(OGRParseDateWrapper("2017/11/31 12:34:56.789", &sField, 0), TRUE);
     594           1 :     EXPECT_EQ(sField.Date.Second, 56.789f);
     595             : 
     596             :     // Leap second
     597           1 :     EXPECT_EQ(OGRParseDateWrapper("2017/11/31 12:34:60", &sField, 0), TRUE);
     598           1 :     EXPECT_EQ(sField.Date.Second, 60.0f);
     599             : 
     600           1 :     EXPECT_EQ(OGRParseDateWrapper("2017-11-31T12:34:56", &sField, 0), TRUE);
     601           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     602           1 :     EXPECT_EQ(sField.Date.Month, 11);
     603           1 :     EXPECT_EQ(sField.Date.Day, 31);
     604           1 :     EXPECT_EQ(sField.Date.Hour, 12);
     605           1 :     EXPECT_EQ(sField.Date.Minute, 34);
     606           1 :     EXPECT_EQ(sField.Date.Second, 56.0f);
     607           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     608             : 
     609           1 :     EXPECT_EQ(OGRParseDateWrapper("2017-11-31T12:34:56Z", &sField, 0), TRUE);
     610           1 :     EXPECT_EQ(sField.Date.Second, 56.0f);
     611           1 :     EXPECT_EQ(sField.Date.TZFlag, 100);
     612             : 
     613           1 :     EXPECT_EQ(OGRParseDateWrapper("2017-11-31T12:34:56.789Z", &sField, 0),
     614             :               TRUE);
     615           1 :     EXPECT_EQ(sField.Date.Second, 56.789f);
     616           1 :     EXPECT_EQ(sField.Date.TZFlag, 100);
     617             : 
     618           1 :     EXPECT_EQ(OGRParseDateWrapper("2017-11-31T23:59:59.999999Z", &sField, 0),
     619             :               TRUE);
     620           1 :     EXPECT_EQ(sField.Date.Hour, 23);
     621           1 :     EXPECT_EQ(sField.Date.Minute, 59);
     622           1 :     EXPECT_EQ(sField.Date.Second, 59.999f);
     623             : 
     624           1 :     EXPECT_EQ(OGRParseDateWrapper("2017-11-31", &sField, 0), TRUE);
     625           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     626           1 :     EXPECT_EQ(sField.Date.Month, 11);
     627           1 :     EXPECT_EQ(sField.Date.Day, 31);
     628           1 :     EXPECT_EQ(sField.Date.Hour, 0);
     629           1 :     EXPECT_EQ(sField.Date.Minute, 0);
     630           1 :     EXPECT_EQ(sField.Date.Second, 0.0f);
     631           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     632             : 
     633           1 :     EXPECT_EQ(OGRParseDateWrapper("2017-11-31Z", &sField, 0), TRUE);
     634           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     635           1 :     EXPECT_EQ(sField.Date.Month, 11);
     636           1 :     EXPECT_EQ(sField.Date.Day, 31);
     637           1 :     EXPECT_EQ(sField.Date.Hour, 0);
     638           1 :     EXPECT_EQ(sField.Date.Minute, 0);
     639           1 :     EXPECT_EQ(sField.Date.Second, 0.0f);
     640           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     641             : 
     642           1 :     EXPECT_EQ(OGRParseDateWrapper("12:34", &sField, 0), TRUE);
     643           1 :     EXPECT_EQ(sField.Date.Year, 0);
     644           1 :     EXPECT_EQ(sField.Date.Month, 0);
     645           1 :     EXPECT_EQ(sField.Date.Day, 0);
     646           1 :     EXPECT_EQ(sField.Date.Hour, 12);
     647           1 :     EXPECT_EQ(sField.Date.Minute, 34);
     648           1 :     EXPECT_EQ(sField.Date.Second, 0.0f);
     649           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     650             : 
     651           1 :     EXPECT_EQ(OGRParseDateWrapper("12:34:56", &sField, 0), TRUE);
     652           1 :     EXPECT_EQ(OGRParseDateWrapper("12:34:56.789", &sField, 0), TRUE);
     653             : 
     654           1 :     EXPECT_EQ(OGRParseDateWrapper("T12:34:56", &sField, 0), TRUE);
     655           1 :     EXPECT_EQ(sField.Date.Year, 0);
     656           1 :     EXPECT_EQ(sField.Date.Month, 0);
     657           1 :     EXPECT_EQ(sField.Date.Day, 0);
     658           1 :     EXPECT_EQ(sField.Date.Hour, 12);
     659           1 :     EXPECT_EQ(sField.Date.Minute, 34);
     660           1 :     EXPECT_EQ(sField.Date.Second, 56.0f);
     661           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     662             : 
     663           1 :     EXPECT_EQ(OGRParseDateWrapper("T123456", &sField, 0), TRUE);
     664           1 :     EXPECT_EQ(sField.Date.Year, 0);
     665           1 :     EXPECT_EQ(sField.Date.Month, 0);
     666           1 :     EXPECT_EQ(sField.Date.Day, 0);
     667           1 :     EXPECT_EQ(sField.Date.Hour, 12);
     668           1 :     EXPECT_EQ(sField.Date.Minute, 34);
     669           1 :     EXPECT_EQ(sField.Date.Second, 56.0f);
     670           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     671             : 
     672           1 :     EXPECT_EQ(OGRParseDateWrapper("T123456.789", &sField, 0), TRUE);
     673           1 :     EXPECT_EQ(sField.Date.Year, 0);
     674           1 :     EXPECT_EQ(sField.Date.Month, 0);
     675           1 :     EXPECT_EQ(sField.Date.Day, 0);
     676           1 :     EXPECT_EQ(sField.Date.Hour, 12);
     677           1 :     EXPECT_EQ(sField.Date.Minute, 34);
     678           1 :     EXPECT_EQ(sField.Date.Second, 56.789f);
     679           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     680             : 
     681           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     682           1 :     EXPECT_TRUE(!OGRParseDateWrapper("123456-01-01", &sField, 0));
     683           1 :     CPLPopErrorHandler();
     684           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017", &sField, 0));
     685           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017x-01-01", &sField, 0));
     686           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-1-01", &sField, 0));
     687           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-1", &sField, 0));
     688           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-01x", &sField, 0));
     689           1 :     EXPECT_TRUE(!OGRParseDateWrapper("12:", &sField, 0));
     690           1 :     EXPECT_TRUE(!OGRParseDateWrapper("12:3", &sField, 0));
     691           1 :     EXPECT_TRUE(!OGRParseDateWrapper("1:23", &sField, 0));
     692           1 :     EXPECT_TRUE(!OGRParseDateWrapper("12:34:5", &sField, 0));
     693           1 :     EXPECT_TRUE(!OGRParseDateWrapper("1a:34", &sField, 0));
     694           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-a-31T12:34:56", &sField, 0));
     695           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-00-31T12:34:56", &sField, 0));
     696           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-13-31T12:34:56", &sField, 0));
     697           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-00T12:34:56", &sField, 0));
     698           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-aT12:34:56", &sField, 0));
     699           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-32T12:34:56", &sField, 0));
     700           1 :     EXPECT_TRUE(!OGRParseDateWrapper("a:34:56", &sField, 0));
     701           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-01Ta:34:56", &sField, 0));
     702           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-01T25:34:56", &sField, 0));
     703           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-01T00:a:00", &sField, 0));
     704           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-01T00: 34:56", &sField, 0));
     705           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-01T00:61:00", &sField, 0));
     706           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-01T00:00:61", &sField, 0));
     707           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-01-01T00:00:a", &sField, 0));
     708             : 
     709             :     // Test OGRPARSEDATE_OPTION_LAX
     710           1 :     EXPECT_EQ(OGRParseDateWrapper("2017-1-9", &sField, OGRPARSEDATE_OPTION_LAX),
     711             :               TRUE);
     712           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     713           1 :     EXPECT_EQ(sField.Date.Month, 1);
     714           1 :     EXPECT_EQ(sField.Date.Day, 9);
     715             : 
     716           1 :     EXPECT_EQ(
     717             :         OGRParseDateWrapper("2017-1-31", &sField, OGRPARSEDATE_OPTION_LAX),
     718             :         TRUE);
     719           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     720           1 :     EXPECT_EQ(sField.Date.Month, 1);
     721           1 :     EXPECT_EQ(sField.Date.Day, 31);
     722             : 
     723           1 :     EXPECT_EQ(OGRParseDateWrapper("2017-1-31T1:2:3", &sField,
     724             :                                   OGRPARSEDATE_OPTION_LAX),
     725             :               TRUE);
     726           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     727           1 :     EXPECT_EQ(sField.Date.Month, 1);
     728           1 :     EXPECT_EQ(sField.Date.Day, 31);
     729           1 :     EXPECT_EQ(sField.Date.Hour, 1);
     730           1 :     EXPECT_EQ(sField.Date.Minute, 2);
     731           1 :     EXPECT_EQ(sField.Date.Second, 3.0f);
     732           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     733             : 
     734           1 :     EXPECT_EQ(
     735             :         OGRParseDateWrapper("2017-1-31T1:3", &sField, OGRPARSEDATE_OPTION_LAX),
     736             :         TRUE);
     737           1 :     EXPECT_EQ(sField.Date.Year, 2017);
     738           1 :     EXPECT_EQ(sField.Date.Month, 1);
     739           1 :     EXPECT_EQ(sField.Date.Day, 31);
     740           1 :     EXPECT_EQ(sField.Date.Hour, 1);
     741           1 :     EXPECT_EQ(sField.Date.Minute, 3);
     742           1 :     EXPECT_EQ(sField.Date.Second, 0.0f);
     743           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     744             : 
     745           1 :     EXPECT_EQ(OGRParseDateWrapper("1:3", &sField, OGRPARSEDATE_OPTION_LAX),
     746             :               TRUE);
     747           1 :     EXPECT_EQ(sField.Date.Year, 0);
     748           1 :     EXPECT_EQ(sField.Date.Month, 0);
     749           1 :     EXPECT_EQ(sField.Date.Day, 0);
     750           1 :     EXPECT_EQ(sField.Date.Hour, 1);
     751           1 :     EXPECT_EQ(sField.Date.Minute, 3);
     752           1 :     EXPECT_EQ(sField.Date.Second, 0.0f);
     753           1 :     EXPECT_EQ(sField.Date.TZFlag, 0);
     754             : 
     755           1 :     EXPECT_TRUE(
     756             :         !OGRParseDateWrapper("2017-a-01", &sField, OGRPARSEDATE_OPTION_LAX));
     757           1 :     EXPECT_TRUE(
     758             :         !OGRParseDateWrapper("2017-0-01", &sField, OGRPARSEDATE_OPTION_LAX));
     759           1 :     EXPECT_TRUE(
     760             :         !OGRParseDateWrapper("2017-1", &sField, OGRPARSEDATE_OPTION_LAX));
     761           1 :     EXPECT_TRUE(
     762             :         !OGRParseDateWrapper("2017-1-", &sField, OGRPARSEDATE_OPTION_LAX));
     763           1 :     EXPECT_TRUE(
     764             :         !OGRParseDateWrapper("2017-1-a", &sField, OGRPARSEDATE_OPTION_LAX));
     765           1 :     EXPECT_TRUE(
     766             :         !OGRParseDateWrapper("2017-1-0", &sField, OGRPARSEDATE_OPTION_LAX));
     767           1 :     EXPECT_TRUE(
     768             :         !OGRParseDateWrapper("2017-1-32", &sField, OGRPARSEDATE_OPTION_LAX));
     769           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-1-1Ta:00:00", &sField,
     770             :                                      OGRPARSEDATE_OPTION_LAX));
     771           1 :     EXPECT_TRUE(
     772             :         !OGRParseDateWrapper("2017-1-1T1", &sField, OGRPARSEDATE_OPTION_LAX));
     773           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-1-1T00:a:00", &sField,
     774             :                                      OGRPARSEDATE_OPTION_LAX));
     775           1 :     EXPECT_TRUE(
     776             :         !OGRParseDateWrapper("2017-1-1T1:", &sField, OGRPARSEDATE_OPTION_LAX));
     777           1 :     EXPECT_TRUE(!OGRParseDateWrapper("2017-1-1T00:00:a", &sField,
     778             :                                      OGRPARSEDATE_OPTION_LAX));
     779           1 :     EXPECT_TRUE(!OGRParseDateWrapper("1a:3", &sField, OGRPARSEDATE_OPTION_LAX));
     780           1 : }
     781             : 
     782             : // Test OGRPolygon::IsPointOnSurface()
     783           4 : TEST_F(test_ogr, IsPointOnSurface)
     784             : {
     785           1 :     OGRPolygon oPoly;
     786             : 
     787           1 :     OGRPoint oEmptyPoint;
     788           1 :     ASSERT_TRUE(!oPoly.IsPointOnSurface(&oEmptyPoint));
     789             : 
     790           1 :     OGRPoint oPoint;
     791           1 :     oPoint.setX(1);
     792           1 :     oPoint.setY(1);
     793           1 :     ASSERT_TRUE(!oPoly.IsPointOnSurface(&oPoint));
     794             : 
     795           1 :     const char *pszPoly =
     796             :         "POLYGON((0 0,0 10,10 10,10 0,0 0),(4 4,4 6,6 6,6 4,4 4))";
     797           1 :     oPoly.importFromWkt(&pszPoly);
     798             : 
     799           1 :     ASSERT_TRUE(!oPoly.IsPointOnSurface(&oEmptyPoint));
     800             : 
     801           1 :     ASSERT_EQ(oPoly.IsPointOnSurface(&oPoint), TRUE);
     802             : 
     803           1 :     oPoint.setX(5);
     804           1 :     oPoint.setY(5);
     805           1 :     ASSERT_TRUE(!oPoly.IsPointOnSurface(&oPoint));
     806             : }
     807             : 
     808             : // Test gpb.h
     809           4 : TEST_F(test_ogr, gpb_h)
     810             : {
     811           1 :     ASSERT_EQ(GetVarUIntSize(0), 1);
     812           1 :     ASSERT_EQ(GetVarUIntSize(127), 1);
     813           1 :     ASSERT_EQ(GetVarUIntSize(128), 2);
     814           1 :     ASSERT_EQ(GetVarUIntSize((1 << 14) - 1), 2);
     815           1 :     ASSERT_EQ(GetVarUIntSize(1 << 14), 3);
     816           1 :     ASSERT_EQ(GetVarUIntSize(GUINT64_MAX), 10);
     817             : 
     818           1 :     ASSERT_EQ(GetVarIntSize(0), 1);
     819           1 :     ASSERT_EQ(GetVarIntSize(127), 1);
     820           1 :     ASSERT_EQ(GetVarIntSize(128), 2);
     821           1 :     ASSERT_EQ(GetVarIntSize((1 << 14) - 1), 2);
     822           1 :     ASSERT_EQ(GetVarIntSize(1 << 14), 3);
     823           1 :     ASSERT_EQ(GetVarIntSize(GINT64_MAX), 9);
     824           1 :     ASSERT_EQ(GetVarIntSize(-1), 10);
     825           1 :     ASSERT_EQ(GetVarIntSize(GINT64_MIN), 10);
     826             : 
     827           1 :     ASSERT_EQ(GetVarSIntSize(0), 1);
     828           1 :     ASSERT_EQ(GetVarSIntSize(63), 1);
     829           1 :     ASSERT_EQ(GetVarSIntSize(64), 2);
     830           1 :     ASSERT_EQ(GetVarSIntSize(-1), 1);
     831           1 :     ASSERT_EQ(GetVarSIntSize(-64), 1);
     832           1 :     ASSERT_EQ(GetVarSIntSize(-65), 2);
     833           1 :     ASSERT_EQ(GetVarSIntSize(GINT64_MIN), 10);
     834           1 :     ASSERT_EQ(GetVarSIntSize(GINT64_MAX), 10);
     835             : 
     836           1 :     ASSERT_EQ(GetTextSize(""), 1);
     837           1 :     ASSERT_EQ(GetTextSize(" "), 2);
     838           1 :     ASSERT_EQ(GetTextSize(std::string(" ")), 2);
     839             : 
     840           1 :     GByte abyBuffer[11] = {0};
     841             :     GByte *pabyBuffer;
     842             :     const GByte *pabyBufferRO;
     843             : 
     844           1 :     pabyBuffer = abyBuffer;
     845           1 :     WriteVarUInt(&pabyBuffer, 0);
     846           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 1);
     847           1 :     pabyBufferRO = abyBuffer;
     848           1 :     ASSERT_EQ(ReadVarUInt64(&pabyBufferRO), 0U);
     849             : 
     850           1 :     pabyBuffer = abyBuffer;
     851           1 :     WriteVarUInt(&pabyBuffer, 127);
     852           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 1);
     853           1 :     pabyBufferRO = abyBuffer;
     854           1 :     ASSERT_EQ(ReadVarUInt64(&pabyBufferRO), 127U);
     855             : 
     856           1 :     pabyBuffer = abyBuffer;
     857           1 :     WriteVarUInt(&pabyBuffer, 0xDEADBEEFU);
     858           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 5);
     859           1 :     pabyBufferRO = abyBuffer;
     860           1 :     ASSERT_EQ(ReadVarUInt64(&pabyBufferRO), 0xDEADBEEFU);
     861             : 
     862           1 :     pabyBuffer = abyBuffer;
     863           1 :     WriteVarUInt(&pabyBuffer, GUINT64_MAX);
     864           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 10);
     865           1 :     pabyBufferRO = abyBuffer;
     866           1 :     ASSERT_EQ(ReadVarUInt64(&pabyBufferRO), GUINT64_MAX);
     867             : 
     868           1 :     pabyBuffer = abyBuffer;
     869           1 :     WriteVarInt(&pabyBuffer, GINT64_MAX);
     870           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 9);
     871           1 :     pabyBufferRO = abyBuffer;
     872           1 :     ASSERT_EQ(ReadVarInt64(&pabyBufferRO), GINT64_MAX);
     873             : 
     874           1 :     pabyBuffer = abyBuffer;
     875           1 :     WriteVarInt(&pabyBuffer, -1);
     876           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 10);
     877           1 :     pabyBufferRO = abyBuffer;
     878           1 :     ASSERT_EQ(ReadVarInt64(&pabyBufferRO), -1);
     879             : 
     880           1 :     pabyBuffer = abyBuffer;
     881           1 :     WriteVarInt(&pabyBuffer, GINT64_MIN);
     882           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 10);
     883           1 :     pabyBufferRO = abyBuffer;
     884           1 :     ASSERT_EQ(ReadVarInt64(&pabyBufferRO), GINT64_MIN);
     885             : 
     886           1 :     pabyBuffer = abyBuffer;
     887           1 :     WriteVarSInt(&pabyBuffer, 0);
     888           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 1);
     889             :     {
     890             :         GIntBig nVal;
     891           1 :         pabyBufferRO = abyBuffer;
     892           1 :         READ_VARSINT64(pabyBufferRO, abyBuffer + 10, nVal);
     893           1 :         ASSERT_EQ(nVal, 0);
     894             :     }
     895             : 
     896           1 :     pabyBuffer = abyBuffer;
     897           1 :     WriteVarSInt(&pabyBuffer, 1);
     898           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 1);
     899             :     {
     900             :         GIntBig nVal;
     901           1 :         pabyBufferRO = abyBuffer;
     902           1 :         READ_VARSINT64(pabyBufferRO, abyBuffer + 10, nVal);
     903           1 :         ASSERT_EQ(nVal, 1);
     904             :     }
     905             : 
     906           1 :     pabyBuffer = abyBuffer;
     907           1 :     WriteVarSInt(&pabyBuffer, -1);
     908           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 1);
     909             :     {
     910             :         GIntBig nVal;
     911           1 :         pabyBufferRO = abyBuffer;
     912           1 :         READ_VARSINT64(pabyBufferRO, abyBuffer + 10, nVal);
     913           1 :         ASSERT_EQ(nVal, -1);
     914             :     }
     915             : 
     916           1 :     pabyBuffer = abyBuffer;
     917           1 :     WriteVarSInt(&pabyBuffer, GINT64_MAX);
     918           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 10);
     919             :     {
     920             :         GIntBig nVal;
     921           1 :         pabyBufferRO = abyBuffer;
     922           1 :         READ_VARSINT64(pabyBufferRO, abyBuffer + 10, nVal);
     923           1 :         ASSERT_EQ(nVal, GINT64_MAX);
     924             :     }
     925             : 
     926           1 :     pabyBuffer = abyBuffer;
     927           1 :     WriteVarSInt(&pabyBuffer, GINT64_MIN);
     928           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 10);
     929             :     {
     930             :         GIntBig nVal;
     931           1 :         pabyBufferRO = abyBuffer;
     932           1 :         READ_VARSINT64(pabyBufferRO, abyBuffer + 10, nVal);
     933           1 :         ASSERT_EQ(nVal, GINT64_MIN);
     934             :     }
     935             : 
     936           1 :     pabyBuffer = abyBuffer;
     937           1 :     WriteText(&pabyBuffer, "x");
     938           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 2);
     939           1 :     ASSERT_EQ(abyBuffer[0], 1);
     940           1 :     ASSERT_EQ(abyBuffer[1], 'x');
     941             : 
     942           1 :     pabyBuffer = abyBuffer;
     943           1 :     WriteText(&pabyBuffer, std::string("x"));
     944           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 2);
     945           1 :     ASSERT_EQ(abyBuffer[0], 1);
     946           1 :     ASSERT_EQ(abyBuffer[1], 'x');
     947             : 
     948           1 :     pabyBuffer = abyBuffer;
     949           1 :     WriteFloat32(&pabyBuffer, 1.25f);
     950           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 4);
     951           1 :     pabyBufferRO = abyBuffer;
     952           1 :     ASSERT_EQ(ReadFloat32(&pabyBufferRO, abyBuffer + 4), 1.25f);
     953             : 
     954           1 :     pabyBuffer = abyBuffer;
     955           1 :     WriteFloat64(&pabyBuffer, 1.25);
     956           1 :     ASSERT_EQ(pabyBuffer - abyBuffer, 8);
     957           1 :     pabyBufferRO = abyBuffer;
     958           1 :     ASSERT_EQ(ReadFloat64(&pabyBufferRO, abyBuffer + 8), 1.25);
     959             : }
     960             : 
     961             : // Test OGRGeometry::toXXXXX()
     962           4 : TEST_F(test_ogr, OGRGeometry_toXXXXX)
     963             : {
     964             : #define CONCAT(X, Y) X##Y
     965             : #define TEST_OGRGEOMETRY_TO(X)                                                 \
     966             :     {                                                                          \
     967             :         CONCAT(OGR, X) o;                                                      \
     968             :         OGRGeometry *poGeom = &o;                                              \
     969             :         ASSERT_EQ(poGeom->CONCAT(to, X)(), &o);                                \
     970             :     }
     971             : 
     972           1 :     TEST_OGRGEOMETRY_TO(Point);
     973           1 :     TEST_OGRGEOMETRY_TO(LineString);
     974           1 :     TEST_OGRGEOMETRY_TO(LinearRing);
     975           1 :     TEST_OGRGEOMETRY_TO(CircularString);
     976           1 :     TEST_OGRGEOMETRY_TO(CompoundCurve);
     977           1 :     TEST_OGRGEOMETRY_TO(CurvePolygon);
     978           1 :     TEST_OGRGEOMETRY_TO(Polygon);
     979           1 :     TEST_OGRGEOMETRY_TO(GeometryCollection);
     980           1 :     TEST_OGRGEOMETRY_TO(MultiSurface);
     981           1 :     TEST_OGRGEOMETRY_TO(MultiPolygon);
     982           1 :     TEST_OGRGEOMETRY_TO(MultiPoint);
     983           1 :     TEST_OGRGEOMETRY_TO(MultiCurve);
     984           1 :     TEST_OGRGEOMETRY_TO(MultiLineString);
     985           1 :     TEST_OGRGEOMETRY_TO(Triangle);
     986           1 :     TEST_OGRGEOMETRY_TO(PolyhedralSurface);
     987           1 :     TEST_OGRGEOMETRY_TO(TriangulatedSurface);
     988             :     {
     989           1 :         OGRLineString o;
     990           1 :         OGRGeometry *poGeom = &o;
     991           1 :         ASSERT_EQ(poGeom->toCurve(), &o);
     992             :     }
     993             :     {
     994           1 :         OGRPolygon o;
     995           1 :         OGRGeometry *poGeom = &o;
     996           1 :         ASSERT_EQ(poGeom->toSurface(), &o);
     997             :     }
     998             : 
     999             :     {
    1000           1 :         OGRPoint o;
    1001             :         // ASSERT_EQ(o.toPoint(), &o);
    1002             :     }
    1003             : 
    1004             :     {
    1005           1 :         OGRLineString o;
    1006           1 :         ASSERT_EQ(o.toCurve(), &o);
    1007           1 :         ASSERT_EQ(o.toSimpleCurve(), &o);
    1008             :         // ASSERT_EQ(o.toLineString(), &o);
    1009             : 
    1010             :         {
    1011           1 :             OGRCurve &oRef = o;
    1012           1 :             ASSERT_EQ(oRef.toLineString(), &o);
    1013             :         }
    1014             : 
    1015             :         {
    1016           1 :             OGRSimpleCurve &oRef = o;
    1017           1 :             ASSERT_EQ(oRef.toLineString(), &o);
    1018             :         }
    1019             :     }
    1020             : 
    1021             :     {
    1022           1 :         OGRLinearRing o;
    1023           1 :         ASSERT_EQ(o.toCurve(), &o);
    1024           1 :         ASSERT_EQ(o.toSimpleCurve(), &o);
    1025             :         // ASSERT_EQ(o.toLinearRing(), &o);
    1026             : 
    1027             :         {
    1028           1 :             OGRCurve &oRef = o;
    1029           1 :             ASSERT_EQ(oRef.toLinearRing(), &o);
    1030             :         }
    1031             :         {
    1032           1 :             OGRSimpleCurve &oRef = o;
    1033           1 :             ASSERT_EQ(oRef.toLinearRing(), &o);
    1034             :         }
    1035             :         {
    1036           1 :             OGRLineString &oRef = o;
    1037           1 :             ASSERT_EQ(oRef.toLinearRing(), &o);
    1038             :         }
    1039             :     }
    1040             : 
    1041             :     {
    1042           1 :         OGRCircularString o;
    1043           1 :         ASSERT_EQ(o.toCurve(), &o);
    1044           1 :         ASSERT_EQ(o.toSimpleCurve(), &o);
    1045             :         // ASSERT_EQ(o.toCircularString(), &o);
    1046             : 
    1047             :         {
    1048           1 :             OGRCurve &oRef = o;
    1049           1 :             ASSERT_EQ(oRef.toCircularString(), &o);
    1050             :         }
    1051             : 
    1052             :         {
    1053           1 :             OGRSimpleCurve &oRef = o;
    1054           1 :             ASSERT_EQ(oRef.toCircularString(), &o);
    1055             :         }
    1056             :     }
    1057             : 
    1058             :     {
    1059           1 :         OGRCompoundCurve o;
    1060           1 :         ASSERT_EQ(o.toCurve(), &o);
    1061             :         // ASSERT_EQ(o.toCompoundCurve(), &o);
    1062             : 
    1063             :         {
    1064           1 :             OGRCurve &oRef = o;
    1065           1 :             ASSERT_EQ(oRef.toCompoundCurve(), &o);
    1066             :         }
    1067             :     }
    1068             : 
    1069             :     {
    1070           1 :         OGRCurvePolygon o;
    1071           1 :         ASSERT_EQ(o.toSurface(), &o);
    1072             :         // ASSERT_EQ(o.toCurvePolygon(), &o);
    1073             : 
    1074             :         {
    1075           1 :             OGRSurface &oRef = o;
    1076           1 :             ASSERT_EQ(oRef.toCurvePolygon(), &o);
    1077             :         }
    1078             :     }
    1079             : 
    1080             :     {
    1081           1 :         OGRPolygon o;
    1082           1 :         ASSERT_EQ(o.toSurface(), &o);
    1083           1 :         ASSERT_EQ(o.toCurvePolygon(), &o);
    1084             :         // ASSERT_EQ(o.toPolygon(), &o);
    1085             : 
    1086             :         {
    1087           1 :             OGRSurface &oRef = o;
    1088           1 :             ASSERT_EQ(oRef.toPolygon(), &o);
    1089             :         }
    1090             : 
    1091             :         {
    1092           1 :             OGRCurvePolygon &oRef = o;
    1093           1 :             ASSERT_EQ(oRef.toPolygon(), &o);
    1094             :         }
    1095             :     }
    1096             : 
    1097             :     {
    1098           1 :         OGRTriangle o;
    1099           1 :         ASSERT_EQ(o.toSurface(), &o);
    1100           1 :         ASSERT_EQ(o.toCurvePolygon(), &o);
    1101           1 :         ASSERT_EQ(o.toPolygon(), &o);
    1102             :         // ASSERT_EQ(o.toTriangle(), &o);
    1103             : 
    1104             :         {
    1105           1 :             OGRSurface &oRef = o;
    1106           1 :             ASSERT_EQ(oRef.toTriangle(), &o);
    1107             :         }
    1108             : 
    1109             :         {
    1110           1 :             OGRCurvePolygon &oRef = o;
    1111           1 :             ASSERT_EQ(oRef.toTriangle(), &o);
    1112             :         }
    1113             : 
    1114             :         {
    1115           1 :             OGRPolygon &oRef = o;
    1116           1 :             ASSERT_EQ(oRef.toTriangle(), &o);
    1117             :         }
    1118             :     }
    1119             : 
    1120             :     {
    1121           1 :         OGRMultiPoint o;
    1122           1 :         ASSERT_EQ(o.toGeometryCollection(), &o);
    1123             :         // ASSERT_EQ(o.toMultiPoint(), &o);
    1124             : 
    1125             :         {
    1126           1 :             OGRGeometryCollection &oRef = o;
    1127           1 :             ASSERT_EQ(oRef.toMultiPoint(), &o);
    1128             :         }
    1129             :     }
    1130             : 
    1131             :     {
    1132           1 :         OGRMultiCurve o;
    1133           1 :         ASSERT_EQ(o.toGeometryCollection(), &o);
    1134             :         // ASSERT_EQ(o.toMultiCurve(), &o);
    1135             : 
    1136             :         {
    1137           1 :             OGRGeometryCollection &oRef = o;
    1138           1 :             ASSERT_EQ(oRef.toMultiCurve(), &o);
    1139             :         }
    1140             :     }
    1141             : 
    1142             :     {
    1143           1 :         OGRMultiLineString o;
    1144           1 :         ASSERT_EQ(o.toGeometryCollection(), &o);
    1145           1 :         ASSERT_EQ(o.toMultiCurve(), &o);
    1146             :         // ASSERT_EQ(o.toMultiLineString(), &o);
    1147             : 
    1148             :         {
    1149           1 :             OGRMultiCurve &oRef = o;
    1150           1 :             ASSERT_EQ(oRef.toMultiLineString(), &o);
    1151             :         }
    1152             : 
    1153             :         {
    1154           1 :             OGRGeometryCollection &oRef = o;
    1155           1 :             ASSERT_EQ(oRef.toMultiLineString(), &o);
    1156             :         }
    1157             :     }
    1158             : 
    1159             :     {
    1160           1 :         OGRMultiSurface o;
    1161           1 :         ASSERT_EQ(o.toGeometryCollection(), &o);
    1162             :         // ASSERT_EQ(o.toMultiSurface(), &o);
    1163             : 
    1164             :         {
    1165           1 :             OGRGeometryCollection &oRef = o;
    1166           1 :             ASSERT_EQ(oRef.toMultiSurface(), &o);
    1167             :         }
    1168             :     }
    1169             : 
    1170             :     {
    1171           1 :         OGRMultiPolygon o;
    1172           1 :         ASSERT_EQ(o.toGeometryCollection(), &o);
    1173           1 :         ASSERT_EQ(o.toMultiSurface(), &o);
    1174             :         // ASSERT_EQ(o.toMultiPolygon(), &o);
    1175             : 
    1176             :         {
    1177           1 :             OGRMultiSurface &oRef = o;
    1178           1 :             ASSERT_EQ(oRef.toMultiPolygon(), &o);
    1179             :         }
    1180             : 
    1181             :         {
    1182           1 :             OGRGeometryCollection &oRef = o;
    1183           1 :             ASSERT_EQ(oRef.toMultiPolygon(), &o);
    1184             :         }
    1185             :     }
    1186             : 
    1187             :     {
    1188           1 :         OGRPolyhedralSurface o;
    1189           1 :         ASSERT_EQ(o.toSurface(), &o);
    1190             :         // ASSERT_EQ(o.toPolyhedralSurface(), &o);
    1191             : 
    1192             :         {
    1193           1 :             OGRSurface &oRef = o;
    1194           1 :             ASSERT_EQ(oRef.toPolyhedralSurface(), &o);
    1195             :         }
    1196             :     }
    1197             : 
    1198             :     {
    1199           1 :         OGRTriangulatedSurface o;
    1200           1 :         ASSERT_EQ(o.toSurface(), &o);
    1201           1 :         ASSERT_EQ(o.toPolyhedralSurface(), &o);
    1202             :         // ASSERT_EQ(o.toTriangulatedSurface(), &o);
    1203             : 
    1204             :         {
    1205           1 :             OGRSurface &oRef = o;
    1206           1 :             ASSERT_EQ(oRef.toTriangulatedSurface(), &o);
    1207             :         }
    1208             : 
    1209             :         {
    1210           1 :             OGRPolyhedralSurface &oRef = o;
    1211           1 :             ASSERT_EQ(oRef.toTriangulatedSurface(), &o);
    1212             :         }
    1213             :     }
    1214             : }
    1215             : 
    1216          34 : template <typename T> void TestIterator(T *obj, int nExpectedPointCount)
    1217             : {
    1218          34 :     int nCount = 0;
    1219          71 :     for (auto &elt : obj)
    1220             :     {
    1221          25 :         nCount++;
    1222          25 :         CPL_IGNORE_RET_VAL(elt);
    1223             :     }
    1224          34 :     ASSERT_EQ(nCount, nExpectedPointCount);
    1225             : 
    1226          34 :     nCount = 0;
    1227          34 :     const T *const_obj(obj);
    1228          71 :     for (const auto &elt : const_obj)
    1229             :     {
    1230          25 :         nCount++;
    1231          25 :         CPL_IGNORE_RET_VAL(elt);
    1232             :     }
    1233          34 :     ASSERT_EQ(nCount, nExpectedPointCount);
    1234             : }
    1235             : 
    1236             : template <typename Concrete, typename Abstract = Concrete>
    1237          34 : void TestIterator(const char *pszWKT = nullptr, int nExpectedPointCount = 0)
    1238             : {
    1239          68 :     Concrete obj;
    1240          34 :     if (pszWKT)
    1241             :     {
    1242          18 :         obj.importFromWkt(&pszWKT);
    1243             :     }
    1244          34 :     TestIterator<Abstract>(&obj, nExpectedPointCount);
    1245          34 : }
    1246             : 
    1247             : // Test geometry visitor
    1248           4 : TEST_F(test_ogr, OGRGeometry_visitor)
    1249             : {
    1250             :     static const struct
    1251             :     {
    1252             :         const char *pszWKT;
    1253             :         int nExpectedPointCount;
    1254             :     } asTests[] = {
    1255             :         {"POINT(0 0)", 1},
    1256             :         {"LINESTRING(0 0)", 1},
    1257             :         {"POLYGON((0 0),(0 0))", 2},
    1258             :         {"MULTIPOINT(0 0)", 1},
    1259             :         {"MULTILINESTRING((0 0))", 1},
    1260             :         {"MULTIPOLYGON(((0 0)))", 1},
    1261             :         {"GEOMETRYCOLLECTION(POINT(0 0))", 1},
    1262             :         {"CIRCULARSTRING(0 0,1 1,0 0)", 3},
    1263             :         {"COMPOUNDCURVE((0 0,1 1))", 2},
    1264             :         {"CURVEPOLYGON((0 0,1 1,1 0,0 0))", 4},
    1265             :         {"MULTICURVE((0 0))", 1},
    1266             :         {"MULTISURFACE(((0 0)))", 1},
    1267             :         {"TRIANGLE((0 0,0 1,1 1,0 0))", 4},
    1268             :         {"POLYHEDRALSURFACE(((0 0,0 1,1 1,0 0)))", 4},
    1269             :         {"TIN(((0 0,0 1,1 1,0 0)))", 4},
    1270             :     };
    1271             : 
    1272             :     class PointCounterVisitor : public OGRDefaultGeometryVisitor
    1273             :     {
    1274             :         int m_nPoints = 0;
    1275             : 
    1276             :       public:
    1277          15 :         PointCounterVisitor()
    1278          15 :         {
    1279          15 :         }
    1280             : 
    1281             :         using OGRDefaultGeometryVisitor::visit;
    1282             : 
    1283          31 :         void visit(OGRPoint *) override
    1284             :         {
    1285          31 :             m_nPoints++;
    1286          31 :         }
    1287             : 
    1288          15 :         int getNumPoints() const
    1289             :         {
    1290          15 :             return m_nPoints;
    1291             :         }
    1292             :     };
    1293             : 
    1294             :     class PointCounterConstVisitor : public OGRDefaultConstGeometryVisitor
    1295             :     {
    1296             :         int m_nPoints = 0;
    1297             : 
    1298             :       public:
    1299          15 :         PointCounterConstVisitor()
    1300          15 :         {
    1301          15 :         }
    1302             : 
    1303             :         using OGRDefaultConstGeometryVisitor::visit;
    1304             : 
    1305          31 :         void visit(const OGRPoint *) override
    1306             :         {
    1307          31 :             m_nPoints++;
    1308          31 :         }
    1309             : 
    1310          15 :         int getNumPoints() const
    1311             :         {
    1312          15 :             return m_nPoints;
    1313             :         }
    1314             :     };
    1315             : 
    1316          16 :     for (size_t i = 0; i < CPL_ARRAYSIZE(asTests); i++)
    1317             :     {
    1318          15 :         OGRGeometry *poGeom = nullptr;
    1319          15 :         OGRGeometryFactory::createFromWkt(asTests[i].pszWKT, nullptr, &poGeom);
    1320          15 :         PointCounterVisitor oVisitor;
    1321          15 :         poGeom->accept(&oVisitor);
    1322          15 :         ASSERT_EQ(oVisitor.getNumPoints(), asTests[i].nExpectedPointCount);
    1323          15 :         PointCounterConstVisitor oConstVisitor;
    1324          15 :         poGeom->accept(&oConstVisitor);
    1325          15 :         ASSERT_EQ(oConstVisitor.getNumPoints(), asTests[i].nExpectedPointCount);
    1326          15 :         delete poGeom;
    1327             :     }
    1328             : 
    1329             :     {
    1330           2 :         OGRLineString ls;
    1331           1 :         ls.setNumPoints(2);
    1332           2 :         auto oIter1 = ls.begin();
    1333           1 :         EXPECT_TRUE(oIter1 != ls.end());
    1334           1 :         EXPECT_TRUE(!(oIter1 != ls.begin()));
    1335           2 :         auto oIter2 = ls.begin();
    1336           1 :         EXPECT_TRUE(!(oIter1 != oIter2));
    1337           1 :         ++oIter2;
    1338           1 :         EXPECT_TRUE(oIter1 != oIter2);
    1339           1 :         ++oIter2;
    1340           1 :         EXPECT_TRUE(oIter1 != oIter2);
    1341             :     }
    1342             : 
    1343             :     {
    1344           2 :         OGRLineString ls;
    1345           1 :         EXPECT_TRUE(!(ls.begin() != ls.end()));
    1346             :     }
    1347             : 
    1348           1 :     TestIterator<OGRLineString>();
    1349           1 :     TestIterator<OGRLineString>("LINESTRING(0 0)", 1);
    1350           1 :     TestIterator<OGRLineString, OGRCurve>("LINESTRING(0 0)", 1);
    1351           1 :     TestIterator<OGRLineString, OGRCurve>();
    1352           1 :     TestIterator<OGRLinearRing>();
    1353           1 :     TestIterator<OGRCircularString>();
    1354           1 :     TestIterator<OGRCircularString>("CIRCULARSTRING(0 0,1 1,0 0)", 3);
    1355           1 :     TestIterator<OGRCircularString, OGRCurve>("CIRCULARSTRING(0 0,1 1,0 0)", 3);
    1356           1 :     TestIterator<OGRCompoundCurve>();
    1357           1 :     TestIterator<OGRCompoundCurve>("COMPOUNDCURVE((0 0,1 1))", 1);
    1358           1 :     TestIterator<OGRCompoundCurve, OGRCurve>(
    1359             :         "COMPOUNDCURVE((0 0,1 1),CIRCULARSTRING(1 1,2 2,3 3))", 4);
    1360           1 :     TestIterator<OGRCompoundCurve>("COMPOUNDCURVE(CIRCULARSTRING EMPTY)", 1);
    1361           1 :     TestIterator<OGRCurvePolygon>();
    1362           1 :     TestIterator<OGRCurvePolygon>("CURVEPOLYGON((0 0,1 1,1 0,0 0))", 1);
    1363           1 :     TestIterator<OGRPolygon>();
    1364           1 :     TestIterator<OGRPolygon>("POLYGON((0 0,1 1,1 0,0 0))", 1);
    1365           1 :     TestIterator<OGRGeometryCollection>();
    1366           1 :     TestIterator<OGRGeometryCollection>("GEOMETRYCOLLECTION(POINT(0 0))", 1);
    1367           1 :     TestIterator<OGRMultiSurface>();
    1368           1 :     TestIterator<OGRMultiSurface>("MULTISURFACE(((0 0)))", 1);
    1369           1 :     TestIterator<OGRMultiPolygon>();
    1370           1 :     TestIterator<OGRMultiPolygon>("MULTIPOLYGON(((0 0)))", 1);
    1371           1 :     TestIterator<OGRMultiPoint>();
    1372           1 :     TestIterator<OGRMultiPoint>("MULTIPOINT(0 0)", 1);
    1373           1 :     TestIterator<OGRMultiCurve>();
    1374           1 :     TestIterator<OGRMultiCurve>("MULTICURVE((0 0))", 1);
    1375           1 :     TestIterator<OGRMultiLineString>();
    1376           1 :     TestIterator<OGRMultiLineString>("MULTILINESTRING((0 0))", 1);
    1377           1 :     TestIterator<OGRTriangle>();
    1378           1 :     TestIterator<OGRTriangle>("TRIANGLE((0 0,0 1,1 1,0 0))", 1);
    1379           1 :     TestIterator<OGRPolyhedralSurface>();
    1380           1 :     TestIterator<OGRPolyhedralSurface>("POLYHEDRALSURFACE(((0 0,0 1,1 1,0 0)))",
    1381             :                                        1);
    1382           1 :     TestIterator<OGRTriangulatedSurface>();
    1383           1 :     TestIterator<OGRTriangulatedSurface>("TIN(((0 0,0 1,1 1,0 0)))", 1);
    1384             : 
    1385             :     // Test that the update of the iterated point of a linestring is
    1386             :     // immediately taken into account
    1387             :     // (https://github.com/OSGeo/gdal/issues/6215)
    1388             :     {
    1389           1 :         OGRLineString oLS;
    1390           1 :         oLS.addPoint(1, 2);
    1391           1 :         oLS.addPoint(3, 4);
    1392           1 :         int i = 0;
    1393           3 :         for (auto &&p : oLS)
    1394             :         {
    1395           2 :             p.setX(i * 10);
    1396           2 :             p.setY(i * 10 + 1);
    1397           2 :             p.setZ(i * 10 + 2);
    1398           2 :             p.setM(i * 10 + 3);
    1399           2 :             ASSERT_EQ(oLS.getX(i), p.getX());
    1400           2 :             ASSERT_EQ(oLS.getY(i), p.getY());
    1401           2 :             ASSERT_EQ(oLS.getZ(i), p.getZ());
    1402           2 :             ASSERT_EQ(oLS.getM(i), p.getM());
    1403           2 :             ++i;
    1404             :         }
    1405             :     }
    1406             : 
    1407             :     {
    1408             :         class PointCounterVisitorAndUpdate : public OGRDefaultGeometryVisitor
    1409             :         {
    1410             :           public:
    1411             :             PointCounterVisitorAndUpdate() = default;
    1412             : 
    1413             :             using OGRDefaultGeometryVisitor::visit;
    1414             : 
    1415           2 :             void visit(OGRPoint *poPoint) override
    1416             :             {
    1417           2 :                 poPoint->setZ(100);
    1418           2 :                 poPoint->setM(1000);
    1419           2 :             }
    1420             :         };
    1421             : 
    1422           1 :         OGRLineString oLS;
    1423           1 :         oLS.addPoint(1, 2);
    1424           1 :         oLS.addPoint(3, 4);
    1425           0 :         PointCounterVisitorAndUpdate oVisitor;
    1426           1 :         oLS.accept(&oVisitor);
    1427             : 
    1428           1 :         ASSERT_EQ(oLS.getZ(0), 100.0);
    1429           1 :         ASSERT_EQ(oLS.getZ(1), 100.0);
    1430           1 :         ASSERT_EQ(oLS.getM(0), 1000.0);
    1431           1 :         ASSERT_EQ(oLS.getM(1), 1000.0);
    1432             :     }
    1433             : }
    1434             : 
    1435             : // Test OGRToOGCGeomType()
    1436           4 : TEST_F(test_ogr, OGRToOGCGeomType)
    1437             : {
    1438           1 :     EXPECT_STREQ(OGRToOGCGeomType(wkbPoint), "POINT");
    1439           1 :     EXPECT_STREQ(OGRToOGCGeomType(wkbPointM), "POINT");
    1440           1 :     EXPECT_STREQ(OGRToOGCGeomType(wkbPoint, /*bCamelCase=*/true), "Point");
    1441           1 :     EXPECT_STREQ(
    1442             :         OGRToOGCGeomType(wkbPoint, /*bCamelCase=*/true, /*bAddZM=*/true),
    1443             :         "Point");
    1444           1 :     EXPECT_STREQ(
    1445             :         OGRToOGCGeomType(wkbPoint25D, /*bCamelCase=*/true, /*bAddZM=*/true),
    1446             :         "PointZ");
    1447           1 :     EXPECT_STREQ(
    1448             :         OGRToOGCGeomType(wkbPointM, /*bCamelCase=*/true, /*bAddZM=*/true),
    1449             :         "PointM");
    1450           1 :     EXPECT_STREQ(
    1451             :         OGRToOGCGeomType(wkbPointZM, /*bCamelCase=*/true, /*bAddZM=*/true),
    1452             :         "PointZM");
    1453           1 :     EXPECT_STREQ(OGRToOGCGeomType(wkbPointZM, /*bCamelCase=*/true,
    1454             :                                   /*bAddZM=*/true, /*bAddSpaceBeforeZM=*/true),
    1455             :                  "Point ZM");
    1456           1 : }
    1457             : 
    1458             : // Test layer, dataset-feature and layer-feature iterators
    1459           4 : TEST_F(test_ogr, DatasetFeature_and_LayerFeature_iterators)
    1460             : {
    1461           1 :     if (!GDALGetDriverByName("ESRI Shapefile"))
    1462             :     {
    1463           0 :         GTEST_SKIP() << "ESRI Shapefile driver missing";
    1464             :     }
    1465             :     else
    1466             :     {
    1467           1 :         std::string file(data_ + SEP + "poly.shp");
    1468             :         GDALDatasetUniquePtr poDS(
    1469           1 :             GDALDataset::Open(file.c_str(), GDAL_OF_VECTOR));
    1470           1 :         ASSERT_TRUE(poDS != nullptr);
    1471             : 
    1472             :         {
    1473           1 :             GIntBig nExpectedFID = 0;
    1474          11 :             for (const auto &oFeatureLayerPair : poDS->GetFeatures())
    1475             :             {
    1476          10 :                 ASSERT_EQ(oFeatureLayerPair.feature->GetFID(), nExpectedFID);
    1477          10 :                 nExpectedFID++;
    1478          10 :                 ASSERT_EQ(oFeatureLayerPair.layer, poDS->GetLayer(0));
    1479             :             }
    1480           1 :             ASSERT_EQ(nExpectedFID, 10);
    1481             :         }
    1482             : 
    1483           1 :         ASSERT_EQ(poDS->GetLayers().size(), 1U);
    1484           1 :         ASSERT_EQ(poDS->GetLayers()[0], poDS->GetLayer(0));
    1485           1 :         ASSERT_EQ(poDS->GetLayers()[static_cast<size_t>(0)], poDS->GetLayer(0));
    1486           1 :         ASSERT_EQ(poDS->GetLayers()["poly"], poDS->GetLayer(0));
    1487             : 
    1488           2 :         for (auto poLayer : poDS->GetLayers())
    1489             :         {
    1490           1 :             GIntBig nExpectedFID = 0;
    1491          11 :             for (const auto &poFeature : poLayer)
    1492             :             {
    1493          10 :                 ASSERT_EQ(poFeature->GetFID(), nExpectedFID);
    1494          10 :                 nExpectedFID++;
    1495             :             }
    1496           1 :             ASSERT_EQ(nExpectedFID, 10);
    1497             : 
    1498           1 :             nExpectedFID = 0;
    1499          11 :             for (const auto &oFeatureLayerPair : poDS->GetFeatures())
    1500             :             {
    1501          10 :                 ASSERT_EQ(oFeatureLayerPair.feature->GetFID(), nExpectedFID);
    1502          10 :                 nExpectedFID++;
    1503          10 :                 ASSERT_EQ(oFeatureLayerPair.layer, poLayer);
    1504             :             }
    1505           1 :             ASSERT_EQ(nExpectedFID, 10);
    1506             : 
    1507           1 :             nExpectedFID = 0;
    1508           5 :             OGR_FOR_EACH_FEATURE_BEGIN(hFeat,
    1509             :                                        reinterpret_cast<OGRLayerH>(poLayer))
    1510             :             {
    1511           5 :                 if (nExpectedFID == 0)
    1512             :                 {
    1513           1 :                     nExpectedFID = 1;
    1514           1 :                     continue;
    1515             :                 }
    1516           4 :                 ASSERT_EQ(OGR_F_GetFID(hFeat), nExpectedFID);
    1517           4 :                 nExpectedFID++;
    1518           4 :                 if (nExpectedFID == 5)
    1519           1 :                     break;
    1520             :             }
    1521           5 :             OGR_FOR_EACH_FEATURE_END(hFeat)
    1522           1 :             ASSERT_EQ(nExpectedFID, 5);
    1523             : 
    1524           1 :             auto oIter = poLayer->begin();
    1525           1 :             CPLPushErrorHandler(CPLQuietErrorHandler);
    1526             :             // Only one feature iterator can be active at a time
    1527           1 :             auto oIter2 = poLayer->begin();
    1528           1 :             CPLPopErrorHandler();
    1529           1 :             ASSERT_TRUE(!(oIter2 != poLayer->end()));
    1530           1 :             ASSERT_TRUE(oIter != poLayer->end());
    1531             :         }
    1532             : 
    1533           1 :         poDS.reset(GetGDALDriverManager()->GetDriverByName("MEM")->Create(
    1534             :             "", 0, 0, 0, GDT_Unknown, nullptr));
    1535           1 :         int nCountLayers = 0;
    1536           1 :         for (auto poLayer : poDS->GetLayers())
    1537             :         {
    1538           0 :             CPL_IGNORE_RET_VAL(poLayer);
    1539           0 :             nCountLayers++;
    1540             :         }
    1541           1 :         ASSERT_EQ(nCountLayers, 0);
    1542             : 
    1543           1 :         poDS->CreateLayer("foo");
    1544           1 :         poDS->CreateLayer("bar", nullptr);
    1545           3 :         for (auto poLayer : poDS->GetLayers())
    1546             :         {
    1547           2 :             if (nCountLayers == 0)
    1548             :             {
    1549           1 :                 EXPECT_STREQ(poLayer->GetName(), "foo")
    1550           0 :                     << "layer " << poLayer->GetName();
    1551             :             }
    1552           1 :             else if (nCountLayers == 1)
    1553             :             {
    1554           1 :                 EXPECT_STREQ(poLayer->GetName(), "bar")
    1555           0 :                     << "layer " << poLayer->GetName();
    1556             :             }
    1557           2 :             nCountLayers++;
    1558             :         }
    1559           1 :         ASSERT_EQ(nCountLayers, 2);
    1560             : 
    1561           1 :         auto layers = poDS->GetLayers();
    1562             :         {
    1563             :             // std::copy requires a InputIterator
    1564           1 :             std::vector<OGRLayer *> oTarget;
    1565           1 :             oTarget.resize(2);
    1566           1 :             std::copy(layers.begin(), layers.end(), oTarget.begin());
    1567           1 :             ASSERT_EQ(oTarget[0], layers[0]);
    1568           1 :             ASSERT_EQ(oTarget[1], layers[1]);
    1569             : 
    1570             :             // but in practice not necessarily uses the postincrement iterator.
    1571           1 :             oTarget.clear();
    1572           1 :             oTarget.resize(2);
    1573           1 :             auto input_iterator = layers.begin();
    1574           1 :             auto output_iterator = oTarget.begin();
    1575           3 :             while (input_iterator != layers.end())
    1576             :             {
    1577           2 :                 *output_iterator++ = *input_iterator++;
    1578             :             }
    1579           1 :             ASSERT_EQ(oTarget[0], layers[0]);
    1580           1 :             ASSERT_EQ(oTarget[1], layers[1]);
    1581             :         }
    1582             : 
    1583             :         // Test copy constructor
    1584             :         {
    1585           1 :             GDALDataset::Layers::Iterator srcIter(poDS->GetLayers().begin());
    1586           1 :             ++srcIter;
    1587           1 :             GDALDataset::Layers::Iterator newIter(srcIter);
    1588           1 :             srcIter = layers.begin();  // avoid Coverity Scan warning
    1589           1 :             ASSERT_EQ(*newIter, layers[1]);
    1590             :         }
    1591             : 
    1592             :         // Test assignment operator
    1593             :         {
    1594           1 :             GDALDataset::Layers::Iterator srcIter(poDS->GetLayers().begin());
    1595           1 :             ++srcIter;
    1596           1 :             GDALDataset::Layers::Iterator newIter;
    1597           1 :             newIter = srcIter;
    1598           1 :             srcIter = layers.begin();  // avoid Coverity Scan warning
    1599           1 :             ASSERT_EQ(*newIter, layers[1]);
    1600             :         }
    1601             : 
    1602             :         // Test move constructor
    1603             :         {
    1604           1 :             GDALDataset::Layers::Iterator srcIter(poDS->GetLayers().begin());
    1605           1 :             ++srcIter;
    1606           1 :             GDALDataset::Layers::Iterator newIter(std::move(srcIter));
    1607           1 :             ASSERT_EQ(*newIter, layers[1]);
    1608             :         }
    1609             : 
    1610             :         // Test move assignment operator
    1611             :         {
    1612           1 :             GDALDataset::Layers::Iterator srcIter(poDS->GetLayers().begin());
    1613           1 :             ++srcIter;
    1614           1 :             GDALDataset::Layers::Iterator newIter;
    1615           1 :             newIter = std::move(srcIter);
    1616           1 :             ASSERT_EQ(*newIter, layers[1]);
    1617             :         }
    1618             : 
    1619           1 :         const GDALDataset *poConstDS = poDS.get();
    1620           1 :         ASSERT_EQ(poConstDS->GetLayers().size(), 2U);
    1621           1 :         ASSERT_EQ(poConstDS->GetLayers()[0], poConstDS->GetLayer(0));
    1622           1 :         ASSERT_EQ(poConstDS->GetLayers()[static_cast<size_t>(0)],
    1623             :                   poConstDS->GetLayer(0));
    1624           1 :         ASSERT_EQ(poConstDS->GetLayers()["foo"], poConstDS->GetLayer(0));
    1625           1 :         nCountLayers = 0;
    1626           3 :         for (auto &&poLayer : poDS->GetLayers())
    1627             :         {
    1628           2 :             if (nCountLayers == 0)
    1629             :             {
    1630           1 :                 EXPECT_STREQ(poLayer->GetName(), "foo")
    1631           0 :                     << "layer " << poLayer->GetName();
    1632             :             }
    1633           1 :             else if (nCountLayers == 1)
    1634             :             {
    1635           1 :                 EXPECT_STREQ(poLayer->GetName(), "bar")
    1636           0 :                     << "layer " << poLayer->GetName();
    1637             :             }
    1638           2 :             nCountLayers++;
    1639             :         }
    1640           1 :         ASSERT_EQ(nCountLayers, 2);
    1641             : 
    1642           1 :         auto constLayers = poConstDS->GetLayers();
    1643             :         {
    1644             :             // std::copy requires a InputIterator
    1645           1 :             std::vector<const OGRLayer *> oTarget;
    1646           1 :             oTarget.resize(2);
    1647           1 :             std::copy(constLayers.begin(), constLayers.end(), oTarget.begin());
    1648           1 :             ASSERT_EQ(oTarget[0], constLayers[0]);
    1649           1 :             ASSERT_EQ(oTarget[1], constLayers[1]);
    1650             : 
    1651             :             // but in practice not necessarily uses the postincrement iterator.
    1652           1 :             oTarget.clear();
    1653           1 :             oTarget.resize(2);
    1654           1 :             auto input_iterator = constLayers.begin();
    1655           1 :             auto output_iterator = oTarget.begin();
    1656           3 :             while (input_iterator != constLayers.end())
    1657             :             {
    1658           2 :                 *output_iterator++ = *input_iterator++;
    1659             :             }
    1660           1 :             ASSERT_EQ(oTarget[0], constLayers[0]);
    1661           1 :             ASSERT_EQ(oTarget[1], constLayers[1]);
    1662             :         }
    1663             : 
    1664             :         // Test copy constructor
    1665             :         {
    1666           1 :             auto srcIter(poConstDS->GetLayers().begin());
    1667           1 :             ++srcIter;
    1668           1 :             auto newIter(srcIter);
    1669           1 :             srcIter = constLayers.begin();  // avoid Coverity Scan warning
    1670           1 :             ASSERT_EQ(*newIter, constLayers[1]);
    1671             :         }
    1672             : 
    1673             :         // Test assignment operator
    1674             :         {
    1675           1 :             auto srcIter(poConstDS->GetLayers().begin());
    1676           1 :             ++srcIter;
    1677           1 :             GDALDataset::ConstLayers::Iterator newIter;
    1678           1 :             newIter = srcIter;
    1679           1 :             srcIter = constLayers.begin();  // avoid Coverity Scan warning
    1680           1 :             ASSERT_EQ(*newIter, constLayers[1]);
    1681             :         }
    1682             : 
    1683             :         // Test move constructor
    1684             :         {
    1685           1 :             auto srcIter(poConstDS->GetLayers().begin());
    1686           1 :             ++srcIter;
    1687           1 :             auto newIter(std::move(srcIter));
    1688           1 :             ASSERT_EQ(*newIter, constLayers[1]);
    1689             :         }
    1690             : 
    1691             :         // Test move assignment operator
    1692             :         {
    1693           1 :             auto srcIter(poConstDS->GetLayers().begin());
    1694           1 :             ++srcIter;
    1695           1 :             GDALDataset::ConstLayers::Iterator newIter;
    1696           1 :             newIter = std::move(srcIter);
    1697           1 :             ASSERT_EQ(*newIter, constLayers[1]);
    1698             :         }
    1699             :     }
    1700             : }
    1701             : 
    1702             : // Test field iterator
    1703           4 : TEST_F(test_ogr, field_iterator)
    1704             : {
    1705           1 :     OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn();
    1706           1 :     poFeatureDefn->Reference();
    1707             :     {
    1708           2 :         OGRFieldDefn oFieldDefn("str_field", OFTString);
    1709           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1710             :     }
    1711             :     {
    1712           2 :         OGRFieldDefn oFieldDefn("int_field", OFTInteger);
    1713           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1714             :     }
    1715             :     {
    1716           2 :         OGRFieldDefn oFieldDefn("int64_field", OFTInteger64);
    1717           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1718             :     }
    1719             :     {
    1720           2 :         OGRFieldDefn oFieldDefn("double_field", OFTReal);
    1721           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1722             :     }
    1723             :     {
    1724           2 :         OGRFieldDefn oFieldDefn("null_field", OFTReal);
    1725           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1726             :     }
    1727             :     {
    1728           2 :         OGRFieldDefn oFieldDefn("unset_field", OFTReal);
    1729           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1730             :     }
    1731             :     {
    1732           2 :         OGRFieldDefn oFieldDefn("dt_field", OFTDateTime);
    1733           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1734             :     }
    1735             :     {
    1736           2 :         OGRFieldDefn oFieldDefn("strlist_field", OFTStringList);
    1737           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1738             :     }
    1739             :     {
    1740           2 :         OGRFieldDefn oFieldDefn("intlist_field", OFTIntegerList);
    1741           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1742             :     }
    1743             :     {
    1744           2 :         OGRFieldDefn oFieldDefn("int64list_field", OFTInteger64List);
    1745           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1746             :     }
    1747             :     {
    1748           2 :         OGRFieldDefn oFieldDefn("doublelist_field", OFTRealList);
    1749           1 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1750             :     }
    1751           1 :     OGRFeature oFeature(poFeatureDefn);
    1752             : 
    1753             :     {
    1754           1 :         OGRFeature oFeatureTmp(poFeatureDefn);
    1755           1 :         oFeatureTmp[0] = "bar";
    1756           2 :         ASSERT_STREQ(oFeatureTmp[0].GetString(), "bar");
    1757             :         {
    1758             :             // Proxy reference
    1759           1 :             auto &&x = oFeatureTmp[0];
    1760           1 :             auto &xRef(x);
    1761           1 :             x = xRef;
    1762           2 :             ASSERT_STREQ(oFeatureTmp[0].GetString(), "bar");
    1763             :         }
    1764             :         {
    1765           1 :             oFeatureTmp[0] = oFeatureTmp[0];
    1766           2 :             ASSERT_STREQ(oFeatureTmp[0].GetString(), "bar");
    1767             :         }
    1768             :         {
    1769             :             // Proxy reference
    1770           1 :             auto &&x = oFeatureTmp[0];
    1771           1 :             x = "baz";
    1772           1 :             ASSERT_STREQ(x.GetString(), "baz");
    1773             :         }
    1774           1 :         oFeatureTmp["str_field"] = std::string("foo");
    1775           1 :         oFeatureTmp["int_field"] = 123;
    1776           1 :         oFeatureTmp["int64_field"] = oFeatureTmp["int_field"];
    1777           1 :         ASSERT_EQ(oFeatureTmp["int64_field"].GetInteger64(), 123);
    1778           1 :         oFeatureTmp["int64_field"] = static_cast<GIntBig>(1234567890123);
    1779           1 :         oFeatureTmp["double_field"] = 123.45;
    1780           1 :         oFeatureTmp["null_field"].SetNull();
    1781           1 :         oFeatureTmp["unset_field"].clear();
    1782           1 :         oFeatureTmp["unset_field"].Unset();
    1783           1 :         oFeatureTmp["dt_field"].SetDateTime(2018, 4, 5, 12, 34, 56.75f, 0);
    1784           1 :         oFeatureTmp["strlist_field"] = CPLStringList().List();
    1785           1 :         oFeatureTmp["strlist_field"] = std::vector<std::string>();
    1786           3 :         oFeatureTmp["strlist_field"] = std::vector<std::string>{"foo", "bar"};
    1787             :         oFeatureTmp["strlist_field"] =
    1788           1 :             static_cast<CSLConstList>(oFeatureTmp["strlist_field"]);
    1789           1 :         ASSERT_EQ(
    1790             :             CSLCount(static_cast<CSLConstList>(oFeatureTmp["strlist_field"])),
    1791             :             2);
    1792           1 :         oFeatureTmp["intlist_field"] = std::vector<int>();
    1793           1 :         oFeatureTmp["intlist_field"] = std::vector<int>{12, 34};
    1794           1 :         oFeatureTmp["int64list_field"] = std::vector<GIntBig>();
    1795             :         oFeatureTmp["int64list_field"] =
    1796           1 :             std::vector<GIntBig>{1234567890123, 34};
    1797           1 :         oFeatureTmp["doublelist_field"] = std::vector<double>();
    1798           1 :         oFeatureTmp["doublelist_field"] = std::vector<double>{12.25, 56.75};
    1799             : 
    1800          12 :         for (const auto &oField : oFeatureTmp)
    1801             :         {
    1802          11 :             oFeature[oField.GetIndex()] = oField;
    1803             :         }
    1804             :     }
    1805             : 
    1806             :     {
    1807           1 :         int x = oFeature[1];
    1808           1 :         ASSERT_EQ(x, 123);
    1809             :     }
    1810             :     {
    1811           1 :         int x = oFeature["int_field"];
    1812           1 :         ASSERT_EQ(x, 123);
    1813             :     }
    1814             :     {
    1815           1 :         GIntBig x = oFeature["int64_field"];
    1816           1 :         ASSERT_EQ(x, static_cast<GIntBig>(1234567890123));
    1817             :     }
    1818             :     {
    1819           1 :         double x = oFeature["double_field"];
    1820           1 :         ASSERT_EQ(x, 123.45);
    1821             :     }
    1822             :     {
    1823           1 :         const char *x = oFeature["str_field"];
    1824           1 :         ASSERT_STREQ(x, "foo");
    1825             :     }
    1826           1 :     bool bExceptionHit = false;
    1827             :     try
    1828             :     {
    1829           1 :         oFeature["inexisting_field"];
    1830             :     }
    1831           1 :     catch (const OGRFeature::FieldNotFoundException &)
    1832             :     {
    1833           1 :         bExceptionHit = true;
    1834             :     }
    1835           1 :     ASSERT_TRUE(bExceptionHit);
    1836             : 
    1837           1 :     int iIter = 0;
    1838           1 :     const OGRFeature *poConstFeature = &oFeature;
    1839          12 :     for (const auto &oField : *poConstFeature)
    1840             :     {
    1841          11 :         ASSERT_EQ(oField.GetIndex(), iIter);
    1842          11 :         ASSERT_EQ(oField.GetDefn(), poFeatureDefn->GetFieldDefn(iIter));
    1843          22 :         ASSERT_EQ(CPLString(oField.GetName()),
    1844             :                   CPLString(oField.GetDefn()->GetNameRef()));
    1845          11 :         ASSERT_EQ(oField.GetType(), oField.GetDefn()->GetType());
    1846          11 :         ASSERT_EQ(oField.GetSubType(), oField.GetDefn()->GetSubType());
    1847          11 :         if (iIter == 0)
    1848             :         {
    1849           1 :             ASSERT_EQ(oField.IsUnset(), false);
    1850           1 :             ASSERT_EQ(oField.IsNull(), false);
    1851           2 :             ASSERT_EQ(CPLString(oField.GetRawValue()->String),
    1852             :                       CPLString("foo"));
    1853           2 :             ASSERT_EQ(CPLString(oField.GetString()), CPLString("foo"));
    1854           2 :             ASSERT_EQ(CPLString(oField.GetAsString()), CPLString("foo"));
    1855             :         }
    1856          10 :         else if (iIter == 1)
    1857             :         {
    1858           1 :             ASSERT_EQ(oField.GetRawValue()->Integer, 123);
    1859           1 :             ASSERT_EQ(oField.GetInteger(), 123);
    1860           1 :             ASSERT_EQ(oField.GetAsInteger(), 123);
    1861           1 :             ASSERT_EQ(oField.GetAsInteger64(), 123);
    1862           1 :             ASSERT_EQ(oField.GetAsDouble(), 123.0);
    1863           2 :             ASSERT_EQ(CPLString(oField.GetAsString()), CPLString("123"));
    1864             :         }
    1865           9 :         else if (iIter == 2)
    1866             :         {
    1867           1 :             ASSERT_EQ(oField.GetRawValue()->Integer64, 1234567890123);
    1868           1 :             ASSERT_EQ(oField.GetInteger64(), 1234567890123);
    1869           1 :             ASSERT_EQ(oField.GetAsInteger(), 2147483647);
    1870           1 :             ASSERT_EQ(oField.GetAsInteger64(), 1234567890123);
    1871           1 :             ASSERT_EQ(oField.GetAsDouble(), 1234567890123.0);
    1872           2 :             ASSERT_EQ(CPLString(oField.GetAsString()),
    1873             :                       CPLString("1234567890123"));
    1874             :         }
    1875           8 :         else if (iIter == 3)
    1876             :         {
    1877           1 :             ASSERT_EQ(oField.GetRawValue()->Real, 123.45);
    1878           1 :             ASSERT_EQ(oField.GetDouble(), 123.45);
    1879           1 :             ASSERT_EQ(oField.GetAsInteger(), 123);
    1880           1 :             ASSERT_EQ(oField.GetAsInteger64(), 123);
    1881           1 :             ASSERT_EQ(oField.GetAsDouble(), 123.45);
    1882           2 :             ASSERT_EQ(CPLString(oField.GetAsString()), CPLString("123.45"));
    1883             :         }
    1884           7 :         else if (iIter == 4)
    1885             :         {
    1886           1 :             ASSERT_EQ(oField.IsUnset(), false);
    1887           1 :             ASSERT_EQ(oField.IsNull(), true);
    1888             :         }
    1889           6 :         else if (iIter == 5)
    1890             :         {
    1891           1 :             ASSERT_EQ(oField.IsUnset(), true);
    1892           1 :             ASSERT_EQ(oField.empty(), true);
    1893           1 :             ASSERT_EQ(oField.IsNull(), false);
    1894             :         }
    1895           5 :         else if (iIter == 6)
    1896             :         {
    1897             :             int nYear, nMonth, nDay, nHour, nMin, nTZFlag;
    1898             :             float fSec;
    1899           1 :             ASSERT_EQ(oField.GetDateTime(&nYear, &nMonth, &nDay, &nHour, &nMin,
    1900             :                                          &fSec, &nTZFlag),
    1901             :                       true);
    1902           1 :             ASSERT_EQ(nYear, 2018);
    1903           1 :             ASSERT_EQ(nMonth, 4);
    1904           1 :             ASSERT_EQ(nDay, 5);
    1905           1 :             ASSERT_EQ(nHour, 12);
    1906           1 :             ASSERT_EQ(nMin, 34);
    1907           1 :             ASSERT_EQ(fSec, 56.75f);
    1908           1 :             ASSERT_EQ(nTZFlag, 0);
    1909             :         }
    1910           4 :         else if (iIter == 7)
    1911             :         {
    1912             :             std::vector<std::string> oExpected{std::string("foo"),
    1913           5 :                                                std::string("bar")};
    1914           1 :             decltype(oExpected) oGot = oField;
    1915           1 :             ASSERT_EQ(oGot.size(), oExpected.size());
    1916           3 :             for (size_t i = 0; i < oExpected.size(); i++)
    1917           2 :                 ASSERT_EQ(oGot[i], oExpected[i]);
    1918             :         }
    1919           3 :         else if (iIter == 8)
    1920             :         {
    1921           1 :             std::vector<int> oExpected{12, 34};
    1922           1 :             decltype(oExpected) oGot = oField;
    1923           1 :             ASSERT_EQ(oGot.size(), oExpected.size());
    1924           3 :             for (size_t i = 0; i < oExpected.size(); i++)
    1925           2 :                 ASSERT_EQ(oGot[i], oExpected[i]);
    1926             :         }
    1927           2 :         else if (iIter == 9)
    1928             :         {
    1929           1 :             std::vector<GIntBig> oExpected{1234567890123, 34};
    1930           1 :             decltype(oExpected) oGot = oField;
    1931           1 :             ASSERT_EQ(oGot.size(), oExpected.size());
    1932           3 :             for (size_t i = 0; i < oExpected.size(); i++)
    1933           2 :                 ASSERT_EQ(oGot[i], oExpected[i]);
    1934             :         }
    1935           1 :         else if (iIter == 10)
    1936             :         {
    1937           1 :             std::vector<double> oExpected{12.25, 56.75};
    1938           1 :             decltype(oExpected) oGot = oField;
    1939           1 :             ASSERT_EQ(oGot.size(), oExpected.size());
    1940           3 :             for (size_t i = 0; i < oExpected.size(); i++)
    1941           2 :                 ASSERT_EQ(oGot[i], oExpected[i]);
    1942             :         }
    1943          11 :         iIter++;
    1944             :     }
    1945           1 :     poFeatureDefn->Release();
    1946             : }
    1947             : 
    1948             : // Test OGRLinearRing::isPointOnRingBoundary()
    1949           4 : TEST_F(test_ogr, isPointOnRingBoundary)
    1950             : {
    1951           1 :     OGRPolygon oPoly;
    1952           1 :     const char *pszPoly = "POLYGON((10 9,11 10,10 11,9 10,10 9))";
    1953           1 :     oPoly.importFromWkt(&pszPoly);
    1954           1 :     auto poRing = oPoly.getExteriorRing();
    1955             : 
    1956             :     // On first vertex
    1957             :     {
    1958           1 :         OGRPoint p(10, 9);
    1959           1 :         ASSERT_TRUE(poRing->isPointOnRingBoundary(&p, false));
    1960             :     }
    1961             : 
    1962             :     // On second vertex
    1963             :     {
    1964           1 :         OGRPoint p(11, 10);
    1965           1 :         ASSERT_TRUE(poRing->isPointOnRingBoundary(&p, false));
    1966             :     }
    1967             : 
    1968             :     // Middle of first segment
    1969             :     {
    1970           1 :         OGRPoint p(10.5, 9.5);
    1971           1 :         ASSERT_TRUE(poRing->isPointOnRingBoundary(&p, false));
    1972             :     }
    1973             : 
    1974             :     // "Before" first segment
    1975             :     {
    1976           1 :         OGRPoint p(10 - 1, 9 - 1);
    1977           1 :         ASSERT_TRUE(!poRing->isPointOnRingBoundary(&p, false));
    1978             :     }
    1979             : 
    1980             :     // "After" first segment
    1981             :     {
    1982           1 :         OGRPoint p(11 + 1, 10 + 1);
    1983           1 :         ASSERT_TRUE(!poRing->isPointOnRingBoundary(&p, false));
    1984             :     }
    1985             : 
    1986             :     // On third vertex
    1987             :     {
    1988           1 :         OGRPoint p(10, 11);
    1989           1 :         ASSERT_TRUE(poRing->isPointOnRingBoundary(&p, false));
    1990             :     }
    1991             : 
    1992             :     // Middle of second segment
    1993             :     {
    1994           1 :         OGRPoint p(10.5, 10.5);
    1995           1 :         ASSERT_TRUE(poRing->isPointOnRingBoundary(&p, false));
    1996             :     }
    1997             : 
    1998             :     // On fourth vertex
    1999             :     {
    2000           1 :         OGRPoint p(9, 10);
    2001           1 :         ASSERT_TRUE(poRing->isPointOnRingBoundary(&p, false));
    2002             :     }
    2003             : 
    2004             :     // Middle of third segment
    2005             :     {
    2006           1 :         OGRPoint p(9.5, 10.5);
    2007           1 :         ASSERT_TRUE(poRing->isPointOnRingBoundary(&p, false));
    2008             :     }
    2009             : 
    2010             :     // Middle of fourth segment
    2011             :     {
    2012           1 :         OGRPoint p(9.5, 9.5);
    2013           1 :         ASSERT_TRUE(poRing->isPointOnRingBoundary(&p, false));
    2014             :     }
    2015             : }
    2016             : 
    2017             : // Test OGRGeometry::exportToWkt()
    2018           4 : TEST_F(test_ogr, OGRGeometry_exportToWkt)
    2019             : {
    2020           1 :     char *pszWKT = nullptr;
    2021           1 :     OGRPoint p(1, 2);
    2022           1 :     p.exportToWkt(&pszWKT);
    2023           1 :     ASSERT_TRUE(pszWKT != nullptr);
    2024           1 :     EXPECT_STREQ(pszWKT, "POINT (1 2)");
    2025           1 :     CPLFree(pszWKT);
    2026             : }
    2027             : 
    2028             : // Test that rounding in OGRGeometry::exportToWkt() never turns a non-zero
    2029             : // coordinate that fits in the default 15 decimals into 0 (#15317)
    2030           4 : TEST_F(test_ogr, OGRGeometry_exportToWkt_small_values_not_zeroed)
    2031             : {
    2032             :     const struct
    2033             :     {
    2034             :         double dfX;
    2035             :         const char *pszExpected;
    2036           1 :     } asTests[] = {
    2037             :         {1e-15, "POINT (0.000000000000001 1.0)"},
    2038             :         {5e-15, "POINT (0.000000000000005 1.0)"},
    2039             :         {1e-14, "POINT (0.00000000000001 1.0)"},
    2040             :         {-1e-14, "POINT (-0.00000000000001 1.0)"},
    2041             :         {1.5e-14, "POINT (0.000000000000015 1.0)"},
    2042             :         {9.9e-14, "POINT (0.000000000000099 1.0)"},
    2043             :         // Unchanged behavior
    2044             :         {1e-13, "POINT (0.0000000000001 1.0)"},
    2045             :         {1e-16, "POINT (0.0 1.0)"},
    2046             :         {0.12345000000001, "POINT (0.12345 1.0)"},
    2047             :     };
    2048             : 
    2049          10 :     for (const auto &sTest : asTests)
    2050             :     {
    2051           9 :         char *pszWKT = nullptr;
    2052           9 :         OGRPoint p(sTest.dfX, 1);
    2053           9 :         p.exportToWkt(&pszWKT);
    2054           9 :         ASSERT_TRUE(pszWKT != nullptr);
    2055           9 :         EXPECT_STREQ(pszWKT, sTest.pszExpected);
    2056           9 :         CPLFree(pszWKT);
    2057             :     }
    2058             : }
    2059             : 
    2060             : // Test OGRGeometry::clone()
    2061           4 : TEST_F(test_ogr, OGRGeometry_clone)
    2062             : {
    2063           1 :     const char *apszWKT[] = {
    2064             :         "POINT (0 0)",
    2065             :         "POINT ZM EMPTY",
    2066             :         "LINESTRING (0 0)",
    2067             :         "LINESTRING ZM EMPTY",
    2068             :         "POLYGON ((0 0),(0 0))",
    2069             :         "MULTIPOLYGON ZM EMPTY",
    2070             :         "MULTIPOINT ((0 0))",
    2071             :         "MULTIPOINT ZM EMPTY",
    2072             :         "MULTILINESTRING ((0 0))",
    2073             :         "MULTILINESTRING ZM EMPTY",
    2074             :         "MULTIPOLYGON (((0 0)))",
    2075             :         "MULTIPOLYGON ZM EMPTY",
    2076             :         "GEOMETRYCOLLECTION (POINT (0 0))",
    2077             :         "GEOMETRYCOLLECTION ZM EMPTY",
    2078             :         "CIRCULARSTRING (0 0,1 1,0 0)",
    2079             :         "CIRCULARSTRING Z EMPTY",
    2080             :         "CIRCULARSTRING ZM EMPTY",
    2081             :         "COMPOUNDCURVE ((0 0,1 1))",
    2082             :         "COMPOUNDCURVE ZM EMPTY",
    2083             :         "CURVEPOLYGON ((0 0,1 1,1 0,0 0))",
    2084             :         "CURVEPOLYGON ZM EMPTY",
    2085             :         "MULTICURVE ((0 0))",
    2086             :         "MULTICURVE ZM EMPTY",
    2087             :         "MULTISURFACE (((0 0)))",
    2088             :         "MULTISURFACE ZM EMPTY",
    2089             :         "TRIANGLE ((0 0,0 1,1 1,0 0))",
    2090             :         "TRIANGLE ZM EMPTY",
    2091             :         "POLYHEDRALSURFACE (((0 0,0 1,1 1,0 0)))",
    2092             :         "POLYHEDRALSURFACE ZM EMPTY",
    2093             :         "TIN (((0 0,0 1,1 1,0 0)))",
    2094             :         "TIN ZM EMPTY",
    2095             :     };
    2096           1 :     OGRSpatialReference oSRS;
    2097          32 :     for (const char *pszWKT : apszWKT)
    2098             :     {
    2099          31 :         OGRGeometry *poGeom = nullptr;
    2100          31 :         OGRGeometryFactory::createFromWkt(pszWKT, &oSRS, &poGeom);
    2101          31 :         auto poClone = poGeom->clone();
    2102          31 :         ASSERT_TRUE(poClone != nullptr);
    2103          31 :         char *outWKT = nullptr;
    2104          31 :         poClone->exportToWkt(&outWKT, wkbVariantIso);
    2105          31 :         EXPECT_STREQ(pszWKT, outWKT);
    2106          31 :         CPLFree(outWKT);
    2107          31 :         delete poClone;
    2108          31 :         delete poGeom;
    2109             :     }
    2110             : }
    2111             : 
    2112             : // Test OGRLineString::removePoint()
    2113           4 : TEST_F(test_ogr, OGRLineString_removePoint)
    2114             : {
    2115             :     {
    2116           1 :         OGRLineString ls;
    2117           1 :         ls.addPoint(0, 1);
    2118           1 :         ls.addPoint(2, 3);
    2119           1 :         ls.addPoint(4, 5);
    2120           1 :         ASSERT_TRUE(!ls.removePoint(-1));
    2121           1 :         ASSERT_TRUE(!ls.removePoint(3));
    2122           1 :         ASSERT_EQ(ls.getNumPoints(), 3);
    2123           1 :         ASSERT_TRUE(ls.removePoint(1));
    2124           1 :         ASSERT_EQ(ls.getNumPoints(), 2);
    2125           1 :         ASSERT_EQ(ls.getX(0), 0.0);
    2126           1 :         ASSERT_EQ(ls.getY(0), 1.0);
    2127           1 :         ASSERT_EQ(ls.getX(1), 4.0);
    2128           1 :         ASSERT_EQ(ls.getY(1), 5.0);
    2129           1 :         ASSERT_TRUE(ls.removePoint(1));
    2130           1 :         ASSERT_EQ(ls.getNumPoints(), 1);
    2131           1 :         ASSERT_TRUE(ls.removePoint(0));
    2132           1 :         ASSERT_EQ(ls.getNumPoints(), 0);
    2133             :     }
    2134             :     {
    2135             :         // With Z, M
    2136           1 :         OGRLineString ls;
    2137           1 :         ls.addPoint(0, 1, 20, 30);
    2138           1 :         ls.addPoint(2, 3, 40, 50);
    2139           1 :         ls.addPoint(4, 5, 60, 70);
    2140           1 :         ASSERT_TRUE(!ls.removePoint(-1));
    2141           1 :         ASSERT_TRUE(!ls.removePoint(3));
    2142           1 :         ASSERT_EQ(ls.getNumPoints(), 3);
    2143           1 :         ASSERT_TRUE(ls.removePoint(1));
    2144           1 :         ASSERT_EQ(ls.getNumPoints(), 2);
    2145           1 :         ASSERT_EQ(ls.getX(0), 0.0);
    2146           1 :         ASSERT_EQ(ls.getY(0), 1.0);
    2147           1 :         ASSERT_EQ(ls.getZ(0), 20.0);
    2148           1 :         ASSERT_EQ(ls.getM(0), 30.0);
    2149           1 :         ASSERT_EQ(ls.getX(1), 4.0);
    2150           1 :         ASSERT_EQ(ls.getY(1), 5.0);
    2151           1 :         ASSERT_EQ(ls.getZ(1), 60.0);
    2152           1 :         ASSERT_EQ(ls.getM(1), 70.0);
    2153           1 :         ASSERT_TRUE(ls.removePoint(1));
    2154           1 :         ASSERT_EQ(ls.getNumPoints(), 1);
    2155           1 :         ASSERT_TRUE(ls.removePoint(0));
    2156           1 :         ASSERT_EQ(ls.getNumPoints(), 0);
    2157             :     }
    2158             : }
    2159             : 
    2160             : // Test effect of MarkSuppressOnClose() on DXF
    2161           4 : TEST_F(test_ogr, DXF_MarkSuppressOnClose)
    2162             : {
    2163           1 :     CPLString tmpFilename(CPLGenerateTempFilename(nullptr));
    2164           1 :     tmpFilename += ".dxf";
    2165           1 :     auto poDrv = GDALDriver::FromHandle(GDALGetDriverByName("DXF"));
    2166           1 :     if (poDrv)
    2167             :     {
    2168             :         auto poDS(GDALDatasetUniquePtr(
    2169           1 :             poDrv->Create(tmpFilename, 0, 0, 0, GDT_Unknown, nullptr)));
    2170           1 :         ASSERT_TRUE(poDS != nullptr);
    2171             : 
    2172             :         OGRLayer *poLayer =
    2173           1 :             poDS->CreateLayer("test", nullptr, wkbPoint, nullptr);
    2174           1 :         ASSERT_TRUE(poLayer != nullptr);
    2175             : 
    2176         101 :         for (double x = 0; x < 100; x++)
    2177             :         {
    2178             :             OGRFeature *poFeature =
    2179         100 :                 OGRFeature::CreateFeature(poLayer->GetLayerDefn());
    2180         100 :             ASSERT_TRUE(poFeature != nullptr);
    2181         100 :             OGRPoint pt(x, 42);
    2182         100 :             ASSERT_EQ(OGRERR_NONE, poFeature->SetGeometry(&pt));
    2183         100 :             ASSERT_EQ(OGRERR_NONE, poLayer->CreateFeature(poFeature));
    2184         100 :             OGRFeature::DestroyFeature(poFeature);
    2185             :         }
    2186             : 
    2187           1 :         poDS->MarkSuppressOnClose();
    2188             : 
    2189           1 :         poDS.reset();
    2190             :         VSIStatBufL sStat;
    2191           1 :         ASSERT_TRUE(0 != VSIStatL(tmpFilename, &sStat));
    2192             :     }
    2193             : }
    2194             : 
    2195             : // Test OGREnvelope
    2196           4 : TEST_F(test_ogr, OGREnvelope)
    2197             : {
    2198           1 :     OGREnvelope s1;
    2199           1 :     ASSERT_TRUE(!s1.IsInit());
    2200             :     {
    2201           1 :         OGREnvelope s2(s1);
    2202           1 :         ASSERT_TRUE(s1 == s2);
    2203           1 :         ASSERT_TRUE(!(s1 != s2));
    2204             :     }
    2205             : 
    2206           1 :     s1.MinX = 0;
    2207           1 :     s1.MinY = 1;
    2208           1 :     s1.MaxX = 2;
    2209           1 :     s1.MaxY = 3;
    2210           1 :     ASSERT_TRUE(s1.IsInit());
    2211             :     {
    2212           1 :         OGREnvelope s2(s1);
    2213           1 :         ASSERT_TRUE(s1 == s2);
    2214           1 :         ASSERT_TRUE(!(s1 != s2));
    2215           1 :         s2.MinX += 1;
    2216           1 :         ASSERT_TRUE(s1 != s2);
    2217           1 :         ASSERT_TRUE(!(s1 == s2));
    2218             :     }
    2219             : }
    2220             : 
    2221             : // Test OGREnvelope3D
    2222           4 : TEST_F(test_ogr, OGREnvelope3D)
    2223             : {
    2224           1 :     OGREnvelope3D s1;
    2225           1 :     EXPECT_TRUE(!s1.IsInit());
    2226             :     {
    2227           1 :         OGREnvelope3D s2(s1);
    2228           1 :         EXPECT_TRUE(s1 == s2);
    2229           1 :         EXPECT_TRUE(!(s1 != s2));
    2230             :     }
    2231             : 
    2232           1 :     s1.MinX = 0;
    2233           1 :     s1.MinY = 1;
    2234           1 :     s1.MaxX = 2;
    2235           1 :     s1.MaxY = 3;
    2236           1 :     EXPECT_TRUE(s1.IsInit());
    2237           1 :     EXPECT_FALSE(s1.Is3D());
    2238           1 :     s1.MinZ = 4;
    2239           1 :     s1.MaxZ = 5;
    2240           1 :     EXPECT_TRUE(s1.Is3D());
    2241             :     {
    2242           1 :         OGREnvelope3D s2(s1);
    2243           1 :         EXPECT_TRUE(s1 == s2);
    2244           1 :         EXPECT_TRUE(!(s1 != s2));
    2245           1 :         s2.MinX += 1;
    2246           1 :         EXPECT_TRUE(s1 != s2);
    2247           1 :         EXPECT_TRUE(!(s1 == s2));
    2248             :     }
    2249           1 : }
    2250             : 
    2251             : // Test OGRStyleMgr::InitStyleString() with a style name
    2252             : // (https://github.com/OSGeo/gdal/issues/5555)
    2253           4 : TEST_F(test_ogr, InitStyleString_with_style_name)
    2254             : {
    2255           1 :     OGRStyleTableH hStyleTable = OGR_STBL_Create();
    2256           1 :     OGR_STBL_AddStyle(hStyleTable, "@my_style", "PEN(c:#FF0000,w:5px)");
    2257           1 :     OGRStyleMgrH hSM = OGR_SM_Create(hStyleTable);
    2258           1 :     EXPECT_EQ(OGR_SM_GetPartCount(hSM, nullptr), 0);
    2259           1 :     EXPECT_TRUE(OGR_SM_InitStyleString(hSM, "@my_style"));
    2260           1 :     EXPECT_EQ(OGR_SM_GetPartCount(hSM, nullptr), 1);
    2261           1 :     EXPECT_TRUE(!OGR_SM_InitStyleString(hSM, "@i_do_not_exist"));
    2262           1 :     OGR_SM_Destroy(hSM);
    2263           1 :     OGR_STBL_Destroy(hStyleTable);
    2264           1 : }
    2265             : 
    2266             : // Test OGR_L_GetArrowStream
    2267           4 : TEST_F(test_ogr, OGR_L_GetArrowStream)
    2268             : {
    2269             :     auto poDS = std::unique_ptr<GDALDataset>(
    2270             :         GetGDALDriverManager()->GetDriverByName("MEM")->Create(
    2271           1 :             "", 0, 0, 0, GDT_Unknown, nullptr));
    2272           1 :     auto poLayer = poDS->CreateLayer("test");
    2273             :     {
    2274           2 :         OGRFieldDefn oFieldDefn("str", OFTString);
    2275           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2276             :     }
    2277             :     {
    2278           2 :         OGRFieldDefn oFieldDefn("bool", OFTInteger);
    2279           1 :         oFieldDefn.SetSubType(OFSTBoolean);
    2280           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2281             :     }
    2282             :     {
    2283           2 :         OGRFieldDefn oFieldDefn("int16", OFTInteger);
    2284           1 :         oFieldDefn.SetSubType(OFSTInt16);
    2285           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2286             :     }
    2287             :     {
    2288           2 :         OGRFieldDefn oFieldDefn("int32", OFTInteger);
    2289           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2290             :     }
    2291             :     {
    2292           2 :         OGRFieldDefn oFieldDefn("int64", OFTInteger64);
    2293           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2294             :     }
    2295             :     {
    2296           2 :         OGRFieldDefn oFieldDefn("float32", OFTReal);
    2297           1 :         oFieldDefn.SetSubType(OFSTFloat32);
    2298           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2299             :     }
    2300             :     {
    2301           2 :         OGRFieldDefn oFieldDefn("float64", OFTReal);
    2302           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2303             :     }
    2304             :     {
    2305           2 :         OGRFieldDefn oFieldDefn("date", OFTDate);
    2306           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2307             :     }
    2308             :     {
    2309           2 :         OGRFieldDefn oFieldDefn("time", OFTTime);
    2310           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2311             :     }
    2312             :     {
    2313           2 :         OGRFieldDefn oFieldDefn("datetime", OFTDateTime);
    2314           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2315             :     }
    2316             :     {
    2317           2 :         OGRFieldDefn oFieldDefn("binary", OFTBinary);
    2318           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2319             :     }
    2320             :     {
    2321           2 :         OGRFieldDefn oFieldDefn("strlist", OFTStringList);
    2322           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2323             :     }
    2324             :     {
    2325           2 :         OGRFieldDefn oFieldDefn("boollist", OFTIntegerList);
    2326           1 :         oFieldDefn.SetSubType(OFSTBoolean);
    2327           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2328             :     }
    2329             :     {
    2330           2 :         OGRFieldDefn oFieldDefn("int16list", OFTIntegerList);
    2331           1 :         oFieldDefn.SetSubType(OFSTInt16);
    2332           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2333             :     }
    2334             :     {
    2335           2 :         OGRFieldDefn oFieldDefn("int32list", OFTIntegerList);
    2336           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2337             :     }
    2338             :     {
    2339           2 :         OGRFieldDefn oFieldDefn("int64list", OFTInteger64List);
    2340           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2341             :     }
    2342             :     {
    2343           2 :         OGRFieldDefn oFieldDefn("float32list", OFTRealList);
    2344           1 :         oFieldDefn.SetSubType(OFSTFloat32);
    2345           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2346             :     }
    2347             :     {
    2348           2 :         OGRFieldDefn oFieldDefn("float64list", OFTRealList);
    2349           1 :         EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    2350             :     }
    2351           1 :     auto poFDefn = poLayer->GetLayerDefn();
    2352             :     struct ArrowArrayStream stream;
    2353           1 :     ASSERT_TRUE(
    2354             :         OGR_L_GetArrowStream(OGRLayer::ToHandle(poLayer), &stream, nullptr));
    2355             :     {
    2356             :         // Cannot start a new stream while one is active
    2357             :         struct ArrowArrayStream stream2;
    2358           1 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2359           1 :         ASSERT_TRUE(OGR_L_GetArrowStream(OGRLayer::ToHandle(poLayer), &stream2,
    2360             :                                          nullptr) == false);
    2361           1 :         CPLPopErrorHandler();
    2362             :     }
    2363           1 :     ASSERT_TRUE(stream.release != nullptr);
    2364             : 
    2365             :     struct ArrowSchema schema;
    2366           1 :     CPLErrorReset();
    2367           1 :     ASSERT_TRUE(stream.get_last_error(&stream) == nullptr);
    2368           1 :     ASSERT_EQ(stream.get_schema(&stream, &schema), 0);
    2369           1 :     ASSERT_TRUE(stream.get_last_error(&stream) == nullptr);
    2370           1 :     ASSERT_TRUE(schema.release != nullptr);
    2371           1 :     ASSERT_EQ(schema.n_children,
    2372             :               1 + poFDefn->GetFieldCount() + poFDefn->GetGeomFieldCount());
    2373           1 :     schema.release(&schema);
    2374             : 
    2375             :     struct ArrowArray array;
    2376             :     // Next batch ==> End of stream
    2377           1 :     ASSERT_EQ(stream.get_next(&stream, &array), 0);
    2378           1 :     ASSERT_TRUE(array.release == nullptr);
    2379             : 
    2380             :     // Release stream
    2381           1 :     stream.release(&stream);
    2382             : 
    2383             :     {
    2384           1 :         auto poFeature = std::unique_ptr<OGRFeature>(new OGRFeature(poFDefn));
    2385           1 :         poFeature->SetField("bool", 1);
    2386           1 :         poFeature->SetField("int16", -12345);
    2387           1 :         poFeature->SetField("int32", 12345678);
    2388           1 :         poFeature->SetField("int64", static_cast<GIntBig>(12345678901234));
    2389           1 :         poFeature->SetField("float32", 1.25);
    2390           1 :         poFeature->SetField("float64", 1.250123);
    2391           1 :         poFeature->SetField("str", "abc");
    2392           1 :         poFeature->SetField("date", "2022-05-31");
    2393           1 :         poFeature->SetField("time", "12:34:56.789");
    2394           1 :         poFeature->SetField("datetime", "2022-05-31T12:34:56.789Z");
    2395           1 :         poFeature->SetField("boollist", "[False,True]");
    2396           1 :         poFeature->SetField("int16list", "[-12345,12345]");
    2397           1 :         poFeature->SetField("int32list", "[-12345678,12345678]");
    2398           1 :         poFeature->SetField("int64list", "[-12345678901234,12345678901234]");
    2399           1 :         poFeature->SetField("float32list", "[-1.25,1.25]");
    2400           1 :         poFeature->SetField("float64list", "[-1.250123,1.250123]");
    2401           1 :         poFeature->SetField("strlist", "[\"abc\",\"defghi\"]");
    2402           1 :         poFeature->SetField(poFDefn->GetFieldIndex("binary"), 2, "\xDE\xAD");
    2403           1 :         OGRGeometry *poGeom = nullptr;
    2404           1 :         OGRGeometryFactory::createFromWkt("POINT(1 2)", nullptr, &poGeom);
    2405           1 :         poFeature->SetGeometryDirectly(poGeom);
    2406           1 :         ASSERT_EQ(poLayer->CreateFeature(poFeature.get()), OGRERR_NONE);
    2407             :     }
    2408             : 
    2409             :     // Get a new stream now that we've released it
    2410           1 :     ASSERT_TRUE(
    2411             :         OGR_L_GetArrowStream(OGRLayer::ToHandle(poLayer), &stream, nullptr));
    2412           1 :     ASSERT_TRUE(stream.release != nullptr);
    2413             : 
    2414           1 :     ASSERT_EQ(stream.get_next(&stream, &array), 0);
    2415           1 :     ASSERT_TRUE(array.release != nullptr);
    2416           1 :     ASSERT_EQ(array.n_children,
    2417             :               1 + poFDefn->GetFieldCount() + poFDefn->GetGeomFieldCount());
    2418           1 :     ASSERT_EQ(array.length, poLayer->GetFeatureCount(false));
    2419           1 :     ASSERT_EQ(array.null_count, 0);
    2420           1 :     ASSERT_EQ(array.n_buffers, 1);
    2421           1 :     ASSERT_TRUE(array.buffers[0] == nullptr);  // no bitmap
    2422          21 :     for (int i = 0; i < array.n_children; i++)
    2423             :     {
    2424          20 :         ASSERT_TRUE(array.children[i]->release != nullptr);
    2425          20 :         ASSERT_EQ(array.children[i]->length, array.length);
    2426          20 :         ASSERT_TRUE(array.children[i]->n_buffers >= 2);
    2427          20 :         ASSERT_TRUE(array.children[i]->buffers[0] == nullptr);  // no bitmap
    2428          20 :         ASSERT_EQ(array.children[i]->null_count, 0);
    2429          20 :         ASSERT_TRUE(array.children[i]->buffers[1] != nullptr);
    2430          20 :         if (array.children[i]->n_buffers == 3)
    2431             :         {
    2432           3 :             ASSERT_TRUE(array.children[i]->buffers[2] != nullptr);
    2433             :         }
    2434             :     }
    2435           1 :     array.release(&array);
    2436             : 
    2437             :     // Next batch ==> End of stream
    2438           1 :     ASSERT_EQ(stream.get_next(&stream, &array), 0);
    2439           1 :     ASSERT_TRUE(array.release == nullptr);
    2440             : 
    2441             :     // Release stream
    2442           1 :     stream.release(&stream);
    2443             : 
    2444             :     // Insert 2 empty features
    2445             :     {
    2446           1 :         auto poFeature = std::unique_ptr<OGRFeature>(new OGRFeature(poFDefn));
    2447           1 :         ASSERT_EQ(poLayer->CreateFeature(poFeature.get()), OGRERR_NONE);
    2448             :     }
    2449             : 
    2450             :     {
    2451           1 :         auto poFeature = std::unique_ptr<OGRFeature>(new OGRFeature(poFDefn));
    2452           1 :         ASSERT_EQ(poLayer->CreateFeature(poFeature.get()), OGRERR_NONE);
    2453             :     }
    2454             : 
    2455             :     // Get a new stream now that we've released it
    2456             :     {
    2457             :         char **papszOptions =
    2458           1 :             CSLSetNameValue(nullptr, "MAX_FEATURES_IN_BATCH", "2");
    2459           1 :         ASSERT_TRUE(OGR_L_GetArrowStream(OGRLayer::ToHandle(poLayer), &stream,
    2460             :                                          papszOptions));
    2461           1 :         CSLDestroy(papszOptions);
    2462             :     }
    2463           1 :     ASSERT_TRUE(stream.release != nullptr);
    2464             : 
    2465           1 :     ASSERT_EQ(stream.get_next(&stream, &array), 0);
    2466           1 :     ASSERT_TRUE(array.release != nullptr);
    2467           1 :     ASSERT_EQ(array.n_children,
    2468             :               1 + poFDefn->GetFieldCount() + poFDefn->GetGeomFieldCount());
    2469           1 :     ASSERT_EQ(array.length, 2);
    2470          21 :     for (int i = 0; i < array.n_children; i++)
    2471             :     {
    2472          20 :         ASSERT_TRUE(array.children[i]->release != nullptr);
    2473          20 :         ASSERT_EQ(array.children[i]->length, array.length);
    2474          20 :         ASSERT_TRUE(array.children[i]->n_buffers >= 2);
    2475          20 :         if (i > 0)
    2476             :         {
    2477          19 :             ASSERT_TRUE(array.children[i]->buffers[0] !=
    2478             :                         nullptr);  // we have a bitmap
    2479          19 :             ASSERT_EQ(array.children[i]->null_count, 1);
    2480             :         }
    2481          20 :         ASSERT_TRUE(array.children[i]->buffers[1] != nullptr);
    2482          20 :         if (array.children[i]->n_buffers == 3)
    2483             :         {
    2484           3 :             ASSERT_TRUE(array.children[i]->buffers[2] != nullptr);
    2485             :         }
    2486             :     }
    2487           1 :     array.release(&array);
    2488             : 
    2489             :     // Next batch
    2490           1 :     ASSERT_EQ(stream.get_next(&stream, &array), 0);
    2491           1 :     ASSERT_TRUE(array.release != nullptr);
    2492           1 :     ASSERT_EQ(array.n_children,
    2493             :               1 + poFDefn->GetFieldCount() + poFDefn->GetGeomFieldCount());
    2494           1 :     ASSERT_EQ(array.length, 1);
    2495           1 :     array.release(&array);
    2496             : 
    2497             :     // Next batch ==> End of stream
    2498           1 :     ASSERT_EQ(stream.get_next(&stream, &array), 0);
    2499           1 :     ASSERT_TRUE(array.release == nullptr);
    2500             : 
    2501             :     // Release stream
    2502           1 :     stream.release(&stream);
    2503             : 
    2504             :     // Get a new stream now that we've released it
    2505           1 :     ASSERT_TRUE(
    2506             :         OGR_L_GetArrowStream(OGRLayer::ToHandle(poLayer), &stream, nullptr));
    2507           1 :     ASSERT_TRUE(stream.release != nullptr);
    2508             : 
    2509             :     // Free dataset & layer
    2510           1 :     poDS.reset();
    2511             : 
    2512             :     // Test releasing the stream after the dataset/layer has been closed
    2513           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2514           1 :     CPLErrorReset();
    2515           1 :     ASSERT_TRUE(stream.get_schema(&stream, &schema) != 0);
    2516           1 :     ASSERT_TRUE(stream.get_last_error(&stream) != nullptr);
    2517           1 :     ASSERT_TRUE(stream.get_next(&stream, &array) != 0);
    2518           1 :     CPLPopErrorHandler();
    2519           1 :     stream.release(&stream);
    2520             : }
    2521             : 
    2522             : // Test field domain cloning
    2523           4 : TEST_F(test_ogr, field_domain_cloning)
    2524             : {
    2525             :     // range domain
    2526             :     OGRField min;
    2527           1 :     min.Real = 5.5;
    2528             :     OGRField max;
    2529           1 :     max.Real = 6.5;
    2530             :     OGRRangeFieldDomain oRange("name", "description", OGRFieldType::OFTReal,
    2531             :                                OGRFieldSubType::OFSTBoolean, min, true, max,
    2532           2 :                                true);
    2533           1 :     oRange.SetMergePolicy(OGRFieldDomainMergePolicy::OFDMP_GEOMETRY_WEIGHTED);
    2534           1 :     oRange.SetSplitPolicy(OGRFieldDomainSplitPolicy::OFDSP_GEOMETRY_RATIO);
    2535           1 :     std::unique_ptr<OGRRangeFieldDomain> poClonedRange(oRange.Clone());
    2536           1 :     ASSERT_EQ(poClonedRange->GetName(), oRange.GetName());
    2537           1 :     ASSERT_EQ(poClonedRange->GetDescription(), oRange.GetDescription());
    2538           1 :     bool originalInclusive = false;
    2539           1 :     bool cloneInclusive = false;
    2540           1 :     ASSERT_EQ(poClonedRange->GetMin(originalInclusive).Real,
    2541             :               oRange.GetMin(cloneInclusive).Real);
    2542           1 :     ASSERT_EQ(originalInclusive, cloneInclusive);
    2543           1 :     ASSERT_EQ(poClonedRange->GetMax(originalInclusive).Real,
    2544             :               oRange.GetMax(cloneInclusive).Real);
    2545           1 :     ASSERT_EQ(originalInclusive, cloneInclusive);
    2546           1 :     ASSERT_EQ(poClonedRange->GetFieldType(), oRange.GetFieldType());
    2547           1 :     ASSERT_EQ(poClonedRange->GetFieldSubType(), oRange.GetFieldSubType());
    2548           1 :     ASSERT_EQ(poClonedRange->GetSplitPolicy(), oRange.GetSplitPolicy());
    2549           1 :     ASSERT_EQ(poClonedRange->GetMergePolicy(), oRange.GetMergePolicy());
    2550             : 
    2551             :     // glob domain
    2552             :     OGRGlobFieldDomain oGlob("name", "description", OGRFieldType::OFTString,
    2553           2 :                              OGRFieldSubType::OFSTBoolean, "*a*");
    2554           1 :     oGlob.SetMergePolicy(OGRFieldDomainMergePolicy::OFDMP_GEOMETRY_WEIGHTED);
    2555           1 :     oGlob.SetSplitPolicy(OGRFieldDomainSplitPolicy::OFDSP_GEOMETRY_RATIO);
    2556           1 :     std::unique_ptr<OGRGlobFieldDomain> poClonedGlob(oGlob.Clone());
    2557           1 :     ASSERT_EQ(poClonedGlob->GetName(), oGlob.GetName());
    2558           1 :     ASSERT_EQ(poClonedGlob->GetDescription(), oGlob.GetDescription());
    2559           1 :     ASSERT_EQ(poClonedGlob->GetGlob(), oGlob.GetGlob());
    2560           1 :     ASSERT_EQ(poClonedGlob->GetFieldType(), oGlob.GetFieldType());
    2561           1 :     ASSERT_EQ(poClonedGlob->GetFieldSubType(), oGlob.GetFieldSubType());
    2562           1 :     ASSERT_EQ(poClonedGlob->GetSplitPolicy(), oGlob.GetSplitPolicy());
    2563           1 :     ASSERT_EQ(poClonedGlob->GetMergePolicy(), oGlob.GetMergePolicy());
    2564             : 
    2565             :     // coded value domain
    2566             :     OGRCodedFieldDomain oCoded("name", "description", OGRFieldType::OFTString,
    2567           2 :                                OGRFieldSubType::OFSTBoolean, {OGRCodedValue()});
    2568           1 :     oCoded.SetMergePolicy(OGRFieldDomainMergePolicy::OFDMP_GEOMETRY_WEIGHTED);
    2569           1 :     oCoded.SetSplitPolicy(OGRFieldDomainSplitPolicy::OFDSP_GEOMETRY_RATIO);
    2570           1 :     std::unique_ptr<OGRCodedFieldDomain> poClonedCoded(oCoded.Clone());
    2571           1 :     ASSERT_EQ(poClonedCoded->GetName(), oCoded.GetName());
    2572           1 :     ASSERT_EQ(poClonedCoded->GetDescription(), oCoded.GetDescription());
    2573           1 :     ASSERT_EQ(poClonedCoded->GetFieldType(), oCoded.GetFieldType());
    2574           1 :     ASSERT_EQ(poClonedCoded->GetFieldSubType(), oCoded.GetFieldSubType());
    2575           1 :     ASSERT_EQ(poClonedCoded->GetSplitPolicy(), oCoded.GetSplitPolicy());
    2576           1 :     ASSERT_EQ(poClonedCoded->GetMergePolicy(), oCoded.GetMergePolicy());
    2577             : }
    2578             : 
    2579             : // Test field comments
    2580           4 : TEST_F(test_ogr, field_comments)
    2581             : {
    2582           1 :     OGRFieldDefn oFieldDefn("field1", OFTString);
    2583           1 :     ASSERT_EQ(oFieldDefn.GetComment(), "");
    2584           1 :     oFieldDefn.SetComment("my comment");
    2585           1 :     ASSERT_EQ(oFieldDefn.GetComment(), "my comment");
    2586             : 
    2587           1 :     OGRFieldDefn oFieldDefn2(&oFieldDefn);
    2588           1 :     ASSERT_EQ(oFieldDefn2.GetComment(), "my comment");
    2589           1 :     ASSERT_TRUE(oFieldDefn.IsSame(&oFieldDefn2));
    2590             : 
    2591           1 :     oFieldDefn2.SetComment("my comment 2");
    2592           1 :     ASSERT_FALSE(oFieldDefn.IsSame(&oFieldDefn2));
    2593             : }
    2594             : 
    2595             : // Test OGRFeatureDefn C++ GetFields() iterator
    2596           4 : TEST_F(test_ogr, feature_defn_fields_iterator)
    2597             : {
    2598           2 :     OGRFeatureDefn oFDefn;
    2599             :     {
    2600           2 :         OGRFieldDefn oFieldDefn("field1", OFTString);
    2601           1 :         oFDefn.AddFieldDefn(&oFieldDefn);
    2602             :     }
    2603             :     {
    2604           2 :         OGRFieldDefn oFieldDefn("field2", OFTString);
    2605           1 :         oFDefn.AddFieldDefn(&oFieldDefn);
    2606             :     }
    2607           1 :     EXPECT_EQ(oFDefn.GetFields().size(),
    2608             :               static_cast<size_t>(oFDefn.GetFieldCount()));
    2609           1 :     int i = 0;
    2610           3 :     for (const auto *poFieldDefn : oFDefn.GetFields())
    2611             :     {
    2612           2 :         EXPECT_EQ(oFDefn.GetFields()[i], oFDefn.GetFieldDefn(i));
    2613           2 :         EXPECT_EQ(poFieldDefn, oFDefn.GetFieldDefn(i));
    2614           2 :         ++i;
    2615             :     }
    2616           1 :     EXPECT_EQ(i, oFDefn.GetFieldCount());
    2617           1 : }
    2618             : 
    2619             : // Test OGRFeatureDefn C++ GetGeomFields() iterator
    2620           4 : TEST_F(test_ogr, feature_defn_geomfields_iterator)
    2621             : {
    2622           2 :     OGRFeatureDefn oFDefn;
    2623             :     {
    2624           2 :         OGRGeomFieldDefn oGeomFieldDefn("field1", wkbUnknown);
    2625           1 :         oFDefn.AddGeomFieldDefn(&oGeomFieldDefn);
    2626             :     }
    2627             :     {
    2628           2 :         OGRGeomFieldDefn oGeomFieldDefn("field2", wkbUnknown);
    2629           1 :         oFDefn.AddGeomFieldDefn(&oGeomFieldDefn);
    2630             :     }
    2631           1 :     EXPECT_EQ(oFDefn.GetGeomFields().size(),
    2632             :               static_cast<size_t>(oFDefn.GetGeomFieldCount()));
    2633           1 :     int i = 0;
    2634           4 :     for (const auto *poGeomFieldDefn : oFDefn.GetGeomFields())
    2635             :     {
    2636           3 :         EXPECT_EQ(oFDefn.GetGeomFields()[i], oFDefn.GetGeomFieldDefn(i));
    2637           3 :         EXPECT_EQ(poGeomFieldDefn, oFDefn.GetGeomFieldDefn(i));
    2638           3 :         ++i;
    2639             :     }
    2640           1 :     EXPECT_EQ(i, oFDefn.GetGeomFieldCount());
    2641           1 : }
    2642             : 
    2643             : // Test OGRGeomFieldDefn copy constructor
    2644           4 : TEST_F(test_ogr, geom_field_defn_copy_constructor)
    2645             : {
    2646             :     {
    2647           2 :         OGRGeomFieldDefn oGeomFieldDefn("field1", wkbPoint);
    2648           1 :         oGeomFieldDefn.SetNullable(false);
    2649           2 :         OGRGeomFieldDefn oGeomFieldDefn2("field2", wkbLineString);
    2650           1 :         oGeomFieldDefn2 = oGeomFieldDefn;
    2651           1 :         EXPECT_TRUE(oGeomFieldDefn2.IsSame(&oGeomFieldDefn));
    2652             :     }
    2653             : 
    2654             :     {
    2655           2 :         OGRSpatialReference oSRS;
    2656           1 :         oSRS.SetFromUserInput("WGS84");
    2657           1 :         EXPECT_EQ(oSRS.GetReferenceCount(), 1);
    2658           2 :         OGRGeomFieldDefn oGeomFieldDefn("field1", wkbPoint);
    2659           1 :         oGeomFieldDefn.SetSpatialRef(&oSRS);
    2660           1 :         EXPECT_EQ(oSRS.GetReferenceCount(), 2);
    2661           2 :         OGRGeomFieldDefn oGeomFieldDefn2("field2", wkbLineString);
    2662           1 :         oGeomFieldDefn2 = oGeomFieldDefn;
    2663           1 :         EXPECT_EQ(oSRS.GetReferenceCount(), 3);
    2664           1 :         EXPECT_TRUE(oGeomFieldDefn2.IsSame(&oGeomFieldDefn));
    2665             : 
    2666             :         // oGeomFieldDefn2 already points to oSRS
    2667           1 :         oGeomFieldDefn2 = oGeomFieldDefn;
    2668           1 :         EXPECT_EQ(oSRS.GetReferenceCount(), 3);
    2669           1 :         EXPECT_TRUE(oGeomFieldDefn2.IsSame(&oGeomFieldDefn));
    2670             :     }
    2671           1 : }
    2672             : 
    2673             : // Test GDALDataset QueryLoggerFunc callback
    2674           4 : TEST_F(test_ogr, GDALDatasetSetQueryLoggerFunc)
    2675             : {
    2676           1 :     if (GDALGetDriverByName("GPKG") == nullptr)
    2677             :     {
    2678           0 :         GTEST_SKIP() << "GPKG driver missing";
    2679             :     }
    2680             : 
    2681           1 :     auto tmpGPKG{testing::TempDir() + "/poly-1-feature.gpkg"};
    2682             :     {
    2683           2 :         std::string srcfilename(data_ + SEP + "poly-1-feature.gpkg");
    2684           2 :         std::ifstream src(srcfilename, std::ios::binary);
    2685           2 :         std::ofstream dst(tmpGPKG, std::ios::binary);
    2686           1 :         dst << src.rdbuf();
    2687             :     }
    2688             : 
    2689             :     struct QueryLogEntry
    2690             :     {
    2691             :         std::string sql;
    2692             :         std::string error;
    2693             :         int64_t numRecords;
    2694             :         int64_t executionTimeMilliseconds;
    2695             :     };
    2696             : 
    2697             :     // Note: this must be constructed before poDS or the order
    2698             :     //       of destruction will make the callback call the already
    2699             :     //       destructed vector
    2700           1 :     std::vector<QueryLogEntry> queryLog;
    2701             : 
    2702             :     auto poDS = std::unique_ptr<GDALDataset>(
    2703           1 :         GDALDataset::Open(tmpGPKG.c_str(), GDAL_OF_VECTOR | GDAL_OF_UPDATE));
    2704           1 :     ASSERT_TRUE(poDS);
    2705           1 :     auto hDS = GDALDataset::ToHandle(poDS.get());
    2706           1 :     ASSERT_TRUE(hDS);
    2707             : 
    2708           1 :     const bool retVal = GDALDatasetSetQueryLoggerFunc(
    2709             :         hDS,
    2710          18 :         [](const char *pszSQL, const char *pszError, int64_t lNumRecords,
    2711             :            int64_t lExecutionTimeMilliseconds, void *pQueryLoggerArg)
    2712             :         {
    2713          18 :             std::vector<QueryLogEntry> *queryLogLocal{
    2714             :                 reinterpret_cast<std::vector<QueryLogEntry> *>(
    2715             :                     pQueryLoggerArg)};
    2716          36 :             QueryLogEntry entryLocal;
    2717          18 :             if (pszSQL)
    2718             :             {
    2719          18 :                 entryLocal.sql = pszSQL;
    2720             :             }
    2721          18 :             entryLocal.numRecords = lNumRecords;
    2722          18 :             entryLocal.executionTimeMilliseconds = lExecutionTimeMilliseconds;
    2723          18 :             if (pszError)
    2724             :             {
    2725           1 :                 entryLocal.error = pszError;
    2726             :             }
    2727          18 :             queryLogLocal->push_back(std::move(entryLocal));
    2728          18 :         },
    2729           1 :         &queryLog);
    2730             : 
    2731           1 :     ASSERT_TRUE(retVal);
    2732           1 :     auto hLayer{GDALDatasetGetLayer(hDS, 0)};
    2733           1 :     ASSERT_TRUE(hLayer);
    2734           1 :     ASSERT_STREQ(OGR_L_GetName(hLayer), "poly");
    2735             :     auto poFeature = std::unique_ptr<OGRFeature>(
    2736           1 :         OGRFeature::FromHandle(OGR_L_GetNextFeature(hLayer)));
    2737           1 :     auto hFeature = OGRFeature::ToHandle(poFeature.get());
    2738           1 :     ASSERT_TRUE(hFeature);
    2739           1 :     ASSERT_GT(queryLog.size(), 1U);
    2740             : 
    2741           1 :     QueryLogEntry entry{queryLog.back()};
    2742           1 :     ASSERT_EQ(entry.sql.find("SELECT", 0), 0);
    2743           1 :     ASSERT_TRUE(entry.executionTimeMilliseconds >= 0);
    2744           1 :     ASSERT_EQ(entry.numRecords, -1);
    2745           1 :     ASSERT_TRUE(entry.error.empty());
    2746             : 
    2747             :     // Test erroneous query
    2748           1 :     OGRLayerH queryResultLayerH{GDALDatasetExecuteSQL(
    2749             :         hDS, "SELECT * FROM not_existing_table", nullptr, nullptr)};
    2750           1 :     GDALDatasetReleaseResultSet(hDS, queryResultLayerH);
    2751           1 :     ASSERT_FALSE(queryResultLayerH);
    2752             : 
    2753           1 :     entry = queryLog.back();
    2754           1 :     ASSERT_EQ(entry.sql.find("SELECT * FROM not_existing_table", 0), 0);
    2755           1 :     ASSERT_EQ(entry.executionTimeMilliseconds, -1);
    2756           1 :     ASSERT_EQ(entry.numRecords, -1);
    2757           1 :     ASSERT_FALSE(entry.error.empty());
    2758             : 
    2759             :     // Test prepared arg substitution
    2760           1 :     hFeature = OGR_F_Create(OGR_L_GetLayerDefn(hLayer));
    2761           1 :     poFeature.reset(OGRFeature::FromHandle(hFeature));
    2762           1 :     OGR_F_SetFieldInteger(hFeature, 1, 123);
    2763           1 :     OGRErr err = OGR_L_CreateFeature(hLayer, hFeature);
    2764           1 :     ASSERT_EQ(OGRERR_NONE, err);
    2765             : 
    2766             :     auto insertEntry = std::find_if(
    2767          16 :         queryLog.cbegin(), queryLog.cend(), [](const QueryLogEntry &e)
    2768          17 :         { return e.sql.find(R"sql(INSERT INTO "poly")sql", 0) == 0; });
    2769             : 
    2770           1 :     ASSERT_TRUE(insertEntry != queryLog.end());
    2771           1 :     ASSERT_EQ(
    2772             :         insertEntry->sql.find(
    2773             :             R"sql(INSERT INTO "poly" ( "geom", "AREA", "EAS_ID", "PRFEDEA") VALUES (NULL, NULL, 123, NULL))sql",
    2774             :             0),
    2775             :         0);
    2776             : }
    2777             : 
    2778           4 : TEST_F(test_ogr, OGRParseDateTimeYYYYMMDDTHHMMZ)
    2779             : {
    2780             :     {
    2781           1 :         char szInput[] = "2023-07-11T17:27Z";
    2782             :         OGRField sField;
    2783           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMZ(szInput, &sField), true);
    2784           1 :         EXPECT_EQ(sField.Date.Year, 2023);
    2785           1 :         EXPECT_EQ(sField.Date.Month, 7);
    2786           1 :         EXPECT_EQ(sField.Date.Day, 11);
    2787           1 :         EXPECT_EQ(sField.Date.Hour, 17);
    2788           1 :         EXPECT_EQ(sField.Date.Minute, 27);
    2789           1 :         EXPECT_EQ(sField.Date.Second, 0.0f);
    2790           1 :         EXPECT_EQ(sField.Date.TZFlag, 100);
    2791             :     }
    2792             :     {
    2793           1 :         char szInput[] = "2023-07-11T17:27";
    2794             :         OGRField sField;
    2795           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMZ(szInput, &sField), true);
    2796           1 :         EXPECT_EQ(sField.Date.Year, 2023);
    2797           1 :         EXPECT_EQ(sField.Date.Month, 7);
    2798           1 :         EXPECT_EQ(sField.Date.Day, 11);
    2799           1 :         EXPECT_EQ(sField.Date.Hour, 17);
    2800           1 :         EXPECT_EQ(sField.Date.Minute, 27);
    2801           1 :         EXPECT_EQ(sField.Date.Second, 0.0f);
    2802           1 :         EXPECT_EQ(sField.Date.TZFlag, 0);
    2803             :     }
    2804             :     {
    2805             :         // Invalid
    2806           1 :         char szInput[] = "2023-07-11T17:2";
    2807             :         OGRField sField;
    2808           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMZ(szInput, &sField), false);
    2809             :     }
    2810             :     {
    2811             :         // Invalid
    2812           1 :         char szInput[] = "2023-07-11T17:99";
    2813             :         OGRField sField;
    2814           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMZ(szInput, &sField), false);
    2815             :     }
    2816           1 : }
    2817             : 
    2818           4 : TEST_F(test_ogr, OGRParseDateTimeYYYYMMDDTHHMMSSZ)
    2819             : {
    2820             :     {
    2821           1 :         char szInput[] = "2023-07-11T17:27:34Z";
    2822             :         OGRField sField;
    2823           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMSSZ(szInput, &sField), true);
    2824           1 :         EXPECT_EQ(sField.Date.Year, 2023);
    2825           1 :         EXPECT_EQ(sField.Date.Month, 7);
    2826           1 :         EXPECT_EQ(sField.Date.Day, 11);
    2827           1 :         EXPECT_EQ(sField.Date.Hour, 17);
    2828           1 :         EXPECT_EQ(sField.Date.Minute, 27);
    2829           1 :         EXPECT_EQ(sField.Date.Second, 34.0f);
    2830           1 :         EXPECT_EQ(sField.Date.TZFlag, 100);
    2831             :     }
    2832             :     {
    2833           1 :         char szInput[] = "2023-07-11T17:27:34";
    2834             :         OGRField sField;
    2835           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMSSZ(szInput, &sField), true);
    2836           1 :         EXPECT_EQ(sField.Date.Year, 2023);
    2837           1 :         EXPECT_EQ(sField.Date.Month, 7);
    2838           1 :         EXPECT_EQ(sField.Date.Day, 11);
    2839           1 :         EXPECT_EQ(sField.Date.Hour, 17);
    2840           1 :         EXPECT_EQ(sField.Date.Minute, 27);
    2841           1 :         EXPECT_EQ(sField.Date.Second, 34.0f);
    2842           1 :         EXPECT_EQ(sField.Date.TZFlag, 0);
    2843             :     }
    2844             :     {
    2845             :         // Invalid
    2846           1 :         char szInput[] = "2023-07-11T17:27:3";
    2847             :         OGRField sField;
    2848           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMSSZ(szInput, &sField), false);
    2849             :     }
    2850             :     {
    2851             :         // Invalid
    2852           1 :         char szInput[] = "2023-07-11T17:27:99";
    2853             :         OGRField sField;
    2854           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMSSZ(szInput, &sField), false);
    2855             :     }
    2856           1 : }
    2857             : 
    2858           4 : TEST_F(test_ogr, OGRParseDateTimeYYYYMMDDTHHMMSSsssZ)
    2859             : {
    2860             :     {
    2861           1 :         char szInput[] = "2023-07-11T17:27:34.123Z";
    2862             :         OGRField sField;
    2863           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(szInput, &sField), true);
    2864           1 :         EXPECT_EQ(sField.Date.Year, 2023);
    2865           1 :         EXPECT_EQ(sField.Date.Month, 7);
    2866           1 :         EXPECT_EQ(sField.Date.Day, 11);
    2867           1 :         EXPECT_EQ(sField.Date.Hour, 17);
    2868           1 :         EXPECT_EQ(sField.Date.Minute, 27);
    2869           1 :         EXPECT_EQ(sField.Date.Second, 34.123f);
    2870           1 :         EXPECT_EQ(sField.Date.TZFlag, 100);
    2871             :     }
    2872             :     {
    2873           1 :         char szInput[] = "2023-07-11T17:27:34.123";
    2874             :         OGRField sField;
    2875           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(szInput, &sField), true);
    2876           1 :         EXPECT_EQ(sField.Date.Year, 2023);
    2877           1 :         EXPECT_EQ(sField.Date.Month, 7);
    2878           1 :         EXPECT_EQ(sField.Date.Day, 11);
    2879           1 :         EXPECT_EQ(sField.Date.Hour, 17);
    2880           1 :         EXPECT_EQ(sField.Date.Minute, 27);
    2881           1 :         EXPECT_EQ(sField.Date.Second, 34.123f);
    2882           1 :         EXPECT_EQ(sField.Date.TZFlag, 0);
    2883             :     }
    2884             :     {
    2885             :         // Invalid
    2886           1 :         char szInput[] = "2023-07-11T17:27:34.12";
    2887             :         OGRField sField;
    2888           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(szInput, &sField), false);
    2889             :     }
    2890             :     {
    2891             :         // Invalid
    2892           1 :         char szInput[] = "2023-07-11T17:27:99.123";
    2893             :         OGRField sField;
    2894           1 :         EXPECT_EQ(OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(szInput, &sField), false);
    2895             :     }
    2896           1 : }
    2897             : 
    2898           4 : TEST_F(test_ogr, OGRGetISO8601DateTime)
    2899             : {
    2900             :     OGRField sField;
    2901           1 :     sField.Date.Year = 2023;
    2902           1 :     sField.Date.Month = 7;
    2903           1 :     sField.Date.Day = 11;
    2904           1 :     sField.Date.Hour = 17;
    2905           1 :     sField.Date.Minute = 27;
    2906           1 :     sField.Date.Second = 34.567f;
    2907           1 :     sField.Date.TZFlag = 100;
    2908             :     {
    2909             :         char szResult[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
    2910             :         OGRISO8601Format sFormat;
    2911           1 :         sFormat.ePrecision = OGRISO8601Precision::AUTO;
    2912           1 :         OGRGetISO8601DateTime(&sField, sFormat, szResult);
    2913           1 :         EXPECT_STREQ(szResult, "2023-07-11T17:27:34.567Z");
    2914             :     }
    2915             :     {
    2916             :         char szResult[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
    2917             :         OGRISO8601Format sFormat;
    2918           1 :         sFormat.ePrecision = OGRISO8601Precision::MILLISECOND;
    2919           1 :         OGRGetISO8601DateTime(&sField, sFormat, szResult);
    2920           1 :         EXPECT_STREQ(szResult, "2023-07-11T17:27:34.567Z");
    2921             :     }
    2922             :     {
    2923             :         char szResult[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
    2924             :         OGRISO8601Format sFormat;
    2925           1 :         sFormat.ePrecision = OGRISO8601Precision::SECOND;
    2926           1 :         OGRGetISO8601DateTime(&sField, sFormat, szResult);
    2927           1 :         EXPECT_STREQ(szResult, "2023-07-11T17:27:35Z");
    2928             :     }
    2929             :     {
    2930             :         char szResult[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
    2931             :         OGRISO8601Format sFormat;
    2932           1 :         sFormat.ePrecision = OGRISO8601Precision::MINUTE;
    2933           1 :         OGRGetISO8601DateTime(&sField, sFormat, szResult);
    2934           1 :         EXPECT_STREQ(szResult, "2023-07-11T17:27Z");
    2935             :     }
    2936           1 :     sField.Date.Second = 34.0f;
    2937             :     {
    2938             :         char szResult[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
    2939             :         OGRISO8601Format sFormat;
    2940           1 :         sFormat.ePrecision = OGRISO8601Precision::AUTO;
    2941           1 :         OGRGetISO8601DateTime(&sField, sFormat, szResult);
    2942           1 :         EXPECT_STREQ(szResult, "2023-07-11T17:27:34Z");
    2943             :     }
    2944           1 : }
    2945             : 
    2946             : // Test calling importFromWkb() multiple times on the same geometry object
    2947           4 : TEST_F(test_ogr, importFromWkbReuse)
    2948             : {
    2949             :     {
    2950           2 :         OGRPoint oPoint;
    2951             :         {
    2952           1 :             size_t nBytesConsumed = 0;
    2953           1 :             EXPECT_EQ(oPoint.importFromWkb(
    2954             :                           reinterpret_cast<const GByte *>(
    2955             :                               "\x01\x01\x00\x00\x00"                // Point
    2956             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"    // 1.0
    2957             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"),  // 2.0
    2958             :                           21, wkbVariantIso, nBytesConsumed),
    2959             :                       OGRERR_NONE);
    2960           1 :             EXPECT_EQ(nBytesConsumed, 21);
    2961           1 :             EXPECT_EQ(oPoint.getX(), 1.0);
    2962           1 :             EXPECT_EQ(oPoint.getY(), 2.0);
    2963             :         }
    2964             :         {
    2965           1 :             size_t nBytesConsumed = 0;
    2966           1 :             EXPECT_EQ(oPoint.importFromWkb(
    2967             :                           reinterpret_cast<const GByte *>(
    2968             :                               "\x01\x01\x00\x00\x00"              // Point
    2969             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"  // 2.0
    2970             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"  // 1.0
    2971             :                               ),
    2972             :                           21, wkbVariantIso, nBytesConsumed),
    2973             :                       OGRERR_NONE);
    2974           1 :             EXPECT_EQ(nBytesConsumed, 21);
    2975           1 :             EXPECT_EQ(oPoint.getX(), 2.0);
    2976           1 :             EXPECT_EQ(oPoint.getY(), 1.0);
    2977             :         }
    2978             :     }
    2979             : 
    2980             :     {
    2981           1 :         OGRLineString oLS;
    2982             :         {
    2983           1 :             size_t nBytesConsumed = 0;
    2984           1 :             EXPECT_EQ(oLS.importFromWkb(
    2985             :                           reinterpret_cast<const GByte *>(
    2986             :                               "\x01\x02\x00\x00\x00"              // LineString
    2987             :                               "\x01\x00\x00\x00"                  // 1 point
    2988             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"  // 1.0
    2989             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"),  // 2.0
    2990             :                           25, wkbVariantIso, nBytesConsumed),
    2991             :                       OGRERR_NONE);
    2992           1 :             EXPECT_EQ(nBytesConsumed, 25);
    2993           1 :             ASSERT_EQ(oLS.getNumPoints(), 1);
    2994           1 :             EXPECT_EQ(oLS.getX(0), 1.0);
    2995           1 :             EXPECT_EQ(oLS.getY(0), 2.0);
    2996             :         }
    2997             :         {
    2998           1 :             size_t nBytesConsumed = 0;
    2999           1 :             EXPECT_EQ(oLS.importFromWkb(
    3000             :                           reinterpret_cast<const GByte *>(
    3001             :                               "\x01\x02\x00\x00\x00"              // LineString
    3002             :                               "\x02\x00\x00\x00"                  // 2 points
    3003             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"  // 1.0
    3004             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"  // 2.0
    3005             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"  // 2.0
    3006             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"),  // 1.0
    3007             :                           41, wkbVariantIso, nBytesConsumed),
    3008             :                       OGRERR_NONE);
    3009           1 :             EXPECT_EQ(nBytesConsumed, 41);
    3010           1 :             ASSERT_EQ(oLS.getNumPoints(), 2);
    3011           1 :             EXPECT_EQ(oLS.getX(0), 1.0);
    3012           1 :             EXPECT_EQ(oLS.getY(0), 2.0);
    3013           1 :             EXPECT_EQ(oLS.getX(1), 2.0);
    3014           1 :             EXPECT_EQ(oLS.getY(1), 1.0);
    3015             :         }
    3016             :         {
    3017           1 :             size_t nBytesConsumed = 0;
    3018           1 :             EXPECT_EQ(oLS.importFromWkb(
    3019             :                           reinterpret_cast<const GByte *>(
    3020             :                               "\x01\x02\x00\x00\x00"              // LineString
    3021             :                               "\x01\x00\x00\x00"                  // 1 point
    3022             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"  // 2.0
    3023             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"),  // 1.0
    3024             :                           25, wkbVariantIso, nBytesConsumed),
    3025             :                       OGRERR_NONE);
    3026           1 :             EXPECT_EQ(nBytesConsumed, 25);
    3027           1 :             ASSERT_EQ(oLS.getNumPoints(), 1);
    3028           1 :             EXPECT_EQ(oLS.getX(0), 2.0);
    3029           1 :             EXPECT_EQ(oLS.getY(0), 1.0);
    3030             :         }
    3031             :     }
    3032             : 
    3033             :     {
    3034           1 :         OGRPolygon oPoly;
    3035             :         {
    3036           1 :             size_t nBytesConsumed = 0;
    3037           1 :             EXPECT_EQ(oPoly.importFromWkb(
    3038             :                           reinterpret_cast<const GByte *>(
    3039             :                               "\x01\x03\x00\x00\x00"                // Polygon
    3040             :                               "\x01\x00\x00\x00"                    // 1 ring
    3041             :                               "\x01\x00\x00\x00"                    // 1 point
    3042             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"    // 1.0
    3043             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"),  // 2.0
    3044             :                           29, wkbVariantIso, nBytesConsumed),
    3045             :                       OGRERR_NONE);
    3046           1 :             EXPECT_EQ(nBytesConsumed, 29);
    3047           1 :             ASSERT_TRUE(oPoly.getExteriorRing() != nullptr);
    3048           1 :             ASSERT_EQ(oPoly.getNumInteriorRings(), 0);
    3049           1 :             auto poLS = oPoly.getExteriorRing();
    3050           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3051           1 :             EXPECT_EQ(poLS->getX(0), 1.0);
    3052           1 :             EXPECT_EQ(poLS->getY(0), 2.0);
    3053             :         }
    3054             :         {
    3055           1 :             size_t nBytesConsumed = 0;
    3056           1 :             EXPECT_EQ(oPoly.importFromWkb(
    3057             :                           reinterpret_cast<const GByte *>(
    3058             :                               "\x01\x03\x00\x00\x00"                // Polygon
    3059             :                               "\x01\x00\x00\x00"                    // 1 ring
    3060             :                               "\x01\x00\x00\x00"                    // 1 point
    3061             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"    // 2.0
    3062             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"),  // 1.0
    3063             :                           29, wkbVariantIso, nBytesConsumed),
    3064             :                       OGRERR_NONE);
    3065           1 :             EXPECT_EQ(nBytesConsumed, 29);
    3066           1 :             ASSERT_TRUE(oPoly.getExteriorRing() != nullptr);
    3067           1 :             ASSERT_EQ(oPoly.getNumInteriorRings(), 0);
    3068           1 :             auto poLS = oPoly.getExteriorRing();
    3069           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3070           1 :             EXPECT_EQ(poLS->getX(0), 2.0);
    3071           1 :             EXPECT_EQ(poLS->getY(0), 1.0);
    3072             :         }
    3073             :         {
    3074           1 :             size_t nBytesConsumed = 0;
    3075           1 :             EXPECT_EQ(oPoly.importFromWkb(reinterpret_cast<const GByte *>(
    3076             :                                               "\x01\x03\x00\x00\x00"  // Polygon
    3077             :                                               "\x00\x00\x00\x00"),    // 0 ring
    3078             :                                           9, wkbVariantIso, nBytesConsumed),
    3079             :                       OGRERR_NONE);
    3080           1 :             EXPECT_EQ(nBytesConsumed, 9);
    3081           1 :             ASSERT_TRUE(oPoly.getExteriorRing() == nullptr);
    3082           1 :             ASSERT_EQ(oPoly.getNumInteriorRings(), 0);
    3083             :         }
    3084             :         {
    3085           1 :             size_t nBytesConsumed = 0;
    3086           1 :             EXPECT_EQ(oPoly.importFromWkb(
    3087             :                           reinterpret_cast<const GByte *>(
    3088             :                               "\x01\x03\x00\x00\x00"                // Polygon
    3089             :                               "\x01\x00\x00\x00"                    // 1 ring
    3090             :                               "\x01\x00\x00\x00"                    // 1 point
    3091             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"    // 1.0
    3092             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"),  // 2.0
    3093             :                           29, wkbVariantIso, nBytesConsumed),
    3094             :                       OGRERR_NONE);
    3095           1 :             EXPECT_EQ(nBytesConsumed, 29);
    3096           1 :             ASSERT_TRUE(oPoly.getExteriorRing() != nullptr);
    3097           1 :             ASSERT_EQ(oPoly.getNumInteriorRings(), 0);
    3098           1 :             auto poLS = oPoly.getExteriorRing();
    3099           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3100           1 :             EXPECT_EQ(poLS->getX(0), 1.0);
    3101           1 :             EXPECT_EQ(poLS->getY(0), 2.0);
    3102             :         }
    3103             :         {
    3104           1 :             size_t nBytesConsumed = 0;
    3105           1 :             EXPECT_EQ(oPoly.importFromWkb(
    3106             :                           reinterpret_cast<const GByte *>(
    3107             :                               "\x01\x03\x00\x00\x00"                // Polygon
    3108             :                               "\x01\x00\x00\x00"                    // 1 ring
    3109             :                               "\x01\x00\x00\x00"                    // 1 point
    3110             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"    // 2.0
    3111             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"),  // 1.0
    3112             :                           static_cast<size_t>(-1), wkbVariantIso,
    3113             :                           nBytesConsumed),
    3114             :                       OGRERR_NONE);
    3115           1 :             EXPECT_EQ(nBytesConsumed, 29);
    3116           1 :             ASSERT_TRUE(oPoly.getExteriorRing() != nullptr);
    3117           1 :             ASSERT_EQ(oPoly.getNumInteriorRings(), 0);
    3118           1 :             auto poLS = oPoly.getExteriorRing();
    3119           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3120           1 :             EXPECT_EQ(poLS->getX(0), 2.0);
    3121           1 :             EXPECT_EQ(poLS->getY(0), 1.0);
    3122             :         }
    3123             :         {
    3124           1 :             size_t nBytesConsumed = 0;
    3125             :             // Truncated WKB
    3126           1 :             EXPECT_NE(oPoly.importFromWkb(reinterpret_cast<const GByte *>(
    3127             :                                               "\x01\x03\x00\x00\x00"  // Polygon
    3128             :                                               "\x01\x00\x00\x00"      // 1 ring
    3129             :                                               "\x01\x00\x00\x00"),    // 1 point
    3130             :                                           13, wkbVariantIso, nBytesConsumed),
    3131             :                       OGRERR_NONE);
    3132           1 :             ASSERT_TRUE(oPoly.getExteriorRing() == nullptr);
    3133           1 :             ASSERT_EQ(oPoly.getNumInteriorRings(), 0);
    3134             :         }
    3135             :     }
    3136             : 
    3137             :     {
    3138           1 :         OGRMultiLineString oMLS;
    3139             :         {
    3140           1 :             size_t nBytesConsumed = 0;
    3141           1 :             EXPECT_EQ(oMLS.importFromWkb(
    3142             :                           reinterpret_cast<const GByte *>(
    3143             :                               "\x01\x05\x00\x00\x00"  // MultiLineString
    3144             :                               "\x01\x00\x00\x00"      // 1-part
    3145             :                               "\x01\x02\x00\x00\x00"  // LineString
    3146             :                               "\x01\x00\x00\x00"      // 1 point
    3147             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"    // 1.0
    3148             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"),  // 2.0
    3149             :                           34, wkbVariantIso, nBytesConsumed),
    3150             :                       OGRERR_NONE);
    3151           1 :             EXPECT_EQ(nBytesConsumed, 34);
    3152           1 :             ASSERT_EQ(oMLS.getNumGeometries(), 1);
    3153           1 :             auto poLS = oMLS.getGeometryRef(0);
    3154           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3155           1 :             EXPECT_EQ(poLS->getX(0), 1.0);
    3156           1 :             EXPECT_EQ(poLS->getY(0), 2.0);
    3157             :         }
    3158             :         {
    3159           1 :             size_t nBytesConsumed = 0;
    3160           1 :             EXPECT_EQ(oMLS.importFromWkb(
    3161             :                           reinterpret_cast<const GByte *>(
    3162             :                               "\x01\x05\x00\x00\x00"  // MultiLineString
    3163             :                               "\x01\x00\x00\x00"      // 1-part
    3164             :                               "\x01\x02\x00\x00\x00"  // LineString
    3165             :                               "\x01\x00\x00\x00"      // 1 point
    3166             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"    // 2.0
    3167             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"),  // 1.0
    3168             :                           34, wkbVariantIso, nBytesConsumed),
    3169             :                       OGRERR_NONE);
    3170           1 :             EXPECT_EQ(nBytesConsumed, 34);
    3171           1 :             ASSERT_EQ(oMLS.getNumGeometries(), 1);
    3172           1 :             auto poLS = oMLS.getGeometryRef(0);
    3173           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3174           1 :             EXPECT_EQ(poLS->getX(0), 2.0);
    3175           1 :             EXPECT_EQ(poLS->getY(0), 1.0);
    3176             :         }
    3177             :         {
    3178           1 :             size_t nBytesConsumed = 0;
    3179           1 :             EXPECT_EQ(oMLS.importFromWkb(
    3180             :                           reinterpret_cast<const GByte *>(
    3181             :                               "\x01\x05\x00\x00\x00"  // MultiLineString
    3182             :                               "\x01\x00\x00\x00"      // 1-part
    3183             :                               "\x01\x02\x00\x00\x00"  // LineString
    3184             :                               "\x01\x00\x00\x00"      // 1 point
    3185             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"    // 1.0
    3186             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"),  // 2.0
    3187             :                           static_cast<size_t>(-1), wkbVariantIso,
    3188             :                           nBytesConsumed),
    3189             :                       OGRERR_NONE);
    3190           1 :             EXPECT_EQ(nBytesConsumed, 34);
    3191           1 :             ASSERT_EQ(oMLS.getNumGeometries(), 1);
    3192           1 :             auto poLS = oMLS.getGeometryRef(0);
    3193           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3194           1 :             EXPECT_EQ(poLS->getX(0), 1.0);
    3195           1 :             EXPECT_EQ(poLS->getY(0), 2.0);
    3196             :         }
    3197             :         {
    3198           1 :             size_t nBytesConsumed = 0;
    3199             :             // Truncated WKB
    3200           1 :             EXPECT_NE(oMLS.importFromWkb(
    3201             :                           reinterpret_cast<const GByte *>(
    3202             :                               "\x01\x05\x00\x00\x00"  // MultiLineString
    3203             :                               "\x01\x00\x00\x00"      // 1-part
    3204             :                               "\x01\x02\x00\x00\x00"  // LineString
    3205             :                               "\x01\x00\x00\x00"      // 1 point
    3206             :                               ),
    3207             :                           18, wkbVariantIso, nBytesConsumed),
    3208             :                       OGRERR_NONE);
    3209           1 :             ASSERT_EQ(oMLS.getNumGeometries(), 0);
    3210             :         }
    3211             :     }
    3212             : 
    3213             :     {
    3214           1 :         OGRMultiPolygon oMP;
    3215             :         {
    3216           1 :             size_t nBytesConsumed = 0;
    3217           1 :             EXPECT_EQ(oMP.importFromWkb(
    3218             :                           reinterpret_cast<const GByte *>(
    3219             :                               "\x01\x06\x00\x00\x00"  // MultiPolygon
    3220             :                               "\x01\x00\x00\x00"      // 1-part
    3221             :                               "\x01\x03\x00\x00\x00"  // Polygon
    3222             :                               "\x01\x00\x00\x00"      // 1 ring
    3223             :                               "\x01\x00\x00\x00"      // 1 point
    3224             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"    // 1.0
    3225             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"),  // 2.0
    3226             :                           38, wkbVariantIso, nBytesConsumed),
    3227             :                       OGRERR_NONE);
    3228           1 :             EXPECT_EQ(nBytesConsumed, 38);
    3229           1 :             ASSERT_EQ(oMP.getNumGeometries(), 1);
    3230           1 :             auto poPoly = oMP.getGeometryRef(0);
    3231           1 :             ASSERT_TRUE(poPoly->getExteriorRing() != nullptr);
    3232           1 :             ASSERT_EQ(poPoly->getNumInteriorRings(), 0);
    3233           1 :             auto poLS = poPoly->getExteriorRing();
    3234           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3235           1 :             EXPECT_EQ(poLS->getX(0), 1.0);
    3236           1 :             EXPECT_EQ(poLS->getY(0), 2.0);
    3237             :         }
    3238             :         {
    3239           1 :             size_t nBytesConsumed = 0;
    3240           1 :             EXPECT_EQ(oMP.importFromWkb(
    3241             :                           reinterpret_cast<const GByte *>(
    3242             :                               "\x01\x06\x00\x00\x00"  // MultiPolygon
    3243             :                               "\x01\x00\x00\x00"      // 1-part
    3244             :                               "\x01\x03\x00\x00\x00"  // Polygon
    3245             :                               "\x01\x00\x00\x00"      // 1 ring
    3246             :                               "\x01\x00\x00\x00"      // 1 point
    3247             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"    // 2.0
    3248             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"),  // 1.0
    3249             :                           38, wkbVariantIso, nBytesConsumed),
    3250             :                       OGRERR_NONE);
    3251           1 :             EXPECT_EQ(nBytesConsumed, 38);
    3252           1 :             ASSERT_EQ(oMP.getNumGeometries(), 1);
    3253           1 :             auto poPoly = oMP.getGeometryRef(0);
    3254           1 :             ASSERT_TRUE(poPoly->getExteriorRing() != nullptr);
    3255           1 :             ASSERT_EQ(poPoly->getNumInteriorRings(), 0);
    3256           1 :             auto poLS = poPoly->getExteriorRing();
    3257           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3258           1 :             EXPECT_EQ(poLS->getX(0), 2.0);
    3259           1 :             EXPECT_EQ(poLS->getY(0), 1.0);
    3260             :         }
    3261             :         {
    3262           1 :             size_t nBytesConsumed = 0;
    3263           1 :             EXPECT_EQ(oMP.importFromWkb(
    3264             :                           reinterpret_cast<const GByte *>(
    3265             :                               "\x01\x06\x00\x00\x00"  // MultiPolygon
    3266             :                               "\x01\x00\x00\x00"      // 1-part
    3267             :                               "\x01\x03\x00\x00\x00"  // Polygon
    3268             :                               "\x01\x00\x00\x00"      // 1 ring
    3269             :                               "\x01\x00\x00\x00"      // 1 point
    3270             :                               "\x00\x00\x00\x00\x00\x00\xf0\x3f"    // 1.0
    3271             :                               "\x00\x00\x00\x00\x00\x00\x00\x40"),  // 2.0
    3272             :                           static_cast<size_t>(-1), wkbVariantIso,
    3273             :                           nBytesConsumed),
    3274             :                       OGRERR_NONE);
    3275           1 :             EXPECT_EQ(nBytesConsumed, 38);
    3276           1 :             ASSERT_EQ(oMP.getNumGeometries(), 1);
    3277           1 :             auto poPoly = oMP.getGeometryRef(0);
    3278           1 :             ASSERT_TRUE(poPoly->getExteriorRing() != nullptr);
    3279           1 :             ASSERT_EQ(poPoly->getNumInteriorRings(), 0);
    3280           1 :             auto poLS = poPoly->getExteriorRing();
    3281           1 :             ASSERT_EQ(poLS->getNumPoints(), 1);
    3282           1 :             EXPECT_EQ(poLS->getX(0), 1.0);
    3283           1 :             EXPECT_EQ(poLS->getY(0), 2.0);
    3284             :         }
    3285             :         {
    3286           1 :             size_t nBytesConsumed = 0;
    3287             :             // Truncated WKB
    3288           1 :             EXPECT_NE(
    3289             :                 oMP.importFromWkb(reinterpret_cast<const GByte *>(
    3290             :                                       "\x01\x06\x00\x00\x00"  // MultiPolygon
    3291             :                                       "\x01\x00\x00\x00"      // 1-part
    3292             :                                       "\x01\x03\x00\x00\x00"  // Polygon
    3293             :                                       "\x01\x00\x00\x00"      // 1 ring
    3294             :                                       "\x01\x00\x00\x00"      // 1 point
    3295             :                                       ),
    3296             :                                   22, wkbVariantIso, nBytesConsumed),
    3297             :                 OGRERR_NONE);
    3298           1 :             ASSERT_EQ(oMP.getNumGeometries(), 0);
    3299             :         }
    3300             :     }
    3301             : }
    3302             : 
    3303             : // Test sealing functionality on OGRFieldDefn
    3304           4 : TEST_F(test_ogr, OGRFieldDefn_sealing)
    3305             : {
    3306           2 :     OGRFieldDefn oFieldDefn("test", OFTString);
    3307           1 :     oFieldDefn.Seal();
    3308             : 
    3309             :     {
    3310           1 :         CPLErrorReset();
    3311           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3312           1 :         oFieldDefn.SetName("new_name");
    3313           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3314             :     }
    3315             : 
    3316             :     {
    3317           1 :         CPLErrorReset();
    3318           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3319           1 :         oFieldDefn.SetType(OFTInteger);
    3320           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3321             :     }
    3322             : 
    3323             :     {
    3324           1 :         CPLErrorReset();
    3325           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3326           1 :         oFieldDefn.SetSubType(OFSTJSON);
    3327           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3328             :     }
    3329             : 
    3330             :     {
    3331           1 :         CPLErrorReset();
    3332           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3333           1 :         oFieldDefn.SetWidth(1);
    3334           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3335             :     }
    3336             : 
    3337             :     {
    3338           1 :         CPLErrorReset();
    3339           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3340           1 :         oFieldDefn.SetPrecision(1);
    3341           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3342             :     }
    3343             : 
    3344             :     {
    3345           1 :         CPLErrorReset();
    3346           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3347           1 :         oFieldDefn.SetDefault("");
    3348           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3349             :     }
    3350             : 
    3351             :     {
    3352           1 :         CPLErrorReset();
    3353           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3354           1 :         oFieldDefn.SetUnique(true);
    3355           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3356             :     }
    3357             : 
    3358             :     {
    3359           1 :         CPLErrorReset();
    3360           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3361           1 :         oFieldDefn.SetNullable(false);
    3362           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3363             :     }
    3364             : 
    3365             :     {
    3366           1 :         CPLErrorReset();
    3367           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3368           1 :         oFieldDefn.SetComment("");
    3369           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3370             :     }
    3371             : 
    3372             :     {
    3373           1 :         CPLErrorReset();
    3374           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3375           1 :         oFieldDefn.SetAlternativeName("");
    3376           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3377             :     }
    3378             : 
    3379             :     {
    3380           1 :         CPLErrorReset();
    3381           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3382           1 :         oFieldDefn.SetDomainName("");
    3383           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3384             :     }
    3385             : 
    3386             :     {
    3387           1 :         CPLErrorReset();
    3388           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3389           1 :         oFieldDefn.SetTZFlag(0);
    3390           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3391             :     }
    3392             : 
    3393             :     {
    3394           2 :         auto oTemporaryUnsealer(oFieldDefn.GetTemporaryUnsealer());
    3395           1 :         CPLErrorReset();
    3396           1 :         oFieldDefn.SetName("new_name");
    3397           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") == nullptr);
    3398             :     }
    3399             : 
    3400             :     {
    3401           1 :         CPLErrorReset();
    3402           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3403           1 :         oFieldDefn.SetName("new_name");
    3404           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3405             :     }
    3406             : 
    3407             :     {
    3408           1 :         CPLErrorReset();
    3409           1 :         whileUnsealing(&oFieldDefn)->SetName("new_name");
    3410           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3411             :     }
    3412           1 : }
    3413             : 
    3414             : // Test sealing functionality on OGRGeomFieldDefn
    3415           4 : TEST_F(test_ogr, OGRGeomFieldDefn_sealing)
    3416             : {
    3417           2 :     OGRGeomFieldDefn oFieldDefn("test", wkbUnknown);
    3418             : 
    3419           1 :     oFieldDefn.Seal();
    3420             : 
    3421             :     {
    3422           1 :         CPLErrorReset();
    3423           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3424           1 :         oFieldDefn.SetName("new_name");
    3425           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3426             :     }
    3427             : 
    3428             :     {
    3429           1 :         CPLErrorReset();
    3430           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3431           1 :         oFieldDefn.SetType(wkbPoint);
    3432           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3433             :     }
    3434             : 
    3435             :     {
    3436           1 :         CPLErrorReset();
    3437           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3438           1 :         oFieldDefn.SetSpatialRef(nullptr);
    3439           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3440             :     }
    3441             : 
    3442             :     {
    3443           1 :         CPLErrorReset();
    3444           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3445           1 :         oFieldDefn.SetNullable(false);
    3446           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3447             :     }
    3448             : 
    3449             :     {
    3450           2 :         auto oTemporaryUnsealer(oFieldDefn.GetTemporaryUnsealer());
    3451           1 :         CPLErrorReset();
    3452           1 :         oFieldDefn.SetName("new_name");
    3453           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3454             :     }
    3455             : 
    3456             :     {
    3457           1 :         CPLErrorReset();
    3458           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3459           1 :         oFieldDefn.SetName("new_name");
    3460           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3461             :     }
    3462             : 
    3463             :     {
    3464           1 :         CPLErrorReset();
    3465           1 :         whileUnsealing(&oFieldDefn)->SetName("new_name");
    3466           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3467             :     }
    3468           1 : }
    3469             : 
    3470             : // Test sealing functionality on OGRFeatureDefn
    3471           4 : TEST_F(test_ogr, OGRFeatureDefn_sealing)
    3472             : {
    3473           2 :     OGRFeatureDefn oFDefn;
    3474           1 :     CPLErrorReset();
    3475             :     {
    3476           1 :         oFDefn.SetName("new_name");
    3477           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3478             :     }
    3479             :     {
    3480           2 :         OGRFieldDefn oFieldDefn("test", OFTString);
    3481           1 :         oFDefn.AddFieldDefn(&oFieldDefn);
    3482           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3483             :     }
    3484             :     {
    3485           2 :         OGRGeomFieldDefn oFieldDefn("test", wkbUnknown);
    3486           1 :         oFDefn.AddGeomFieldDefn(&oFieldDefn);
    3487           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3488             :     }
    3489             : 
    3490             :     {
    3491           1 :         CPLErrorReset();
    3492           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3493           1 :         oFDefn.Unseal(true);
    3494           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "unsealed") != nullptr);
    3495             : 
    3496           1 :         CPLErrorReset();
    3497           2 :         auto oTemporaryUnsealer1(oFDefn.GetTemporaryUnsealer(false));
    3498           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "unsealed") != nullptr);
    3499           1 :         CPLErrorReset();
    3500           2 :         auto oTemporaryUnsealer2(oFDefn.GetTemporaryUnsealer(false));
    3501           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3502             :     }
    3503             : 
    3504           1 :     oFDefn.Seal(true);
    3505             : 
    3506             :     {
    3507           1 :         CPLErrorReset();
    3508           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3509           1 :         oFDefn.Seal(true);
    3510           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3511             :     }
    3512             : 
    3513             :     {
    3514           1 :         CPLErrorReset();
    3515           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3516           1 :         oFDefn.SetName("new_name");
    3517           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3518             :     }
    3519             : 
    3520             :     {
    3521           1 :         CPLErrorReset();
    3522           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3523           2 :         OGRFieldDefn oFieldDefn("test2", OFTString);
    3524           1 :         oFDefn.AddFieldDefn(&oFieldDefn);
    3525           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3526             :     }
    3527             : 
    3528             :     {
    3529           1 :         CPLErrorReset();
    3530           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3531           1 :         oFDefn.DeleteFieldDefn(0);
    3532           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3533             :     }
    3534             : 
    3535             :     {
    3536           1 :         CPLErrorReset();
    3537           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3538           1 :         int map[] = {0};
    3539           1 :         oFDefn.ReorderFieldDefns(map);
    3540           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3541             :     }
    3542             : 
    3543             :     {
    3544           1 :         CPLErrorReset();
    3545           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3546           2 :         OGRGeomFieldDefn oFieldDefn("test2", wkbUnknown);
    3547           1 :         oFDefn.AddGeomFieldDefn(&oFieldDefn);
    3548           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3549             :     }
    3550             : 
    3551             :     {
    3552           1 :         CPLErrorReset();
    3553           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3554           1 :         oFDefn.DeleteGeomFieldDefn(0);
    3555           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3556             :     }
    3557             : 
    3558             :     {
    3559           2 :         auto oTemporaryUnsealer(oFDefn.GetTemporaryUnsealer(false));
    3560           1 :         CPLErrorReset();
    3561           1 :         oFDefn.SetName("new_name");
    3562           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3563             : 
    3564           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3565           1 :         CPLErrorReset();
    3566           1 :         oFDefn.GetFieldDefn(0)->SetName("new_name");
    3567           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3568             : 
    3569           1 :         CPLErrorReset();
    3570           1 :         oFDefn.GetGeomFieldDefn(0)->SetName("new_name");
    3571           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3572             :     }
    3573             : 
    3574             :     {
    3575             : 
    3576           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3577           1 :         CPLErrorReset();
    3578           1 :         oFDefn.GetFieldDefn(0)->SetName("new_name");
    3579           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3580             : 
    3581           1 :         CPLErrorReset();
    3582           1 :         oFDefn.GetGeomFieldDefn(0)->SetName("new_name");
    3583           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3584             :     }
    3585             : 
    3586             :     {
    3587           2 :         auto oTemporaryUnsealer(oFDefn.GetTemporaryUnsealer(true));
    3588           1 :         CPLErrorReset();
    3589           1 :         oFDefn.SetName("new_name");
    3590           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3591             : 
    3592           1 :         oFDefn.GetFieldDefn(0)->SetName("new_name");
    3593           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3594             : 
    3595           2 :         auto oTemporaryUnsealer2(oFDefn.GetTemporaryUnsealer(true));
    3596             : 
    3597           1 :         oFDefn.GetGeomFieldDefn(0)->SetName("new_name");
    3598           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3599             :     }
    3600             : 
    3601             :     {
    3602             : 
    3603           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3604           1 :         CPLErrorReset();
    3605           1 :         oFDefn.GetFieldDefn(0)->SetName("new_name");
    3606           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3607             : 
    3608           1 :         CPLErrorReset();
    3609           1 :         oFDefn.GetGeomFieldDefn(0)->SetName("new_name");
    3610           1 :         EXPECT_TRUE(strstr(CPLGetLastErrorMsg(), "sealed") != nullptr);
    3611             :     }
    3612             : 
    3613             :     {
    3614           1 :         CPLErrorReset();
    3615           1 :         whileUnsealing(&oFDefn)->SetName("new_name");
    3616           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    3617             :     }
    3618           1 : }
    3619             : 
    3620             : // Test wkbExportOptions
    3621           4 : TEST_F(test_ogr, wkbExportOptions_default)
    3622             : {
    3623           1 :     OGRwkbExportOptions *psOptions = OGRwkbExportOptionsCreate();
    3624           1 :     ASSERT_TRUE(psOptions != nullptr);
    3625           2 :     OGRPoint p(1.23456789012345678, 2.23456789012345678, 3);
    3626           2 :     std::vector<GByte> abyWKB(p.WkbSize());
    3627           1 :     OGR_G_ExportToWkbEx(OGRGeometry::ToHandle(&p), &abyWKB[0], psOptions);
    3628           1 :     OGRwkbExportOptionsDestroy(psOptions);
    3629             : 
    3630           2 :     std::vector<GByte> abyRegularWKB(p.WkbSize());
    3631           1 :     OGR_G_ExportToWkb(OGRGeometry::ToHandle(&p), wkbNDR, &abyRegularWKB[0]);
    3632             : 
    3633           1 :     EXPECT_TRUE(abyWKB == abyRegularWKB);
    3634             : }
    3635             : 
    3636             : // Test wkbExportOptions
    3637           4 : TEST_F(test_ogr, wkbExportOptions)
    3638             : {
    3639           1 :     OGRwkbExportOptions *psOptions = OGRwkbExportOptionsCreate();
    3640           1 :     ASSERT_TRUE(psOptions != nullptr);
    3641           1 :     OGRwkbExportOptionsSetByteOrder(psOptions, wkbXDR);
    3642           1 :     OGRwkbExportOptionsSetVariant(psOptions, wkbVariantIso);
    3643             : 
    3644           1 :     auto hPrec = OGRGeomCoordinatePrecisionCreate();
    3645           1 :     OGRGeomCoordinatePrecisionSet(hPrec, 1e-1, 1e-2, 1e-4);
    3646           1 :     OGRwkbExportOptionsSetPrecision(psOptions, hPrec);
    3647           1 :     OGRGeomCoordinatePrecisionDestroy(hPrec);
    3648             : 
    3649             :     OGRPoint p(1.23456789012345678, -1.23456789012345678, 1.23456789012345678,
    3650           1 :                1.23456789012345678);
    3651           1 :     std::vector<GByte> abyWKB(p.WkbSize());
    3652           1 :     OGR_G_ExportToWkbEx(OGRGeometry::ToHandle(&p), &abyWKB[0], psOptions);
    3653           1 :     OGRwkbExportOptionsDestroy(psOptions);
    3654             : 
    3655             :     const std::vector<GByte> expectedWKB{
    3656             :         0x00, 0x00, 0x00, 0x0B, 0xB9, 0x3F, 0xF3, 0x80, 0x00, 0x00,
    3657             :         0x00, 0x00, 0x00, 0xBF, 0xF3, 0x80, 0x00, 0x00, 0x00, 0x00,
    3658             :         0x00, 0x3F, 0xF3, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
    3659           1 :         0xF3, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00};
    3660           1 :     EXPECT_TRUE(abyWKB == expectedWKB);
    3661             : 
    3662           1 :     OGRGeometry *poGeom = nullptr;
    3663           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3664           1 :     ASSERT_NE(poGeom, nullptr);
    3665           1 :     EXPECT_NEAR(poGeom->toPoint()->getX(), 1.2, 1e-1);
    3666           1 :     EXPECT_NEAR(poGeom->toPoint()->getY(), -1.2, 1e-1);
    3667           1 :     EXPECT_NEAR(poGeom->toPoint()->getZ(), 1.23, 1e-2);
    3668           1 :     EXPECT_NEAR(poGeom->toPoint()->getM(), 1.2346, 1e-4);
    3669           1 :     delete poGeom;
    3670             : }
    3671             : 
    3672             : // Test OGRGeometry::roundCoordinatesIEEE754()
    3673           4 : TEST_F(test_ogr, roundCoordinatesIEEE754)
    3674             : {
    3675           2 :     OGRLineString oLS;
    3676           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234, -1.2345678901234, 0.012345);
    3677           1 :     oLS.addPoint(-1.2345678901234, 1.2345678901234, 1.2345678901234, -0.012345);
    3678           1 :     oLS.addPoint(std::numeric_limits<double>::infinity(),
    3679             :                  std::numeric_limits<double>::quiet_NaN());
    3680           1 :     OGRGeomCoordinateBinaryPrecision sBinaryPrecision;
    3681           2 :     OGRGeomCoordinatePrecision sPrecision;
    3682           1 :     sPrecision.dfXYResolution = 1e-10;
    3683           1 :     sPrecision.dfZResolution = 1e-3;
    3684           1 :     sPrecision.dfMResolution = 1e-5;
    3685           1 :     sBinaryPrecision.SetFrom(sPrecision);
    3686           2 :     OGRLineString oLSOri(oLS);
    3687           1 :     oLS.roundCoordinatesIEEE754(sBinaryPrecision);
    3688           1 :     EXPECT_NE(oLS.getX(0), oLSOri.getX(0));
    3689           1 :     EXPECT_NE(oLS.getY(0), oLSOri.getY(0));
    3690           1 :     EXPECT_NE(oLS.getZ(0), oLSOri.getZ(0));
    3691           1 :     EXPECT_NE(oLS.getM(0), oLSOri.getM(0));
    3692           1 :     EXPECT_NEAR(oLS.getX(0), oLSOri.getX(0), sPrecision.dfXYResolution);
    3693           1 :     EXPECT_NEAR(oLS.getY(0), oLSOri.getY(0), sPrecision.dfXYResolution);
    3694           1 :     EXPECT_NEAR(oLS.getZ(0), oLSOri.getZ(0), sPrecision.dfZResolution);
    3695           1 :     EXPECT_NEAR(oLS.getM(0), oLSOri.getM(0), sPrecision.dfMResolution);
    3696           1 :     EXPECT_NEAR(oLS.getX(1), oLSOri.getX(1), sPrecision.dfXYResolution);
    3697           1 :     EXPECT_NEAR(oLS.getY(1), oLSOri.getY(1), sPrecision.dfXYResolution);
    3698           1 :     EXPECT_NEAR(oLS.getZ(1), oLSOri.getZ(1), sPrecision.dfZResolution);
    3699           1 :     EXPECT_NEAR(oLS.getM(1), oLSOri.getM(1), sPrecision.dfMResolution);
    3700           1 :     EXPECT_EQ(oLS.getX(2), std::numeric_limits<double>::infinity());
    3701           1 :     EXPECT_TRUE(std::isnan(oLS.getY(2)));
    3702           1 : }
    3703             : 
    3704             : // Test discarding of bits in WKB export
    3705           4 : TEST_F(test_ogr, wkb_linestring_2d_xy_precision)
    3706             : {
    3707           2 :     OGRLineString oLS;
    3708           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234);
    3709           1 :     oLS.addPoint(-1.2345678901234, 1.2345678901234);
    3710           1 :     OGRwkbExportOptions sOptions;
    3711           2 :     OGRGeomCoordinatePrecision sPrecision;
    3712           1 :     sPrecision.dfXYResolution = 1e-10;
    3713           1 :     sOptions.sPrecision.SetFrom(sPrecision);
    3714           2 :     std::vector<GByte> abyWKB(oLS.WkbSize());
    3715           1 :     oLS.exportToWkb(&abyWKB[0], &sOptions);
    3716           3 :     for (int i = 0; i < oLS.getDimension() * oLS.getNumPoints(); ++i)
    3717             :     {
    3718           2 :         EXPECT_EQ(abyWKB[5 + 4 + 0 + 8 * i], 0);
    3719             :     }
    3720           1 :     OGRGeometry *poGeom = nullptr;
    3721           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3722           1 :     EXPECT_NE(poGeom->toLineString()->getX(0), oLS.getX(0));
    3723           1 :     EXPECT_NE(poGeom->toLineString()->getY(0), oLS.getY(0));
    3724           1 :     EXPECT_NEAR(poGeom->toLineString()->getX(0), oLS.getX(0),
    3725             :                 sPrecision.dfXYResolution);
    3726           1 :     EXPECT_NEAR(poGeom->toLineString()->getY(0), oLS.getY(0),
    3727             :                 sPrecision.dfXYResolution);
    3728           1 :     EXPECT_NEAR(poGeom->toLineString()->getX(1), oLS.getX(1),
    3729             :                 sPrecision.dfXYResolution);
    3730           1 :     EXPECT_NEAR(poGeom->toLineString()->getY(1), oLS.getY(1),
    3731             :                 sPrecision.dfXYResolution);
    3732           1 :     delete poGeom;
    3733           1 : }
    3734             : 
    3735             : // Test discarding of bits in WKB export
    3736           4 : TEST_F(test_ogr, wkb_linestring_3d_discard_lsb_bits)
    3737             : {
    3738           2 :     OGRLineString oLS;
    3739           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234, -1.2345678901234);
    3740           1 :     oLS.addPoint(-1.2345678901234, 1.2345678901234, 1.2345678901234);
    3741           1 :     OGRwkbExportOptions sOptions;
    3742           2 :     OGRGeomCoordinatePrecision sPrecision;
    3743           1 :     sPrecision.dfXYResolution = 1e-10;
    3744           1 :     sPrecision.dfZResolution = 1e-3;
    3745           1 :     sOptions.sPrecision.SetFrom(sPrecision);
    3746           2 :     std::vector<GByte> abyWKB(oLS.WkbSize());
    3747           1 :     oLS.exportToWkb(&abyWKB[0], &sOptions);
    3748           3 :     for (int i = 0; i < oLS.getDimension() * oLS.getNumPoints(); ++i)
    3749             :     {
    3750           2 :         EXPECT_EQ(abyWKB[5 + 4 + 0 + 8 * i], 0);
    3751             :     }
    3752           1 :     OGRGeometry *poGeom = nullptr;
    3753           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3754           1 :     EXPECT_NE(poGeom->toLineString()->getX(0), oLS.getX(0));
    3755           1 :     EXPECT_NE(poGeom->toLineString()->getY(0), oLS.getY(0));
    3756           1 :     EXPECT_NE(poGeom->toLineString()->getZ(0), oLS.getZ(0));
    3757           1 :     EXPECT_NEAR(poGeom->toLineString()->getX(0), oLS.getX(0),
    3758             :                 sPrecision.dfXYResolution);
    3759           1 :     EXPECT_NEAR(poGeom->toLineString()->getY(0), oLS.getY(0),
    3760             :                 sPrecision.dfXYResolution);
    3761           1 :     EXPECT_NEAR(poGeom->toLineString()->getZ(0), oLS.getZ(0),
    3762             :                 sPrecision.dfZResolution);
    3763           1 :     EXPECT_NEAR(poGeom->toLineString()->getX(1), oLS.getX(1),
    3764             :                 sPrecision.dfXYResolution);
    3765           1 :     EXPECT_NEAR(poGeom->toLineString()->getY(1), oLS.getY(1),
    3766             :                 sPrecision.dfXYResolution);
    3767           1 :     EXPECT_NEAR(poGeom->toLineString()->getZ(1), oLS.getZ(1),
    3768             :                 sPrecision.dfZResolution);
    3769           1 :     delete poGeom;
    3770           1 : }
    3771             : 
    3772             : // Test discarding of bits in WKB export
    3773           4 : TEST_F(test_ogr, wkb_linestring_xym_discard_lsb_bits)
    3774             : {
    3775           2 :     OGRLineString oLS;
    3776           1 :     oLS.addPointM(1.2345678901234, -1.2345678901234, -1.2345678901234);
    3777           1 :     oLS.addPointM(-1.2345678901234, 1.2345678901234, 1.2345678901234);
    3778           1 :     OGRwkbExportOptions sOptions;
    3779           2 :     OGRGeomCoordinatePrecision sPrecision;
    3780           1 :     sPrecision.dfXYResolution = 1e-10;
    3781           1 :     sPrecision.dfMResolution = 1e-3;
    3782           1 :     sOptions.sPrecision.SetFrom(sPrecision);
    3783           2 :     std::vector<GByte> abyWKB(oLS.WkbSize());
    3784           1 :     oLS.exportToWkb(&abyWKB[0], &sOptions);
    3785           3 :     for (int i = 0; i < oLS.getDimension() * oLS.getNumPoints(); ++i)
    3786             :     {
    3787           2 :         EXPECT_EQ(abyWKB[5 + 4 + 0 + 8 * i], 0);
    3788             :     }
    3789           1 :     OGRGeometry *poGeom = nullptr;
    3790           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3791           1 :     EXPECT_NE(poGeom->toLineString()->getX(0), oLS.getX(0));
    3792           1 :     EXPECT_NE(poGeom->toLineString()->getY(0), oLS.getY(0));
    3793           1 :     EXPECT_NE(poGeom->toLineString()->getM(0), oLS.getM(0));
    3794           1 :     EXPECT_NEAR(poGeom->toLineString()->getX(0), oLS.getX(0),
    3795             :                 sPrecision.dfXYResolution);
    3796           1 :     EXPECT_NEAR(poGeom->toLineString()->getY(0), oLS.getY(0),
    3797             :                 sPrecision.dfXYResolution);
    3798           1 :     EXPECT_NEAR(poGeom->toLineString()->getM(0), oLS.getM(0),
    3799             :                 sPrecision.dfMResolution);
    3800           1 :     EXPECT_NEAR(poGeom->toLineString()->getX(1), oLS.getX(1),
    3801             :                 sPrecision.dfXYResolution);
    3802           1 :     EXPECT_NEAR(poGeom->toLineString()->getY(1), oLS.getY(1),
    3803             :                 sPrecision.dfXYResolution);
    3804           1 :     EXPECT_NEAR(poGeom->toLineString()->getM(1), oLS.getM(1),
    3805             :                 sPrecision.dfMResolution);
    3806           1 :     delete poGeom;
    3807           1 : }
    3808             : 
    3809             : // Test discarding of bits in WKB export
    3810           4 : TEST_F(test_ogr, wkb_linestring_xyzm_discard_lsb_bits)
    3811             : {
    3812           2 :     OGRLineString oLS;
    3813           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234, -1.2345678901234, 0.012345);
    3814           1 :     oLS.addPoint(-1.2345678901234, 1.2345678901234, 1.2345678901234, 0.012345);
    3815           1 :     OGRwkbExportOptions sOptions;
    3816           2 :     OGRGeomCoordinatePrecision sPrecision;
    3817           1 :     sPrecision.dfXYResolution = 1e-10;
    3818           1 :     sPrecision.dfZResolution = 1e-3;
    3819           1 :     sPrecision.dfMResolution = 1e-5;
    3820           1 :     sOptions.sPrecision.SetFrom(sPrecision);
    3821           2 :     std::vector<GByte> abyWKB(oLS.WkbSize());
    3822           1 :     oLS.exportToWkb(&abyWKB[0], &sOptions);
    3823           3 :     for (int i = 0; i < oLS.getDimension() * oLS.getNumPoints(); ++i)
    3824             :     {
    3825           2 :         EXPECT_EQ(abyWKB[5 + 4 + 0 + 8 * i], 0);
    3826             :     }
    3827           1 :     OGRGeometry *poGeom = nullptr;
    3828           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3829           1 :     EXPECT_NE(poGeom->toLineString()->getX(0), oLS.getX(0));
    3830           1 :     EXPECT_NE(poGeom->toLineString()->getY(0), oLS.getY(0));
    3831           1 :     EXPECT_NE(poGeom->toLineString()->getZ(0), oLS.getZ(0));
    3832           1 :     EXPECT_NE(poGeom->toLineString()->getM(0), oLS.getM(0));
    3833           1 :     EXPECT_NEAR(poGeom->toLineString()->getX(0), oLS.getX(0),
    3834             :                 sPrecision.dfXYResolution);
    3835           1 :     EXPECT_NEAR(poGeom->toLineString()->getY(0), oLS.getY(0),
    3836             :                 sPrecision.dfXYResolution);
    3837           1 :     EXPECT_NEAR(poGeom->toLineString()->getZ(0), oLS.getZ(0),
    3838             :                 sPrecision.dfZResolution);
    3839           1 :     EXPECT_NEAR(poGeom->toLineString()->getM(0), oLS.getM(0),
    3840             :                 sPrecision.dfMResolution);
    3841           1 :     EXPECT_NEAR(poGeom->toLineString()->getX(1), oLS.getX(1),
    3842             :                 sPrecision.dfXYResolution);
    3843           1 :     EXPECT_NEAR(poGeom->toLineString()->getY(1), oLS.getY(1),
    3844             :                 sPrecision.dfXYResolution);
    3845           1 :     EXPECT_NEAR(poGeom->toLineString()->getZ(1), oLS.getZ(1),
    3846             :                 sPrecision.dfZResolution);
    3847           1 :     EXPECT_NEAR(poGeom->toLineString()->getM(1), oLS.getM(1),
    3848             :                 sPrecision.dfMResolution);
    3849           1 :     delete poGeom;
    3850           1 : }
    3851             : 
    3852             : // Test discarding of bits in WKB export
    3853           4 : TEST_F(test_ogr, wkb_polygon_2d_xy_precision)
    3854             : {
    3855           2 :     OGRLinearRing oLS;
    3856           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234);
    3857           1 :     oLS.addPoint(-1.2345678901234, -1.2345678901234);
    3858           1 :     oLS.addPoint(-2.2345678901234, 1.2345678901234);
    3859           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234);
    3860           2 :     OGRPolygon oPoly;
    3861           1 :     oPoly.addRing(&oLS);
    3862           1 :     OGRwkbExportOptions sOptions;
    3863           2 :     OGRGeomCoordinatePrecision sPrecision;
    3864           1 :     sPrecision.dfXYResolution = 1e-10;
    3865           1 :     sOptions.sPrecision.SetFrom(sPrecision);
    3866           2 :     std::vector<GByte> abyWKB(oPoly.WkbSize());
    3867           1 :     oPoly.exportToWkb(&abyWKB[0], &sOptions);
    3868           5 :     for (int i = 0; i < oLS.getDimension() * oLS.getNumPoints(); ++i)
    3869             :     {
    3870           4 :         EXPECT_EQ(abyWKB[5 + 4 + 4 + 0 + 8 * i], 0);
    3871             :     }
    3872           1 :     OGRGeometry *poGeom = nullptr;
    3873           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3874           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getX(0), oLS.getX(0));
    3875           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getY(0), oLS.getY(0));
    3876           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getX(0), oLS.getX(0),
    3877             :                 sPrecision.dfXYResolution);
    3878           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getY(0), oLS.getY(0),
    3879             :                 sPrecision.dfXYResolution);
    3880           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getX(1), oLS.getX(1),
    3881             :                 sPrecision.dfXYResolution);
    3882           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getY(1), oLS.getY(1),
    3883             :                 sPrecision.dfXYResolution);
    3884           1 :     delete poGeom;
    3885           1 : }
    3886             : 
    3887             : // Test discarding of bits in WKB export
    3888           4 : TEST_F(test_ogr, wkb_polygon_3d_discard_lsb_bits)
    3889             : {
    3890           2 :     OGRLinearRing oLS;
    3891           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234, 1.2345678901234);
    3892           1 :     oLS.addPoint(-1.2345678901234, -1.2345678901234, -1.2345678901234);
    3893           1 :     oLS.addPoint(-2.2345678901234, 1.2345678901234, -1.2345678901234);
    3894           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234, 1.2345678901234);
    3895           2 :     OGRPolygon oPoly;
    3896           1 :     oPoly.addRing(&oLS);
    3897           2 :     OGRSpatialReference oSRS;
    3898           1 :     oSRS.importFromEPSG(4326);
    3899           1 :     OGRwkbExportOptions sOptions;
    3900           2 :     OGRGeomCoordinatePrecision sPrecision;
    3901           1 :     sPrecision.SetFromMeter(&oSRS, 1e-3, 1e-3, 0);
    3902           1 :     sOptions.sPrecision.SetFrom(sPrecision);
    3903           2 :     std::vector<GByte> abyWKB(oPoly.WkbSize());
    3904           1 :     oPoly.exportToWkb(&abyWKB[0], &sOptions);
    3905           5 :     for (int i = 0; i < oLS.getDimension() * oLS.getNumPoints(); ++i)
    3906             :     {
    3907           4 :         EXPECT_EQ(abyWKB[5 + 4 + 4 + 0 + 8 * i], 0);
    3908             :     }
    3909           1 :     OGRGeometry *poGeom = nullptr;
    3910           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3911           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getX(0), oLS.getX(0));
    3912           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getY(0), oLS.getY(0));
    3913           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getZ(0), oLS.getZ(0));
    3914           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getX(0), oLS.getX(0),
    3915             :                 8.9e-9);
    3916           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getY(0), oLS.getY(0),
    3917             :                 8.9e-9);
    3918           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getZ(0), oLS.getZ(0),
    3919             :                 1e-3);
    3920           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getX(1), oLS.getX(1),
    3921             :                 8.9e-9);
    3922           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getY(1), oLS.getY(1),
    3923             :                 8.9e-9);
    3924           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getZ(1), oLS.getZ(1),
    3925             :                 1e-3);
    3926           1 :     delete poGeom;
    3927           1 : }
    3928             : 
    3929             : // Test discarding of bits in WKB export
    3930           4 : TEST_F(test_ogr, wkb_polygon_xym_discard_lsb_bits)
    3931             : {
    3932           2 :     OGRLinearRing oLS;
    3933           1 :     oLS.addPointM(1.2345678901234, -1.2345678901234, 1.2345678901234);
    3934           1 :     oLS.addPointM(-1.2345678901234, -1.2345678901234, -1.2345678901234);
    3935           1 :     oLS.addPointM(-2.2345678901234, 1.2345678901234, -1.2345678901234);
    3936           1 :     oLS.addPointM(1.2345678901234, -1.2345678901234, 1.2345678901234);
    3937           2 :     OGRPolygon oPoly;
    3938           1 :     oPoly.addRing(&oLS);
    3939           2 :     OGRSpatialReference oSRS;
    3940           1 :     oSRS.importFromEPSG(4326);
    3941           1 :     OGRwkbExportOptions sOptions;
    3942           2 :     OGRGeomCoordinatePrecision sPrecision;
    3943           1 :     sPrecision.SetFromMeter(&oSRS, 1e-3, 0, 1e-3);
    3944           1 :     sOptions.sPrecision.SetFrom(sPrecision);
    3945           2 :     std::vector<GByte> abyWKB(oPoly.WkbSize());
    3946           1 :     oPoly.exportToWkb(&abyWKB[0], &sOptions);
    3947           5 :     for (int i = 0; i < oLS.getDimension() * oLS.getNumPoints(); ++i)
    3948             :     {
    3949           4 :         EXPECT_EQ(abyWKB[5 + 4 + 4 + 0 + 8 * i], 0);
    3950             :     }
    3951           1 :     OGRGeometry *poGeom = nullptr;
    3952           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3953           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getX(0), oLS.getX(0));
    3954           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getY(0), oLS.getY(0));
    3955           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getM(0), oLS.getM(0));
    3956           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getX(0), oLS.getX(0),
    3957             :                 8.9e-9);
    3958           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getY(0), oLS.getY(0),
    3959             :                 8.9e-9);
    3960           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getM(0), oLS.getM(0),
    3961             :                 1e-3);
    3962           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getX(1), oLS.getX(1),
    3963             :                 8.9e-9);
    3964           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getY(1), oLS.getY(1),
    3965             :                 8.9e-9);
    3966           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getM(1), oLS.getM(1),
    3967             :                 1e-3);
    3968           1 :     delete poGeom;
    3969           1 : }
    3970             : 
    3971             : // Test discarding of bits in WKB export
    3972           4 : TEST_F(test_ogr, wkb_polygon_xyzm_discard_lsb_bits)
    3973             : {
    3974           2 :     OGRLinearRing oLS;
    3975           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234, 1.2345678901234, 0.012345);
    3976           1 :     oLS.addPoint(-1.2345678901234, -1.2345678901234, -1.2345678901234, 12345);
    3977           1 :     oLS.addPoint(-2.2345678901234, 1.2345678901234, -1.2345678901234, 0.012345);
    3978           1 :     oLS.addPoint(1.2345678901234, -1.2345678901234, 1.2345678901234, 0.012345);
    3979           2 :     OGRPolygon oPoly;
    3980           1 :     oPoly.addRing(&oLS);
    3981           2 :     OGRSpatialReference oSRS;
    3982           1 :     oSRS.importFromEPSG(4326);
    3983           1 :     OGRwkbExportOptions sOptions;
    3984           2 :     OGRGeomCoordinatePrecision sPrecision;
    3985           1 :     sPrecision.SetFromMeter(&oSRS, 1e-3, 1e-3, 1e-4);
    3986           1 :     sOptions.sPrecision.SetFrom(sPrecision);
    3987           2 :     std::vector<GByte> abyWKB(oPoly.WkbSize());
    3988           1 :     oPoly.exportToWkb(&abyWKB[0], &sOptions);
    3989           5 :     for (int i = 0; i < oLS.getDimension() * oLS.getNumPoints(); ++i)
    3990             :     {
    3991           4 :         EXPECT_EQ(abyWKB[5 + 4 + 4 + 0 + 8 * i], 0);
    3992             :     }
    3993           1 :     OGRGeometry *poGeom = nullptr;
    3994           1 :     OGRGeometryFactory::createFromWkb(abyWKB.data(), nullptr, &poGeom);
    3995           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getX(0), oLS.getX(0));
    3996           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getY(0), oLS.getY(0));
    3997           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getZ(0), oLS.getZ(0));
    3998           1 :     EXPECT_NE(poGeom->toPolygon()->getExteriorRing()->getM(0), oLS.getM(0));
    3999           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getX(0), oLS.getX(0),
    4000             :                 8.9e-9);
    4001           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getY(0), oLS.getY(0),
    4002             :                 8.9e-9);
    4003           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getZ(0), oLS.getZ(0),
    4004             :                 1e-3);
    4005           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getM(0), oLS.getM(0),
    4006             :                 1e-4);
    4007           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getX(1), oLS.getX(1),
    4008             :                 8.9e-9);
    4009           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getY(1), oLS.getY(1),
    4010             :                 8.9e-9);
    4011           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getZ(1), oLS.getZ(1),
    4012             :                 1e-3);
    4013           1 :     EXPECT_NEAR(poGeom->toPolygon()->getExteriorRing()->getM(1), oLS.getM(1),
    4014             :                 1e-4);
    4015           1 :     delete poGeom;
    4016           1 : }
    4017             : 
    4018             : // Test OGRFeature::SerializeToBinary() and DeserializeFromBinary();
    4019           4 : TEST_F(test_ogr, OGRFeature_SerializeToBinary)
    4020             : {
    4021             :     {
    4022           2 :         OGRFeatureDefn oFDefn;
    4023           1 :         oFDefn.SetGeomType(wkbNone);
    4024           1 :         oFDefn.Reference();
    4025             : 
    4026             :         {
    4027           2 :             OGRFeature oFeatSrc(&oFDefn);
    4028           1 :             oFeatSrc.SetFID(1);
    4029           2 :             std::vector<GByte> abyBuffer;
    4030             : 
    4031           1 :             EXPECT_TRUE(oFeatSrc.SerializeToBinary(abyBuffer));
    4032           1 :             EXPECT_EQ(abyBuffer.size(), 1);
    4033           1 :             EXPECT_EQ(abyBuffer[0], 1);
    4034             : 
    4035           2 :             OGRFeature oFeatDst(&oFDefn);
    4036           1 :             EXPECT_FALSE(oFeatDst.DeserializeFromBinary(abyBuffer.data(), 0));
    4037           1 :             EXPECT_TRUE(oFeatDst.DeserializeFromBinary(abyBuffer.data(),
    4038             :                                                        abyBuffer.size()));
    4039           1 :             EXPECT_EQ(oFeatDst.GetFID(), 1);
    4040             :         }
    4041             : 
    4042             :         {
    4043           2 :             OGRFeature oFeatSrc(&oFDefn);
    4044           1 :             oFeatSrc.SetFID(static_cast<GIntBig>(-12345678901234));
    4045           2 :             std::vector<GByte> abyBuffer;
    4046             : 
    4047           1 :             EXPECT_TRUE(oFeatSrc.SerializeToBinary(abyBuffer));
    4048             : 
    4049           2 :             OGRFeature oFeatDst(&oFDefn);
    4050             :             // Try truncated buffers
    4051           8 :             for (size_t i = 0; i < abyBuffer.size(); ++i)
    4052             :             {
    4053           7 :                 EXPECT_FALSE(
    4054             :                     oFeatDst.DeserializeFromBinary(abyBuffer.data(), i));
    4055             :             }
    4056           1 :             EXPECT_TRUE(oFeatDst.DeserializeFromBinary(abyBuffer.data(),
    4057             :                                                        abyBuffer.size()));
    4058           1 :             EXPECT_EQ(oFeatDst.GetFID(), static_cast<GIntBig>(-12345678901234));
    4059             :         }
    4060             :     }
    4061             : 
    4062             :     {
    4063           1 :         OGRFeatureDefn oFDefn;
    4064           1 :         oFDefn.Reference();
    4065             :         {
    4066           2 :             OGRFieldDefn oFieldDefn("int", OFTInteger);
    4067           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4068             :         }
    4069             :         {
    4070           2 :             OGRFieldDefn oFieldDefn("int64", OFTInteger64);
    4071           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4072             :         }
    4073             :         {
    4074           2 :             OGRFieldDefn oFieldDefn("real", OFTReal);
    4075           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4076             :         }
    4077             :         {
    4078           2 :             OGRFieldDefn oFieldDefn("str", OFTString);
    4079           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4080             :         }
    4081             :         {
    4082           2 :             OGRFieldDefn oFieldDefn("binary", OFTBinary);
    4083           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4084             :         }
    4085             :         {
    4086           2 :             OGRFieldDefn oFieldDefn("intlist", OFTIntegerList);
    4087           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4088             :         }
    4089             :         {
    4090           2 :             OGRFieldDefn oFieldDefn("int64list", OFTInteger64List);
    4091           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4092             :         }
    4093             :         {
    4094           2 :             OGRFieldDefn oFieldDefn("reallist", OFTRealList);
    4095           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4096             :         }
    4097             :         {
    4098           2 :             OGRFieldDefn oFieldDefn("strlist", OFTStringList);
    4099           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4100             :         }
    4101             :         {
    4102           2 :             OGRFieldDefn oFieldDefn("date", OFTDate);
    4103           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4104             :         }
    4105             :         {
    4106           2 :             OGRFieldDefn oFieldDefn("time", OFTTime);
    4107           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4108             :         }
    4109             :         {
    4110           2 :             OGRFieldDefn oFieldDefn("datetime", OFTDateTime);
    4111           1 :             oFDefn.AddFieldDefn(&oFieldDefn);
    4112             :         }
    4113             : 
    4114             :         {
    4115           2 :             OGRFeature oFeatSrc(&oFDefn);
    4116           2 :             std::vector<GByte> abyBuffer;
    4117             : 
    4118           1 :             EXPECT_TRUE(oFeatSrc.SerializeToBinary(abyBuffer));
    4119           1 :             EXPECT_EQ(abyBuffer.size(), 5);
    4120             : 
    4121           2 :             OGRFeature oFeatDst(&oFDefn);
    4122           6 :             for (size_t i = 0; i < abyBuffer.size(); ++i)
    4123             :             {
    4124           5 :                 EXPECT_FALSE(
    4125             :                     oFeatDst.DeserializeFromBinary(abyBuffer.data(), i));
    4126             :             }
    4127           1 :             EXPECT_TRUE(oFeatDst.DeserializeFromBinary(abyBuffer.data(),
    4128             :                                                        abyBuffer.size()));
    4129           1 :             EXPECT_TRUE(oFeatDst.Equal(&oFeatSrc));
    4130             :         }
    4131             : 
    4132             :         {
    4133           1 :             OGRFeature oFeatSrc(&oFDefn);
    4134           1 :             std::vector<GByte> abyBuffer;
    4135             : 
    4136           1 :             const int iFieldInt = oFDefn.GetFieldIndex("int");
    4137           1 :             ASSERT_TRUE(iFieldInt >= 0);
    4138           1 :             oFeatSrc.SetFieldNull(iFieldInt);
    4139           1 :             EXPECT_TRUE(oFeatSrc.SerializeToBinary(abyBuffer));
    4140           1 :             EXPECT_EQ(abyBuffer.size(), 5);
    4141             : 
    4142           2 :             OGRFeature oFeatDst(&oFDefn);
    4143             : 
    4144             :             // Try truncated buffers
    4145           6 :             for (size_t i = 0; i < abyBuffer.size(); ++i)
    4146             :             {
    4147           5 :                 EXPECT_FALSE(
    4148             :                     oFeatDst.DeserializeFromBinary(abyBuffer.data(), i));
    4149             :             }
    4150             : 
    4151           1 :             EXPECT_TRUE(oFeatDst.DeserializeFromBinary(abyBuffer.data(),
    4152             :                                                        abyBuffer.size()));
    4153           1 :             EXPECT_TRUE(oFeatDst.Equal(&oFeatSrc));
    4154             :         }
    4155             : 
    4156             :         {
    4157           1 :             OGRFeature oFeatSrc(&oFDefn);
    4158           1 :             oFeatSrc.SetFID(1);
    4159           1 :             oFeatSrc.SetField("int", -123);
    4160           1 :             oFeatSrc.SetField("int64", static_cast<GIntBig>(-12345678901234));
    4161           1 :             oFeatSrc.SetField("real", 1.25);
    4162           1 :             oFeatSrc.SetField("str", "foo");
    4163           1 :             const int iFieldBinary = oFDefn.GetFieldIndex("binary");
    4164           1 :             ASSERT_TRUE(iFieldBinary >= 0);
    4165           1 :             oFeatSrc.SetField(iFieldBinary, 3,
    4166             :                               static_cast<const void *>("abc"));
    4167           1 :             oFeatSrc.SetField("intlist", 2,
    4168           2 :                               std::vector<int>{1, -123456}.data());
    4169           1 :             oFeatSrc.SetField("int64list", 2,
    4170           2 :                               std::vector<GIntBig>{1, -12345678901234}.data());
    4171           1 :             oFeatSrc.SetField("reallist", 2,
    4172           2 :                               std::vector<double>{1.5, -2.5}.data());
    4173           2 :             CPLStringList aosList;
    4174           1 :             aosList.AddString("foo");
    4175           1 :             aosList.AddString("barbaz");
    4176           1 :             oFeatSrc.SetField("strlist", aosList.List());
    4177           1 :             oFeatSrc.SetField("date", 2023, 1, 3);
    4178           1 :             oFeatSrc.SetField("time", 0, 0, 0, 12, 34, 56.789f);
    4179           1 :             oFeatSrc.SetField("datetime", 2023, 1, 3, 12, 34, 56.789f);
    4180           2 :             OGRPoint p(1, 2);
    4181           1 :             oFeatSrc.SetGeometry(&p);
    4182           2 :             std::vector<GByte> abyBuffer;
    4183             : 
    4184           1 :             EXPECT_TRUE(oFeatSrc.SerializeToBinary(abyBuffer));
    4185             : 
    4186           2 :             OGRFeature oFeatDst(&oFDefn);
    4187             : 
    4188             :             // Try truncated buffers
    4189         118 :             for (size_t i = 0; i < abyBuffer.size(); ++i)
    4190             :             {
    4191         117 :                 EXPECT_FALSE(
    4192             :                     oFeatDst.DeserializeFromBinary(abyBuffer.data(), i));
    4193             :             }
    4194             : 
    4195             :             // Try corrupted buffers
    4196             :             {
    4197           2 :                 CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    4198         118 :                 for (size_t i = 0; i < abyBuffer.size(); ++i)
    4199             :                 {
    4200             :                     // Might succeed or fail, but shouldn't crash..
    4201         117 :                     const GByte backup = abyBuffer[i];
    4202         117 :                     abyBuffer[i] = static_cast<GByte>(~abyBuffer[i]);
    4203         117 :                     (void)oFeatDst.DeserializeFromBinary(abyBuffer.data(),
    4204             :                                                          abyBuffer.size());
    4205         117 :                     abyBuffer[i] = backup;
    4206             :                 }
    4207             :             }
    4208             : 
    4209           1 :             EXPECT_TRUE(oFeatDst.DeserializeFromBinary(abyBuffer.data(),
    4210             :                                                        abyBuffer.size()));
    4211             :             // oFeatSrc.DumpReadable(stdout);
    4212             :             // oFeatDst.DumpReadable(stdout);
    4213           1 :             EXPECT_TRUE(oFeatDst.Equal(&oFeatSrc));
    4214             :         }
    4215             :     }
    4216             : }
    4217             : 
    4218             : // Test OGRGeometry::IsRectangle()
    4219           4 : TEST_F(test_ogr, OGRGeometry_IsRectangle)
    4220             : {
    4221             :     // Not a polygon
    4222             :     {
    4223           1 :         OGRGeometry *poGeom = nullptr;
    4224           1 :         OGRGeometryFactory::createFromWkt("POINT EMPTY", nullptr, &poGeom);
    4225           1 :         ASSERT_NE(poGeom, nullptr);
    4226           1 :         EXPECT_FALSE(poGeom->IsRectangle());
    4227           1 :         delete poGeom;
    4228             :     }
    4229             :     // Polygon empty
    4230             :     {
    4231           1 :         OGRGeometry *poGeom = nullptr;
    4232           1 :         OGRGeometryFactory::createFromWkt("POLYGON EMPTY", nullptr, &poGeom);
    4233           1 :         ASSERT_NE(poGeom, nullptr);
    4234           1 :         EXPECT_FALSE(poGeom->IsRectangle());
    4235           1 :         delete poGeom;
    4236             :     }
    4237             :     // Polygon with inner ring
    4238             :     {
    4239           1 :         OGRGeometry *poGeom = nullptr;
    4240           1 :         OGRGeometryFactory::createFromWkt(
    4241             :             "POLYGON ((0 0,0 1,1 1,1 0,0 0),(0.2 0.2,0.2 0.8,0.8 0.8,0.8 "
    4242             :             "0.2,0.2 0.2))",
    4243             :             nullptr, &poGeom);
    4244           1 :         ASSERT_NE(poGeom, nullptr);
    4245           1 :         EXPECT_FALSE(poGeom->IsRectangle());
    4246           1 :         delete poGeom;
    4247             :     }
    4248             :     // Polygon with 3 points
    4249             :     {
    4250           1 :         OGRGeometry *poGeom = nullptr;
    4251           1 :         OGRGeometryFactory::createFromWkt("POLYGON ((0 0,0 1,1 1))", nullptr,
    4252             :                                           &poGeom);
    4253           1 :         ASSERT_NE(poGeom, nullptr);
    4254           1 :         EXPECT_FALSE(poGeom->IsRectangle());
    4255           1 :         delete poGeom;
    4256             :     }
    4257             :     // Polygon with 6 points
    4258             :     {
    4259           1 :         OGRGeometry *poGeom = nullptr;
    4260           1 :         OGRGeometryFactory::createFromWkt(
    4261             :             "POLYGON ((0 0,0.1 0,0.2 0,0.3 0,1 1,0 0))", nullptr, &poGeom);
    4262           1 :         ASSERT_NE(poGeom, nullptr);
    4263           1 :         EXPECT_FALSE(poGeom->IsRectangle());
    4264           1 :         delete poGeom;
    4265             :     }
    4266             :     // Polygon with 5 points, but last one not matching first (invalid)
    4267             :     {
    4268           1 :         OGRGeometry *poGeom = nullptr;
    4269           1 :         OGRGeometryFactory::createFromWkt(
    4270             :             "POLYGON ((0 0,0 1,1 1,1 0,-999 -999))", nullptr, &poGeom);
    4271           1 :         ASSERT_NE(poGeom, nullptr);
    4272           1 :         EXPECT_FALSE(poGeom->IsRectangle());
    4273           1 :         delete poGeom;
    4274             :     }
    4275             :     // Polygon with 5 points, but not rectangle
    4276             :     {
    4277           1 :         OGRGeometry *poGeom = nullptr;
    4278           1 :         OGRGeometryFactory::createFromWkt("POLYGON ((0 0,0 1.1,1 1,1 0,0 0))",
    4279             :                                           nullptr, &poGeom);
    4280           1 :         ASSERT_NE(poGeom, nullptr);
    4281           1 :         EXPECT_FALSE(poGeom->IsRectangle());
    4282           1 :         delete poGeom;
    4283             :     }
    4284             :     // Rectangle (type 1)
    4285             :     {
    4286           1 :         OGRGeometry *poGeom = nullptr;
    4287           1 :         OGRGeometryFactory::createFromWkt("POLYGON ((0 0,0 1,1 1,1 0,0 0))",
    4288             :                                           nullptr, &poGeom);
    4289           1 :         ASSERT_NE(poGeom, nullptr);
    4290           1 :         EXPECT_TRUE(poGeom->IsRectangle());
    4291           1 :         delete poGeom;
    4292             :     }
    4293             :     // Rectangle2(type 1)
    4294             :     {
    4295           1 :         OGRGeometry *poGeom = nullptr;
    4296           1 :         OGRGeometryFactory::createFromWkt("POLYGON ((0 0,1 0,1 1,0 1,0 0))",
    4297             :                                           nullptr, &poGeom);
    4298           1 :         ASSERT_NE(poGeom, nullptr);
    4299           1 :         EXPECT_TRUE(poGeom->IsRectangle());
    4300           1 :         delete poGeom;
    4301             :     }
    4302             : }
    4303             : 
    4304             : // Test OGRGeometry::removeEmptyParts()
    4305           4 : TEST_F(test_ogr, OGRGeometry_removeEmptyParts)
    4306             : {
    4307             :     {
    4308           1 :         OGRGeometry *poGeom = nullptr;
    4309           1 :         OGRGeometryFactory::createFromWkt("POINT EMPTY", nullptr, &poGeom);
    4310           1 :         ASSERT_NE(poGeom, nullptr);
    4311           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4312           1 :         poGeom->removeEmptyParts();
    4313           1 :         EXPECT_TRUE(poGeom->IsEmpty());
    4314           1 :         delete poGeom;
    4315             :     }
    4316             :     {
    4317           1 :         OGRGeometry *poGeom = nullptr;
    4318           1 :         OGRGeometryFactory::createFromWkt("POLYGON ((0 0,0 1,1 0,0 0))",
    4319             :                                           nullptr, &poGeom);
    4320           1 :         ASSERT_NE(poGeom, nullptr);
    4321           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4322           1 :         poGeom->removeEmptyParts();
    4323           1 :         EXPECT_NE(poGeom->toPolygon()->getExteriorRing(), nullptr);
    4324           1 :         delete poGeom;
    4325             :     }
    4326             :     {
    4327           1 :         OGRGeometry *poGeom = nullptr;
    4328           1 :         OGRGeometryFactory::createFromWkt("POLYGON ((0 0,0 1,1 0,0 0))",
    4329             :                                           nullptr, &poGeom);
    4330           1 :         ASSERT_NE(poGeom, nullptr);
    4331           1 :         poGeom->toPolygon()->addRingDirectly(new OGRLinearRing());
    4332           1 :         EXPECT_EQ(poGeom->toPolygon()->getNumInteriorRings(), 1);
    4333           1 :         EXPECT_TRUE(poGeom->hasEmptyParts());
    4334           1 :         poGeom->removeEmptyParts();
    4335           1 :         EXPECT_NE(poGeom->toPolygon()->getExteriorRing(), nullptr);
    4336           1 :         EXPECT_EQ(poGeom->toPolygon()->getNumInteriorRings(), 0);
    4337           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4338           1 :         delete poGeom;
    4339             :     }
    4340             :     {
    4341           1 :         OGRGeometry *poGeom = nullptr;
    4342           1 :         OGRGeometryFactory::createFromWkt("COMPOUNDCURVE ((0 0,1 1))", nullptr,
    4343             :                                           &poGeom);
    4344           1 :         ASSERT_NE(poGeom, nullptr);
    4345           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4346           1 :         poGeom->removeEmptyParts();
    4347           1 :         EXPECT_EQ(poGeom->toCompoundCurve()->getNumCurves(), 1);
    4348           1 :         delete poGeom;
    4349             :     }
    4350             :     {
    4351           1 :         OGRGeometry *poGeom = nullptr;
    4352           1 :         OGRGeometryFactory::createFromWkt("COMPOUNDCURVE ((0 0,1 1),(1 1,2 2))",
    4353             :                                           nullptr, &poGeom);
    4354           1 :         ASSERT_NE(poGeom, nullptr);
    4355           1 :         poGeom->toCompoundCurve()->getCurve(1)->empty();
    4356           1 :         EXPECT_EQ(poGeom->toCompoundCurve()->getNumCurves(), 2);
    4357           1 :         EXPECT_TRUE(poGeom->hasEmptyParts());
    4358           1 :         poGeom->removeEmptyParts();
    4359           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4360           1 :         EXPECT_EQ(poGeom->toCompoundCurve()->getNumCurves(), 1);
    4361           1 :         delete poGeom;
    4362             :     }
    4363             :     {
    4364           1 :         OGRGeometry *poGeom = nullptr;
    4365           1 :         OGRGeometryFactory::createFromWkt("GEOMETRYCOLLECTION (POINT(0 0))",
    4366             :                                           nullptr, &poGeom);
    4367           1 :         ASSERT_NE(poGeom, nullptr);
    4368           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4369           1 :         poGeom->removeEmptyParts();
    4370           1 :         EXPECT_EQ(poGeom->toGeometryCollection()->getNumGeometries(), 1);
    4371           1 :         delete poGeom;
    4372             :     }
    4373             :     {
    4374           1 :         OGRGeometry *poGeom = nullptr;
    4375           1 :         OGRGeometryFactory::createFromWkt(
    4376             :             "GEOMETRYCOLLECTION (POINT EMPTY,POINT(0 0),POINT EMPTY)", nullptr,
    4377             :             &poGeom);
    4378           1 :         ASSERT_NE(poGeom, nullptr);
    4379           1 :         EXPECT_EQ(poGeom->toGeometryCollection()->getNumGeometries(), 3);
    4380           1 :         EXPECT_TRUE(poGeom->hasEmptyParts());
    4381           1 :         poGeom->removeEmptyParts();
    4382           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4383           1 :         EXPECT_EQ(poGeom->toGeometryCollection()->getNumGeometries(), 1);
    4384           1 :         delete poGeom;
    4385             :     }
    4386             :     {
    4387           1 :         OGRGeometry *poGeom = nullptr;
    4388           1 :         OGRGeometryFactory::createFromWkt("GEOMETRYCOLLECTION EMPTY", nullptr,
    4389             :                                           &poGeom);
    4390           1 :         ASSERT_NE(poGeom, nullptr);
    4391           1 :         OGRGeometry *poPoly = nullptr;
    4392           1 :         OGRGeometryFactory::createFromWkt("POLYGON ((0 0,0 1,1 0,0 0))",
    4393             :                                           nullptr, &poPoly);
    4394           1 :         EXPECT_NE(poPoly, nullptr);
    4395           1 :         if (poPoly)
    4396             :         {
    4397           1 :             poPoly->toPolygon()->addRingDirectly(new OGRLinearRing());
    4398           1 :             poGeom->toGeometryCollection()->addGeometryDirectly(poPoly);
    4399           1 :             EXPECT_EQ(poGeom->toGeometryCollection()->getNumGeometries(), 1);
    4400           1 :             EXPECT_TRUE(poGeom->hasEmptyParts());
    4401           1 :             poGeom->removeEmptyParts();
    4402           1 :             EXPECT_FALSE(poGeom->hasEmptyParts());
    4403           1 :             EXPECT_EQ(poGeom->toGeometryCollection()->getNumGeometries(), 1);
    4404             :         }
    4405           1 :         delete poGeom;
    4406             :     }
    4407             :     {
    4408           1 :         OGRGeometry *poGeom = nullptr;
    4409           1 :         OGRGeometryFactory::createFromWkt(
    4410             :             "POLYHEDRALSURFACE (((0 0,0 1,1 1,0 0)))", nullptr, &poGeom);
    4411           1 :         ASSERT_NE(poGeom, nullptr);
    4412           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4413           1 :         poGeom->removeEmptyParts();
    4414           1 :         EXPECT_EQ(poGeom->toPolyhedralSurface()->getNumGeometries(), 1);
    4415           1 :         delete poGeom;
    4416             :     }
    4417             :     {
    4418           1 :         OGRGeometry *poGeom = nullptr;
    4419           1 :         OGRGeometryFactory::createFromWkt(
    4420             :             "POLYHEDRALSURFACE (((0 0,0 1,1 1,0 0)))", nullptr, &poGeom);
    4421           1 :         ASSERT_NE(poGeom, nullptr);
    4422           1 :         poGeom->toPolyhedralSurface()->addGeometryDirectly(new OGRPolygon());
    4423           1 :         EXPECT_EQ(poGeom->toPolyhedralSurface()->getNumGeometries(), 2);
    4424           1 :         EXPECT_TRUE(poGeom->hasEmptyParts());
    4425           1 :         poGeom->removeEmptyParts();
    4426           1 :         EXPECT_FALSE(poGeom->hasEmptyParts());
    4427           1 :         EXPECT_EQ(poGeom->toPolyhedralSurface()->getNumGeometries(), 1);
    4428           1 :         delete poGeom;
    4429             :     }
    4430             : }
    4431             : 
    4432             : // Test OGRCurve::reversePoints()
    4433           4 : TEST_F(test_ogr, OGRCurve_reversePoints)
    4434             : {
    4435             :     {
    4436           1 :         OGRGeometry *poGeom = nullptr;
    4437           1 :         OGRGeometryFactory::createFromWkt(
    4438             :             "COMPOUNDCURVE ZM (CIRCULARSTRING ZM (0 0 10 20,1 1 11 21,2 0 12 "
    4439             :             "22),(2 0 12 22,3 0 13 2))",
    4440             :             nullptr, &poGeom);
    4441           1 :         ASSERT_NE(poGeom, nullptr);
    4442           1 :         poGeom->toCurve()->reversePoints();
    4443           1 :         char *pszWKT = nullptr;
    4444           1 :         poGeom->exportToWkt(&pszWKT, wkbVariantIso);
    4445           1 :         EXPECT_TRUE(pszWKT != nullptr);
    4446           1 :         if (pszWKT)
    4447             :         {
    4448           1 :             EXPECT_STREQ(
    4449             :                 pszWKT, "COMPOUNDCURVE ZM ((3 0 13 2,2 0 12 22),CIRCULARSTRING "
    4450             :                         "ZM (2 0 12 22,1 1 11 21,0 0 10 20))");
    4451             :         }
    4452           1 :         CPLFree(pszWKT);
    4453           1 :         delete poGeom;
    4454             :     }
    4455             : }
    4456             : 
    4457             : // Test OGRGeometryFactory::transformWithOptions()
    4458           4 : TEST_F(test_ogr, transformWithOptions)
    4459             : {
    4460             :     // Projected CRS to national geographic CRS (not including poles or antimeridian)
    4461           1 :     auto [poGeom, err] = OGRGeometryFactory::createFromWkt(
    4462           1 :         "LINESTRING(700000 6600000, 700001 6600001)");
    4463           1 :     ASSERT_NE(poGeom, nullptr);
    4464             : 
    4465           1 :     OGRSpatialReference oEPSG_2154;
    4466           1 :     oEPSG_2154.importFromEPSG(2154);  // "RGF93 v1 / Lambert-93"
    4467           1 :     OGRSpatialReference oEPSG_4171;
    4468           1 :     oEPSG_4171.importFromEPSG(4171);  // "RGF93 v1"
    4469           1 :     oEPSG_4171.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    4470             :     auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    4471           1 :         OGRCreateCoordinateTransformation(&oEPSG_2154, &oEPSG_4171));
    4472           1 :     OGRGeometryFactory::TransformWithOptionsCache oCache;
    4473             :     auto poNewGeom =
    4474             :         std::unique_ptr<OGRGeometry>(OGRGeometryFactory::transformWithOptions(
    4475           1 :             poGeom.get(), poCT.get(), nullptr, oCache));
    4476           1 :     ASSERT_NE(poNewGeom, nullptr);
    4477           1 :     EXPECT_NEAR(poNewGeom->toLineString()->getX(0), 3, 1e-8);
    4478           1 :     EXPECT_NEAR(poNewGeom->toLineString()->getY(0), 46.5, 1e-8);
    4479             : }
    4480             : 
    4481             : #ifdef HAVE_GEOS
    4482             : 
    4483             : // Test OGRGeometryFactory::transformWithOptions()
    4484           4 : TEST_F(test_ogr, transformWithOptions_GEOS)
    4485             : {
    4486             :     // Projected CRS to national geographic CRS including antimeridian
    4487           1 :     auto [poGeom, err] = OGRGeometryFactory::createFromWkt(
    4488           1 :         "LINESTRING(657630.64 4984896.17,815261.43 4990738.26)");
    4489           1 :     ASSERT_NE(poGeom, nullptr);
    4490             : 
    4491           1 :     OGRSpatialReference oEPSG_6329;
    4492           1 :     oEPSG_6329.importFromEPSG(6329);  // "NAD83(2011) / UTM zone 60N"
    4493           1 :     OGRSpatialReference oEPSG_6318;
    4494           1 :     oEPSG_6318.importFromEPSG(6318);  // "NAD83(2011)"
    4495           1 :     oEPSG_6318.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    4496             :     auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    4497           1 :         OGRCreateCoordinateTransformation(&oEPSG_6329, &oEPSG_6318));
    4498           1 :     OGRGeometryFactory::TransformWithOptionsCache oCache;
    4499             :     auto poNewGeom =
    4500             :         std::unique_ptr<OGRGeometry>(OGRGeometryFactory::transformWithOptions(
    4501           1 :             poGeom.get(), poCT.get(), nullptr, oCache));
    4502           1 :     ASSERT_NE(poNewGeom, nullptr);
    4503           1 :     EXPECT_EQ(poNewGeom->getGeometryType(), wkbMultiLineString);
    4504           1 :     if (poNewGeom->getGeometryType() == wkbMultiLineString)
    4505             :     {
    4506           1 :         const auto poMLS = poNewGeom->toMultiLineString();
    4507           1 :         EXPECT_EQ(poMLS->getNumGeometries(), 2);
    4508           1 :         if (poMLS->getNumGeometries() == 2)
    4509             :         {
    4510           1 :             const auto poLS = poMLS->getGeometryRef(0);
    4511           1 :             EXPECT_EQ(poLS->getNumPoints(), 2);
    4512           1 :             if (poLS->getNumPoints() == 2)
    4513             :             {
    4514           1 :                 EXPECT_NEAR(poLS->getX(0), 179, 1e-6);
    4515           1 :                 EXPECT_NEAR(poLS->getY(0), 45, 1e-6);
    4516           1 :                 EXPECT_NEAR(poLS->getX(1), 180, 1e-6);
    4517           1 :                 EXPECT_NEAR(poLS->getY(1), 45.004384301691303, 1e-6);
    4518             :             }
    4519             :         }
    4520             :     }
    4521             : }
    4522             : #endif
    4523             : 
    4524             : // Test OGRCurvePolygon::addRingDirectly
    4525           4 : TEST_F(test_ogr, OGRCurvePolygon_addRingDirectly)
    4526             : {
    4527           1 :     OGRCurvePolygon cp;
    4528             :     OGRGeometry *ring;
    4529             : 
    4530             :     // closed CircularString
    4531           1 :     OGRGeometryFactory::createFromWkt(
    4532             :         "CIRCULARSTRING (0 0, 1 1, 2 0, 1 -1, 0 0)", nullptr, &ring);
    4533           1 :     ASSERT_TRUE(ring);
    4534           1 :     EXPECT_EQ(cp.addRingDirectly(ring->toCurve()), OGRERR_NONE);
    4535             : 
    4536             :     // open CircularString
    4537           1 :     OGRGeometryFactory::createFromWkt("CIRCULARSTRING (0 0, 1 1, 2 0)", nullptr,
    4538             :                                       &ring);
    4539           1 :     ASSERT_TRUE(ring);
    4540             :     {
    4541             :         CPLConfigOptionSetter oSetter("OGR_GEOMETRY_ACCEPT_UNCLOSED_RING", "NO",
    4542           1 :                                       false);
    4543           1 :         ASSERT_EQ(cp.addRingDirectly(ring->toCurve()),
    4544             :                   OGRERR_UNSUPPORTED_GEOMETRY_TYPE);
    4545             :     }
    4546           1 :     EXPECT_EQ(cp.addRingDirectly(ring->toCurve()), OGRERR_NONE);
    4547             : 
    4548             :     // closed CompoundCurve
    4549           1 :     OGRGeometryFactory::createFromWkt(
    4550             :         "COMPOUNDCURVE( CIRCULARSTRING (0 0, 1 1, 2 0), (2 0, 0 0))", nullptr,
    4551             :         &ring);
    4552           1 :     ASSERT_TRUE(ring);
    4553           1 :     EXPECT_EQ(cp.addRingDirectly(ring->toCurve()), OGRERR_NONE);
    4554             : 
    4555             :     // closed LineString
    4556           1 :     OGRGeometryFactory::createFromWkt("LINESTRING (0 0, 1 0, 1 1, 0 1, 0 0)",
    4557             :                                       nullptr, &ring);
    4558           1 :     ASSERT_TRUE(ring);
    4559           1 :     EXPECT_EQ(cp.addRingDirectly(ring->toCurve()), OGRERR_NONE);
    4560             : 
    4561             :     // LinearRing
    4562           1 :     auto lr = std::make_unique<OGRLinearRing>();
    4563           1 :     lr->addPoint(0, 0);
    4564           1 :     lr->addPoint(1, 0);
    4565           1 :     lr->addPoint(1, 1);
    4566           1 :     lr->addPoint(0, 1);
    4567           1 :     lr->addPoint(0, 0);
    4568           1 :     ASSERT_TRUE(ring);
    4569           1 :     ASSERT_EQ(cp.addRingDirectly(lr.get()), OGRERR_UNSUPPORTED_GEOMETRY_TYPE);
    4570             : }
    4571             : 
    4572             : // Test OGRPolygon::addRingDirectly
    4573           4 : TEST_F(test_ogr, OGRPolygon_addRingDirectly)
    4574             : {
    4575           1 :     OGRPolygon p;
    4576             :     OGRGeometry *ring;
    4577             : 
    4578             :     // closed CircularString
    4579           1 :     OGRGeometryFactory::createFromWkt(
    4580             :         "CIRCULARSTRING (0 0, 1 1, 2 0, 1 -1, 0 0)", nullptr, &ring);
    4581           1 :     ASSERT_TRUE(ring);
    4582           1 :     EXPECT_EQ(p.addRingDirectly(ring->toCurve()),
    4583             :               OGRERR_UNSUPPORTED_GEOMETRY_TYPE);
    4584           1 :     delete ring;
    4585             : 
    4586             :     // closed LineString
    4587           1 :     OGRGeometryFactory::createFromWkt("LINESTRING (0 0, 1 0, 1 1, 0 1, 0 0)",
    4588             :                                       nullptr, &ring);
    4589           1 :     ASSERT_TRUE(ring);
    4590           1 :     EXPECT_EQ(p.addRingDirectly(ring->toCurve()),
    4591             :               OGRERR_UNSUPPORTED_GEOMETRY_TYPE);
    4592           1 :     delete ring;
    4593             : 
    4594             :     // open LineString
    4595           1 :     OGRGeometryFactory::createFromWkt("LINESTRING (0 0, 1 0)", nullptr, &ring);
    4596           1 :     ASSERT_TRUE(ring);
    4597           1 :     EXPECT_EQ(p.addRingDirectly(ring->toCurve()),
    4598             :               OGRERR_UNSUPPORTED_GEOMETRY_TYPE);
    4599           1 :     delete ring;
    4600             : 
    4601             :     // LinearRing
    4602           1 :     auto lr = std::make_unique<OGRLinearRing>();
    4603           1 :     lr->addPoint(0, 0);
    4604           1 :     lr->addPoint(1, 0);
    4605           1 :     lr->addPoint(1, 1);
    4606           1 :     lr->addPoint(0, 1);
    4607           1 :     lr->addPoint(0, 0);
    4608           1 :     ASSERT_EQ(p.addRingDirectly(lr.release()), OGRERR_NONE);
    4609             : }
    4610             : 
    4611           4 : TEST_F(test_ogr, OGRFeature_SetGeometry)
    4612             : {
    4613           1 :     OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn();
    4614           1 :     poFeatureDefn->Reference();
    4615             : 
    4616           1 :     OGRFeature oFeat(poFeatureDefn);
    4617           1 :     auto [poGeom, err] = OGRGeometryFactory::createFromWkt("POINT (3 7)");
    4618           1 :     ASSERT_EQ(err, OGRERR_NONE);
    4619             : 
    4620           1 :     ASSERT_EQ(oFeat.SetGeometry(std::move(poGeom)), OGRERR_NONE);
    4621           1 :     EXPECT_EQ(oFeat.GetGeometryRef()->toPoint()->getX(), 3);
    4622           1 :     EXPECT_EQ(oFeat.GetGeometryRef()->toPoint()->getY(), 7);
    4623             : 
    4624             :     // set it again to make sure previous feature geometry is freed
    4625           1 :     std::tie(poGeom, err) = OGRGeometryFactory::createFromWkt("POINT (2 8)");
    4626           1 :     ASSERT_EQ(err, OGRERR_NONE);
    4627           1 :     ASSERT_EQ(oFeat.SetGeometry(std::move(poGeom)), OGRERR_NONE);
    4628           1 :     EXPECT_EQ(oFeat.GetGeometryRef()->toPoint()->getX(), 2);
    4629           1 :     EXPECT_EQ(oFeat.GetGeometryRef()->toPoint()->getY(), 8);
    4630             : 
    4631           1 :     poFeatureDefn->Release();
    4632             : }
    4633             : 
    4634           4 : TEST_F(test_ogr, OGRFeature_SetGeomField)
    4635             : {
    4636           1 :     OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn();
    4637           1 :     poFeatureDefn->Reference();
    4638             : 
    4639           1 :     OGRGeomFieldDefn oGeomField("second", wkbPoint);
    4640           1 :     poFeatureDefn->AddGeomFieldDefn(&oGeomField);
    4641             : 
    4642           1 :     OGRFeature oFeat(poFeatureDefn);
    4643             : 
    4644             :     // failure
    4645             :     {
    4646           1 :         auto [poGeom, err] = OGRGeometryFactory::createFromWkt("POINT (3 7)");
    4647           1 :         ASSERT_EQ(err, OGRERR_NONE);
    4648           1 :         EXPECT_EQ(oFeat.SetGeomField(13, std::move(poGeom)), OGRERR_FAILURE);
    4649             :     }
    4650             : 
    4651             :     // success
    4652             :     {
    4653           1 :         auto [poGeom, err] = OGRGeometryFactory::createFromWkt("POINT (3 7)");
    4654           1 :         ASSERT_EQ(err, OGRERR_NONE);
    4655           1 :         EXPECT_EQ(oFeat.SetGeomField(1, std::move(poGeom)), OGRERR_NONE);
    4656             :     }
    4657             : 
    4658           1 :     poFeatureDefn->Release();
    4659             : }
    4660             : 
    4661           4 : TEST_F(test_ogr, GetArrowStream_DateTime_As_String)
    4662             : {
    4663             :     auto poDS = std::unique_ptr<GDALDataset>(
    4664             :         GetGDALDriverManager()->GetDriverByName("MEM")->Create(
    4665           1 :             "", 0, 0, 0, GDT_Unknown, nullptr));
    4666           1 :     auto poLayer = poDS->CreateLayer("test", nullptr, wkbNone);
    4667           1 :     OGRFieldDefn oFieldDefn("dt", OFTDateTime);
    4668           1 :     EXPECT_EQ(poLayer->CreateField(&oFieldDefn), OGRERR_NONE);
    4669             :     struct ArrowArrayStream stream;
    4670           1 :     CPLStringList aosOptions;
    4671           1 :     aosOptions.SetNameValue("INCLUDE_FID", "NO");
    4672           1 :     aosOptions.SetNameValue("DATETIME_AS_STRING", "YES");
    4673           1 :     ASSERT_TRUE(poLayer->GetArrowStream(&stream, aosOptions.List()));
    4674             :     struct ArrowSchema schema;
    4675           1 :     memset(&schema, 0, sizeof(schema));
    4676           1 :     EXPECT_EQ(stream.get_schema(&stream, &schema), 0);
    4677           1 :     EXPECT_TRUE(schema.n_children == 1 &&
    4678             :                 strcmp(schema.children[0]->format, "u") == 0)
    4679           0 :         << schema.n_children;
    4680           1 :     if (schema.n_children == 1 && strcmp(schema.children[0]->format, "u") == 0)
    4681             :     {
    4682           1 :         EXPECT_TRUE(schema.children[0]->metadata != nullptr);
    4683           1 :         if (schema.children[0]->metadata)
    4684             :         {
    4685             :             auto oMapKeyValue =
    4686           2 :                 OGRParseArrowMetadata(schema.children[0]->metadata);
    4687           1 :             EXPECT_EQ(oMapKeyValue.size(), 1);
    4688           1 :             if (oMapKeyValue.size() == 1)
    4689             :             {
    4690           1 :                 EXPECT_STREQ(oMapKeyValue.begin()->first.c_str(),
    4691             :                              "GDAL:OGR:type");
    4692           1 :                 EXPECT_STREQ(oMapKeyValue.begin()->second.c_str(), "DateTime");
    4693             :             }
    4694             :         }
    4695             :     }
    4696           1 :     schema.release(&schema);
    4697           1 :     stream.release(&stream);
    4698             : }
    4699             : 
    4700             : // Test OGRFeatureDefn::GetFieldSubTypeByName()
    4701           4 : TEST_F(test_ogr, OGRFieldDefnGetFieldSubTypeByName)
    4702             : {
    4703           6 :     for (int i = 0; i < OFSTMaxSubType; i++)
    4704             :     {
    4705             :         const char *pszName =
    4706           5 :             OGRFieldDefn::GetFieldSubTypeName(static_cast<OGRFieldSubType>(i));
    4707           5 :         if (pszName != nullptr)
    4708             :         {
    4709           5 :             EXPECT_EQ(OGRFieldDefn::GetFieldSubTypeByName(pszName), i);
    4710             :         }
    4711             :     }
    4712           1 : }
    4713             : 
    4714             : // Test OGRFeatureDefn::GetFieldTypeByName()
    4715           4 : TEST_F(test_ogr, OGRFieldDefnGetFieldTypeByName)
    4716             : {
    4717          14 :     for (int i = 0; i < OFTMaxType; i++)
    4718             :     {
    4719             :         // deprecated types
    4720          13 :         if (i == OFTWideString || i == OFTWideStringList)
    4721             :         {
    4722           2 :             continue;
    4723             :         }
    4724             :         const char *pszName =
    4725          11 :             OGRFieldDefn::GetFieldTypeName(static_cast<OGRFieldType>(i));
    4726          11 :         if (pszName != nullptr)
    4727             :         {
    4728          11 :             EXPECT_EQ(OGRFieldDefn::GetFieldTypeByName(pszName), i);
    4729             :         }
    4730             :     }
    4731           1 : }
    4732             : 
    4733             : // Test OGRGeometryFactory::GetDefaultArcStepSize()
    4734           4 : TEST_F(test_ogr, GetDefaultArcStepSize)
    4735             : {
    4736           1 :     if (CPLGetConfigOption("OGR_ARC_STEPSIZE", nullptr) == nullptr)
    4737             :     {
    4738           1 :         EXPECT_EQ(OGRGeometryFactory::GetDefaultArcStepSize(), 4.0);
    4739             :     }
    4740             :     {
    4741             :         CPLConfigOptionSetter oSetter("OGR_ARC_STEPSIZE", "0.00001",
    4742           2 :                                       /* bSetOnlyIfUndefined = */ false);
    4743           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    4744           1 :         EXPECT_EQ(OGRGeometryFactory::GetDefaultArcStepSize(), 1e-2);
    4745           1 :         EXPECT_TRUE(
    4746             :             strstr(CPLGetLastErrorMsg(),
    4747             :                    "Too small value for OGR_ARC_STEPSIZE. Clamping it to"));
    4748             :     }
    4749             :     {
    4750             :         CPLConfigOptionSetter oSetter("OGR_ARC_STEPSIZE", "190",
    4751           2 :                                       /* bSetOnlyIfUndefined = */ false);
    4752           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    4753           1 :         EXPECT_EQ(OGRGeometryFactory::GetDefaultArcStepSize(), 180);
    4754           1 :         EXPECT_TRUE(
    4755             :             strstr(CPLGetLastErrorMsg(),
    4756             :                    "Too large value for OGR_ARC_STEPSIZE. Clamping it to"));
    4757             :     }
    4758           1 : }
    4759             : 
    4760           4 : TEST_F(test_ogr, OGRPolygon_two_vertex_constructor)
    4761             : {
    4762           2 :     OGRPolygon p(1, 2, 3, 4);
    4763           1 :     char *outWKT = nullptr;
    4764           1 :     p.exportToWkt(&outWKT, wkbVariantIso);
    4765           1 :     EXPECT_STREQ(outWKT, "POLYGON ((1 2,1 4,3 4,3 2,1 2))");
    4766           1 :     CPLFree(outWKT);
    4767           1 : }
    4768             : 
    4769             : // Test OGRGeometryCollection::addComponents()
    4770           4 : TEST_F(test_ogr, OGRGeometryCollection_addComponents)
    4771             : {
    4772           1 :     auto gc1 = std::make_unique<OGRGeometryCollection>();
    4773           1 :     auto gc2 = std::make_unique<OGRGeometryCollection>();
    4774             : 
    4775           3 :     for (const auto &wkt : {"POINT (3 7)", "LINESTRING M (9 3 2, 4 1 9)"})
    4776             :     {
    4777           2 :         auto [poGeom, err] = OGRGeometryFactory::createFromWkt(wkt);
    4778           2 :         ASSERT_EQ(err, OGRERR_NONE);
    4779           2 :         gc1->addGeometry(std::move(poGeom));
    4780             :     }
    4781             : 
    4782           2 :     for (const auto &wkt :
    4783           3 :          {"POINT Z (6 4 8)", "POLYGON ((0 0, 1 1, 0 1, 0 0))"})
    4784             :     {
    4785           2 :         auto [poGeom, err] = OGRGeometryFactory::createFromWkt(wkt);
    4786           2 :         ASSERT_EQ(err, OGRERR_NONE);
    4787           2 :         gc2->addGeometry(std::move(poGeom));
    4788             :     }
    4789             : 
    4790           1 :     ASSERT_EQ(gc1->addGeometryComponents(std::move(gc2)), OGRERR_NONE);
    4791             : 
    4792           1 :     OGRWktOptions wktOptions;
    4793           1 :     wktOptions.variant = wkbVariantIso;
    4794             : 
    4795           1 :     EXPECT_EQ(gc1->getNumGeometries(), 4);
    4796           1 :     EXPECT_EQ(gc1->getGeometryType(), wkbGeometryCollectionZM);
    4797           2 :     EXPECT_EQ(gc1->getGeometryRef(0)->exportToWkt(wktOptions),
    4798             :               "POINT ZM (3 7 0 0)");
    4799           2 :     EXPECT_EQ(gc1->getGeometryRef(1)->exportToWkt(wktOptions),
    4800             :               "LINESTRING ZM (9 3 0 2,4 1 0 9)");
    4801           2 :     EXPECT_EQ(gc1->getGeometryRef(2)->exportToWkt(wktOptions),
    4802             :               "POINT ZM (6 4 8 0)");
    4803           2 :     EXPECT_EQ(gc1->getGeometryRef(3)->exportToWkt(wktOptions),
    4804             :               "POLYGON ZM ((0 0 0 0,1 1 0 0,0 1 0 0,0 0 0 0))");
    4805             : }
    4806             : 
    4807             : struct ProgressData
    4808             : {
    4809             :     std::vector<double> adfRatios;
    4810             :     bool bInterrupt{false};
    4811             : };
    4812             : 
    4813         107 : int CPL_STDCALL MyProgress(double dfComplete, const char *, void *userData)
    4814             : {
    4815         107 :     auto *progressData = static_cast<ProgressData *>(userData);
    4816         107 :     progressData->adfRatios.push_back(dfComplete);
    4817         107 :     return !progressData->bInterrupt;
    4818             : }
    4819             : 
    4820           4 : TEST_F(test_ogr, GEOSProgress)
    4821             : {
    4822           1 :     auto coll = std::make_unique<OGRGeometryCollection>();
    4823             : 
    4824          11 :     for (int i = 0; i < 10; i++)
    4825             :     {
    4826         110 :         for (int j = 0; j < 10; j++)
    4827             :         {
    4828             :             std::unique_ptr<OGRGeometry> g(OGRGeometry::FromHandle(
    4829         100 :                 OGR_G_CreateFromEnvelope(i, j, i + 1, j + 1, nullptr)));
    4830         100 :             coll->addGeometry(std::move(g));
    4831             :         }
    4832             :     }
    4833             : 
    4834             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4835             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 15)
    4836             :     // Run to completion
    4837             :     {
    4838           1 :         ProgressData progressData;
    4839             :         std::unique_ptr<OGRGeometry> result(
    4840           1 :             coll->UnaryUnion(MyProgress, &progressData));
    4841             : 
    4842           1 :         ASSERT_TRUE(progressData.adfRatios.size() > 3);
    4843           1 :         EXPECT_TRUE(std::is_sorted(progressData.adfRatios.begin(),
    4844             :                                    progressData.adfRatios.end()));
    4845           1 :         EXPECT_EQ(progressData.adfRatios.back(), 1);
    4846             :     }
    4847             : 
    4848             :     // Interrupt GEOS
    4849             :     {
    4850           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4851             : 
    4852           2 :         ProgressData progressData;
    4853           1 :         progressData.bInterrupt = true;
    4854             :         std::unique_ptr<OGRGeometry> result(
    4855           2 :             coll->UnaryUnion(MyProgress, &progressData));
    4856             : 
    4857           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4858           1 :         EXPECT_TRUE(cpl::starts_with(CPLGetLastErrorMsg(), "Interrupted"));
    4859           1 :         EXPECT_EQ(result, nullptr);
    4860             :     }
    4861             : #else
    4862             :     // Run to completion
    4863             :     {
    4864             :         ProgressData progressData;
    4865             :         std::unique_ptr<OGRGeometry> result(
    4866             :             coll->UnaryUnion(&MyProgress, &progressData));
    4867             :         EXPECT_TRUE(progressData.adfRatios.empty());
    4868             :     }
    4869             : #endif
    4870             : }
    4871             : 
    4872             : }  // namespace

Generated by: LCOV version 1.14