[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