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

Generated by: LCOV version 1.14