Bug#711107: gtk+3.0: FTBFS on ia64 and mips
Stephan Schreiber
info at fs-driver.org
Sat Jun 15 14:43:52 UTC 2013
tags 711107 - help
tags 711107 + patch
thanks
Emilio Pozuelo Monfort<pochu at debian.org> wrote:
> What happens if you do `make check' or `fakeroot make check' again?
> make check
> will set Xvfb differently than xvfb-run.
I didn't check that out. I assume the test will always fail with 'make check'.
It doesn't matter what X server you are using, or how xvfb is set.
The test fails when gtester is invoked:
xvfb-run gtester -k --verbose children
fails, but
xvfb-run ./children
passes the test.
This is very interesting, but a misleading and useless information ;-).
The real problem is in the tests/a11y/children.c source code file
within the test_add_remove() function; it reads local variables
without initialization:
static void
test_add_remove (GtkWidget *widget)
{
AtkObject *accessible;
AtkObject *child_accessible;
SignalData add_data;
SignalData remove_data;
SignalData parent_data[3];
STATE state;
gint i, j;
gint step_children;
state.widget = widget;
accessible = gtk_widget_get_accessible (widget);
add_data.count = 0;
remove_data.count = 0;
g_signal_connect (accessible, "children_changed::add",
G_CALLBACK (children_changed), &add_data);
g_signal_connect (accessible, "children_changed::remove",
G_CALLBACK (children_changed), &remove_data);
step_children = atk_object_get_n_accessible_children (accessible);
for (i = 0; i < 3; i++)
{
if (!do_create_child (&state, i))
break;
if (!GTK_IS_ENTRY (widget))
{
parent_data[i].count = 0;
child_accessible = gtk_widget_get_accessible (state.child[i]);
g_signal_connect (child_accessible, "notify::accessible-parent",
G_CALLBACK (parent_notify), &(parent_data[i]));
gtk_container_add (GTK_CONTAINER (widget), state.child[i]);
}
else
child_accessible = atk_object_ref_accessible_child (accessible, i);
g_assert_cmpint (add_data.count, ==, i + 1);
g_assert_cmpint (add_data.n_children, ==, step_children + i + 1);
g_assert_cmpint (remove_data.count, ==, 0);
if (!GTK_IS_ENTRY (widget))
g_assert_cmpint (parent_data[i].count, ==, 1);
if (GTK_IS_SCROLLED_WINDOW (widget) ||
GTK_IS_NOTEBOOK (widget))
g_assert (atk_object_get_parent (ATK_OBJECT
(parent_data[i].parent)) == accessible);
else if (GTK_IS_ENTRY (widget))
g_assert (atk_object_get_parent (child_accessible) == accessible);
else
g_assert (parent_data[i].parent == accessible);
if (GTK_IS_ENTRY (widget))
g_object_unref (child_accessible);
}
for (j = 0 ; j < i; j++)
{
remove_child (&state, j);
g_assert_cmpint (add_data.count, ==, i);
g_assert_cmpint (remove_data.count, ==, j + 1);
g_assert_cmpint (remove_data.n_children, ==, step_children + i - j - 1);
if (parent_data[j].count == 2)
g_assert (parent_data[j].parent == NULL);
else if (!GTK_IS_ENTRY (widget))
{
AtkStateSet *set;
set = atk_object_ref_state_set (ATK_OBJECT (parent_data[j].parent));
g_assert (atk_state_set_contains_state (set, ATK_STATE_DEFUNCT));
g_object_unref (set);
}
}
g_signal_handlers_disconnect_by_func (accessible, G_CALLBACK
(children_changed), &add_data);
g_signal_handlers_disconnect_by_func (accessible, G_CALLBACK
(children_changed), &remove_data);
}
When you take a look at the main() function in children.c, you realize
that test_add_remove() is called multiple times; it gets via its
widget paramter object instances of different types.
Almost all object types are derived from GtkWidget or GtkContainer;
GTK_IS_ENTRY() evaluates to false for them.
The last type is GtkEntry (created by gtk_entry_new()); GTK_IS_ENTRY()
evaluates to true for it. This is the test we have the trouble with.
Please take a look at the fifth line within the function:
SignalData parent_data[3];
Let's assume that we are running the buggy test; widget is a GtkEntry
instance; GTK_IS_ENTRY (widget) is true.
The first time you see some code for parent_data[].count is in the
first for loop. But if GTK_IS_ENTRY (widget) is true, parent_data
isnt'touched at all. No pointer to parent_data is provided to
somewhere else:
if (!GTK_IS_ENTRY (widget))
{
parent_data[i].count = 0;
child_accessible = gtk_widget_get_accessible (state.child[i]);
g_signal_connect (child_accessible, "notify::accessible-parent",
G_CALLBACK (parent_notify), &(parent_data[i]));
...
}
else
child_accessible = atk_object_ref_accessible_child (accessible, i);
In the second for loop:
if (parent_data[j].count == 2)
g_assert (parent_data[j].parent == NULL);
else if (!GTK_IS_ENTRY (widget))
parent_data is read here without any preceding initialization. Thus,
we are running into trouble.
parent_data is not used at all on the GtkEntry case. It must not be in
any assertion in that case.
Second problem:
If we take a closer look at the entire if-else statement, we realize
that the block after the else-if statement is never executed. Dead code.
It isn't executed on the considered last test with the GtkEntry
instance since GTK_IS_ENTRY (widget) is true.
It isn't executed on the other tests with a GtkWidget or GtkContainer
instance, because on that tests the term parent_data[j].count is
*always* 2 (see below).
if (parent_data[j].count == 2)
g_assert (parent_data[j].parent == NULL);
else if (!GTK_IS_ENTRY (widget))
{
AtkStateSet *set;
set = atk_object_ref_state_set (ATK_OBJECT (parent_data[j].parent));
g_assert (atk_state_set_contains_state (set, ATK_STATE_DEFUNCT));
g_object_unref (set);
}
The following code lines are executed for each used element of
parent_data[] on the tests with GtkWidget/GtkContainer (GTK_IS_ENTRY
(widget) is false):
parent_data[i].count = 0;
g_signal_connect (child_accessible, "notify::accessible-parent",
G_CALLBACK (parent_notify), &(parent_data[i]));
The last code line causes two subsequent calls of children_changed() -
first time when the container is added; second time when it is removed:
static void
children_changed (AtkObject *accessible,
guint index,
gpointer child,
SignalData *data)
{
data->count++;
data->index = index;
data->n_children = atk_object_get_n_accessible_children (accessible);
}
Since the data parameter points to the appropriate parent_data
element, its count member is always 2 after that.
To prove that this is correct, we add an assertion that checks whether
that count is always 2.
After deleting the dead code, the code reads:
static void
test_add_remove (GtkWidget *widget)
{
AtkObject *accessible;
AtkObject *child_accessible;
SignalData add_data;
SignalData remove_data;
SignalData parent_data[3];
STATE state;
gint i, j;
gint step_children;
state.widget = widget;
accessible = gtk_widget_get_accessible (widget);
add_data.count = 0;
remove_data.count = 0;
g_signal_connect (accessible, "children_changed::add",
G_CALLBACK (children_changed), &add_data);
g_signal_connect (accessible, "children_changed::remove",
G_CALLBACK (children_changed), &remove_data);
step_children = atk_object_get_n_accessible_children (accessible);
for (i = 0; i < 3; i++)
{
if (!do_create_child (&state, i))
break;
if (!GTK_IS_ENTRY (widget))
{
parent_data[i].count = 0;
child_accessible = gtk_widget_get_accessible (state.child[i]);
g_signal_connect (child_accessible, "notify::accessible-parent",
G_CALLBACK (parent_notify), &(parent_data[i]));
gtk_container_add (GTK_CONTAINER (widget), state.child[i]);
}
else
child_accessible = atk_object_ref_accessible_child (accessible, i);
g_assert_cmpint (add_data.count, ==, i + 1);
g_assert_cmpint (add_data.n_children, ==, step_children + i + 1);
g_assert_cmpint (remove_data.count, ==, 0);
if (!GTK_IS_ENTRY (widget))
g_assert_cmpint (parent_data[i].count, ==, 1);
if (GTK_IS_SCROLLED_WINDOW (widget) ||
GTK_IS_NOTEBOOK (widget))
g_assert (atk_object_get_parent (ATK_OBJECT
(parent_data[i].parent)) == accessible);
else if (GTK_IS_ENTRY (widget))
g_assert (atk_object_get_parent (child_accessible) == accessible);
else
g_assert (parent_data[i].parent == accessible);
if (GTK_IS_ENTRY (widget))
g_object_unref (child_accessible);
}
for (j = 0 ; j < i; j++)
{
remove_child (&state, j);
g_assert_cmpint (add_data.count, ==, i);
g_assert_cmpint (remove_data.count, ==, j + 1);
g_assert_cmpint (remove_data.n_children, ==, step_children + i - j - 1);
if (!GTK_IS_ENTRY (widget))
{
g_assert_cmpint (parent_data[j].count, ==, 2);
g_assert (parent_data[j].parent == NULL);
}
}
g_signal_handlers_disconnect_by_func (accessible, G_CALLBACK
(children_changed), &add_data);
g_signal_handlers_disconnect_by_func (accessible, G_CALLBACK
(children_changed), &remove_data);
}
This is the commit which introduced the bugs.
https://git.gnome.org/browse/gtk+/commit/tests/a11y/children.c?h=gtk-3-8&id=b7743430aa1471c9ec054606d2234700e2da3eda
Although it was seen on ia64 only, all archs are affected. The other
archs just had more luck.
The patch corrects the problem which we have on ia64. The problem on
mips is likely a completely different one. Please could you file a new
separate bug report for that?
Stephan
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ia64_children_test.patch
Type: application/octet-stream
Size: 1527 bytes
Desc: ia64_children_test.patch
URL: <http://lists.alioth.debian.org/pipermail/pkg-gnome-maintainers/attachments/20130615/c209d087/attachment.obj>
More information about the pkg-gnome-maintainers
mailing list