Merge lp:~autopilot/xpathselect/experimental into lp:xpathselect

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
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 }

Subscribers

People subscribed via source and target branches

to all changes: