1 #include "catch.hpp"
2 
3 #include <osmium/builder/attr.hpp>
4 #include <osmium/memory/buffer.hpp>
5 #include <osmium/osm/tag.hpp>
6 #include <osmium/tags/filter.hpp>
7 #include <osmium/tags/regex_filter.hpp>
8 #include <osmium/tags/taglist.hpp>
9 
10 #include <algorithm>
11 #include <initializer_list>
12 #include <iterator>
13 #include <regex>
14 #include <utility>
15 #include <vector>
16 
17 template <class TFilter>
check_filter(const osmium::TagList & tag_list,const TFilter filter,const std::vector<bool> & reference)18 void check_filter(const osmium::TagList& tag_list,
19                   const TFilter filter,
20                   const std::vector<bool>& reference) {
21     REQUIRE(tag_list.size() == reference.size());
22     auto t_it = tag_list.begin();
23     for (auto it = reference.begin(); it != reference.end(); ++t_it, ++it) {
24         REQUIRE(filter(*t_it) == *it);
25     }
26 
27     typename TFilter::iterator fi_begin{filter, tag_list.begin(), tag_list.end()};
28     typename TFilter::iterator fi_end{filter, tag_list.end(), tag_list.end()};
29 
30     REQUIRE(std::distance(fi_begin, fi_end) == std::count(reference.begin(), reference.end(), true));
31 }
32 
make_tag_list(osmium::memory::Buffer & buffer,const std::initializer_list<std::pair<const char *,const char * >> & tags)33 const osmium::TagList& make_tag_list(osmium::memory::Buffer& buffer,
34                                      const std::initializer_list<std::pair<const char*, const char*>>& tags) {
35     const auto pos = osmium::builder::add_tag_list(buffer, osmium::builder::attr::_tags(tags));
36     return buffer.get<osmium::TagList>(pos);
37 }
38 
39 
40 TEST_CASE("KeyFilter") {
41     osmium::memory::Buffer buffer{10240};
42     osmium::tags::KeyFilter filter{false};
43 
44     const osmium::TagList& tag_list = make_tag_list(buffer, {
45         { "highway", "primary" },
46         { "name", "Main Street" },
47         { "source", "GPS" }
48     });
49 
50     SECTION("KeyFilter matches some tags") {
51         filter.add(true, "highway")
52               .add(true, "railway");
53 
54         const std::vector<bool> results = { true, false, false };
55 
56         check_filter(tag_list, filter, results);
57     }
58 
59     SECTION("KeyFilter iterator filters tags") {
60         filter.add(true, "highway")
61               .add(true, "source");
62 
63         osmium::tags::KeyFilter::iterator it{filter, tag_list.begin(),
64                                                      tag_list.end()};
65 
66         const osmium::tags::KeyFilter::iterator end{filter, tag_list.end(),
67                                                             tag_list.end()};
68 
69         REQUIRE(2 == std::distance(it, end));
70 
71         REQUIRE(it != end);
72         REQUIRE(std::string("highway") == it->key());
73         REQUIRE(std::string("primary") == it->value());
74         ++it;
75         REQUIRE(std::string("source") == it->key());
76         REQUIRE(std::string("GPS") == it->value());
77         REQUIRE(++it == end);
78     }
79 
80 }
81 
82 TEST_CASE("KeyValueFilter") {
83     osmium::memory::Buffer buffer{10240};
84 
85     SECTION("KeyValueFilter matches some tags") {
86         osmium::tags::KeyValueFilter filter{false};
87 
88         filter.add(true, "highway", "residential")
89               .add(true, "highway", "primary")
90               .add(true, "railway");
91 
92         const osmium::TagList& tag_list = make_tag_list(buffer, {
93             { "highway", "primary" },
94             { "railway", "tram" },
95             { "source", "GPS" }
96         });
97 
98         const std::vector<bool> results = {true, true, false};
99 
100         check_filter(tag_list, filter, results);
101     }
102 
103     SECTION("KeyValueFilter ordering matters") {
104         osmium::tags::KeyValueFilter filter1(false);
105         filter1.add(true, "highway")
106                .add(false, "highway", "road");
107 
108         osmium::tags::KeyValueFilter filter2(false);
109         filter2.add(false, "highway", "road")
110                .add(true, "highway");
111 
112         const osmium::TagList& tag_list1 = make_tag_list(buffer, {
113             { "highway", "road" },
114             { "name", "Main Street" }
115         });
116 
117         const osmium::TagList& tag_list2 = make_tag_list(buffer, {
118             { "highway", "primary" },
119             { "name", "Main Street" }
120         });
121 
122         check_filter(tag_list1, filter1, {true, false});
123         check_filter(tag_list1, filter2, {false, false});
124         check_filter(tag_list2, filter2, {true, false});
125     }
126 
127     SECTION("KeyValueFilter matches against taglist with any") {
128         osmium::tags::KeyValueFilter filter{false};
129 
130         filter.add(true, "highway", "primary")
131               .add(true, "name");
132 
133         const osmium::TagList& tag_list = make_tag_list(buffer, {
134             { "highway", "primary" },
135             { "railway", "tram" },
136             { "source", "GPS" }
137         });
138 
139         REQUIRE(     osmium::tags::match_any_of(tag_list, filter));
140         REQUIRE_FALSE(osmium::tags::match_all_of(tag_list, filter));
141         REQUIRE_FALSE(osmium::tags::match_none_of(tag_list, filter));
142     }
143 
144     SECTION("KeyValueFilter matches against taglist with_all") {
145         osmium::tags::KeyValueFilter filter{false};
146 
147         filter.add(true, "highway", "primary")
148               .add(true, "name");
149 
150         const osmium::TagList& tag_list = make_tag_list(buffer, {
151             { "highway", "primary" },
152             { "name", "Main Street" }
153         });
154 
155         REQUIRE(      osmium::tags::match_any_of(tag_list, filter));
156         REQUIRE(      osmium::tags::match_all_of(tag_list, filter));
157         REQUIRE_FALSE(osmium::tags::match_none_of(tag_list, filter));
158     }
159 
160     SECTION("KeyValueFilter matches against taglist with none") {
161         osmium::tags::KeyValueFilter filter{false};
162 
163         filter.add(true, "highway", "road")
164               .add(true, "source");
165 
166         const osmium::TagList& tag_list = make_tag_list(buffer, {
167             { "highway", "primary" },
168             { "name", "Main Street" }
169         });
170 
171         REQUIRE_FALSE(osmium::tags::match_any_of(tag_list, filter));
172         REQUIRE_FALSE(osmium::tags::match_all_of(tag_list, filter));
173         REQUIRE(      osmium::tags::match_none_of(tag_list, filter));
174     }
175 
176     SECTION("KeyValueFilter matches against taglist with any called with rvalue") {
177         const osmium::TagList& tag_list = make_tag_list(buffer, {
178             { "highway", "primary" },
179             { "railway", "tram" },
180             { "source", "GPS" }
181         });
182 
183         REQUIRE(osmium::tags::match_any_of(tag_list,
184                                            osmium::tags::KeyValueFilter()
185                                                .add(true, "highway", "primary")
186                                                .add(true, "name")));
187     }
188 
189 }
190 
191 TEST_CASE("RegexFilter matches some tags") {
192     osmium::memory::Buffer buffer{10240};
193 
194     osmium::tags::RegexFilter filter{false};
195     filter.add(true, "highway", std::regex{".*_link"});
196 
197     const osmium::TagList& tag_list1 = make_tag_list(buffer, {
198         { "highway", "primary_link" },
199         { "source", "GPS" }
200     });
201     const osmium::TagList& tag_list2 = make_tag_list(buffer, {
202         { "highway", "primary" },
203         { "source", "GPS" }
204     });
205 
206     check_filter(tag_list1, filter, {true, false});
207     check_filter(tag_list2, filter, {false, false});
208 }
209 
210 TEST_CASE("RegexFilter matches some tags with lvalue regex") {
211     osmium::memory::Buffer buffer{10240};
212     osmium::tags::RegexFilter filter{false};
213     std::regex r{".*straße"};
214     filter.add(true, "name", r);
215 
216     const osmium::TagList& tag_list = make_tag_list(buffer, {
217         { "highway", "primary" },
218         { "name", "Hauptstraße" }
219     });
220 
221     check_filter(tag_list, filter, {false, true});
222 }
223 
224 TEST_CASE("KeyPrefixFilter matches some keys") {
225     osmium::memory::Buffer buffer{10240};
226 
227     osmium::tags::KeyPrefixFilter filter{false};
228     filter.add(true, "name:");
229 
230     const osmium::TagList& tag_list = make_tag_list(buffer, {
231         { "highway", "primary" },
232         { "name:de", "Hauptstraße" }
233     });
234 
235     check_filter(tag_list, filter, {false, true});
236 
237 }
238 
239 TEST_CASE("Generic Filter with regex matches some keys") {
240     osmium::memory::Buffer buffer{10240};
241 
242     osmium::tags::Filter<std::regex> filter{false};
243     filter.add(true, std::regex{"restriction.+conditional"});
244 
245     const osmium::TagList& tag_list = make_tag_list(buffer, {
246         { "highway", "primary" },
247         { "restrictionconditional", "only_right_turn @ (Mo-Fr 07:00-14:00)" },
248         { "restriction:conditional", "only_right_turn @ (Mo-Fr 07:00-14:00)" },
249         { "restriction:psv:conditional", "only_right_turn @ (Mo-Fr 07:00-14:00)" }
250     });
251 
252     check_filter(tag_list, filter, {false, false, true, true});
253 
254 }
255