1284 lines
40 KiB
C++
1284 lines
40 KiB
C++
/**
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*
|
|
* This file is part of osm2pgsql (https://osm2pgsql.org/).
|
|
*
|
|
* Copyright (C) 2006-2024 by the osm2pgsql developer community.
|
|
* For a full list of authors see the git log.
|
|
*/
|
|
|
|
#include <catch.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
|
|
#include <osmium/osm/crc.hpp>
|
|
#include <osmium/osm/crc_zlib.hpp>
|
|
|
|
#include "middle-pgsql.hpp"
|
|
#include "middle-ram.hpp"
|
|
#include "osmdata.hpp"
|
|
#include "output-null.hpp"
|
|
#include "output-requirements.hpp"
|
|
|
|
#include "common-buffer.hpp"
|
|
#include "common-cleanup.hpp"
|
|
#include "common-options.hpp"
|
|
#include "common-pg.hpp"
|
|
|
|
namespace {
|
|
|
|
testing::pg::tempdb_t db;
|
|
|
|
void check_locations_are_equal(osmium::Location a, osmium::Location b)
|
|
{
|
|
CHECK(a.lat() == Approx(b.lat()));
|
|
CHECK(a.lon() == Approx(b.lon()));
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
struct options_slim_default
|
|
{
|
|
static options_t options(testing::pg::tempdb_t const &tmpdb)
|
|
{
|
|
return testing::opt_t().slim(tmpdb);
|
|
}
|
|
};
|
|
|
|
struct options_slim_with_lc_prefix
|
|
{
|
|
static options_t options(testing::pg::tempdb_t const &tmpdb)
|
|
{
|
|
options_t o = testing::opt_t().slim(tmpdb);
|
|
o.prefix = "pre";
|
|
return o;
|
|
}
|
|
};
|
|
|
|
struct options_slim_with_uc_prefix
|
|
{
|
|
static options_t options(testing::pg::tempdb_t const &tmpdb)
|
|
{
|
|
options_t o = testing::opt_t().slim(tmpdb);
|
|
o.prefix = "PRE";
|
|
return o;
|
|
}
|
|
};
|
|
|
|
struct options_slim_with_schema
|
|
{
|
|
static options_t options(testing::pg::tempdb_t const &tmpdb)
|
|
{
|
|
options_t o = testing::opt_t().slim(tmpdb);
|
|
o.middle_dbschema = "osm";
|
|
return o;
|
|
}
|
|
};
|
|
|
|
struct options_flat_node_cache
|
|
{
|
|
static options_t options(testing::pg::tempdb_t const &tmpdb)
|
|
{
|
|
return testing::opt_t().slim(tmpdb).flatnodes();
|
|
}
|
|
};
|
|
|
|
struct options_ram_optimized
|
|
{
|
|
static options_t options(testing::pg::tempdb_t const &)
|
|
{
|
|
return testing::opt_t();
|
|
}
|
|
};
|
|
|
|
TEMPLATE_TEST_CASE("middle import", "", options_slim_default,
|
|
options_slim_with_lc_prefix, options_slim_with_uc_prefix,
|
|
options_slim_with_schema, options_ram_optimized)
|
|
{
|
|
options_t const options = TestType::options(db);
|
|
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
|
|
|
|
auto conn = db.connect();
|
|
auto const num_tables =
|
|
conn.get_count("pg_catalog.pg_tables", "schemaname = 'public'");
|
|
auto const num_indexes =
|
|
conn.get_count("pg_catalog.pg_indexes", "schemaname = 'public'");
|
|
auto const num_procs =
|
|
conn.get_count("pg_catalog.pg_proc",
|
|
"pronamespace = (SELECT oid FROM "
|
|
"pg_catalog.pg_namespace WHERE nspname = 'public')");
|
|
|
|
if (options.middle_dbschema == "osm") {
|
|
conn.exec("CREATE SCHEMA IF NOT EXISTS osm;");
|
|
}
|
|
|
|
auto thread_pool = std::make_shared<thread_pool_t>(1U);
|
|
auto mid = create_middle(thread_pool, options);
|
|
mid->start();
|
|
|
|
output_requirements requirements;
|
|
requirements.full_ways = true;
|
|
requirements.full_relations = true;
|
|
mid->set_requirements(requirements);
|
|
|
|
auto const mid_q = mid->get_query_instance();
|
|
|
|
test_buffer_t buffer;
|
|
|
|
SECTION("Set and retrieve a single node")
|
|
{
|
|
auto const &node = buffer.add_node("n1234 x98.7654321 y12.3456789");
|
|
|
|
// set the node
|
|
mid->node(node);
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
// getting it back works only via a waylist
|
|
auto &nodes = buffer.add_way("w3 Nn1234").nodes();
|
|
|
|
// get it back
|
|
REQUIRE(mid_q->nodes_get_list(&nodes) == nodes.size());
|
|
check_locations_are_equal(nodes[0].location(), node.location());
|
|
|
|
// other nodes are not retrievable
|
|
auto &n2 = buffer.add_way("w3 Nn1,n2,n1235").nodes();
|
|
REQUIRE(mid_q->nodes_get_list(&n2) == 0);
|
|
|
|
// check the same thing again using get_node_location() function
|
|
check_locations_are_equal(mid_q->get_node_location(1234),
|
|
node.location());
|
|
CHECK_FALSE(mid_q->get_node_location(1).valid());
|
|
CHECK_FALSE(mid_q->get_node_location(2).valid());
|
|
CHECK_FALSE(mid_q->get_node_location(1235).valid());
|
|
}
|
|
|
|
SECTION("Set and retrieve a single way")
|
|
{
|
|
osmid_t const way_id = 1;
|
|
double const lon = 98.7654321;
|
|
double const lat = 12.3456789;
|
|
idlist_t nds;
|
|
|
|
// set nodes
|
|
for (osmid_t i = 1; i <= 10; ++i) {
|
|
nds.push_back(i);
|
|
auto const &node = buffer.add_node(fmt::format(
|
|
"n{} x{:.7f} y{:.7f}", i, lon - static_cast<double>(i) * 0.003,
|
|
lat + static_cast<double>(i) * 0.001));
|
|
mid->node(node);
|
|
}
|
|
mid->after_nodes();
|
|
|
|
// set the way
|
|
mid->way(buffer.add_way(way_id, nds));
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
// get it back
|
|
osmium::memory::Buffer outbuf{4096,
|
|
osmium::memory::Buffer::auto_grow::yes};
|
|
|
|
REQUIRE(mid_q->way_get(way_id, &outbuf));
|
|
|
|
auto &way = outbuf.get<osmium::Way>(0);
|
|
|
|
CHECK(way.id() == way_id);
|
|
REQUIRE(way.nodes().size() == nds.size());
|
|
|
|
REQUIRE(mid_q->nodes_get_list(&(way.nodes())) == nds.size());
|
|
for (osmid_t i = 1; i <= 10; ++i) {
|
|
auto const &nr = way.nodes()[static_cast<size_t>(i) - 1];
|
|
CHECK(nr.ref() == i);
|
|
CHECK(nr.location().lon() == Approx(lon - i * 0.003));
|
|
CHECK(nr.location().lat() == Approx(lat + i * 0.001));
|
|
}
|
|
|
|
// other ways are not retrievable
|
|
REQUIRE_FALSE(mid_q->way_get(way_id + 1, &outbuf));
|
|
}
|
|
|
|
SECTION("Set and retrieve a single relation with supporting ways")
|
|
{
|
|
std::array<idlist_t, 3> const nds = {
|
|
{{4, 5, 13, 14, 342}, {45, 90}, {30, 3, 45}}};
|
|
|
|
// set the node
|
|
mid->node(buffer.add_node("n1 x4.1 y12.8"));
|
|
mid->after_nodes();
|
|
|
|
// set the ways
|
|
osmid_t wid = 10;
|
|
for (auto const &n : nds) {
|
|
mid->way(buffer.add_way(wid, n));
|
|
++wid;
|
|
}
|
|
mid->after_ways();
|
|
|
|
// set the relation
|
|
auto const &relation =
|
|
buffer.add_relation("r123 Mw11@,w10@outer,n1@,w12@inner");
|
|
|
|
std::vector<std::pair<osmium::item_type, osmid_t>> const expected = {
|
|
{osmium::item_type::way, 11},
|
|
{osmium::item_type::way, 10},
|
|
{osmium::item_type::node, 1},
|
|
{osmium::item_type::way, 12},
|
|
};
|
|
|
|
osmium::CRC<osmium::CRC_zlib> orig_crc;
|
|
orig_crc.update(relation);
|
|
|
|
mid->relation(relation);
|
|
mid->after_relations();
|
|
|
|
// retrieve the relation
|
|
osmium::memory::Buffer outbuf{4096,
|
|
osmium::memory::Buffer::auto_grow::yes};
|
|
REQUIRE(mid_q->relation_get(123, &outbuf));
|
|
auto const &rel = outbuf.get<osmium::Relation>(0);
|
|
|
|
CHECK(rel.id() == 123);
|
|
CHECK(rel.members().size() == 4);
|
|
|
|
osmium::CRC<osmium::CRC_zlib> crc;
|
|
crc.update(rel);
|
|
CHECK(orig_crc().checksum() == crc().checksum());
|
|
|
|
// retrieve node members only
|
|
osmium::memory::Buffer memberbuf{
|
|
4096, osmium::memory::Buffer::auto_grow::yes};
|
|
REQUIRE(mid_q->rel_members_get(rel, &memberbuf,
|
|
osmium::osm_entity_bits::node) == 1);
|
|
|
|
{
|
|
auto const objects = memberbuf.select<osmium::OSMObject>();
|
|
auto it = objects.cbegin();
|
|
auto const end = objects.cend();
|
|
for (auto const &p : expected) {
|
|
if (p.first == osmium::item_type::node) {
|
|
REQUIRE(it != end);
|
|
REQUIRE(it->type() == p.first);
|
|
REQUIRE(it->id() == p.second);
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
memberbuf.clear();
|
|
|
|
// retrieve way members only
|
|
REQUIRE(mid_q->rel_members_get(rel, &memberbuf,
|
|
osmium::osm_entity_bits::way) == 3);
|
|
|
|
{
|
|
auto const objects = memberbuf.select<osmium::OSMObject>();
|
|
auto it = objects.cbegin();
|
|
auto const end = objects.cend();
|
|
for (auto const &p : expected) {
|
|
if (p.first == osmium::item_type::way) {
|
|
REQUIRE(it != end);
|
|
REQUIRE(it->type() == p.first);
|
|
REQUIRE(it->id() == p.second);
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
memberbuf.clear();
|
|
|
|
// retrieve all members
|
|
REQUIRE(mid_q->rel_members_get(rel, &memberbuf,
|
|
osmium::osm_entity_bits::node |
|
|
osmium::osm_entity_bits::way) == 4);
|
|
|
|
{
|
|
auto const objects = memberbuf.select<osmium::OSMObject>();
|
|
auto it = objects.cbegin();
|
|
auto const end = objects.cend();
|
|
for (auto const &p : expected) {
|
|
REQUIRE(it != end);
|
|
REQUIRE(it->type() == p.first);
|
|
REQUIRE(it->id() == p.second);
|
|
++it;
|
|
}
|
|
}
|
|
|
|
// other relations are not retrievable
|
|
REQUIRE_FALSE(mid_q->relation_get(999, &outbuf));
|
|
}
|
|
|
|
if (options.middle_dbschema != "public") {
|
|
REQUIRE(num_tables == conn.get_count("pg_catalog.pg_tables",
|
|
"schemaname = 'public'"));
|
|
REQUIRE(num_indexes == conn.get_count("pg_catalog.pg_indexes",
|
|
"schemaname = 'public'"));
|
|
REQUIRE(num_procs ==
|
|
conn.get_count(
|
|
"pg_catalog.pg_proc",
|
|
"pronamespace = (SELECT oid FROM "
|
|
"pg_catalog.pg_namespace WHERE nspname = 'public')"));
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* Check that the node is in the mid with the right id and location.
|
|
*/
|
|
void check_node(std::shared_ptr<middle_pgsql_t> const &mid,
|
|
osmium::Node const &node)
|
|
{
|
|
test_buffer_t buffer;
|
|
auto &nodes = buffer.add_way(999, {node.id()}).nodes();
|
|
auto const mid_q = mid->get_query_instance();
|
|
REQUIRE(mid_q->nodes_get_list(&nodes) == 1);
|
|
REQUIRE(nodes[0].ref() == node.id());
|
|
REQUIRE(nodes[0].location() == node.location());
|
|
}
|
|
|
|
/// Return true if the node with the specified id is not in the mid.
|
|
bool no_node(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
|
|
{
|
|
test_buffer_t buffer;
|
|
auto &nodes = buffer.add_way(999, {id}).nodes();
|
|
auto const mid_q = mid->get_query_instance();
|
|
return mid_q->nodes_get_list(&nodes) == 0;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
TEMPLATE_TEST_CASE("middle: add, delete and update node", "",
|
|
options_slim_default, options_flat_node_cache)
|
|
{
|
|
auto thread_pool = std::make_shared<thread_pool_t>(1U);
|
|
|
|
options_t options = TestType::options(db);
|
|
|
|
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
|
|
|
|
// Prepare a buffer with some nodes which we will add and change.
|
|
test_buffer_t buffer;
|
|
auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
|
|
auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
|
|
auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
|
|
|
|
auto const &node10a = buffer.add_node("n10 x1.0 y1.0");
|
|
|
|
auto const &node5d = buffer.add_node("n5 dD");
|
|
auto const &node10d = buffer.add_node("n10 dD");
|
|
auto const &node12d = buffer.add_node("n12 dD");
|
|
auto const &node42d = buffer.add_node("n42 dD");
|
|
|
|
// Set up middle in "create" mode to get a cleanly initialized database
|
|
// and add some nodes. Does this in its own scope so that the mid is
|
|
// closed properly.
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
|
|
mid->start();
|
|
|
|
mid->node(node10);
|
|
mid->node(node11);
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
check_node(mid, node10);
|
|
check_node(mid, node11);
|
|
}
|
|
|
|
// From now on use append mode to not destroy the data we just added.
|
|
options.append = true;
|
|
|
|
SECTION("Added nodes are there and no others")
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
check_node(mid, node10);
|
|
check_node(mid, node11);
|
|
REQUIRE(no_node(mid, 5));
|
|
REQUIRE(no_node(mid, 42));
|
|
}
|
|
|
|
SECTION("Delete existing and non-existing node")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->node(node5d);
|
|
mid->node(node10d);
|
|
mid->node(node42d);
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
REQUIRE(no_node(mid, 5));
|
|
REQUIRE(no_node(mid, 10));
|
|
check_node(mid, node11);
|
|
REQUIRE(no_node(mid, 42));
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_node(mid, 5));
|
|
REQUIRE(no_node(mid, 10));
|
|
check_node(mid, node11);
|
|
REQUIRE(no_node(mid, 42));
|
|
}
|
|
}
|
|
|
|
SECTION("Change (delete and set) existing and non-existing node")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->node(node10d);
|
|
mid->node(node10a);
|
|
mid->node(node12d);
|
|
mid->node(node12);
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
check_node(mid, node10a);
|
|
check_node(mid, node11);
|
|
check_node(mid, node12);
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
check_node(mid, node10a);
|
|
check_node(mid, node11);
|
|
check_node(mid, node12);
|
|
}
|
|
}
|
|
|
|
SECTION("Add new node")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->node(node12);
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
REQUIRE(no_node(mid, 5));
|
|
check_node(mid, node10);
|
|
check_node(mid, node11);
|
|
check_node(mid, node12);
|
|
REQUIRE(no_node(mid, 42));
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_node(mid, 5));
|
|
check_node(mid, node10);
|
|
check_node(mid, node11);
|
|
check_node(mid, node12);
|
|
REQUIRE(no_node(mid, 42));
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* Check that the way is in the mid with the right attributes and tags.
|
|
* Does not check node locations.
|
|
*/
|
|
void check_way(std::shared_ptr<middle_pgsql_t> const &mid,
|
|
osmium::Way const &orig_way)
|
|
{
|
|
auto const mid_q = mid->get_query_instance();
|
|
|
|
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
|
|
REQUIRE(mid_q->way_get(orig_way.id(), &outbuf));
|
|
auto const &way = outbuf.get<osmium::Way>(0);
|
|
|
|
CHECK(orig_way.id() == way.id());
|
|
CHECK(orig_way.timestamp() == way.timestamp());
|
|
CHECK(orig_way.version() == way.version());
|
|
CHECK(orig_way.changeset() == way.changeset());
|
|
CHECK(orig_way.uid() == way.uid());
|
|
CHECK(std::strcmp(orig_way.user(), way.user()) == 0);
|
|
|
|
REQUIRE(orig_way.tags().size() == way.tags().size());
|
|
for (auto it1 = orig_way.tags().begin(), it2 = way.tags().begin();
|
|
it1 != orig_way.tags().end(); ++it1, ++it2) {
|
|
CHECK(*it1 == *it2);
|
|
}
|
|
|
|
REQUIRE(orig_way.nodes().size() == way.nodes().size());
|
|
for (auto it1 = orig_way.nodes().begin(), it2 = way.nodes().begin();
|
|
it1 != orig_way.nodes().end(); ++it1, ++it2) {
|
|
CHECK(*it1 == *it2);
|
|
}
|
|
|
|
osmium::CRC<osmium::CRC_zlib> orig_crc;
|
|
orig_crc.update(orig_way);
|
|
|
|
osmium::CRC<osmium::CRC_zlib> test_crc;
|
|
test_crc.update(way);
|
|
|
|
REQUIRE(orig_crc().checksum() == test_crc().checksum());
|
|
}
|
|
|
|
/**
|
|
* Check that the nodes (ids and locations) of the way with the way_id in the
|
|
* mid are identical to the nodes in the nodes vector.
|
|
*/
|
|
void check_way_nodes(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t way_id,
|
|
std::vector<osmium::Node const *> const &nodes)
|
|
{
|
|
auto const mid_q = mid->get_query_instance();
|
|
|
|
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
|
|
REQUIRE(mid_q->way_get(way_id, &outbuf));
|
|
auto &way = outbuf.get<osmium::Way>(0);
|
|
|
|
REQUIRE(mid_q->nodes_get_list(&way.nodes()) == way.nodes().size());
|
|
REQUIRE(way.nodes().size() == nodes.size());
|
|
|
|
REQUIRE(std::equal(way.nodes().cbegin(), way.nodes().cend(), nodes.cbegin(),
|
|
[](osmium::NodeRef const &nr, osmium::Node const *node) {
|
|
return nr.ref() == node->id() &&
|
|
nr.location() == node->location();
|
|
}));
|
|
}
|
|
|
|
/// Return true if the way with the specified id is not in the mid.
|
|
bool no_way(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
|
|
{
|
|
auto const mid_q = mid->get_query_instance();
|
|
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
|
|
return !mid_q->way_get(id, &outbuf);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
TEMPLATE_TEST_CASE("middle: add, delete and update way", "",
|
|
options_slim_default, options_flat_node_cache)
|
|
{
|
|
auto thread_pool = std::make_shared<thread_pool_t>(1U);
|
|
|
|
options_t options = TestType::options(db);
|
|
|
|
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
|
|
|
|
// Create some ways we'll use for the tests.
|
|
test_buffer_t buffer;
|
|
auto const &way20 =
|
|
buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
|
|
|
|
auto const &way21 = buffer.add_way("w21 Nn11,n12");
|
|
|
|
auto const &way22 = buffer.add_way("w22 Nn12,n10 Tpower=line");
|
|
|
|
auto &way20a =
|
|
buffer.add_way("w20 Nn10,n12 Thighway=primary,name=High_Street");
|
|
|
|
auto const &way5d = buffer.add_way("w5 dD");
|
|
auto &way20d = buffer.add_way("w20 dD");
|
|
auto &way22d = buffer.add_way("w22 dD");
|
|
auto &way42d = buffer.add_way("w42 dD");
|
|
|
|
// Set up middle in "create" mode to get a cleanly initialized database and
|
|
// add some ways. Does this in its own scope so that the mid is closed
|
|
// properly.
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->way(way20);
|
|
mid->way(way21);
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
check_way(mid, way20);
|
|
check_way(mid, way21);
|
|
}
|
|
|
|
// From now on use append mode to not destroy the data we just added.
|
|
options.append = true;
|
|
|
|
SECTION("Added ways are there and no others")
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_way(mid, 5));
|
|
check_way(mid, way20);
|
|
check_way(mid, way21);
|
|
REQUIRE(no_way(mid, 22));
|
|
}
|
|
|
|
SECTION("Delete existing and non-existing way")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->way(way5d);
|
|
mid->way(way20d);
|
|
mid->way(way42d);
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
REQUIRE(no_way(mid, 5));
|
|
REQUIRE(no_way(mid, 20));
|
|
check_way(mid, way21);
|
|
REQUIRE(no_way(mid, 42));
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_way(mid, 5));
|
|
REQUIRE(no_way(mid, 20));
|
|
check_way(mid, way21);
|
|
REQUIRE(no_way(mid, 42));
|
|
}
|
|
}
|
|
|
|
SECTION("Change (delete and set) existing and non-existing way")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->way(way20d);
|
|
mid->way(way20a);
|
|
mid->way(way22d);
|
|
mid->way(way22);
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
REQUIRE(no_way(mid, 5));
|
|
check_way(mid, way20a);
|
|
check_way(mid, way21);
|
|
check_way(mid, way22);
|
|
REQUIRE(no_way(mid, 42));
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_way(mid, 5));
|
|
check_way(mid, way20a);
|
|
check_way(mid, way21);
|
|
check_way(mid, way22);
|
|
REQUIRE(no_way(mid, 42));
|
|
}
|
|
}
|
|
|
|
SECTION("Add new way")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->way(way22);
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
REQUIRE(no_way(mid, 5));
|
|
check_way(mid, way20);
|
|
check_way(mid, way21);
|
|
check_way(mid, way22);
|
|
REQUIRE(no_way(mid, 42));
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_way(mid, 5));
|
|
check_way(mid, way20);
|
|
check_way(mid, way21);
|
|
check_way(mid, way22);
|
|
REQUIRE(no_way(mid, 42));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEMPLATE_TEST_CASE("middle: add way with attributes", "", options_slim_default,
|
|
options_flat_node_cache)
|
|
{
|
|
auto thread_pool = std::make_shared<thread_pool_t>(1U);
|
|
|
|
options_t options = TestType::options(db);
|
|
|
|
SECTION("With attributes") { options.extra_attributes = true; }
|
|
SECTION("No attributes") { options.extra_attributes = false; }
|
|
|
|
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
|
|
|
|
// Create some ways we'll use for the tests.
|
|
test_buffer_t buffer;
|
|
auto &way20 =
|
|
buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
|
|
way20.set_version(123);
|
|
way20.set_timestamp(1234567890);
|
|
way20.set_changeset(456);
|
|
way20.set_uid(789);
|
|
|
|
// The same way but with default attributes.
|
|
auto &way20_no_attr =
|
|
buffer.add_way("w20 Nn10,n11 Thighway=residential,name=High_Street");
|
|
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->way(way20);
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
check_way(mid, options.extra_attributes ? way20 : way20_no_attr);
|
|
}
|
|
|
|
// From now on use append mode to not destroy the data we just added.
|
|
options.append = true;
|
|
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
check_way(mid, options.extra_attributes ? way20 : way20_no_attr);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* Check that the relation is in the mid with the right attributes, members
|
|
* and tags. Only checks the relation, does not recurse into members.
|
|
*/
|
|
void check_relation(std::shared_ptr<middle_pgsql_t> const &mid,
|
|
osmium::Relation const &orig_relation)
|
|
{
|
|
auto const mid_q = mid->get_query_instance();
|
|
|
|
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
|
|
REQUIRE(mid_q->relation_get(orig_relation.id(), &outbuf));
|
|
auto const &relation = outbuf.get<osmium::Relation>(0);
|
|
|
|
CHECK(orig_relation.id() == relation.id());
|
|
CHECK(orig_relation.timestamp() == relation.timestamp());
|
|
CHECK(orig_relation.version() == relation.version());
|
|
CHECK(orig_relation.changeset() == relation.changeset());
|
|
CHECK(orig_relation.uid() == relation.uid());
|
|
CHECK(std::strcmp(orig_relation.user(), relation.user()) == 0);
|
|
|
|
REQUIRE(orig_relation.tags().size() == relation.tags().size());
|
|
for (auto it1 = orig_relation.tags().begin(), it2 = relation.tags().begin();
|
|
it1 != orig_relation.tags().end(); ++it1, ++it2) {
|
|
CHECK(*it1 == *it2);
|
|
}
|
|
|
|
REQUIRE(orig_relation.members().size() == relation.members().size());
|
|
for (auto it1 = orig_relation.members().begin(),
|
|
it2 = relation.members().begin();
|
|
it1 != orig_relation.members().end(); ++it1, ++it2) {
|
|
CHECK(it1->type() == it2->type());
|
|
CHECK(it1->ref() == it2->ref());
|
|
CHECK(std::strcmp(it1->role(), it2->role()) == 0);
|
|
}
|
|
|
|
osmium::CRC<osmium::CRC_zlib> orig_crc;
|
|
orig_crc.update(orig_relation);
|
|
|
|
osmium::CRC<osmium::CRC_zlib> test_crc;
|
|
test_crc.update(relation);
|
|
|
|
REQUIRE(orig_crc().checksum() == test_crc().checksum());
|
|
}
|
|
|
|
/// Return true if the relation with the specified id is not in the mid.
|
|
bool no_relation(std::shared_ptr<middle_pgsql_t> const &mid, osmid_t id)
|
|
{
|
|
auto const mid_q = mid->get_query_instance();
|
|
osmium::memory::Buffer outbuf{4096, osmium::memory::Buffer::auto_grow::yes};
|
|
return !mid_q->relation_get(id, &outbuf);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
TEMPLATE_TEST_CASE("middle: add, delete and update relation", "",
|
|
options_slim_default, options_flat_node_cache)
|
|
{
|
|
auto thread_pool = std::make_shared<thread_pool_t>(1U);
|
|
|
|
options_t options = TestType::options(db);
|
|
|
|
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
|
|
|
|
// Create some relations we'll use for the tests.
|
|
test_buffer_t buffer;
|
|
auto const &relation30 = buffer.add_relation(
|
|
"r30 Mw10@outer,w11@innder Tname=Penguin_Park,type=multipolygon");
|
|
|
|
auto const &relation31 = buffer.add_relation("r31 Mn10@");
|
|
|
|
auto const &relation32 = buffer.add_relation("r32 Mr39@ Ttype=site");
|
|
|
|
auto const &relation30a = buffer.add_relation(
|
|
"r30 Mw10@outer,w11@outer Tname=Pigeon_Park,type=multipolygon");
|
|
|
|
auto const &relation5d = buffer.add_relation("r5 dD");
|
|
auto const &relation30d = buffer.add_relation("r30 dD");
|
|
auto const &relation32d = buffer.add_relation("r32 dD");
|
|
auto const &relation42d = buffer.add_relation("r42 dD");
|
|
|
|
// Set up middle in "create" mode to get a cleanly initialized database and
|
|
// add some relations. Does this in its own scope so that the mid is closed
|
|
// properly.
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->relation(relation30);
|
|
mid->relation(relation31);
|
|
mid->after_relations();
|
|
|
|
check_relation(mid, relation30);
|
|
check_relation(mid, relation31);
|
|
}
|
|
|
|
// From now on use append mode to not destroy the data we just added.
|
|
options.append = true;
|
|
|
|
SECTION("Added relations are there and no others")
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_relation(mid, 5));
|
|
check_relation(mid, relation30);
|
|
check_relation(mid, relation31);
|
|
REQUIRE(no_relation(mid, 32));
|
|
}
|
|
|
|
SECTION("Delete existing and non-existing relation")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->relation(relation5d);
|
|
mid->relation(relation30d);
|
|
mid->relation(relation42d);
|
|
mid->after_relations();
|
|
|
|
REQUIRE(no_relation(mid, 5));
|
|
REQUIRE(no_relation(mid, 30));
|
|
check_relation(mid, relation31);
|
|
REQUIRE(no_relation(mid, 42));
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_relation(mid, 5));
|
|
REQUIRE(no_relation(mid, 30));
|
|
check_relation(mid, relation31);
|
|
REQUIRE(no_relation(mid, 42));
|
|
}
|
|
}
|
|
|
|
SECTION("Change (delete and set) existing and non-existing relation")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->relation(relation30d);
|
|
mid->relation(relation30a);
|
|
mid->relation(relation32d);
|
|
mid->relation(relation32);
|
|
mid->after_relations();
|
|
|
|
REQUIRE(no_relation(mid, 5));
|
|
check_relation(mid, relation30a);
|
|
check_relation(mid, relation31);
|
|
check_relation(mid, relation32);
|
|
REQUIRE(no_relation(mid, 42));
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_relation(mid, 5));
|
|
check_relation(mid, relation30a);
|
|
check_relation(mid, relation31);
|
|
check_relation(mid, relation32);
|
|
REQUIRE(no_relation(mid, 42));
|
|
}
|
|
}
|
|
|
|
SECTION("Add new relation")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->relation(relation32);
|
|
mid->after_relations();
|
|
|
|
REQUIRE(no_relation(mid, 5));
|
|
check_relation(mid, relation30);
|
|
check_relation(mid, relation31);
|
|
check_relation(mid, relation32);
|
|
REQUIRE(no_relation(mid, 42));
|
|
}
|
|
{
|
|
// Check with a new mid.
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
REQUIRE(no_relation(mid, 5));
|
|
check_relation(mid, relation30);
|
|
check_relation(mid, relation31);
|
|
check_relation(mid, relation32);
|
|
REQUIRE(no_relation(mid, 42));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEMPLATE_TEST_CASE("middle: add relation with attributes", "",
|
|
options_slim_default, options_flat_node_cache)
|
|
{
|
|
auto thread_pool = std::make_shared<thread_pool_t>(1U);
|
|
|
|
options_t options = TestType::options(db);
|
|
|
|
SECTION("With attributes") { options.extra_attributes = true; }
|
|
SECTION("No attributes") { options.extra_attributes = false; }
|
|
|
|
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
|
|
|
|
// Create some relations we'll use for the tests.
|
|
test_buffer_t buffer;
|
|
auto const &relation30 = buffer.add_relation(
|
|
"r30 v123 c456 i789 t2009-02-13T23:31:30Z Mw10@outer,w11@inner "
|
|
"Tname=Penguin_Park,type=multipolygon");
|
|
|
|
// The same relation but with default attributes.
|
|
auto const &relation30_no_attr = buffer.add_relation(
|
|
"r30 Mw10@outer,w11@inner Tname=Penguin_Park,type=multipolygon");
|
|
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->after_ways();
|
|
mid->relation(relation30);
|
|
mid->after_relations();
|
|
|
|
check_relation(mid, options.extra_attributes ? relation30
|
|
: relation30_no_attr);
|
|
}
|
|
|
|
// From now on use append mode to not destroy the data we just added.
|
|
options.append = true;
|
|
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
check_relation(mid, options.extra_attributes ? relation30
|
|
: relation30_no_attr);
|
|
}
|
|
}
|
|
|
|
TEMPLATE_TEST_CASE("middle: change nodes in way", "", options_slim_default,
|
|
options_flat_node_cache)
|
|
{
|
|
auto thread_pool = std::make_shared<thread_pool_t>(1U);
|
|
|
|
options_t options = TestType::options(db);
|
|
|
|
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
|
|
|
|
// create some nodes and ways we'll use for the tests
|
|
test_buffer_t buffer;
|
|
auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
|
|
auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
|
|
auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
|
|
auto const &node10a = buffer.add_node("n10 x2.0 y0.0");
|
|
|
|
auto const &node10d = buffer.add_node("n10 dD");
|
|
|
|
auto &way20 = buffer.add_way("w20 Nn10,n11");
|
|
auto &way21 = buffer.add_way("w21 Nn11,n12");
|
|
auto &way22 = buffer.add_way("w22 Nn12,n10");
|
|
auto &way20a = buffer.add_way("w20 Nn11,n12");
|
|
|
|
auto &way20d = buffer.add_way("w20 dD");
|
|
|
|
// Set up middle in "create" mode to get a cleanly initialized database and
|
|
// add some nodes and ways. Does this in its own scope so that the mid is
|
|
// closed properly.
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
auto output = std::make_shared<output_null_t>(mid->get_query_instance(),
|
|
thread_pool, options);
|
|
osmdata_t osmdata{mid, output, options};
|
|
|
|
osmdata.node(node10);
|
|
osmdata.node(node11);
|
|
osmdata.node(node12);
|
|
osmdata.after_nodes();
|
|
osmdata.way(way20);
|
|
osmdata.way(way21);
|
|
osmdata.after_ways();
|
|
osmdata.after_relations();
|
|
|
|
check_node(mid, node10);
|
|
check_node(mid, node11);
|
|
check_node(mid, node12);
|
|
check_way(mid, way20);
|
|
check_way_nodes(mid, way20.id(), {&node10, &node11});
|
|
check_way(mid, way21);
|
|
check_way_nodes(mid, way21.id(), {&node11, &node12});
|
|
|
|
REQUIRE(osmdata.get_pending_way_ids().empty());
|
|
REQUIRE(osmdata.get_pending_relation_ids().empty());
|
|
|
|
osmdata.stop();
|
|
}
|
|
|
|
// From now on use append mode to not destroy the data we just added.
|
|
options.append = true;
|
|
|
|
SECTION("Single way affected")
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
auto output = std::make_shared<output_null_t>(mid->get_query_instance(),
|
|
thread_pool, options);
|
|
osmdata_t osmdata{mid, output, options};
|
|
|
|
osmdata.node(node10d);
|
|
osmdata.node(node10a);
|
|
osmdata.after_nodes();
|
|
osmdata.after_ways();
|
|
osmdata.after_relations();
|
|
|
|
idlist_t const &way_ids = osmdata.get_pending_way_ids();
|
|
REQUIRE(way_ids == idlist_t{20});
|
|
|
|
REQUIRE(osmdata.get_pending_relation_ids().empty());
|
|
|
|
check_way(mid, way20);
|
|
check_way_nodes(mid, way20.id(), {&node10a, &node11});
|
|
}
|
|
|
|
SECTION("Two ways affected")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->after_nodes();
|
|
mid->way(way22);
|
|
mid->after_ways();
|
|
mid->after_relations();
|
|
|
|
check_way(mid, way22);
|
|
}
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
auto output = std::make_shared<output_null_t>(
|
|
mid->get_query_instance(), thread_pool, options);
|
|
osmdata_t osmdata{mid, output, options};
|
|
|
|
osmdata.node(node10d);
|
|
osmdata.node(node10a);
|
|
osmdata.after_nodes();
|
|
osmdata.after_ways();
|
|
osmdata.after_relations();
|
|
|
|
idlist_t const &way_ids = osmdata.get_pending_way_ids();
|
|
REQUIRE(way_ids == idlist_t{20, 22});
|
|
|
|
REQUIRE(osmdata.get_pending_relation_ids().empty());
|
|
|
|
check_way(mid, way20);
|
|
check_way_nodes(mid, way20.id(), {&node10a, &node11});
|
|
check_way(mid, way22);
|
|
check_way_nodes(mid, way22.id(), {&node12, &node10a});
|
|
}
|
|
}
|
|
|
|
SECTION("Change way so the changing node isn't in it any more")
|
|
{
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
auto output = std::make_shared<output_null_t>(
|
|
mid->get_query_instance(), thread_pool, options);
|
|
osmdata_t osmdata{mid, output, options};
|
|
|
|
osmdata.after_nodes();
|
|
osmdata.way(way20d);
|
|
osmdata.way(way20a);
|
|
osmdata.after_ways();
|
|
osmdata.after_relations();
|
|
|
|
check_way(mid, way20a);
|
|
check_way_nodes(mid, way20.id(), {&node11, &node12});
|
|
}
|
|
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
auto output = std::make_shared<output_null_t>(
|
|
mid->get_query_instance(), thread_pool, options);
|
|
osmdata_t osmdata{mid, output, options};
|
|
|
|
osmdata.node(node10d);
|
|
osmdata.node(node10a);
|
|
osmdata.after_nodes();
|
|
osmdata.after_ways();
|
|
osmdata.after_relations();
|
|
|
|
REQUIRE(osmdata.get_pending_way_ids().empty());
|
|
REQUIRE(osmdata.get_pending_relation_ids().empty());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEMPLATE_TEST_CASE("middle: change nodes in relation", "", options_slim_default,
|
|
options_flat_node_cache)
|
|
{
|
|
auto thread_pool = std::make_shared<thread_pool_t>(1U);
|
|
|
|
options_t options = TestType::options(db);
|
|
|
|
testing::cleanup::file_t const flatnode_cleaner{options.flat_node_file};
|
|
|
|
// create some nodes, ways, and relations we'll use for the tests
|
|
test_buffer_t buffer;
|
|
auto const &node10 = buffer.add_node("n10 x1.0 y0.0");
|
|
auto const &node11 = buffer.add_node("n11 x1.1 y0.0");
|
|
auto const &node12 = buffer.add_node("n12 x1.2 y0.0");
|
|
auto const &node10a = buffer.add_node("n10 x1.0 y1.0");
|
|
auto const &node11a = buffer.add_node("n11 x1.1 y1.0");
|
|
|
|
auto const &node10d = buffer.add_node("n10 dD");
|
|
auto const &node11d = buffer.add_node("n11 dD");
|
|
|
|
auto const &way20 = buffer.add_way("w20 Nn11,n12");
|
|
|
|
auto const &rel30 = buffer.add_relation("r30 Mn10@");
|
|
auto const &rel31 = buffer.add_relation("r31 Mw20@");
|
|
|
|
// Set up middle in "create" mode to get a cleanly initialized database and
|
|
// add some nodes and ways. Does this in its own scope so that the mid is
|
|
// closed properly.
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
mid->node(node10);
|
|
mid->node(node11);
|
|
mid->node(node12);
|
|
mid->after_nodes();
|
|
mid->way(way20);
|
|
mid->after_ways();
|
|
mid->relation(rel30);
|
|
mid->relation(rel31);
|
|
mid->after_relations();
|
|
|
|
mid->stop();
|
|
mid->wait();
|
|
}
|
|
|
|
// From now on use append mode to not destroy the data we just added.
|
|
options.append = true;
|
|
|
|
SECTION("Single relation directly affected")
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
auto output = std::make_shared<output_null_t>(mid->get_query_instance(),
|
|
thread_pool, options);
|
|
osmdata_t osmdata{mid, output, options};
|
|
|
|
osmdata.node(node10d);
|
|
osmdata.node(node10a);
|
|
osmdata.after_nodes();
|
|
osmdata.after_ways();
|
|
osmdata.after_relations();
|
|
|
|
REQUIRE(osmdata.get_pending_way_ids().empty());
|
|
idlist_t const &rel_ids = osmdata.get_pending_relation_ids();
|
|
REQUIRE(rel_ids == idlist_t{30});
|
|
|
|
check_relation(mid, rel30);
|
|
}
|
|
|
|
SECTION("Single relation indirectly affected (through way)")
|
|
{
|
|
auto mid = std::make_shared<middle_pgsql_t>(thread_pool, &options);
|
|
mid->start();
|
|
|
|
auto output = std::make_shared<output_null_t>(mid->get_query_instance(),
|
|
thread_pool, options);
|
|
osmdata_t osmdata{mid, output, options};
|
|
|
|
osmdata.node(node11d);
|
|
osmdata.node(node11a);
|
|
osmdata.after_nodes();
|
|
osmdata.after_ways();
|
|
osmdata.after_relations();
|
|
|
|
idlist_t const &way_ids = osmdata.get_pending_way_ids();
|
|
REQUIRE(way_ids == idlist_t{20});
|
|
idlist_t const &rel_ids = osmdata.get_pending_relation_ids();
|
|
REQUIRE(rel_ids == idlist_t{31});
|
|
check_relation(mid, rel31);
|
|
}
|
|
}
|