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