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