164 lines
6.2 KiB
C++
164 lines
6.2 KiB
C++
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
|
#include "gadm_reader/gadm_reader.h"
|
|
#include <cmath>
|
|
|
|
using namespace gadm;
|
|
using Catch::Matchers::WithinAbs;
|
|
using Catch::Matchers::WithinRel;
|
|
|
|
// ── Helper: fixtures path ───────────────────────────────────────────────────
|
|
// Tests are run with WORKING_DIRECTORY = CMAKE_SOURCE_DIR (server/cpp)
|
|
|
|
static const std::string CACHE_DIR = "cache/gadm";
|
|
|
|
// ── country_code ────────────────────────────────────────────────────────────
|
|
|
|
TEST_CASE("country_code: simple ISO3", "[gadm][util]") {
|
|
REQUIRE(country_code("ABW") == "ABW");
|
|
}
|
|
|
|
TEST_CASE("country_code: dotted GID", "[gadm][util]") {
|
|
REQUIRE(country_code("AFG.1.1_1") == "AFG");
|
|
REQUIRE(country_code("ESP.6.1_1") == "ESP");
|
|
}
|
|
|
|
// ── infer_level ─────────────────────────────────────────────────────────────
|
|
|
|
TEST_CASE("infer_level: level 0 (country)", "[gadm][util]") {
|
|
REQUIRE(infer_level("ABW") == 0);
|
|
REQUIRE(infer_level("AFG") == 0);
|
|
}
|
|
|
|
TEST_CASE("infer_level: level 1", "[gadm][util]") {
|
|
REQUIRE(infer_level("AFG.1_1") == 1);
|
|
}
|
|
|
|
TEST_CASE("infer_level: level 2", "[gadm][util]") {
|
|
REQUIRE(infer_level("AFG.1.1_1") == 2);
|
|
}
|
|
|
|
TEST_CASE("infer_level: level 3", "[gadm][util]") {
|
|
REQUIRE(infer_level("ESP.6.1.4_1") == 3);
|
|
}
|
|
|
|
// ── load_boundary_file: ABW level 0 ────────────────────────────────────────
|
|
|
|
TEST_CASE("Load ABW level 0: basic structure", "[gadm][file]") {
|
|
auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json");
|
|
REQUIRE(res.error.empty());
|
|
REQUIRE(res.features.size() == 1);
|
|
|
|
const auto& f = res.features[0];
|
|
REQUIRE(f.gid == "ABW");
|
|
REQUIRE(f.name == "Aruba");
|
|
REQUIRE(f.level == 0);
|
|
REQUIRE(f.isOuter == true);
|
|
}
|
|
|
|
TEST_CASE("Load ABW level 0: has rings", "[gadm][file]") {
|
|
auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json");
|
|
REQUIRE(res.error.empty());
|
|
const auto& f = res.features[0];
|
|
|
|
REQUIRE(f.rings.size() >= 1);
|
|
REQUIRE(f.rings[0].size() > 10); // ABW has ~55 coords
|
|
}
|
|
|
|
TEST_CASE("Load ABW level 0: GHS population data", "[gadm][file]") {
|
|
auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json");
|
|
REQUIRE(res.error.empty());
|
|
const auto& f = res.features[0];
|
|
|
|
REQUIRE_THAT(f.ghsPopulation, WithinRel(104847.0, 0.01));
|
|
REQUIRE(f.ghsPopCenters.size() == 5);
|
|
// First pop center: [-70.04183, 12.53341, 104.0]
|
|
REQUIRE_THAT(f.ghsPopCenters[0][0], WithinAbs(-70.04183, 0.0001));
|
|
REQUIRE_THAT(f.ghsPopCenters[0][1], WithinAbs(12.53341, 0.0001));
|
|
REQUIRE_THAT(f.ghsPopCenters[0][2], WithinAbs(104.0, 0.1));
|
|
}
|
|
|
|
TEST_CASE("Load ABW level 0: GHS built data", "[gadm][file]") {
|
|
auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json");
|
|
REQUIRE(res.error.empty());
|
|
const auto& f = res.features[0];
|
|
|
|
REQUIRE_THAT(f.ghsBuiltWeight, WithinRel(22900682.0, 0.01));
|
|
REQUIRE(f.ghsBuiltCenters.size() == 5);
|
|
REQUIRE_THAT(f.ghsBuiltCenter.lon, WithinAbs(-69.99304, 0.001));
|
|
REQUIRE_THAT(f.ghsBuiltCenter.lat, WithinAbs(12.51234, 0.001));
|
|
}
|
|
|
|
TEST_CASE("Load ABW level 0: computed bbox", "[gadm][file]") {
|
|
auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json");
|
|
REQUIRE(res.error.empty());
|
|
const auto& f = res.features[0];
|
|
|
|
// ABW bbox should be roughly in the Caribbean
|
|
REQUIRE(f.bbox.minLon < -69.8);
|
|
REQUIRE(f.bbox.maxLon > -70.1);
|
|
REQUIRE(f.bbox.minLat > 12.4);
|
|
REQUIRE(f.bbox.maxLat < 12.7);
|
|
}
|
|
|
|
TEST_CASE("Load ABW level 0: computed area", "[gadm][file]") {
|
|
auto res = load_boundary_file(CACHE_DIR + "/boundary_ABW_0.json");
|
|
REQUIRE(res.error.empty());
|
|
const auto& f = res.features[0];
|
|
|
|
// Aruba is ~180 km²
|
|
REQUIRE_THAT(f.areaSqKm, WithinRel(180.0, 0.15)); // 15% tolerance
|
|
}
|
|
|
|
// ── load_boundary_file: AFG level 2 ────────────────────────────────────────
|
|
|
|
TEST_CASE("Load AFG.1.1_1 level 2: basic structure", "[gadm][file]") {
|
|
auto res = load_boundary_file(CACHE_DIR + "/boundary_AFG.1.1_1_2.json");
|
|
REQUIRE(res.error.empty());
|
|
REQUIRE(res.features.size() == 1);
|
|
|
|
const auto& f = res.features[0];
|
|
REQUIRE(f.gid == "AFG.1.1_1");
|
|
REQUIRE(f.name == "Baharak");
|
|
REQUIRE(f.level == 2);
|
|
}
|
|
|
|
TEST_CASE("Load AFG.1.1_1 level 2: has GHS data", "[gadm][file]") {
|
|
auto res = load_boundary_file(CACHE_DIR + "/boundary_AFG.1.1_1_2.json");
|
|
REQUIRE(res.error.empty());
|
|
const auto& f = res.features[0];
|
|
|
|
REQUIRE(f.ghsPopCenters.size() == 5);
|
|
REQUIRE(f.ghsBuiltCenters.size() == 5);
|
|
REQUIRE(f.ghsPopulation > 0);
|
|
}
|
|
|
|
// ── load_boundary: path resolution ──────────────────────────────────────────
|
|
|
|
TEST_CASE("load_boundary: direct GID match", "[gadm][resolve]") {
|
|
auto res = load_boundary("ABW", 0, CACHE_DIR);
|
|
REQUIRE(res.error.empty());
|
|
REQUIRE(res.features.size() == 1);
|
|
REQUIRE(res.features[0].gid == "ABW");
|
|
}
|
|
|
|
TEST_CASE("load_boundary: sub-region GID", "[gadm][resolve]") {
|
|
auto res = load_boundary("AFG.1.1_1", 2, CACHE_DIR);
|
|
REQUIRE(res.error.empty());
|
|
REQUIRE(res.features[0].gid == "AFG.1.1_1");
|
|
}
|
|
|
|
TEST_CASE("load_boundary: missing file returns error", "[gadm][resolve]") {
|
|
auto res = load_boundary("DOESNOTEXIST", 0, CACHE_DIR);
|
|
REQUIRE(!res.error.empty());
|
|
REQUIRE(res.features.empty());
|
|
}
|
|
|
|
// ── Error handling ──────────────────────────────────────────────────────────
|
|
|
|
TEST_CASE("load_boundary_file: nonexistent file", "[gadm][error]") {
|
|
auto res = load_boundary_file("nonexistent.json");
|
|
REQUIRE(!res.error.empty());
|
|
REQUIRE(res.features.empty());
|
|
}
|