diff options
| -rw-r--r-- | debian/control | 12 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/test_introspection.cpp | 356 | ||||
| -rw-r--r-- | unity-shared/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | unity-shared/DebugDBusInterface.cpp | 295 | ||||
| -rw-r--r-- | unity-shared/DebugDBusInterface.h | 2 | ||||
| -rw-r--r-- | unity-shared/XPathQueryPart.cpp | 186 | ||||
| -rw-r--r-- | unity-shared/XPathQueryPart.h | 45 | 
8 files changed, 177 insertions, 721 deletions
| diff --git a/debian/control b/debian/control index a0bbfb8cb..f3a0451c0 100644 --- a/debian/control +++ b/debian/control @@ -43,6 +43,7 @@ Build-Depends: cmake,  libxfixes-dev (>= 1:5.0-4ubuntu4),  libgtest-dev,  google-mock, + libxpathselect-dev,  Standards-Version: 3.9.3  Homepage: https://launchpad.net/unity  # If you aren't a member of ~unity-team but need to upload packaging changes, @@ -167,6 +168,7 @@ Section: python  Architecture: all  Depends: ${misc:Depends},  ${python:Depends}, + libxpathselect1.1,  Description: Autopiloted tests for Unity  We test Unity automatically through autopilot, a framework which enables us  to trigger keyboard and mouse events on the fly as well as introspecting @@ -185,7 +187,7 @@ Package: libunity-2d-private0  Depends: unity, ${misc:Depends}  Architecture: all  Section: oldlibs -Description: transitional dummy package  +Description: transitional dummy package  This is a transitional dummy package for unity-2d -> unity migration.  It can safely be removed. @@ -193,7 +195,7 @@ Package: libunity-2d-private-dev  Depends: unity, ${misc:Depends}  Architecture: all  Section: oldlibs -Description: transitional dummy package  +Description: transitional dummy package  This is a transitional dummy package for unity-2d -> unity migration.  It can safely be removed. @@ -201,7 +203,7 @@ Package: unity-2d-panel  Depends: unity, ${misc:Depends}  Architecture: all  Section: oldlibs -Description: transitional dummy package  +Description: transitional dummy package  This is a transitional dummy package for unity-2d -> unity migration.  It can safely be removed. @@ -209,7 +211,7 @@ Package: unity-2d-spread  Depends: unity, ${misc:Depends}  Architecture: all  Section: oldlibs -Description: transitional dummy package  +Description: transitional dummy package  This is a transitional dummy package for unity-2d -> unity migration.  It can safely be removed. @@ -217,7 +219,7 @@ Package: unity-2d-common  Depends: unity, ${misc:Depends}  Architecture: all  Section: oldlibs -Description: transitional dummy package  +Description: transitional dummy package  This is a transitional dummy package for unity-2d -> unity migration.  It can safely be removed. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f9c22b03f..9e44de072 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -112,7 +112,6 @@ if (GTEST_SRC_DIR AND  test_indicator_appmenu.cpp  test_indicator_entry.cpp  test_indicators.cpp - test_introspection.cpp  test_favorite_store.cpp  test_favorite_store_gsettings.cpp  test_favorite_store_private.cpp diff --git a/tests/test_introspection.cpp b/tests/test_introspection.cpp deleted file mode 100644 index 53b9f626a..000000000 --- a/tests/test_introspection.cpp +++ /dev/null @@ -1,356 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2010 Canonical Ltd - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authored by: Thomi Richards <thomi.richards@canonical.com> - */ -#include <gtest/gtest.h> -#include <glib.h> -#include <memory> -#include <boost/foreach.hpp> - -#include "Introspectable.h" -#include "DebugDBusInterface.h" - - -using namespace unity::debug; - -class MockIntrospectable : public Introspectable -{ -public: - MockIntrospectable(std::string const& name) - : name_(name) - {} - - std::string GetName() const - { -	return name_; - } - void AddProperties(GVariantBuilder* builder) - { - g_variant_builder_add (builder, "{sv}", "Name", g_variant_new_string (name_.c_str()) ); - g_variant_builder_add (builder, "{sv}", "SomeProperty", g_variant_new_string ("SomeValue") ); - g_variant_builder_add (builder, "{sv}", "BoolPropertyTrue", g_variant_new_boolean (TRUE) ); - g_variant_builder_add (builder, "{sv}", "BoolPropertyFalse", g_variant_new_boolean (FALSE) ); - // 8-bit integer types: - g_variant_builder_add (builder, "{sv}", "BytePropertyPos", g_variant_new_byte (12) ); - // 16-bit integer types: - g_variant_builder_add (builder, "{sv}", "Int16PropertyPos", g_variant_new_int16 (1012) ); - g_variant_builder_add (builder, "{sv}", "Int16PropertyNeg", g_variant_new_int16 (-1034) ); - g_variant_builder_add (builder, "{sv}", "UInt16PropertyPos", g_variant_new_uint16 (1056) ); - // 32-bit integer types: - g_variant_builder_add (builder, "{sv}", "Int32PropertyPos", g_variant_new_int32 (100012) ); - g_variant_builder_add (builder, "{sv}", "Int32PropertyNeg", g_variant_new_int32 (-100034) ); - g_variant_builder_add (builder, "{sv}", "UInt32PropertyPos", g_variant_new_uint32 (100056) ); - // 64-bit integer types - g_variant_builder_add (builder, "{sv}", "Int64PropertyPos", g_variant_new_int32 (100000012) ); - g_variant_builder_add (builder, "{sv}", "Int64PropertyNeg", g_variant_new_int32 (-100000034) ); - g_variant_builder_add (builder, "{sv}", "UInt64PropertyPos", g_variant_new_uint32 (100000056) ); - - } -private: - std::string name_; -}; - -class TestIntrospection : public ::testing::Test -{ -public: - TestIntrospection() - : root_(new MockIntrospectable("Unity")), - dc_(new MockIntrospectable("DashController")), - pc_(new MockIntrospectable("PanelController")), - foo1_(new MockIntrospectable("Foo")), - foo2_(new MockIntrospectable("Foo")), - foo3_(new MockIntrospectable("Foo")) - { - root_->AddChild(dc_.get()); - root_->AddChild(pc_.get()); - dc_->AddChild(foo1_.get()); - dc_->AddChild(foo2_.get()); - dc_->AddChild(foo3_.get()); - - //root_->SetProperty(g_variant_new("{sv}", "SomeProperty", g_variant_new_string("SomeValue"))); - } - -protected: - std::shared_ptr<MockIntrospectable> root_; - std::shared_ptr<MockIntrospectable> dc_; - std::shared_ptr<MockIntrospectable> pc_; - std::shared_ptr<MockIntrospectable> foo1_; - std::shared_ptr<MockIntrospectable> foo2_; - std::shared_ptr<MockIntrospectable> foo3_; - -}; - -TEST_F(TestIntrospection, TestTest) -{ - ASSERT_STREQ("Unity", root_->GetName().c_str()); -} - -TEST_F(TestIntrospection, TestVariousRootQueries) -{ - std::list<Introspectable*> results; - std::string query; - - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(1, results.size()); - EXPECT_STREQ("Unity", results.front()->GetName().c_str()); - - query = "/"; - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(1, results.size()); - EXPECT_STREQ("Unity", results.front()->GetName().c_str()); - - query = "/Unity"; - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(1, results.size()); - EXPECT_STREQ("Unity", results.front()->GetName().c_str()); -} - -TEST_F(TestIntrospection, TestAsteriskWildcard) -{ - std::list<Introspectable*> results; - std::string query = "/Unity/*"; - - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(2, results.size()); - - for(auto p : results) - { - ASSERT_TRUE( - p->GetName() == "DashController" || - p->GetName() == "PanelController" - ); - } -} - -TEST_F(TestIntrospection, TestRelativeAsteriskWildcard) -{ - std::list<Introspectable*> results; - std::string query = "//DashController/*"; - - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(3, results.size()); - - for(auto p : results) - { - ASSERT_TRUE(p->GetName() == "Foo"); - } -} - -TEST_F(TestIntrospection, TestAbsoluteQueries) -{ - std::list<Introspectable*> results; - std::string query = "/Unity/DashController"; - - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(1, results.size()); - EXPECT_STREQ("DashController", results.front()->GetName().c_str()); -} - -TEST_F(TestIntrospection, TestMalformedRelativeQueries) -{ - std::list<Introspectable*> results; - std::string query = "Unity"; - - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(1, results.size()); - EXPECT_STREQ("Unity", results.front()->GetName().c_str()); - - query = "Foo"; - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(3, results.size()); - for(auto p : results) - { - EXPECT_STREQ("Foo", p->GetName().c_str()); - } -} - -TEST_F(TestIntrospection, TestSimpleRelativeQueries) -{ - std::list<Introspectable*> results; - std::string query = "//Unity"; - - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(1, results.size()); - EXPECT_STREQ("Unity", results.front()->GetName().c_str()); - - query = "//Foo"; - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(3, results.size()); - for(auto p : results) - { - EXPECT_STREQ("Foo", p->GetName().c_str()); - } -} - -TEST_F(TestIntrospection, TestComplexRelativeQueries) -{ - std::list<Introspectable*> results; - std::string query = "//DashController/Foo"; - - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(3, results.size()); - for(auto p : results) - { - EXPECT_STREQ("Foo", p->GetName().c_str()); - } -} - -TEST_F(TestIntrospection, TestQueriesWithNoResults) -{ - std::list<Introspectable*> results; - std::string query = "//Does/Not/Exist"; - - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(0, results.size()); - - query = "DoesNotEverExist"; - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(0, results.size()); - - query = "/Does/Not/Ever/Exist"; - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(0, results.size()); -} - -TEST_F(TestIntrospection, TestQueriesWithParams) -{ - std::list<Introspectable*> results; - // this should find our root node: - results = GetIntrospectableNodesFromQuery("/Unity[SomeProperty=SomeValue]", root_.get()); - ASSERT_EQ(1, results.size()); - EXPECT_STREQ("Unity", results.front()->GetName().c_str()); - // but this should find nothing: - results = GetIntrospectableNodesFromQuery("/Unity[SomeProperty=SomeOtherValue]", root_.get()); - ASSERT_EQ(0, results.size()); - - // make sure relative paths work: - results = GetIntrospectableNodesFromQuery("//Foo[Name=Foo]", root_.get()); - ASSERT_EQ(3, results.size()); - for(auto p : results) - { - EXPECT_STREQ("Foo", p->GetName().c_str()); - } - - // make sure param queries work with descendant nodes as well: - results = GetIntrospectableNodesFromQuery("/Unity[SomeProperty=SomeValue]/DashController[Name=DashController]/Foo", root_.get()); - ASSERT_EQ(3, results.size()); - for(auto p : results) - { - EXPECT_STREQ("Foo", p->GetName().c_str()); - } -} - -TEST_F(TestIntrospection, TestQueryTypeBool) -{ - std::list<Introspectable*> results; - - // These are all equivilent and should return the root item and nothing more: - std::list<std::string> queries = {"/Unity[BoolPropertyTrue=True]", - "/Unity[BoolPropertyTrue=true]", - "/Unity[BoolPropertyTrue=trUE]", - "/Unity[BoolPropertyTrue=yes]", - "/Unity[BoolPropertyTrue=ON]", - "/Unity[BoolPropertyTrue=1]"}; - - for(auto query : queries) - { - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(1, results.size()); - EXPECT_STREQ("Unity", results.front()->GetName().c_str()); - } - - // For boolean properties, anything that's not True, Yes, On or 1 is treated as false: - queries = {"/Unity[BoolPropertyTrue=False]", - "/Unity[BoolPropertyTrue=fAlSE]", - "/Unity[BoolPropertyTrue=No]", - "/Unity[BoolPropertyTrue=OFF]", - "/Unity[BoolPropertyTrue=0]", - "/Unity[BoolPropertyTrue=ThereWasAManFromNantucket]"}; - for(auto query : queries) - { - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(0, results.size()); - } -} - -TEST_F(TestIntrospection, TestQueryTypeInt) -{ - std::list<Introspectable*> results; - - // these should all select the root Unity node: - std::list<std::string> queries = {"/Unity[BytePropertyPos=12]", - "/Unity[Int16PropertyPos=1012]", - "/Unity[Int16PropertyNeg=-1034]", - "/Unity[UInt16PropertyPos=1056]", - "/Unity[Int32PropertyPos=100012]", - "/Unity[Int32PropertyNeg=-100034]", - "/Unity[UInt32PropertyPos=100056]", - "/Unity[Int64PropertyPos=100000012]", - "/Unity[Int64PropertyNeg=-100000034]", - "/Unity[UInt64PropertyPos=100000056]"}; - for(auto query : queries) - { - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(1, results.size()) << "Failing query: " << query; - EXPECT_STREQ("Unity", results.front()->GetName().c_str()); - } - - // but these shouldn't: - queries = {"/Unity[BytePropertyPos=1234]", - "/Unity[Int16PropertyPos=0]", - "/Unity[Int16PropertyNeg=-0]", - "/Unity[Int16PropertyNeg=-]", - "/Unity[UInt16PropertyPos=-1056]", - "/Unity[Int32PropertyPos=999999999999999]", - "/Unity[Int32PropertyNeg=Garbage]", - "/Unity[UInt32PropertyPos=-23]"}; - for(auto query : queries) - { - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(0, results.size()); - } -} - -TEST_F(TestIntrospection, TestMalformedQueries) -{ - // this should work - we have not yet specified a parameter to test against. - std::list<Introspectable*> results = GetIntrospectableNodesFromQuery("/Unity[", root_.get()); - ASSERT_EQ(1, results.size()); - - std::list<std::string> queries = {"/Unity[BoolPropertyTrue", - "/Unity[BoolPropertyTrue=", - "/Unity[BoolPropertyTrue=]", - "/Unity[BytePropertyPos=", - "/Unity[BytePropertyPos=]", - "/Unity[Int16PropertyPos=", - "/Unity[Int16PropertyPos=]", - "/Unity[Int16PropertyNeg=", - "/Unity[Int16PropertyNeg=]", - "/Unity[UInt16PropertyPos[=]]", - "/Unity[Int32PropertyPos[[", - "/Unity[Int32PropertyNeg]", - "/Unity[UInt32PropertyPos=[", - "/Unity[Int64PropertyPos[[", - "/Unity[Int64PropertyNeg", - "/Unity[UInt64PropertyPos]"}; - - for (std::string query : queries) - { - results = GetIntrospectableNodesFromQuery(query, root_.get()); - ASSERT_EQ(0, results.size()) << "Failing query: " << query; - } -} diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index ead1ec981..3f02801d9 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -64,7 +64,6 @@ set (UNITY_SHARED_SOURCES  VScrollBarOverlayWindow.cpp  WindowButtons.cpp  WindowManager.cpp - XPathQueryPart.cpp  )  if(ENABLE_X_SUPPORT) diff --git a/unity-shared/DebugDBusInterface.cpp b/unity-shared/DebugDBusInterface.cpp index 7aedca53a..c95534297 100644 --- a/unity-shared/DebugDBusInterface.cpp +++ b/unity-shared/DebugDBusInterface.cpp @@ -28,10 +28,12 @@  #include <boost/bind.hpp>  #include <NuxCore/Logger.h>  #include <NuxCore/LoggingWriter.h> +#include <xpathselect/node.h> +#include <xpathselect/xpathselect.h> +#include <dlfcn.h>  #include "DebugDBusInterface.h"  #include "Introspectable.h" -#include "XPathQueryPart.h"  namespace unity  { @@ -45,9 +47,149 @@ namespace  namespace local  {  std::ofstream output_file; + + class IntrospectableAdapter: public xpathselect::Node + { + public: + typedef std::shared_ptr<IntrospectableAdapter> Ptr; + IntrospectableAdapter(Introspectable* node) + : node_(node) + {} + + std::string GetName() const + { + return node_->GetName(); + } + + bool MatchProperty(const std::string& name, const std::string& value) const + { + bool matches = false; + + GVariantBuilder child_builder; + g_variant_builder_init(&child_builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&child_builder, "{sv}", "id", g_variant_new_uint64(node_->GetIntrospectionId())); + node_->AddProperties(&child_builder); + GVariant* prop_dict = g_variant_builder_end(&child_builder); + GVariant *prop_value = g_variant_lookup_value(prop_dict, name.c_str(), NULL); + + if (prop_value != NULL) + { + GVariantClass prop_val_type = g_variant_classify(prop_value); + // it'd be nice to be able to do all this with one method. However, the booleans need + // special treatment, and I can't figure out how to group all the integer types together + // without resorting to template functions.... and we all know what happens when you + // start doing that... + switch (prop_val_type) + { + case G_VARIANT_CLASS_STRING: + { + const gchar* prop_val = g_variant_get_string(prop_value, NULL); + if (g_strcmp0(prop_val, value.c_str()) == 0) + { + matches = true; + } + } + break; + case G_VARIANT_CLASS_BOOLEAN: + { + std::string value = boost::to_upper_copy(value); + bool p = value == "TRUE" || + value == "ON" || + value == "YES" || + value == "1"; + matches = (g_variant_get_boolean(prop_value) == p); + } + break; + case G_VARIANT_CLASS_BYTE: + { + // It would be nice if I could do all the integer types together, but I couldn't see how... + std::stringstream stream(value); + int val; // changing this to guchar causes problems. + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_byte(prop_value); + } + break; + case G_VARIANT_CLASS_INT16: + { + std::stringstream stream(value); + gint16 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_int16(prop_value); + } + break; + case G_VARIANT_CLASS_UINT16: + { + std::stringstream stream(value); + guint16 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_uint16(prop_value); + } + break; + case G_VARIANT_CLASS_INT32: + { + std::stringstream stream(value); + gint32 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_int32(prop_value); + } + break; + case G_VARIANT_CLASS_UINT32: + { + std::stringstream stream(value); + guint32 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_uint32(prop_value); + } + break; + case G_VARIANT_CLASS_INT64: + { + std::stringstream stream(value); + gint64 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_int64(prop_value); + } + break; + case G_VARIANT_CLASS_UINT64: + { + std::stringstream stream(value); + guint64 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_uint64(prop_value); + } + break; + default: + LOG_WARNING(logger) << "Unable to match against property of unknown type."; + }; + } + g_variant_unref(prop_value); + g_variant_unref(prop_dict); + return matches; + } + + std::vector<xpathselect::Node::Ptr> Children() const + { + std::vector<xpathselect::Node::Ptr> children; + for(auto child: node_->GetIntrospectableChildren()) + { + children.push_back(std::make_shared<IntrospectableAdapter>(child)); + } + return children; + + } + + Introspectable* node_; + };  }  } +bool TryLoadXPathImplementation();  GVariant* GetState(std::string const& query);  void StartLogToFile(std::string const& file_path);  void ResetLogging(); @@ -221,20 +363,40 @@ DebugDBusInterface::HandleDBusMethodCall(GDBusConnection* connection,  }  } -  GVariant* GetState(std::string const& query)  { - // process the XPath query: - std::list<Introspectable*> parts = GetIntrospectableNodesFromQuery(query, _parent_introspectable);  GVariantBuilder builder;  g_variant_builder_init(&builder, G_VARIANT_TYPE("a(sv)")); - if (parts.empty()) + + // try load the xpathselect library: + void* driver = dlopen("libxpathselect.so", RTLD_LAZY); + if (driver)  { - LOG_WARNING(logger) << "Query '" << query << "' Did not match anything."; + typedef decltype(&xpathselect::SelectNodes) entry_t; + // clear errors: + dlerror(); + entry_t entry_point = (entry_t) dlsym(driver, "SelectNodes"); + const char* err = dlerror(); + if (err) + { + LOG_ERROR(logger) << "Unable to load entry point in libxpathselect: " << err; + } + else + { + // process the XPath query: + local::IntrospectableAdapter::Ptr root_node = std::make_shared<local::IntrospectableAdapter>(_parent_introspectable); + auto nodes = entry_point(root_node, query); + for (auto n : nodes) + { + auto p = std::static_pointer_cast<local::IntrospectableAdapter>(n); + if (p) + g_variant_builder_add(&builder, "(sv)", p->node_->GetName().c_str(), p->node_->Introspect()); + } + }  } - for (Introspectable *node : parts) + else  { - g_variant_builder_add(&builder, "(sv)", node->GetName().c_str(), node->Introspect()); + LOG_WARNING(logger) << "Cannot complete introspection request because libxpathselect is not installed.";  }  return g_variant_new("(a(sv))", &builder); @@ -274,122 +436,5 @@ void LogMessage(std::string const& severity,  }  } -/* - * Do a breadth-first search of the introspection tree and find all nodes that match the - * query. - */ -std::list<Introspectable*> GetIntrospectableNodesFromQuery(std::string const& query, Introspectable* tree_root) -{ - std::list<Introspectable*> start_points; - std::string sanitised_query; - // Allow user to be lazy when specifying root node. - if (query == "" || query == "/") - { - sanitised_query = "/" + tree_root->GetName(); - } - else - { - sanitised_query = query; - } - // split query into parts - std::list<XPathQueryPart> query_parts; - - { - std::list<std::string> query_strings; - boost::algorithm::split(query_strings, sanitised_query, boost::algorithm::is_any_of("/")); - // Boost's split() implementation does not match it's documentation! According to the - // docs, it's not supposed to add empty strings, but it does, which is a PITA. This - // next line removes them: - query_strings.erase( std::remove_if( query_strings.begin(), - query_strings.end(), - boost::bind( &std::string::empty, _1 ) ), - query_strings.end()); - for (auto part : query_strings) - { - query_parts.push_back(XPathQueryPart(part)); - } - } - - // absolute or relative query string? - if (sanitised_query.at(0) == '/' && sanitised_query.at(1) != '/') - { - // absolute query - start point is tree root node. - if (query_parts.front().Matches(tree_root)) - { - start_points.push_back(tree_root); - } - } - else - { - // relative - need to do a depth first tree search for all nodes that match the - // first node in the query. - - // warn about malformed queries (all queries must start with '/') - if (sanitised_query.at(0) != '/') - { - LOG_WARNING(logger) << "Malformed relative introspection query: '" << query << "'."; - } - - // non-recursive BFS traversal to find starting points: - std::queue<Introspectable*> queue; - queue.push(tree_root); - while (!queue.empty()) - { - Introspectable *node = queue.front(); - queue.pop(); - if (query_parts.front().Matches(node)) - { - // found one. We keep going deeper, as there may be another node beneath this one - // with the same node name. - start_points.push_back(node); - } - // Add all children of current node to queue. - for (Introspectable* child : node->GetIntrospectableChildren()) - { - queue.push(child); - } - } - } - - // now we have the tree start points, process them: - query_parts.pop_front(); - typedef std::pair<Introspectable*, std::list<XPathQueryPart>::iterator> node_match_pair; - - std::queue<node_match_pair> traverse_queue; - for (Introspectable *node : start_points) - { - traverse_queue.push(node_match_pair(node, query_parts.begin())); - } - start_points.clear(); - - while (!traverse_queue.empty()) - { - node_match_pair p = traverse_queue.front(); - traverse_queue.pop(); - - Introspectable *node = p.first; - auto query_it = p.second; - - if (query_it == query_parts.end()) - { - // found a match: - start_points.push_back(node); - } - else - { - // push all children of current node to start of queue, advance search iterator, and loop again. - for (Introspectable* child : node->GetIntrospectableChildren()) - { - if (query_it->Matches(child)) - { - auto it_copy(query_it); - ++it_copy; - traverse_queue.push(node_match_pair(child, it_copy)); - } - } - } - } - return start_points; -}  }  } diff --git a/unity-shared/DebugDBusInterface.h b/unity-shared/DebugDBusInterface.h index 3c11a5a0b..95c9c8098 100644 --- a/unity-shared/DebugDBusInterface.h +++ b/unity-shared/DebugDBusInterface.h @@ -31,8 +31,6 @@ extern const std::string DBUS_BUS_NAME;  namespace debug  {  class Introspectable; -std::list<Introspectable*> GetIntrospectableNodesFromQuery(std::string const& query, Introspectable *tree_root); -  class DebugDBusInterface  {  public: diff --git a/unity-shared/XPathQueryPart.cpp b/unity-shared/XPathQueryPart.cpp deleted file mode 100644 index 3608a50b4..000000000 --- a/unity-shared/XPathQueryPart.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2010 Canonical Ltd - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authored by: Thomi Richards <thomi.richards@canonical.com> - */ - -#include <sstream> -#include <boost/algorithm/string.hpp> -#include <boost/algorithm/string/split.hpp> -#include <boost/algorithm/string/classification.hpp> -#include <boost/bind.hpp> -#include <NuxCore/Logger.h> -#include "XPathQueryPart.h" -#include "Introspectable.h" - -namespace unity -{ - -namespace debug -{ -DECLARE_LOGGER(logger, "unity.debug.xpath"); - -// Stores a part of an XPath query. -XPathQueryPart::XPathQueryPart(std::string const& query_part) -{ - std::vector<std::string> part_pieces; - boost::algorithm::split(part_pieces, query_part, boost::algorithm::is_any_of("[]=")); - // Boost's split() implementation does not match it's documentation! According to the - // docs, it's not supposed to add empty strings, but it does, which is a PITA. This - // next line removes them: - part_pieces.erase( std::remove_if( part_pieces.begin(), - part_pieces.end(), - boost::bind( &std::string::empty, _1 ) ), - part_pieces.end()); - if (part_pieces.size() == 1) - { - node_name_ = part_pieces.at(0); - } - else if (part_pieces.size() == 3) - { - node_name_ = part_pieces.at(0); - param_name_ = part_pieces.at(1); - param_value_ = part_pieces.at(2); - } - else - { - LOG_WARNING(logger) << "Malformed query part: " << query_part; - // assume it's just a node name: - node_name_ = query_part; - } -} - -bool XPathQueryPart::Matches(Introspectable* node) const -{ - bool matches = false; - if (param_name_ == "") - { - matches = (node_name_ == "*" || node->GetName() == node_name_); - } - else - { - GVariantBuilder child_builder; - g_variant_builder_init(&child_builder, G_VARIANT_TYPE("a{sv}")); - g_variant_builder_add(&child_builder, "{sv}", "id", g_variant_new_uint64(node->GetIntrospectionId())); - node->AddProperties(&child_builder); - GVariant* prop_dict = g_variant_builder_end(&child_builder); - GVariant *prop_value = g_variant_lookup_value(prop_dict, param_name_.c_str(), NULL); - - if (prop_value != NULL) - { - GVariantClass prop_val_type = g_variant_classify(prop_value); - // it'd be nice to be able to do all this with one method. However, the booleans need - // special treatment, and I can't figure out how to group all the integer types together - // without resorting to template functions.... and we all know what happens when you - // start doing that... - switch (prop_val_type) - { - case G_VARIANT_CLASS_STRING: - { - const gchar* prop_val = g_variant_get_string(prop_value, NULL); - if (g_strcmp0(prop_val, param_value_.c_str()) == 0) - { - matches = true; - } - } - break; - case G_VARIANT_CLASS_BOOLEAN: - { - std::string value = boost::to_upper_copy(param_value_); - bool p = value == "TRUE" || - value == "ON" || - value == "YES" || - value == "1"; - matches = (g_variant_get_boolean(prop_value) == p); - } - break; - case G_VARIANT_CLASS_BYTE: - { - // It would be nice if I could do all the integer types together, but I couldn't see how... - std::stringstream stream(param_value_); - int val; // changing this to guchar causes problems. - stream >> val; - matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && - val == g_variant_get_byte(prop_value); - } - break; - case G_VARIANT_CLASS_INT16: - { - std::stringstream stream(param_value_); - gint16 val; - stream >> val; - matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && - val == g_variant_get_int16(prop_value); - } - break; - case G_VARIANT_CLASS_UINT16: - { - std::stringstream stream(param_value_); - guint16 val; - stream >> val; - matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && - val == g_variant_get_uint16(prop_value); - } - break; - case G_VARIANT_CLASS_INT32: - { - std::stringstream stream(param_value_); - gint32 val; - stream >> val; - matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && - val == g_variant_get_int32(prop_value); - } - break; - case G_VARIANT_CLASS_UINT32: - { - std::stringstream stream(param_value_); - guint32 val; - stream >> val; - matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && - val == g_variant_get_uint32(prop_value); - } - break; - case G_VARIANT_CLASS_INT64: - { - std::stringstream stream(param_value_); - gint64 val; - stream >> val; - matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && - val == g_variant_get_int64(prop_value); - } - break; - case G_VARIANT_CLASS_UINT64: - { - std::stringstream stream(param_value_); - guint64 val; - stream >> val; - matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && - val == g_variant_get_uint64(prop_value); - } - break; - default: - LOG_WARNING(logger) << "Unable to match against property of unknown type."; - }; - } - g_variant_unref(prop_value); - g_variant_unref(prop_dict); - } - - return matches; -} - -} -} diff --git a/unity-shared/XPathQueryPart.h b/unity-shared/XPathQueryPart.h deleted file mode 100644 index b5beb3f4f..000000000 --- a/unity-shared/XPathQueryPart.h +++ /dev/null @@ -1,45 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2010 Canonical Ltd - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * Authored by: Thomi Richards <thomi.richards@canonical.com> - */ - -#include <string> - -#ifndef _XPATH_QUERY_PART -#define _XPATH_QUERY_PART - -namespace unity -{ -namespace debug -{ - class Introspectable; - // Stores a part of an XPath query. - class XPathQueryPart - { - public: - XPathQueryPart(std::string const& query_part); - bool Matches(Introspectable* node) const; - private: - std::string node_name_; - std::string param_name_; - std::string param_value_; - }; - -} -} - -#endif | 
