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

Generated by: LCOV version 1.14