1
2 #include <test.hpp>
3
4 #include <vtzero/builder.hpp>
5 #include <vtzero/index.hpp>
6 #include <vtzero/output.hpp>
7
8 #include <cstdint>
9 #include <string>
10 #include <type_traits>
11
12 template <typename T>
13 struct movable_not_copyable {
14 constexpr static bool value = !std::is_copy_constructible<T>::value &&
15 !std::is_copy_assignable<T>::value &&
16 std::is_nothrow_move_constructible<T>::value &&
17 std::is_nothrow_move_assignable<T>::value;
18 };
19
20 static_assert(movable_not_copyable<vtzero::tile_builder>::value, "tile_builder should be nothrow movable, but not copyable");
21 static_assert(movable_not_copyable<vtzero::point_feature_builder>::value, "point_feature_builder should be nothrow movable, but not copyable");
22 static_assert(movable_not_copyable<vtzero::linestring_feature_builder>::value, "linestring_feature_builder should be nothrow movable, but not copyable");
23 static_assert(movable_not_copyable<vtzero::polygon_feature_builder>::value, "polygon_feature_builder should be nothrow movable, but not copyable");
24 static_assert(movable_not_copyable<vtzero::geometry_feature_builder>::value, "geometry_feature_builder should be nothrow movable, but not copyable");
25
26 TEST_CASE("Create tile from existing layers") {
27 const auto buffer = load_test_tile();
28 vtzero::vector_tile tile{buffer};
29
30 vtzero::tile_builder tbuilder;
31
32 SECTION("add_existing_layer(layer)") {
33 while (auto layer = tile.next_layer()) {
34 tbuilder.add_existing_layer(layer);
35 }
36 }
37
38 SECTION("add_existing_layer(data_view)") {
39 while (auto layer = tile.next_layer()) {
40 tbuilder.add_existing_layer(layer.data());
41 }
42 }
43
44 const std::string data = tbuilder.serialize();
45
46 REQUIRE(data == buffer);
47 }
48
49 TEST_CASE("Create layer based on existing layer") {
50 const auto buffer = load_test_tile();
51 vtzero::vector_tile tile{buffer};
52 const auto layer = tile.get_layer_by_name("place_label");
53
54 vtzero::tile_builder tbuilder;
55 vtzero::layer_builder lbuilder{tbuilder, layer};
56 vtzero::point_feature_builder fbuilder{lbuilder};
57 fbuilder.set_id(42);
58 fbuilder.add_point(10, 20);
59 fbuilder.commit();
60
61 const std::string data = tbuilder.serialize();
62 vtzero::vector_tile new_tile{data};
63 const auto new_layer = new_tile.next_layer();
64 REQUIRE(std::string(new_layer.name()) == "place_label");
65 REQUIRE(new_layer.version() == 1);
66 REQUIRE(new_layer.extent() == 4096);
67 }
68
69 TEST_CASE("Create layer and add keys/values") {
70 vtzero::tile_builder tbuilder;
71 vtzero::layer_builder lbuilder{tbuilder, "name"};
72
73 const auto ki1 = lbuilder.add_key_without_dup_check("key1");
74 const auto ki2 = lbuilder.add_key("key2");
75 const auto ki3 = lbuilder.add_key("key1");
76
77 REQUIRE(ki1 != ki2);
78 REQUIRE(ki1 == ki3);
79
80 const auto vi1 = lbuilder.add_value_without_dup_check(vtzero::encoded_property_value{"value1"});
81 vtzero::encoded_property_value value2{"value2"};
82 const auto vi2 = lbuilder.add_value_without_dup_check(vtzero::property_value{value2.data()});
83
84 const auto vi3 = lbuilder.add_value(vtzero::encoded_property_value{"value1"});
85 const auto vi4 = lbuilder.add_value(vtzero::encoded_property_value{19});
86 const auto vi5 = lbuilder.add_value(vtzero::encoded_property_value{19.0});
87 const auto vi6 = lbuilder.add_value(vtzero::encoded_property_value{22});
88 vtzero::encoded_property_value nineteen{19};
89 const auto vi7 = lbuilder.add_value(vtzero::property_value{nineteen.data()});
90
91 REQUIRE(vi1 != vi2);
92 REQUIRE(vi1 == vi3);
93 REQUIRE(vi1 != vi4);
94 REQUIRE(vi1 != vi5);
95 REQUIRE(vi1 != vi6);
96 REQUIRE(vi4 != vi5);
97 REQUIRE(vi4 != vi6);
98 REQUIRE(vi4 == vi7);
99 }
100
101 TEST_CASE("Committing a feature succeeds after a geometry was added") {
102 vtzero::tile_builder tbuilder;
103 vtzero::layer_builder lbuilder{tbuilder, "test"};
104
105 { // explicit commit after geometry
106 vtzero::point_feature_builder fbuilder{lbuilder};
107 fbuilder.set_id(1);
108 fbuilder.add_point(10, 10);
109 fbuilder.commit();
110 }
111
112 { // explicit commit after properties
113 vtzero::point_feature_builder fbuilder{lbuilder};
114 fbuilder.set_id(2);
115 fbuilder.add_point(10, 10);
116 fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
117 fbuilder.commit();
118 }
119
120 { // extra commits or rollbacks are okay but no other calls
121 vtzero::point_feature_builder fbuilder{lbuilder};
122 fbuilder.set_id(3);
123 fbuilder.add_point(10, 10);
124 fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
125 fbuilder.commit();
126
127 SECTION("superfluous commit()") {
128 fbuilder.commit();
129 }
130 SECTION("superfluous rollback()") {
131 fbuilder.rollback();
132 }
133
134 REQUIRE_THROWS_AS(fbuilder.set_id(10), const assert_error&);
135 REQUIRE_THROWS_AS(fbuilder.add_point(20, 20), const assert_error&);
136 REQUIRE_THROWS_AS(fbuilder.add_property("x", "y"), const assert_error&);
137 }
138
139 const std::string data = tbuilder.serialize();
140
141 vtzero::vector_tile tile{data};
142 auto layer = tile.next_layer();
143
144 uint64_t n = 1;
145 while (auto feature = layer.next_feature()) {
146 REQUIRE(feature.id() == n++);
147 }
148
149 REQUIRE(n == 4);
150 }
151
152 TEST_CASE("Committing a feature fails with assert if no geometry was added") {
153 vtzero::tile_builder tbuilder;
154 vtzero::layer_builder lbuilder{tbuilder, "test"};
155
156 SECTION("explicit immediate commit") {
157 vtzero::point_feature_builder fbuilder{lbuilder};
158 REQUIRE_THROWS_AS(fbuilder.commit(), const assert_error&);
159 }
160
161 SECTION("explicit commit after setting id") {
162 vtzero::point_feature_builder fbuilder{lbuilder};
163 fbuilder.set_id(2);
164 REQUIRE_THROWS_AS(fbuilder.commit(), const assert_error&);
165 }
166 }
167
168 TEST_CASE("Rollback feature") {
169 vtzero::tile_builder tbuilder;
170 vtzero::layer_builder lbuilder{tbuilder, "test"};
171
172 {
173 vtzero::point_feature_builder fbuilder{lbuilder};
174 fbuilder.set_id(1);
175 fbuilder.add_point(10, 10);
176 fbuilder.commit();
177 }
178
179 { // immediate rollback
180 vtzero::point_feature_builder fbuilder{lbuilder};
181 fbuilder.set_id(2);
182 fbuilder.rollback();
183 }
184
185 { // rollback after setting id
186 vtzero::point_feature_builder fbuilder{lbuilder};
187 fbuilder.set_id(3);
188 fbuilder.rollback();
189 }
190
191 { // rollback after geometry
192 vtzero::point_feature_builder fbuilder{lbuilder};
193 fbuilder.set_id(4);
194 fbuilder.add_point(20, 20);
195 fbuilder.rollback();
196 }
197
198 { // rollback after properties
199 vtzero::point_feature_builder fbuilder{lbuilder};
200 fbuilder.set_id(5);
201 fbuilder.add_point(20, 20);
202 fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
203 fbuilder.rollback();
204 }
205
206 { // implicit rollback after geometry
207 vtzero::point_feature_builder fbuilder{lbuilder};
208 fbuilder.set_id(6);
209 fbuilder.add_point(10, 10);
210 }
211
212 { // implicit rollback after properties
213 vtzero::point_feature_builder fbuilder{lbuilder};
214 fbuilder.set_id(7);
215 fbuilder.add_point(10, 10);
216 fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
217 }
218
219 {
220 vtzero::point_feature_builder fbuilder{lbuilder};
221 fbuilder.set_id(8);
222 fbuilder.add_point(30, 30);
223 fbuilder.commit();
224 }
225
226 const std::string data = tbuilder.serialize();
227
228 vtzero::vector_tile tile{data};
229 auto layer = tile.next_layer();
230
231 auto feature = layer.next_feature();
232 REQUIRE(feature.id() == 1);
233
234 feature = layer.next_feature();
235 REQUIRE(feature.id() == 8);
236
237 feature = layer.next_feature();
238 REQUIRE_FALSE(feature);
239 }
240
vector_tile_equal(const std::string & t1,const std::string & t2)241 static bool vector_tile_equal(const std::string& t1, const std::string& t2) {
242 vtzero::vector_tile vt1{t1};
243 vtzero::vector_tile vt2{t2};
244
245 for (auto l1 = vt1.next_layer(), l2 = vt2.next_layer();
246 l1 && l2;
247 l1 = vt1.next_layer(), l2 = vt2.next_layer()) {
248 if (l1.empty()) {
249 l1 = vt1.next_layer();
250 if (!l1) {
251 return true;
252 }
253 }
254 if (l2.empty()) {
255 l2 = vt2.next_layer();
256 if (!l2) {
257 return true;
258 }
259 }
260
261 if (!l1 ||
262 !l2 ||
263 l1.version() != l2.version() ||
264 l1.extent() != l2.extent() ||
265 l1.num_features() != l2.num_features() ||
266 l1.name() != l2.name()) {
267 return false;
268 }
269
270 for (auto f1 = l1.next_feature(), f2 = l2.next_feature();
271 f1 && f2;
272 f1 = l1.next_feature(), f2 = l2.next_feature()) {
273 if (f1.id() != f2.id() ||
274 f1.geometry_type() != f2.geometry_type() ||
275 f1.num_properties() != f2.num_properties() ||
276 f1.geometry().data() != f2.geometry().data()) {
277 return false;
278 }
279 for (auto p1 = f1.next_property(), p2 = f2.next_property();
280 p1 && p2;
281 p1 = f1.next_property(), p2 = f2.next_property()) {
282 if (p1.key() != p2.key() || p1.value() != p2.value()) {
283 return false;
284 }
285 }
286 }
287 }
288
289 return true;
290 }
291
292 TEST_CASE("Copy tile") {
293 const auto buffer = load_test_tile();
294 vtzero::vector_tile tile{buffer};
295
296 vtzero::tile_builder tbuilder;
297
298 while (auto layer = tile.next_layer()) {
299 vtzero::layer_builder lbuilder{tbuilder, layer};
300 while (auto feature = layer.next_feature()) {
301 lbuilder.add_feature(feature);
302 }
303 }
304
305 const std::string data = tbuilder.serialize();
306 REQUIRE(vector_tile_equal(buffer, data));
307 }
308
309 TEST_CASE("Copy tile using geometry_feature_builder") {
310 const auto buffer = load_test_tile();
311 vtzero::vector_tile tile{buffer};
312
313 vtzero::tile_builder tbuilder;
314
315 while (auto layer = tile.next_layer()) {
316 vtzero::layer_builder lbuilder{tbuilder, layer};
317 while (auto feature = layer.next_feature()) {
318 vtzero::geometry_feature_builder fbuilder{lbuilder};
319 fbuilder.set_id(feature.id());
320 fbuilder.set_geometry(feature.geometry());
321 while (auto property = feature.next_property()) {
322 fbuilder.add_property(property.key(), property.value());
323 }
324 fbuilder.commit();
325 }
326 }
327
328 const std::string data = tbuilder.serialize();
329 REQUIRE(vector_tile_equal(buffer, data));
330 }
331
332 TEST_CASE("Copy only point geometries using geometry_feature_builder") {
333 const auto buffer = load_test_tile();
334 vtzero::vector_tile tile{buffer};
335
336 vtzero::tile_builder tbuilder;
337
338 int n = 0;
339 while (auto layer = tile.next_layer()) {
340 vtzero::layer_builder lbuilder{tbuilder, layer};
341 while (auto feature = layer.next_feature()) {
342 vtzero::geometry_feature_builder fbuilder{lbuilder};
343 fbuilder.set_id(feature.id());
344 if (feature.geometry().type() == vtzero::GeomType::POINT) {
345 fbuilder.set_geometry(feature.geometry());
346 while (auto property = feature.next_property()) {
347 fbuilder.add_property(property.key(), property.value());
348 }
349 fbuilder.commit();
350 ++n;
351 } else {
352 fbuilder.rollback();
353 }
354 }
355 }
356 REQUIRE(n == 17);
357
358 const std::string data = tbuilder.serialize();
359
360 n = 0;
361 vtzero::vector_tile result_tile{data};
362 while (auto layer = result_tile.next_layer()) {
363 while (auto feature = layer.next_feature()) {
364 ++n;
365 }
366 }
367
368 REQUIRE(n == 17);
369 }
370
371 TEST_CASE("Build point feature from container with too many points") {
372
373 // fake container pretending to contain too many points
374 struct test_container {
375
sizetest_container376 std::size_t size() const noexcept {
377 return 1ul << 29u;
378 }
379
begintest_container380 vtzero::point* begin() const noexcept {
381 return nullptr;
382 }
383
endtest_container384 vtzero::point* end() const noexcept {
385 return nullptr;
386 }
387
388 };
389
390 vtzero::tile_builder tbuilder;
391 vtzero::layer_builder lbuilder{tbuilder, "test"};
392 vtzero::point_feature_builder fbuilder{lbuilder};
393
394 fbuilder.set_id(1);
395
396 test_container tc;
397 REQUIRE_THROWS_AS(fbuilder.add_points_from_container(tc), const vtzero::geometry_exception&);
398 }
399
400 TEST_CASE("Moving a feature builder is allowed") {
401 vtzero::tile_builder tbuilder;
402 vtzero::layer_builder lbuilder{tbuilder, "test"};
403 vtzero::point_feature_builder fbuilder{lbuilder};
404
405 auto fbuilder2 = std::move(fbuilder);
406 vtzero::point_feature_builder fbuilder3{std::move(fbuilder2)};
407 }
408
409