/*
* 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
* Neil Jagdish Patel
* Mikkel Kamstrup Erlandsen
*
*/
#include
#include
#include
#include
#include
typedef struct
{
DeeModel *m;
} Fixture;
static void sequence_model_setup (Fixture *fix, gconstpointer data);
static void sequence_model_teardown (Fixture *fix, gconstpointer data);
static void shared_model_setup (Fixture *fix, gconstpointer data);
static void shared_model_teardown (Fixture *fix, gconstpointer data);
static void test_no_tags (Fixture *fix, gconstpointer data);
static void test_one_tag (Fixture *fix, gconstpointer data);
static void test_two_tags (Fixture *fix, gconstpointer data);
static void test_late_tag (Fixture *fix, gconstpointer data);
static void test_destroy_tag (Fixture *fix, gconstpointer data);
static void test_tag_access_in_row_removed_handler (Fixture *fix, gconstpointer data);
void
test_model_tags_create_suite (void)
{
#define SEQUENCE_MODEL_DOMAIN "/ModelTags/SequenceModel"
#define SHARED_MODEL_DOMAIN "/ModelTags/SharedModel"
g_test_add (SEQUENCE_MODEL_DOMAIN"/NoTags", Fixture, 0,
sequence_model_setup, test_no_tags, sequence_model_teardown);
g_test_add (SHARED_MODEL_DOMAIN"/NoTags", Fixture, 0,
shared_model_setup, test_no_tags, shared_model_teardown);
g_test_add (SEQUENCE_MODEL_DOMAIN"/OneTag", Fixture, 0,
sequence_model_setup, test_one_tag, sequence_model_teardown);
g_test_add (SHARED_MODEL_DOMAIN"/OneTag", Fixture, 0,
shared_model_setup, test_one_tag, shared_model_teardown);
g_test_add (SEQUENCE_MODEL_DOMAIN"/TwoTags", Fixture, 0,
sequence_model_setup, test_two_tags, sequence_model_teardown);
g_test_add (SHARED_MODEL_DOMAIN"/TwoTags", Fixture, 0,
shared_model_setup, test_two_tags, shared_model_teardown);
g_test_add (SEQUENCE_MODEL_DOMAIN"/LateTag", Fixture, 0,
sequence_model_setup, test_late_tag, sequence_model_teardown);
g_test_add (SHARED_MODEL_DOMAIN"/LateTag", Fixture, 0,
shared_model_setup, test_late_tag, shared_model_teardown);
g_test_add (SEQUENCE_MODEL_DOMAIN"/DestroyFunc", Fixture, 0,
sequence_model_setup, test_destroy_tag, sequence_model_teardown);
g_test_add (SHARED_MODEL_DOMAIN"/DestroyFunc", Fixture, 0,
shared_model_setup, test_destroy_tag, shared_model_teardown);
g_test_add (SEQUENCE_MODEL_DOMAIN"/TagAccessInRowRemovedHandler", Fixture, 0,
sequence_model_setup, test_tag_access_in_row_removed_handler, sequence_model_teardown);
g_test_add (SHARED_MODEL_DOMAIN"/TagAccessInRowRemovedHandler", Fixture, 0,
shared_model_setup, test_tag_access_in_row_removed_handler, shared_model_teardown);
}
static void
sequence_model_setup (Fixture *fix, gconstpointer data)
{
fix->m = dee_sequence_model_new ();
dee_model_set_schema (fix->m, "i", "s", NULL);
}
static void
sequence_model_teardown (Fixture *fix, gconstpointer data)
{
g_object_unref (fix->m);
fix->m = NULL;
}
static void
shared_model_setup (Fixture *fix, gconstpointer data)
{
fix->m = dee_shared_model_new ("org.example.ThisIsNotATest");
dee_model_set_schema (fix->m, "i", "s", NULL);
}
static void
shared_model_teardown (Fixture *fix, gconstpointer data)
{
g_object_unref (fix->m);
fix->m = NULL;
}
static void
test_no_tags (Fixture *fix, gconstpointer data)
{
DeeModelIter *iter;
gpointer tag;
iter = dee_model_append (fix->m, 27, "Hello");
/* Check that getting an undefined tag fails gracefully */
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
tag = NULL;
tag = dee_model_get_tag (fix->m, iter, GUINT_TO_POINTER (123));
g_assert (tag == NULL);
exit (0); /* successful test run */
}
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*Unable to look up tag. No tags registered on DeeSequenceModel*");
/* Ditto for setting undefined tags */
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
dee_model_set_tag (fix->m, iter, GUINT_TO_POINTER (123), GUINT_TO_POINTER (321));
exit (0); /* successful test run */
}
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*Unable to look up tag. No tags registered on DeeSequenceModel*");
}
static void
test_one_tag (Fixture *fix, gconstpointer data)
{
DeeModelTag *tag;
DeeModelIter *iter1, *iter2;
tag = dee_model_register_tag (fix->m, NULL);
iter1 = dee_model_append (fix->m, 27, "Hello");
iter2 = dee_model_append (fix->m, 68, "world");
/* Set+Get */
dee_model_set_tag (fix->m, iter1, tag, "tag1");
dee_model_set_tag (fix->m, iter2, tag, "tag2");
g_assert_cmpstr ("tag1", ==, dee_model_get_tag (fix->m, iter1, tag));
g_assert_cmpstr ("tag2", ==, dee_model_get_tag (fix->m, iter2, tag));
/* Clear a tag */
dee_model_clear_tag (fix->m, iter1, tag);
g_assert (dee_model_get_tag (fix->m, iter1, tag) == NULL);
g_assert_cmpstr ("tag2", ==, dee_model_get_tag (fix->m, iter2, tag));
/* Override exiting */
dee_model_set_tag (fix->m, iter2, tag, "tag3");
g_assert (dee_model_get_tag (fix->m, iter1, tag) == NULL);
g_assert_cmpstr ("tag3", ==, dee_model_get_tag (fix->m, iter2, tag));
/* Check that getting an undefined tag fails gracefully */
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
tag = NULL;
tag = dee_model_get_tag (fix->m, iter1, GUINT_TO_POINTER (123));
g_assert (tag == NULL);
exit (0); /* successful test run */
}
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*Unable to find tag 123*");
/* Ditto for setting undefined tags */
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
dee_model_set_tag (fix->m, iter1, GUINT_TO_POINTER (123), GUINT_TO_POINTER (321));
exit (0); /* successful test run */
}
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*Unable to find tag 123*");
}
static void
test_two_tags (Fixture *fix, gconstpointer data)
{
DeeModelTag *tag1, *tag2;
DeeModelIter *iter1, *iter2;
tag1 = dee_model_register_tag (fix->m, NULL);
tag2 = dee_model_register_tag (fix->m, (GDestroyNotify) g_free);
iter1 = dee_model_append (fix->m, 27, "Hello");
iter2 = dee_model_append (fix->m, 68, "world");
dee_model_set_tag (fix->m, iter1, tag1, "tag1-value1");
dee_model_set_tag (fix->m, iter2, tag1, "tag1-value2");
dee_model_set_tag (fix->m, iter1, tag2, g_strdup ("tag2-value1"));
dee_model_set_tag (fix->m, iter2, tag2, g_strdup ("tag2-value2"));
g_assert_cmpstr ("tag1-value1", ==, dee_model_get_tag (fix->m, iter1, tag1));
g_assert_cmpstr ("tag1-value2", ==, dee_model_get_tag (fix->m, iter2, tag1));
g_assert_cmpstr ("tag2-value1", ==, dee_model_get_tag (fix->m, iter1, tag2));
g_assert_cmpstr ("tag2-value2", ==, dee_model_get_tag (fix->m, iter2, tag2));
/* Clear one tag. The rest should be unchanged */
dee_model_clear_tag (fix->m, iter1, tag1);
g_assert (dee_model_get_tag (fix->m, iter1, tag1) == NULL);
g_assert_cmpstr ("tag1-value2", ==, dee_model_get_tag (fix->m, iter2, tag1));
g_assert_cmpstr ("tag2-value1", ==, dee_model_get_tag (fix->m, iter1, tag2));
g_assert_cmpstr ("tag2-value2", ==, dee_model_get_tag (fix->m, iter2, tag2));
}
static void
test_late_tag (Fixture *fix, gconstpointer data)
{
/* The point here is to try and register a tag *after* we begun populating
* the model */
DeeModelTag *tag1, *tag2;
DeeModelIter *iter1, *iter2;
tag1 = dee_model_register_tag (fix->m, NULL);
iter1 = dee_model_append (fix->m, 27, "Hello");
iter2 = dee_model_append (fix->m, 68, "world");
/* Start working randomly with the model */
dee_model_set_tag (fix->m, iter1, tag1, "tag1-value1");
g_assert_cmpstr ("tag1-value1", ==, dee_model_get_tag (fix->m, iter1, tag1));
g_assert (dee_model_get_tag (fix->m, iter2, tag1) == NULL);
/* With some rows and tags in the model, now register another tag.
* Assert that all rows have this tag in unset state */
tag2 = dee_model_register_tag (fix->m, (GDestroyNotify) g_free);
g_assert (dee_model_get_tag (fix->m, iter1, tag2) == NULL);
g_assert (dee_model_get_tag (fix->m, iter2, tag2) == NULL);
/* Set the new tag and verify the complete state */
dee_model_set_tag (fix->m, iter1, tag2, g_strdup ("tag2-value1"));
dee_model_set_tag (fix->m, iter2, tag2, g_strdup ("tag2-value2"));
g_assert_cmpstr ("tag1-value1", ==, dee_model_get_tag (fix->m, iter1, tag1));
g_assert (dee_model_get_tag (fix->m, iter2, tag1) == NULL);
g_assert_cmpstr ("tag2-value1", ==, dee_model_get_tag (fix->m, iter1, tag2));
g_assert_cmpstr ("tag2-value2", ==, dee_model_get_tag (fix->m, iter2, tag2));
}
static void
destroy_tag (gpointer tag_value)
{
/* A GDestroyNotify that twiddles with a string */
((gchar *) tag_value)[0] = '*';
}
static void
test_destroy_tag (Fixture *fix, gconstpointer data)
{
/* Assert that the destroy function is indeed called */
gchar *tag_value = g_strdup ("#");
DeeModelTag *tag;
DeeModelIter *iter1;
tag = dee_model_register_tag (fix->m, destroy_tag);
iter1 = dee_model_append (fix->m, 27, "Hello");
/* Set+Get */
dee_model_set_tag (fix->m, iter1, tag, tag_value);
g_assert_cmpstr ("#", ==, dee_model_get_tag (fix->m, iter1, tag));
/* Assert destroy func triggered. Changing "#" to "*" */
dee_model_clear_tag (fix->m, iter1, tag);
g_assert (dee_model_get_tag (fix->m, iter1, tag) == NULL);
g_assert_cmpstr ("*", ==, tag_value);
/* Change the annotation back to "#" and make sure that destroy is also
* called when we remove the row */
tag_value[0] = '#';
dee_model_set_tag (fix->m, iter1, tag, tag_value);
g_assert_cmpstr ("#", ==, dee_model_get_tag (fix->m, iter1, tag));
dee_model_remove (fix->m, iter1);
g_assert_cmpstr ("*", ==, tag_value);
g_free (tag_value);
}
static int row_removed_handler_called = 0;
static void
row_removed_handler (DeeModel *model, DeeModelIter *iter, DeeModelTag *tag)
{
/* Assert that we can read the tag before it's freed. We use a dummy
* free that sets the tag to "*" to detect this */
g_assert_cmpstr (dee_model_get_tag (model, iter, tag), ==, "#");
row_removed_handler_called = 1;
}
static void
test_tag_access_in_row_removed_handler (Fixture *fix, gconstpointer data)
{
/* Check that we can access a tag in the row-removed handler,
* before the tag is destroyed */
gchar *tag_value = g_strdup ("#");
DeeModelTag *tag;
DeeModelIter *iter1;
tag = dee_model_register_tag (fix->m, destroy_tag);
iter1 = dee_model_append (fix->m, 27, "Hello");
dee_model_set_tag (fix->m, iter1, tag, tag_value);
g_signal_connect (fix->m, "row-removed",
G_CALLBACK (row_removed_handler), tag);
dee_model_clear (fix->m);
g_assert_cmpint (row_removed_handler_called, ==, 1);
g_assert_cmpstr ("*", ==, tag_value);
g_free (tag_value);
}