/*
* 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 Michal Hruby
*
*/
#include
#include
#include
#include
#define TIMEOUT 100
#define PEER_NAME "com.canonical.Dee.Peer.Tests.Interactions"
#define MODEL_NAME "com.canonical.Dee.Peer.Tests.Interactions"
/* A command line that launches the appropriate *-helper-* executable,
* giving $name as first argument */
#define SERVER_HELPER(helper,name,count) \
(gchar *[]) { "./server-helper-"#helper, name, #count, NULL }
#define MODEL_HELPER(helper,name) \
(gchar *[]) { "./model-helper-"#helper, name, "DeeClient", NULL }
typedef struct
{
DeeServer *server;
} ServerFixture;
typedef struct
{
DeeModel *model;
} Fixture;
static void server_setup (ServerFixture *fix, gconstpointer data);
static void server_teardown (ServerFixture *fix, gconstpointer data);
static void model_setup (Fixture *fix, gconstpointer data);
static void model_teardown (Fixture *fix, gconstpointer data);
static void model_setup_null (Fixture *fix, gconstpointer data);
static void model_teardown_null (Fixture *fix, gconstpointer data);
static void test_allocation (ServerFixture *fix, gconstpointer data);
static void test_become_leader (ServerFixture *fix, gconstpointer data);
static void test_valid_client_address (ServerFixture *fix, gconstpointer data);
static void test_one_client (ServerFixture *fix, gconstpointer data);
static void test_multiple_clients (ServerFixture *fix, gconstpointer data);
static void test_shared_server (ServerFixture *fix, gconstpointer data);
static void test_ready (Fixture *fix, gconstpointer data);
static void test_clone (Fixture *fix, gconstpointer data);
static void test_row_added (Fixture *fix, gconstpointer data);
static void test_row_changed (Fixture *fix, gconstpointer data);
static void test_row_removed (Fixture *fix, gconstpointer data);
static void test_model_clear (Fixture *fix, gconstpointer data);
static void test_clear_add (Fixture *fix, gconstpointer data);
static void test_clear_then_add (Fixture *fix, gconstpointer data);
static void test_row_inserted (Fixture *fix, gconstpointer data);
static void test_schemaless_leader (Fixture *fix, gconstpointer data);
static void test_client_commit (Fixture *fix, gconstpointer data);
static void test_multiple_models (Fixture *fix, gconstpointer data);
static void test_multiple_models2 (Fixture *fix, gconstpointer data);
static void test_remote_append (Fixture *fix, gconstpointer data);
static void test_disabled_writes (Fixture *fix, gconstpointer data);
void
test_client_server_interactions_create_suite (void)
{
#define DOMAIN "/ClientServer/Interactions"
g_test_add (DOMAIN"/Allocation", ServerFixture, 0,
server_setup, test_allocation, server_teardown);
g_test_add (DOMAIN"/BecomeLeader", ServerFixture, 0,
server_setup, test_become_leader, server_teardown);
g_test_add (DOMAIN"/ValidClientAddress", ServerFixture, 0,
server_setup, test_valid_client_address, server_teardown);
g_test_add (DOMAIN"/OneClient", ServerFixture, 0,
server_setup, test_one_client, server_teardown);
g_test_add (DOMAIN"/MultipleClients", ServerFixture, 0,
server_setup, test_multiple_clients, server_teardown);
g_test_add (DOMAIN"/SharedServer", ServerFixture, 0,
server_setup, test_shared_server, server_teardown);
#undef DOMAIN
#define DOMAIN "/ClientServer/Model/Interactions"
g_test_add (DOMAIN"/Ready", Fixture, 0,
model_setup, test_ready, model_teardown);
g_test_add (DOMAIN"/Clone", Fixture, 0,
model_setup, test_clone, model_teardown);
g_test_add (DOMAIN"/RowAdded", Fixture, 0,
model_setup, test_row_added, model_teardown);
g_test_add (DOMAIN"/RowChanged", Fixture, 0,
model_setup, test_row_changed, model_teardown);
g_test_add (DOMAIN"/RowRemoved", Fixture, 0,
model_setup, test_row_removed, model_teardown);
g_test_add (DOMAIN"/Clear", Fixture, 0,
model_setup, test_model_clear, model_teardown);
g_test_add (DOMAIN"/ClearAndAdd", Fixture, 0,
model_setup, test_clear_add, model_teardown);
g_test_add (DOMAIN"/ClearThenAdd", Fixture, 0,
model_setup, test_clear_then_add, model_teardown);
g_test_add (DOMAIN"/RowInserted", Fixture, 0,
model_setup, test_row_inserted, model_teardown);
g_test_add (DOMAIN"/SchemalessLeader", Fixture, 0,
model_setup_null, test_schemaless_leader, model_teardown_null);
g_test_add (DOMAIN"/ClientCommit", Fixture, 0,
model_setup, test_client_commit, model_teardown);
g_test_add (DOMAIN"/MultipleModels", Fixture, 0,
model_setup_null, test_multiple_models, model_teardown_null);
g_test_add (DOMAIN"/MultipleModels2", Fixture, 0,
model_setup_null, test_multiple_models2, model_teardown_null);
g_test_add (DOMAIN"/RemoteAppend", Fixture, 0,
model_setup, test_remote_append, model_teardown);
g_test_add (DOMAIN"/DisabledWrites", Fixture, 0,
model_setup_null, test_disabled_writes, model_teardown_null);
}
static void
server_setup (ServerFixture *fix, gconstpointer data)
{
fix->server = dee_server_new (PEER_NAME);
g_assert_cmpint (0, ==, dee_peer_is_swarm_leader (DEE_PEER (fix->server)));
g_assert (DEE_IS_SERVER (fix->server));
}
static void
server_teardown (ServerFixture *fix, gconstpointer data)
{
gtx_assert_last_unref (fix->server);
/* Spin the mainloop a bit to check if we have any post-test
* async effect crashing us */
gtx_yield_main_loop (200);
}
static void
model_setup (Fixture *fix, gconstpointer data)
{
DeeServer *server = dee_server_new (MODEL_NAME);
fix->model = (DeeModel*) dee_shared_model_new_for_peer (DEE_PEER (server));
dee_model_set_schema (fix->model, "i", "s", NULL);
g_assert (DEE_IS_MODEL (fix->model));
}
static void
model_teardown (Fixture *fix, gconstpointer data)
{
gtx_assert_last_unref (fix->model);
/* Spin the mainloop so the socket service gets into usable state again */
gtx_yield_main_loop (200);
}
static void
model_setup_null (Fixture *fix, gconstpointer data)
{
}
static void
model_teardown_null (Fixture *fix, gconstpointer data)
{
}
/******************** The actual test cases ********************/
static void
test_allocation (ServerFixture *fix, gconstpointer data)
{
/* Do nothing, this test basically just asserts that
* the fix->server is cleaned up after immediate construction */
}
static void
test_become_leader (ServerFixture *fix, gconstpointer data)
{
gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT,
"notify::swarm-leader", NULL);
/* Assert that we have become swarm leaders.
* No other peers should be running */
g_assert_cmpint (0, !=, dee_peer_is_swarm_leader (DEE_PEER (fix->server)));
}
static void
test_valid_client_address (ServerFixture *fix, gconstpointer data)
{
gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT,
"notify::swarm-leader", NULL);
g_assert (dee_server_get_client_address (fix->server) != NULL);
}
static void
on_connection_acquired (DeeServer *server, GDBusConnection *connection,
GSList **list)
{
/* Yes, I _know_ that append() is slow, but this is a test! */
*list = g_slist_append (*list, connection);
}
static void
test_one_client (ServerFixture *fix, gconstpointer data)
{
GSList *list = NULL;
g_signal_connect (fix->server, "connection-acquired",
G_CALLBACK (on_connection_acquired), &list);
gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT,
"notify::swarm-leader", NULL);
/* We are leaders - launch the helper */
if (gtx_wait_for_command (TESTDIR,
SERVER_HELPER (client, PEER_NAME, 1),
2000))
g_critical ("Peer helper timed out");
gtx_assert_last_command_status (0);
g_assert_cmpuint (g_slist_length (list), ==, 1);
g_slist_free (list);
}
static void
test_multiple_clients (ServerFixture *fix, gconstpointer data)
{
GSList *list = NULL;
g_signal_connect (fix->server, "connection-acquired",
G_CALLBACK (on_connection_acquired), &list);
gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT,
"notify::swarm-leader", NULL);
/* We are leaders - launch the helper */
if (gtx_wait_for_command (TESTDIR,
SERVER_HELPER (client, PEER_NAME, 4),
4000))
g_critical ("Peer helper timed out");
gtx_assert_last_command_status (0);
g_assert_cmpuint (g_slist_length (list), ==, 4);
g_slist_free (list);
}
static void
test_shared_server (ServerFixture *fix, gconstpointer data)
{
DeeServer *another_server;
gtx_wait_for_signal (G_OBJECT (fix->server), TIMEOUT,
"notify::swarm-leader", NULL);
/* Make sure we're able to use the same bus_address on multiple servers */
another_server = dee_server_new_for_address (
PEER_NAME ".T1", dee_server_get_client_address (fix->server));
gtx_wait_for_signal (G_OBJECT (another_server), TIMEOUT,
"notify::swarm-leader", NULL);
g_assert_cmpstr (dee_peer_get_swarm_leader (DEE_PEER (fix->server)), ==,
dee_peer_get_swarm_leader (DEE_PEER (another_server)));
g_assert_cmpstr (dee_server_get_client_address (fix->server), ==,
dee_server_get_client_address (another_server));
g_object_unref (another_server);
}
static void
test_ready (Fixture *fix, gconstpointer data)
{
GParamSpec *pspec;
gboolean synchronized;
/* Test the GObject property reports FALSE */
g_object_get (fix->model, "synchronized", &synchronized, NULL);
g_assert_cmpint (0, == , synchronized);
/* Check that convenience getter now reports FALSE */
g_assert_cmpint (0, ==, dee_shared_model_is_synchronized (DEE_SHARED_MODEL (fix->model)));
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", &pspec))
g_critical ("Model never synchronized");
else
g_param_spec_unref (pspec);
/* Test the GObject property reports TRUE */
g_object_get (fix->model, "synchronized", &synchronized, NULL);
g_assert_cmpint (0, != , synchronized);
/* Check that convenience getter now reports TRUE */
g_assert_cmpint (0, !=, dee_shared_model_is_synchronized (DEE_SHARED_MODEL (fix->model)));
if (!gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", &pspec))
{
g_critical ("Model changed synchronization state twice");
g_param_spec_unref (pspec);
}
}
static gboolean
_add3rows (DeeModel *model)
{
g_return_val_if_fail (DEE_IS_MODEL (model), FALSE);
dee_model_append (model, 0, "zero");
dee_model_append (model, 1, "one");
dee_model_append (model, 2, "two");
return FALSE;
}
static gboolean
_add5rows (DeeModel *model)
{
g_return_val_if_fail (DEE_IS_MODEL (model), FALSE);
dee_model_append (model, 0, "zero");
dee_model_append (model, 1, "one");
dee_model_append (model, 2, "two");
dee_model_append (model, 3, "three");
dee_model_append (model, 4, "four");
return FALSE;
}
static gboolean
_change3rows (DeeModel *model)
{
DeeModelIter *iter;
g_return_val_if_fail (DEE_IS_MODEL (model), FALSE);
iter = dee_model_get_iter_at_row (model, 0);
dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_zero"));
iter = dee_model_get_iter_at_row (model, 1);
dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_one"));
iter = dee_model_get_iter_at_row (model, 2);
dee_model_set_value (model, iter, 1, g_variant_new_string ("changed_two"));
return FALSE;
}
/* Assumes a model with 5 rows. Removes rows 0, 4, and 2
* in that order. Accounting for rows shifts this becomes
* 0, 3, and 1. Leaving the original rows 1 and 3 now in
* positions 0 and 1 */
static gboolean
_remove3rows (DeeModel *model)
{
DeeModelIter *iter0, *iter4, *iter2;
DeeModelIter *orig_iter1, *orig_iter3;
g_return_val_if_fail (DEE_IS_MODEL (model), FALSE);
g_return_val_if_fail (dee_model_get_n_rows (model) == 5, FALSE);
iter0 = dee_model_get_iter_at_row (model, 0);
iter4 = dee_model_get_iter_at_row (model, 4);
iter2 = dee_model_get_iter_at_row (model, 2);
orig_iter1 = dee_model_get_iter_at_row (model, 1);
orig_iter3 = dee_model_get_iter_at_row (model, 3);
dee_model_remove (model, iter0);
dee_model_remove (model, iter4);
dee_model_remove (model, iter2);
g_assert_cmpint (dee_model_get_n_rows (model), ==, 2);
g_assert (dee_model_get_iter_at_row (model, 0) == orig_iter1);
g_assert (dee_model_get_iter_at_row (model, 1) == orig_iter3);
return FALSE;
}
static gboolean
_insert1row (DeeModel *model)
{
g_return_val_if_fail (DEE_IS_MODEL (model), FALSE);
dee_model_insert (model, 1, 27, "twentyseven");
return FALSE;
}
static gboolean
_clear_model (DeeModel *model)
{
g_return_val_if_fail (DEE_IS_MODEL (model), FALSE);
dee_model_clear (model);
return FALSE;
}
static gboolean
_clear_and_add5rows (DeeModel *model)
{
g_return_val_if_fail (DEE_IS_MODEL (model), FALSE);
_clear_model (model);
_add5rows (model);
return FALSE;
}
static void
test_clone (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never synchronized");
_add3rows (fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (clone3rows, MODEL_NAME),
1000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
/* We test that we can do this two times */
/*if (gtx_wait_for_command (TESTDIR, MODEL_HELPER (3rows, MODEL_NAME), 1000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);*/
}
static void
test_row_added (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
g_timeout_add (500, (GSourceFunc)_add3rows, fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (add3rows, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
}
static void
test_row_changed (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
_add3rows (fix->model);
g_timeout_add (500, (GSourceFunc)_change3rows, fix->model);
//const gchar *cmd[] = {"dbus-monitor", NULL};
//const gchar *cmd[] = {"sleep", "1",NULL};
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (change3rows, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
}
static void
test_row_removed (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
_add5rows (fix->model);
g_timeout_add (500, (GSourceFunc)_remove3rows, fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (remove3rows, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
}
static void
test_model_clear (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
_add3rows (fix->model);
g_timeout_add (500, (GSourceFunc)_clear_model, fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (clear3rows, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
}
static void
test_clear_add (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
g_timeout_add (500, (GSourceFunc)_add3rows, fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (add3rows, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
g_timeout_add (500, (GSourceFunc)_clear_and_add5rows, fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (clear3add5, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
}
static void
test_clear_then_add (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
_clear_model (fix->model);
g_timeout_add (500, (GSourceFunc)_add3rows, fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (add3rows, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
g_timeout_add (500, (GSourceFunc)_clear_model, fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (clear3rows, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
}
static void
test_row_inserted (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
_add3rows (fix->model);
g_timeout_add (500, (GSourceFunc)_insert1row, fix->model);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (insert1row, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
}
static void
_ready (DeeModel *model, GParamSpec *pspec, GSList **rows_so_far)
{
/* We must not have any rows when 'ready' is emitted */
g_assert (*rows_so_far == NULL);
}
static void
_collect_row (DeeModel *model, DeeModelIter *iter, GSList **rows_so_far)
{
/* Yes, I _know_ that append() is slow, but this is a test! */
*rows_so_far = g_slist_append (*rows_so_far, iter);
}
/* This case must run without a Fixture */
static void
test_schemaless_leader (Fixture *fix, gconstpointer data)
{
DeeModel *model;
DeeModelIter *iter;
GSList *rows_added;
g_assert (fix->model == NULL);
/* Set up a clean model *without* a schema. We will pick up the schema
* with the first transaction from the slave. Or at least, that's what we
* want to assert ;-) */
model = dee_shared_model_new_for_peer (DEE_PEER (dee_server_new (MODEL_NAME)));
// no set_schema() on purpose!
/* Listen for changes */
rows_added = NULL;
g_signal_connect (model, "row-added", G_CALLBACK (_collect_row), &rows_added);
g_signal_connect (model, "notify::synchronized", G_CALLBACK (_ready), &rows_added);
/* Remote process defines column types and adds two rows */
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (schemaless, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
/* Check that we got the right schema from the peer */
g_assert_cmpint (dee_model_get_n_columns (model), ==, 2);
g_assert_cmpstr (dee_model_get_column_schema (model, 0), ==, "i");
g_assert_cmpstr (dee_model_get_column_schema (model, 1), ==, "s");
/* Check that we got what we expected */
g_assert_cmpint (g_slist_length (rows_added), == , 2);
g_assert_cmpint (dee_model_get_n_rows (model), ==, 2);
g_assert_cmpint (dee_model_get_n_columns (model), ==, 2);
iter = (DeeModelIter*) g_slist_nth (rows_added, 0)->data;
g_assert_cmpint (dee_model_get_position (model, iter), == , 0);
g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 27);
g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "skunkworks");
iter = (DeeModelIter*) g_slist_nth (rows_added, 1)->data;
g_assert_cmpint (dee_model_get_position (model, iter), == , 1);
g_assert_cmpint (dee_model_get_int32 (model, iter, 0), == , 68);
g_assert_cmpstr (dee_model_get_string (model, iter, 1), == , "wumbo");
gtx_assert_last_unref (model);
g_slist_free (rows_added);
/* Spin the mainloop so the socket service gets into usable state again */
gtx_yield_main_loop (200);
}
static void
test_client_commit (Fixture *fix, gconstpointer data)
{
gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT,
"notify::synchronized", NULL);
DeeModel *client_model1 = dee_shared_model_new_for_peer (
DEE_PEER (dee_client_new (MODEL_NAME)));
DeeModel *client_model2 = dee_shared_model_new_for_peer (
DEE_PEER (dee_client_new (MODEL_NAME)));
gtx_wait_for_signal (G_OBJECT (client_model1), TIMEOUT,
"notify::synchronized", NULL);
g_assert (dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model1)));
if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model2)))
{
gtx_wait_for_signal (G_OBJECT (client_model2), TIMEOUT,
"notify::synchronized", NULL);
}
dee_model_append (client_model1, 38, "client_change");
gtx_yield_main_loop (500);
g_assert_cmpuint (dee_model_get_n_rows (fix->model), >, 0);
g_assert_cmpuint (dee_model_get_n_rows (client_model1), >, 0);
g_assert_cmpuint (dee_model_get_n_rows (client_model2), >, 0);
g_object_unref (client_model1);
g_object_unref (client_model2);
}
static void
test_multiple_models (Fixture *fix, gconstpointer data)
{
gchar *address = dee_server_bus_address_for_name (PEER_NAME, TRUE);
DeeModel *model1 = dee_shared_model_new_for_peer (
DEE_PEER (dee_server_new_for_address (PEER_NAME ".T1", address))
);
DeeModel *model2 = dee_shared_model_new_for_peer (
DEE_PEER (dee_server_new_for_address (PEER_NAME ".T2", address))
);
dee_model_set_schema (model1, "i", "s", NULL);
dee_model_set_schema (model2, "i", "s", NULL);
gtx_wait_for_signal (G_OBJECT (model1), TIMEOUT,
"notify::synchronized", NULL);
if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model2)))
{
gtx_wait_for_signal (G_OBJECT (model2), TIMEOUT,
"notify::synchronized", NULL);
}
g_assert (dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model1)) &&
dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model2)));
DeeModel *client_model1 = dee_shared_model_new_for_peer (
DEE_PEER (dee_client_new_for_address (PEER_NAME ".T1", address)));
DeeModel *client_model2 = dee_shared_model_new_for_peer (
DEE_PEER (dee_client_new_for_address (PEER_NAME ".T2", address)));
_add5rows (model1);
_add3rows (model2);
gtx_wait_for_signal (G_OBJECT (client_model1), TIMEOUT,
"notify::synchronized", NULL);
g_assert_cmpuint (dee_model_get_n_rows (client_model1), ==, 5);
g_assert_cmpuint (dee_model_get_n_rows (model1), ==, 5);
if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model2)))
{
gtx_wait_for_signal (G_OBJECT (client_model2), TIMEOUT,
"notify::synchronized", NULL);
}
g_assert_cmpuint (dee_model_get_n_rows (client_model2), ==, 3);
g_assert_cmpuint (dee_model_get_n_rows (model2), ==, 3);
/* Make sure the first model still has 5 rows */
g_assert_cmpuint (dee_model_get_n_rows (client_model1), ==, 5);
g_assert_cmpuint (dee_model_get_n_rows (model1), ==, 5);
g_object_unref (client_model1);
g_object_unref (client_model2);
g_object_unref (model1);
g_object_unref (model2);
g_free (address);
/* Spin the mainloop so the socket service gets into usable state again */
gtx_yield_main_loop (200);
}
static void
test_multiple_models2 (Fixture *fix, gconstpointer data)
{
gchar *address = dee_server_bus_address_for_name (PEER_NAME, TRUE);
DeeModel *model1 = dee_shared_model_new_for_peer (
DEE_PEER (dee_server_new_for_address (PEER_NAME ".T1", address))
);
DeeModel *model2 = dee_shared_model_new_for_peer (
DEE_PEER (dee_server_new_for_address (PEER_NAME ".T2", address))
);
dee_model_set_schema (model1, "i", "s", NULL);
dee_model_set_schema (model2, "i", "s", NULL);
gtx_wait_for_signal (G_OBJECT (model1), TIMEOUT,
"notify::synchronized", NULL);
if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model2)))
{
gtx_wait_for_signal (G_OBJECT (model2), TIMEOUT,
"notify::synchronized", NULL);
}
g_assert (dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model1)) &&
dee_shared_model_is_synchronized (DEE_SHARED_MODEL (model2)));
DeeModel *client_model1 = dee_shared_model_new_for_peer (
DEE_PEER (dee_client_new_for_address (PEER_NAME ".T1", address)));
DeeModel *client_model2 = dee_shared_model_new_for_peer (
DEE_PEER (dee_client_new_for_address (PEER_NAME ".T2", address)));
_add5rows (model1);
_add3rows (model2);
gtx_wait_for_signal (G_OBJECT (client_model1), TIMEOUT,
"notify::synchronized", NULL);
g_assert_cmpuint (dee_model_get_n_rows (client_model1), ==, 5);
g_assert_cmpuint (dee_model_get_n_rows (model1), ==, 5);
if (!dee_shared_model_is_synchronized (DEE_SHARED_MODEL (client_model2)))
{
gtx_wait_for_signal (G_OBJECT (client_model2), TIMEOUT,
"notify::synchronized", NULL);
}
g_assert_cmpuint (dee_model_get_n_rows (client_model2), ==, 3);
g_assert_cmpuint (dee_model_get_n_rows (model2), ==, 3);
/* Make sure the first model still has 5 rows */
g_assert_cmpuint (dee_model_get_n_rows (client_model1), ==, 5);
g_assert_cmpuint (dee_model_get_n_rows (model1), ==, 5);
/* get rid of the first client and server */
g_object_unref (client_model1);
g_object_unref (model1);
/* and make sure the second ones still work fine */
dee_model_clear (model2);
gtx_yield_main_loop (500); /* sync */
g_assert_cmpuint (dee_model_get_n_rows (client_model2), ==, 0);
g_assert_cmpuint (dee_model_get_n_rows (model2), ==, 0);
g_object_unref (client_model2);
g_object_unref (model2);
g_free (address);
/* Spin the mainloop so the socket service gets into usable state again */
gtx_yield_main_loop (200);
}
static void
changeset_signal (DeeModel *model, gboolean *value)
{
*value = TRUE;
}
static void
test_remote_append (Fixture *fix, gconstpointer data)
{
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
/* We should be leader before starting the helper process */
g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model)));
g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 0);
gboolean got_changeset_start = FALSE;
gboolean got_changeset_finish = FALSE;
g_signal_connect (fix->model, "changeset-started",
G_CALLBACK (changeset_signal), &got_changeset_start);
g_signal_connect (fix->model, "changeset-finished",
G_CALLBACK (changeset_signal), &got_changeset_finish);
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (append1, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
/* There should be a new row in the model */
g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1);
g_assert (got_changeset_start);
g_assert (got_changeset_finish);
}
static void
test_disabled_writes (Fixture *fix, gconstpointer data)
{
DeePeer *peer;
DeeModelIter *iter;
peer = DEE_PEER (dee_server_new (MODEL_NAME));
fix->model = DEE_MODEL (g_object_new (DEE_TYPE_SHARED_MODEL,
"peer", peer,
"back-end", dee_sequence_model_new (),
"access-mode", DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE, NULL));
dee_model_set_schema (fix->model, "i", "s", NULL);
g_object_unref (G_OBJECT (peer));
if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
g_critical ("Model never emitted 'ready' signal");
/* We should be leader before starting the helper process */
g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model)));
dee_model_prepend (fix->model, 81, "eightyone");
if (gtx_wait_for_command (TESTDIR,
MODEL_HELPER (append1, MODEL_NAME),
2000))
g_critical ("Model helper timed out");
gtx_assert_last_command_status (0);
/* The peer tried to append a row, but we should have ignored that */
g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1);
iter = dee_model_get_first_iter (fix->model);
g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "eightyone");
}