#include #include #include #include #include #include using namespace vanetza; using namespace vanetza::geodesy; using vanetza::units::degree; namespace { template void append(std::vector& buf, T v) { EndianType e; e = host_cast(v); auto raw = e.get(); const auto* p = reinterpret_cast(&raw); buf.insert(buf.end(), p, p + sizeof(raw)); } void append_u16le(std::vector& buf, uint16_t v) { append(buf, v); } void append_u32le(std::vector& buf, uint32_t v) { append(buf, v); } void append_f64le(std::vector& buf, double v) { uint64_t bits; std::memcpy(&bits, &v, sizeof(bits)); append(buf, bits); } // Build WKB Polygon (LE) for a rectangle std::vector make_rect_wkb(double lon_min, double lat_min, double lon_max, double lat_max) { std::vector wkb; wkb.push_back(0x01); // LE append_u32le(wkb, 3); // Polygon append_u32le(wkb, 1); // 1 ring append_u32le(wkb, 5); // 5 points (closed) append_f64le(wkb, lon_min); append_f64le(wkb, lat_min); append_f64le(wkb, lon_max); append_f64le(wkb, lat_min); append_f64le(wkb, lon_max); append_f64le(wkb, lat_max); append_f64le(wkb, lon_min); append_f64le(wkb, lat_max); append_f64le(wkb, lon_min); append_f64le(wkb, lat_min); return wkb; } std::vector make_entry(uint16_t m49, const std::vector& wkb) { std::vector entry; append_u16le(entry, m49); append_u32le(entry, static_cast(wkb.size())); entry.insert(entry.end(), wkb.begin(), wkb.end()); return entry; } const uint16_t germany_m49 = 276; const uint16_t france_m49 = 250; std::vector make_test_data() { // Germany bounding box (rough): lon 5.9-15.0, lat 47.3-55.1 auto de_wkb = make_rect_wkb(5.9, 47.3, 15.0, 55.1); // France bounding box (rough): lon -5.1-9.6, lat 42.3-51.1 auto fr_wkb = make_rect_wkb(-5.1, 42.3, 9.6, 51.1); auto de_entry = make_entry(germany_m49, de_wkb); auto fr_entry = make_entry(france_m49, fr_wkb); std::vector data; append_u16le(data, vanetza::geodesy::detail::country_data_format_version); data.insert(data.end(), de_entry.begin(), de_entry.end()); data.insert(data.end(), fr_entry.begin(), fr_entry.end()); return data; } } // anonymous namespace TEST(CountryDatabase, initially_empty) { CountryDatabase db; EXPECT_TRUE(db.empty()); } TEST(CountryDatabase, load_from_buffer) { auto data = make_test_data(); CountryDatabase db; EXPECT_TRUE(db.load(data.data(), data.size())); EXPECT_FALSE(db.empty()); } TEST(CountryDatabase, berlin_inside_germany) { auto data = make_test_data(); CountryDatabase db; ASSERT_TRUE(db.load(data.data(), data.size())); GeodeticPosition berlin(52.52 * degree, 13.405 * degree); EXPECT_TRUE(db.is_inside(M49Code(germany_m49), berlin)); } TEST(CountryDatabase, paris_inside_france) { auto data = make_test_data(); CountryDatabase db; ASSERT_TRUE(db.load(data.data(), data.size())); GeodeticPosition paris(48.8566 * degree, 2.3522 * degree); EXPECT_TRUE(db.is_inside(M49Code(france_m49), paris)); } TEST(CountryDatabase, berlin_not_in_france) { auto data = make_test_data(); CountryDatabase db; ASSERT_TRUE(db.load(data.data(), data.size())); GeodeticPosition berlin(52.52 * degree, 13.405 * degree); EXPECT_FALSE(db.is_inside(M49Code(france_m49), berlin)); } TEST(CountryDatabase, mid_atlantic_in_no_country) { auto data = make_test_data(); CountryDatabase db; ASSERT_TRUE(db.load(data.data(), data.size())); GeodeticPosition ocean(40.0 * degree, -30.0 * degree); EXPECT_FALSE(db.is_inside(M49Code(germany_m49), ocean)); EXPECT_FALSE(db.is_inside(M49Code(france_m49), ocean)); } TEST(CountryDatabase, unknown_country_code) { auto data = make_test_data(); CountryDatabase db; ASSERT_TRUE(db.load(data.data(), data.size())); GeodeticPosition berlin(52.52 * degree, 13.405 * degree); EXPECT_FALSE(db.is_inside(M49Code(999), berlin)); } TEST(CountryDatabase, load_invalid_data) { std::vector bad_data = {0x14}; CountryDatabase db; std::string error; EXPECT_FALSE(db.load(bad_data.data(), bad_data.size(), &error)); EXPECT_FALSE(error.empty()); EXPECT_TRUE(db.empty()); } #ifdef VANETZA_WITH_EMBEDDED_COUNTRY_DATA TEST(CountryDatabase, embedded_not_empty) { auto db = CountryDatabase::embedded(); EXPECT_FALSE(db.empty()); } TEST(CountryDatabase, embedded_ingolstadt_in_germany) { auto db = CountryDatabase::embedded(); // Ingolstadt, Germany GeodeticPosition ingolstadt(48.7665 * degree, 11.4258 * degree); EXPECT_TRUE(db.is_inside(M49Code(germany_m49), ingolstadt)); } TEST(CountryDatabase, embedded_etsi_hq_in_france) { auto db = CountryDatabase::embedded(); // ETSI headquarters, Sophia Antipolis, France GeodeticPosition etsi_hq(43.6244 * degree, 7.0494 * degree); EXPECT_TRUE(db.is_inside(M49Code(france_m49), etsi_hq)); } #endif // VANETZA_WITH_EMBEDDED_COUNTRY_DATA