diff options
| -rw-r--r-- | debian/control | 11 | ||||
| -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 | 304 | ||||
| -rw-r--r-- | unity-shared/DebugDBusInterface.h | 1 | ||||
| -rw-r--r-- | unity-shared/XPathQueryPart.cpp | 186 | ||||
| -rw-r--r-- | unity-shared/XPathQueryPart.h | 45 |
8 files changed, 185 insertions, 720 deletions
diff --git a/debian/control b/debian/control index 550384a1e..cba288189 100644 --- a/debian/control +++ b/debian/control @@ -165,6 +165,7 @@ Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python-windowmocker, + libxpathselect (>= 1.3), 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 @@ -183,7 +184,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. @@ -191,7 +192,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. @@ -199,7 +200,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. @@ -207,7 +208,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. @@ -215,7 +216,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 04fd3dd35..e5219d3af 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -146,7 +146,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 b4078c418..de923de86 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -65,7 +65,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 8d6888433..2792c6d31 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 { @@ -44,9 +46,158 @@ namespace namespace local { std::ofstream output_file; + + class IntrospectableAdapter: public xpathselect::Node + { + public: + typedef std::shared_ptr<IntrospectableAdapter> Ptr; + IntrospectableAdapter(Introspectable* node, std::string const& parent_path) + : node_(node) + { + full_path_ = parent_path + "/" + GetName(); + } + + std::string GetName() const + { + return node_->GetName(); + } + + std::string GetPath() const + { + return full_path_; + } + + 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, GetPath() )); + } + return children; + + } + + Introspectable* node_; + private: + std::string full_path_; + }; } } +bool TryLoadXPathImplementation(); GVariant* GetState(std::string const& query); void StartLogToFile(std::string const& file_path); void ResetLogging(); @@ -155,20 +306,40 @@ GVariant* DebugDBusInterface::HandleDBusMethodCall(std::string const& method, GV return nullptr; } - 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, std::string()); + 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->GetPath().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); @@ -208,122 +379,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 99dbb1751..59fd06268 100644 --- a/unity-shared/DebugDBusInterface.h +++ b/unity-shared/DebugDBusInterface.h @@ -33,7 +33,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 { 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 |
