Merge lp:~pitti/autopilot-gtk/enum-flags-properties into lp:autopilot-gtk

Proposed by Martin Pitt on 2013-06-27
Status: Merged
Approved by: Martin Pitt on 2013-06-27
Approved revision: 49
Merged at revision: 47
Proposed branch: lp:~pitti/autopilot-gtk/enum-flags-properties
Merge into: lp:autopilot-gtk
Diff against target: 174 lines (+86/-50)
2 files modified
lib/GtkNode.cpp (+71/-45)
tests/autopilot/tests/test_properties.py (+15/-5)
To merge this branch: bzr merge lp:~pitti/autopilot-gtk/enum-flags-properties
Reviewer Review Type Date Requested Status
Thomi Richards (community) 2013-06-27 Approve on 2013-06-27
PS Jenkins bot continuous-integration Approve on 2013-06-27
Review via email: mp+171713@code.launchpad.net

Commit message

Handle enum and flags properties. (LP: #1193342)

Description of the change

This branch enables enum and flag properties, such as GtkButton.relief or
GtkWidget.events. The first commit refactors the MatchProperty() method, to avoid having to introduce more duplicated code, and fix the GValue leak. After that, introducing support for new property types is a matter of only adding them to the convert_value() helper function, and then introspecting and matching will both support it.

To post a comment you must log in.
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/GtkNode.cpp'
2--- lib/GtkNode.cpp 2013-06-26 10:16:59 +0000
3+++ lib/GtkNode.cpp 2013-06-27 04:52:28 +0000
4@@ -37,6 +37,36 @@
5 g_clear_object(&object_);
6 }
7
8+// we cannot represent GEnums, GFlags, etc. through D-BUS and autopilot's API,
9+// so convert them to strings, ints, and other primitive types
10+static void convert_value (GParamSpec *pspec, GValue *value)
11+{
12+ if (G_VALUE_HOLDS_ENUM(value)) {
13+ GEnumValue *ev = g_enum_get_value(G_PARAM_SPEC_ENUM(pspec)->enum_class,
14+ g_value_get_enum(value));
15+ if (ev != NULL) {
16+ //g_debug("attribute %s of type %s holds enum %s", g_param_spec_get_name(pspec),
17+ // g_type_name(pspec->value_type), ev->value_name);
18+ g_value_unset(value);
19+ *value = G_VALUE_INIT;
20+ g_value_init(value, G_TYPE_STRING);
21+ g_value_set_string(value, ev->value_name);
22+ }
23+ }
24+
25+ // representing flags as strings is too unwieldy; let's just represent them
26+ // as integer
27+ if (G_VALUE_HOLDS_FLAGS(value)) {
28+ guint flags = g_value_get_flags(value);
29+ g_warning("attribute %s of type %s holds flags %x", g_param_spec_get_name(pspec),
30+ g_type_name(pspec->value_type), flags);
31+ g_value_unset(value);
32+ *value = G_VALUE_INIT;
33+ g_value_init(value, G_TYPE_UINT);
34+ g_value_set_uint(value, flags);
35+ }
36+}
37+
38 GVariant* GtkNode::Introspect() const
39 {
40 GVariantBuilder builder;
41@@ -59,6 +89,7 @@
42 GValue value = G_VALUE_INIT;
43 g_value_init(&value, param_spec->value_type);
44 g_object_get_property(object_, g_param_spec_get_name(param_spec), &value);
45+ convert_value(param_spec, &value);
46 builder_wrapper.add(param_spec->name, &value);
47 g_value_unset(&value); //Free the memory accquired by the value object. Absence of this was causig the applications to crash.
48 }
49@@ -181,55 +212,50 @@
50
51 bool GtkNode::MatchProperty(const std::string& name,
52 const std::string& value) const {
53-
54- // g_debug("attempting to match a node's property");
55-
56- if (name == "id") {
57+ if (name == "id")
58 return value == std::to_string(GetObjectId());
59
60- } else {
61- GObjectClass* klass = G_OBJECT_GET_CLASS(object_);
62- GParamSpec* pspec = g_object_class_find_property(klass, name.c_str());
63- if (pspec == NULL)
64+ GObjectClass* klass = G_OBJECT_GET_CLASS(object_);
65+ GParamSpec* pspec = g_object_class_find_property(klass, name.c_str());
66+ if (pspec == NULL)
67+ return false;
68+
69+ // read the property into a GValue
70+ g_debug("Matching property %s of type (%s).", g_param_spec_get_name(pspec),
71+ g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec)));
72+
73+ GValue dest_value = G_VALUE_INIT;
74+ g_value_init(&dest_value, G_PARAM_SPEC_VALUE_TYPE(pspec));
75+ g_object_get_property(object_, name.c_str(), &dest_value);
76+ convert_value(pspec, &dest_value);
77+ std::string dest_string;
78+
79+ // convert it to a string; always doing string comparison avoids having to do
80+ // type comparison, conversion and error handling on value
81+ switch (G_VALUE_TYPE(&dest_value)) {
82+ case G_TYPE_INT:
83+ dest_string = std::to_string(g_value_get_int(&dest_value));
84+ break;
85+ case G_TYPE_UINT:
86+ dest_string = std::to_string(g_value_get_uint(&dest_value));
87+ break;
88+ case G_TYPE_DOUBLE:
89+ dest_string = std::to_string(g_value_get_double(&dest_value));
90+ break;
91+ case G_TYPE_STRING: {
92+ const gchar *str = g_value_get_string(&dest_value);
93+ dest_string = (str != NULL) ? str : "";
94+ break;
95+ }
96+ default:
97+ g_debug("Unhandled type %s for matching property %s",
98+ g_type_name(G_VALUE_TYPE(&dest_value)), g_param_spec_get_name(pspec));
99+ g_value_unset(&dest_value);
100 return false;
101-
102- g_debug("Matching a property of type (%s).", g_type_name(G_PARAM_SPEC_VALUE_TYPE(pspec)));
103-
104- if (pspec && G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_INT) {
105- GValue dest_value = G_VALUE_INIT;
106- g_value_init(&dest_value, G_TYPE_INT);
107- g_object_get_property(object_, name.c_str(), &dest_value);
108-
109- const gint cint = g_value_get_int(&dest_value);
110- g_value_unset(&dest_value);
111- std::stringstream out;
112- out << cint;
113- std::string dest_string(out.str());
114- return dest_string == value;
115-
116- } else if (pspec && G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_DOUBLE) {
117- GValue dest_value = G_VALUE_INIT;
118- g_value_init(&dest_value, G_TYPE_DOUBLE);
119- g_object_get_property(object_, name.c_str(), &dest_value);
120-
121- const gdouble cdbl = g_value_get_double(&dest_value);
122- g_value_unset(&dest_value);
123- std::stringstream out;
124- out << cdbl;
125- std::string dest_string(out.str());
126- return dest_string == value;
127-
128- } else if (pspec && G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_STRING) {
129- gchar *strval = NULL;
130- g_object_get(object_, name.c_str(), &strval, NULL);
131- if (strval == NULL)
132- return false;
133-
134- std::string dest_string(strval);
135- return dest_string == value;
136- }
137- return false;
138 }
139+
140+ g_value_unset(&dest_value);
141+ return dest_string == value;
142 }
143
144 xpathselect::NodeList GtkNode::Children() const {
145
146=== modified file 'tests/autopilot/tests/test_properties.py'
147--- tests/autopilot/tests/test_properties.py 2013-06-26 07:21:14 +0000
148+++ tests/autopilot/tests/test_properties.py 2013-06-27 04:52:28 +0000
149@@ -90,10 +90,20 @@
150 self.assertEqual(entry_name.text_length, 0)
151 self.assertEqual(entry_color.text_length, 0)
152
153- #https://launchpad.net/bugs/1193342
154- @unittest.expectedFailure
155- def test_enum_properties(self):
156- '''enum properties'''
157+ def test_enum_flags_properties(self):
158+ '''enum and flags properties'''
159
160+ # enum
161 btn_greet = self.app.select_single('GtkButton', label='Greet')
162- self.assertTrue(hasattr(btn_greet, 'relief'))
163+ self.assertEqual(btn_greet.relief, 'GTK_RELIEF_NORMAL')
164+ self.assertEqual(btn_greet.resize_mode, 'GTK_RESIZE_PARENT')
165+
166+ res = self.app.select_many(relief='GTK_RELIEF_NORMAL', visible=True)
167+ self.assertGreaterEqual(len(res), 3)
168+ self.assertIn('Button', str(type(res[0])))
169+
170+ # flags
171+ self.assertGreaterEqual(btn_greet.events, 0)
172+
173+ res = self.app.select_many('GtkButton', events=btn_greet.events)
174+ self.assertGreater(len(res), 0)

Subscribers

People subscribed via source and target branches

to all changes: