autopilot-gtk-1.4+15.10.20150826/0000755000015300001610000000000012567421566016534 5ustar pbuserpbgroup00000000000000autopilot-gtk-1.4+15.10.20150826/lib/0000755000015300001610000000000012567421566017302 5ustar pbuserpbgroup00000000000000autopilot-gtk-1.4+15.10.20150826/lib/GtkRootNode.cpp0000644000015300001610000000603112567421316022176 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 . * * Authored by: Allan LeSage */ #include "GtkRootNode.h" #include #include #include #include #include "Variant.h" GtkRootNode::GtkRootNode() : GtkNode(NULL) { } GVariant* GtkRootNode::Introspect() const { GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); variant::BuilderWrapper builder_wrapper(&builder); // add our unique autopilot-id builder_wrapper.add(AP_ID_NAME.c_str(), GetId()); // add the names of our children builder_wrapper.add("Children", GetChildNodeNames()); return g_variant_builder_end(&builder); } int32_t GtkRootNode::GetId() const { return 1; } std::string GtkRootNode::GetName() const { return "Root"; } std::string GtkRootNode::GetPath() const { return "/" + GetName(); } bool GtkRootNode::MatchIntegerProperty(const std::string& name, int32_t value) const { // Root node only matches one property - id: if (name == "id") return value == GetId(); return false; } bool GtkRootNode::MatchBooleanProperty(const std::string& name, bool value) const { return false; } bool GtkRootNode::MatchStringProperty(const std::string& name, const std::string& value) const { return false; } xpathselect::NodeVector GtkRootNode::Children() const { //g_debug("getting the children of a node"); xpathselect::NodeVector children; // add all the toplevel nodes as children to the root node GList* toplevels_list = gtk_window_list_toplevels(); GList* elem; for (elem = toplevels_list; elem; elem = elem->next) { GObject *node = reinterpret_cast(elem->data); children.push_back(std::make_shared(node, shared_from_this())); // if the AtkObjects are available, expose the Atk hierarchy as well AtkObject *atk_object = gtk_widget_get_accessible(GTK_WIDGET(node)); if (atk_object != NULL) children.push_back(std::make_shared(G_OBJECT(atk_object), shared_from_this())); } g_list_free(toplevels_list); return children; } GVariant* GtkRootNode::GetChildNodeNames() const { //g_debug("getting the names of a node's children"); GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_STRING_ARRAY); for (xpathselect::Node::Ptr child : Children()) { g_variant_builder_add(&builder, "s", child->GetName().c_str()); } return g_variant_builder_end(&builder); } autopilot-gtk-1.4+15.10.20150826/lib/Variant.cpp0000644000015300001610000002001712567421316021403 0ustar pbuserpbgroup00000000000000// -*- 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 . * * Authored by: Tim Penhey */ #include #include #include "Variant.h" #include "autopilot_types.h" namespace variant { BuilderWrapper::BuilderWrapper(GVariantBuilder* builder) : builder_(builder) {} BuilderWrapper& BuilderWrapper::add(char const* name, bool value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_boolean(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, char const* value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); if (value) g_variant_builder_add(&b, "v", g_variant_new_string(value)); else g_variant_builder_add(&b, "v", g_variant_new_string("")); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, std::string const& value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_string(value.c_str())); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, int value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_int32(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, long int value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_int64(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, long long int value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_int64(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, unsigned int value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_uint32(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, long unsigned int value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_uint64(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, long long unsigned int value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_uint64(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, float value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_double(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, double value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", g_variant_new_double(value)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } BuilderWrapper& BuilderWrapper::add(char const* name, GVariant* value) { if (value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_PLAIN)); g_variant_builder_add(&b, "v", value); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); } return *this; } BuilderWrapper& BuilderWrapper::add_gvalue(char const* name, GValue* value) { switch (G_VALUE_TYPE(value)) { case G_TYPE_CHAR: { add(name, g_value_get_schar(value)); } break; case G_TYPE_UCHAR: { add(name, g_value_get_uchar(value)); } break; case G_TYPE_BOOLEAN: { add(name, (bool) g_value_get_boolean(value)); } break; case G_TYPE_INT: { add(name, g_value_get_int(value)); } break; case G_TYPE_UINT: { add(name, g_value_get_uint(value)); } break; case G_TYPE_LONG: { add(name, g_value_get_long(value)); } break; case G_TYPE_ULONG: { add(name, g_value_get_ulong(value)); } break; case G_TYPE_INT64: { add(name, g_value_get_int64(value)); } break; case G_TYPE_UINT64: { add(name, g_value_get_uint64(value)); } break; case G_TYPE_ENUM: { add(name, g_value_get_enum(value)); } break; case G_TYPE_FLAGS: { add(name, g_value_get_flags(value)); } break; case G_TYPE_FLOAT: { add(name, g_value_get_float(value)); } break; case G_TYPE_DOUBLE: { add(name, g_value_get_double(value)); } break; case G_TYPE_STRING: { add(name, g_value_get_string(value)); } break; case G_TYPE_POINTER: { add(name, g_value_get_pointer(value)); } break; case G_TYPE_BOXED: { add(name, g_value_get_boxed(value)); } break; case G_TYPE_PARAM: { add(name, g_value_get_param(value)); } break; case G_TYPE_OBJECT: { add(name, g_value_get_object(value)); } break; default: g_debug("unsupported type: %s", g_type_name(G_VALUE_TYPE(value))); {} break; } return *this; } BuilderWrapper& BuilderWrapper::BuilderWrapper::add(char const* name, GdkRectangle value) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("av")); g_variant_builder_add(&b, "v", g_variant_new_int32(TYPE_RECT)); g_variant_builder_add(&b, "v", g_variant_new_int32(value.x)); g_variant_builder_add(&b, "v", g_variant_new_int32(value.y)); g_variant_builder_add(&b, "v", g_variant_new_int32(value.width)); g_variant_builder_add(&b, "v", g_variant_new_int32(value.height)); g_variant_builder_add( builder_, "{sv}", name, g_variant_builder_end(&b) ); return *this; } } autopilot-gtk-1.4+15.10.20150826/lib/GtkNode.cpp0000644000015300001610000003431412567421316021337 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 . * * Authored by: Allan LeSage */ #include #include #include #include "GtkNode.h" #include "Variant.h" const std::string GtkNode::AP_ID_NAME = "id"; static guint32 cur_obj_id = 2; // start at 2 since 1 is reserved for the root node GtkNode::GtkNode(GObject* obj, GtkNode::Ptr const& parent) : object_(obj) , parent_(parent) { std::string parent_path = parent ? parent->GetPath() : ""; full_path_ = parent_path + "/" + GetName(); if (object_ != NULL) { g_object_ref(object_); GQuark OBJ_ID = g_quark_from_static_string("AUTOPILOT_OBJECT_ID"); gpointer val = g_object_get_qdata (object_, OBJ_ID); if (val == NULL) { g_object_set_qdata (object_, OBJ_ID, reinterpret_cast(cur_obj_id++)); } } } GtkNode::GtkNode(GObject* obj) : object_(obj) { full_path_ = "/" + GetName(); if (object_ != NULL) { g_object_ref(object_); GQuark OBJ_ID = g_quark_from_static_string("AUTOPILOT_OBJECT_ID"); gpointer val = g_object_get_qdata (object_, OBJ_ID); if (val == NULL) { g_object_set_qdata (object_, OBJ_ID, reinterpret_cast(cur_obj_id++)); } } } GtkNode::~GtkNode() { g_clear_object(&object_); } // we cannot represent GEnums, GFlags, etc. through D-BUS and autopilot's API, // so convert them to strings, ints, and other primitive types static void convert_value (GParamSpec *pspec, GValue *value) { if (G_VALUE_HOLDS_ENUM(value)) { GEnumValue *ev = g_enum_get_value(G_PARAM_SPEC_ENUM(pspec)->enum_class, g_value_get_enum(value)); if (ev != NULL) { //g_debug("attribute %s of type %s holds enum %s", g_param_spec_get_name(pspec), // g_type_name(pspec->value_type), ev->value_name); g_value_unset(value); *value = G_VALUE_INIT; g_value_init(value, G_TYPE_STRING); g_value_set_string(value, ev->value_name); } } // representing flags as strings is too unwieldy; let's just represent them // as integer if (G_VALUE_HOLDS_FLAGS(value)) { guint flags = g_value_get_flags(value); //g_debug("attribute %s of type %s holds flags %x", g_param_spec_get_name(pspec), // g_type_name(pspec->value_type), flags); g_value_unset(value); *value = G_VALUE_INIT; g_value_init(value, G_TYPE_UINT); g_value_set_uint(value, flags); } if (pspec->value_type == GTK_TYPE_TEXT_BUFFER) { GtkTextBuffer *buf = GTK_TEXT_BUFFER(g_value_get_object(value)); if (buf != NULL) { //g_debug("attribute %s of type %s holds GtkTextBuffer", g_param_spec_get_name(pspec), // g_type_name(pspec->value_type)); GtkTextIter start, end; gtk_text_buffer_get_start_iter(buf, &start); gtk_text_buffer_get_end_iter(buf, &end); gchar* text = gtk_text_iter_get_text(&start, &end); g_value_unset(value); *value = G_VALUE_INIT; g_value_init(value, G_TYPE_STRING); g_value_set_string(value, (text != NULL) ? text : ""); g_free(text); } } } GVariant* GtkNode::Introspect() const { GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); // add a GVariant of all our properties guint length; GParamSpec** properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(object_), &length); variant::BuilderWrapper builder_wrapper(&builder); for (uint i = 0; i < length; ++i) { GParamSpec* param_spec = properties[i]; // ATK's accessible-table-* properties generate "invalid property id" warnings if (g_str_has_prefix(g_param_spec_get_name(param_spec), "accessible-table-")) continue; // see Launchpad bug #1108155: GtkTreePath mis-casts while copying, actuates here in "root" property if (g_strcmp0(g_type_name(param_spec->value_type), "GtkTreePath") != 0) { // some properties are only writeable; some toxic nodes change their names (?) if (param_spec->flags & G_PARAM_READABLE) { GValue value = G_VALUE_INIT; g_value_init(&value, param_spec->value_type); g_object_get_property(object_, g_param_spec_get_name(param_spec), &value); convert_value(param_spec, &value); builder_wrapper.add_gvalue(param_spec->name, &value); g_value_unset(&value); //Free the memory acquired by the value object. Absence of this was causig the applications to crash. } } else { //g_debug("skipped %s of type GtkTreePath", g_param_spec_get_name(param_spec)); } } g_free(properties); // add our unique autopilot-id builder_wrapper.add(AP_ID_NAME.c_str(), GetId()); // add the names of our children builder_wrapper.add("Children", GetChildNodeNames()); // add the GtkBuilder name if (GTK_IS_BUILDABLE (object_)) builder_wrapper.add("BuilderName", gtk_buildable_get_name(GTK_BUILDABLE (object_))); // add the GlobalRect property: "I am a GtkWidget" edition if (GTK_IS_WIDGET(object_)) { // FIXME: we'd like to remove this duplication (to GetGlobalRect) GtkWidget *widget = GTK_WIDGET (object_); GdkWindow *gdk_window = gtk_widget_get_window(widget); if (GDK_IS_WINDOW(gdk_window)) { GdkRectangle rect; GetGlobalRect(&rect); builder_wrapper.add("globalRect", rect); } } else if (ATK_IS_COMPONENT(object_)) { AddAtkComponentProperties(builder_wrapper, ATK_COMPONENT(object_)); } return g_variant_builder_end(&builder); } void GtkNode::AddAtkComponentProperties(variant::BuilderWrapper &builder_wrapper, AtkComponent *atk_component) const { AtkStateSet *states = atk_object_ref_state_set(ATK_OBJECT(atk_component)); /* Expose a few states which might be especially interesting for autopilot */ bool visible = atk_state_set_contains_state(states, ATK_STATE_VISIBLE); builder_wrapper.add("visible", visible); if (visible) { gint x, y, width, height; x = y = width = height = -1; atk_component_get_extents(atk_component, &x, &y, &width, &height, ATK_XY_SCREEN); GdkRectangle r; r.x = x; r.y = y; r.width = width; r.height = height; builder_wrapper.add("globalRect", r); } builder_wrapper.add("active", bool(atk_state_set_contains_state(states, ATK_STATE_ACTIVE))); builder_wrapper.add("checked", bool(atk_state_set_contains_state(states, ATK_STATE_CHECKED))); builder_wrapper.add("editable", bool(atk_state_set_contains_state(states, ATK_STATE_EDITABLE))); builder_wrapper.add("enabled", bool(atk_state_set_contains_state(states, ATK_STATE_ENABLED))); builder_wrapper.add("focused", bool(atk_state_set_contains_state(states, ATK_STATE_FOCUSED))); builder_wrapper.add("pressed", bool(atk_state_set_contains_state(states, ATK_STATE_PRESSED))); builder_wrapper.add("selected", bool(atk_state_set_contains_state(states, ATK_STATE_SELECTED))); builder_wrapper.add("sensitive", bool(atk_state_set_contains_state(states, ATK_STATE_SENSITIVE))); builder_wrapper.add("showing", bool(atk_state_set_contains_state(states, ATK_STATE_SHOWING))); g_object_unref(G_OBJECT(states)); } void GtkNode::GetGlobalRect(GdkRectangle* rect) const { GtkWidget *widget = GTK_WIDGET(object_); GdkWindow *gdk_window = gtk_widget_get_window(widget); GtkAllocation allocation; gint x, y; gtk_widget_get_allocation (widget, &allocation); gdk_window_get_root_coords(gdk_window, allocation.x, allocation.y, &x, &y); // if we wished to get the root root coords we might do this //gdk_window_get_root_coords(gdk_window, 0, 0, &x, &y); //g_debug ("root coords for widget %p(%s): %d %d (size %d %d)\n", widget, G_OBJECT_TYPE_NAME(widget), x, y, allocation.width, allocation.height); // FIXME: suck this up rect->x = x; rect->y = y; rect->width = allocation.width; rect->height = allocation.height; return; } std::string GtkNode::GetName() const { // autopilot uses the name of the GObject type if (!object_) { return std::string(); } return G_OBJECT_TYPE_NAME(object_); } std::string GtkNode::GetPath() const { return full_path_; } int32_t GtkNode::GetId() const { GQuark OBJ_ID = g_quark_from_static_string("AUTOPILOT_OBJECT_ID"); gpointer val = g_object_get_qdata (object_, OBJ_ID); // this uglyness is required in order to stop the compiler complaining about the fact // that we're casting a 64 bit type (gpointer) down to a 32 bit type (gint32) and may // be truncating the value. It's safe to do, however, since we control what values are // set in this quark, and they were initial gint32 values anyway. guint32 id = static_cast(reinterpret_cast(val)); return id; } xpathselect::Node::Ptr GtkNode::GetParent() const { return parent_; } bool GtkNode::MatchStringProperty(const std::string& name, const std::string& value) const { if (name == "BuilderName" && GTK_IS_BUILDABLE(object_)) { const gchar* builder_name = gtk_buildable_get_name(GTK_BUILDABLE (object_)); return builder_name != NULL && value.compare(builder_name) == 0; } GObjectClass* klass = G_OBJECT_GET_CLASS(object_); GParamSpec* pspec = g_object_class_find_property(klass, name.c_str()); if (pspec == NULL) return false; // read the property into a GValue g_debug("Matching property %s of type (%s).", g_param_spec_get_name(pspec), g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec))); GValue dest_value = G_VALUE_INIT; g_value_init(&dest_value, G_PARAM_SPEC_VALUE_TYPE(pspec)); g_object_get_property(object_, name.c_str(), &dest_value); convert_value(pspec, &dest_value); if (G_VALUE_TYPE(&dest_value) == G_TYPE_STRING) { const gchar *str = g_value_get_string(&dest_value); int result = g_strcmp0 (str, value.c_str()); g_value_unset(&dest_value); return result == 0; } else { g_debug("Property %s exists, but is not a string (is %s).", g_param_spec_get_name(pspec), g_type_name(G_VALUE_TYPE(&dest_value)) ); g_value_unset(&dest_value); return false; } } bool GtkNode::MatchIntegerProperty(const std::string& name, int32_t value) const { if (name == "id") return value == GetId(); GObjectClass* klass = G_OBJECT_GET_CLASS(object_); GParamSpec* pspec = g_object_class_find_property(klass, name.c_str()); if (pspec == NULL) return false; // read the property into a GValue g_debug("Matching property %s of type (%s).", g_param_spec_get_name(pspec), g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec))); GValue dest_value = G_VALUE_INIT; g_value_init(&dest_value, G_PARAM_SPEC_VALUE_TYPE(pspec)); g_object_get_property(object_, name.c_str(), &dest_value); convert_value(pspec, &dest_value); if (G_VALUE_TYPE(&dest_value) == G_TYPE_INT) { int v = g_value_get_int(&dest_value); g_value_unset(&dest_value); return value == v; } else if (G_VALUE_TYPE(&dest_value) == G_TYPE_UINT) { int v = g_value_get_uint(&dest_value); g_value_unset(&dest_value); return value == v; } else { g_debug("Property %s exists, but is not an integer (is %s).", g_param_spec_get_name(pspec), g_type_name(G_VALUE_TYPE(&dest_value)) ); g_value_unset(&dest_value); return false; } } bool GtkNode::MatchBooleanProperty(const std::string& name, bool value) const { GObjectClass* klass = G_OBJECT_GET_CLASS(object_); GParamSpec* pspec = g_object_class_find_property(klass, name.c_str()); if (pspec == NULL) return false; // read the property into a GValue g_debug("Matching property %s of type (%s).", g_param_spec_get_name(pspec), g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec))); GValue dest_value = G_VALUE_INIT; g_value_init(&dest_value, G_PARAM_SPEC_VALUE_TYPE(pspec)); g_object_get_property(object_, name.c_str(), &dest_value); convert_value(pspec, &dest_value); if (G_VALUE_TYPE(&dest_value) == G_TYPE_BOOLEAN) { bool v = g_value_get_boolean(&dest_value); g_value_unset(&dest_value); return value == v; } else { g_debug("Property %s exists, but is not a boolean (is %s).", g_param_spec_get_name(pspec), g_type_name(G_VALUE_TYPE(&dest_value)) ); g_value_unset(&dest_value); return false; } } xpathselect::NodeVector GtkNode::Children() const { //g_debug("getting the children of a node"); xpathselect::NodeVector children; if (GTK_IS_CONTAINER(object_)) { GList* gtk_children = gtk_container_get_children(GTK_CONTAINER(object_)); for (GList* elem = gtk_children; elem; elem = elem->next) { children.push_back(std::make_shared(G_OBJECT(elem->data), shared_from_this())); } g_list_free(gtk_children); } else if (ATK_IS_OBJECT(object_)) { AtkObject *atk_object = ATK_OBJECT(object_); int n_children = atk_object_get_n_accessible_children(atk_object); for (int i = 0; i < n_children; i++) { AtkObject *child = atk_object_ref_accessible_child(atk_object, i); children.push_back(std::make_shared(G_OBJECT(child), shared_from_this())); } } return children; } GVariant* GtkNode::GetChildNodeNames() const { //g_debug("getting the names of a node's children"); GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_STRING_ARRAY); for (xpathselect::Node::Ptr child : Children()) { g_variant_builder_add(&builder, "s", child->GetName().c_str()); } return g_variant_builder_end(&builder); } autopilot-gtk-1.4+15.10.20150826/lib/main.h0000644000015300001610000000153112567421316020370 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 . * * Authored by: Allan LeSage */ #ifndef MAIN_H #define MAIN_H extern "C" { int gtk_module_init(gint argc, char *argv[]); } #endif // MAIN_H autopilot-gtk-1.4+15.10.20150826/lib/Introspection.cpp0000644000015300001610000001041112567421316022634 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 . * * Authored by: Allan LeSage */ #include "Introspection.h" #include #include #include #include #include #include "GtkNode.h" #include "GtkRootNode.h" std::string AUTOPILOT_INTROSPECTION_OBJECT_PATH = "/com/canonical/Autopilot/Introspection"; void bus_acquired (GObject *object, GAsyncResult * res, gpointer user_data) { //g_debug("bus acquired"); GDBusConnection *bus; GError *error = NULL; bus = g_bus_get_finish (res, &error); if (!bus) { //g_warning ("unable to connect to the session bus: %s", error->message); g_error_free (error); return; } g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (autopilot_introspection), bus, AUTOPILOT_INTROSPECTION_OBJECT_PATH.c_str(), &error); if (error) { //g_warning ("unable to export autopilot introspection service on dbus: %s", error->message); g_error_free (error); return; } g_signal_connect (autopilot_introspection, "handle-get-state", G_CALLBACK(handle_get_state), NULL); g_signal_connect (autopilot_introspection, "handle-get-version", G_CALLBACK(handle_get_version), NULL); g_object_unref (bus); } gboolean handle_get_state (AutopilotIntrospection* introspection_service, GDBusMethodInvocation* invocation, const gchar * arg, gpointer user_data) { //g_debug("handling get-state method call"); GVariant* state; state = Introspect(arg); autopilot_introspection_complete_get_state(introspection_service, invocation, state); return TRUE; } gboolean handle_get_version (AutopilotIntrospection *introspection_service, GDBusMethodInvocation *invocation) { autopilot_introspection_complete_get_version(introspection_service, invocation, WIRE_PROTO_VERSION.c_str()); return TRUE; } GVariant* Introspect(std::string const& query_string) { //g_debug("introspecting our current GTK+ context"); GVariantBuilder* builder = g_variant_builder_new(G_VARIANT_TYPE("a(sv)")); std::list node_list = GetNodesThatMatchQuery(query_string); for (auto node: node_list) { std::string object_path = node->GetPath(); g_variant_builder_add(builder, "(sv)", object_path.c_str(), node->Introspect()); //g_debug("dumped object '%s'", object_path.c_str()); } GVariant* state = g_variant_new("a(sv)", builder); g_variant_builder_unref(builder); return state; } std::list GetNodesThatMatchQuery(std::string const& query_string) { //g_debug("getting nodes that match query"); std::shared_ptr root = std::make_shared(); //g_debug("selecting nodes"); std::list node_list; xpathselect::NodeVector selected_nodes_list; selected_nodes_list = xpathselect::SelectNodes(root, query_string); //g_debug("finished selecting nodes"); for (auto node : selected_nodes_list) { // node may be our root node wrapper *or* an ordinary GObject wrapper auto object_ptr = std::static_pointer_cast(node); if (object_ptr) node_list.push_back(object_ptr); } return node_list; } autopilot-gtk-1.4+15.10.20150826/lib/IntrospectionService.xml0000644000015300001610000000073312567421316024201 0ustar pbuserpbgroup00000000000000 autopilot-gtk-1.4+15.10.20150826/lib/main.cpp0000644000015300001610000000604112567421316020724 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 . * * Authored by: Allan LeSage */ #include #include #include #include #include #include "main.h" #include "Introspection.h" #include "IntrospectionService.h" namespace { std::string filename; GLogLevelFlags levels_to_log; std::ostream& get_log_stream() { if (!filename.empty()) { static std::ofstream fstream(filename); return fstream; } else { return std::cout; } } std::string get_level_name(GLogLevelFlags lvl) { switch(lvl) { case G_LOG_LEVEL_DEBUG: return "DEBUG"; case G_LOG_LEVEL_INFO: return "INFO"; case G_LOG_LEVEL_MESSAGE: return "MESSAGE"; case G_LOG_LEVEL_WARNING: return "WARNING"; case G_LOG_LEVEL_CRITICAL: return "CRITICAL"; case G_LOG_LEVEL_ERROR: return "ERROR"; default: return "UNKNOWN"; } } } AutopilotIntrospection* autopilot_introspection = NULL; void LogHandler (const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data) { if (log_level & levels_to_log) { std::string domain = log_domain ? log_domain : "default"; get_log_stream() << "[" << domain << "] " << get_level_name(log_level) << ": " << message << std::endl; } } void initialise_logging() { if (getenv("AP_GTK_LOG_VERBOSE")) { levels_to_log = (GLogLevelFlags) ( G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION); } else { levels_to_log = (GLogLevelFlags)( G_LOG_LEVEL_WARNING | G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION); } char* fname = getenv("AP_GTK_LOG_FILE"); if (fname && strcmp(fname, "") != 0) { filename = fname; } g_log_set_default_handler(LogHandler, NULL); } int gtk_module_init(gint argc, char *argv[]) { initialise_logging(); autopilot_introspection = autopilot_introspection_skeleton_new (); g_bus_get (G_BUS_TYPE_SESSION, NULL, bus_acquired, NULL); // always log this: std::cout << "Autopilot GTK interface loaded. Wire protocol version is " << WIRE_PROTO_VERSION << "." << std::endl; return 0; } int display_init_func(GdkDisplay* display) { // FIXME: module fails to load if this is not defined, but is it necessary? return 0; } autopilot-gtk-1.4+15.10.20150826/lib/Variant.h0000644000015300001610000000350612567421316021054 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 . * * Authored by: Tim Penhey */ #ifndef VARIANT_H #define VARIANT_H #include #include #include #include namespace variant { class BuilderWrapper { public: BuilderWrapper(GVariantBuilder* builder); BuilderWrapper& add(char const* name, bool value); BuilderWrapper& add(char const* name, char const* value); BuilderWrapper& add(char const* name, std::string const& value); BuilderWrapper& add(char const* name, int value); BuilderWrapper& add(char const* name, long int value); BuilderWrapper& add(char const* name, long long int value); BuilderWrapper& add(char const* name, unsigned int value); BuilderWrapper& add(char const* name, long unsigned int value); BuilderWrapper& add(char const* name, long long unsigned int value); BuilderWrapper& add(char const* name, float value); BuilderWrapper& add(char const* name, double value); BuilderWrapper& add(char const* name, GVariant* value); BuilderWrapper& add_gvalue(char const* name, GValue* value); BuilderWrapper& add(char const* name, GdkRectangle value); private: GVariantBuilder* builder_; }; } #endif // VARIANT_H autopilot-gtk-1.4+15.10.20150826/lib/GtkRootNode.h0000644000015300001610000000271012567421316021643 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 . * * Authored by: Allan LeSage */ #ifndef ROOTGTKNODE_H #define ROOTGTKNODE_H #include "GtkNode.h" #include #include #include class GtkRootNode: public GtkNode { public: GtkRootNode(); virtual GVariant* Introspect() const; virtual std::string GetName() const; virtual std::string GetPath() const; virtual int32_t GetId() const; virtual bool MatchIntegerProperty(const std::string& name, int32_t value) const; virtual bool MatchBooleanProperty(const std::string& name, bool value) const; virtual bool MatchStringProperty(const std::string& name, const std::string& value) const; virtual xpathselect::NodeVector Children() const; private: virtual GVariant* GetChildNodeNames() const; }; #endif // ROOTGTKNODE_H autopilot-gtk-1.4+15.10.20150826/lib/autopilot_types.h0000644000015300001610000000210412567421316022705 0ustar pbuserpbgroup00000000000000/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- * Copyright (C) 2013 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 . * */ #ifndef AUTOPILOT_TYPES_H #define AUTOPILOT_TYPES_H /// IMPORTANT: THese constants are taken from the autopilot XPathSelect protocol document. /// Only add options here if the support has been added for them in autopilot itself. enum autopilot_type_id { TYPE_PLAIN = 0, TYPE_RECT = 1, TYPE_POINT = 2, TYPE_SIZE = 3, TYPE_COLOR = 4, TYPE_DATETIME = 5, TYPE_TIME = 6, }; #endif autopilot-gtk-1.4+15.10.20150826/lib/GtkNode.h0000644000015300001610000000425512567421316021005 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 . * * Authored by: Allan LeSage */ #ifndef GTKNODE_H #define GTKNODE_H #include #include #include #include #include #include #include "Variant.h" // #include class GtkNode: public xpathselect::Node, public std::enable_shared_from_this { public: typedef std::shared_ptr Ptr; GtkNode(GObject* object, Ptr const& parent); GtkNode(GObject* object); virtual ~GtkNode(); virtual GVariant* Introspect() const; virtual std::string GetName() const; virtual std::string GetPath() const; virtual int32_t GetId() const; virtual xpathselect::Node::Ptr GetParent() const; virtual bool MatchStringProperty(const std::string& name, const std::string& value) const; virtual bool MatchIntegerProperty(const std::string& name, int32_t value) const; virtual bool MatchBooleanProperty(const std::string& name, bool value) const; virtual xpathselect::NodeVector Children() const; static const std::string AP_ID_NAME; private: GObject *object_; std::string full_path_; Ptr parent_; virtual GVariant* GetChildNodeNames() const; virtual void GetGlobalRect(GdkRectangle* rect) const; void AddAtkComponentProperties(variant::BuilderWrapper &builder_wrapper, AtkComponent *atk_component) const; }; #endif // GTKNODE_H autopilot-gtk-1.4+15.10.20150826/lib/CMakeLists.txt0000644000015300001610000000241712567421316022037 0ustar pbuserpbgroup00000000000000 set(SOURCES main.cpp Variant.cpp GtkNode.cpp Introspection.cpp IntrospectionService.c GtkRootNode.cpp) set(HEADERS main.h GtkNode.h GtkRootNode.h Introspection.h IntrospectionService.h Variant.h) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Werror -Wl,--no-undefined") add_library(autopilot SHARED ${SOURCES}) pkg_check_modules(GLIB REQUIRED glib-2.0) pkg_check_modules(GTK REQUIRED gtk+-3.0) pkg_check_modules(XPATHSELECT REQUIRED xpathselect) include_directories(${GLIB_INCLUDE_DIRS} ${GTK_INCLUDE_DIRS} ${XPATHSELECT_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) link_directories(${GLIB_LIBRARY_DIRS}) target_link_libraries(autopilot ${GLIB_LIBRARIES} ${GTK_LIBRARIES} ${XPATHSELECT_LIBRARIES}) add_custom_command( OUTPUT IntrospectionService.c IntrospectionService.h COMMAND gdbus-codegen ARGS --generate-c-code IntrospectionService --interface-prefix com.canonical ${CMAKE_CURRENT_SOURCE_DIR}/IntrospectionService.xml DEPENDS IntrospectionService.xml ) add_custom_target(generate_introspection_service DEPENDS IntrospectionService.xml) add_dependencies(autopilot generate_introspection_service) execute_process(COMMAND ln -s . lib/modules) install(TARGETS autopilot LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/gtk-3.0/modules) autopilot-gtk-1.4+15.10.20150826/lib/Introspection.h0000644000015300001610000000316512567421316022311 0ustar pbuserpbgroup00000000000000// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 . * * Authored by: Allan LeSage */ #ifndef INTROSPECTION_H #define INTROSPECTION_H #include #include #include "GtkNode.h" #include "IntrospectionService.h" extern AutopilotIntrospection* autopilot_introspection; static std::string WIRE_PROTO_VERSION("1.4"); void bus_acquired (GObject *object, GAsyncResult * res, gpointer user_data); gboolean handle_get_state (AutopilotIntrospection* introspection_service, GDBusMethodInvocation* invocation, const gchar * arg, gpointer user_data); gboolean handle_get_version (AutopilotIntrospection *introspection_service, GDBusMethodInvocation *invocation); GVariant* Introspect(std::string const& query_string); std::list GetNodesThatMatchQuery(std::string const& query_string); #endif // INTROSPECTION_H autopilot-gtk-1.4+15.10.20150826/tests/0000755000015300001610000000000012567421566017676 5ustar pbuserpbgroup00000000000000autopilot-gtk-1.4+15.10.20150826/tests/autopilot/0000755000015300001610000000000012567421566021716 5ustar pbuserpbgroup00000000000000autopilot-gtk-1.4+15.10.20150826/tests/autopilot/tests/0000755000015300001610000000000012567421566023060 5ustar pbuserpbgroup00000000000000autopilot-gtk-1.4+15.10.20150826/tests/autopilot/tests/test_widget_tree.py0000644000015300001610000001542512567421316026773 0ustar pbuserpbgroup00000000000000# blackbox testing of autopilot API against our hello_color.py test GTK program # Author: Martin Pitt # Copyright (C) 2013 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 . import os.path import unittest from autopilot.introspection.dbus import StateNotFoundError from autopilot.testcase import AutopilotTestCase from testtools.matchers import raises tests_dir = os.path.dirname(os.path.dirname(os.path.dirname( os.path.realpath(__file__)))) test_app = os.path.join(tests_dir, 'hello_color.py') class WidgetTreeTest(AutopilotTestCase): """Widget tree iteration and search""" def setUp(self): super(WidgetTreeTest, self).setUp() self.app = self.launch_test_application(test_app, app_type='gtk') def test_get_children_recursive(self): """Recursive get_children() This should not crash, and deliver valid widgets. """ widgets = set() self._get_widgets(self.app, widgets) for c in widgets: self.assertIn('.Gtk', str(type(c))) self.assertGreaterEqual(c.id, 0) # uncomment this to get a dump of all widgets and properties #print(type(c)) #for p in c.get_properties(): # print ' ', p, repr(getattr(c, p)) def test_get_children_by_type(self): # multiple instances res = self.app.get_children_by_type('GtkWindow') self.assertGreaterEqual(len(res), 3) self.assertIn('.GtkWindow', str(type(res[0]))) # one qualified instance res = self.app.get_children_by_type('GtkWindow', Children=['GtkBox']) self.assertGreaterEqual(len(res), 1) # no instances self.assertEqual(self.app.get_children_by_type('GtkTable'), []) def test_select_single_unique(self): """select_single() on widget types with only one instance""" for wtype in ('GtkMenuBar', 'GtkAboutDialog'): w = self.app.select_single(wtype) self.assertIn('.' + wtype, str(type(w))) def test_select_single_nonunique(self): """select_single() on widget types with multiple instances""" # we have more than one instance of these for wtype in ('GtkButton', 'GtkEntry'): self.assertRaises(ValueError, self.app.select_single, wtype) # we have no instances of these for wtype in ('GtkTable', 'GtkRadioButton'): self.assertThat( lambda: self.app.select_single(wtype), raises(StateNotFoundError) ) # qualified: visible property is not unique self.assertRaises(ValueError, self.app.select_single, 'GtkButton', visible=True) # qualified: label property is unique within GtkButton w = self.app.select_single('GtkButton', label='gtk-quit') self.assertIn('.GtkButton', str(type(w))) self.assertEqual(w.label, 'gtk-quit') def test_select_single_noclass(self): """select_single() without specifying a class""" # gtk-delete label is unique to our Button w = self.app.select_single(label='gtk-delete') self.assertIn('.GtkButton', str(type(w))) self.assertEqual(w.label, 'gtk-delete') # gtk-quit label is not unique globally, it's also a menu item self.assertRaises(ValueError, self.app.select_single, label='gtk-quit') # ... but it is unique for focussable widgets (menus don't allow that) w = self.app.select_single(label='gtk-quit', can_focus=True) self.assertIn('.GtkButton', str(type(w))) self.assertEqual(w.label, 'gtk-quit') def test_select_many_string(self): """select_many() with string properties""" # by class, unqualified, multiple instances res = self.app.select_many('GtkButton') # we have three in our main window, plus some in the about dialog self.assertGreaterEqual(len(res), 3) self.assertIn('.GtkButton', str(type(res[0]))) # .. but exactly three in the main window main_window = self.app.select_single('GtkWindow', Children=['GtkBox'], visible=True) res = main_window.select_many('GtkButton') self.assertEqual(len(res), 3) # by class, unqualified, single instance res = self.app.select_many('GtkMenuBar') self.assertEqual(len(res), 1) self.assertIn('.GtkMenuBar', str(type(res[0]))) # by class, unqualified, no instance res = self.app.select_many('GtkTable') self.assertEqual(res, []) # by class, qualified res = self.app.select_many('GtkButton', label='Greet') self.assertEqual(len(res), 1) self.assertIn('.GtkButton', str(type(res[0]))) self.assertEqual(res[0].label, 'Greet') # untyped res = self.app.select_many(label='gtk-delete') self.assertEqual(len(res), 1) self.assertIn('.GtkButton', str(type(res[0]))) self.assertEqual(res[0].label, 'gtk-delete') res = self.app.select_many(label='gtk-quit') # button and menu item self.assertEqual(len(res), 2) def test_select_int(self): """select_*() with int properties""" # with class res = self.app.select_many('GtkButtonBox', border_width=5) self.assertEqual(len(res), 1) self.assertNotEqual(self.app.select_single('GtkButtonBox', border_width=5), None) # without class res = self.app.select_many(border_width=5) self.assertGreater(len(res), 2) self.assertNotEqual(self.app.select_single(border_width=2), None) def test_select_bool(self): """select_*() with boolean properties""" # with class res = self.app.select_many('GtkButton', visible=True) self.assertGreater(len(res), 2) res = self.app.select_many('GtkAboutDialog', visible=False) self.assertGreater(len(res), 0) # without class res = self.app.select_many(visible=True) self.assertGreater(len(res), 5) res = self.app.select_many(visible=False) self.assertGreater(len(res), 4) @classmethod def _get_widgets(klass, obj, widget_set): """Recursively add all children of obj to widget_set""" for c in obj.get_children(): widget_set.add(c) klass._get_widgets(c, widget_set) autopilot-gtk-1.4+15.10.20150826/tests/autopilot/tests/test_actions.py0000644000015300001610000001532012567421324026122 0ustar pbuserpbgroup00000000000000# blackbox testing of autopilot API against our hello_color.py test GTK program # Author: Martin Pitt # Copyright (C) 2013 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 . import os.path import os from autopilot.testcase import AutopilotTestCase from autopilot.introspection.dbus import StateNotFoundError from autopilot.matchers import Eventually from testtools.matchers import Equals, NotEquals, raises tests_dir = os.path.dirname(os.path.dirname(os.path.dirname( os.path.realpath(__file__)))) test_app = os.path.join(tests_dir, 'hello_color.py') class ActionsTest(AutopilotTestCase): """Test performing actions in the UI and verify results""" def setUp(self): super(ActionsTest, self).setUp() # we want to test the position of GtkMenuItems here, disable global menubar os.environ['UBUNTU_MENUPROXY'] = '0' self.app = self.launch_test_application(test_app, app_type='gtk') def test_greeting_keyboard(self): """Greeting with keyboard navigation""" entry_name = self.app.select_single(BuilderName='entry_name') entry_color = self.app.select_single(BuilderName='entry_color') # FIXME: This isn't necessary for real X, but under Xvfb there is no # default focus sometimes if not entry_name.has_focus: self.mouse.click_object(entry_name) # type in name and color self.keyboard.type('Joe') self.keyboard.press_and_release('Tab') self.keyboard.type('red') # entries should now have the typed text self.assertThat(entry_name.text, Eventually(Equals('Joe'))) self.assertThat(entry_color.text, Eventually(Equals('red'))) # should not have any dialogs self.assertThat( lambda: self.app.select_single('GtkMessageDialog'), raises(StateNotFoundError) ) # focus and activate the "Greet" button self.keyboard.press_and_release('Tab') self.keyboard.press_and_release('Enter') # should get the greeting dialog md = self.app.wait_select_single('GtkMessageDialog', visible=True) # we expect the message dialog to show the corresponding greeting self.assertNotEqual(md.select_single('GtkLabel', label=u'Hello Joe, you like red.'), None) # close the dialog self.keyboard.press_and_release('Enter') self.assertThat( lambda: self.app.select_single('GtkMessageDialog', visible=True), raises(StateNotFoundError) ) def test_greeting_mouse(self): """Greeting with mouse navigation""" entry_name = self.app.select_single(BuilderName='entry_name') entry_color = self.app.select_single(BuilderName='entry_color') # FIXME: This isn't necessary for real X, but under Xvfb there is no # default focus sometimes if not entry_name.has_focus: self.mouse.click_object(entry_name) # type in name and color self.keyboard.type('Joe') self.mouse.click_object(entry_color) self.keyboard.type('blue') # entries should now have the typed text self.assertThat(entry_name.text, Eventually(Equals('Joe'))) self.assertThat(entry_color.text, Eventually(Equals('blue'))) # should not have any dialogs self.assertThat( lambda: self.app.select_single('GtkMessageDialog'), raises(StateNotFoundError) ) # focus and activate the "Greet" button btn = self.app.select_single('GtkButton', label='Greet') self.assertNotEqual(btn, None) self.mouse.click_object(btn) # should get the greeting dialog md = self.app.wait_select_single('GtkMessageDialog', visible=True) # we expect the message dialog to show the corresponding greeting self.assertNotEqual(md.select_single('GtkLabel', label=u'Hello Joe, you like blue.'), None) # close the dialog btn = md.wait_select_single('GtkButton', visible=True) self.mouse.click_object(btn) # We don't have Eventually(raises(... support, so wait until the second # assert should be okay to test. self.assertThat(md.visible, Eventually(Equals(False))) self.assertThat( lambda: self.app.select_single('GtkMessageDialog', visible=True), raises(StateNotFoundError) ) def test_clear(self): """Using Clear button with mouse""" # type in name and color self.keyboard.type('Joe') self.keyboard.press_and_release('Tab') self.keyboard.type('blue') # clear btn = self.app.select_single('GtkButton', label='gtk-delete') self.mouse.click_object(btn) # entries should be clear now entries = self.app.select_many('GtkEntry') self.assertEqual(len(entries), 2) for e in entries: self.assertThat(e.text, Eventually(Equals(''))) def test_menu(self): """Browse the menu""" file_menu = self.app.select_single('GtkMenuItem', label='_File') help_menu = self.app.select_single('GtkMenuItem', label='_Help') self.assertNotEqual(file_menu, None) self.assertNotEqual(help_menu, None) # the top-level menus should be visible and thus have a rect for m in (file_menu, help_menu): self.assertGreaterEqual(m.globalRect[0], 0) self.assertGreaterEqual(m.globalRect[1], 0) self.assertGreater(m.globalRect[2], 0) self.assertGreater(m.globalRect[3], 0) # the submenus are not visible by default m = self.app.select_single('GtkImageMenuItem', label='gtk-open') self.assertFalse(hasattr(m, 'globalRect')) # after opening, submenus should become visible self.mouse.click_object(file_menu) m = self.app.wait_select_single('GtkImageMenuItem', label='gtk-open', visible=True) self.assertGreaterEqual(m.globalRect[0], 0) self.assertGreaterEqual(m.globalRect[1], 0) self.assertGreater(m.globalRect[2], 0) self.assertGreater(m.globalRect[3], 0) autopilot-gtk-1.4+15.10.20150826/tests/autopilot/tests/__init__.py0000644000015300001610000000000012567421316025150 0ustar pbuserpbgroup00000000000000autopilot-gtk-1.4+15.10.20150826/tests/autopilot/tests/test_xpath_query.py0000644000015300001610000001030012567421316027025 0ustar pbuserpbgroup00000000000000# blackbox testing of autopilot API against our hello_color.py test GTK program # Author: Martin Pitt # Copyright (C) 2013 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 . import os.path import unittest from autopilot.testcase import AutopilotTestCase tests_dir = os.path.dirname(os.path.dirname(os.path.dirname( os.path.realpath(__file__)))) test_app = os.path.join(tests_dir, 'hello_color.py') class XPathQueryTest(AutopilotTestCase): """XPath queries""" def setUp(self): super(XPathQueryTest, self).setUp() self.app = self.launch_test_application(test_app, app_type='gtk') def xtest_have_path(self): """All children have a unique path""" widgets = set() self._get_widgets(self.app, widgets) seen_paths = set() for widget in widgets: path = widget.get_class_query_string() self.assertNotIn(path, seen_paths) seen_paths.add(path) # we can resolve the path back to the widget state = self.app.get_state_by_path(path) # state is an array with one (path, props) element props = state[0][1] self.assertEqual(props['id'], widget.id) self.assertEqual(props['visible'], widget.visible) def xtest_select_full_path(self): """Select widgets with full XPath""" # three buttons in main dialog's ButtonBox state = self.app.get_state_by_path('/Root/GtkWindow/GtkBox/GtkButtonBox/GtkButton') self.assertEqual(len(state), 3) labels = [str(props[1]['label']) for props in state] labels.sort() self.assertEqual(labels, ['Greet', 'gtk-delete', 'gtk-quit']) # select button with particular label for l in ['Greet', 'gtk-delete', 'gtk-quit']: state = self.app.get_state_by_path('/Root/GtkWindow/GtkBox/GtkButtonBox/GtkButton[label=%s]' % l) self.assertEqual(len(state), 1) self.assertEqual(state[0][1]['label'], l) def xtest_select_path_pattern(self): """Select widgets with XPath path pattern""" # three buttons in main dialog's ButtonBox state = self.app.get_state_by_path('//GtkWindow//GtkButton') self.assertEqual(len(state), 3) labels = [str(props[1]['label']) for props in state] labels.sort() self.assertEqual(labels, ['Greet', 'gtk-delete', 'gtk-quit']) # at least four buttons in the whole tree state = self.app.get_state_by_path('/Root//GtkButton') self.assertGreaterEqual(len(state), 4) def test_select_by_attribute(self): """Select widgets with attribute pattern""" state = self.app.get_state_by_path('//*[label="gtk-delete"]') self.assertEqual(len(state), 1, state) self.assertEqual(state[0][1]['label'], [0, 'gtk-delete']) self.assertTrue(state[0][0].endswith('/GtkButton'), state[0][0]) # https://launchpad.net/bugs/1179806 # TODO: Make this pass! @unittest.expectedFailure def test_select_by_attribute_spaces(self): """Select widgets with attribute pattern containing spaces""" for state_str in ('//*[label="Hello\\x20Color!"]', '//*[label="Hello Color!"]'): state = self.app.get_state_by_path(state_str) self.assertEqual(len(state), 1, str(state)) self.assertEqual(state[0][1]['label'], 'Hello Color!') self.assertTrue(state[0][0].endswith('/GtkLabel'), state[0][0]) @classmethod def _get_widgets(klass, obj, widget_set): """Recursively add all children of obj to widget_set""" for c in obj.get_children(): widget_set.add(c) klass._get_widgets(c, widget_set) autopilot-gtk-1.4+15.10.20150826/tests/autopilot/tests/test_properties.py0000644000015300001610000001277012567421316026665 0ustar pbuserpbgroup00000000000000# blackbox testing of autopilot API against our hello_color.py test GTK program # Author: Martin Pitt # Copyright (C) 2013 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 . import os.path import unittest from autopilot.testcase import AutopilotTestCase tests_dir = os.path.dirname(os.path.dirname(os.path.dirname( os.path.realpath(__file__)))) test_app = os.path.join(tests_dir, 'hello_color.py') class PropertyTest(AutopilotTestCase): """Widget properties""" def setUp(self): super(PropertyTest, self).setUp() self.app = self.launch_test_application(test_app, app_type='gtk') def test_gtk_builder_name(self): """GtkBuilder name lookup""" w = self.app.select_single(BuilderName='button_greet') self.assertNotEqual(w, None) self.assertEqual(w.label, 'Greet') w = self.app.select_single(BuilderName='button_quit') self.assertNotEqual(w, None) self.assertEqual(w.label, 'gtk-quit') w = self.app.select_single(BuilderName='entry_color') self.assertNotEqual(w, None) def test_button(self): """GtkButton properties""" btn_greet = self.app.select_single('GtkButton', label='Greet') self.assertNotEqual(btn_greet, None) btn_quit = self.app.select_single('GtkButton', label='gtk-quit') self.assertNotEqual(btn_quit, None) self.assertEqual(btn_greet.use_stock, False) self.assertEqual(btn_quit.use_stock, True) # only GtkButton, GtkFileChooserButton, and GtkComboBox have # focus-on-click, and we don't use the latter two self.assertEqual(btn_greet.focus_on_click, True) self.assertEqual(btn_quit.focus_on_click, True) # all buttons are visible and thus should have a rect self.assertTrue(btn_greet.visible) self.assertTrue(btn_quit.visible) self.assertEqual(len(btn_greet.globalRect), 4) self.assertEqual(len(btn_quit.globalRect), 4) # all our buttons have a GtkBuilder ID self.assertEqual(btn_greet.BuilderName, 'button_greet') self.assertEqual(btn_quit.BuilderName, 'button_quit') def test_entry(self): """GtkEntry properties""" entry_name = self.app.select_single(BuilderName='entry_name') entry_color = self.app.select_single(BuilderName='entry_color') self.assertTrue(entry_name.visible) self.assertTrue(entry_color.visible) # the entries should have the same size and x alignment self.assertEqual(entry_name.globalRect[0], entry_color.globalRect[0]) self.assertEqual(entry_name.globalRect[2:], entry_color.globalRect[2:]) # FIXME: This isn't necessary for real X, but under Xvfb there is no # default focus sometimes if not entry_name.has_focus: self.mouse.click_object(entry_name) # the color entry is below the name entry self.assertLess(entry_name.globalRect[1], entry_color.globalRect[1]) # first entry has default focus self.assertEqual(entry_name.has_focus, True) self.assertEqual(entry_color.has_focus, False) # both entries are empty by default self.assertEqual(entry_name.text, '') self.assertEqual(entry_color.text, '') # text-length is an unique property for GtkEntry self.assertEqual(entry_name.text_length, 0) self.assertEqual(entry_color.text_length, 0) def test_enum_flags_properties(self): '''enum and flags properties''' # enum btn_greet = self.app.select_single('GtkButton', label='Greet') self.assertEqual(btn_greet.relief, 'GTK_RELIEF_NORMAL') self.assertEqual(btn_greet.resize_mode, 'GTK_RESIZE_PARENT') res = self.app.select_many(relief='GTK_RELIEF_NORMAL', visible=True) self.assertGreaterEqual(len(res), 3) self.assertIn('Button', str(type(res[0]))) # flags self.assertGreaterEqual(btn_greet.events, 0) res = self.app.select_many('GtkButton', events=btn_greet.events) self.assertGreater(len(res), 0) def test_textview_properties(self): """GtkTextView properties""" t = self.app.select_single(BuilderName='textview_demo') self.assertNotEqual(t, None) self.assertEqual(t.editable, True) self.assertEqual(t.overwrite, False) # the buffer property points to a GtkTextBuffer object, which we want # to translate to a plain string self.assertEqual(t.buffer, 'This is a test application.') # select by buffer contents w = self.app.select_single(buffer='This is a test application.') self.assertEqual(w.BuilderName, 'textview_demo') def test_stress(self): """Query lots widgets in a tight loop""" for i in range(300): w = self.app.select_single(BuilderName='button_greet') self.assertEqual(w.label, 'Greet') w = self.app.select_single('GtkButton', label='gtk-quit') self.assertEqual(w.use_stock, True) autopilot-gtk-1.4+15.10.20150826/tests/autopilot/tests/test_gnome_app.py0000644000015300001610000000347612567421316026441 0ustar pbuserpbgroup00000000000000# blackbox testing of autopilot API against gnome-calculator # Author: Martin Pitt # Copyright (C) 2013 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 . from autopilot.testcase import AutopilotTestCase from autopilot.matchers import Eventually from testtools.matchers import Equals class GnomeAppTest(AutopilotTestCase): """Test autopilot against an actual GNOME application""" def setUp(self): super(GnomeAppTest, self).setUp() self.patch_environment('LANGUAGE', '') self.patch_environment('LANG', '') self.patch_environment('LC_MESSAGES', 'C') self.app = self.launch_test_application('gnome-calculator') def test_builder_button(self): """Find button by builder ID""" l = self.app.select_single(BuilderName='calc_result_button') self.assertNotEqual(l, None) self.assertEqual(l.visible, True) self.assertEqual(l.label, '=') def test_calc(self): """Run a calculation""" display = self.app.select_single(BuilderName='displayitem') self.mouse.click_object(display) self.assertThat(display.buffer, Equals('')) self.keyboard.type('1+1') self.keyboard.press_and_release('Enter') self.assertThat(display.buffer, Eventually(Equals('2'))) autopilot-gtk-1.4+15.10.20150826/tests/hello_color.py0000755000015300001610000000342212567421316022546 0ustar pbuserpbgroup00000000000000#!/usr/bin/python import sys import os.path from gi.repository import Gtk class HelloColorApp(Gtk.Application): def __init__(self): self.widgets = Gtk.Builder.new() self.widgets.add_from_file((os.path.join(os.path.dirname(sys.argv[0]), 'hello_color.ui'))) assert self.widgets.connect_signals(self) is None def run(self): self.widgets.get_object('window_app').show() Gtk.main() def on_quit(self, *args): Gtk.main_quit() def on_file_open(self, *args): md = Gtk.FileChooserDialog('Select a file..', parent=self.widgets.get_object('window_app'), buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) result = md.run() md.hide() if result == Gtk.ResponseType.OK: self.widgets.get_object('label_status').set_text('Loaded %s' % md.get_filenames()[0]) def on_button_greet(self, *args): name = self.widgets.get_object('entry_name').get_text() color = self.widgets.get_object('entry_color').get_text() md = Gtk.MessageDialog(message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.CLOSE, text='Hello %s, you like %s.' % (name, color)) md.run() md.hide() def on_button_clear(self, *args): self.widgets.get_object('entry_name').set_text('') self.widgets.get_object('entry_color').set_text('') self.widgets.get_object('label_status').set_text('') def on_about(self, *args): d = self.widgets.get_object('dialog_about') d.run() d.hide() if __name__ == '__main__': HelloColorApp().run() autopilot-gtk-1.4+15.10.20150826/tests/hello_color.ui0000644000015300001610000002742512567421316022541 0ustar pbuserpbgroup00000000000000 False 5 dialog Hello Color! (C) 2013 Canonical Ltd. Martin Pitt gpl-3-0 False TextView Demo True False True False textbuffer_demo This is a test application. False Hello Color True False vertical True False True False _File True True False gtk-open True False True True True False gtk-quit True False True True True False _Help True True False gtk-about True False True True False True 0 True False start 5 5 5 5 5 5 True False Name 0 0 1 1 True False Color 0 1 1 1 True True True 1 0 1 1 True True 1 1 1 1 True True 1 True False 0 5 10 False True 2 True False 10 True end Greet True True True False True 0 gtk-delete True True True True False True 1 gtk-quit True True True True right False True 2 False True 3 autopilot-gtk-1.4+15.10.20150826/CMakeLists.txt0000644000015300001610000000060412567421316021265 0ustar pbuserpbgroup00000000000000 cmake_minimum_required(VERSION 2.6) project(autopilot-gtk) include (GNUInstallDirs) set (VERSION 0.2) set (CMAKE_CXX_FLAGS "-DGNOME_DESKTOP_USE_UNSTABLE_API -std=c++11 -fno-permissive") INCLUDE(FindPkgConfig) add_subdirectory(lib) enable_testing() add_test(nose sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/tests/autopilot; GTK_PATH=${CMAKE_CURRENT_BINARY_DIR}/lib autopilot run -v tests")