From 4c45d6c74faf54cad80c7c58918eb923406dd47f Mon Sep 17 00:00:00 2001 From: Paul Meems Date: Thu, 12 Nov 2026 10:57:01 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=85=20test(cmake):=20integrate=20Goog?= =?UTF-8?q?leTest=20with=20CTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add the native test target with shared ActiveX sources and test hooks. - Register per-test discovery, runtime dependencies, and XML reports with CTest. - Document CMake test workflows and preserve the MSBuild activation path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CMakeLists.txt | 5 + docs/BUILDING.md | 15 +-- docs/TESTING.md | 61 +++++++++- tests/MapWinGIS.Core.Tests/CMakeLists.txt | 112 ++++++++++++++++++ .../Verify-ActiveXActivation.ps1 | 19 +-- 5 files changed, 194 insertions(+), 18 deletions(-) create mode 100644 tests/MapWinGIS.Core.Tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c9a01d..5a2b221 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,3 +71,8 @@ mwg_add_runtime_data_target() add_subdirectory(src/MapWinGIS.Core) add_subdirectory(src/MapWinGIS.ActiveX) + +if(MWG_BUILD_TESTS) + enable_testing() + add_subdirectory(tests/MapWinGIS.Core.Tests) +endif() diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 2547947..f8046ac 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -198,19 +198,20 @@ MSBuild settings and the decisions the CMake build follows are in ### Building with CMake -The CMake build currently covers `MapWinGIS.Core.dll` and `MapWinGIS.ocx`; the -tests and samples follow. The OCX needs the **C++ ATL for latest v145 build -tools** component for each architecture you build, and `midl.exe` needs `cl.exe` -on `PATH`, so configure and build from a Visual Studio toolchain (CLion) or a -Developer PowerShell. CMake 3.28 or later and Ninja are required; both ship -with Visual Studio 2026 and CLion. Run the bootstrap script first, then -configure from a VS 2026 Developer PowerShell (or any shell after +The CMake build covers `MapWinGIS.Core.dll`, `MapWinGIS.ocx`, and the native +GoogleTest suite; the samples follow. The OCX needs the **C++ ATL for latest +v145 build tools** component for each architecture you build. `midl.exe` needs +`cl.exe` on `PATH`, so configure and build from a Visual Studio toolchain +(CLion) or a Developer PowerShell. CMake 3.28 or later and Ninja are required; +both ship with Visual Studio 2026 and CLion. Run the bootstrap script first, +then configure from a VS 2026 Developer PowerShell (or any shell after `vcvarsall.bat x64`): ```powershell pwsh -File build\scripts\bootstrap-vcpkg.ps1 cmake --preset x64-debug cmake --build --preset x64-debug +ctest --preset x64-debug --output-on-failure -LE interactive ``` | Preset | Generator | Build directory | diff --git a/docs/TESTING.md b/docs/TESTING.md index fdc2a42..60c679e 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -1,8 +1,9 @@ # Testing -- Framework: GoogleTest, provided by the vcpkg `tests` feature (enabled by - default through `MwgVcpkgFeatures`). -- Test project: `tests/MapWinGIS.Core.Tests`, built with the solution. +- Framework: GoogleTest from the vcpkg `tests` feature (enabled by the MSBuild + default and when `MWG_BUILD_TESTS` is on for CMake). +- Test project: `tests/MapWinGIS.Core.Tests`, built with the MSBuild solution + or CMake. - Shared fixtures and helpers live in `TestData.h/.cpp` (`TempDirectory`, `MakeSampleShapefile`, `MakeProvincesDataset`, and layer constants). Extend these instead of writing per-file fixture code. @@ -17,7 +18,59 @@ bin\Release\x64\MapWinGIS.Core.Tests.exe --gtest_output=xml:testresults.xml # Also runnable from Visual Studio Test Explorer (GoogleTest adapter picks it up automatically). -## ActiveX host integration checks +### CMake and CTest + +The CMake test target uses the same GoogleTest sources and fixtures: + +```powershell +cmake --preset x64-debug +cmake --build --preset x64-debug +ctest --preset x64-debug --output-on-failure -LE interactive +``` + +`gtest_discover_tests()` discovers individual cases before CTest runs them. +Each test runs from the build's runtime directory, which contains the OCX, +dependent DLLs, and GDAL/PROJ data. GoogleTest writes per-test XML files under +`test-results` in the build directory. CTest can also write a single JUnit +report with `ctest --test-dir out/b/x64d --output-junit out/b/x64d/ctest.xml`. + +To run one case from CTest, match its fully qualified GoogleTest name: + +```powershell +ctest --test-dir out/b/x64d -R 'VectorDatasetTest.ReportsTheDriverAndPath' --output-on-failure +``` + +## ActiveX integration checks + +### Registered native control check + +The native verification script registers the control for the current user, +creates it through its ProgID, checks the automation boundary, and removes +the per-user registration. It checks both per-user registry views first and +refuses to run if the control's CLSID or either ProgID is already registered. +Run it from a PowerShell process that matches the target architecture: + +```powershell +pwsh -File tests\MapWinGIS.Core.Tests\Verify-ActiveXActivation.ps1 -Configuration Debug -Platform x64 +``` + +For a CMake build, pass its runtime directory: + +```powershell +pwsh -File tests\MapWinGIS.Core.Tests\Verify-ActiveXActivation.ps1 ` + -Configuration Debug -Platform x64 -BinDirectory out\b\x64d\bin +``` + +CTest registers this registration-based check with the `interactive` label +when a matching PowerShell process is available. It changes the per-user COM +registration and is excluded from the CTest presets and CI; run it explicitly +from an interactive desktop when needed: + +```powershell +ctest --test-dir out/b/x64d -R '^Verify-ActiveXActivation$' --output-on-failure +``` + +### C# ActiveX test host The .NET 10 WinForms host exercises the real ActiveX control through `AxHost` using registration-free COM activation from its embedded application diff --git a/tests/MapWinGIS.Core.Tests/CMakeLists.txt b/tests/MapWinGIS.Core.Tests/CMakeLists.txt new file mode 100644 index 0000000..7a83683 --- /dev/null +++ b/tests/MapWinGIS.Core.Tests/CMakeLists.txt @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: MPL-2.0 +# +# Native GoogleTest suite for the Core API and ActiveX rendering components. + +find_package(GDAL CONFIG REQUIRED) +find_package(GTest CONFIG REQUIRED) +include(GoogleTest) + +# vcpkg marks GoogleTest headers as system, conflicting with /external:W4. +set_property(TARGET GTest::gtest PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "") + +set(MWG_CORE_TEST_HEADERS + TestData.h +) + +set(MWG_CORE_TEST_SOURCES + ActiveXActivationTests.cpp + ComConversionTests.cpp + GdalAlgorithmRunnerTests.cpp + GdalEnvironmentTests.cpp + GdalModernCapabilitiesTests.cpp + GdalOperationDispatcherTests.cpp + GdalRuntimeTests.cpp + GdalTypedOptionsTests.cpp + GdalUtilsTests.cpp + Main.cpp + RenderingBaselineTests.cpp + TestData.cpp + VectorDatasetTests.cpp + VectorLayerRendererTests.cpp + VectorLayerTests.cpp + VectorRenderSceneTests.cpp +) + +add_executable(MapWinGIS.Core.Tests) +target_sources(MapWinGIS.Core.Tests PRIVATE + ${MWG_CORE_TEST_HEADERS} + ${MWG_CORE_TEST_SOURCES} + ${MWG_ACTIVEX_SHARED_SOURCES} +) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${MWG_CORE_TEST_HEADERS} + ${MWG_CORE_TEST_SOURCES} +) +source_group("ActiveX Test Sources" FILES ${MWG_ACTIVEX_SHARED_SOURCES}) + +target_include_directories(MapWinGIS.Core.Tests PRIVATE + "${PROJECT_SOURCE_DIR}/src/MapWinGIS.ActiveX" +) + +target_compile_definitions(MapWinGIS.Core.Tests PRIVATE + MWG_ENABLE_RENDERER_TEST_HOOKS +) + +target_link_libraries(MapWinGIS.Core.Tests PRIVATE + MapWinGIS::Core + MapWinGIS.ActiveX.Interface.Generated + GDAL::GDAL + GTest::gtest + ole32 + oleaut32 + uuid + gdi32 + user32 + opengl32 + psapi +) + +mwg_apply_common_options(MapWinGIS.Core.Tests) +target_link_options(MapWinGIS.Core.Tests PRIVATE /SUBSYSTEM:CONSOLE) +set_target_properties(MapWinGIS.Core.Tests PROPERTIES FOLDER "tests") + +add_dependencies(MapWinGIS.Core.Tests + MapWinGIS.ActiveX + MapWinGIS.RuntimeData +) + +file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/test-results") +gtest_discover_tests(MapWinGIS.Core.Tests + DISCOVERY_MODE PRE_TEST + DISCOVERY_TIMEOUT 120 + WORKING_DIRECTORY "${MWG_RUNTIME_DIR}" + XML_OUTPUT_DIR "${PROJECT_BINARY_DIR}/test-results" +) + +if(MWG_PLATFORM STREQUAL "Win32") + set(_mwg_activation_shell + "$ENV{WINDIR}/SysWOW64/WindowsPowerShell/v1.0/powershell.exe") + if(NOT EXISTS "${_mwg_activation_shell}") + unset(_mwg_activation_shell) + endif() +else() + find_program(_mwg_activation_shell NAMES pwsh powershell) +endif() + +if(_mwg_activation_shell) + add_test(NAME Verify-ActiveXActivation + COMMAND "${_mwg_activation_shell}" -NoProfile -File + "${CMAKE_CURRENT_SOURCE_DIR}/Verify-ActiveXActivation.ps1" + -Configuration "$" + -Platform "${MWG_PLATFORM}" + -BinDirectory "${MWG_RUNTIME_DIR}" + ) + set_tests_properties(Verify-ActiveXActivation PROPERTIES + LABELS "interactive" + WORKING_DIRECTORY "${MWG_RUNTIME_DIR}" + ) +else() + message(STATUS + "Skipping the interactive ActiveX activation CTest entry: no PowerShell process " + "matching ${MWG_PLATFORM} was found.") +endif() diff --git a/tests/MapWinGIS.Core.Tests/Verify-ActiveXActivation.ps1 b/tests/MapWinGIS.Core.Tests/Verify-ActiveXActivation.ps1 index 2b847b9..df748d4 100644 --- a/tests/MapWinGIS.Core.Tests/Verify-ActiveXActivation.ps1 +++ b/tests/MapWinGIS.Core.Tests/Verify-ActiveXActivation.ps1 @@ -3,7 +3,9 @@ param( [string]$Configuration = 'Debug', [ValidateSet('Win32', 'x64')] - [string]$Platform = 'x64' + [string]$Platform = 'x64', + + [string]$BinDirectory = '' ) $ErrorActionPreference = 'Stop' @@ -13,9 +15,14 @@ if ([IntPtr]::Size -ne $expectedPointerSize) { throw "Run this script in a $Platform PowerShell process to match the control." } -$controlPath = [System.IO.Path]::GetFullPath( - (Join-Path $PSScriptRoot "..\..\bin\$Configuration\$Platform\MapWinGIS.ocx") -) +$binDirectory = if ([string]::IsNullOrWhiteSpace($BinDirectory)) { + Join-Path $PSScriptRoot "..\..\bin\$Configuration\$Platform" +} else { + $BinDirectory +} +$binDirectory = [System.IO.Path]::GetFullPath($binDirectory) + +$controlPath = Join-Path $binDirectory 'MapWinGIS.ocx' if (-not (Test-Path -LiteralPath $controlPath -PathType Leaf)) { throw "The ActiveX control was not found at '$controlPath'. Build the solution first." } @@ -78,9 +85,7 @@ try { [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($control) $control = $null - $testExecutable = [System.IO.Path]::GetFullPath( - (Join-Path $PSScriptRoot "..\..\bin\$Configuration\$Platform\MapWinGIS.Core.Tests.exe") - ) + $testExecutable = Join-Path $binDirectory 'MapWinGIS.Core.Tests.exe' if (-not (Test-Path -LiteralPath $testExecutable -PathType Leaf)) { throw "The ActiveX hosting test executable was not found at '$testExecutable'. Build the solution first." } From 8e19246275a86dd54b601a30c28beaabc4d1be77 Mon Sep 17 00:00:00 2001 From: Paul Meems Date: Thu, 12 Nov 2026 11:01:48 +0100 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=A7=20build(cmake):=20explain=20Ac?= =?UTF-8?q?tiveX=20MIDL=20include=20parity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Classify the MSBuild ActiveX intermediate path replaced by CMake MIDL output. - Keep the test-target parity report focused on unexplained flag differences. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build/scripts/Compare-CompileCommands.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/scripts/Compare-CompileCommands.ps1 b/build/scripts/Compare-CompileCommands.ps1 index 3b3cfbf..19467d8 100644 --- a/build/scripts/Compare-CompileCommands.ps1 +++ b/build/scripts/Compare-CompileCommands.ps1 @@ -89,6 +89,7 @@ $explainedLinkDifferences = [ordered]@{ # Compile differences that are deliberate (docs/CMAKE-MIGRATION.md). $explainedCompileDifferences = [ordered]@{ + '^/I/obj/MapWinGIS\.ActiveX/[^/]+/[^/]+$' = 'MSBuild ActiveX intdir replaced by the CMake MIDL directory (D3)' '^/I$' = 'MIDL output moved from the intermediate directory to /.../midl (D3)' '^/I/.*/midl$' = 'MIDL output directory (D3)' '^/I/[^/]+/include$' = 'vcpkg auto-include; targets without a vcpkg dependency do not get it, the others get it from imported targets'