[mapguide-commits] r10242 - in branches/4.0/MgDev: Server/src/Services/Feature Web/src/HttpHandler Web/src/WebTestRunner

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Mon Nov 2 15:43:24 PST 2026


Author: jng
Date: 2026-11-02 15:43:21 -0800 (Mon, 02 Nov 2026)
New Revision: 10242

Modified:
   branches/4.0/MgDev/Server/src/Services/Feature/TransformCache.h
   branches/4.0/MgDev/Server/src/Services/Feature/WfsQueryAdapter.cpp
   branches/4.0/MgDev/Web/src/HttpHandler/HttpWmsGetFeatureInfo.cpp
   branches/4.0/MgDev/Web/src/WebTestRunner/OgcTest.cpp
Log:
#2894: Plug more leaks at the web tier and add REGRESS.xml.awd self-testing to WebTestRunner

Modified: branches/4.0/MgDev/Server/src/Services/Feature/TransformCache.h
===================================================================
--- branches/4.0/MgDev/Server/src/Services/Feature/TransformCache.h	2026-11-02 16:29:42 UTC (rev 10241)
+++ branches/4.0/MgDev/Server/src/Services/Feature/TransformCache.h	2026-11-02 23:43:21 UTC (rev 10242)
@@ -39,6 +39,9 @@
     void SetEnvelope(MgEnvelope* extent);
     MgEnvelope* GetEnvelope();
 
+    // Ownership: with reverse == true the returned entry belongs to `cache` (released by Clear);
+    // with reverse == false a fresh entry is built and not cached, so the caller owns it and must
+    // delete it. The two directions cannot share a cache key, hence the asymmetry.
     static TransformCache* GetLayerToMapTransform(TransformCacheMap& cache,
                                                   CREFSTRING featureName,
                                                   MgResourceIdentifier* resId,

Modified: branches/4.0/MgDev/Server/src/Services/Feature/WfsQueryAdapter.cpp
===================================================================
--- branches/4.0/MgDev/Server/src/Services/Feature/WfsQueryAdapter.cpp	2026-11-02 16:29:42 UTC (rev 10241)
+++ branches/4.0/MgDev/Server/src/Services/Feature/WfsQueryAdapter.cpp	2026-11-02 23:43:21 UTC (rev 10242)
@@ -111,8 +111,12 @@
     }
 
     //get a transform from feature space to mapping space
-    TransformCache* item = TransformCache::GetLayerToMapTransform(m_transformCache, m_className, m_fs, m_mapCs, &fact, m_featSvc, false);
-    m_xform = item ? item->GetMgTransform() : NULL;
+    // A reverse == false lookup is not cached - the entries the cache holds are the map-to-feature
+    // direction, so both directions cannot share a key - which makes the result this call's to
+    // delete. The transform it carries is reference counted, so m_xform keeps what the query needs.
+    unique_ptr<TransformCache> featureToMapXform(
+        TransformCache::GetLayerToMapTransform(m_transformCache, m_className, m_fs, m_mapCs, &fact, m_featSvc, false));
+    m_xform = featureToMapXform.get() ? featureToMapXform->GetMgTransform() : NULL;
 
     assert(m_classDef.p != NULL);
     if (NULL == m_classDef.p)

Modified: branches/4.0/MgDev/Web/src/HttpHandler/HttpWmsGetFeatureInfo.cpp
===================================================================
--- branches/4.0/MgDev/Web/src/HttpHandler/HttpWmsGetFeatureInfo.cpp	2026-11-02 16:29:42 UTC (rev 10241)
+++ branches/4.0/MgDev/Web/src/HttpHandler/HttpWmsGetFeatureInfo.cpp	2026-11-02 23:43:21 UTC (rev 10242)
@@ -168,12 +168,18 @@
     Ptr<MgGeometryFactory> geometryFact = new MgGeometryFactory();
     Ptr<MgCoordinateCollection> linearRingCoords = new MgCoordinateCollection();
 
-    linearRingCoords->Add(new MgCoordinateXY(mcsMinX, mcsMinY));
-    linearRingCoords->Add(new MgCoordinateXY(mcsMinX, mcsMaxY));
-    linearRingCoords->Add(new MgCoordinateXY(mcsMaxX, mcsMaxY));
-    linearRingCoords->Add(new MgCoordinateXY(mcsMaxX, mcsMinY));
-    linearRingCoords->Add(new MgCoordinateXY(mcsMinX, mcsMinY));
+    // Add() takes its own reference, so each of these needs a home for the one that new gives it.
+    Ptr<MgCoordinateXY> mcsMinMin = new MgCoordinateXY(mcsMinX, mcsMinY);
+    Ptr<MgCoordinateXY> mcsMinMax = new MgCoordinateXY(mcsMinX, mcsMaxY);
+    Ptr<MgCoordinateXY> mcsMaxMax = new MgCoordinateXY(mcsMaxX, mcsMaxY);
+    Ptr<MgCoordinateXY> mcsMaxMin = new MgCoordinateXY(mcsMaxX, mcsMinY);
 
+    linearRingCoords->Add(mcsMinMin);
+    linearRingCoords->Add(mcsMinMax);
+    linearRingCoords->Add(mcsMaxMax);
+    linearRingCoords->Add(mcsMaxMin);
+    linearRingCoords->Add(mcsMinMin); // closes the ring
+
     Ptr<MgLinearRing> linearRing = geometryFact->CreateLinearRing(linearRingCoords);
     selectionGeometry = geometryFact->CreatePolygon(linearRing, NULL);
 

Modified: branches/4.0/MgDev/Web/src/WebTestRunner/OgcTest.cpp
===================================================================
--- branches/4.0/MgDev/Web/src/WebTestRunner/OgcTest.cpp	2026-11-02 16:29:42 UTC (rev 10241)
+++ branches/4.0/MgDev/Web/src/WebTestRunner/OgcTest.cpp	2026-11-02 23:43:21 UTC (rev 10242)
@@ -289,4 +289,60 @@
     REQUIRE((L"application/json" == result->GetResultContentType()));
     // This CRS string should be in the GeoJSON
     REQUIRE(respContent.find(L"urn:ogc:def:crs:OGC:1.3:CRS84") != STRING::npos);
-}
\ No newline at end of file
+}
+
+#ifdef _DEBUG
+// The WMS REGRESS template (server/Wms/REGRESS.xml.awd) is a self-test for the OGC template engine:
+// it evaluates each engine feature against a constant baseline and writes <Success/> or <Failure/>
+// for every <Test> it contains. Only a Debug build honours VERSION=REGRESS (the negotiation bypass
+// and the <?Diagnostic?> PI the template relies on are compiled out of Release), so this case runs
+// only there.
+static int CountOccurrences(CREFSTRING text, CREFSTRING token)
+{
+    int count = 0;
+    for (STRING::size_type pos = text.find(token); pos != STRING::npos; pos = text.find(token, pos + token.length()))
+    {
+        count++;
+    }
+    return count;
+}
+
+TEST_CASE("Wms_GetCapabilities_OgcServerSelfTest", "[Wms]")
+{
+    STRING uri = L"";
+    Ptr<MgHttpRequest> req = new MgHttpRequest(uri);
+    Ptr<MgHttpRequestParam> param = req->GetRequestParam();
+
+    MgConfiguration* cfg = MgConfiguration::GetInstance();
+    STRING pwd;
+    cfg->GetStringValue(MgConfigProperties::OgcPropertiesSection, MgConfigProperties::WmsPassword, pwd, L"");
+
+    param->AddParameter(L"USERNAME", MgUser::WmsUser);
+    param->AddParameter(L"PASSWORD", pwd);
+
+    param->AddParameter(L"SERVICE", L"WMS");
+    param->AddParameter(L"REQUEST", L"GETCAPABILITIES");
+    param->AddParameter(L"VERSION", L"REGRESS");
+
+    Ptr<MgHttpResponse> resp = req->Execute();
+    Ptr<MgHttpResult> result = resp->GetResult();
+
+    STRING err = result->GetDetailedErrorMessage();
+    REQUIRE(err.empty());
+
+    REQUIRE((L"text/xml" == result->GetResultContentType()));
+
+    Ptr<MgByteReader> reader = dynamic_cast<MgByteReader*>(result->GetResultObject());
+    REQUIRE(reader.p != nullptr);
+    STRING respContent = reader->ToString();
+
+    // The self-test document, not a real capabilities response, was selected
+    REQUIRE(respContent.find(L"<Tests>") != STRING::npos);
+
+    // Every <Test> writes exactly one <Success/> or <Failure/>
+    int tests = CountOccurrences(respContent, L"<Test name=");
+    REQUIRE(tests > 0);
+    REQUIRE(CountOccurrences(respContent, L"<Success/>") == tests);
+    REQUIRE(CountOccurrences(respContent, L"<Failure/>") == 0);
+}
+#endif



More information about the mapguide-commits mailing list