dee

Merge lp:~mhr3/dee/more-props into lp:dee

Proposed by Michal Hruby
Status: Merged
Approved by: Mikkel Kamstrup Erlandsen
Approved revision: 364
Merged at revision: 353
Proposed branch: lp:~mhr3/dee/more-props
Merge into: lp:dee
Prerequisite: lp:~mhr3/dee/more-tests
Diff against target: 890 lines (+512/-30)
10 files modified
src/dee-peer.c (+67/-4)
src/dee-peer.h (+10/-8)
src/dee-shared-model.c (+92/-11)
src/dee-shared-model.h (+28/-3)
tests/Makefile.am (+11/-3)
tests/model-helper-append1.c (+55/-0)
tests/model-helper-replace.c (+70/-0)
tests/test-client-server.c (+63/-1)
tests/test-model-interactions.c (+106/-0)
vapi/dee-1.0.vapi (+10/-0)
To merge this branch: bzr merge lp:~mhr3/dee/more-props
Reviewer Review Type Date Requested Status
Mikkel Kamstrup Erlandsen (community) Approve
Review via email: mp+94746@code.launchpad.net

Description of the change

Added extra properties to help us deal with the problem of remote peers writing to a model which they shouldn't.

Added tests for the new properties. UNBLOCK

To post a comment you must log in.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Why rename *self to *peer in all method args? That goes the conventions used elsewhere in Dee..?

272 + * Note that this does NOT mean that the peer is owner of the swarm! Check also
273 + * dee_peer_is_swarm_leader().

s/peer is owner/peer is leader/

375 /**
376 + * DeeSharedModel:disable-remote-writes:
377 + *
378 + * Boolean property defining whether or not the model will accept changes
379 + * done by remote peers.
380 + *
381 + * This is useful when one process is considered an "owner" of a model and
382 + * all the other peers are supposed to only synchronize it for reading.
383 + * Note that this will only have effect if the associated peer is swarm
384 + * leader.
385 + */

Can you add a cross ref to the DeePeer:swarm-owner property

On "disable-remote-writes": I think we need to turn this into a set of flags or an enum in order to ensure forward compat. And enum is probably best. This way we can prevent us getting into an n! combinatorial explosion (or worse, semantic ambiguity) if we need to add new access modes.

I am thinking; call the property "access-mode" and define a DeeSharedModelAccessMode enumeration with two values (for now) DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE (the default value) and DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE.

We might also want to add client side validation on the writability of the model... (Just logging a critical and leaving the model unchanged) In order to catch programming errors more easily and not leading to desktop crunch if we get into a Invalidate/Clone loop... I think this could be done cleanly in DeeProxyModel with a "writable" boolean property (which we can then bind to "is leader" to state of the DeeSharedModel)

And finally - does the testing also apply against the p2p mode of DeeSharedModel? I think that needs to be validated...

review: Needs Fixing
Revision history for this message
Michal Hruby (mhr3) wrote :

Ok, most of these should be fixed, (minus the writable prop on ProxyModel), I'm still unsure about constructors taking the access_mode - do we want to leave this up to apps to call g_object_new with all the props themselves, or are we going for a new constructor (I really don't like that idea if it's supposed to do the initial Clone as well).

We already have quite a few of the constructor variants, do we really want dee_shared_model_new_for_peer_with_backend_accessed_as(Peer, Model, AccessMode)?
Drop the peer? - then there's no simple way to use it with DeeClient/Server
Drop the model? - that probably makes sense if we're doing initial Clone anyway, then again DeeServer can't do initial Clone

Or perhaps just do a static method that will try to do the initial clone, and leave the constructors alone? Sounds most sane to me.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

I think the role of the remote-writes-disabled property is unclear. It is implied by the value of access-mode property in almost all cases, and checking for weird combinations of these properties might make the API more confusing than good is.

Also; if we do want to keep disable-remote-writesthen I think we should rename it to a non-verb form ala remote-writes-disabled.

96 + * DeePeer::swarm-owner:

Use only one single colon here, double colons are for signals. As it is the docs doesn't pick up this comment.

147 +dee_peer_get_swarm_owner (DeePeer *self)

Should be *_is_swarm_owner() to be consistent with the other boolean properties. Same applies for the disable_remote_writes if we keep it.

281 + * This is useful when a model created with ::access-mode set to

Property refs in gtk-doc needs single colons. The refs to the DeeSharedModelAccessMode enum values in this chunk also needs #-prefixes to give correct cross refs.

302 + * Setting this to DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE is useful

Should be #DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE (hash prefix, again)

306 + * See also DeePeer::swarm-owner property to ensure ownership of a swarm.

Double colon -> single colon

465 +typedef enum
466 +{
467 + DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE,
468 + DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE
469 +} DeeSharedModelAccessMode;

Should be documented.

418 + * the ::disable-remote-writes property to ignore any misbehaving clients
438 + * Gets value of the ::disable-remote-writes property.

Single colon for property xref.

review: Needs Fixing
Revision history for this message
Michal Hruby (mhr3) wrote :

As discussed on IRC, the disable-remote-writes property was removed.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Ok, I am happy :-) Great work!

review: Approve
Revision history for this message
Unity Merger (unity-merger) wrote :

The prerequisite lp:~mhr3/dee/more-tests has not yet been merged into lp:dee.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/dee-peer.c'
2--- src/dee-peer.c 2012-01-02 20:31:33 +0000
3+++ src/dee-peer.c 2012-02-28 10:59:18 +0000
4@@ -98,6 +98,8 @@
5 /* The GDBus name watcher id from g_bus_watch_name() */
6 guint name_watcher_id;
7
8+ /* Swarm related properties */
9+ gboolean swarm_owner;
10 const gchar *own_name;
11 gchar *swarm_name;
12 gchar *swarm_path;
13@@ -116,6 +118,9 @@
14
15 /* Protecting the priv->peers table from concurrent access in
16 * the GDBus message dispatch thread */
17+#if GLIB_CHECK_VERSION(2, 31, 16)
18+ GMutex lock_real;
19+#endif
20 GMutex *lock;
21 };
22
23@@ -124,7 +129,8 @@
24 {
25 PROP_0,
26 PROP_SWARM_NAME,
27- PROP_SWARM_LEADER
28+ PROP_SWARM_LEADER,
29+ PROP_SWARM_OWNER
30 };
31
32 enum
33@@ -289,7 +295,11 @@
34 }
35 if (priv->lock != NULL)
36 {
37+#if GLIB_CHECK_VERSION(2, 31, 16)
38+ g_mutex_clear (priv->lock);
39+#else
40 g_mutex_free (priv->lock);
41+#endif
42 priv->lock = NULL;
43 }
44 if (priv->head_count != NULL)
45@@ -310,8 +320,9 @@
46 static void
47 dee_peer_constructed (GObject *self)
48 {
49- DeePeerPrivate *priv;
50- gpointer *weak_self;
51+ DeePeerPrivate *priv;
52+ gpointer *weak_self;
53+ GBusNameOwnerFlags flags;
54
55 priv = DEE_PEER (self)->priv;
56
57@@ -335,9 +346,12 @@
58 g_object_add_weak_pointer (self, weak_self);
59
60 /* Contend to be swarm leaders. Pick me! Pick me! */
61+ flags = priv->swarm_owner ?
62+ G_BUS_NAME_OWNER_FLAGS_REPLACE : G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
63+
64 priv->name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
65 priv->swarm_name, /* name to own */
66- G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
67+ flags,
68 on_bus_acquired,
69 on_leadership_acquired,
70 on_leadership_lost,
71@@ -373,6 +387,9 @@
72 g_free (priv->swarm_leader);
73 priv->swarm_leader = g_value_dup_string (value);
74 break;
75+ case PROP_SWARM_OWNER:
76+ priv->swarm_owner = g_value_get_boolean (value);
77+ break;
78 default:
79 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
80 break;
81@@ -393,6 +410,9 @@
82 case PROP_SWARM_LEADER:
83 g_value_set_string (value, dee_peer_get_swarm_leader (DEE_PEER (object)));
84 break;
85+ case PROP_SWARM_OWNER:
86+ g_value_set_boolean (value, DEE_PEER (object)->priv->swarm_owner);
87+ break;
88 default:
89 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
90 break;
91@@ -517,6 +537,25 @@
92 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
93 g_object_class_install_property (obj_class, PROP_SWARM_LEADER, pspec);
94
95+ /**
96+ * DeePeer::swarm-owner:
97+ *
98+ * If set, this peer will try to become a leader of the swarm.
99+ *
100+ * Creating a #DeeSharedModel with a peer that successfully assumes ownership
101+ * of a swarm will skip cloning of the model, therefore you need to set
102+ * the schema and fill the model with data yourself.
103+ *
104+ * Setting this property to TRUE does NOT guarantee that this peer will
105+ * become a leader. You should always check the :swarm-leader property.
106+ **/
107+ pspec = g_param_spec_boolean ("swarm-owner", "Swarm Owner",
108+ "Try to assume leadership of the swarm",
109+ FALSE,
110+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
111+ | G_PARAM_STATIC_STRINGS);
112+ g_object_class_install_property (obj_class, PROP_SWARM_OWNER, pspec);
113+
114 /* Add private data */
115 g_type_class_add_private (obj_class, sizeof (DeePeerPrivate));
116 }
117@@ -542,7 +581,12 @@
118 priv->has_been_leader = FALSE;
119 priv->is_first_name_check = TRUE;
120
121+#if GLIB_CHECK_VERSION(2, 31, 16)
122+ g_mutex_init (&priv->lock_real);
123+ priv->lock = &priv->lock_real;
124+#else
125 priv->lock = g_mutex_new ();
126+#endif
127
128 priv->head_count_source = 0;
129 }
130@@ -845,6 +889,25 @@
131 return klass->list_peers (self);
132 }
133
134+/**
135+ * dee_peer_is_swarm_owner:
136+ * @self: a #DeePeer
137+ *
138+ * Gets the value of the :swarm-owner property.
139+ *
140+ * Note that this does NOT mean that the peer is leader of the swarm! Check also
141+ * dee_peer_is_swarm_leader().
142+ *
143+ * Return value: TRUE if the :swarm-owner property was set during construction.
144+ */
145+gboolean
146+dee_peer_is_swarm_owner (DeePeer *self)
147+{
148+ g_return_val_if_fail (DEE_IS_PEER (self), FALSE);
149+
150+ return self->priv->swarm_owner;
151+}
152+
153 static void
154 emit_peer_found (DeePeer *self,
155 const gchar *name)
156
157=== modified file 'src/dee-peer.h'
158--- src/dee-peer.h 2012-01-02 15:46:25 +0000
159+++ src/dee-peer.h 2012-02-28 10:59:18 +0000
160@@ -75,16 +75,16 @@
161 /*< public >*/
162
163 /*< signals >*/
164- void (*peer_found) (DeePeer *peer, const gchar *name);
165- void (*peer_lost) (DeePeer *peer, const gchar *name);
166- void (*connection_acquired) (DeePeer *peer, GDBusConnection *connection);
167- void (*connection_closed) (DeePeer *peer, GDBusConnection *connection);
168+ void (*peer_found) (DeePeer *self, const gchar *name);
169+ void (*peer_lost) (DeePeer *self, const gchar *name);
170+ void (*connection_acquired) (DeePeer *self, GDBusConnection *connection);
171+ void (*connection_closed) (DeePeer *self, GDBusConnection *connection);
172
173 /*< vtable >*/
174- const gchar* (*get_swarm_leader) (DeePeer *peer);
175- gboolean (*is_swarm_leader) (DeePeer *peer);
176- GSList* (*get_connections) (DeePeer *peer);
177- gchar** (*list_peers) (DeePeer *peer);
178+ const gchar* (*get_swarm_leader) (DeePeer *self);
179+ gboolean (*is_swarm_leader) (DeePeer *self);
180+ GSList* (*get_connections) (DeePeer *self);
181+ gchar** (*list_peers) (DeePeer *self);
182
183 /*< private >*/
184 void (*_dee_peer_1) (void);
185@@ -113,6 +113,8 @@
186
187 gchar** dee_peer_list_peers (DeePeer *self);
188
189+gboolean dee_peer_is_swarm_owner (DeePeer *self);
190+
191 G_END_DECLS
192
193 #endif /* _HAVE_DEE_PEER_H */
194
195=== modified file 'src/dee-shared-model.c'
196--- src/dee-shared-model.c 2012-02-28 10:59:18 +0000
197+++ src/dee-shared-model.c 2012-02-28 10:59:18 +0000
198@@ -1,5 +1,5 @@
199 /*
200- * Copyright (C) 2010 Canonical, Ltd.
201+ * Copyright (C) 2010-2012 Canonical, Ltd.
202 *
203 * This library is free software; you can redistribute it and/or modify
204 * it under the terms of the GNU Lesser General Public License
205@@ -100,6 +100,8 @@
206 gboolean synchronized;
207 gboolean found_first_peer;
208 gboolean suppress_remote_signals;
209+
210+ DeeSharedModelAccessMode access_mode;
211 };
212
213 typedef struct
214@@ -126,6 +128,8 @@
215 PROP_0,
216 PROP_PEER,
217 PROP_SYNCHRONIZED,
218+ PROP_DISABLE_REMOTE_WRITES,
219+ PROP_ACCESS_MODE,
220 };
221
222 typedef enum
223@@ -549,6 +553,9 @@
224 case PROP_SYNCHRONIZED:
225 g_critical ("Trying to set read only property DeeSharedModel:synchronized");
226 break;
227+ case PROP_ACCESS_MODE:
228+ priv->access_mode = g_value_get_enum (value);
229+ break;
230 default:
231 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
232 break;
233@@ -573,6 +580,9 @@
234 case PROP_SYNCHRONIZED:
235 g_value_set_boolean (value, priv->synchronized);
236 break;
237+ case PROP_ACCESS_MODE:
238+ g_value_set_enum (value, priv->access_mode);
239+ break;
240 default:
241 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
242 break;
243@@ -668,7 +678,8 @@
244 pspec = g_param_spec_object ("peer", "Peer",
245 "The peer object that monitors the swarm",
246 DEE_TYPE_PEER,
247- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
248+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
249+ | G_PARAM_STATIC_STRINGS);
250 g_object_class_install_property (obj_class, PROP_PEER, pspec);
251
252 /**
253@@ -684,10 +695,29 @@
254 pspec = g_param_spec_boolean("synchronized", "Synchronized",
255 "Whether the model is synchronized with its peers",
256 FALSE,
257- G_PARAM_READABLE);
258+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
259 g_object_class_install_property (obj_class, PROP_SYNCHRONIZED, pspec);
260
261 /**
262+ * DeeSharedModel:access-mode:
263+ *
264+ * Enumeration defining behavior of this model when trying to write to it.
265+ *
266+ * Setting this to #DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE is useful
267+ * when one process is considered an "owner" of a model and all the other
268+ * peers are supposed to only synchronize it for reading.
269+ *
270+ * See also DeePeer:swarm-owner property to ensure ownership of a swarm.
271+ */
272+ pspec = g_param_spec_enum ("access-mode", "Access Mode",
273+ "Access mode used by this shared model",
274+ DEE_TYPE_SHARED_MODEL_ACCESS_MODE,
275+ DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE,
276+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
277+ | G_PARAM_STATIC_STRINGS);
278+ g_object_class_install_property (obj_class, PROP_ACCESS_MODE, pspec);
279+
280+ /**
281 * DeeSharedModel::begin-transaction:
282 * @model: The shared model the signal is emitted on
283 * @begin_seqnum: The seqnum the model has now
284@@ -1056,6 +1086,8 @@
285 {
286 DeeSharedModel *model;
287 const gchar *unique_name;
288+ gboolean forced_ignore;
289+ gboolean disable_write;
290
291 g_return_if_fail (DEE_IS_SHARED_MODEL (user_data));
292
293@@ -1073,14 +1105,35 @@
294 if (g_strcmp0 (signal_name, "Commit") == 0)
295 {
296 model = DEE_SHARED_MODEL (user_data);
297- commit_transaction (model, sender_name, parameters);
298-
299- if (g_slist_length (model->priv->connections) > 1)
300- {
301- /* this is a server and a client (non-leader) just committed a change
302- * to the model, let's invalidate all other clients */
303- invalidate_peer (model, sender_name, connection);
304- }
305+ disable_write = model->priv->access_mode ==
306+ DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE;
307+ forced_ignore = dee_peer_is_swarm_leader (model->priv->swarm) &&
308+ disable_write;
309+
310+ if (!disable_write)
311+ {
312+ commit_transaction (model, sender_name, parameters);
313+ }
314+ else if (!forced_ignore)
315+ {
316+ /* remote writes are disabled, but we're not leader - commit anyway */
317+ g_warning ("Tried to prevent remote write, but SharedModel[%p] is "
318+ "not owned by peer named %s.",
319+ model, dee_peer_get_swarm_name (model->priv->swarm));
320+ commit_transaction (model, sender_name, parameters);
321+ }
322+
323+ if (forced_ignore)
324+ {
325+ /* invalidate all the peers if remote writes are disabled */
326+ invalidate_peer (model, sender_name, NULL);
327+ }
328+ else if (g_slist_length (model->priv->connections) > 1)
329+ {
330+ /* this is a server and a client (non-leader) just committed a change
331+ * to the model, let's invalidate all other clients */
332+ invalidate_peer (model, sender_name, connection);
333+ }
334 }
335 else
336 g_warning ("Unexpected signal %s.%s from %s",
337@@ -1464,6 +1517,34 @@
338
339 /* Public Methods */
340
341+GType dee_shared_model_access_mode_get_type (void)
342+{
343+ static GType shared_model_access_mode_type = 0;
344+ if (shared_model_access_mode_type == 0)
345+ {
346+ static const GEnumValue values[] =
347+ {
348+ {
349+ DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE,
350+ "DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE",
351+ "world-writable"
352+ },
353+ {
354+ DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE,
355+ "DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE",
356+ "leader-writable"
357+ },
358+ {
359+ 0, NULL, NULL
360+ }
361+ };
362+ shared_model_access_mode_type =
363+ g_enum_register_static ("DeeSharedModelAccessMode", values);
364+ }
365+
366+ return shared_model_access_mode_type;
367+}
368+
369 /**
370 * dee_shared_model_new:
371 * @name: A well known name to publish this model under. Models sharing this name
372
373=== modified file 'src/dee-shared-model.h'
374--- src/dee-shared-model.h 2011-12-14 14:09:58 +0000
375+++ src/dee-shared-model.h 2012-02-28 10:59:18 +0000
376@@ -92,6 +92,29 @@
377 DEE_SHARED_MODEL_ERROR_LEADER_INVALIDATED
378 } DeeSharedModelError;
379
380+#define DEE_TYPE_SHARED_MODEL_ACCESS_MODE \
381+ (dee_shared_model_access_mode_get_type ())
382+
383+/**
384+ * DeeSharedModelAccessMode:
385+ *
386+ * Enumeration defining behavior of the model with regards to writes from
387+ * other peers in the swarm.
388+ */
389+typedef enum
390+{
391+ DEE_SHARED_MODEL_ACCESS_MODE_WORLD_WRITABLE,
392+ DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE
393+} DeeSharedModelAccessMode;
394+
395+/**
396+ * dee_shared_model_access_mode_get_type:
397+ *
398+ * The GType of #DeeSharedModelAccessMode
399+ *
400+ * Return value: the #GType of #DeeSharedModelAccessMode
401+ **/
402+GType dee_shared_model_access_mode_get_type (void);
403
404 /**
405 * dee_shared_model_get_type:
406@@ -106,8 +129,9 @@
407
408 DeeModel* dee_shared_model_new_for_peer (DeePeer *peer);
409
410-DeeModel* dee_shared_model_new_with_back_end (const gchar *name,
411- DeeModel *back_end);
412+DeeModel* dee_shared_model_new_with_back_end
413+ (const gchar *name,
414+ DeeModel *back_end);
415
416 const gchar* dee_shared_model_get_swarm_name (DeeSharedModel *self);
417
418@@ -117,7 +141,8 @@
419
420 gboolean dee_shared_model_is_synchronized (DeeSharedModel *self);
421
422-guint dee_shared_model_flush_revision_queue (DeeSharedModel *self);
423+guint dee_shared_model_flush_revision_queue
424+ (DeeSharedModel *self);
425
426 G_END_DECLS
427
428
429=== modified file 'tests/Makefile.am'
430--- tests/Makefile.am 2012-02-28 10:59:18 +0000
431+++ tests/Makefile.am 2012-02-28 10:59:18 +0000
432@@ -58,6 +58,7 @@
433
434 model_helpers = \
435 model-helper-add3rows.c \
436+ model-helper-append1.c \
437 model-helper-change3rows.c \
438 model-helper-clear3rows.c \
439 model-helper-clear6rows.c \
440@@ -66,6 +67,7 @@
441 model-helper-insert1row.c \
442 model-helper-introspect.c \
443 model-helper-remove3rows.c \
444+ model-helper-replace.c \
445 model-helper-schemaless.c \
446 $(NULL)
447
448@@ -88,6 +90,9 @@
449 model_helper_add3rows_SOURCES = model-helper-add3rows.c
450 model_helper_add3rows_LDADD = $(test_dee_LDADD)
451
452+model_helper_append1_SOURCES = model-helper-append1.c
453+model_helper_append1_LDADD = $(test_dee_LDADD)
454+
455 model_helper_change3rows_SOURCES = model-helper-change3rows.c
456 model_helper_change3rows_LDADD = $(test_dee_LDADD)
457
458@@ -112,6 +117,9 @@
459 model_helper_introspect_SOURCES = model-helper-introspect.c
460 model_helper_introspect_LDADD = $(test_dee_LDADD)
461
462+model_helper_replace_SOURCES = model-helper-replace.c
463+model_helper_replace_LDADD = $(test_dee_LDADD)
464+
465 peer_helper_1peer_SOURCES = peer-helper-1peer.c
466 peer_helper_1peer_LDADD = $(test_dee_LDADD)
467
468@@ -135,7 +143,7 @@
469
470 .PHONY: test
471 test:
472- @dbus-test-runner --task gtester \
473+ @dbus-test-runner -m 60 --task gtester \
474 --parameter --verbose \
475 --parameter -o=test-dee-results.xml \
476 --parameter -k \
477@@ -144,7 +152,7 @@
478
479 .PHONY: check-report full-report
480 check-report:
481- @dbus-test-runner --task gtester \
482+ @dbus-test-runner -m 60 --task gtester \
483 --parameter -o=test-dee-results.xml \
484 --parameter -k \
485 --parameter ./test-dee \
486@@ -154,7 +162,7 @@
487 && gnome-open ./test-dee-results.html
488
489 full-report:
490- @dbus-test-runner --task gtester \
491+ @dbus-test-runner -m 60 --task gtester \
492 --parameter -o=test-dee-results.xml \
493 --parameter -k \
494 --parameter -m=slow \
495
496=== added file 'tests/model-helper-append1.c'
497--- tests/model-helper-append1.c 1970-01-01 00:00:00 +0000
498+++ tests/model-helper-append1.c 2012-02-28 10:59:18 +0000
499@@ -0,0 +1,55 @@
500+/*
501+ * Copyright (C) 2010-2012 Canonical Ltd
502+ *
503+ * This program is free software: you can redistribute it and/or modify
504+ * it under the terms of the GNU General Public License version 3 as
505+ * published by the Free Software Foundation.
506+ *
507+ * This program is distributed in the hope that it will be useful,
508+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
509+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
510+ * GNU General Public License for more details.
511+ *
512+ * You should have received a copy of the GNU General Public License
513+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
514+ *
515+ * Authored by
516+ * Michal Hruby <michal.hruby@canonical.com>
517+ *
518+ */
519+
520+#include "config.h"
521+#include <glib.h>
522+#include <glib-object.h>
523+
524+#include <gtx.h>
525+#include <dee.h>
526+
527+/* Joins an existing model, and then tries to append a new row */
528+gint
529+main (gint argc, gchar *argv[])
530+{
531+ DeeModel *model;
532+
533+ g_type_init ();
534+ g_thread_init (NULL);
535+
536+ if (argc == 2)
537+ model = dee_shared_model_new (argv[1]);
538+ else
539+ model = dee_shared_model_new_for_peer ((DeePeer*) dee_client_new (argv[1]));
540+
541+ if (gtx_wait_for_signal (G_OBJECT (model), 300, "notify::synchronized", NULL))
542+ {
543+ g_critical ("Model never synchronized");
544+ return 1;
545+ }
546+
547+ dee_model_append (model, 68, "wumbo");
548+
549+ gtx_yield_main_loop (500);
550+
551+ gtx_assert_last_unref (model);
552+
553+ return 0;
554+}
555
556=== added file 'tests/model-helper-replace.c'
557--- tests/model-helper-replace.c 1970-01-01 00:00:00 +0000
558+++ tests/model-helper-replace.c 2012-02-28 10:59:18 +0000
559@@ -0,0 +1,70 @@
560+/*
561+ * Copyright (C) 2010-2012 Canonical Ltd
562+ *
563+ * This program is free software: you can redistribute it and/or modify
564+ * it under the terms of the GNU General Public License version 3 as
565+ * published by the Free Software Foundation.
566+ *
567+ * This program is distributed in the hope that it will be useful,
568+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
569+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
570+ * GNU General Public License for more details.
571+ *
572+ * You should have received a copy of the GNU General Public License
573+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
574+ *
575+ * Authored by
576+ * Michal Hruby <michal.hruby@canonical.com>
577+ *
578+ */
579+
580+#include "config.h"
581+#include <glib.h>
582+#include <glib-object.h>
583+
584+#include <gtx.h>
585+#include <dee.h>
586+
587+/* Joins an existing model, and then tries to define the column types,
588+ * and add two rows with these types */
589+gint
590+main (gint argc, gchar *argv[])
591+{
592+ DeePeer *peer;
593+ DeeModel *model;
594+
595+ g_type_init ();
596+ g_thread_init (NULL);
597+
598+ if (argc == 2)
599+ {
600+ peer = DEE_PEER (g_object_new (DEE_TYPE_PEER, "swarm-name", argv[1],
601+ "swarm-owner", TRUE, NULL));
602+ model = dee_shared_model_new_for_peer (peer);
603+ dee_model_set_schema (model, "i", "s", NULL);
604+ }
605+ else
606+ {
607+ g_critical ("Missing swarm name! Use \"%s [swarm name]\"", argv[0]);
608+ return 1;
609+ }
610+
611+ if (gtx_wait_for_signal (G_OBJECT (model), 300, "notify::synchronized", NULL))
612+ {
613+ g_critical ("Model never synchronized");
614+ return 1;
615+ }
616+
617+ g_assert (dee_peer_is_swarm_leader (peer));
618+ dee_model_append (model, 27, "skunkworks");
619+ dee_model_append (model, 68, "wumbo");
620+
621+ dee_shared_model_flush_revision_queue (DEE_SHARED_MODEL (model));
622+ gtx_yield_main_loop (500);
623+ /* And we're still the leader */
624+ g_assert (dee_peer_is_swarm_leader (peer));
625+
626+ gtx_assert_last_unref (model);
627+
628+ return 0;
629+}
630
631=== modified file 'tests/test-client-server.c'
632--- tests/test-client-server.c 2011-12-20 14:19:07 +0000
633+++ tests/test-client-server.c 2012-02-28 10:59:18 +0000
634@@ -73,6 +73,8 @@
635 static void test_client_commit (Fixture *fix, gconstpointer data);
636 static void test_multiple_models (Fixture *fix, gconstpointer data);
637 static void test_multiple_models2 (Fixture *fix, gconstpointer data);
638+static void test_remote_append (Fixture *fix, gconstpointer data);
639+static void test_disabled_writes (Fixture *fix, gconstpointer data);
640
641 void
642 test_client_server_interactions_create_suite (void)
643@@ -121,6 +123,10 @@
644 model_setup_null, test_multiple_models, model_teardown_null);
645 g_test_add (DOMAIN"/MultipleModels2", Fixture, 0,
646 model_setup_null, test_multiple_models2, model_teardown_null);
647+ g_test_add (DOMAIN"/RemoteAppend", Fixture, 0,
648+ model_setup, test_remote_append, model_teardown);
649+ g_test_add (DOMAIN"/DisabledWrites", Fixture, 0,
650+ model_setup_null, test_disabled_writes, model_teardown_null);
651 }
652
653 static void
654@@ -248,7 +254,7 @@
655 /* We are leaders - launch the helper */
656 if (gtx_wait_for_command (TESTDIR,
657 SERVER_HELPER (client, PEER_NAME, 4),
658- 2000))
659+ 4000))
660 g_critical ("Peer helper timed out");
661
662 gtx_assert_last_command_status (0);
663@@ -813,3 +819,59 @@
664 gtx_yield_main_loop (200);
665 }
666
667+static void
668+test_remote_append (Fixture *fix, gconstpointer data)
669+{
670+ if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
671+ g_critical ("Model never emitted 'ready' signal");
672+
673+ /* We should be leader before starting the helper process */
674+ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model)));
675+ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 0);
676+
677+ if (gtx_wait_for_command (TESTDIR,
678+ MODEL_HELPER (append1, MODEL_NAME),
679+ 2000))
680+ g_critical ("Model helper timed out");
681+
682+ gtx_assert_last_command_status (0);
683+
684+ /* There should be a new row in the model */
685+ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1);
686+}
687+
688+static void
689+test_disabled_writes (Fixture *fix, gconstpointer data)
690+{
691+ DeePeer *peer;
692+ DeeModelIter *iter;
693+
694+ peer = DEE_PEER (dee_server_new (MODEL_NAME));
695+ fix->model = DEE_MODEL (g_object_new (DEE_TYPE_SHARED_MODEL,
696+ "peer", peer,
697+ "back-end", dee_sequence_model_new (),
698+ "access-mode", DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE, NULL));
699+ dee_model_set_schema (fix->model, "i", "s", NULL);
700+
701+ g_object_unref (G_OBJECT (peer));
702+
703+ if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
704+ g_critical ("Model never emitted 'ready' signal");
705+
706+ /* We should be leader before starting the helper process */
707+ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model)));
708+ dee_model_prepend (fix->model, 81, "eightyone");
709+
710+ if (gtx_wait_for_command (TESTDIR,
711+ MODEL_HELPER (append1, MODEL_NAME),
712+ 2000))
713+ g_critical ("Model helper timed out");
714+
715+ gtx_assert_last_command_status (0);
716+
717+ /* The peer tried to append a row, but we should have ignored that */
718+ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1);
719+ iter = dee_model_get_first_iter (fix->model);
720+ g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "eightyone");
721+}
722+
723
724=== modified file 'tests/test-model-interactions.c'
725--- tests/test-model-interactions.c 2012-02-28 10:59:18 +0000
726+++ tests/test-model-interactions.c 2012-02-28 10:59:18 +0000
727@@ -55,6 +55,9 @@
728 static void test_row_inserted (Fixture *fix, gconstpointer data);
729 static void test_schemaless_leader (Fixture *fix, gconstpointer data);
730 static void test_introspect (Fixture *fix, gconstpointer data);
731+static void test_ownership_stealing (Fixture *fix, gconstpointer data);
732+static void test_remote_append (Fixture *fix, gconstpointer data);
733+static void test_disabled_writes (Fixture *fix, gconstpointer data);
734
735 void
736 test_model_interactions_create_suite (void)
737@@ -87,6 +90,12 @@
738 model_setup_null, test_schemaless_leader, model_teardown_null);
739 g_test_add (DOMAIN"/Introspect", Fixture, 0,
740 model_setup, test_introspect, model_teardown);
741+ g_test_add (DOMAIN"/OwnershipStealing", Fixture, 0,
742+ model_setup, test_ownership_stealing, model_teardown);
743+ g_test_add (DOMAIN"/RemoteAppend", Fixture, 0,
744+ model_setup, test_remote_append, model_teardown);
745+ g_test_add (DOMAIN"/DisabledWrites", Fixture, 0,
746+ model_setup_null, test_disabled_writes, model_teardown_null);
747 }
748
749 static void
750@@ -592,3 +601,100 @@
751 gtx_assert_last_command_status (0);
752 }
753
754+static void
755+stealing_notify (GObject *object, GParamSpec *pspec, Fixture *fix)
756+{
757+ g_assert (DEE_IS_PEER (object));
758+
759+ if (!dee_peer_is_swarm_leader (DEE_PEER (object)))
760+ {
761+ g_object_set_data (G_OBJECT (fix->model), "stealing-success",
762+ GINT_TO_POINTER (1));
763+ }
764+}
765+
766+static void
767+test_ownership_stealing (Fixture *fix, gconstpointer data)
768+{
769+ if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
770+ g_critical ("Model never emitted 'ready' signal");
771+
772+ /* We should be leader before starting the helper process */
773+ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model)));
774+ /* But loose it later on */
775+ g_signal_connect (dee_shared_model_get_peer (DEE_SHARED_MODEL (fix->model)),
776+ "notify::swarm-leader", G_CALLBACK (stealing_notify), fix);
777+
778+ if (gtx_wait_for_command (TESTDIR,
779+ MODEL_HELPER (replace, MODEL_NAME),
780+ 2000))
781+ g_critical ("Model helper timed out");
782+
783+ gtx_assert_last_command_status (0);
784+ gtx_flush_sources (FALSE);
785+
786+ g_assert (g_object_get_data (G_OBJECT (fix->model), "stealing-success"));
787+ g_assert_cmpuint (dee_model_get_n_rows (fix->model), >, 0);
788+}
789+
790+static void
791+test_remote_append (Fixture *fix, gconstpointer data)
792+{
793+ if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
794+ g_critical ("Model never emitted 'ready' signal");
795+
796+ /* We should be leader before starting the helper process */
797+ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model)));
798+ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 0);
799+
800+ if (gtx_wait_for_command (TESTDIR,
801+ MODEL_HELPER (append1, MODEL_NAME),
802+ 2000))
803+ g_critical ("Model helper timed out");
804+
805+ gtx_assert_last_command_status (0);
806+
807+ /* There should be a new row in the model */
808+ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1);
809+}
810+
811+static void
812+test_disabled_writes (Fixture *fix, gconstpointer data)
813+{
814+ DeePeer *peer;
815+ DeeModelIter *iter;
816+
817+ peer = dee_peer_new (MODEL_NAME);
818+ fix->model = DEE_MODEL (
819+ g_object_new (DEE_TYPE_SHARED_MODEL,
820+ "peer", peer,
821+ "back-end", dee_sequence_model_new (),
822+ "access-mode", DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE,
823+ NULL));
824+ dee_model_set_schema (fix->model, "i", "s", NULL);
825+
826+ g_object_unref (G_OBJECT (peer));
827+
828+ if (gtx_wait_for_signal (G_OBJECT (fix->model), TIMEOUT, "notify::synchronized", NULL))
829+ g_critical ("Model never emitted 'ready' signal");
830+
831+ /* We should be leader before starting the helper process */
832+ g_assert (dee_shared_model_is_leader (DEE_SHARED_MODEL (fix->model)));
833+ dee_model_prepend (fix->model, 81, "eightyone");
834+
835+ if (gtx_wait_for_command (TESTDIR,
836+ MODEL_HELPER (append1, MODEL_NAME),
837+ 2000))
838+ g_critical ("Model helper timed out");
839+
840+ gtx_assert_last_command_status (0);
841+
842+ /* The peer tried to append a row, but we should have ignored that */
843+ g_assert_cmpuint (dee_model_get_n_rows (fix->model), ==, 1);
844+ iter = dee_model_get_first_iter (fix->model);
845+ g_assert_cmpstr (dee_model_get_string (fix->model, iter, 1), ==, "eightyone");
846+
847+ gtx_assert_last_unref (fix->model);
848+ fix->model = NULL;
849+}
850+
851
852=== modified file 'vapi/dee-1.0.vapi'
853--- vapi/dee-1.0.vapi 2012-02-21 11:38:02 +0000
854+++ vapi/dee-1.0.vapi 2012-02-28 10:59:18 +0000
855@@ -98,11 +98,14 @@
856 public virtual unowned string get_swarm_leader ();
857 public unowned string get_swarm_name ();
858 public virtual bool is_swarm_leader ();
859+ public bool is_swarm_owner ();
860 [CCode (array_length = false, array_null_terminated = true)]
861 public virtual string[] list_peers ();
862 public string swarm_leader { get; }
863 [NoAccessorMethod]
864 public string swarm_name { owned get; set construct; }
865+ [NoAccessorMethod]
866+ public bool swarm_owner { get; construct; }
867 public virtual signal void connection_acquired (GLib.DBusConnection connection);
868 public virtual signal void connection_closed (GLib.DBusConnection connection);
869 public virtual signal void peer_found (string name);
870@@ -156,6 +159,8 @@
871 public bool is_synchronized ();
872 [CCode (has_construct_function = false, type = "DeeModel*")]
873 public SharedModel.with_back_end (string name, owned Dee.Model back_end);
874+ [NoAccessorMethod]
875+ public Dee.SharedModelAccessMode access_mode { get; construct; }
876 public Dee.Peer peer { get; construct; }
877 [NoAccessorMethod]
878 public bool synchronized { get; }
879@@ -305,6 +310,11 @@
880 public static Dee.ModelReader new_for_uint32_column (uint column);
881 public string read (Dee.Model model, Dee.ModelIter iter);
882 }
883+ [CCode (cheader_filename = "dee.h", cprefix = "DEE_SHARED_MODEL_ACCESS_MODE_")]
884+ public enum SharedModelAccessMode {
885+ WORLD_WRITABLE,
886+ LEADER_WRITABLE
887+ }
888 [CCode (cheader_filename = "dee.h", cprefix = "DEE_TERM_MATCH_")]
889 [Flags]
890 public enum TermMatchFlag {

Subscribers

People subscribed via source and target branches

to all changes: