Merge lp:~autopilot/xpathselect/experimental into lp:xpathselect
- experimental
- Merge into trunk
Proposed by
Thomi Richards
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Martin Pitt | ||||||||||||
Approved revision: | 51 | ||||||||||||
Merged at revision: | 38 | ||||||||||||
Proposed branch: | lp:~autopilot/xpathselect/experimental | ||||||||||||
Merge into: | lp:xpathselect | ||||||||||||
Diff against target: |
1575 lines (+812/-188) 12 files modified
CMakeLists.txt (+1/-1) debian/changelog (+7/-0) debian/control (+2/-2) lib/node.h (+14/-6) lib/parser.h (+91/-11) lib/xpathquerypart.h (+34/-5) lib/xpathselect.cpp (+31/-6) lib/xpathselect.h (+1/-1) test/dummynode.h (+62/-16) test/test_parser.cpp (+503/-114) test/test_xpath_simple.cpp (+5/-5) test/test_xpath_tree.cpp (+61/-21) |
||||||||||||
To merge this branch: | bzr merge lp:~autopilot/xpathselect/experimental | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Pitt (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+185846@code.launchpad.net |
Commit message
XPathSelect type changes and API/ABI bump.
Description of the change
Change for 1.4.
To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : | # |
review:
Approve
(continuous-integration)
Revision history for this message
Martin Pitt (pitti) wrote : | # |
I didn't re-review every single line of course, but structurally this seems okay. It properly bumps the SONAME and all that, and test coverage is fine.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2013-04-18 03:29:31 +0000 |
3 | +++ CMakeLists.txt 2013-09-16 15:53:33 +0000 |
4 | @@ -6,7 +6,7 @@ |
5 | |
6 | include (GNUInstallDirs) |
7 | |
8 | -set (VERSION 1.3) |
9 | +set (VERSION 1.4) |
10 | |
11 | if(CMAKE_BUILD_TYPE STREQUAL "coverage") |
12 | message("Building for coverage") |
13 | |
14 | === modified file 'debian/changelog' |
15 | --- debian/changelog 2013-06-05 09:01:46 +0000 |
16 | +++ debian/changelog 2013-09-16 15:53:33 +0000 |
17 | @@ -1,3 +1,10 @@ |
18 | +xpathselect (1.4-0ubuntu1) saucy; urgency=low |
19 | + |
20 | + [ Thomi Richards ] |
21 | + * New API, bump version to 1.4. |
22 | + |
23 | + -- Thomi Richards <thomi.richards@canonical.com> Thu, 15 Aug 2013 11:08:08 +1200 |
24 | + |
25 | xpathselect (1.3daily13.06.05.1-0ubuntu1) saucy; urgency=low |
26 | |
27 | [ Thomi Richards ] |
28 | |
29 | === modified file 'debian/control' |
30 | --- debian/control 2013-04-18 03:29:31 +0000 |
31 | +++ debian/control 2013-09-16 15:53:33 +0000 |
32 | @@ -20,14 +20,14 @@ |
33 | Section: libdevel |
34 | Architecture: any |
35 | Depends: ${misc:Depends}, |
36 | - libxpathselect1.3 (= ${binary:Version}), |
37 | + libxpathselect1.4 (= ${binary:Version}), |
38 | Description: Select objects in an object tree using XPath queries - development files |
39 | This library allows you to select arbitrary objects in an object tree using a |
40 | small subset of the XPath specification. |
41 | . |
42 | This package contains development files for xpathselect. |
43 | |
44 | -Package: libxpathselect1.3 |
45 | +Package: libxpathselect1.4 |
46 | Section: libs |
47 | Architecture: any |
48 | Multi-Arch: same |
49 | |
50 | === renamed file 'debian/libxpathselect1.3.install' => 'debian/libxpathselect1.4.install' |
51 | === modified file 'lib/node.h' |
52 | --- lib/node.h 2013-04-22 02:18:53 +0000 |
53 | +++ lib/node.h 2013-09-16 15:53:33 +0000 |
54 | @@ -20,7 +20,9 @@ |
55 | |
56 | #include <string> |
57 | #include <vector> |
58 | +#include <list> |
59 | #include <memory> |
60 | +#include <cstdint> |
61 | |
62 | namespace xpathselect |
63 | { |
64 | @@ -29,7 +31,7 @@ |
65 | class Node |
66 | { |
67 | public: |
68 | - typedef std::shared_ptr<Node> Ptr; |
69 | + typedef std::shared_ptr<const Node> Ptr; |
70 | |
71 | /// Get the node's name. |
72 | virtual std::string GetName() const =0; |
73 | @@ -37,17 +39,23 @@ |
74 | /// Get the node's full path |
75 | virtual std::string GetPath() const =0; |
76 | |
77 | - /// Return true if the node matches the property with the given name & value |
78 | - ///\note: All property values are treated as strings. It is the implementors |
79 | - /// responsibility to convert the value to the appropriate type. |
80 | - virtual bool MatchProperty(const std::string& name, const std::string& value) const =0; |
81 | + /// Get this node's ID. |
82 | + virtual int32_t GetId() const =0; |
83 | + |
84 | + virtual bool MatchBooleanProperty(const std::string& name, bool value) const =0; |
85 | + virtual bool MatchIntegerProperty(const std::string& name, int32_t value) const =0; |
86 | + virtual bool MatchStringProperty(const std::string& name, const std::string& value) const =0; |
87 | |
88 | /// Return a list of the children of this node. |
89 | virtual std::vector<Node::Ptr> Children() const =0; |
90 | + |
91 | + /// Return a pointer to the parent class. |
92 | + virtual Node::Ptr GetParent() const =0; |
93 | }; |
94 | |
95 | /// NodeList is how we return lists of nodes. |
96 | - typedef std::vector<Node::Ptr> NodeList; |
97 | + typedef std::vector<Node::Ptr> NodeVector; |
98 | + typedef std::list<Node::Ptr> NodeList; |
99 | } |
100 | |
101 | #endif |
102 | |
103 | === modified file 'lib/parser.h' |
104 | --- lib/parser.h 2013-04-24 20:38:18 +0000 |
105 | +++ lib/parser.h 2013-09-16 15:53:33 +0000 |
106 | @@ -18,12 +18,15 @@ |
107 | #define _PARSER_H |
108 | |
109 | #include <string> |
110 | +#include <cstdint> |
111 | |
112 | #include <boost/config/warning_disable.hpp> |
113 | #include <boost/fusion/include/adapt_struct.hpp> |
114 | #include <boost/spirit/include/phoenix_object.hpp> |
115 | #include <boost/spirit/include/phoenix_operator.hpp> |
116 | #include <boost/spirit/include/qi.hpp> |
117 | +#include <boost/spirit/include/qi_bool.hpp> |
118 | +#include <boost/spirit/include/qi_int.hpp> |
119 | |
120 | #include "xpathquerypart.h" |
121 | |
122 | @@ -37,7 +40,7 @@ |
123 | BOOST_FUSION_ADAPT_STRUCT( |
124 | xpathselect::XPathQueryParam, |
125 | (std::string, param_name) |
126 | - (std::string, param_value) |
127 | + (xpathselect::XPathQueryParam::ParamValueType, param_value) |
128 | ); |
129 | |
130 | namespace xpathselect |
131 | @@ -47,6 +50,33 @@ |
132 | namespace qi = boost::spirit::qi; |
133 | namespace phoenix = boost::phoenix; |
134 | |
135 | + // python_bool_policy determines what can be considered truthy. We follow python |
136 | + // repr format. |
137 | + struct python_bool_policy : qi::bool_policies<> |
138 | + { |
139 | + template <typename Iterator, typename Attribute> |
140 | + static bool parse_true(Iterator& first, Iterator const& last, Attribute& attr) |
141 | + { |
142 | + if (qi::detail::string_parse("True", first, last, qi::unused)) |
143 | + { |
144 | + boost::spirit::traits::assign_to(true, attr); // result is true |
145 | + return true; // parsing succeeded |
146 | + } |
147 | + return false; // parsing failed |
148 | + } |
149 | + |
150 | + template <typename Iterator, typename Attribute> |
151 | + static bool parse_false(Iterator& first, Iterator const& last, Attribute& attr) |
152 | + { |
153 | + if (qi::detail::string_parse("False", first, last, qi::unused)) |
154 | + { |
155 | + boost::spirit::traits::assign_to(false, attr); // result is false |
156 | + return true; |
157 | + } |
158 | + return false; |
159 | + } |
160 | + }; |
161 | + |
162 | // This is the main XPath grammar. It looks horrible, until you emerse yourself in it for a few |
163 | // days, then the beauty of boost::spirit creeps into your brain. To help future programmers, |
164 | // I've heavily commented this. |
165 | @@ -61,6 +91,46 @@ |
166 | { |
167 | using namespace qi::labels; |
168 | |
169 | + // character escape codes. The input on the left will produce the output on |
170 | + // the right: |
171 | + unesc_char.add("\\a", '\a') |
172 | + ("\\b", '\b') |
173 | + ("\\f", '\f') |
174 | + ("\\n", '\n') |
175 | + ("\\r", '\r') |
176 | + ("\\t", '\t') |
177 | + ("\\v", '\v') |
178 | + ("\\\\", '\\') |
179 | + ("\\\'", '\'') |
180 | + ("\\\"", '\"'); |
181 | + |
182 | + unesc_str = '"' >> *( |
183 | + unesc_char | |
184 | + qi::alnum | |
185 | + qi::space | |
186 | + "\\x" >> qi::hex |
187 | + ) >> '"'; |
188 | + |
189 | + unesc_str = '"' |
190 | + >> *(unesc_char | "\\x" >> qi::hex | (qi::print - '"')) |
191 | + >> '"' |
192 | + ; |
193 | + |
194 | + int_type = qi::int_parser<int32_t>(); |
195 | + |
196 | + // Parameter grammar: |
197 | + // parameter name can contain some basic text (no spaces or '.') |
198 | + param_name = +qi::char_("a-zA-Z0-9_\\-"); |
199 | + |
200 | + // parameter values can be several different types. |
201 | + // Alternatives are tried left to right, and the first match found is the one used. |
202 | + param_value = unesc_str | int_type | bool_type; |
203 | + // parameter specification is simple: name=value |
204 | + param %= param_name >> '=' >> param_value; |
205 | + // a parameter list is a list of parameters separated by ',''s surrounded in '[...]' |
206 | + param_list = '[' >> param % ',' >> ']'; |
207 | + |
208 | + |
209 | // spec_node_name is a node name that has been explicitly specified. |
210 | // it must start and end with a non-space character, but you can have |
211 | // spaces in the middle. |
212 | @@ -68,14 +138,6 @@ |
213 | // a wildcard node name is simply a '*' |
214 | wildcard_node_name = qi::char_("*"); |
215 | |
216 | - // parameter name can contain some basic text (no spaces or '.') |
217 | - param_name = +qi::char_("a-zA-Z0-9_\\-"); |
218 | - // parameter values can have more stuff in them. |
219 | - param_value = +qi::char_("a-z.A-Z0-9_\\-") >> *(+qi::char_(" ") >> +qi::char_("a-z.A-Z0-9_\\-")); |
220 | - // parameter specification is simple: name=value |
221 | - param %= param_name >> '=' >> param_value; |
222 | - // a parameter list is a list of parameters separated by ',''s surrounded in '[...]' |
223 | - param_list = '[' >> param % ',' >> ']'; |
224 | |
225 | // a spec_node consists of a specified node name, followed by an *optional* parameter list. |
226 | spec_node %= spec_node_name >> -(param_list); |
227 | @@ -83,15 +145,18 @@ |
228 | wildcard_node %= wildcard_node_name >> !param_list; |
229 | // wildcard nodes can also have parameters: |
230 | wildcard_node_with_params %= wildcard_node_name >> param_list; |
231 | + // A parent node is '..' as long as it's followed by a normal separator or end of input: |
232 | + parent_node = qi::lit("..")[qi::_val = XPathQueryPart("..")]; |
233 | |
234 | // node is simply any kind of code defined thus far: |
235 | - node = spec_node | wildcard_node_with_params | wildcard_node; |
236 | + node = spec_node | wildcard_node_with_params | wildcard_node | parent_node; |
237 | |
238 | // a search node is '//' as long as it's followed by a spec node or a wildcard node with parameters. |
239 | // we don't allow '//*' since it would match everything in the tree, and cause HUGE amounts of |
240 | // data to be transmitted. |
241 | search_node = "//" >> &(spec_node | wildcard_node_with_params)[qi::_val = XPathQueryPart()]; |
242 | |
243 | + |
244 | // a normal separator is a '/' as long as it's followed by something other than another '/' |
245 | normal_sep = '/' >> !qi::lit('/'); |
246 | separator = normal_sep | search_node; // nodes can be separated by normal_sep or search_node. |
247 | @@ -147,14 +212,29 @@ |
248 | #endif |
249 | } |
250 | // declare all the rules. The second template parameter is the type they produce. |
251 | + // basic type rules: |
252 | + |
253 | + // parse python Boolean represetnations 'True' or 'False': |
254 | + qi::bool_parser<bool, python_bool_policy> bool_type; |
255 | + |
256 | + // parse an escaped byte string. |
257 | + qi::rule<Iterator, std::string()> unesc_str; |
258 | + // symbol table for chracter scape codes. |
259 | + qi::symbols<char const, char const> unesc_char; |
260 | + |
261 | + // parse integers, first signed then unsigned: |
262 | + qi::rule<Iterator, int32_t()> int_type; |
263 | + |
264 | + // more complicated language rules: |
265 | qi::rule<Iterator, std::string()> spec_node_name; |
266 | qi::rule<Iterator, std::string()> wildcard_node_name; |
267 | qi::rule<Iterator, XPathQueryPart()> search_node; |
268 | + qi::rule<Iterator, XPathQueryPart()> parent_node; |
269 | qi::rule<Iterator> normal_sep; |
270 | qi::rule<Iterator, xpathselect::QueryList()> separator; |
271 | |
272 | qi::rule<Iterator, std::string()> param_name; |
273 | - qi::rule<Iterator, std::string()> param_value; |
274 | + qi::rule<Iterator, xpathselect::XPathQueryParam::ParamValueType()> param_value; |
275 | qi::rule<Iterator, XPathQueryParam()> param; |
276 | qi::rule<Iterator, xpathselect::ParamList()> param_list; |
277 | |
278 | |
279 | === modified file 'lib/xpathquerypart.h' |
280 | --- lib/xpathquerypart.h 2013-06-05 07:31:01 +0000 |
281 | +++ lib/xpathquerypart.h 2013-09-16 15:53:33 +0000 |
282 | @@ -24,6 +24,8 @@ |
283 | #include <iostream> |
284 | |
285 | #include <boost/optional/optional.hpp> |
286 | +#include <boost/variant/variant.hpp> |
287 | +#include <boost/variant/get.hpp> |
288 | |
289 | #include "node.h" |
290 | |
291 | @@ -32,8 +34,11 @@ |
292 | // stores a parameter name, value pair. |
293 | struct XPathQueryParam |
294 | { |
295 | + typedef boost::variant<std::string, |
296 | + bool, |
297 | + int> ParamValueType; |
298 | std::string param_name; |
299 | - std::string param_value; |
300 | + ParamValueType param_value; |
301 | }; |
302 | |
303 | typedef std::vector<XPathQueryParam> ParamList; |
304 | @@ -47,7 +52,7 @@ |
305 | : node_name_(node_name) |
306 | {} |
307 | |
308 | - enum class QueryPartType {Normal, Search}; |
309 | + enum class QueryPartType {Normal, Search, Parent}; |
310 | |
311 | bool Matches(Node::Ptr const& node) const |
312 | { |
313 | @@ -56,15 +61,39 @@ |
314 | { |
315 | for (auto param : parameter) |
316 | { |
317 | - matches &= node->MatchProperty(param.param_name, param.param_value); |
318 | - |
319 | + switch(param.param_value.which()) |
320 | + { |
321 | + case 0: |
322 | + { |
323 | + matches &= node->MatchStringProperty(param.param_name, boost::get<std::string>(param.param_value)); |
324 | + } |
325 | + break; |
326 | + case 1: |
327 | + { |
328 | + matches &= node->MatchBooleanProperty(param.param_name, boost::get<bool>(param.param_value)); |
329 | + } |
330 | + break; |
331 | + case 2: |
332 | + { |
333 | + matches &= node->MatchIntegerProperty(param.param_name, boost::get<int>(param.param_value)); |
334 | + } |
335 | + break; |
336 | + } |
337 | } |
338 | } |
339 | |
340 | return matches; |
341 | } |
342 | |
343 | - QueryPartType Type() const { return (node_name_ == "") ? QueryPartType::Search : QueryPartType::Normal; } |
344 | + QueryPartType Type() const |
345 | + { |
346 | + if (node_name_ == "") |
347 | + return QueryPartType::Search; |
348 | + else if (node_name_ == "..") |
349 | + return QueryPartType::Parent; |
350 | + else |
351 | + return QueryPartType::Normal; |
352 | + } |
353 | |
354 | void Dump() const |
355 | { |
356 | |
357 | === modified file 'lib/xpathselect.cpp' |
358 | --- lib/xpathselect.cpp 2013-04-24 08:57:16 +0000 |
359 | +++ lib/xpathselect.cpp 2013-09-16 15:53:33 +0000 |
360 | @@ -82,7 +82,7 @@ |
361 | } |
362 | } // end of anonymous namespace |
363 | |
364 | - NodeList SelectNodes(Node::Ptr const& root, std::string query) |
365 | + NodeVector SelectNodes(Node::Ptr const& root, std::string query) |
366 | { |
367 | // allow users to be lazy when specifying tree root: |
368 | if (query == "" || query == "/" || query == "//") |
369 | @@ -92,7 +92,7 @@ |
370 | |
371 | QueryList query_parts = GetQueryPartsFromQuery(query); |
372 | if (query_parts.empty()) |
373 | - return NodeList(); |
374 | + return NodeVector(); |
375 | auto query_part = query_parts.cbegin(); |
376 | NodeList start_nodes { root }; |
377 | while (query_part != query_parts.cend()) |
378 | @@ -105,12 +105,24 @@ |
379 | // do some sanity checking... |
380 | if (query_part->Type() == XPathQueryPart::QueryPartType::Search) |
381 | // invalid query - cannot specify multiple search sequences in a row. |
382 | - return NodeList(); |
383 | + return NodeVector(); |
384 | // then find all the nodes that match the new query part, and store them as |
385 | // the new start nodes. We pass in 'start_nodes' rather than 'root' since |
386 | // there's a chance we'll be doing more than one search in different parts of the tree. |
387 | start_nodes = SearchTreeForNode(start_nodes, *query_part); |
388 | } |
389 | + else if (query_part->Type() == XPathQueryPart::QueryPartType::Parent) |
390 | + { |
391 | + // This part of the query selects the parent node. If the current node has no |
392 | + // parent (i.e.- we're already at the root of the tree) then this is a no-op: |
393 | + NodeList new_start_nodes; |
394 | + for (auto n: start_nodes) |
395 | + { |
396 | + auto parent = n->GetParent(); |
397 | + new_start_nodes.push_back(parent ? parent : n); |
398 | + } |
399 | + start_nodes = new_start_nodes; |
400 | + } |
401 | else |
402 | { |
403 | // this isn't a search token. Look at each node in the start_nodes list, |
404 | @@ -128,8 +140,11 @@ |
405 | ); |
406 | } |
407 | // then replace each node still in the list with all it's children. |
408 | - // ... but only if we're not on the last query part: |
409 | - if (query_part + 1 != query_parts.cend()) |
410 | + // ... but only if we're not on the last query part, and only if the |
411 | + // next query part is not a parent node... |
412 | + auto next_query_part = query_part + 1; |
413 | + if (next_query_part != query_parts.cend() |
414 | + && next_query_part->Type() != XPathQueryPart::QueryPartType::Parent) |
415 | { |
416 | NodeList new_start_nodes; |
417 | for (auto node: start_nodes) |
418 | @@ -147,6 +162,16 @@ |
419 | } |
420 | ++query_part; |
421 | } |
422 | - return start_nodes; |
423 | + // remove duplicate nodes by sorting & unique'ing: |
424 | + // we could probably do this better, but since start_nodes is |
425 | + // typically very small at this stage, I'm not sure it's worth it: |
426 | + start_nodes.sort([](Node::Ptr a, Node::Ptr b) -> bool { |
427 | + return a->GetId() < b->GetId(); |
428 | + }); |
429 | + start_nodes.unique([](Node::Ptr a, Node::Ptr b) -> bool { |
430 | + return a->GetId() == b->GetId(); |
431 | + }); |
432 | + |
433 | + return NodeVector(start_nodes.begin(), start_nodes.end()); |
434 | } |
435 | } |
436 | |
437 | === modified file 'lib/xpathselect.h' |
438 | --- lib/xpathselect.h 2013-02-01 23:20:24 +0000 |
439 | +++ lib/xpathselect.h 2013-09-16 15:53:33 +0000 |
440 | @@ -24,7 +24,7 @@ |
441 | { |
442 | /// Search the node tree beginning with 'root' and return nodes that |
443 | /// match 'query'. |
444 | - extern "C" NodeList SelectNodes(Node::Ptr const& root, std::string query); |
445 | + extern "C" NodeVector SelectNodes(Node::Ptr const& root, std::string query); |
446 | } |
447 | |
448 | #endif |
449 | |
450 | === modified file 'test/dummynode.h' |
451 | --- test/dummynode.h 2013-04-18 03:29:31 +0000 |
452 | +++ test/dummynode.h 2013-09-16 15:53:33 +0000 |
453 | @@ -23,12 +23,17 @@ |
454 | #include <map> |
455 | |
456 | // simple implementation of the node interface for testing purposes. |
457 | -class DummyNode: public xpathselect::Node |
458 | +class DummyNode: public xpathselect::Node, public std::enable_shared_from_this<DummyNode> |
459 | { |
460 | public: |
461 | + typedef std::shared_ptr<DummyNode> Ptr; |
462 | + |
463 | DummyNode(std::string name="DummyNode") |
464 | : name_(name) |
465 | - {} |
466 | + { |
467 | + static int32_t id = 1; |
468 | + id_ = id++; |
469 | + } |
470 | |
471 | std::string GetName() const override |
472 | { |
473 | @@ -40,38 +45,79 @@ |
474 | return std::string(); |
475 | } |
476 | |
477 | + int32_t GetId() const override |
478 | + { |
479 | + return id_; |
480 | + } |
481 | + |
482 | void SetName(std::string const& name) |
483 | { |
484 | name_ = name; |
485 | } |
486 | |
487 | - bool MatchProperty(const std::string& name, const std::string& value) const override |
488 | - { |
489 | - auto it = properties_.find(name); |
490 | - if (it == properties_.end() || it->second != value) |
491 | - return false; |
492 | - return true; |
493 | - } |
494 | - |
495 | - xpathselect::NodeList Children() const override |
496 | + bool MatchStringProperty(const std::string& name, const std::string& value) const override |
497 | + { |
498 | + auto it = string_properties_.find(name); |
499 | + if (it == string_properties_.end() || it->second != value) |
500 | + return false; |
501 | + return true; |
502 | + } |
503 | + |
504 | + bool MatchBooleanProperty(const std::string& name, bool value) const override |
505 | + { |
506 | + auto it = bool_properties_.find(name); |
507 | + if (it == bool_properties_.end() || it->second != value) |
508 | + return false; |
509 | + return true; |
510 | + } |
511 | + |
512 | + bool MatchIntegerProperty(const std::string& name, int value) const override |
513 | + { |
514 | + auto it = int_properties_.find(name); |
515 | + if (it == int_properties_.end() || it->second != value) |
516 | + return false; |
517 | + return true; |
518 | + } |
519 | + |
520 | + xpathselect::NodeVector Children() const override |
521 | { |
522 | return children_; |
523 | } |
524 | |
525 | - void AddChild(Node::Ptr const& child) |
526 | - { |
527 | + virtual Node::Ptr GetParent() const override |
528 | + { |
529 | + return parent_; |
530 | + } |
531 | + |
532 | + void AddChild(Ptr const& child) |
533 | + { |
534 | + child->parent_ = shared_from_this(); |
535 | children_.push_back(child); |
536 | } |
537 | |
538 | void AddProperty(std::string const& name, std::string const& value) |
539 | { |
540 | - properties_[name] = value; |
541 | + string_properties_[name] = value; |
542 | + } |
543 | + |
544 | + void AddProperty(std::string const& name, bool value) |
545 | + { |
546 | + bool_properties_[name] = value; |
547 | + } |
548 | + |
549 | + void AddProperty(std::string const& name, int value) |
550 | + { |
551 | + int_properties_[name] = value; |
552 | } |
553 | |
554 | private: |
555 | + int32_t id_; |
556 | std::string name_; |
557 | - xpathselect::NodeList children_; |
558 | - std::map<std::string, std::string> properties_; |
559 | + xpathselect::Node::Ptr parent_; |
560 | + xpathselect::NodeVector children_; |
561 | + std::map<std::string, std::string> string_properties_; |
562 | + std::map<std::string, int> int_properties_; |
563 | + std::map<std::string, bool> bool_properties_; |
564 | }; |
565 | |
566 | |
567 | |
568 | === modified file 'test/test_parser.cpp' |
569 | --- test/test_parser.cpp 2013-04-24 09:38:10 +0000 |
570 | +++ test/test_parser.cpp 2013-09-16 15:53:33 +0000 |
571 | @@ -24,9 +24,13 @@ |
572 | #include <boost/spirit/include/phoenix_core.hpp> |
573 | #include <boost/spirit/include/qi_char_class.hpp> |
574 | #include <boost/spirit/include/phoenix_operator.hpp> |
575 | +#include <boost/variant/variant.hpp> |
576 | + |
577 | #include <iostream> |
578 | #include <string> |
579 | #include <cstdlib> |
580 | +#include <typeinfo> |
581 | +#include <limits> |
582 | |
583 | namespace qi = boost::spirit::qi; |
584 | namespace ascii = boost::spirit::ascii; |
585 | @@ -69,6 +73,355 @@ |
586 | } |
587 | } |
588 | |
589 | +// a boost static visitor that checks value equality and type equality. |
590 | +template <typename T> |
591 | +class variant_equality_assertion : public boost::static_visitor<> |
592 | +{ |
593 | +public: |
594 | + variant_equality_assertion( T const& expected) |
595 | + : expected_(expected) |
596 | + {} |
597 | + |
598 | + void operator()( T & operand ) const |
599 | + { |
600 | + ASSERT_EQ(expected_, operand); |
601 | + } |
602 | + |
603 | + template <typename U> void operator()( U & operand ) const |
604 | + { |
605 | + FAIL() << "Variant contained incorrect type! Expected: '" |
606 | + << expected_ |
607 | + << "' Actual: '" |
608 | + << operand |
609 | + << "' Actual type is: " |
610 | + << typeid(U).name(); |
611 | + } |
612 | +private: |
613 | + T expected_; |
614 | +}; |
615 | + |
616 | +////////////////////////////////////// |
617 | +// Tests for basic type support: |
618 | +////////////////////////////////////// |
619 | + |
620 | + |
621 | +// Test python representations for boolean values: |
622 | +TEST(TestXPathParser, test_basic_type_boolean) |
623 | +{ |
624 | + bool result = false; |
625 | + parser::xpath_grammar<std::string::iterator> g; |
626 | + |
627 | + ASSERT_TRUE(test_parser_attr("True", g.bool_type, result)); |
628 | + ASSERT_TRUE(result); |
629 | + |
630 | + ASSERT_TRUE(test_parser_attr("False", g.bool_type, result)); |
631 | + ASSERT_FALSE(result); |
632 | + |
633 | + ASSERT_FALSE(test_parser_attr("true", g.bool_type, result)); |
634 | + ASSERT_FALSE(test_parser_attr("false", g.bool_type, result)); |
635 | + ASSERT_FALSE(test_parser_attr("1", g.bool_type, result)); |
636 | + ASSERT_FALSE(test_parser_attr("0", g.bool_type, result)); |
637 | +} |
638 | + |
639 | + |
640 | +// test character escape codes: |
641 | +class TestXPathParserCharacterEscapeCodes : public ::testing::TestWithParam<std::pair<std::string, char> > |
642 | +{ |
643 | +}; |
644 | + |
645 | + |
646 | +TEST_P(TestXPathParserCharacterEscapeCodes, test_character_escape_codes) |
647 | +{ |
648 | + auto p = GetParam(); |
649 | + |
650 | + std::string input = p.first; |
651 | + char expected_result = p.second; |
652 | + |
653 | + char actual_result = 0; |
654 | + parser::xpath_grammar<std::string::iterator> g; |
655 | + |
656 | + ASSERT_TRUE(test_parser_attr(input, g.unesc_char, actual_result)); |
657 | + ASSERT_EQ(expected_result, actual_result); |
658 | +} |
659 | + |
660 | +INSTANTIATE_TEST_CASE_P(BasicCharacterCodes, |
661 | + TestXPathParserCharacterEscapeCodes, |
662 | + ::testing::Values( |
663 | + std::pair<std::string, char>("\\a", '\a'), |
664 | + std::pair<std::string, char>("\\b", '\b'), |
665 | + std::pair<std::string, char>("\\f", '\f'), |
666 | + std::pair<std::string, char>("\\n", '\n'), |
667 | + std::pair<std::string, char>("\\r", '\r'), |
668 | + std::pair<std::string, char>("\\t", '\t'), |
669 | + std::pair<std::string, char>("\\v", '\v'), |
670 | + std::pair<std::string, char>("\\\\", '\\'), |
671 | + std::pair<std::string, char>("\\\'", '\''), |
672 | + std::pair<std::string, char>("\\\"", '\"') |
673 | + )); |
674 | + |
675 | + |
676 | +class QuotedStringTests : public ::testing::TestWithParam<std::tuple<const char*, const char*, bool> > |
677 | +{ |
678 | +}; |
679 | + |
680 | +TEST_P(QuotedStringTests, quoted_string_parameter_test) |
681 | +{ |
682 | + std::string input = std::get<0>(GetParam()); |
683 | + std::string expected_output = std::get<1>(GetParam()); |
684 | + bool expected_pass = std::get<2>(GetParam()); |
685 | + |
686 | + std::string actual_result; |
687 | + parser::xpath_grammar<std::string::iterator> g; |
688 | + |
689 | + ASSERT_EQ(expected_pass, test_parser_attr(input, g.unesc_str, actual_result)); |
690 | + if (expected_pass) |
691 | + ASSERT_EQ(expected_output, actual_result); |
692 | +} |
693 | + |
694 | + |
695 | +INSTANTIATE_TEST_CASE_P(BasicStrings, |
696 | + QuotedStringTests, |
697 | + ::testing::Values( |
698 | + std::make_tuple("\"Hello\"", "Hello", true), |
699 | + std::make_tuple("\"Hello World\"", "Hello World", true), |
700 | + std::make_tuple("\"a b c d\"", "a b c d", true), |
701 | + std::make_tuple("\"\\x41\"", "A", true), |
702 | + std::make_tuple("\"\\x08\"", "\b", true) |
703 | + )); |
704 | + |
705 | +INSTANTIATE_TEST_CASE_P(PunctuationStrings, |
706 | + QuotedStringTests, |
707 | + ::testing::Values( |
708 | + std::make_tuple("\".\"", ".", true), |
709 | + std::make_tuple("\",\"", ",", true), |
710 | + std::make_tuple("\"<\"", "<", true), |
711 | + std::make_tuple("\">\"", ">", true), |
712 | + std::make_tuple("\"/\"", "/", true), |
713 | + std::make_tuple("\"?\"", "?", true), |
714 | + std::make_tuple("\":\"", ":", true), |
715 | + std::make_tuple("\";\"", ";", true), |
716 | + std::make_tuple("\"'\"", "'", true), // '"' tested below |
717 | + std::make_tuple("\"[\"", "[", true), |
718 | + std::make_tuple("\"]\"", "]", true), |
719 | + std::make_tuple("\"{\"", "{", true), |
720 | + std::make_tuple("\"}\"", "}", true), |
721 | + std::make_tuple("\"\\\\\"", "\\", true), |
722 | + std::make_tuple("\"|\"", "|", true), |
723 | + std::make_tuple("\"~\"", "~", true), |
724 | + std::make_tuple("\"`\"", "`", true), |
725 | + std::make_tuple("\"!\"", "!", true), |
726 | + std::make_tuple("\"@\"", "@", true), |
727 | + std::make_tuple("\"#\"", "#", true), |
728 | + std::make_tuple("\"$\"", "$", true), |
729 | + std::make_tuple("\"%\"", "%", true), |
730 | + std::make_tuple("\"^\"", "^", true), |
731 | + std::make_tuple("\"&\"", "&", true), |
732 | + std::make_tuple("\"*\"", "*", true), |
733 | + std::make_tuple("\"(\"", "(", true), |
734 | + std::make_tuple("\")\"", ")", true), |
735 | + std::make_tuple("\"-\"", "-", true), |
736 | + std::make_tuple("\"_\"", "_", true), |
737 | + std::make_tuple("\"+\"", "+", true), |
738 | + std::make_tuple("\"=\"", "=", true) |
739 | + )); |
740 | + |
741 | +INSTANTIATE_TEST_CASE_P(QuoteStrings, |
742 | + QuotedStringTests, |
743 | + ::testing::Values( |
744 | + std::make_tuple("\"\\\"\"", "\"", true), |
745 | + std::make_tuple("\"\\\'\"", "\'", true) |
746 | + )); |
747 | + |
748 | +INSTANTIATE_TEST_CASE_P(NumberStrings, |
749 | + QuotedStringTests, |
750 | + ::testing::Values( |
751 | + std::make_tuple("\"0\"", "0", true), |
752 | + std::make_tuple("\"1\"", "1", true), |
753 | + std::make_tuple("\"2\"", "2", true), |
754 | + std::make_tuple("\"3\"", "3", true), |
755 | + std::make_tuple("\"4\"", "4", true), |
756 | + std::make_tuple("\"5\"", "5", true), |
757 | + std::make_tuple("\"6\"", "6", true), |
758 | + std::make_tuple("\"7\"", "7", true), |
759 | + std::make_tuple("\"8\"", "8", true), |
760 | + std::make_tuple("\"9\"", "9", true) |
761 | + )); |
762 | + |
763 | + |
764 | +TEST(TestIntegerTypes, test_signed_integers) |
765 | +{ |
766 | + int result = 0; |
767 | + parser::xpath_grammar<std::string::iterator> g; |
768 | + |
769 | + ASSERT_TRUE(test_parser_attr("123", g.int_type, result)); |
770 | + ASSERT_EQ(123, result); |
771 | + |
772 | + ASSERT_TRUE(test_parser_attr("+456", g.int_type, result)); |
773 | + ASSERT_EQ(456, result); |
774 | + |
775 | + ASSERT_TRUE(test_parser_attr("-123", g.int_type, result)); |
776 | + ASSERT_EQ(-123, result); |
777 | +} |
778 | + |
779 | + |
780 | +// This test fails due to a bug in boost::spirit: https://svn.boost.org/trac/boost/ticket/9007 |
781 | +// TEST(TestIntegerTypes, test_integer_overflow) |
782 | +// { |
783 | +// int result; |
784 | + |
785 | +// // store range of int in a long, since we'll be extending them |
786 | +// long min_int = std::numeric_limits<int>::min(); |
787 | +// long max_int = std::numeric_limits<int>::max(); |
788 | + |
789 | +// qi::int_parser<int> r; |
790 | + |
791 | +// ASSERT_TRUE(test_parser_attr(std::to_string(min_int), r, result)); |
792 | +// ASSERT_EQ(min_int, result); |
793 | + |
794 | +// ASSERT_TRUE(test_parser_attr(std::to_string(max_int), r, result)); |
795 | +// ASSERT_EQ(max_int, result); |
796 | + |
797 | +// min_int -= 1; |
798 | +// max_int += 1; |
799 | + |
800 | +// // these last two assertions are failing. I expect the parsing to fail, but it's passing |
801 | +// // for some reason. |
802 | +// ASSERT_FALSE(test_parser_attr(std::to_string(min_int), r, result)) << min_int; |
803 | +// ASSERT_FALSE(test_parser_attr(std::to_string(max_int), r, result)) << max_int; |
804 | +// } |
805 | + |
806 | + |
807 | +//////////////////////////////////// |
808 | +// more complicated grammar tests |
809 | +//////////////////////////////////// |
810 | + |
811 | +/// Tests for parameter names: |
812 | +class TestXPathParserParamNames : public ::testing::TestWithParam<std::pair<std::string, bool> > |
813 | +{ |
814 | +}; |
815 | + |
816 | +TEST_P(TestXPathParserParamNames, test_param_name) |
817 | +{ |
818 | + auto p = GetParam(); |
819 | + |
820 | + std::string input = p.first; |
821 | + bool expect_pass = p.second; |
822 | + |
823 | + parser::xpath_grammar<std::string::iterator> g; |
824 | + |
825 | + std::string result; |
826 | + ASSERT_EQ( expect_pass, test_parser_attr(input, g.param_name, result) ); |
827 | + if (expect_pass) |
828 | + ASSERT_EQ(input, result); |
829 | +} |
830 | + |
831 | +INSTANTIATE_TEST_CASE_P(BasicNodeNames, |
832 | + TestXPathParserParamNames, |
833 | + ::testing::Values( |
834 | + std::pair<std::string, bool>("a b", false), |
835 | + std::pair<std::string, bool>("a*", false), |
836 | + std::pair<std::string, bool>("HelloWorld", true), |
837 | + std::pair<std::string, bool>("H", true), |
838 | + std::pair<std::string, bool>("h", true), |
839 | + std::pair<std::string, bool>("1", true), |
840 | + std::pair<std::string, bool>("node-name", true), |
841 | + std::pair<std::string, bool>("node_name", true), |
842 | + std::pair<std::string, bool>("node\\name", true), |
843 | + std::pair<std::string, bool>("node.name", false), |
844 | + std::pair<std::string, bool>("node name", false), |
845 | + std::pair<std::string, bool>("..", false) |
846 | + )); |
847 | + |
848 | +/// Tests for parameter values. This test is much larger than it should be, since it seems to be |
849 | +// impossible to parameterise tests for both type and value. The solution I use here is to have |
850 | +// the actual test in a base class template method, and have several derive classes use different |
851 | +// value parameters. Ugly, but probably the best we can do with google test. |
852 | + |
853 | +class TestParamValues : public ::testing::Test |
854 | +{ |
855 | +public: |
856 | + template <typename PairType> |
857 | + void test_param_value(PairType const& input_pair) const |
858 | + { |
859 | + RecordProperty("FirstType", typeid(typename PairType::first_type).name()); |
860 | + RecordProperty("SecondType", typeid(typename PairType::second_type).name()); |
861 | + std::string input = input_pair.first; |
862 | + typename PairType::second_type expected_result = input_pair.second; |
863 | + |
864 | + parser::xpath_grammar<std::string::iterator> g; |
865 | + |
866 | + xpathselect::XPathQueryParam::ParamValueType result; |
867 | + ASSERT_TRUE( test_parser_attr(input, g.param_value, result) ); |
868 | + boost::apply_visitor( |
869 | + variant_equality_assertion<typename PairType::second_type>(expected_result), |
870 | + result |
871 | + ); |
872 | + } |
873 | +}; |
874 | + |
875 | +// test string parameter values: |
876 | +class TestStringParamValues |
877 | + : public ::testing::WithParamInterface<std::pair<std::string, std::string> > |
878 | + , public TestParamValues |
879 | +{ |
880 | +}; |
881 | + |
882 | +TEST_P(TestStringParamValues, test_param_value_str) |
883 | +{ |
884 | + auto p = GetParam(); |
885 | + test_param_value(p); |
886 | +} |
887 | + |
888 | +INSTANTIATE_TEST_CASE_P(StringParams, |
889 | + TestStringParamValues, |
890 | + ::testing::Values( |
891 | + std::pair<std::string, std::string>("\"a b\"", "a b" ), |
892 | + std::pair<std::string, std::string>("\"a.b,c/\\d^\"", "a.b,c/\\d^" ) |
893 | + )); |
894 | + |
895 | +// test boolean parameter values: |
896 | +class TestBoolParamValues |
897 | + : public ::testing::WithParamInterface<std::pair<std::string, bool> > |
898 | + , public TestParamValues |
899 | +{ |
900 | +}; |
901 | + |
902 | +TEST_P(TestBoolParamValues, test_param_value_bool) |
903 | +{ |
904 | + auto p = GetParam(); |
905 | + test_param_value(p); |
906 | +} |
907 | + |
908 | +INSTANTIATE_TEST_CASE_P(StringParams, |
909 | + TestBoolParamValues, |
910 | + ::testing::Values( |
911 | + std::pair<std::string, bool>("True", true ), |
912 | + std::pair<std::string, bool>("False", false ) |
913 | + )); |
914 | + |
915 | +// test integer parameter values: |
916 | +class TestIntParamValues |
917 | + : public ::testing::WithParamInterface<std::pair<std::string, int> > |
918 | + , public TestParamValues |
919 | +{ |
920 | +}; |
921 | + |
922 | +TEST_P(TestIntParamValues, test_param_value_bool) |
923 | +{ |
924 | + auto p = GetParam(); |
925 | + test_param_value(p); |
926 | +} |
927 | + |
928 | +INSTANTIATE_TEST_CASE_P(IntegerParams, |
929 | + TestIntParamValues, |
930 | + ::testing::Values( |
931 | + std::pair<std::string, int>("123", 123 ), |
932 | + std::pair<std::string, int>("0", 0 ), |
933 | + std::pair<std::string, int>("-123", -123 ) |
934 | + )); |
935 | + |
936 | + |
937 | +/// Tests for the node names: |
938 | class TestXPathParserNodeNames : public ::testing::TestWithParam<std::pair<std::string, bool> > |
939 | { |
940 | }; |
941 | @@ -108,87 +461,6 @@ |
942 | std::pair<std::string, bool>("node\\name", true) |
943 | )); |
944 | |
945 | -class TestXPathParserParamNames : public ::testing::TestWithParam<std::pair<std::string, bool> > |
946 | -{ |
947 | -}; |
948 | - |
949 | -TEST_P(TestXPathParserParamNames, test_param_name) |
950 | -{ |
951 | - auto p = GetParam(); |
952 | - |
953 | - std::string input = p.first; |
954 | - bool expect_pass = p.second; |
955 | - |
956 | - parser::xpath_grammar<std::string::iterator> g; |
957 | - |
958 | - std::string result; |
959 | - ASSERT_EQ( expect_pass, test_parser_attr(input, g.param_name, result) ); |
960 | - if (expect_pass) |
961 | - ASSERT_EQ(input, result); |
962 | -} |
963 | - |
964 | -INSTANTIATE_TEST_CASE_P(BasicNodeNames, |
965 | - TestXPathParserParamNames, |
966 | - ::testing::Values( |
967 | - std::pair<std::string, bool>("a b", false), |
968 | - std::pair<std::string, bool>("a*", false), |
969 | - std::pair<std::string, bool>("HelloWorld", true), |
970 | - std::pair<std::string, bool>("H", true), |
971 | - std::pair<std::string, bool>("h", true), |
972 | - std::pair<std::string, bool>("1", true), |
973 | - std::pair<std::string, bool>("node-name", true), |
974 | - std::pair<std::string, bool>("node_name", true), |
975 | - std::pair<std::string, bool>("node\\name", true), |
976 | - std::pair<std::string, bool>("node.name", false), |
977 | - std::pair<std::string, bool>("node name", false) |
978 | - )); |
979 | - |
980 | -class TestXPathParserParamValues : public ::testing::TestWithParam<std::pair<std::string, bool> > |
981 | -{ |
982 | -}; |
983 | - |
984 | -TEST_P(TestXPathParserParamValues, test_param_value) |
985 | -{ |
986 | - auto p = GetParam(); |
987 | - |
988 | - std::string input = p.first; |
989 | - bool expect_pass = p.second; |
990 | - |
991 | - parser::xpath_grammar<std::string::iterator> g; |
992 | - |
993 | - std::string result; |
994 | - ASSERT_EQ( expect_pass, test_parser_attr(input, g.param_value, result) ); |
995 | - if (expect_pass) |
996 | - ASSERT_EQ(input, result); |
997 | -} |
998 | - |
999 | -INSTANTIATE_TEST_CASE_P(BasicNodeNames, |
1000 | - TestXPathParserParamValues, |
1001 | - ::testing::Values( |
1002 | - std::pair<std::string, bool>("a b", true), |
1003 | - std::pair<std::string, bool>("a*", false), |
1004 | - std::pair<std::string, bool>("HelloWorld", true), |
1005 | - std::pair<std::string, bool>("H", true), |
1006 | - std::pair<std::string, bool>("h", true), |
1007 | - std::pair<std::string, bool>("1", true), |
1008 | - std::pair<std::string, bool>("node-name", true), |
1009 | - std::pair<std::string, bool>("node_name", true), |
1010 | - std::pair<std::string, bool>("node\\name", true), |
1011 | - std::pair<std::string, bool>("node name", true), |
1012 | - std::pair<std::string, bool>("node.name", true) |
1013 | - )); |
1014 | - |
1015 | - |
1016 | -TEST(TestXPathParser, test_wildcard_node_name_accepts_wildcards) |
1017 | -{ |
1018 | - std::string input("*"); |
1019 | - |
1020 | - parser::xpath_grammar<std::string::iterator> g; |
1021 | - |
1022 | - std::string result; |
1023 | - ASSERT_EQ( true, test_parser_attr(input, g.wildcard_node_name, result) ); |
1024 | - ASSERT_EQ(input, result); |
1025 | -} |
1026 | |
1027 | class TestXPathParserWildcardNodeName : public ::testing::TestWithParam<std::pair<std::string, bool> > |
1028 | { |
1029 | @@ -224,16 +496,49 @@ |
1030 | )); |
1031 | |
1032 | |
1033 | -TEST(TestXPathParser, test_param_parser_works) |
1034 | -{ |
1035 | - std::string input("name=value"); |
1036 | - |
1037 | - parser::xpath_grammar<std::string::iterator> g; |
1038 | - |
1039 | - xpathselect::XPathQueryParam result; |
1040 | - ASSERT_EQ( true, test_parser_attr(input, g.param, result) ); |
1041 | - ASSERT_EQ("name", result.param_name); |
1042 | - ASSERT_EQ("value", result.param_value); |
1043 | +TEST(TestXPathParser, test_param_parser_string_value_works) |
1044 | +{ |
1045 | + std::string input("name=\"value\""); |
1046 | + |
1047 | + parser::xpath_grammar<std::string::iterator> g; |
1048 | + |
1049 | + xpathselect::XPathQueryParam result; |
1050 | + ASSERT_EQ( true, test_parser_attr(input, g.param, result) ); |
1051 | + ASSERT_EQ("name", result.param_name); |
1052 | + boost::apply_visitor( |
1053 | + variant_equality_assertion<std::string>("value"), |
1054 | + result.param_value |
1055 | + ); |
1056 | +} |
1057 | + |
1058 | +TEST(TestXPathParser, test_param_parser_bool_value_works) |
1059 | +{ |
1060 | + std::string input("name=True"); |
1061 | + |
1062 | + parser::xpath_grammar<std::string::iterator> g; |
1063 | + |
1064 | + xpathselect::XPathQueryParam result; |
1065 | + ASSERT_EQ( true, test_parser_attr(input, g.param, result) ); |
1066 | + ASSERT_EQ("name", result.param_name); |
1067 | + boost::apply_visitor( |
1068 | + variant_equality_assertion<bool>(true), |
1069 | + result.param_value |
1070 | + ); |
1071 | +} |
1072 | + |
1073 | +TEST(TestXPathParser, test_param_parser_string_int_works) |
1074 | +{ |
1075 | + std::string input("name=123456"); |
1076 | + |
1077 | + parser::xpath_grammar<std::string::iterator> g; |
1078 | + |
1079 | + xpathselect::XPathQueryParam result; |
1080 | + ASSERT_EQ( true, test_parser_attr(input, g.param, result) ); |
1081 | + ASSERT_EQ("name", result.param_name); |
1082 | + boost::apply_visitor( |
1083 | + variant_equality_assertion<int>(123456), |
1084 | + result.param_value |
1085 | + ); |
1086 | } |
1087 | |
1088 | TEST(TestXPathParser, test_param_parser_fails) |
1089 | @@ -256,12 +561,15 @@ |
1090 | ASSERT_EQ( true, test_parser_attr(input, g.param_list, result) ); |
1091 | ASSERT_EQ(1, result.size()); |
1092 | ASSERT_EQ("name", result.at(0).param_name); |
1093 | - ASSERT_EQ("123", result.at(0).param_value); |
1094 | + boost::apply_visitor( |
1095 | + variant_equality_assertion<int>(123), |
1096 | + result.at(0).param_value |
1097 | + ); |
1098 | } |
1099 | |
1100 | TEST(TestXPathParser, test_param_list_two_values) |
1101 | { |
1102 | - std::string input("[name=value,other=foo]"); |
1103 | + std::string input("[name=\"value\",visible=True]"); |
1104 | |
1105 | parser::xpath_grammar<std::string::iterator> g; |
1106 | |
1107 | @@ -269,9 +577,15 @@ |
1108 | ASSERT_EQ( true, test_parser_attr(input, g.param_list, result) ); |
1109 | ASSERT_EQ(2, result.size()); |
1110 | ASSERT_EQ("name", result.at(0).param_name); |
1111 | - ASSERT_EQ("value", result.at(0).param_value); |
1112 | - ASSERT_EQ("other", result.at(1).param_name); |
1113 | - ASSERT_EQ("foo", result.at(1).param_value); |
1114 | + boost::apply_visitor( |
1115 | + variant_equality_assertion<std::string>("value"), |
1116 | + result.at(0).param_value |
1117 | + ); |
1118 | + ASSERT_EQ("visible", result.at(1).param_name); |
1119 | + boost::apply_visitor( |
1120 | + variant_equality_assertion<bool>(true), |
1121 | + result.at(1).param_value |
1122 | + ); |
1123 | } |
1124 | |
1125 | TEST(TestXPathParser, test_spec_node_with_parameter) |
1126 | @@ -285,7 +599,10 @@ |
1127 | ASSERT_EQ("node_name", result.node_name_); |
1128 | ASSERT_FALSE(result.parameter.empty()); |
1129 | ASSERT_EQ("param_name", result.parameter.at(0).param_name); |
1130 | - ASSERT_EQ("123", result.parameter.at(0).param_value); |
1131 | + boost::apply_visitor( |
1132 | + variant_equality_assertion<int>(123), |
1133 | + result.parameter.at(0).param_value |
1134 | + ); |
1135 | } |
1136 | |
1137 | TEST(TestXPathParser, test_spec_node_without_parameter) |
1138 | @@ -333,13 +650,16 @@ |
1139 | ASSERT_EQ("*", result.node_name_); |
1140 | ASSERT_FALSE(result.parameter.empty()); |
1141 | ASSERT_EQ("param_name", result.parameter.at(0).param_name); |
1142 | - ASSERT_EQ("123", result.parameter.at(0).param_value); |
1143 | + boost::apply_visitor( |
1144 | + variant_equality_assertion<int>(123), |
1145 | + result.parameter.at(0).param_value |
1146 | + ); |
1147 | } |
1148 | |
1149 | |
1150 | TEST(TestXPathParser, test_node_can_be_a_wildcard_node_with_params) |
1151 | { |
1152 | - std::string input("*[name=value]"); |
1153 | + std::string input("*[name=\"value\"]"); |
1154 | |
1155 | parser::xpath_grammar<std::string::iterator> g; |
1156 | |
1157 | @@ -349,7 +669,10 @@ |
1158 | ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Normal, result.Type() ); |
1159 | ASSERT_EQ( 1, result.parameter.size() ); |
1160 | ASSERT_EQ( "name", result.parameter.at(0).param_name ); |
1161 | - ASSERT_EQ( "value", result.parameter.at(0).param_value ); |
1162 | + boost::apply_visitor( |
1163 | + variant_equality_assertion<std::string>("value"), |
1164 | + result.parameter.at(0).param_value |
1165 | + ); |
1166 | } |
1167 | |
1168 | TEST(TestXPathParser, test_node_can_be_a_wildcard_node_without_params) |
1169 | @@ -366,7 +689,7 @@ |
1170 | |
1171 | TEST(TestXPathParser, test_node_can_be_a_spec_node_with_params) |
1172 | { |
1173 | - std::string input("foo[name=value]"); |
1174 | + std::string input("foo[name=\"value\"]"); |
1175 | |
1176 | parser::xpath_grammar<std::string::iterator> g; |
1177 | |
1178 | @@ -376,7 +699,10 @@ |
1179 | ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Normal, result.Type() ); |
1180 | ASSERT_EQ( 1, result.parameter.size() ); |
1181 | ASSERT_EQ( "name", result.parameter.at(0).param_name ); |
1182 | - ASSERT_EQ( "value", result.parameter.at(0).param_value ); |
1183 | + boost::apply_visitor( |
1184 | + variant_equality_assertion<std::string>("value"), |
1185 | + result.parameter.at(0).param_value |
1186 | + ); |
1187 | } |
1188 | |
1189 | TEST(TestXPathParser, test_node_can_be_a_spec_node_without_params) |
1190 | @@ -391,6 +717,18 @@ |
1191 | ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Normal, result.Type() ); |
1192 | } |
1193 | |
1194 | +TEST(TestXPathParser, test_node_can_be_a_parent_node) |
1195 | +{ |
1196 | + std::string input(".."); |
1197 | + |
1198 | + parser::xpath_grammar<std::string::iterator> g; |
1199 | + |
1200 | + xpathselect::XPathQueryPart result; |
1201 | + ASSERT_EQ( true, test_parser_attr(input, g.node, result) ); |
1202 | + ASSERT_EQ( "..", result.node_name_ ); |
1203 | + ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Parent, result.Type() ); |
1204 | +} |
1205 | + |
1206 | TEST(TestXPathParser, test_search_node_followed_by_normal_node) |
1207 | { |
1208 | // the search_node grammar fails if it's at the end of the line, so we need |
1209 | @@ -409,7 +747,7 @@ |
1210 | { |
1211 | // the search_node grammar fails if it's at the end of the line, so we need |
1212 | // to give it some more data, even though we're not actually matching it. |
1213 | - std::string input("//*[foo=bar]"); |
1214 | + std::string input("//*[foo=\"bar\"]"); |
1215 | parser::xpath_grammar<std::string::iterator> g; |
1216 | xpathselect::XPathQueryPart result; |
1217 | |
1218 | @@ -429,6 +767,17 @@ |
1219 | ASSERT_FALSE( test_parser_attr(input, g.search_node, result) ); |
1220 | } |
1221 | |
1222 | +TEST(TestXPathParser, test_parent_node) |
1223 | +{ |
1224 | + std::string input(".."); |
1225 | + |
1226 | + parser::xpath_grammar<std::string::iterator> g; |
1227 | + |
1228 | + xpathselect::XPathQueryPart result; |
1229 | + ASSERT_TRUE( test_parser_attr(input, g.parent_node, result) ); |
1230 | + ASSERT_TRUE( result.Type() == xpathselect::XPathQueryPart::QueryPartType::Parent ); |
1231 | +} |
1232 | + |
1233 | TEST(TestXPathParser, test_normal_sep_works) |
1234 | { |
1235 | std::string input("/"); |
1236 | @@ -503,7 +852,7 @@ |
1237 | |
1238 | TEST(TestXPathParser, test_mix_search_and_long_normal) |
1239 | { |
1240 | - std::string input("/node1//node2[name=val]/node3"); |
1241 | + std::string input("/node1//node2[name=\"val\"]/node3"); |
1242 | |
1243 | parser::xpath_grammar<std::string::iterator> g; |
1244 | |
1245 | @@ -521,17 +870,55 @@ |
1246 | ASSERT_EQ("node2", result.at(2).node_name_); |
1247 | ASSERT_EQ(1, result.at(2).parameter.size()); |
1248 | ASSERT_EQ("name", result.at(2).parameter.at(0).param_name); |
1249 | - ASSERT_EQ("val", result.at(2).parameter.at(0).param_value); |
1250 | - |
1251 | + boost::apply_visitor( |
1252 | + variant_equality_assertion<std::string>("val"), |
1253 | + result.at(2).parameter.at(0).param_value |
1254 | + ); |
1255 | ASSERT_EQ("node3", result.at(3).node_name_); |
1256 | ASSERT_TRUE(result.at(3).parameter.empty()); |
1257 | } |
1258 | |
1259 | +TEST(TestXPathParser, test_mix_normal_and_parent) |
1260 | +{ |
1261 | + std::string input("/node1/.."); |
1262 | + |
1263 | + parser::xpath_grammar<std::string::iterator> g; |
1264 | + |
1265 | + xpathselect::QueryList result; |
1266 | + ASSERT_TRUE(test_parser_attr(input, g.node_sequence, result)); |
1267 | + |
1268 | + ASSERT_EQ(2, result.size()); |
1269 | + |
1270 | + ASSERT_EQ("node1", result.at(0).node_name_); |
1271 | + ASSERT_TRUE(result.at(0).parameter.empty()); |
1272 | + |
1273 | + ASSERT_TRUE(result.at(1).Type() == xpathselect::XPathQueryPart::QueryPartType::Parent ); |
1274 | + ASSERT_TRUE(result.at(1).parameter.empty()); |
1275 | +} |
1276 | + |
1277 | +TEST(TestXPathParser, test_mix_normal_and_parent_and_wildcard) |
1278 | +{ |
1279 | + std::string input("/node1/../*"); |
1280 | + |
1281 | + parser::xpath_grammar<std::string::iterator> g; |
1282 | + |
1283 | + xpathselect::QueryList result; |
1284 | + ASSERT_TRUE(test_parser_attr(input, g.node_sequence, result)); |
1285 | + |
1286 | + ASSERT_EQ(3, result.size()); |
1287 | + |
1288 | + ASSERT_EQ("node1", result.at(0).node_name_); |
1289 | + ASSERT_TRUE(result.at(0).parameter.empty()); |
1290 | + |
1291 | + ASSERT_TRUE(result.at(1).Type() == xpathselect::XPathQueryPart::QueryPartType::Parent ); |
1292 | + ASSERT_TRUE(result.at(1).parameter.empty()); |
1293 | + |
1294 | + ASSERT_TRUE(result.at(2).node_name_ == "*" ); |
1295 | + ASSERT_TRUE(result.at(2).parameter.empty()); |
1296 | +} |
1297 | |
1298 | class TestXPathParserQueryStrings : public ::testing::TestWithParam<std::pair<std::string, bool> > |
1299 | -{ |
1300 | -}; |
1301 | - |
1302 | +{}; |
1303 | |
1304 | TEST_P(TestXPathParserQueryStrings, test_query_acceptance) |
1305 | { |
1306 | @@ -556,11 +943,11 @@ |
1307 | std::pair<std::string, bool>("/root//node1", true), |
1308 | std::pair<std::string, bool>("//root", true), |
1309 | std::pair<std::string, bool>("/root//node1/node2", true), |
1310 | - std::pair<std::string, bool>("/root[p=1]//node1[p=2]/node3", true), |
1311 | - std::pair<std::string, bool>("/root[p=1,n=2,d=e3]", true), |
1312 | - std::pair<std::string, bool>("//root[p=1,n=2,d=e3]", true), |
1313 | + std::pair<std::string, bool>("/root[p=1]//node1[p=\"2\"]/node3", true), |
1314 | + std::pair<std::string, bool>("/root[p=True,n=2,d=\"e3\"]", true), |
1315 | + std::pair<std::string, bool>("//root[p=1,n=2,d=\"e3\"]", true), |
1316 | std::pair<std::string, bool>("/Root//*[p=1]", true), |
1317 | - std::pair<std::string, bool>("/Root//*[p=1,v=sj,c=true]", true), |
1318 | + std::pair<std::string, bool>("/Root//*[p=1,v=\"sj\",c=False]", true), |
1319 | // queries that must not parse correctly: |
1320 | std::pair<std::string, bool>("//", false), |
1321 | std::pair<std::string, bool>("/root//", false), |
1322 | @@ -570,5 +957,7 @@ |
1323 | std::pair<std::string, bool>(" ", false), |
1324 | std::pair<std::string, bool>("//*", false), |
1325 | std::pair<std::string, bool>("/Root///Leaf", false), |
1326 | - std::pair<std::string, bool>("/Root////", false) |
1327 | + std::pair<std::string, bool>("/Root////", false), |
1328 | + std::pair<std::string, bool>("/Root/..*", false), |
1329 | + std::pair<std::string, bool>("/Root/../Child//..", false) |
1330 | )); |
1331 | |
1332 | === modified file 'test/test_xpath_simple.cpp' |
1333 | --- test/test_xpath_simple.cpp 2012-08-10 04:06:05 +0000 |
1334 | +++ test/test_xpath_simple.cpp 2013-09-16 15:53:33 +0000 |
1335 | @@ -26,7 +26,7 @@ |
1336 | TEST(TestXPath, test_select_empty_tree) |
1337 | { |
1338 | xpathselect::Node::Ptr tree_root = std::make_shared<DummyNode>(); |
1339 | - xpathselect::NodeList result = xpathselect::SelectNodes(tree_root, ""); |
1340 | + xpathselect::NodeVector result = xpathselect::SelectNodes(tree_root, ""); |
1341 | |
1342 | ASSERT_EQ(1, result.size()); |
1343 | } |
1344 | @@ -35,7 +35,7 @@ |
1345 | TEST(TestXPath, test_select_tree_root) |
1346 | { |
1347 | xpathselect::Node::Ptr tree_root = std::make_shared<DummyNode>("RootNode"); |
1348 | - xpathselect::NodeList result = xpathselect::SelectNodes(tree_root, "/"); |
1349 | + xpathselect::NodeVector result = xpathselect::SelectNodes(tree_root, "/"); |
1350 | |
1351 | ASSERT_EQ(result.size(), 1); |
1352 | ASSERT_EQ(result.front(), tree_root); |
1353 | @@ -45,7 +45,7 @@ |
1354 | TEST(TestXPath, test_select_tree_root_with_name) |
1355 | { |
1356 | xpathselect::Node::Ptr tree_root = std::make_shared<DummyNode>("RootNode"); |
1357 | - xpathselect::NodeList result = xpathselect::SelectNodes(tree_root, "/RootNode"); |
1358 | + xpathselect::NodeVector result = xpathselect::SelectNodes(tree_root, "/RootNode"); |
1359 | |
1360 | ASSERT_EQ(result.size(), 1); |
1361 | ASSERT_EQ(result.front(), tree_root); |
1362 | @@ -55,7 +55,7 @@ |
1363 | TEST(TestXPath, test_select_tree_root_with_relative_query) |
1364 | { |
1365 | xpathselect::Node::Ptr tree_root = std::make_shared<DummyNode>("RootNode"); |
1366 | - xpathselect::NodeList result = xpathselect::SelectNodes(tree_root, "//RootNode"); |
1367 | + xpathselect::NodeVector result = xpathselect::SelectNodes(tree_root, "//RootNode"); |
1368 | |
1369 | ASSERT_EQ(result.size(), 1); |
1370 | ASSERT_EQ(result.front(), tree_root); |
1371 | @@ -65,7 +65,7 @@ |
1372 | TEST(TestXPath, test_select_tree_root_with_empty_relative_query) |
1373 | { |
1374 | xpathselect::Node::Ptr tree_root = std::make_shared<DummyNode>("RootNode"); |
1375 | - xpathselect::NodeList result = xpathselect::SelectNodes(tree_root, "//"); |
1376 | + xpathselect::NodeVector result = xpathselect::SelectNodes(tree_root, "//"); |
1377 | |
1378 | ASSERT_EQ(result.size(), 1); |
1379 | ASSERT_EQ(result.front(), tree_root); |
1380 | |
1381 | === modified file 'test/test_xpath_tree.cpp' |
1382 | --- test/test_xpath_tree.cpp 2013-04-24 03:48:30 +0000 |
1383 | +++ test/test_xpath_tree.cpp 2013-09-16 15:53:33 +0000 |
1384 | @@ -49,14 +49,14 @@ |
1385 | |
1386 | TEST_F(TestTreeFixture, test_simple) |
1387 | { |
1388 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/"); |
1389 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/"); |
1390 | |
1391 | ASSERT_EQ(1, result.size()); |
1392 | } |
1393 | |
1394 | TEST_F(TestTreeFixture, test_simple_absolute) |
1395 | { |
1396 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1"); |
1397 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1"); |
1398 | |
1399 | ASSERT_EQ(1, result.size()); |
1400 | auto expected = child_l1_; |
1401 | @@ -66,7 +66,7 @@ |
1402 | |
1403 | TEST_F(TestTreeFixture, test_simple_relative) |
1404 | { |
1405 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "//ChildRight1"); |
1406 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "//ChildRight1"); |
1407 | |
1408 | ASSERT_EQ(1, result.size()); |
1409 | ASSERT_EQ(child_r1_, result.front()); |
1410 | @@ -74,7 +74,7 @@ |
1411 | |
1412 | TEST_F(TestTreeFixture, test_complex_relative) |
1413 | { |
1414 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "//Root/ChildRight1"); |
1415 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "//Root/ChildRight1"); |
1416 | |
1417 | ASSERT_EQ(1, result.size()); |
1418 | ASSERT_EQ(child_r1_, result.front()); |
1419 | @@ -82,7 +82,7 @@ |
1420 | |
1421 | TEST_F(TestTreeFixture, test_relative_multiple_return) |
1422 | { |
1423 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "//Leaf"); |
1424 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "//Leaf"); |
1425 | |
1426 | ASSERT_EQ(2, result.size()); |
1427 | |
1428 | @@ -94,7 +94,7 @@ |
1429 | |
1430 | TEST_F(TestTreeFixture, test_relative_wildcard) |
1431 | { |
1432 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "//ChildLeft1/*"); |
1433 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "//ChildLeft1/*"); |
1434 | |
1435 | ASSERT_EQ(2, result.size()); |
1436 | |
1437 | @@ -106,7 +106,7 @@ |
1438 | |
1439 | TEST_F(TestTreeFixture, test_absolute_wildcard) |
1440 | { |
1441 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1/*"); |
1442 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1/*"); |
1443 | |
1444 | ASSERT_EQ(2, result.size()); |
1445 | |
1446 | @@ -119,7 +119,7 @@ |
1447 | TEST_F(TestTreeFixture, test_simple_absolute_property_match) |
1448 | { |
1449 | child_l1_->AddProperty("visible", "True"); |
1450 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1[visible=True]"); |
1451 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1[visible=True]"); |
1452 | |
1453 | ASSERT_EQ(1, result.size()); |
1454 | ASSERT_EQ(child_l1_, result.front()); |
1455 | @@ -127,8 +127,8 @@ |
1456 | |
1457 | TEST_F(TestTreeFixture, test_simple_relative_property_match) |
1458 | { |
1459 | - child_l1_->AddProperty("visible", "True"); |
1460 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "//ChildLeft1[visible=True]"); |
1461 | + child_l1_->AddProperty("visible", true); |
1462 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "//ChildLeft1[visible=True]"); |
1463 | |
1464 | ASSERT_EQ(1, result.size()); |
1465 | ASSERT_EQ(child_l1_, result.front()); |
1466 | @@ -136,9 +136,9 @@ |
1467 | |
1468 | TEST_F(TestTreeFixture, test_absolute_multiple_property_match) |
1469 | { |
1470 | - root_->AddProperty("number", "45"); |
1471 | - child_l1_->AddProperty("visible", "True"); |
1472 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root[number=45]/ChildLeft1[visible=True]"); |
1473 | + root_->AddProperty("number", 45); |
1474 | + child_l1_->AddProperty("visible", true); |
1475 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root[number=45]/ChildLeft1[visible=True]"); |
1476 | |
1477 | ASSERT_EQ(1, result.size()); |
1478 | ASSERT_EQ(child_l1_, result.front()); |
1479 | @@ -146,7 +146,7 @@ |
1480 | |
1481 | TEST_F(TestTreeFixture, test_mixed_query_simple) |
1482 | { |
1483 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root//Leaf"); |
1484 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root//Leaf"); |
1485 | ASSERT_EQ(2, result.size()); |
1486 | for(auto n : result) |
1487 | { |
1488 | @@ -156,8 +156,8 @@ |
1489 | |
1490 | TEST_F(TestTreeFixture, test_mixed_query_property_match) |
1491 | { |
1492 | - leaf_1_->AddProperty("visible", "True"); |
1493 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root//Leaf[visible=True]"); |
1494 | + leaf_1_->AddProperty("visible", true); |
1495 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root//Leaf[visible=True]"); |
1496 | |
1497 | ASSERT_EQ(1, result.size()); |
1498 | ASSERT_EQ(leaf_1_, result.front()); |
1499 | @@ -165,8 +165,8 @@ |
1500 | |
1501 | TEST_F(TestTreeFixture, test_search_node_with_wildcard_and_property) |
1502 | { |
1503 | - child_l1_->AddProperty("visible", "True"); |
1504 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root//*[visible=True]"); |
1505 | + child_l1_->AddProperty("visible", true); |
1506 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root//*[visible=True]"); |
1507 | |
1508 | ASSERT_EQ(1, result.size()); |
1509 | ASSERT_EQ(child_l1_, result.front()); |
1510 | @@ -174,7 +174,7 @@ |
1511 | |
1512 | TEST_F(TestTreeFixture, test_wildcard) |
1513 | { |
1514 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root/*"); |
1515 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/*"); |
1516 | ASSERT_EQ(2, result.size()); |
1517 | for(auto n : result) |
1518 | { |
1519 | @@ -182,14 +182,54 @@ |
1520 | } |
1521 | } |
1522 | |
1523 | +TEST_F(TestTreeFixture, test_parent) |
1524 | +{ |
1525 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1/.."); |
1526 | + |
1527 | + ASSERT_EQ(1, result.size()); |
1528 | + ASSERT_EQ(root_, result.front()); |
1529 | +} |
1530 | + |
1531 | +TEST_F(TestTreeFixture, test_parent_on_root) |
1532 | +{ |
1533 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/.."); |
1534 | + |
1535 | + ASSERT_EQ(1, result.size()); |
1536 | + ASSERT_EQ(root_, result.front()); |
1537 | +} |
1538 | + |
1539 | +TEST_F(TestTreeFixture, test_parent_on_leaf) |
1540 | +{ |
1541 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1/Leaf/.."); |
1542 | + |
1543 | + ASSERT_EQ(1, result.size()); |
1544 | + ASSERT_EQ(child_l1_, result.front()); |
1545 | +} |
1546 | + |
1547 | +TEST_F(TestTreeFixture, test_double_parent_on_leaf) |
1548 | +{ |
1549 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1/Leaf/../.."); |
1550 | + |
1551 | + ASSERT_EQ(1, result.size()); |
1552 | + ASSERT_EQ(root_, result.front()); |
1553 | +} |
1554 | + |
1555 | +TEST_F(TestTreeFixture, test_parent_and_child) |
1556 | +{ |
1557 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1/../ChildLeft1/../ChildLeft1"); |
1558 | + |
1559 | + ASSERT_EQ(1, result.size()); |
1560 | + ASSERT_EQ(child_l1_, result.front()); |
1561 | +} |
1562 | + |
1563 | TEST_F(TestTreeFixture, test_invalid_query_search) |
1564 | { |
1565 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root///Leaf"); |
1566 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root///Leaf"); |
1567 | ASSERT_EQ(0, result.size()); |
1568 | } |
1569 | |
1570 | TEST_F(TestTreeFixture, test_invalid_query_multiple_searches) |
1571 | { |
1572 | - xpathselect::NodeList result = xpathselect::SelectNodes(root_, "/Root////"); |
1573 | + xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root////"); |
1574 | ASSERT_EQ(0, result.size()); |
1575 | } |
PASSED: Continuous integration, rev:51 jenkins. qa.ubuntu. com/job/ xpathselect- ci/5/ jenkins. qa.ubuntu. com/job/ xpathselect- saucy-amd64- ci/1 jenkins. qa.ubuntu. com/job/ xpathselect- saucy-armhf- ci/1
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ xpathselect- ci/5/rebuild
http://