Merge lp:~thomir-deactivatedaccount/xpathselect/trunk-extend-type-name-options into lp:xpathselect

Proposed by Thomi Richards
Status: Needs review
Proposed branch: lp:~thomir-deactivatedaccount/xpathselect/trunk-extend-type-name-options
Merge into: lp:xpathselect
Diff against target: 1347 lines (+609/-308)
8 files modified
lib/CMakeLists.txt (+2/-2)
lib/parser.cpp (+34/-1)
lib/parser.h (+55/-50)
lib/xpathquerypart.cpp (+129/-0)
lib/xpathquerypart.h (+57/-73)
lib/xpathselect.cpp (+69/-31)
test/test_parser.cpp (+241/-150)
test/test_xpath_tree.cpp (+22/-1)
To merge this branch: bzr merge lp:~thomir-deactivatedaccount/xpathselect/trunk-extend-type-name-options
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Autopilot Hackers Pending
Review via email: mp+227854@code.launchpad.net

Commit message

Extend xpathselect grammar to allow multiple node names per query.

Description of the change

This branch changes the XPathSelect grammar such that you can now say "get me all the nodes named X, Y, or Z at this location". This is required to work around a problem with versioned Qml objects, and is also useful in other scenarios.

This involved extending the boost::spirit grammar (in parser.h), which in turn meant re-working the parser return types to be different structs, and using boost::variant.

As you'd expect, tests have been added / updated.

To post a comment you must log in.
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Note: Please do not merge this. While this MP is considered complete, I need to write the autopilot-side of things, and I'd like to land the two at the same time to avoid back-and-forth merge proposals between the two projects.

Reviews are very welcome though :)

Revision history for this message
Brendan Donegan (brendan-donegan) wrote :

That's a whole lotta code to parse! A couple of probably useless comments for you though.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) :
Revision history for this message
Christopher Lee (veebers) wrote :

Just a quick look at this point, couple of inline comments re; TODOs. Deeper review to come.

65. By Thomi Richards

Code cleanups.

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 see anything obviously wrong with this, but then again my C++ skills are largely nonexisting, and most of the syntax is totally unfamiliar to me :/ Just two small and unimportant comments. But the test case coverage of this is quite good, and I suppose you'll test it together with the AP testsuite.

66. By Thomi Richards

Fix test as per MP comment.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Unmerged revisions

66. By Thomi Richards

Fix test as per MP comment.

65. By Thomi Richards

Code cleanups.

64. By Thomi Richards

Remove use of boost::optional that wasn't needed.

63. By Thomi Richards

whitespace-only change.

62. By Thomi Richards

Addded test, cleaned up the code a little bit.

61. By Thomi Richards

Remove some commented out code.

60. By Thomi Richards

Add missing file - whoops!

59. By Thomi Richards

Use a template function to remove some code.

58. By Thomi Richards

Hook up new xpathselect grammar, added test to those already present.

57. By Thomi Richards

Builds, and tests all pass again.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/CMakeLists.txt'
2--- lib/CMakeLists.txt 2013-01-22 02:39:32 +0000
3+++ lib/CMakeLists.txt 2014-07-28 19:46:11 +0000
4@@ -1,11 +1,11 @@
5 FIND_PACKAGE( Boost 1.40 REQUIRED )
6 INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR} )
7
8-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wl,--no-undefined")
9+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wl,--no-undefined -ftemplate-backtrace-limit=0")
10 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
11 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG" )
12 endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
13-set(SOURCES node.cpp xpathselect.h xpathselect.cpp parser.cpp parser.h)
14+set(SOURCES node.cpp xpathselect.h xpathselect.cpp xpathquerypart.cpp parser.cpp parser.h)
15 set(HEADERS node.h xpathselect.h)
16
17 if(CMAKE_COMPILER_IS_GNUCXX)
18
19=== modified file 'lib/parser.cpp'
20--- lib/parser.cpp 2013-01-21 02:27:45 +0000
21+++ lib/parser.cpp 2014-07-28 19:46:11 +0000
22@@ -1,5 +1,5 @@
23 /*
24-* Copyright (C) 2013 Canonical Ltd
25+* Copyright (C) 2013-2014 Canonical Ltd
26 *
27 * This program is free software: you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License version 3 as
29@@ -16,3 +16,36 @@
30 */
31
32 #include "parser.h"
33+
34+xpathselect::QueryList xpathselect::parser::parse_query(std::string const& query)
35+{
36+ bool s_;
37+ return parse_query(query, s_);
38+}
39+
40+xpathselect::QueryList xpathselect::parser::parse_query(std::string const& query, bool& success)
41+{
42+ xpathselect::parser::xpath_grammar<std::string::const_iterator> grammar;
43+ QueryList query_parts;
44+ NodeSequence results;
45+
46+ auto begin = query.cbegin();
47+ auto end = query.cend();
48+ if (boost::spirit::qi::parse(begin, end, grammar, results) && (begin == end))
49+ {
50+ success = true;
51+ for (auto n: results)
52+ {
53+ if (auto p1 = boost::fusion::at_c<0>(n))
54+ {
55+ query_parts.push_back(xpathselect::AnyNode(*p1));
56+ }
57+ query_parts.push_back(boost::fusion::at_c<1>(n));
58+ }
59+ }
60+ else
61+ {
62+ success = false;
63+ }
64+ return query_parts;
65+}
66
67=== modified file 'lib/parser.h'
68--- lib/parser.h 2014-02-19 19:18:58 +0000
69+++ lib/parser.h 2014-07-28 19:46:11 +0000
70@@ -1,5 +1,5 @@
71 /*
72-* Copyright (C) 2013 Canonical Ltd
73+* Copyright (C) 2013-2014 Canonical Ltd
74 *
75 * This program is free software: you can redistribute it and/or modify
76 * it under the terms of the GNU General Public License version 3 as
77@@ -22,26 +22,27 @@
78
79 #include <boost/config/warning_disable.hpp>
80 #include <boost/fusion/include/adapt_struct.hpp>
81-#include <boost/spirit/include/phoenix_object.hpp>
82-#include <boost/spirit/include/phoenix_operator.hpp>
83-#include <boost/spirit/include/qi.hpp>
84-#include <boost/spirit/include/qi_bool.hpp>
85-#include <boost/spirit/include/qi_int.hpp>
86+#include <boost/spirit/include/phoenix.hpp>
87+#include <boost/spirit/include/qi.hpp>
88
89 #include "xpathquerypart.h"
90
91-// this allows spirit to lazily construct these two structs...
92-BOOST_FUSION_ADAPT_STRUCT(
93- xpathselect::XPathQueryPart,
94- (std::string, node_name_)
95- (xpathselect::ParamList, parameter)
96- );
97+BOOST_FUSION_ADAPT_STRUCT(
98+ xpathselect::XPathWildcardNode,
99+ (xpathselect::ParamList, parameters)
100+);
101+
102+BOOST_FUSION_ADAPT_STRUCT(
103+ xpathselect::XPathSpecifiedNode,
104+ (xpathselect::NodeNameList, names)
105+ (xpathselect::ParamList, parameters)
106+);
107
108 BOOST_FUSION_ADAPT_STRUCT(
109 xpathselect::XPathQueryParam,
110 (std::string, param_name)
111 (xpathselect::XPathQueryParam::ParamValueType, param_value)
112- );
113+);
114
115 namespace xpathselect
116 {
117@@ -85,7 +86,7 @@
118 // on - it must adhere to std::forward_iterator. The second template parameter is the type
119 // that this grammar will produce (in this case: a list of XPathQueryPart objects).
120 template <typename Iterator>
121- struct xpath_grammar : qi::grammar<Iterator, QueryList()>
122+ struct xpath_grammar : qi::grammar<Iterator, NodeSequence()>
123 {
124 xpath_grammar() : xpath_grammar::base_type(node_sequence) // node_sequence is the start rule.
125 {
126@@ -135,80 +136,75 @@
127 // it must start and end with a non-space character, but you can have
128 // spaces in the middle.
129 spec_node_name = +qi::char_("a-zA-Z0-9_\\-") >> *(+qi::char_(" :") >> +qi::char_("a-zA-Z0-9_\\-"));
130+ spec_node_name_list = spec_node_name % ',';
131 // a wildcard node name is simply a '*'
132- wildcard_node_name = qi::char_("*");
133+ wildcard_node_name = "*";
134
135
136 // a spec_node consists of a specified node name, followed by an *optional* parameter list.
137- spec_node %= spec_node_name >> -(param_list);
138+ spec_node %= spec_node_name_list >> -(param_list);
139 // a wildcard node is a '*' without parameters:
140 wildcard_node %= wildcard_node_name >> !param_list;
141 // wildcard nodes can also have parameters:
142 wildcard_node_with_params %= wildcard_node_name >> param_list;
143 // A parent node is '..' as long as it's followed by a normal separator or end of input:
144- parent_node = qi::lit("..")[qi::_val = XPathQueryPart("..")];
145+ parent_node = qi::lit("..") >> qi::attr(XPathParentNode());
146
147 // node is simply any kind of code defined thus far:
148- node = spec_node | wildcard_node_with_params | wildcard_node | parent_node;
149+ any_node = spec_node | wildcard_node_with_params | wildcard_node | parent_node;
150
151 // a search node is '//' as long as it's followed by a spec node or a wildcard node with parameters.
152 // we don't allow '//*' since it would match everything in the tree, and cause HUGE amounts of
153 // data to be transmitted.
154- search_node = "//" >> &(spec_node | wildcard_node_with_params)[qi::_val = XPathQueryPart()];
155-
156+ search_node = qi::lit("//") >> &(spec_node | wildcard_node_with_params) >> qi::attr(XPathSearchNode());
157
158 // a normal separator is a '/' as long as it's followed by something other than another '/'
159 normal_sep = '/' >> !qi::lit('/');
160- separator = normal_sep | search_node; // nodes can be separated by normal_sep or search_node.
161+ separator = search_node | normal_sep; // nodes can be separated by normal_sep or search_node.
162 // this is the money shot: a node sequence is one or more of a separator, followed by an
163 // optional node.
164- node_sequence %= +(separator >> -node);
165+ sep_node_pair = separator >> any_node;
166+ node_sequence = +sep_node_pair;
167
168 // DEBUGGING SUPPORT:
169 // define DEBUG in order to have boost::spirit spit out useful debug information:
170 #ifdef DEBUG
171 // this gives english names to all the grammar rules:
172 spec_node_name.name("spec_node_name");
173+ spec_node_name_list.name("spec_node_name_list");
174 wildcard_node_name.name("wildcard_node_name");
175 search_node.name("search_node");
176- normal_sep.name("normal_separator");
177+ parent_node.name("parent_node");
178+ normal_sep.name("normal_sep");
179 separator.name("separator");
180 param_name.name("param_name");
181 param_value.name("param_value");
182 param.name("param");
183+ param_list.name("param_list");
184 spec_node.name("spec_node");
185 wildcard_node.name("wildcard_node");
186- wildcard_node.name("wildcard_node_with_params");
187- node.name("node");
188+ wildcard_node_with_params.name("wildcard_node_with_params");
189+ any_node.name("any_node");
190+ sep_node_pair.name("sep_node_pair");
191 node_sequence.name("node_sequence");
192- param_list.name("param_list");
193
194- // set up error logging:
195- qi::on_error<qi::fail>(
196- node_sequence,
197- std::cout
198- << phoenix::val("Error! Expecting ")
199- << qi::_4 // what failed?
200- << phoenix::val(" here: \"")
201- << phoenix::construct<std::string>(qi::_3, qi::_2) // iterators to error-pos, end
202- << phoenix::val("\"")
203- << std::endl
204- );
205- // specify which rules we want debug info about (all of them):
206 qi::debug(spec_node_name);
207+ qi::debug(spec_node_name_list);
208 qi::debug(wildcard_node_name);
209 qi::debug(search_node);
210+ qi::debug(parent_node);
211 qi::debug(normal_sep);
212 qi::debug(separator);
213 qi::debug(param_name);
214 qi::debug(param_value);
215 qi::debug(param);
216+ qi::debug(param_list);
217 qi::debug(spec_node);
218 qi::debug(wildcard_node);
219 qi::debug(wildcard_node_with_params);
220- qi::debug(node);
221+ qi::debug(any_node);
222+ qi::debug(sep_node_pair);
223 qi::debug(node_sequence);
224- qi::debug(param_list);
225 #endif
226 }
227 // declare all the rules. The second template parameter is the type they produce.
228@@ -222,30 +218,39 @@
229 // symbol table for chracter scape codes.
230 qi::symbols<char const, char const> unesc_char;
231
232- // parse integers, first signed then unsigned:
233+ // parse integers:
234 qi::rule<Iterator, int32_t()> int_type;
235
236 // more complicated language rules:
237 qi::rule<Iterator, std::string()> spec_node_name;
238- qi::rule<Iterator, std::string()> wildcard_node_name;
239- qi::rule<Iterator, XPathQueryPart()> search_node;
240- qi::rule<Iterator, XPathQueryPart()> parent_node;
241+ qi::rule<Iterator, xpathselect::NodeNameList()> spec_node_name_list;
242+ qi::rule<Iterator> wildcard_node_name;
243+ qi::rule<Iterator, XPathSearchNode()> search_node;
244+ qi::rule<Iterator, XPathParentNode()> parent_node;
245 qi::rule<Iterator> normal_sep;
246- qi::rule<Iterator, xpathselect::QueryList()> separator;
247+ qi::rule<Iterator, xpathselect::MaybeSearchNode()> separator;
248
249 qi::rule<Iterator, std::string()> param_name;
250 qi::rule<Iterator, xpathselect::XPathQueryParam::ParamValueType()> param_value;
251 qi::rule<Iterator, XPathQueryParam()> param;
252 qi::rule<Iterator, xpathselect::ParamList()> param_list;
253
254- qi::rule<Iterator, XPathQueryPart()> spec_node;
255- qi::rule<Iterator, XPathQueryPart()> wildcard_node;
256- qi::rule<Iterator, XPathQueryPart()> wildcard_node_with_params;
257- qi::rule<Iterator, XPathQueryPart()> node;
258+ qi::rule<Iterator, XPathSpecifiedNode()> spec_node;
259+ qi::rule<Iterator, XPathWildcardNode()> wildcard_node;
260+ qi::rule<Iterator, XPathWildcardNode()> wildcard_node_with_params;
261+ qi::rule<Iterator, AnyNode()> any_node;
262+ qi::rule<Iterator, SepNodePair()> sep_node_pair;
263
264- qi::rule<Iterator, xpathselect::QueryList()> node_sequence;
265+ qi::rule<Iterator, NodeSequence()> node_sequence;
266 };
267
268+ /// Take some input, parse it, and return a flattened data structure,
269+ /// which is what most people actually want (means they don't need to
270+ /// deal with boost::optional components)
271+ QueryList parse_query(std::string const& query);
272+ /// Similar to the above, but allows the caller to determine if the grammar
273+ /// parsed the whole input or not.
274+ QueryList parse_query(std::string const& query, bool& success);
275 }
276 }
277
278
279=== added file 'lib/xpathquerypart.cpp'
280--- lib/xpathquerypart.cpp 1970-01-01 00:00:00 +0000
281+++ lib/xpathquerypart.cpp 2014-07-28 19:46:11 +0000
282@@ -0,0 +1,129 @@
283+/*
284+* Copyright (C) 2013 Canonical Ltd
285+*
286+* This program is free software: you can redistribute it and/or modify
287+* it under the terms of the GNU General Public License version 3 as
288+* published by the Free Software Foundation.
289+*
290+* This program is distributed in the hope that it will be useful,
291+* but WITHOUT ANY WARRANTY; without even the implied warranty of
292+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
293+* GNU General Public License for more details.
294+*
295+* You should have received a copy of the GNU General Public License
296+* along with this program. If not, see <http://www.gnu.org/licenses/>.
297+*
298+*/
299+
300+#include <string>
301+#include <vector>
302+#include <memory>
303+#include <algorithm>
304+#include <iostream>
305+
306+#include <boost/optional/optional.hpp>
307+#include <boost/variant/variant.hpp>
308+#include <boost/variant/get.hpp>
309+
310+#include "xpathquerypart.h"
311+#include "node.h"
312+
313+namespace xpathselect
314+{
315+ namespace
316+ {
317+ bool nodeMatchesParameters(Node::Ptr const& node, ParamList const& parameters)
318+ {
319+ bool matches = true;
320+ if (!parameters.empty())
321+ {
322+ for (auto param : parameters)
323+ {
324+ switch(param.param_value.which())
325+ {
326+ case 0:
327+ {
328+ matches &= node->MatchStringProperty(param.param_name, boost::get<std::string>(param.param_value));
329+ }
330+ break;
331+ case 1:
332+ {
333+ matches &= node->MatchBooleanProperty(param.param_name, boost::get<bool>(param.param_value));
334+ }
335+ break;
336+ case 2:
337+ {
338+ matches &= node->MatchIntegerProperty(param.param_name, boost::get<int>(param.param_value));
339+ }
340+ break;
341+ }
342+ }
343+ }
344+ return matches;
345+ }
346+ }
347+
348+#ifdef DEBUG
349+ std::string XPathSearchNode::Dump() const
350+ {
351+ return "SearchNode";
352+ }
353+
354+ std::string XPathParentNode::Dump() const
355+ {
356+ return "ParentNode";
357+ }
358+
359+ std::string XPathWildcardNode::Dump() const
360+ {
361+ return "WildcardNode";
362+ }
363+
364+ std::string XPathSpecifiedNode::Dump() const
365+ {
366+ std::string s("SpecifiedNode(");
367+ for (auto n: names)
368+ s += n + " ";
369+ s += ")";
370+ return s;
371+ }
372+#endif
373+
374+ bool XPathWildcardNode::Matches(Node::Ptr const& node) const
375+ {
376+ return nodeMatchesParameters(node, parameters);
377+ }
378+
379+ bool XPathSpecifiedNode::Matches(Node::Ptr const& node) const
380+ {
381+ bool matches = std::find(names.cbegin(), names.cend(), node->GetName()) != names.cend();
382+ return matches && nodeMatchesParameters(node, parameters);
383+ }
384+
385+#ifdef DEBUG
386+ std::ostream &operator<<(std::ostream &stream, XPathWildcardNode const& ob)
387+ {
388+ stream << ob.Dump() << ' ';
389+ return stream;
390+ }
391+
392+ std::ostream &operator<<(std::ostream &stream, XPathSpecifiedNode const& ob)
393+ {
394+ stream << ob.Dump() << ' ';
395+ return stream;
396+ }
397+
398+ std::ostream &operator<<(std::ostream &stream, XPathSearchNode const& ob)
399+ {
400+ stream << ob.Dump() << ' ';
401+ return stream;
402+ }
403+
404+ std::ostream &operator<<(std::ostream &stream, XPathParentNode const& ob)
405+ {
406+ stream << ob.Dump() << ' ';
407+ return stream;
408+ }
409+#endif
410+
411+}
412
413=== modified file 'lib/xpathquerypart.h'
414--- lib/xpathquerypart.h 2013-09-03 03:28:14 +0000
415+++ lib/xpathquerypart.h 2014-07-28 19:46:11 +0000
416@@ -1,5 +1,5 @@
417 /*
418-* Copyright (C) 2013 Canonical Ltd
419+* Copyright (C) 2013-2014 Canonical Ltd
420 *
421 * This program is free software: you can redistribute it and/or modify
422 * it under the terms of the GNU General Public License version 3 as
423@@ -20,12 +20,10 @@
424
425 #include <string>
426 #include <vector>
427-#include <memory>
428-#include <iostream>
429
430 #include <boost/optional/optional.hpp>
431 #include <boost/variant/variant.hpp>
432-#include <boost/variant/get.hpp>
433+#include <boost/fusion/include/vector.hpp>
434
435 #include "node.h"
436
437@@ -40,76 +38,62 @@
438 std::string param_name;
439 ParamValueType param_value;
440 };
441-
442+
443 typedef std::vector<XPathQueryParam> ParamList;
444-
445- // Stores a part of an XPath query.
446- struct XPathQueryPart
447- {
448- public:
449- XPathQueryPart() {}
450- XPathQueryPart(std::string node_name)
451- : node_name_(node_name)
452- {}
453-
454- enum class QueryPartType {Normal, Search, Parent};
455-
456- bool Matches(Node::Ptr const& node) const
457- {
458- bool matches = (node_name_ == "*" || node->GetName() == node_name_);
459- if (!parameter.empty())
460- {
461- for (auto param : parameter)
462- {
463- switch(param.param_value.which())
464- {
465- case 0:
466- {
467- matches &= node->MatchStringProperty(param.param_name, boost::get<std::string>(param.param_value));
468- }
469- break;
470- case 1:
471- {
472- matches &= node->MatchBooleanProperty(param.param_name, boost::get<bool>(param.param_value));
473- }
474- break;
475- case 2:
476- {
477- matches &= node->MatchIntegerProperty(param.param_name, boost::get<int>(param.param_value));
478- }
479- break;
480- }
481- }
482- }
483-
484- return matches;
485- }
486-
487- QueryPartType Type() const
488- {
489- if (node_name_ == "")
490- return QueryPartType::Search;
491- else if (node_name_ == "..")
492- return QueryPartType::Parent;
493- else
494- return QueryPartType::Normal;
495- }
496-
497- void Dump() const
498- {
499- if (Type() == QueryPartType::Search)
500- std::cout << "<search> ";
501- else
502- std::cout << "[" << node_name_ << "] ";
503- }
504-
505- std::string node_name_;
506- ParamList parameter;
507- };
508-
509-
510-
511- typedef std::vector<XPathQueryPart> QueryList;
512+ typedef std::vector<std::string> NodeNameList;
513+
514+ struct XPathSearchNode
515+ {
516+#ifdef DEBUG
517+ std::string Dump() const;
518+#endif
519+ };
520+
521+ struct XPathParentNode
522+ {
523+#ifdef DEBUG
524+ std::string Dump() const;
525+#endif
526+ };
527+
528+ struct XPathWildcardNode
529+ {
530+ bool Matches(Node::Ptr const& node) const;
531+#ifdef DEBUG
532+ std::string Dump() const;
533+#endif
534+ ParamList parameters;
535+ };
536+
537+ struct XPathSpecifiedNode
538+ {
539+ bool Matches(Node::Ptr const& node) const;
540+#ifdef DEBUG
541+ std::string Dump() const;
542+#endif
543+ NodeNameList names;
544+ ParamList parameters;
545+ };
546+
547+#ifdef DEBUG
548+ std::ostream &operator<<(std::ostream &stream, XPathWildcardNode const& ob);
549+ std::ostream &operator<<(std::ostream &stream, XPathSpecifiedNode const& ob);
550+ std::ostream &operator<<(std::ostream &stream, XPathSearchNode const& ob);
551+ std::ostream &operator<<(std::ostream &stream, XPathParentNode const& ob);
552+#endif
553+
554+ typedef boost::variant<
555+ XPathSearchNode,
556+ XPathParentNode,
557+ XPathWildcardNode,
558+ XPathSpecifiedNode
559+ > AnyNode;
560+ typedef boost::optional<XPathSearchNode> MaybeSearchNode;
561+
562+ typedef boost::fusion::vector<MaybeSearchNode, AnyNode> SepNodePair;
563+ typedef std::vector<SepNodePair> NodeSequence;
564+
565+ typedef std::vector<AnyNode> QueryList;
566
567 }
568
569
570=== modified file 'lib/xpathselect.cpp'
571--- lib/xpathselect.cpp 2013-09-03 03:28:14 +0000
572+++ lib/xpathselect.cpp 2014-07-28 19:46:11 +0000
573@@ -25,35 +25,76 @@
574
575 namespace xpathselect
576 {
577+#ifdef DEBUG
578+ namespace debug
579+ {
580+ class dump_visitor: public boost::static_visitor<std::string>
581+ {
582+ public:
583+
584+ template <typename T>
585+ std::string operator()(const T& t) const
586+ {
587+ return t.Dump();
588+ }
589+ };
590+
591+ void dump_query_list(QueryList const& ql)
592+ {
593+ std::cout << "Query parts are: ";
594+ for (auto n : ql)
595+ std::cout << boost::apply_visitor(dump_visitor(), n) << " ";
596+ std::cout << std::endl;
597+ }
598+ }
599+#endif
600 // anonymous namespace for internal-only utility class:
601 namespace
602 {
603 QueryList GetQueryPartsFromQuery(std::string const& query)
604 {
605- xpathselect::parser::xpath_grammar<std::string::const_iterator> grammar;
606- QueryList query_parts;
607-
608- auto begin = query.cbegin();
609- auto end = query.cend();
610- if (boost::spirit::qi::parse(begin, end, grammar, query_parts) && (begin == end))
611- {
612-#ifdef DEBUG
613- std::cout << "Query parts are: ";
614- for (auto n : query_parts)
615- n.Dump();
616- std::cout << std::endl;
617-#endif
618- return query_parts;
619- }
620-#ifdef DEBUG
621- std::cout << "Query failed." << std::endl;
622-#endif
623- return QueryList();
624+ QueryList query_parts = xpathselect::parser::parse_query(query);
625+#ifdef DEBUG
626+ ::xpathselect::debug::dump_query_list(*query_parts);
627+#endif
628+ return query_parts;
629+ }
630+
631+ class matches_visitor: public boost::static_visitor<bool>
632+ {
633+ public:
634+ matches_visitor(Node::Ptr const& n)
635+ : node_(n) {}
636+
637+ bool operator()(XPathSpecifiedNode const& n) const
638+ {
639+ return n.Matches(node_);
640+ }
641+
642+ bool operator()(XPathWildcardNode const& n) const
643+ {
644+ return n.Matches(node_);
645+ }
646+
647+ // everything else:
648+ template <typename U>
649+ bool operator()(U n) const
650+ {
651+ return false;
652+ }
653+
654+ Node::Ptr const& node_;
655+ };
656+
657+ // return true if 'p' is one of the matchable types, and p matches 'node'
658+ bool maybe_matches(Node::Ptr const& node, AnyNode const& p)
659+ {
660+ return boost::apply_visitor( matches_visitor(node), p );
661 }
662
663 // Starting at each node listed in 'start_points', search the tree for nodes that match
664 // 'next_match'. next_match *must* be a normal query part object, not a search token.
665- NodeList SearchTreeForNode(NodeList const& start_points, XPathQueryPart const& next_match)
666+ NodeList SearchTreeForNode(NodeList const& start_points, AnyNode const& next_match)
667 {
668 NodeList matches;
669 for (auto root: start_points)
670@@ -65,7 +106,8 @@
671 {
672 Node::Ptr node = queue.front();
673 queue.pop();
674- if (next_match.Matches(node))
675+
676+ if (maybe_matches(node, next_match))
677 {
678 // found one. We keep going deeper, as there may be another node beneath this one
679 // with the same node name.
680@@ -98,20 +140,16 @@
681 while (query_part != query_parts.cend())
682 {
683 // If the current query piece is a recursive search token ('//')...
684- if (query_part->Type() == XPathQueryPart::QueryPartType::Search)
685+ if (query_part->type() == typeid(XPathSearchNode))
686 {
687 // advance to look at the next piece.
688 ++query_part;
689- // do some sanity checking...
690- if (query_part->Type() == XPathQueryPart::QueryPartType::Search)
691- // invalid query - cannot specify multiple search sequences in a row.
692- return NodeVector();
693+
694 // then find all the nodes that match the new query part, and store them as
695- // the new start nodes. We pass in 'start_nodes' rather than 'root' since
696- // there's a chance we'll be doing more than one search in different parts of the tree.
697+ // the new start nodes.
698 start_nodes = SearchTreeForNode(start_nodes, *query_part);
699 }
700- else if (query_part->Type() == XPathQueryPart::QueryPartType::Parent)
701+ else if (query_part->type() == typeid(XPathParentNode))
702 {
703 // This part of the query selects the parent node. If the current node has no
704 // parent (i.e.- we're already at the root of the tree) then this is a no-op:
705@@ -133,7 +171,7 @@
706 start_nodes.begin(),
707 start_nodes.end(),
708 [query_part](Node::Ptr n) -> bool {
709- return ! query_part->Matches(n);
710+ return ! maybe_matches(n, *query_part);
711 }
712 ),
713 start_nodes.end()
714@@ -144,7 +182,7 @@
715 // next query part is not a parent node...
716 auto next_query_part = query_part + 1;
717 if (next_query_part != query_parts.cend()
718- && next_query_part->Type() != XPathQueryPart::QueryPartType::Parent)
719+ && next_query_part->type() != typeid(XPathParentNode))
720 {
721 NodeList new_start_nodes;
722 for (auto node: start_nodes)
723
724=== modified file 'test/test_parser.cpp'
725--- test/test_parser.cpp 2014-02-19 19:16:49 +0000
726+++ test/test_parser.cpp 2014-07-28 19:46:11 +0000
727@@ -100,6 +100,83 @@
728 T expected_;
729 };
730
731+// Some helper functions to make dealing with these new node classes a little easier.
732+
733+void assert_is_specified_node(xpathselect::AnyNode const& node)
734+{
735+ ASSERT_EQ(node.type(), typeid(xpathselect::XPathSpecifiedNode));
736+}
737+
738+void assert_is_wildcard_node(xpathselect::AnyNode const& node)
739+{
740+ ASSERT_EQ(node.type(), typeid(xpathselect::XPathWildcardNode));
741+}
742+
743+void assert_is_parent_node(xpathselect::AnyNode const& node)
744+{
745+ ASSERT_EQ(node.type(), typeid(xpathselect::XPathParentNode));
746+}
747+
748+void assert_is_search_node(xpathselect::AnyNode const& node)
749+{
750+ ASSERT_EQ(node.type(), typeid(xpathselect::XPathSearchNode));
751+}
752+
753+// assert that the node is a specified node, and has the given name:
754+void assert_node_is_specified_with_name(std::string const& name, xpathselect::AnyNode const& node)
755+{
756+ assert_is_specified_node(node);
757+ ASSERT_EQ(name, boost::get<xpathselect::XPathSpecifiedNode>(node).names.at(0));
758+}
759+
760+// assert that the node is a specified node and has no parameters:
761+void assert_node_is_specified_with_num_parameters(int expected, xpathselect::AnyNode const& node)
762+{
763+ assert_is_specified_node(node);
764+ ASSERT_EQ(expected, boost::get<xpathselect::XPathSpecifiedNode>(node).parameters.size());
765+}
766+
767+void assert_node_is_wildcard_with_num_parameters(int expected, xpathselect::AnyNode const& node)
768+{
769+ assert_is_wildcard_node(node);
770+ ASSERT_EQ(expected, boost::get<xpathselect::XPathWildcardNode>(node).parameters.size());
771+}
772+
773+// assert that the parameter on 'node' at index 'index' has name and value equal to 'name' and 'val'
774+template <typename T>
775+void assert_parameter_name_and_value(
776+ xpathselect::AnyNode const& node,
777+ int index,
778+ std::string const& name,
779+ variant_equality_assertion<T> const& val
780+)
781+{
782+ if (node.type() == typeid(xpathselect::XPathSpecifiedNode))
783+ {
784+ auto n = boost::get<xpathselect::XPathSpecifiedNode>(node);
785+ ASSERT_EQ("name", n.parameters.at(index).param_name);
786+ boost::apply_visitor(
787+ val,
788+ n.parameters.at(0).param_value
789+ );
790+ }
791+ else if (node.type() == typeid(xpathselect::XPathWildcardNode))
792+ {
793+ auto n = boost::get<xpathselect::XPathWildcardNode>(node);
794+ ASSERT_EQ("name", n.parameters.at(index).param_name);
795+ boost::apply_visitor(
796+ val,
797+ n.parameters.at(0).param_value
798+ );
799+
800+ }
801+ else
802+ {
803+ FAIL() << "Node is not specified or wildcard node, and thus does not have parameters.";
804+ }
805+}
806+
807+
808 //////////////////////////////////////
809 // Tests for basic type support:
810 //////////////////////////////////////
811@@ -333,6 +410,55 @@
812 std::pair<std::string, bool>("..", false)
813 ));
814
815+// Test node name lists.
816+TEST(TestNodeNameList, test_single_name)
817+{
818+ xpathselect::NodeNameList result;
819+ parser::xpath_grammar<std::string::iterator> g;
820+
821+ ASSERT_TRUE(test_parser_attr("someNode", g.spec_node_name_list, result));
822+ ASSERT_EQ(1, result.size());
823+ ASSERT_EQ("someNode", result.at(0));
824+}
825+
826+TEST(TestNodeNameList, test_two_nodes)
827+{
828+ xpathselect::NodeNameList result;
829+ parser::xpath_grammar<std::string::iterator> g;
830+
831+ ASSERT_TRUE(test_parser_attr("someNode,someOtherNode", g.spec_node_name_list, result));
832+ ASSERT_EQ(2, result.size());
833+ ASSERT_EQ("someNode", result.at(0));
834+ ASSERT_EQ("someOtherNode", result.at(1));
835+}
836+
837+TEST(TestNodeNameList, test_empty_string)
838+{
839+ xpathselect::NodeNameList result;
840+ parser::xpath_grammar<std::string::iterator> g;
841+
842+ ASSERT_FALSE(test_parser_attr("", g.spec_node_name_list, result));
843+}
844+
845+
846+TEST(TestNodeNameList, test_comma_string)
847+{
848+ xpathselect::NodeNameList result;
849+ parser::xpath_grammar<std::string::iterator> g;
850+
851+ ASSERT_FALSE(test_parser_attr(",", g.spec_node_name_list, result));
852+}
853+
854+
855+TEST(TestNodeNameList, test_wildcard_not_allowed_in_list)
856+{
857+ xpathselect::NodeNameList result;
858+ parser::xpath_grammar<std::string::iterator> g;
859+
860+ ASSERT_FALSE(test_parser_attr("foo,*", g.spec_node_name_list, result));
861+}
862+
863+
864 /// Tests for parameter values. This test is much larger than it should be, since it seems to be
865 // impossible to parameterise tests for both type and value. The solution I use here is to have
866 // the actual test in a base class template method, and have several derive classes use different
867@@ -480,8 +606,6 @@
868
869 std::string result;
870 ASSERT_EQ( expect_pass, test_parser_attr(input, g.wildcard_node_name, result) );
871- if (expect_pass)
872- ASSERT_EQ(input, result);
873 }
874
875 INSTANTIATE_TEST_CASE_P(BasicNodeNames,
876@@ -597,14 +721,14 @@
877
878 parser::xpath_grammar<std::string::iterator> g;
879
880- xpathselect::XPathQueryPart result;
881+ xpathselect::XPathSpecifiedNode result;
882 ASSERT_EQ( true, test_parser_attr(input, g.spec_node, result) );
883- ASSERT_EQ("node_name", result.node_name_);
884- ASSERT_FALSE(result.parameter.empty());
885- ASSERT_EQ("param_name", result.parameter.at(0).param_name);
886+ ASSERT_EQ("node_name", result.names.at(0));
887+ ASSERT_FALSE(result.parameters.empty());
888+ ASSERT_EQ("param_name", result.parameters.at(0).param_name);
889 boost::apply_visitor(
890 variant_equality_assertion<int>(123),
891- result.parameter.at(0).param_value
892+ result.parameters.at(0).param_value
893 );
894 }
895
896@@ -614,10 +738,10 @@
897
898 parser::xpath_grammar<std::string::iterator> g;
899
900- xpathselect::XPathQueryPart result;
901+ xpathselect::XPathSpecifiedNode result;
902 ASSERT_EQ( true, test_parser_attr(input, g.spec_node, result) );
903- ASSERT_EQ("node_name", result.node_name_);
904- ASSERT_TRUE(result.parameter.empty());
905+ ASSERT_EQ("node_name", result.names.at(0));
906+ ASSERT_TRUE(result.parameters.empty());
907 }
908
909 TEST(TestXPathParser, test_wildcard_node)
910@@ -626,10 +750,9 @@
911
912 parser::xpath_grammar<std::string::iterator> g;
913
914- xpathselect::XPathQueryPart result;
915+ xpathselect::XPathWildcardNode result;
916 ASSERT_EQ( true, test_parser_attr(input, g.wildcard_node, result) );
917- ASSERT_EQ("*", result.node_name_);
918- ASSERT_TRUE(result.parameter.empty());
919+ ASSERT_TRUE(result.parameters.empty());
920 }
921
922 TEST(TestXPathParser, test_wildcard_node_rejects_parameters)
923@@ -638,7 +761,7 @@
924
925 parser::xpath_grammar<std::string::iterator> g;
926
927- xpathselect::XPathQueryPart result;
928+ xpathselect::XPathWildcardNode result;
929 ASSERT_FALSE( test_parser_attr(input, g.wildcard_node, result) );
930 }
931
932@@ -648,15 +771,14 @@
933
934 parser::xpath_grammar<std::string::iterator> g;
935
936- xpathselect::XPathQueryPart result;
937+ xpathselect::XPathWildcardNode result;
938 ASSERT_EQ( true, test_parser_attr(input, g.wildcard_node_with_params, result) );
939- ASSERT_EQ("*", result.node_name_);
940- ASSERT_FALSE(result.parameter.empty());
941- ASSERT_EQ("param_name", result.parameter.at(0).param_name);
942+ ASSERT_FALSE(result.parameters.empty());
943+ ASSERT_EQ("param_name", result.parameters.at(0).param_name);
944 boost::apply_visitor(
945 variant_equality_assertion<int>(123),
946- result.parameter.at(0).param_value
947- );
948+ result.parameters.at(0).param_value
949+ );
950 }
951
952
953@@ -666,16 +788,16 @@
954
955 parser::xpath_grammar<std::string::iterator> g;
956
957- xpathselect::XPathQueryPart result;
958- ASSERT_EQ( true, test_parser_attr(input, g.node, result) );
959- ASSERT_EQ( "*", result.node_name_ );
960- ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Normal, result.Type() );
961- ASSERT_EQ( 1, result.parameter.size() );
962- ASSERT_EQ( "name", result.parameter.at(0).param_name );
963- boost::apply_visitor(
964- variant_equality_assertion<std::string>("value"),
965- result.parameter.at(0).param_value
966- );
967+ xpathselect::AnyNode result;
968+ ASSERT_EQ( true, test_parser_attr(input, g.any_node, result) );
969+
970+ assert_node_is_wildcard_with_num_parameters(1, result);
971+ assert_parameter_name_and_value(
972+ result,
973+ 0,
974+ "name",
975+ variant_equality_assertion<std::string>("value")
976+ );
977 }
978
979 TEST(TestXPathParser, test_node_can_be_a_wildcard_node_without_params)
980@@ -684,10 +806,9 @@
981
982 parser::xpath_grammar<std::string::iterator> g;
983
984- xpathselect::XPathQueryPart result;
985- ASSERT_EQ( true, test_parser_attr(input, g.node, result) );
986- ASSERT_EQ( "*", result.node_name_ );
987- ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Normal, result.Type() );
988+ xpathselect::AnyNode result;
989+ ASSERT_EQ( true, test_parser_attr(input, g.any_node, result) );
990+ assert_is_wildcard_node(result);
991 }
992
993 TEST(TestXPathParser, test_node_can_be_a_spec_node_with_params)
994@@ -696,16 +817,16 @@
995
996 parser::xpath_grammar<std::string::iterator> g;
997
998- xpathselect::XPathQueryPart result;
999- ASSERT_EQ( true, test_parser_attr(input, g.node, result) );
1000- ASSERT_EQ( "foo", result.node_name_ );
1001- ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Normal, result.Type() );
1002- ASSERT_EQ( 1, result.parameter.size() );
1003- ASSERT_EQ( "name", result.parameter.at(0).param_name );
1004- boost::apply_visitor(
1005- variant_equality_assertion<std::string>("value"),
1006- result.parameter.at(0).param_value
1007- );
1008+ xpathselect::AnyNode result;
1009+ ASSERT_EQ( true, test_parser_attr(input, g.any_node, result) );
1010+
1011+ assert_node_is_specified_with_name("foo", result);
1012+ assert_parameter_name_and_value(
1013+ result,
1014+ 0,
1015+ "name",
1016+ variant_equality_assertion<std::string>("value")
1017+ );
1018 }
1019
1020 TEST(TestXPathParser, test_node_can_be_a_spec_node_without_params)
1021@@ -714,10 +835,10 @@
1022
1023 parser::xpath_grammar<std::string::iterator> g;
1024
1025- xpathselect::XPathQueryPart result;
1026- ASSERT_EQ( true, test_parser_attr(input, g.node, result) );
1027- ASSERT_EQ( "foo", result.node_name_ );
1028- ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Normal, result.Type() );
1029+ xpathselect::AnyNode result;
1030+ ASSERT_EQ( true, test_parser_attr(input, g.any_node, result) );
1031+
1032+ assert_node_is_specified_with_name("foo", result);
1033 }
1034
1035 TEST(TestXPathParser, test_node_can_be_a_parent_node)
1036@@ -726,10 +847,9 @@
1037
1038 parser::xpath_grammar<std::string::iterator> g;
1039
1040- xpathselect::XPathQueryPart result;
1041- ASSERT_EQ( true, test_parser_attr(input, g.node, result) );
1042- ASSERT_EQ( "..", result.node_name_ );
1043- ASSERT_EQ( xpathselect::XPathQueryPart::QueryPartType::Parent, result.Type() );
1044+ xpathselect::AnyNode result;
1045+ ASSERT_EQ( true, test_parser_attr(input, g.any_node, result) );
1046+ assert_is_parent_node(result);
1047 }
1048
1049 TEST(TestXPathParser, test_search_node_followed_by_normal_node)
1050@@ -738,12 +858,11 @@
1051 // to give it some more data, even though we're not actually matching it.
1052 std::string input("//node_name");
1053 parser::xpath_grammar<std::string::iterator> g;
1054- xpathselect::XPathQueryPart result;
1055+ xpathselect::XPathSearchNode result;
1056
1057 // however, this means we can't use the test_parser_attr function, since it
1058 // returns false on a partial match. Use the parse(...) function directly:
1059 ASSERT_TRUE( parse(input.begin(), input.end(),g.search_node, result) );
1060- ASSERT_TRUE( result.Type() == xpathselect::XPathQueryPart::QueryPartType::Search );
1061 }
1062
1063 TEST(TestXPathParser, test_search_node_followed_by_wildcard_node_with_parameters)
1064@@ -752,12 +871,11 @@
1065 // to give it some more data, even though we're not actually matching it.
1066 std::string input("//*[foo=\"bar\"]");
1067 parser::xpath_grammar<std::string::iterator> g;
1068- xpathselect::XPathQueryPart result;
1069+ xpathselect::XPathSearchNode result;
1070
1071 // however, this means we can't use the test_parser_attr function, since it
1072 // returns false on a partial match. Use the parse(...) function directly:
1073 ASSERT_TRUE( parse(input.begin(), input.end(),g.search_node, result) );
1074- ASSERT_TRUE( result.Type() == xpathselect::XPathQueryPart::QueryPartType::Search );
1075 }
1076
1077 TEST(TestXPathParser, test_search_node_cannot_have_parameters)
1078@@ -766,9 +884,20 @@
1079
1080 parser::xpath_grammar<std::string::iterator> g;
1081
1082- xpathselect::XPathQueryPart result;
1083- ASSERT_FALSE( test_parser_attr(input, g.search_node, result) );
1084-}
1085+ xpathselect::XPathSearchNode result;
1086+ ASSERT_FALSE( test_parser_attr(input, g.search_node, result) );
1087+}
1088+
1089+TEST(TestXPathParser, test_cannot_give_two_search_nodes)
1090+{
1091+ std::string input("////");
1092+
1093+ parser::xpath_grammar<std::string::iterator> g;
1094+
1095+ xpathselect::XPathSearchNode result;
1096+ ASSERT_FALSE( test_parser_attr(input, g.search_node, result) );
1097+}
1098+
1099
1100 TEST(TestXPathParser, test_parent_node)
1101 {
1102@@ -776,9 +905,8 @@
1103
1104 parser::xpath_grammar<std::string::iterator> g;
1105
1106- xpathselect::XPathQueryPart result;
1107+ xpathselect::XPathParentNode result;
1108 ASSERT_TRUE( test_parser_attr(input, g.parent_node, result) );
1109- ASSERT_TRUE( result.Type() == xpathselect::XPathQueryPart::QueryPartType::Parent );
1110 }
1111
1112 TEST(TestXPathParser, test_normal_sep_works)
1113@@ -801,123 +929,87 @@
1114
1115 TEST(TestXPathParser, test_can_extract_query_list)
1116 {
1117- std::string input("/node1/node2");
1118-
1119- parser::xpath_grammar<std::string::iterator> g;
1120-
1121- xpathselect::QueryList result;
1122- ASSERT_TRUE(test_parser_attr(input, g.node_sequence, result));
1123+ xpathselect::QueryList result = xpathselect::parser::parse_query("/node1/node2");
1124 ASSERT_EQ(2, result.size());
1125- ASSERT_EQ("node1", result.at(0).node_name_);
1126- ASSERT_TRUE(result.at(0).parameter.empty());
1127- ASSERT_EQ("node2", result.at(1).node_name_);
1128- ASSERT_TRUE(result.at(1).parameter.empty());
1129+ assert_node_is_specified_with_name("node1", result.at(0));
1130+ assert_node_is_specified_with_num_parameters(0, result.at(0));
1131+ assert_node_is_specified_with_name("node2", result.at(1));
1132+ assert_node_is_specified_with_num_parameters(0, result.at(1));
1133 }
1134
1135 TEST(TestXPathParser, test_can_extract_query_list_with_search)
1136 {
1137- std::string input("//node1");
1138-
1139- parser::xpath_grammar<std::string::iterator> g;
1140-
1141- xpathselect::QueryList result;
1142- ASSERT_TRUE(test_parser_attr(input, g.node_sequence, result));
1143+ xpathselect::QueryList result = xpathselect::parser::parse_query("//node1");
1144 ASSERT_EQ(2, result.size());
1145- ASSERT_TRUE(result.at(0).Type() == xpathselect::XPathQueryPart::QueryPartType::Search );
1146- ASSERT_TRUE(result.at(1).Type() == xpathselect::XPathQueryPart::QueryPartType::Normal );
1147- ASSERT_TRUE(result.at(0).parameter.empty());
1148- ASSERT_EQ("node1", result.at(1).node_name_);
1149- ASSERT_TRUE(result.at(1).parameter.empty());
1150+ assert_is_search_node(result.at(0));
1151+ assert_node_is_specified_with_name("node1", result.at(1));
1152+ assert_node_is_specified_with_num_parameters(0, result.at(1));
1153 }
1154
1155 TEST(TestXPathParser, test_mix_search_and_normal)
1156 {
1157- std::string input("/node1//node2");
1158-
1159- parser::xpath_grammar<std::string::iterator> g;
1160-
1161- xpathselect::QueryList result;
1162- ASSERT_TRUE(test_parser_attr(input, g.node_sequence, result));
1163+ xpathselect::QueryList result = xpathselect::parser::parse_query("/node1//node2");
1164
1165 ASSERT_EQ(3, result.size());
1166
1167- ASSERT_EQ("node1", result.at(0).node_name_);
1168- ASSERT_TRUE(result.at(0).Type() == xpathselect::XPathQueryPart::QueryPartType::Normal );
1169- ASSERT_TRUE(result.at(0).parameter.empty());
1170-
1171- ASSERT_TRUE(result.at(1).Type() == xpathselect::XPathQueryPart::QueryPartType::Search );
1172- ASSERT_TRUE(result.at(1).parameter.empty());
1173-
1174- ASSERT_EQ("node2", result.at(2).node_name_);
1175- ASSERT_TRUE(result.at(2).Type() == xpathselect::XPathQueryPart::QueryPartType::Normal );
1176- ASSERT_TRUE(result.at(2).parameter.empty());
1177+ assert_node_is_specified_with_name("node1", result.at(0));
1178+ assert_node_is_specified_with_num_parameters(0, result.at(0));
1179+
1180+ assert_is_search_node(result.at(1));
1181+
1182+ assert_node_is_specified_with_name("node2", result.at(2));
1183+ assert_node_is_specified_with_num_parameters(0, result.at(2));
1184 }
1185
1186 TEST(TestXPathParser, test_mix_search_and_long_normal)
1187 {
1188- std::string input("/node1//node2[name=\"val\"]/node3");
1189-
1190- parser::xpath_grammar<std::string::iterator> g;
1191-
1192- xpathselect::QueryList result;
1193- ASSERT_TRUE(test_parser_attr(input, g.node_sequence, result));
1194-
1195- ASSERT_EQ(4, result.size());
1196-
1197- ASSERT_EQ("node1", result.at(0).node_name_);
1198- ASSERT_TRUE(result.at(0).parameter.empty());
1199-
1200- ASSERT_TRUE(result.at(1).Type() == xpathselect::XPathQueryPart::QueryPartType::Search );
1201- ASSERT_TRUE(result.at(1).parameter.empty());
1202-
1203- ASSERT_EQ("node2", result.at(2).node_name_);
1204- ASSERT_EQ(1, result.at(2).parameter.size());
1205- ASSERT_EQ("name", result.at(2).parameter.at(0).param_name);
1206- boost::apply_visitor(
1207- variant_equality_assertion<std::string>("val"),
1208- result.at(2).parameter.at(0).param_value
1209- );
1210- ASSERT_EQ("node3", result.at(3).node_name_);
1211- ASSERT_TRUE(result.at(3).parameter.empty());
1212+ xpathselect::QueryList result = xpathselect::parser::parse_query(
1213+ "/node1//node2[name=\"val\"]/node3"
1214+ );
1215+
1216+ EXPECT_EQ(4, result.size());
1217+
1218+ assert_node_is_specified_with_name("node1", result.at(0));
1219+ assert_node_is_specified_with_num_parameters(0, result.at(0));
1220+
1221+ assert_is_search_node(result.at(1));
1222+
1223+ assert_node_is_specified_with_name("node2", result.at(2));
1224+ assert_node_is_specified_with_num_parameters(1, result.at(2));
1225+ assert_parameter_name_and_value(
1226+ result.at(2),
1227+ 0,
1228+ "name",
1229+ variant_equality_assertion<std::string>("val")
1230+ );
1231+
1232+ assert_node_is_specified_with_name("node3", result.at(3));
1233+ assert_node_is_specified_with_num_parameters(0, result.at(3));
1234 }
1235
1236 TEST(TestXPathParser, test_mix_normal_and_parent)
1237 {
1238- std::string input("/node1/..");
1239-
1240- parser::xpath_grammar<std::string::iterator> g;
1241-
1242- xpathselect::QueryList result;
1243- ASSERT_TRUE(test_parser_attr(input, g.node_sequence, result));
1244+ xpathselect::QueryList result = xpathselect::parser::parse_query("/node1/..");
1245
1246 ASSERT_EQ(2, result.size());
1247
1248- ASSERT_EQ("node1", result.at(0).node_name_);
1249- ASSERT_TRUE(result.at(0).parameter.empty());
1250+ assert_node_is_specified_with_name("node1", result.at(0));
1251+ assert_node_is_specified_with_num_parameters(0, result.at(0));
1252
1253- ASSERT_TRUE(result.at(1).Type() == xpathselect::XPathQueryPart::QueryPartType::Parent );
1254- ASSERT_TRUE(result.at(1).parameter.empty());
1255+ assert_is_parent_node(result.at(1));
1256 }
1257
1258 TEST(TestXPathParser, test_mix_normal_and_parent_and_wildcard)
1259 {
1260- std::string input("/node1/../*");
1261-
1262- parser::xpath_grammar<std::string::iterator> g;
1263-
1264- xpathselect::QueryList result;
1265- ASSERT_TRUE(test_parser_attr(input, g.node_sequence, result));
1266+ xpathselect::QueryList result = xpathselect::parser::parse_query("/node1/../*");
1267
1268 ASSERT_EQ(3, 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- ASSERT_TRUE(result.at(2).node_name_ == "*" );
1277- ASSERT_TRUE(result.at(2).parameter.empty());
1278+ assert_node_is_specified_with_name("node1", result.at(0));
1279+ assert_node_is_specified_with_num_parameters(0, result.at(0));
1280+
1281+ assert_is_parent_node(result.at(1));
1282+ assert_is_wildcard_node(result.at(2));
1283 }
1284
1285 class TestXPathParserQueryStrings : public ::testing::TestWithParam<std::pair<std::string, bool> >
1286@@ -930,10 +1022,9 @@
1287 std::string input = p.first;
1288 bool expect_pass = p.second;
1289
1290- parser::xpath_grammar<std::string::iterator> g;
1291-
1292- xpathselect::QueryList result;
1293- ASSERT_EQ( expect_pass, test_parser_attr(input, g, result) );
1294+ bool actual_pass;
1295+ xpathselect::QueryList result = xpathselect::parser::parse_query(input, actual_pass);
1296+ ASSERT_EQ( expect_pass, actual_pass );
1297 }
1298
1299 INSTANTIATE_TEST_CASE_P(BasicNodeNames,
1300
1301=== modified file 'test/test_xpath_tree.cpp'
1302--- test/test_xpath_tree.cpp 2013-09-03 03:28:14 +0000
1303+++ test/test_xpath_tree.cpp 2014-07-28 19:46:11 +0000
1304@@ -47,6 +47,16 @@
1305 NodePtr leaf_2_;
1306 };
1307
1308+std::string dump_result(xpathselect::NodeVector result)
1309+{
1310+ // horribly inneficient, but only called when tests fail, so... *shrug*
1311+ std::string s;
1312+ for (auto r: result)
1313+ {
1314+ s += r->GetName() + " ";
1315+ }
1316+ return s;
1317+}
1318 TEST_F(TestTreeFixture, test_simple)
1319 {
1320 xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/");
1321@@ -64,6 +74,17 @@
1322 ASSERT_EQ(expected, actual);
1323 }
1324
1325+TEST_F(TestTreeFixture, test_multi_name_absolute)
1326+{
1327+ xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/ChildLeft1,ChildRight1");
1328+ ASSERT_EQ(2, result.size());
1329+
1330+ std::set<xpathselect::Node::Ptr> to_be_matched { child_l1_, child_r1_ };
1331+ std::set<xpathselect::Node::Ptr> resultset(result.begin(), result.end());
1332+
1333+ ASSERT_EQ(to_be_matched, resultset);
1334+}
1335+
1336 TEST_F(TestTreeFixture, test_simple_relative)
1337 {
1338 xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "//ChildRight1");
1339@@ -175,7 +196,7 @@
1340 TEST_F(TestTreeFixture, test_wildcard)
1341 {
1342 xpathselect::NodeVector result = xpathselect::SelectNodes(root_, "/Root/*");
1343- ASSERT_EQ(2, result.size());
1344+ ASSERT_EQ(2, result.size()) << dump_result(result);
1345 for(auto n : result)
1346 {
1347 ASSERT_TRUE(n == child_l1_ || n == child_r1_ );

Subscribers

People subscribed via source and target branches

to all changes: