diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 403584d1..0b12ca16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -191,4 +191,7 @@ jobs: - name: Test Supercell Wx working-directory: ${{ github.workspace }}/build + env: + MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }} + MAPTILER_API_KEY: ${{ secrets.MAPTILER_API_KEY }} run: ctest -C ${{ matrix.build_type }} --exclude-regex mbgl-test-runner diff --git a/conanfile.py b/conanfile.py index c6fe1dc1..4a803580 100644 --- a/conanfile.py +++ b/conanfile.py @@ -8,7 +8,7 @@ class SupercellWxConan(ConanFile): "geographiclib/1.52", "glew/2.2.0", "glm/cci.20220420", - "gtest/cci.20210126", + "gtest/1.13.0", "libcurl/7.86.0", "libxml2/2.10.3", "openssl/3.1.0", diff --git a/scwx-qt/source/scwx/qt/map/map_provider.cpp b/scwx-qt/source/scwx/qt/map/map_provider.cpp index 7207e3ba..2a431614 100644 --- a/scwx-qt/source/scwx/qt/map/map_provider.cpp +++ b/scwx-qt/source/scwx/qt/map/map_provider.cpp @@ -42,7 +42,7 @@ static const std::unordered_map mapProviderInfo_ { .drawBelow_ {mapboxDrawBelow_}}, {.name_ {"Satellite"}, .url_ {"mapbox://styles/mapbox/satellite-v9"}, - .drawBelow_ {mapboxDrawBelow_}}, + .drawBelow_ {"com.mapbox.annotations.points"}}, {.name_ {"Satellite Streets"}, .url_ {"mapbox://styles/mapbox/satellite-streets-v11"}, .drawBelow_ {mapboxDrawBelow_}}}}}, @@ -52,51 +52,52 @@ static const std::unordered_map mapProviderInfo_ { .cacheDbName_ {"maptiler-cache.db"}, .settingsTemplate_ { QMapLibreGL::Settings::SettingsTemplate::MapTilerSettings}, - .mapStyles_ {{.name_ {"Satellite"}, - .url_ {"maptiler://maps/hybrid"}, - .drawBelow_ {"tunnel"}}, - {.name_ {"Streets"}, - .url_ {"maptiler://maps/streets-v2"}, - .drawBelow_ {"aeroway"}}, - {.name_ {"Streets Dark"}, - .url_ {"maptiler://maps/streets-v2-dark"}, - .drawBelow_ {"aeroway"}}, - {.name_ {"Basic"}, - .url_ {"maptiler://maps/basic-v2"}, - .drawBelow_ {"railway_transit_tunnel"}}, - {.name_ {"Bright"}, - .url_ {"maptiler://maps/bright-v2"}, - .drawBelow_ {"ferry"}}, - {.name_ {"Dataviz"}, - .url_ {"maptiler://maps/dataviz"}, - .drawBelow_ {"aeroway"}}, - {.name_ {"Dataviz Dark"}, - .url_ {"maptiler://maps/dataviz-dark"}, - .drawBelow_ {"aeroway"}}, - {.name_ {"Outdoor"}, - .url_ {"maptiler://maps/outdoor-v2"}, - .drawBelow_ {"aeroway_runway"}}, - {.name_ {"Swisstopo"}, - .url_ {"maptiler://maps/ch-swisstopo-lbm"}, - .drawBelow_ {"pattern_landcover_vineyard"}}, - {.name_ {"Swisstopo Dark"}, - .url_ {"maptiler://maps/ch-swisstopo-lbm-dark"}, - .drawBelow_ {"pattern_landcover_vineyard"}}, - {.name_ {"Swisstopo Grey"}, - .url_ {"maptiler://maps/ch-swisstopo-lbm-grey"}, - .drawBelow_ {"pattern_landcover_vineyard"}}, - {.name_ {"Swisstopo Vivid"}, - .url_ {"maptiler://maps/ch-swisstopo-lbm-vivid"}, - .drawBelow_ {"pattern_landcover_vineyard"}}, - {.name_ {"Topo"}, - .url_ {"maptiler://maps/topo-v2"}, - .drawBelow_ {"aeroway_runway"}}, - {.name_ {"Topo Dark"}, - .url_ {"maptiler://maps/topo-v2-dark"}, - .drawBelow_ {"aeroway_runway"}}, - {.name_ {"Winter"}, - .url_ {"maptiler://maps/winter-v2"}, - .drawBelow_ {"aeroway_runway"}}}}}, + .mapStyles_ { + {.name_ {"Satellite"}, + .url_ {"maptiler://maps/hybrid"}, + .drawBelow_ {"tunnel"}}, + {.name_ {"Streets"}, + .url_ {"maptiler://maps/streets-v2"}, + .drawBelow_ {"aeroway"}}, + {.name_ {"Streets Dark"}, + .url_ {"maptiler://maps/streets-v2-dark"}, + .drawBelow_ {"aeroway"}}, + {.name_ {"Basic"}, + .url_ {"maptiler://maps/basic-v2"}, + .drawBelow_ {"railway_transit_tunnel", "Transit tunnel"}}, + {.name_ {"Bright"}, + .url_ {"maptiler://maps/bright-v2"}, + .drawBelow_ {"ferry"}}, + {.name_ {"Dataviz"}, + .url_ {"maptiler://maps/dataviz"}, + .drawBelow_ {"aeroway"}}, + {.name_ {"Dataviz Dark"}, + .url_ {"maptiler://maps/dataviz-dark"}, + .drawBelow_ {"aeroway"}}, + {.name_ {"Outdoor"}, + .url_ {"maptiler://maps/outdoor-v2"}, + .drawBelow_ {"aeroway_runway", "Aeroway"}}, + {.name_ {"Swisstopo"}, + .url_ {"maptiler://maps/ch-swisstopo-lbm"}, + .drawBelow_ {"pattern_landcover_vineyard", "Vineyard pattern"}}, + {.name_ {"Swisstopo Dark"}, + .url_ {"maptiler://maps/ch-swisstopo-lbm-dark"}, + .drawBelow_ {"pattern_landcover_vineyard", "Vineyard pattern"}}, + {.name_ {"Swisstopo Grey"}, + .url_ {"maptiler://maps/ch-swisstopo-lbm-grey"}, + .drawBelow_ {"pattern_landcover_vineyard", "Vineyard pattern"}}, + {.name_ {"Swisstopo Vivid"}, + .url_ {"maptiler://maps/ch-swisstopo-lbm-vivid"}, + .drawBelow_ {"pattern_landcover_vineyard", "Vineyard pattern"}}, + {.name_ {"Topo"}, + .url_ {"maptiler://maps/topo-v2"}, + .drawBelow_ {"aeroway_runway", "Runway"}}, + {.name_ {"Topo Dark"}, + .url_ {"maptiler://maps/topo-v2-dark"}, + .drawBelow_ {"aeroway_runway", "Runway"}}, + {.name_ {"Winter"}, + .url_ {"maptiler://maps/winter-v2"}, + .drawBelow_ {"aeroway_runway", "Aeroway"}}}}}, {MapProvider::Unknown, MapProviderInfo {}}}; MapProvider GetMapProvider(const std::string& name) diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 595bea03..cc35ef85 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -674,13 +674,14 @@ void MapWidget::AddLayers() const std::string layer = qlayer.toStdString(); // Draw below layers defined in map style - auto it = std::find_if(mapStyle.drawBelow_.cbegin(), - mapStyle.drawBelow_.cend(), - [&layer](const std::string& styleLayer) -> bool - { - std::regex re {styleLayer}; - return std::regex_match(layer, re); - }); + auto it = std::find_if( + mapStyle.drawBelow_.cbegin(), + mapStyle.drawBelow_.cend(), + [&layer](const std::string& styleLayer) -> bool + { + std::regex re {styleLayer, std::regex_constants::icase}; + return std::regex_match(layer, re); + }); if (it != mapStyle.drawBelow_.cend()) { diff --git a/test/source/scwx/qt/map/map_provider.test.cpp b/test/source/scwx/qt/map/map_provider.test.cpp new file mode 100644 index 00000000..f3883992 --- /dev/null +++ b/test/source/scwx/qt/map/map_provider.test.cpp @@ -0,0 +1,153 @@ +#include +#include +#include + +#include + +#include +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace map +{ + +static const std::string logPrefix_ {"scwx::qt::map::map_provider.test"}; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); + +class ByMapProviderTest : + public testing::TestWithParam> +{ +}; + +TEST_P(ByMapProviderTest, MapProviderLayers) +{ + auto& [mapProvider, apiKeyName] = GetParam(); + + // Configure API key + std::string apiKey = scwx::util::GetEnvironment(apiKeyName); + if (apiKey.empty()) + { + logger_->info("API key not set, skipping test"); + EXPECT_EQ(true, true); + return; + } + + // Setup QCoreApplication + int argc = 1; + const char* argv[] = {"arg", nullptr}; + QCoreApplication application(argc, const_cast(argv)); + + // Configure map provider + const MapProviderInfo& mapProviderInfo = GetMapProviderInfo(mapProvider); + + // Configure QMapLibreGL + QMapLibreGL::Settings mapSettings {}; + mapSettings.resetToTemplate(mapProviderInfo.settingsTemplate_); + mapSettings.setApiKey(QString::fromStdString(apiKey)); + + QMapLibreGL::Map map(nullptr, mapSettings, QSize(1, 1)); + application.processEvents(); + + // Connect style load completion signal + QObject::connect( + &map, + &QMapLibreGL::Map::mapChanged, + [&](QMapLibreGL::Map::MapChange mapChange) + { + if (mapChange == + QMapLibreGL::Map::MapChange::MapChangeDidFinishLoadingStyle) + { + application.exit(); + } + }); + + // Connect timeout timer + bool timeout = false; + QTimer timeoutTimer {}; + timeoutTimer.setSingleShot(true); + QObject::connect(&timeoutTimer, + &QTimer::timeout, + [&]() + { + // Reached timeout + logger_->warn("Timed out waiting for style change"); + timeout = true; + + application.exit(); + }); + + // Iterate through each style + for (const auto& mapStyle : mapProviderInfo.mapStyles_) + { + using namespace std::chrono_literals; + + // Load style + timeout = false; + map.setStyleUrl(QString::fromStdString(mapStyle.url_)); + timeoutTimer.start(5000ms); + application.exec(); + timeoutTimer.stop(); + + // Check result + if (!timeout) + { + // Print layer names for debug + std::string layerIdsString = map.layerIds().join(", ").toStdString(); + logger_->debug("{} Layers: [{}]", mapStyle.name_, layerIdsString); + + // Search layer list + bool foundMatch = false; + for (const QString& qlayer : map.layerIds()) + { + const std::string layer = qlayer.toStdString(); + + // Draw below layers defined in map style + auto it = std::find_if( + mapStyle.drawBelow_.cbegin(), + mapStyle.drawBelow_.cend(), + [&layer](const std::string& styleLayer) -> bool + { + std::regex re {styleLayer, std::regex_constants::icase}; + return std::regex_match(layer, re); + }); + + if (it != mapStyle.drawBelow_.cend()) + { + foundMatch = true; + break; + } + } + + // Check match + EXPECT_EQ(foundMatch, true); + + if (!foundMatch) + { + logger_->error("Could not find drawBelow in style {}", + mapStyle.name_); + } + } + else + { + EXPECT_EQ(timeout, false); + } + } + + EXPECT_EQ(false, false); +} + +INSTANTIATE_TEST_SUITE_P( + MapProviderTest, + ByMapProviderTest, + testing::Values(std::make_pair(MapProvider::Mapbox, "MAPBOX_API_KEY"), + std::make_pair(MapProvider::MapTiler, "MAPTILER_API_KEY"))); + +} // namespace map +} // namespace qt +} // namespace scwx diff --git a/test/test.cmake b/test/test.cmake index 0e67647e..b0731ddc 100644 --- a/test/test.cmake +++ b/test/test.cmake @@ -23,6 +23,7 @@ set(SRC_QT_CONFIG_TESTS source/scwx/qt/config/county_database.test.cpp source/scwx/qt/config/radar_site.test.cpp) set(SRC_QT_MANAGER_TESTS source/scwx/qt/manager/settings_manager.test.cpp source/scwx/qt/manager/update_manager.test.cpp) +set(SRC_QT_MAP_TESTS source/scwx/qt/map/map_provider.test.cpp) set(SRC_QT_MODEL_TESTS source/scwx/qt/model/imgui_context_model.test.cpp) set(SRC_QT_SETTINGS_TESTS source/scwx/qt/settings/settings_container.test.cpp source/scwx/qt/settings/settings_variable.test.cpp) @@ -44,6 +45,7 @@ add_executable(wxtest ${SRC_MAIN} ${SRC_PROVIDER_TESTS} ${SRC_QT_CONFIG_TESTS} ${SRC_QT_MANAGER_TESTS} + ${SRC_QT_MAP_TESTS} ${SRC_QT_MODEL_TESTS} ${SRC_QT_SETTINGS_TESTS} ${SRC_QT_UTIL_TESTS} @@ -58,6 +60,7 @@ source_group("Source Files\\network" FILES ${SRC_NETWORK_TESTS}) source_group("Source Files\\provider" FILES ${SRC_PROVIDER_TESTS}) source_group("Source Files\\qt\\config" FILES ${SRC_QT_CONFIG_TESTS}) source_group("Source Files\\qt\\manager" FILES ${SRC_QT_MANAGER_TESTS}) +source_group("Source Files\\qt\\map" FILES ${SRC_QT_MAP_TESTS}) source_group("Source Files\\qt\\model" FILES ${SRC_QT_MODEL_TESTS}) source_group("Source Files\\qt\\settings" FILES ${SRC_QT_SETTINGS_TESTS}) source_group("Source Files\\qt\\util" FILES ${SRC_QT_UTIL_TESTS}) diff --git a/wxdata/include/scwx/util/environment.hpp b/wxdata/include/scwx/util/environment.hpp index e90b8515..4295ba2d 100644 --- a/wxdata/include/scwx/util/environment.hpp +++ b/wxdata/include/scwx/util/environment.hpp @@ -7,7 +7,8 @@ namespace scwx namespace util { -void SetEnvironment(const std::string& name, const std::string& value); +std::string GetEnvironment(const std::string& name); +void SetEnvironment(const std::string& name, const std::string& value); } // namespace util } // namespace scwx diff --git a/wxdata/source/scwx/util/environment.cpp b/wxdata/source/scwx/util/environment.cpp index 0958a395..145b2c38 100644 --- a/wxdata/source/scwx/util/environment.cpp +++ b/wxdata/source/scwx/util/environment.cpp @@ -13,6 +13,40 @@ namespace util static const std::string logPrefix_ {"scwx::util::environment"}; static const auto logger_ = util::Logger::Create(logPrefix_); +std::string GetEnvironment(const std::string& name) +{ + std::string value {}; + +#ifdef _WIN32 + std::size_t requiredSize; + std::vector data {}; + + // Determine environment variable size + getenv_s(&requiredSize, nullptr, 0, name.c_str()); + if (requiredSize == 0) + { + // Environment variable is not set + return value; + } + + // Request environment variable + data.resize(requiredSize); + getenv_s(&requiredSize, data.data(), requiredSize, name.c_str()); + + // Store environment variable + value = data.data(); +#else + const char* data = getenv(name.c_str()); + + if (data != nullptr) + { + value = data; + } +#endif + + return value; +} + void SetEnvironment(const std::string& name, const std::string& value) { #ifdef _WIN32