1 #include "catch.hpp"
2 
3 #include "utils.hpp"
4 
5 #include <osmium/handler.hpp>
6 #include <osmium/io/any_compression.hpp>
7 #include <osmium/io/any_input.hpp>
8 #include <osmium/memory/buffer.hpp>
9 #include <osmium/visitor.hpp>
10 
11 #include <iterator>
12 #include <stdexcept>
13 #include <vector>
14 
15 struct CountHandler : public osmium::handler::Handler {
16 
17     int count = 0;
18 
nodeCountHandler19     void node(const osmium::Node& /*node*/) {
20         ++count;
21     }
22 
23 }; // class CountHandler
24 
25 struct ZeroPositionNodeCountHandler : public osmium::handler::Handler {
26 
27     // number of nodes seen at zero position, or visible with undefined
28     // location.
29     int count = 0;
30     int total_count = 0; // total number of nodes seen
31     const osmium::Location zero = osmium::Location{int32_t(0), int32_t(0)};
32 
nodeZeroPositionNodeCountHandler33     void node(const osmium::Node& node) {
34         // no nodes in the history file have a zero location, and
35         // no visible nodes should have an undefined location.
36         if ((node.location() == zero) ||
37             (node.visible() && !node.location())) {
38             ++count;
39         }
40         ++total_count;
41     }
42 
43 }; // class ZeroPositionNodeCountHandler
44 
45 
46 TEST_CASE("Reader can be initialized with file") {
47     osmium::io::File file{with_data_dir("t/io/data.osm")};
48 
49     const int count = count_fds();
50     osmium::io::Reader reader{file};
51     osmium::handler::Handler handler;
52 
53     osmium::apply(reader, handler);
54 
55     reader.close();
56     REQUIRE(count == count_fds());
57 }
58 
59 TEST_CASE("Reader can be initialized with string") {
60     const int count = count_fds();
61 
62     osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
63     osmium::handler::Handler handler;
64 
65     osmium::apply(reader, handler);
66 
67     reader.close();
68     REQUIRE(count == count_fds());
69 }
70 
71 TEST_CASE("Reader can be initialized with user-provided pool") {
72     const int count = count_fds();
73 
74     osmium::thread::Pool pool{4};
75     osmium::io::File file{with_data_dir("t/io/data.osm")};
76     osmium::io::Reader reader{file, pool};
77     osmium::handler::Handler handler;
78 
79     osmium::apply(reader, handler);
80 
81     reader.close();
82     REQUIRE(count == count_fds());
83 }
84 
85 TEST_CASE("Reader should throw after eof") {
86     const int count = count_fds();
87 
88     osmium::io::File file{with_data_dir("t/io/data.osm")};
89     osmium::io::Reader reader{file};
90 
91     SECTION("Get header") {
92         const auto header = reader.header();
93         REQUIRE(header.get("generator") == "testdata");
94         REQUIRE_FALSE(reader.eof());
95     }
96 
97     REQUIRE_FALSE(reader.eof());
98 
99     while (osmium::memory::Buffer buffer = reader.read()) {
100     }
101 
102     REQUIRE(reader.eof());
103 
104     REQUIRE_THROWS_AS(reader.read(), osmium::io_error);
105 
106     reader.close();
107     REQUIRE(reader.eof());
108     REQUIRE(count == count_fds());
109 }
110 
111 TEST_CASE("Reader should not hang when apply() is called twice on reader") {
112     const int count = count_fds();
113 
114     osmium::io::File file{with_data_dir("t/io/data.osm")};
115     osmium::io::Reader reader{file};
116     osmium::handler::Handler handler;
117 
118     osmium::apply(reader, handler);
119     REQUIRE_THROWS_AS(osmium::apply(reader, handler), osmium::io_error);
120 
121     reader.close();
122     REQUIRE(count == count_fds());
123 }
124 
125 TEST_CASE("Reader should work with a buffer with uncompressed data") {
126     const int count = count_fds();
127 
128     const int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm"));
129     REQUIRE(fd >= 0);
130 
131     const size_t buffer_size = 1000;
132     char buffer[buffer_size];
133     const auto length = ::read(fd, buffer, buffer_size);
134     REQUIRE(length > 0);
135     osmium::io::detail::reliable_close(fd);
136 
137     osmium::io::File file{buffer, static_cast<size_t>(length), "osm"};
138     osmium::io::Reader reader{file};
139     CountHandler handler;
140 
141     REQUIRE(handler.count == 0);
142     osmium::apply(reader, handler);
143     REQUIRE(handler.count == 1);
144 
145     reader.close();
146     REQUIRE(count == count_fds());
147 }
148 
149 TEST_CASE("Reader should work with a buffer with gzip-compressed data") {
150     const int count = count_fds();
151 
152     const int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.gz"));
153     REQUIRE(fd >= 0);
154 
155     const size_t buffer_size = 1000;
156     char buffer[buffer_size];
157     const auto length = ::read(fd, buffer, buffer_size);
158     REQUIRE(length > 0);
159     osmium::io::detail::reliable_close(fd);
160 
161     osmium::io::File file{buffer, static_cast<size_t>(length), "osm.gz"};
162     osmium::io::Reader reader{file};
163     CountHandler handler;
164 
165     REQUIRE(handler.count == 0);
166     osmium::apply(reader, handler);
167     REQUIRE(handler.count == 1);
168 
169     reader.close();
170     REQUIRE(count == count_fds());
171 }
172 
173 TEST_CASE("Reader should work with a buffer with bzip2-compressed data") {
174     const int count = count_fds();
175 
176     const int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.bz2"));
177     REQUIRE(fd >= 0);
178 
179     const size_t buffer_size = 1000;
180     char buffer[buffer_size];
181     const auto length = ::read(fd, buffer, buffer_size);
182     REQUIRE(length > 0);
183     osmium::io::detail::reliable_close(fd);
184 
185     osmium::io::File file{buffer, static_cast<size_t>(length), "osm.bz2"};
186     osmium::io::Reader reader{file};
187     CountHandler handler;
188 
189     REQUIRE(handler.count == 0);
190     osmium::apply(reader, handler);
191     REQUIRE(handler.count == 1);
192 
193     reader.close();
194     REQUIRE(count == count_fds());
195 }
196 
197 TEST_CASE("Reader should decode zero node positions in history (XML)") {
198     const int count = count_fds();
199 
200     osmium::io::Reader reader{with_data_dir("t/io/deleted_nodes.osh"),
201                               osmium::osm_entity_bits::node};
202     ZeroPositionNodeCountHandler handler;
203 
204     REQUIRE(handler.count == 0);
205     REQUIRE(handler.total_count == 0);
206 
207     osmium::apply(reader, handler);
208 
209     REQUIRE(handler.count == 0);
210     REQUIRE(handler.total_count == 2);
211 
212     reader.close();
213     REQUIRE(count == count_fds());
214 }
215 
216 TEST_CASE("Reader should decode zero node positions in history (PBF)") {
217     const int count = count_fds();
218 
219     osmium::io::Reader reader{with_data_dir("t/io/deleted_nodes.osh.pbf"),
220                               osmium::osm_entity_bits::node};
221     ZeroPositionNodeCountHandler handler;
222 
223     REQUIRE(handler.count == 0);
224     REQUIRE(handler.total_count == 0);
225 
226     osmium::apply(reader, handler);
227 
228     REQUIRE(handler.count == 0);
229     REQUIRE(handler.total_count == 2);
230 
231     reader.close();
232     REQUIRE(count == count_fds());
233 }
234 
235 TEST_CASE("Reader should fail with nonexistent file") {
236     const int count = count_fds();
237 
238     REQUIRE_THROWS(osmium::io::Reader{with_data_dir("t/io/nonexistent-file.osm")});
239 
240     REQUIRE(count == count_fds());
241 }
242 
243 TEST_CASE("Reader should fail with nonexistent file (gz)") {
244     const int count = count_fds();
245 
246     REQUIRE_THROWS(osmium::io::Reader{with_data_dir("t/io/nonexistent-file.osm.gz")});
247 
248     REQUIRE(count == count_fds());
249 }
250 
251 TEST_CASE("Reader should fail with nonexistent file (pbf)") {
252     const int count = count_fds();
253 
254     REQUIRE_THROWS(osmium::io::Reader{with_data_dir("t/io/nonexistent-file.osm.pbf")});
255 
256     REQUIRE(count == count_fds());
257 }
258 
259 TEST_CASE("Reader should work when there is an exception in main thread before getting header") {
260     const int count = count_fds();
261 
262     try {
263         osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
264         REQUIRE_FALSE(reader.eof());
265         throw std::runtime_error{"foo"};
266     } catch (...) {
267     }
268 
269     REQUIRE(count == count_fds());
270 }
271 
272 TEST_CASE("Reader should work when there is an exception in main thread while reading") {
273     const int count = count_fds();
274 
275     try {
276         osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
277         REQUIRE_FALSE(reader.eof());
278         auto header = reader.header();
279         throw std::runtime_error{"foo"};
280     } catch (...) {
281     }
282 
283     REQUIRE(count == count_fds());
284 }
285 
286 TEST_CASE("Applying rvalue handler on reader") {
287     const int count = count_fds();
288 
289     osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
290     struct NullHandler : public osmium::handler::Handler { };
291     osmium::apply(reader, NullHandler{});
292 
293     reader.close();
294     REQUIRE(count == count_fds());
295 }
296 
297 TEST_CASE("Can call read() exactly once on Reader with entity_bits nothing") {
298     const int count = count_fds();
299 
300     osmium::io::File file{with_data_dir("t/io/data.osm")};
301     osmium::io::Reader reader{file, osmium::osm_entity_bits::nothing};
302     REQUIRE_FALSE(reader.eof());
303 
304     SECTION("Get header") {
305         const auto header = reader.header();
306         REQUIRE(header.get("generator") == "testdata");
307         REQUIRE_FALSE(reader.eof());
308     }
309 
310     osmium::memory::Buffer buffer = reader.read();
311     REQUIRE_FALSE(buffer);
312     REQUIRE(reader.eof());
313     REQUIRE_THROWS_AS(reader.read(), osmium::io_error);
314 
315     reader.close();
316     REQUIRE(reader.eof());
317 
318     REQUIRE(count == count_fds());
319 }
320 
321 TEST_CASE("Can not read after close") {
322     const int count = count_fds();
323 
324     osmium::io::File file{with_data_dir("t/io/data.osm")};
325     osmium::io::Reader reader{file};
326 
327     SECTION("Get header") {
328         const auto header = reader.header();
329         REQUIRE(header.get("generator") == "testdata");
330         REQUIRE_FALSE(reader.eof());
331     }
332 
333     REQUIRE_FALSE(reader.eof());
334 
335     osmium::memory::Buffer buffer = reader.read();
336     REQUIRE(buffer);
337     REQUIRE_FALSE(reader.eof());
338 
339     reader.close();
340     REQUIRE(reader.eof());
341     REQUIRE_THROWS_AS(reader.read(), osmium::io_error);
342 
343     REQUIRE(count == count_fds());
344 }
345 
346 using object_counts = std::array<std::size_t, 3>;
347 
count_objects_per_buffer(const char * filename,osmium::io::buffers_type btype)348 std::vector<object_counts> count_objects_per_buffer(const char* filename, osmium::io::buffers_type btype) {
349     osmium::io::File file{with_data_dir(filename)};
350     osmium::io::Reader reader{file, btype};
351 
352     std::vector<object_counts> counts;
353     while (osmium::memory::Buffer buffer = reader.read()) {
354         const auto rn = buffer.select<osmium::Node>();
355         const auto rw = buffer.select<osmium::Way>();
356         const auto rr = buffer.select<osmium::Relation>();
357         counts.push_back(object_counts{static_cast<std::size_t>(std::distance(rn.begin(), rn.end())),
358                                        static_cast<std::size_t>(std::distance(rw.begin(), rw.end())),
359                                        static_cast<std::size_t>(std::distance(rr.begin(), rr.end()))});
360     }
361 
362     return counts;
363 }
364 
check_buffer_counts(const std::string & filename,const std::vector<object_counts> & oc,osmium::io::buffers_type btype)365 void check_buffer_counts(const std::string& filename, const std::vector<object_counts>& oc, osmium::io::buffers_type btype) {
366     for (const auto* suffix : {".osm", ".osm.opl", ".osm.o5m"}) {
367         const std::string fn = filename + suffix;
368         const auto counts = count_objects_per_buffer(fn.c_str(), btype);
369         REQUIRE(counts == oc);
370     }
371 }
372 
373 TEST_CASE("Reader with single object type per buffer") {
374     check_buffer_counts("t/io/data-n5w1r3", {{5, 1, 3}}, osmium::io::buffers_type::any);
375     check_buffer_counts("t/io/data-n5w1r3", {{5, 0, 0}, {0, 1, 0}, {0, 0, 3}}, osmium::io::buffers_type::single);
376     check_buffer_counts("t/io/data-n0w1r3", {{0, 1, 0}, {0, 0, 3}}, osmium::io::buffers_type::single);
377     check_buffer_counts("t/io/data-n5w0r3", {{5, 0, 0}, {0, 0, 3}}, osmium::io::buffers_type::single);
378     check_buffer_counts("t/io/data-n5w1r0", {{5, 0, 0}, {0, 1, 0}}, osmium::io::buffers_type::single);
379 }
380 
381