Merge lp:~brandontschaefer/unity/remove-rev-3098 into lp:unity

Proposed by Brandon Schaefer
Status: Merged
Approved by: Andrea Azzarone
Approved revision: no longer in the source branch.
Merged at revision: 3100
Proposed branch: lp:~brandontschaefer/unity/remove-rev-3098
Merge into: lp:unity
Diff against target: 1045 lines (+724/-180)
8 files modified
debian/control (+5/-7)
tests/CMakeLists.txt (+1/-0)
tests/test_introspection.cpp (+356/-0)
unity-shared/CMakeLists.txt (+1/-0)
unity-shared/DebugDBusInterface.cpp (+128/-173)
unity-shared/DebugDBusInterface.h (+2/-0)
unity-shared/XPathQueryPart.cpp (+186/-0)
unity-shared/XPathQueryPart.h (+45/-0)
To merge this branch: bzr merge lp:~brandontschaefer/unity/remove-rev-3098
Reviewer Review Type Date Requested Status
Andrea Azzarone (community) Approve
PS Jenkins bot continuous-integration Pending
Review via email: mp+146221@code.launchpad.net

Commit message

This is causing problem with all the AP tests...ie. Every single AP was failing.

Description of the change

This is causing problem with all the AP tests...ie. Every single AP was failing.

To post a comment you must log in.
Revision history for this message
Andrea Azzarone (azzar1) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2013-02-01 17:01:09 +0000
3+++ debian/control 2013-02-01 20:35:26 +0000
4@@ -43,7 +43,6 @@
5 libxfixes-dev (>= 1:5.0-4ubuntu4),
6 libgtest-dev,
7 google-mock,
8- libxpathselect-dev,
9 Standards-Version: 3.9.3
10 Homepage: https://launchpad.net/unity
11 # If you aren't a member of ~unity-team but need to upload packaging changes,
12@@ -168,7 +167,6 @@
13 Architecture: all
14 Depends: ${misc:Depends},
15 ${python:Depends},
16- libxpathselect1.1,
17 Description: Autopiloted tests for Unity
18 We test Unity automatically through autopilot, a framework which enables us
19 to trigger keyboard and mouse events on the fly as well as introspecting
20@@ -187,7 +185,7 @@
21 Depends: unity, ${misc:Depends}
22 Architecture: all
23 Section: oldlibs
24-Description: transitional dummy package
25+Description: transitional dummy package
26 This is a transitional dummy package for unity-2d -> unity migration.
27 It can safely be removed.
28
29@@ -195,7 +193,7 @@
30 Depends: unity, ${misc:Depends}
31 Architecture: all
32 Section: oldlibs
33-Description: transitional dummy package
34+Description: transitional dummy package
35 This is a transitional dummy package for unity-2d -> unity migration.
36 It can safely be removed.
37
38@@ -203,7 +201,7 @@
39 Depends: unity, ${misc:Depends}
40 Architecture: all
41 Section: oldlibs
42-Description: transitional dummy package
43+Description: transitional dummy package
44 This is a transitional dummy package for unity-2d -> unity migration.
45 It can safely be removed.
46
47@@ -211,7 +209,7 @@
48 Depends: unity, ${misc:Depends}
49 Architecture: all
50 Section: oldlibs
51-Description: transitional dummy package
52+Description: transitional dummy package
53 This is a transitional dummy package for unity-2d -> unity migration.
54 It can safely be removed.
55
56@@ -219,7 +217,7 @@
57 Depends: unity, ${misc:Depends}
58 Architecture: all
59 Section: oldlibs
60-Description: transitional dummy package
61+Description: transitional dummy package
62 This is a transitional dummy package for unity-2d -> unity migration.
63 It can safely be removed.
64
65
66=== modified file 'tests/CMakeLists.txt'
67--- tests/CMakeLists.txt 2013-02-01 17:01:09 +0000
68+++ tests/CMakeLists.txt 2013-02-01 20:35:26 +0000
69@@ -112,6 +112,7 @@
70 test_indicator_appmenu.cpp
71 test_indicator_entry.cpp
72 test_indicators.cpp
73+ test_introspection.cpp
74 test_favorite_store.cpp
75 test_favorite_store_gsettings.cpp
76 test_favorite_store_private.cpp
77
78=== added file 'tests/test_introspection.cpp'
79--- tests/test_introspection.cpp 1970-01-01 00:00:00 +0000
80+++ tests/test_introspection.cpp 2013-02-01 20:35:26 +0000
81@@ -0,0 +1,356 @@
82+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
83+/*
84+ * Copyright (C) 2010 Canonical Ltd
85+ *
86+ * This program is free software: you can redistribute it and/or modify
87+ * it under the terms of the GNU General Public License version 3 as
88+ * published by the Free Software Foundation.
89+ *
90+ * This program is distributed in the hope that it will be useful,
91+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
92+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
93+ * GNU General Public License for more details.
94+ *
95+ * You should have received a copy of the GNU General Public License
96+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
97+ *
98+ * Authored by: Thomi Richards <thomi.richards@canonical.com>
99+ */
100+#include <gtest/gtest.h>
101+#include <glib.h>
102+#include <memory>
103+#include <boost/foreach.hpp>
104+
105+#include "Introspectable.h"
106+#include "DebugDBusInterface.h"
107+
108+
109+using namespace unity::debug;
110+
111+class MockIntrospectable : public Introspectable
112+{
113+public:
114+ MockIntrospectable(std::string const& name)
115+ : name_(name)
116+ {}
117+
118+ std::string GetName() const
119+ {
120+ return name_;
121+ }
122+ void AddProperties(GVariantBuilder* builder)
123+ {
124+ g_variant_builder_add (builder, "{sv}", "Name", g_variant_new_string (name_.c_str()) );
125+ g_variant_builder_add (builder, "{sv}", "SomeProperty", g_variant_new_string ("SomeValue") );
126+ g_variant_builder_add (builder, "{sv}", "BoolPropertyTrue", g_variant_new_boolean (TRUE) );
127+ g_variant_builder_add (builder, "{sv}", "BoolPropertyFalse", g_variant_new_boolean (FALSE) );
128+ // 8-bit integer types:
129+ g_variant_builder_add (builder, "{sv}", "BytePropertyPos", g_variant_new_byte (12) );
130+ // 16-bit integer types:
131+ g_variant_builder_add (builder, "{sv}", "Int16PropertyPos", g_variant_new_int16 (1012) );
132+ g_variant_builder_add (builder, "{sv}", "Int16PropertyNeg", g_variant_new_int16 (-1034) );
133+ g_variant_builder_add (builder, "{sv}", "UInt16PropertyPos", g_variant_new_uint16 (1056) );
134+ // 32-bit integer types:
135+ g_variant_builder_add (builder, "{sv}", "Int32PropertyPos", g_variant_new_int32 (100012) );
136+ g_variant_builder_add (builder, "{sv}", "Int32PropertyNeg", g_variant_new_int32 (-100034) );
137+ g_variant_builder_add (builder, "{sv}", "UInt32PropertyPos", g_variant_new_uint32 (100056) );
138+ // 64-bit integer types
139+ g_variant_builder_add (builder, "{sv}", "Int64PropertyPos", g_variant_new_int32 (100000012) );
140+ g_variant_builder_add (builder, "{sv}", "Int64PropertyNeg", g_variant_new_int32 (-100000034) );
141+ g_variant_builder_add (builder, "{sv}", "UInt64PropertyPos", g_variant_new_uint32 (100000056) );
142+
143+ }
144+private:
145+ std::string name_;
146+};
147+
148+class TestIntrospection : public ::testing::Test
149+{
150+public:
151+ TestIntrospection()
152+ : root_(new MockIntrospectable("Unity")),
153+ dc_(new MockIntrospectable("DashController")),
154+ pc_(new MockIntrospectable("PanelController")),
155+ foo1_(new MockIntrospectable("Foo")),
156+ foo2_(new MockIntrospectable("Foo")),
157+ foo3_(new MockIntrospectable("Foo"))
158+ {
159+ root_->AddChild(dc_.get());
160+ root_->AddChild(pc_.get());
161+ dc_->AddChild(foo1_.get());
162+ dc_->AddChild(foo2_.get());
163+ dc_->AddChild(foo3_.get());
164+
165+ //root_->SetProperty(g_variant_new("{sv}", "SomeProperty", g_variant_new_string("SomeValue")));
166+ }
167+
168+protected:
169+ std::shared_ptr<MockIntrospectable> root_;
170+ std::shared_ptr<MockIntrospectable> dc_;
171+ std::shared_ptr<MockIntrospectable> pc_;
172+ std::shared_ptr<MockIntrospectable> foo1_;
173+ std::shared_ptr<MockIntrospectable> foo2_;
174+ std::shared_ptr<MockIntrospectable> foo3_;
175+
176+};
177+
178+TEST_F(TestIntrospection, TestTest)
179+{
180+ ASSERT_STREQ("Unity", root_->GetName().c_str());
181+}
182+
183+TEST_F(TestIntrospection, TestVariousRootQueries)
184+{
185+ std::list<Introspectable*> results;
186+ std::string query;
187+
188+ results = GetIntrospectableNodesFromQuery(query, root_.get());
189+ ASSERT_EQ(1, results.size());
190+ EXPECT_STREQ("Unity", results.front()->GetName().c_str());
191+
192+ query = "/";
193+ results = GetIntrospectableNodesFromQuery(query, root_.get());
194+ ASSERT_EQ(1, results.size());
195+ EXPECT_STREQ("Unity", results.front()->GetName().c_str());
196+
197+ query = "/Unity";
198+ results = GetIntrospectableNodesFromQuery(query, root_.get());
199+ ASSERT_EQ(1, results.size());
200+ EXPECT_STREQ("Unity", results.front()->GetName().c_str());
201+}
202+
203+TEST_F(TestIntrospection, TestAsteriskWildcard)
204+{
205+ std::list<Introspectable*> results;
206+ std::string query = "/Unity/*";
207+
208+ results = GetIntrospectableNodesFromQuery(query, root_.get());
209+ ASSERT_EQ(2, results.size());
210+
211+ for(auto p : results)
212+ {
213+ ASSERT_TRUE(
214+ p->GetName() == "DashController" ||
215+ p->GetName() == "PanelController"
216+ );
217+ }
218+}
219+
220+TEST_F(TestIntrospection, TestRelativeAsteriskWildcard)
221+{
222+ std::list<Introspectable*> results;
223+ std::string query = "//DashController/*";
224+
225+ results = GetIntrospectableNodesFromQuery(query, root_.get());
226+ ASSERT_EQ(3, results.size());
227+
228+ for(auto p : results)
229+ {
230+ ASSERT_TRUE(p->GetName() == "Foo");
231+ }
232+}
233+
234+TEST_F(TestIntrospection, TestAbsoluteQueries)
235+{
236+ std::list<Introspectable*> results;
237+ std::string query = "/Unity/DashController";
238+
239+ results = GetIntrospectableNodesFromQuery(query, root_.get());
240+ ASSERT_EQ(1, results.size());
241+ EXPECT_STREQ("DashController", results.front()->GetName().c_str());
242+}
243+
244+TEST_F(TestIntrospection, TestMalformedRelativeQueries)
245+{
246+ std::list<Introspectable*> results;
247+ std::string query = "Unity";
248+
249+ results = GetIntrospectableNodesFromQuery(query, root_.get());
250+ ASSERT_EQ(1, results.size());
251+ EXPECT_STREQ("Unity", results.front()->GetName().c_str());
252+
253+ query = "Foo";
254+ results = GetIntrospectableNodesFromQuery(query, root_.get());
255+ ASSERT_EQ(3, results.size());
256+ for(auto p : results)
257+ {
258+ EXPECT_STREQ("Foo", p->GetName().c_str());
259+ }
260+}
261+
262+TEST_F(TestIntrospection, TestSimpleRelativeQueries)
263+{
264+ std::list<Introspectable*> results;
265+ std::string query = "//Unity";
266+
267+ results = GetIntrospectableNodesFromQuery(query, root_.get());
268+ ASSERT_EQ(1, results.size());
269+ EXPECT_STREQ("Unity", results.front()->GetName().c_str());
270+
271+ query = "//Foo";
272+ results = GetIntrospectableNodesFromQuery(query, root_.get());
273+ ASSERT_EQ(3, results.size());
274+ for(auto p : results)
275+ {
276+ EXPECT_STREQ("Foo", p->GetName().c_str());
277+ }
278+}
279+
280+TEST_F(TestIntrospection, TestComplexRelativeQueries)
281+{
282+ std::list<Introspectable*> results;
283+ std::string query = "//DashController/Foo";
284+
285+ results = GetIntrospectableNodesFromQuery(query, root_.get());
286+ ASSERT_EQ(3, results.size());
287+ for(auto p : results)
288+ {
289+ EXPECT_STREQ("Foo", p->GetName().c_str());
290+ }
291+}
292+
293+TEST_F(TestIntrospection, TestQueriesWithNoResults)
294+{
295+ std::list<Introspectable*> results;
296+ std::string query = "//Does/Not/Exist";
297+
298+ results = GetIntrospectableNodesFromQuery(query, root_.get());
299+ ASSERT_EQ(0, results.size());
300+
301+ query = "DoesNotEverExist";
302+ results = GetIntrospectableNodesFromQuery(query, root_.get());
303+ ASSERT_EQ(0, results.size());
304+
305+ query = "/Does/Not/Ever/Exist";
306+ results = GetIntrospectableNodesFromQuery(query, root_.get());
307+ ASSERT_EQ(0, results.size());
308+}
309+
310+TEST_F(TestIntrospection, TestQueriesWithParams)
311+{
312+ std::list<Introspectable*> results;
313+ // this should find our root node:
314+ results = GetIntrospectableNodesFromQuery("/Unity[SomeProperty=SomeValue]", root_.get());
315+ ASSERT_EQ(1, results.size());
316+ EXPECT_STREQ("Unity", results.front()->GetName().c_str());
317+ // but this should find nothing:
318+ results = GetIntrospectableNodesFromQuery("/Unity[SomeProperty=SomeOtherValue]", root_.get());
319+ ASSERT_EQ(0, results.size());
320+
321+ // make sure relative paths work:
322+ results = GetIntrospectableNodesFromQuery("//Foo[Name=Foo]", root_.get());
323+ ASSERT_EQ(3, results.size());
324+ for(auto p : results)
325+ {
326+ EXPECT_STREQ("Foo", p->GetName().c_str());
327+ }
328+
329+ // make sure param queries work with descendant nodes as well:
330+ results = GetIntrospectableNodesFromQuery("/Unity[SomeProperty=SomeValue]/DashController[Name=DashController]/Foo", root_.get());
331+ ASSERT_EQ(3, results.size());
332+ for(auto p : results)
333+ {
334+ EXPECT_STREQ("Foo", p->GetName().c_str());
335+ }
336+}
337+
338+TEST_F(TestIntrospection, TestQueryTypeBool)
339+{
340+ std::list<Introspectable*> results;
341+
342+ // These are all equivilent and should return the root item and nothing more:
343+ std::list<std::string> queries = {"/Unity[BoolPropertyTrue=True]",
344+ "/Unity[BoolPropertyTrue=true]",
345+ "/Unity[BoolPropertyTrue=trUE]",
346+ "/Unity[BoolPropertyTrue=yes]",
347+ "/Unity[BoolPropertyTrue=ON]",
348+ "/Unity[BoolPropertyTrue=1]"};
349+
350+ for(auto query : queries)
351+ {
352+ results = GetIntrospectableNodesFromQuery(query, root_.get());
353+ ASSERT_EQ(1, results.size());
354+ EXPECT_STREQ("Unity", results.front()->GetName().c_str());
355+ }
356+
357+ // For boolean properties, anything that's not True, Yes, On or 1 is treated as false:
358+ queries = {"/Unity[BoolPropertyTrue=False]",
359+ "/Unity[BoolPropertyTrue=fAlSE]",
360+ "/Unity[BoolPropertyTrue=No]",
361+ "/Unity[BoolPropertyTrue=OFF]",
362+ "/Unity[BoolPropertyTrue=0]",
363+ "/Unity[BoolPropertyTrue=ThereWasAManFromNantucket]"};
364+ for(auto query : queries)
365+ {
366+ results = GetIntrospectableNodesFromQuery(query, root_.get());
367+ ASSERT_EQ(0, results.size());
368+ }
369+}
370+
371+TEST_F(TestIntrospection, TestQueryTypeInt)
372+{
373+ std::list<Introspectable*> results;
374+
375+ // these should all select the root Unity node:
376+ std::list<std::string> queries = {"/Unity[BytePropertyPos=12]",
377+ "/Unity[Int16PropertyPos=1012]",
378+ "/Unity[Int16PropertyNeg=-1034]",
379+ "/Unity[UInt16PropertyPos=1056]",
380+ "/Unity[Int32PropertyPos=100012]",
381+ "/Unity[Int32PropertyNeg=-100034]",
382+ "/Unity[UInt32PropertyPos=100056]",
383+ "/Unity[Int64PropertyPos=100000012]",
384+ "/Unity[Int64PropertyNeg=-100000034]",
385+ "/Unity[UInt64PropertyPos=100000056]"};
386+ for(auto query : queries)
387+ {
388+ results = GetIntrospectableNodesFromQuery(query, root_.get());
389+ ASSERT_EQ(1, results.size()) << "Failing query: " << query;
390+ EXPECT_STREQ("Unity", results.front()->GetName().c_str());
391+ }
392+
393+ // but these shouldn't:
394+ queries = {"/Unity[BytePropertyPos=1234]",
395+ "/Unity[Int16PropertyPos=0]",
396+ "/Unity[Int16PropertyNeg=-0]",
397+ "/Unity[Int16PropertyNeg=-]",
398+ "/Unity[UInt16PropertyPos=-1056]",
399+ "/Unity[Int32PropertyPos=999999999999999]",
400+ "/Unity[Int32PropertyNeg=Garbage]",
401+ "/Unity[UInt32PropertyPos=-23]"};
402+ for(auto query : queries)
403+ {
404+ results = GetIntrospectableNodesFromQuery(query, root_.get());
405+ ASSERT_EQ(0, results.size());
406+ }
407+}
408+
409+TEST_F(TestIntrospection, TestMalformedQueries)
410+{
411+ // this should work - we have not yet specified a parameter to test against.
412+ std::list<Introspectable*> results = GetIntrospectableNodesFromQuery("/Unity[", root_.get());
413+ ASSERT_EQ(1, results.size());
414+
415+ std::list<std::string> queries = {"/Unity[BoolPropertyTrue",
416+ "/Unity[BoolPropertyTrue=",
417+ "/Unity[BoolPropertyTrue=]",
418+ "/Unity[BytePropertyPos=",
419+ "/Unity[BytePropertyPos=]",
420+ "/Unity[Int16PropertyPos=",
421+ "/Unity[Int16PropertyPos=]",
422+ "/Unity[Int16PropertyNeg=",
423+ "/Unity[Int16PropertyNeg=]",
424+ "/Unity[UInt16PropertyPos[=]]",
425+ "/Unity[Int32PropertyPos[[",
426+ "/Unity[Int32PropertyNeg]",
427+ "/Unity[UInt32PropertyPos=[",
428+ "/Unity[Int64PropertyPos[[",
429+ "/Unity[Int64PropertyNeg",
430+ "/Unity[UInt64PropertyPos]"};
431+
432+ for (std::string query : queries)
433+ {
434+ results = GetIntrospectableNodesFromQuery(query, root_.get());
435+ ASSERT_EQ(0, results.size()) << "Failing query: " << query;
436+ }
437+}
438
439=== modified file 'unity-shared/CMakeLists.txt'
440--- unity-shared/CMakeLists.txt 2013-02-01 17:01:09 +0000
441+++ unity-shared/CMakeLists.txt 2013-02-01 20:35:26 +0000
442@@ -64,6 +64,7 @@
443 VScrollBarOverlayWindow.cpp
444 WindowButtons.cpp
445 WindowManager.cpp
446+ XPathQueryPart.cpp
447 )
448
449 if(ENABLE_X_SUPPORT)
450
451=== modified file 'unity-shared/DebugDBusInterface.cpp'
452--- unity-shared/DebugDBusInterface.cpp 2013-01-24 04:53:37 +0000
453+++ unity-shared/DebugDBusInterface.cpp 2013-02-01 20:35:26 +0000
454@@ -28,12 +28,10 @@
455 #include <boost/bind.hpp>
456 #include <NuxCore/Logger.h>
457 #include <NuxCore/LoggingWriter.h>
458-#include <xpathselect/node.h>
459-#include <xpathselect/xpathselect.h>
460-#include <dlfcn.h>
461
462 #include "DebugDBusInterface.h"
463 #include "Introspectable.h"
464+#include "XPathQueryPart.h"
465
466 namespace unity
467 {
468@@ -47,149 +45,9 @@
469 namespace local
470 {
471 std::ofstream output_file;
472-
473- class IntrospectableAdapter: public xpathselect::Node
474- {
475- public:
476- typedef std::shared_ptr<IntrospectableAdapter> Ptr;
477- IntrospectableAdapter(Introspectable* node)
478- : node_(node)
479- {}
480-
481- std::string GetName() const
482- {
483- return node_->GetName();
484- }
485-
486- bool MatchProperty(const std::string& name, const std::string& value) const
487- {
488- bool matches = false;
489-
490- GVariantBuilder child_builder;
491- g_variant_builder_init(&child_builder, G_VARIANT_TYPE("a{sv}"));
492- g_variant_builder_add(&child_builder, "{sv}", "id", g_variant_new_uint64(node_->GetIntrospectionId()));
493- node_->AddProperties(&child_builder);
494- GVariant* prop_dict = g_variant_builder_end(&child_builder);
495- GVariant *prop_value = g_variant_lookup_value(prop_dict, name.c_str(), NULL);
496-
497- if (prop_value != NULL)
498- {
499- GVariantClass prop_val_type = g_variant_classify(prop_value);
500- // it'd be nice to be able to do all this with one method. However, the booleans need
501- // special treatment, and I can't figure out how to group all the integer types together
502- // without resorting to template functions.... and we all know what happens when you
503- // start doing that...
504- switch (prop_val_type)
505- {
506- case G_VARIANT_CLASS_STRING:
507- {
508- const gchar* prop_val = g_variant_get_string(prop_value, NULL);
509- if (g_strcmp0(prop_val, value.c_str()) == 0)
510- {
511- matches = true;
512- }
513- }
514- break;
515- case G_VARIANT_CLASS_BOOLEAN:
516- {
517- std::string value = boost::to_upper_copy(value);
518- bool p = value == "TRUE" ||
519- value == "ON" ||
520- value == "YES" ||
521- value == "1";
522- matches = (g_variant_get_boolean(prop_value) == p);
523- }
524- break;
525- case G_VARIANT_CLASS_BYTE:
526- {
527- // It would be nice if I could do all the integer types together, but I couldn't see how...
528- std::stringstream stream(value);
529- int val; // changing this to guchar causes problems.
530- stream >> val;
531- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
532- val == g_variant_get_byte(prop_value);
533- }
534- break;
535- case G_VARIANT_CLASS_INT16:
536- {
537- std::stringstream stream(value);
538- gint16 val;
539- stream >> val;
540- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
541- val == g_variant_get_int16(prop_value);
542- }
543- break;
544- case G_VARIANT_CLASS_UINT16:
545- {
546- std::stringstream stream(value);
547- guint16 val;
548- stream >> val;
549- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
550- val == g_variant_get_uint16(prop_value);
551- }
552- break;
553- case G_VARIANT_CLASS_INT32:
554- {
555- std::stringstream stream(value);
556- gint32 val;
557- stream >> val;
558- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
559- val == g_variant_get_int32(prop_value);
560- }
561- break;
562- case G_VARIANT_CLASS_UINT32:
563- {
564- std::stringstream stream(value);
565- guint32 val;
566- stream >> val;
567- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
568- val == g_variant_get_uint32(prop_value);
569- }
570- break;
571- case G_VARIANT_CLASS_INT64:
572- {
573- std::stringstream stream(value);
574- gint64 val;
575- stream >> val;
576- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
577- val == g_variant_get_int64(prop_value);
578- }
579- break;
580- case G_VARIANT_CLASS_UINT64:
581- {
582- std::stringstream stream(value);
583- guint64 val;
584- stream >> val;
585- matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
586- val == g_variant_get_uint64(prop_value);
587- }
588- break;
589- default:
590- LOG_WARNING(logger) << "Unable to match against property of unknown type.";
591- };
592- }
593- g_variant_unref(prop_value);
594- g_variant_unref(prop_dict);
595- return matches;
596- }
597-
598- std::vector<xpathselect::Node::Ptr> Children() const
599- {
600- std::vector<xpathselect::Node::Ptr> children;
601- for(auto child: node_->GetIntrospectableChildren())
602- {
603- children.push_back(std::make_shared<IntrospectableAdapter>(child));
604- }
605- return children;
606-
607- }
608-
609- Introspectable* node_;
610- };
611-}
612-}
613-
614-bool TryLoadXPathImplementation();
615+}
616+}
617+
618 GVariant* GetState(std::string const& query);
619 void StartLogToFile(std::string const& file_path);
620 void ResetLogging();
621@@ -363,40 +221,20 @@
622 }
623 }
624
625+
626 GVariant* GetState(std::string const& query)
627 {
628+ // process the XPath query:
629+ std::list<Introspectable*> parts = GetIntrospectableNodesFromQuery(query, _parent_introspectable);
630 GVariantBuilder builder;
631 g_variant_builder_init(&builder, G_VARIANT_TYPE("a(sv)"));
632-
633- // try load the xpathselect library:
634- void* driver = dlopen("libxpathselect.so", RTLD_LAZY);
635- if (driver)
636+ if (parts.empty())
637 {
638- typedef decltype(&xpathselect::SelectNodes) entry_t;
639- // clear errors:
640- dlerror();
641- entry_t entry_point = (entry_t) dlsym(driver, "SelectNodes");
642- const char* err = dlerror();
643- if (err)
644- {
645- LOG_ERROR(logger) << "Unable to load entry point in libxpathselect: " << err;
646- }
647- else
648- {
649- // process the XPath query:
650- local::IntrospectableAdapter::Ptr root_node = std::make_shared<local::IntrospectableAdapter>(_parent_introspectable);
651- auto nodes = entry_point(root_node, query);
652- for (auto n : nodes)
653- {
654- auto p = std::static_pointer_cast<local::IntrospectableAdapter>(n);
655- if (p)
656- g_variant_builder_add(&builder, "(sv)", p->node_->GetName().c_str(), p->node_->Introspect());
657- }
658- }
659+ LOG_WARNING(logger) << "Query '" << query << "' Did not match anything.";
660 }
661- else
662+ for (Introspectable *node : parts)
663 {
664- LOG_WARNING(logger) << "Cannot complete introspection request because libxpathselect is not installed.";
665+ g_variant_builder_add(&builder, "(sv)", node->GetName().c_str(), node->Introspect());
666 }
667
668 return g_variant_new("(a(sv))", &builder);
669@@ -436,5 +274,122 @@
670 }
671 }
672
673+/*
674+ * Do a breadth-first search of the introspection tree and find all nodes that match the
675+ * query.
676+ */
677+std::list<Introspectable*> GetIntrospectableNodesFromQuery(std::string const& query, Introspectable* tree_root)
678+{
679+ std::list<Introspectable*> start_points;
680+ std::string sanitised_query;
681+ // Allow user to be lazy when specifying root node.
682+ if (query == "" || query == "/")
683+ {
684+ sanitised_query = "/" + tree_root->GetName();
685+ }
686+ else
687+ {
688+ sanitised_query = query;
689+ }
690+ // split query into parts
691+ std::list<XPathQueryPart> query_parts;
692+
693+ {
694+ std::list<std::string> query_strings;
695+ boost::algorithm::split(query_strings, sanitised_query, boost::algorithm::is_any_of("/"));
696+ // Boost's split() implementation does not match it's documentation! According to the
697+ // docs, it's not supposed to add empty strings, but it does, which is a PITA. This
698+ // next line removes them:
699+ query_strings.erase( std::remove_if( query_strings.begin(),
700+ query_strings.end(),
701+ boost::bind( &std::string::empty, _1 ) ),
702+ query_strings.end());
703+ for (auto part : query_strings)
704+ {
705+ query_parts.push_back(XPathQueryPart(part));
706+ }
707+ }
708+
709+ // absolute or relative query string?
710+ if (sanitised_query.at(0) == '/' && sanitised_query.at(1) != '/')
711+ {
712+ // absolute query - start point is tree root node.
713+ if (query_parts.front().Matches(tree_root))
714+ {
715+ start_points.push_back(tree_root);
716+ }
717+ }
718+ else
719+ {
720+ // relative - need to do a depth first tree search for all nodes that match the
721+ // first node in the query.
722+
723+ // warn about malformed queries (all queries must start with '/')
724+ if (sanitised_query.at(0) != '/')
725+ {
726+ LOG_WARNING(logger) << "Malformed relative introspection query: '" << query << "'.";
727+ }
728+
729+ // non-recursive BFS traversal to find starting points:
730+ std::queue<Introspectable*> queue;
731+ queue.push(tree_root);
732+ while (!queue.empty())
733+ {
734+ Introspectable *node = queue.front();
735+ queue.pop();
736+ if (query_parts.front().Matches(node))
737+ {
738+ // found one. We keep going deeper, as there may be another node beneath this one
739+ // with the same node name.
740+ start_points.push_back(node);
741+ }
742+ // Add all children of current node to queue.
743+ for (Introspectable* child : node->GetIntrospectableChildren())
744+ {
745+ queue.push(child);
746+ }
747+ }
748+ }
749+
750+ // now we have the tree start points, process them:
751+ query_parts.pop_front();
752+ typedef std::pair<Introspectable*, std::list<XPathQueryPart>::iterator> node_match_pair;
753+
754+ std::queue<node_match_pair> traverse_queue;
755+ for (Introspectable *node : start_points)
756+ {
757+ traverse_queue.push(node_match_pair(node, query_parts.begin()));
758+ }
759+ start_points.clear();
760+
761+ while (!traverse_queue.empty())
762+ {
763+ node_match_pair p = traverse_queue.front();
764+ traverse_queue.pop();
765+
766+ Introspectable *node = p.first;
767+ auto query_it = p.second;
768+
769+ if (query_it == query_parts.end())
770+ {
771+ // found a match:
772+ start_points.push_back(node);
773+ }
774+ else
775+ {
776+ // push all children of current node to start of queue, advance search iterator, and loop again.
777+ for (Introspectable* child : node->GetIntrospectableChildren())
778+ {
779+ if (query_it->Matches(child))
780+ {
781+ auto it_copy(query_it);
782+ ++it_copy;
783+ traverse_queue.push(node_match_pair(child, it_copy));
784+ }
785+ }
786+ }
787+ }
788+ return start_points;
789+}
790 }
791 }
792
793=== modified file 'unity-shared/DebugDBusInterface.h'
794--- unity-shared/DebugDBusInterface.h 2013-01-24 04:53:37 +0000
795+++ unity-shared/DebugDBusInterface.h 2013-02-01 20:35:26 +0000
796@@ -31,6 +31,8 @@
797 namespace debug
798 {
799 class Introspectable;
800+std::list<Introspectable*> GetIntrospectableNodesFromQuery(std::string const& query, Introspectable *tree_root);
801+
802 class DebugDBusInterface
803 {
804 public:
805
806=== added file 'unity-shared/XPathQueryPart.cpp'
807--- unity-shared/XPathQueryPart.cpp 1970-01-01 00:00:00 +0000
808+++ unity-shared/XPathQueryPart.cpp 2013-02-01 20:35:26 +0000
809@@ -0,0 +1,186 @@
810+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
811+/*
812+ * Copyright (C) 2010 Canonical Ltd
813+ *
814+ * This program is free software: you can redistribute it and/or modify
815+ * it under the terms of the GNU General Public License version 3 as
816+ * published by the Free Software Foundation.
817+ *
818+ * This program is distributed in the hope that it will be useful,
819+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
820+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
821+ * GNU General Public License for more details.
822+ *
823+ * You should have received a copy of the GNU General Public License
824+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
825+ *
826+ * Authored by: Thomi Richards <thomi.richards@canonical.com>
827+ */
828+
829+#include <sstream>
830+#include <boost/algorithm/string.hpp>
831+#include <boost/algorithm/string/split.hpp>
832+#include <boost/algorithm/string/classification.hpp>
833+#include <boost/bind.hpp>
834+#include <NuxCore/Logger.h>
835+#include "XPathQueryPart.h"
836+#include "Introspectable.h"
837+
838+namespace unity
839+{
840+
841+namespace debug
842+{
843+DECLARE_LOGGER(logger, "unity.debug.xpath");
844+
845+// Stores a part of an XPath query.
846+XPathQueryPart::XPathQueryPart(std::string const& query_part)
847+{
848+ std::vector<std::string> part_pieces;
849+ boost::algorithm::split(part_pieces, query_part, boost::algorithm::is_any_of("[]="));
850+ // Boost's split() implementation does not match it's documentation! According to the
851+ // docs, it's not supposed to add empty strings, but it does, which is a PITA. This
852+ // next line removes them:
853+ part_pieces.erase( std::remove_if( part_pieces.begin(),
854+ part_pieces.end(),
855+ boost::bind( &std::string::empty, _1 ) ),
856+ part_pieces.end());
857+ if (part_pieces.size() == 1)
858+ {
859+ node_name_ = part_pieces.at(0);
860+ }
861+ else if (part_pieces.size() == 3)
862+ {
863+ node_name_ = part_pieces.at(0);
864+ param_name_ = part_pieces.at(1);
865+ param_value_ = part_pieces.at(2);
866+ }
867+ else
868+ {
869+ LOG_WARNING(logger) << "Malformed query part: " << query_part;
870+ // assume it's just a node name:
871+ node_name_ = query_part;
872+ }
873+}
874+
875+bool XPathQueryPart::Matches(Introspectable* node) const
876+{
877+ bool matches = false;
878+ if (param_name_ == "")
879+ {
880+ matches = (node_name_ == "*" || node->GetName() == node_name_);
881+ }
882+ else
883+ {
884+ GVariantBuilder child_builder;
885+ g_variant_builder_init(&child_builder, G_VARIANT_TYPE("a{sv}"));
886+ g_variant_builder_add(&child_builder, "{sv}", "id", g_variant_new_uint64(node->GetIntrospectionId()));
887+ node->AddProperties(&child_builder);
888+ GVariant* prop_dict = g_variant_builder_end(&child_builder);
889+ GVariant *prop_value = g_variant_lookup_value(prop_dict, param_name_.c_str(), NULL);
890+
891+ if (prop_value != NULL)
892+ {
893+ GVariantClass prop_val_type = g_variant_classify(prop_value);
894+ // it'd be nice to be able to do all this with one method. However, the booleans need
895+ // special treatment, and I can't figure out how to group all the integer types together
896+ // without resorting to template functions.... and we all know what happens when you
897+ // start doing that...
898+ switch (prop_val_type)
899+ {
900+ case G_VARIANT_CLASS_STRING:
901+ {
902+ const gchar* prop_val = g_variant_get_string(prop_value, NULL);
903+ if (g_strcmp0(prop_val, param_value_.c_str()) == 0)
904+ {
905+ matches = true;
906+ }
907+ }
908+ break;
909+ case G_VARIANT_CLASS_BOOLEAN:
910+ {
911+ std::string value = boost::to_upper_copy(param_value_);
912+ bool p = value == "TRUE" ||
913+ value == "ON" ||
914+ value == "YES" ||
915+ value == "1";
916+ matches = (g_variant_get_boolean(prop_value) == p);
917+ }
918+ break;
919+ case G_VARIANT_CLASS_BYTE:
920+ {
921+ // It would be nice if I could do all the integer types together, but I couldn't see how...
922+ std::stringstream stream(param_value_);
923+ int val; // changing this to guchar causes problems.
924+ stream >> val;
925+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
926+ val == g_variant_get_byte(prop_value);
927+ }
928+ break;
929+ case G_VARIANT_CLASS_INT16:
930+ {
931+ std::stringstream stream(param_value_);
932+ gint16 val;
933+ stream >> val;
934+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
935+ val == g_variant_get_int16(prop_value);
936+ }
937+ break;
938+ case G_VARIANT_CLASS_UINT16:
939+ {
940+ std::stringstream stream(param_value_);
941+ guint16 val;
942+ stream >> val;
943+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
944+ val == g_variant_get_uint16(prop_value);
945+ }
946+ break;
947+ case G_VARIANT_CLASS_INT32:
948+ {
949+ std::stringstream stream(param_value_);
950+ gint32 val;
951+ stream >> val;
952+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
953+ val == g_variant_get_int32(prop_value);
954+ }
955+ break;
956+ case G_VARIANT_CLASS_UINT32:
957+ {
958+ std::stringstream stream(param_value_);
959+ guint32 val;
960+ stream >> val;
961+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
962+ val == g_variant_get_uint32(prop_value);
963+ }
964+ break;
965+ case G_VARIANT_CLASS_INT64:
966+ {
967+ std::stringstream stream(param_value_);
968+ gint64 val;
969+ stream >> val;
970+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
971+ val == g_variant_get_int64(prop_value);
972+ }
973+ break;
974+ case G_VARIANT_CLASS_UINT64:
975+ {
976+ std::stringstream stream(param_value_);
977+ guint64 val;
978+ stream >> val;
979+ matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 &&
980+ val == g_variant_get_uint64(prop_value);
981+ }
982+ break;
983+ default:
984+ LOG_WARNING(logger) << "Unable to match against property of unknown type.";
985+ };
986+ }
987+ g_variant_unref(prop_value);
988+ g_variant_unref(prop_dict);
989+ }
990+
991+ return matches;
992+}
993+
994+}
995+}
996
997=== added file 'unity-shared/XPathQueryPart.h'
998--- unity-shared/XPathQueryPart.h 1970-01-01 00:00:00 +0000
999+++ unity-shared/XPathQueryPart.h 2013-02-01 20:35:26 +0000
1000@@ -0,0 +1,45 @@
1001+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
1002+/*
1003+ * Copyright (C) 2010 Canonical Ltd
1004+ *
1005+ * This program is free software: you can redistribute it and/or modify
1006+ * it under the terms of the GNU General Public License version 3 as
1007+ * published by the Free Software Foundation.
1008+ *
1009+ * This program is distributed in the hope that it will be useful,
1010+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1011+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1012+ * GNU General Public License for more details.
1013+ *
1014+ * You should have received a copy of the GNU General Public License
1015+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1016+ *
1017+ * Authored by: Thomi Richards <thomi.richards@canonical.com>
1018+ */
1019+
1020+#include <string>
1021+
1022+#ifndef _XPATH_QUERY_PART
1023+#define _XPATH_QUERY_PART
1024+
1025+namespace unity
1026+{
1027+namespace debug
1028+{
1029+ class Introspectable;
1030+ // Stores a part of an XPath query.
1031+ class XPathQueryPart
1032+ {
1033+ public:
1034+ XPathQueryPart(std::string const& query_part);
1035+ bool Matches(Introspectable* node) const;
1036+ private:
1037+ std::string node_name_;
1038+ std::string param_name_;
1039+ std::string param_value_;
1040+ };
1041+
1042+}
1043+}
1044+
1045+#endif