r6890 - in dumbhippo/trunk/client: common/ddm common/hippo linux



Author: otaylor
Date: 2007-11-09 17:06:59 -0600 (Fri, 09 Nov 2007)
New Revision: 6890

Added:
   dumbhippo/trunk/client/common/ddm/ddm-rule.c
   dumbhippo/trunk/client/common/ddm/test-rules.c
   dumbhippo/trunk/client/common/ddm/test-utils.c
   dumbhippo/trunk/client/common/ddm/test-utils.h
Modified:
   dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c
   dumbhippo/trunk/client/common/ddm/ddm-data-model-internal.h
   dumbhippo/trunk/client/common/ddm/ddm-data-model.c
   dumbhippo/trunk/client/common/ddm/ddm-data-model.h
   dumbhippo/trunk/client/common/ddm/ddm-data-resource-internal.h
   dumbhippo/trunk/client/common/ddm/ddm-data-resource.c
   dumbhippo/trunk/client/common/ddm/ddm-rule.h
   dumbhippo/trunk/client/common/ddm/static-file-backend.c
   dumbhippo/trunk/client/common/ddm/test-data.xml
   dumbhippo/trunk/client/common/ddm/test-local-data.xml
   dumbhippo/trunk/client/common/ddm/test-multipart-fetch.c
   dumbhippo/trunk/client/common/ddm/test-notification.c
   dumbhippo/trunk/client/common/ddm/test-static-file-backend.c
   dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c
   dumbhippo/trunk/client/linux/Makefile-libddm.am
Log:
test-utils.[ch] test-static-file-backend.c test-notifcation.c
  test-multipart-fetch.c: Factor out common test code

hippo-data-model-backend.c ddm-data-model-dbus.c: Fix 
  value.u.string = FALSE.

ddm-rule.[ch]: Add internal DDMRule object

ddm-data-model.[ch]: Add ddm_data_model_add_rule()

ddm-data-model.{c,-internal.h}, ddm-data-resource.{c,-internal.h}:
  First cut implementation of rules, with some stunning inefficiency.
  (mostly in _ddm_data_model_find_{source,targets}.

test-rules.c: Simple test of rules


Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -442,7 +442,7 @@
     ddm_data_model_set_global_resource(dbus_model->ddm_model, global_resource);
     
     value.type = DDM_DATA_BOOLEAN;
-    value.u.string = FALSE;
+    value.u.boolean = FALSE;
 
     ddm_data_resource_update_property(global_resource,
                                       ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "online"),

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model-internal.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model-internal.h	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model-internal.h	2007-11-09 23:06:59 UTC (rev 6890)
@@ -4,6 +4,7 @@
 
 #include "ddm-data-model.h"
 #include "ddm-data-resource.h"
+#include "ddm-rule.h"
 #include "ddm-work-item.h"
 
 G_BEGIN_DECLS
@@ -21,6 +22,20 @@
 
 DDMClient *_ddm_data_model_get_local_client (DDMDataModel *model);
 
+/* Result should be freed by caller */
+GSList *_ddm_data_model_find_sources (DDMDataModel *model,
+                                      const char   *source_class_id,
+                                      DDMCondition *condition);
+GSList *_ddm_data_model_find_targets (DDMDataModel *model,
+                                      const char   *target_class_id,
+                                      DDMCondition *condition);
+
+/* Result owned by model */
+GSList *_ddm_data_model_get_target_rules(DDMDataModel *model,
+                                         const char   *class_id);
+GSList *_ddm_data_model_get_source_rules(DDMDataModel *model,
+                                         const char   *class_id);
+
 G_END_DECLS
 
 #endif /* __DDM_DATA_MODEL_INTERNAL__H__ */

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -10,6 +10,7 @@
 #include "ddm-data-resource-internal.h"
 #include "ddm-data-query-internal.h"
 #include "ddm-local-client.h"
+#include "ddm-rule.h"
 
 static void      ddm_data_model_init                (DDMDataModel       *model);
 static void      ddm_data_model_class_init          (DDMDataModelClass  *klass);
@@ -24,6 +25,9 @@
     void *backend_data;
     GFreeFunc free_backend_data_func;
 
+    GHashTable *rules_by_target;
+    GHashTable *rules_by_source;
+
     DDMClient *local_client;
     
     GHashTable *resources;
@@ -58,11 +62,29 @@
 G_DEFINE_TYPE(DDMDataModel, ddm_data_model, G_TYPE_OBJECT);
 
 static void
+free_rule_list(GSList *rule_list)
+{
+    g_slist_foreach(rule_list, (GFunc)ddm_rule_free, NULL);
+    g_slist_free(rule_list);
+}
+
+static void
 ddm_data_model_init(DDMDataModel *model)
 {
     model->resources = g_hash_table_new_full(g_str_hash, g_str_equal,
                                              NULL,
                                              (GDestroyNotify)ddm_data_resource_unref);
+
+    /* rules_by_target and rules_by_source together own the reference to the rule.
+     * We consider the reference owned by rules_by_target
+     */
+    model->rules_by_target = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                   (GDestroyNotify)g_free,
+                                                   (GDestroyNotify)free_rule_list);
+    model->rules_by_source = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                   (GDestroyNotify)g_free,
+                                                   (GDestroyNotify)g_slist_free);
+    
     model->changed_resources = g_hash_table_new(g_direct_hash, NULL);
     model->work_items = g_queue_new();
 
@@ -648,6 +670,47 @@
 }
 
 static void
+get_values_foreach(gpointer key,
+                   gpointer value,
+                   gpointer data)
+{
+    GSList **values = data;
+
+    *values = g_slist_prepend(*values, value);
+}
+
+static GSList *
+hash_table_get_values(GHashTable *hash_table)
+{
+    GSList *values = NULL;
+
+    g_hash_table_foreach(hash_table, get_values_foreach, &values);
+
+    return values;
+}
+
+static void
+data_model_flush_rules(DDMDataModel *model)
+{
+    GSList *resources_to_process;
+    GSList *l;
+    
+    if (g_hash_table_size(model->changed_resources) == 0)
+        return;
+
+    /* We need to snapshot the changed resources, since the set of changd resources
+     * may be extended as we process rules.
+     */
+    resources_to_process = hash_table_get_values(model->changed_resources);
+
+    for (l = resources_to_process; l; l = l->next) {
+        _ddm_data_resource_update_rule_properties(l->data);
+    }
+
+    g_slist_free(resources_to_process);
+}
+
+static void
 data_model_flush_notifications(DDMDataModel *model)
 {
     DDMClientNotificationSet *notification_set;
@@ -728,11 +791,145 @@
     if (model->backend->flush)
         model->backend->flush(model, model->backend_data);
 
+    data_model_flush_rules(model);
     data_model_flush_notifications(model);
     data_model_flush_work_items(model);
 }
 
 void
+ddm_data_model_add_rule (DDMDataModel       *model,
+                         const char         *target_class_id,
+                         const char         *target_property,
+                         const char         *source_class_id,
+                         DDMDataCardinality  cardinality,
+                         gboolean            default_include,
+                         const char         *default_children,
+                         const char         *condition)
+{
+    DDMRule *rule;
+    GSList *target_rules;
+    GSList *source_rules;
+
+    g_return_if_fail(DDM_IS_DATA_MODEL(model));
+    g_return_if_fail(target_class_id != NULL);
+    g_return_if_fail(target_property != NULL);
+    g_return_if_fail(source_class_id != NULL);
+    g_return_if_fail(cardinality == DDM_DATA_CARDINALITY_01 || cardinality == DDM_DATA_CARDINALITY_N);
+    g_return_if_fail(condition != NULL);
+
+    rule = ddm_rule_new(target_class_id, target_property,
+                        source_class_id,
+                        cardinality, default_include, default_children,
+                        condition);
+    
+    if (rule == NULL) /* failed validation, will have warned */
+        return;
+
+    target_rules = g_hash_table_lookup(model->rules_by_target, target_class_id);
+    target_rules = g_slist_prepend(target_rules, rule);
+    g_hash_table_replace(model->rules_by_target,
+                         g_strdup(target_class_id),
+                         target_rules);
+    
+    source_rules = g_hash_table_lookup(model->rules_by_source, source_class_id);
+    source_rules = g_slist_prepend(source_rules, rule);
+    g_hash_table_replace(model->rules_by_source,
+                         g_strdup(source_class_id),
+                         source_rules);
+}
+
+typedef struct {
+    const char *class_id;
+    DDMCondition *condition;
+    gboolean find_sources;
+    GSList *results;
+} FindResourcesClosure;
+
+static void
+find_resources_foreach(gpointer key,
+                       gpointer value,
+                       gpointer data)
+{
+    DDMDataResource *resource = value;
+    FindResourcesClosure *closure = data;
+    const char *class_id = ddm_data_resource_get_class_id(resource);
+    gboolean match;
+
+    if (class_id == NULL || strcmp(class_id, closure->class_id) != 0)
+        return;
+
+    if (closure->find_sources)
+        match = ddm_condition_matches_source(closure->condition, resource);
+    else
+        match = ddm_condition_matches_target(closure->condition, resource);
+
+    if (!match)
+        return;
+
+    closure->results = g_slist_prepend(closure->results, resource);
+}
+
+static GSList *
+find_resources(DDMDataModel *model,
+               const char   *class_id,
+               DDMCondition *condition,
+               gboolean      find_sources)
+{
+    /* This is the most inefficient implementation possible; to improve, you'd
+     * want to:
+     *
+     *  A) Index resources in the model by class_id
+     *  B) Index resources in the model by property values that rules match upon
+     *
+     * It might be easiest to do B with an explicit:
+     *
+     *  ddm_data_model_add_index(DDMDataModel *model,
+     *                           const char   *property_uri);
+     */
+    
+    FindResourcesClosure closure;
+
+    closure.class_id = class_id;
+    closure.condition = condition;
+    closure.find_sources = find_sources;
+    closure.results = NULL;
+
+    g_hash_table_foreach(model->resources, find_resources_foreach, &closure);
+
+    return closure.results;
+}
+
+GSList *
+_ddm_data_model_find_sources(DDMDataModel *model,
+                             const char   *source_class_id,
+                             DDMCondition *condition)
+{
+    return find_resources(model, source_class_id, condition, TRUE);
+}
+
+GSList *
+_ddm_data_model_find_targets(DDMDataModel *model,
+                             const char   *target_class_id,
+                             DDMCondition *condition)
+{
+    return find_resources(model, target_class_id, condition, FALSE);
+}
+
+GSList *
+_ddm_data_model_get_target_rules(DDMDataModel *model,
+                                 const char   *class_id)
+{
+    return g_hash_table_lookup(model->rules_by_target, class_id);
+}
+
+GSList *
+_ddm_data_model_get_source_rules(DDMDataModel *model,
+                                 const char   *class_id)
+{
+    return g_hash_table_lookup(model->rules_by_source, class_id);
+}
+
+void
 _ddm_data_model_query_answered (DDMDataModel *model,
                                 DDMDataQuery *query)
 {

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model.h	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model.h	2007-11-09 23:06:59 UTC (rev 6890)
@@ -38,6 +38,16 @@
                                                  GFreeFunc                  free_backend_data_func);
 /* Used testing purposes; you can't call query or update on such a backend */
 DDMDataModel *ddm_data_model_new_no_backend     (void);
+
+void ddm_data_model_add_rule (DDMDataModel       *model,
+                              const char         *target_class_id,
+                              const char         *target_property,
+                              const char         *source_class_id,
+                              DDMDataCardinality  cardinality,
+                              gboolean            default_include,
+                              const char         *default_children,
+                              const char         *condition);
+
 gboolean      ddm_data_model_get_connected      (DDMDataModel   *model);
 gboolean      ddm_data_model_is_ready           (DDMDataModel   *model);
 
@@ -66,6 +76,7 @@
 DDMDataQuery *ddm_data_model_update_params      (DDMDataModel   *model,
                                                  const char     *method,
                                                  GHashTable     *params);
+
 DDMDataResource *ddm_data_model_lookup_resource (DDMDataModel   *model,
                                                  const char     *resource_id);
 DDMDataResource *ddm_data_model_ensure_resource (DDMDataModel   *model,

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-resource-internal.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-resource-internal.h	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-resource-internal.h	2007-11-09 23:06:59 UTC (rev 6890)
@@ -31,6 +31,8 @@
 void _ddm_data_resource_send_local_notifications (DDMDataResource    *resource,
                                                   GSList             *changed_properties);
 
+void _ddm_data_resource_update_rule_properties(DDMDataResource *resource);
+
 void _ddm_data_resource_resolve_notifications (DDMDataResource          *resource,
                                                DDMClientNotificationSet *notification_set);
 

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-resource.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-resource.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-resource.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -7,6 +7,7 @@
 #include "ddm-data-fetch.h"
 #include "ddm-data-model-internal.h"
 #include "ddm-data-resource-internal.h"
+#include "ddm-rule.h"
 
 typedef enum {
     CONNECTION_TYPE_ANY,
@@ -19,11 +20,24 @@
 typedef struct _DataClient DataClient;
 
 struct _DDMDataProperty {
+    DDMDataResource *resource;
+    
     DDMQName *qname;
     DDMDataValue value;
     DDMDataFetch *default_children;
     guint cardinality : 4;
     guint default_include : 1;
+
+    /* We could use a flag bit and smaller allocation to save space for non-rule-properties */
+
+    /* Rule this property came from or NULL */
+    DDMRule *rule;
+
+    /* List of sources for this rule. For CARDINALITY_N, this is the same as value.u.list,
+     * but for CARDINALITY_01, we pick just one source out out of the list of sources.
+     */
+    GSList *rule_sources;
+    
 };
 
 struct _DataConnection {
@@ -51,6 +65,9 @@
     char *class_id;
     gboolean local;
 
+    /* Properties that reference this resource as the source of a rule */
+    GSList *referencing_rule_properties;
+
     GSList *clients;
     GSList *connections; /* Local connections */
     GSList *properties;
@@ -945,6 +962,7 @@
 {
     DDMDataProperty *property = g_new0(DDMDataProperty, 1);
 
+    property->resource = resource;
     property->qname = qname;
     property->cardinality = cardinality;
     property->value.type = DDM_DATA_NONE;
@@ -962,6 +980,16 @@
     ddm_data_property_free(property);
 }
 
+static void
+mark_property_changed(DDMDataResource *resource,
+                      DDMDataProperty *property)
+{
+    if (g_slist_find(resource->changed_properties, property->qname) == NULL) {
+        resource->changed_properties = g_slist_prepend(resource->changed_properties, property->qname);
+        _ddm_data_model_mark_changed(resource->model, resource);
+    }
+}
+
 /* return value is whether something changed (we need to emit notification) */
 gboolean
 ddm_data_resource_update_property(DDMDataResource    *resource,
@@ -1116,10 +1144,8 @@
             property->default_children = ddm_data_fetch_from_string(default_children);
     }
 
-    if (changed && g_slist_find(resource->changed_properties, property->qname) == NULL) {
-        resource->changed_properties = g_slist_prepend(resource->changed_properties, property->qname);
-        _ddm_data_model_mark_changed(resource->model, resource);
-    }
+    if (changed)
+        mark_property_changed(resource, property);
 
     return changed;
 }
@@ -1514,7 +1540,339 @@
     }
 }
 
+static DDMDataProperty *
+resource_ensure_rule_property(DDMDataResource *resource,
+                              DDMRule         *rule)
+{
+    GSList *l;
+    DDMDataProperty *property;
+    
+    for (l = resource->properties; l; l = l->next) {
+        property = l->data;
+        if (property->qname == rule->target_property)
+            return property;
+    }
+
+    property = g_new0(DDMDataProperty, 1);
+
+    property->resource = resource;
+    property->qname = rule->target_property;
+    property->cardinality = rule->cardinality;
+    property->value.type = DDM_DATA_NONE;
+    property->default_include = rule->default_include;
+    property->default_children = rule->default_children;
+    if (property->default_children)
+        ddm_data_fetch_ref(property->default_children);
+
+    property->rule = rule;
+    property->rule_sources = NULL;
+
+    resource->properties = g_slist_prepend(resource->properties, property);
+
+    return property;
+}
+
+static void
+property_update_value_from_rule_sources(DDMDataProperty *property)
+{
+    gboolean changed = FALSE;
+
+    if (property->cardinality == DDM_DATA_CARDINALITY_N) {
+        if (property->rule_sources == NULL) {
+            if (DDM_DATA_BASE(property->value.type) != DDM_DATA_NONE) {
+                g_assert(property->value.type == (DDM_DATA_RESOURCE | DDM_DATA_LIST));
+                
+                g_slist_free(property->value.u.list);
+                property->value.type = DDM_DATA_NONE;
+                changed = TRUE;
+            }
+        } else {
+            if (DDM_DATA_BASE(property->value.type) != DDM_DATA_NONE) {
+                g_assert(property->value.type == (DDM_DATA_RESOURCE | DDM_DATA_LIST));
+                g_slist_free(property->value.u.list);
+            } else {
+                property->value.type = DDM_DATA_RESOURCE | DDM_DATA_LIST;
+            }
+            
+            property->value.u.list = g_slist_copy(property->rule_sources);
+            changed = TRUE;
+        }
+    } else { /* DDM_DATA_CARDINALITY_01 */
+        if (property->rule_sources == NULL) {
+            if (DDM_DATA_BASE(property->value.type) != DDM_DATA_NONE) {
+                g_assert(property->value.type == DDM_DATA_RESOURCE);
+                
+                property->value.type = DDM_DATA_NONE;
+                changed = TRUE;
+            }
+        } else {
+            if (DDM_DATA_BASE(property->value.type) != DDM_DATA_NONE) {
+                g_assert(property->value.type == DDM_DATA_RESOURCE);
+
+                if (property->rule_sources->data != property->value.u.resource) {
+                    property->value.u.resource = property->rule_sources->data;
+                    changed = TRUE;
+                }
+            } else {
+                property->value.type = DDM_DATA_RESOURCE;
+                property->value.u.resource = property->rule_sources->data;
+                changed = TRUE;
+            }
+        }
+    }
+
+    if (changed)
+        mark_property_changed(property->resource, property);
+
+}
+
+static int
+compare_resources(gconstpointer a,
+                  gconstpointer b)
+{
+    return ((size_t)a < (size_t)b) ? -1 : (((size_t)a == (size_t)b) ? 0 : 1);
+}
+
+static void
+property_add_rule_source(DDMDataProperty *property,
+                         DDMDataResource *source)
+{
+    GSList *l;
+    GSList *prev = NULL;
+
+    l = property->rule_sources;
+    while (TRUE) {
+        int cmp;
+        
+        if (l) {
+            cmp = compare_resources(source, l->data);
+        } else {
+            cmp = 1;
+        }
+
+        if (cmp == 0)
+            return;
+        else if (cmp > 0) {
+            GSList *node = g_slist_prepend(l, source);
+            
+            if (prev)
+                prev->next = node;
+            else
+                property->rule_sources = node;
+
+            g_debug("Adding rule source %s to %s:%s#%s",
+                    source->resource_id, property->resource->resource_id,
+                    property->qname->uri, property->qname->name);
+            
+            source->referencing_rule_properties = g_slist_prepend(source->referencing_rule_properties, property->resource);
+            property_update_value_from_rule_sources(property);
+            
+            return;
+        }
+
+        prev = l;
+        l = l->next;
+    }
+}
+
+static void
+property_remove_rule_source(DDMDataProperty *property,
+                            DDMDataResource *source)
+{
+    GSList *l;
+    GSList *prev = NULL;
+
+    for (l = property->rule_sources; l; l = l->next) {
+        int cmp = compare_resources(source, l->data);
+        if (cmp == 0) {
+            if (prev)
+                prev->next = l->next;
+            else
+                property->rule_sources = l->next;
+            g_slist_free1(l);
+
+            source->referencing_rule_properties = g_slist_remove(source->referencing_rule_properties, property->resource);
+            property_update_value_from_rule_sources(property);
+            
+            g_debug("Removing rule source %s from %s:%s#%s",
+                    source->resource_id, property->resource->resource_id,
+                    property->qname->uri, property->qname->name);
+    
+            return;
+        } else if (cmp > 0) {
+            break;
+        }
+
+        prev = l;
+    }
+}
+
+/* Diff two lists sorted by the comparison function 'compare', and create
+ * two new lists (free with g_slist_free) of the values that were added
+ * or removed.
+ */
+static void
+find_deltas(GSList      *list,
+            GSList      *new_list,
+            GCompareFunc compare,
+            GSList     **added,
+            GSList     **removed)
+{
+    GSList *l = list;
+    GSList *m = new_list;
+
+    *added = NULL;
+    *removed = NULL;
+
+    while (l || m) {
+        int cmp;
+        
+        if (l && m)
+            cmp = (*compare)(l->data, m->data);
+        else if (l)
+            cmp = -1;
+        else
+            cmp = 1;
+
+        if (cmp < 0) {
+            /* value to remove */
+            *removed = g_slist_prepend(*removed, l->data);
+
+            l = l->next;
+        } else if (cmp > 0) {
+            /* value to add */
+            *added = g_slist_prepend(*added, m->data);
+
+            m = m->next;
+        } else {
+            l = l->next;
+            m = m->next;
+        }
+    }
+}
+
+/* takes ownership of sources */
+static void
+property_update_rule_sources(DDMDataProperty *property,
+                             GSList          *sources)
+{
+    GSList *added;
+    GSList *removed;
+    GSList *l;
+
+    sources = g_slist_sort(sources, compare_resources);
+    
+    find_deltas(property->rule_sources, sources, compare_resources, &added, &removed);
+
+    if (added == NULL && removed == NULL) {
+        g_slist_free(sources);
+        return;
+    }
+
+    g_debug("Recomputed rule sources for %s:%s#%s",
+            property->resource->resource_id,
+            property->qname->uri, property->qname->name);
+            
+    g_slist_free(property->rule_sources);
+    property->rule_sources = sources;
+
+    for (l = removed; l; l = l->next) {
+        DDMDataResource *source = l->data;
+        g_debug("   removed %s", source->resource_id);
+        source->referencing_rule_properties = g_slist_remove(source->referencing_rule_properties,
+                                                             property);
+    }
+
+    for (l = added; l; l = l->next) {
+        DDMDataResource *source = l->data;
+        g_debug("   added %s", source->resource_id);
+        source->referencing_rule_properties = g_slist_prepend(source->referencing_rule_properties,
+                                                              property);
+    }
+    
+    property_update_value_from_rule_sources(property);
+
+    g_slist_free(added);
+    g_slist_free(removed);
+}
+
 void
+_ddm_data_resource_update_rule_properties(DDMDataResource *resource)
+{
+    GSList *target_rules;
+    GSList *source_rules;
+    GSList *l;
+    
+    /* First remove any property values that previously referenced this resource
+     * that no longer apply
+     */
+    l = resource->referencing_rule_properties;
+    while (l) {
+        DDMDataProperty *property = l->data;
+        DDMCondition *condition;
+        GSList *l_next = l->next;
+
+        condition = ddm_rule_build_source_condition(property->rule, property->resource);
+
+        if (!ddm_condition_matches_source(condition, resource))
+            property_remove_rule_source(property, resource);
+        
+        ddm_condition_free(condition);
+        
+        l = l_next;
+    }
+
+    /* Now find properties that currently reference this resource and, if necessary
+     * add them.
+     */
+    source_rules = _ddm_data_model_get_source_rules(resource->model, resource->class_id);
+    for (l = source_rules; l; l = l->next) {
+        DDMRule *rule = l->data;
+        DDMCondition *condition;
+        GSList *targets;
+        GSList *ll;
+
+        condition = ddm_rule_build_target_condition(rule, resource);
+        targets = _ddm_data_model_find_targets(resource->model,
+                                               rule->target_class_id,
+                                               condition);
+
+        for (ll = targets; ll; ll = ll->next) {
+            DDMDataResource *target = ll->data;
+            DDMDataProperty *property;
+            
+            property = resource_ensure_rule_property(target, rule);
+            property_add_rule_source(property, resource);
+        }
+
+        ddm_condition_free(condition);
+        g_slist_free(targets);
+    }
+
+    /* Finally, for all rules with this class as target, find all matching sources, and
+     * merge them into the current property value
+     */
+    target_rules = _ddm_data_model_get_target_rules(resource->model, resource->class_id);
+    for (l = target_rules; l; l = l->next) {
+        DDMRule *rule = l->data;
+        DDMCondition *condition;
+        DDMDataProperty *property;
+        GSList *sources;
+
+        condition = ddm_rule_build_source_condition(rule, resource);
+        sources = _ddm_data_model_find_sources(resource->model,
+                                               rule->source_class_id,
+                                               condition);
+
+        property = resource_ensure_rule_property(resource, rule);
+
+        /* update_rule_sources takes ownership of sources */
+        property_update_rule_sources(property, sources);
+        ddm_condition_free(condition);
+    }
+}
+
+void
 _ddm_data_resource_resolve_notifications (DDMDataResource          *resource,
                                           DDMClientNotificationSet *notification_set)
 {

Added: dumbhippo/trunk/client/common/ddm/ddm-rule.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-rule.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/ddm-rule.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -0,0 +1,70 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#include "ddm-rule.h"
+
+DDMRule *
+ddm_rule_new (const char         *target_class_id,
+	      const char         *target_property_uri,
+	      const char         *source_class_id,
+	      DDMDataCardinality  cardinality,
+	      gboolean            default_include,
+	      const char         *default_children_str,
+	      const char         *condition_str)
+{
+    DDMQName *target_property;
+    DDMCondition *condition;
+    DDMDataFetch *default_children;
+    DDMRule *rule;
+
+    target_property = ddm_qname_from_uri(target_property_uri);
+    if (target_property == NULL) /* Will already have warned */
+        return NULL;
+    
+    condition = ddm_condition_from_string(condition_str);
+    if (condition == NULL) /* Will already have warned */
+        return NULL;
+
+    if (default_children_str) {
+        default_children = ddm_data_fetch_from_string(default_children_str);
+        if (default_children == NULL) /* Will have already warned */
+            return NULL;
+    } else {
+        default_children = NULL;
+    }
+
+    rule = g_new0(DDMRule, 1);
+    rule->target_class_id = g_strdup(target_class_id);
+    rule->target_property= target_property;
+    rule->source_class_id = g_strdup(source_class_id);
+    rule->cardinality = cardinality;
+    rule->default_children = default_children;
+    rule->condition = condition;
+
+    return rule;
+}
+
+void
+ddm_rule_free (DDMRule *rule)
+{
+    g_free(rule->target_class_id);
+    g_free(rule->source_class_id);
+    if (rule->default_children)
+        ddm_data_fetch_unref(rule->default_children);
+    ddm_condition_free(rule->condition);
+    
+    g_free(rule);
+}
+
+DDMCondition *
+ddm_rule_build_target_condition(DDMRule         *rule,
+                                DDMDataResource *source_resource)
+{
+    return ddm_condition_reduce_source(rule->condition, source_resource);
+}
+
+DDMCondition *
+ddm_rule_build_source_condition(DDMRule         *rule,
+                                DDMDataResource *target_resource)
+{
+    return ddm_condition_reduce_target(rule->condition, target_resource);
+}

Modified: dumbhippo/trunk/client/common/ddm/ddm-rule.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-rule.h	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/ddm-rule.h	2007-11-09 23:06:59 UTC (rev 6890)
@@ -3,6 +3,7 @@
 #ifndef __DDM_RULE_H__
 #define __DDM_RULE_H__
 
+#include "ddm-data-fetch.h"
 #include "ddm-data-resource.h"
 
 G_BEGIN_DECLS
@@ -50,6 +51,16 @@
     } u;
 };
 
+struct _DDMRule {
+    char *target_class_id;
+    DDMQName *target_property;
+    char *source_class_id;
+    DDMDataCardinality cardinality;
+    gboolean default_include;
+    DDMDataFetch *default_children;
+    DDMCondition *condition;
+};
+
 typedef struct {
     DDMCondition *left;
     DDMCondition *right;
@@ -108,6 +119,13 @@
                             const char         *default_children,
                             const char         *condition);
 
+DDMQName           *ddm_rule_get_target_property (DDMRule *rule);
+DDMDataCardinality *ddm_rule_get_cardinality     (DDMRule *rule);
+const char         *ddm_rule_get_target_class_id (DDMRule *rule);
+const char         *ddm_rule_get_source_class_id (DDMRule *rule);
+
+void ddm_rule_free (DDMRule *rule);
+
 DDMCondition *ddm_rule_build_target_condition(DDMRule         *rule,
                                               DDMDataResource *source_resource);
 DDMCondition *ddm_rule_build_source_condition(DDMRule         *rule,

Modified: dumbhippo/trunk/client/common/ddm/static-file-backend.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/static-file-backend.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/static-file-backend.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -343,6 +343,27 @@
     static_file_flush
 };
 
+static void
+setup_global_resource(DDMDataModel *model)
+{
+    DDMDataResource *global_resource;
+    DDMDataValue value;
+
+    global_resource = ddm_data_model_ensure_resource(model,
+                                                     DDM_GLOBAL_RESOURCE, DDM_GLOBAL_RESOURCE_CLASS);
+    ddm_data_model_set_global_resource(model, global_resource);
+    
+    value.type = DDM_DATA_BOOLEAN;
+    value.u.boolean = TRUE;
+
+    ddm_data_resource_update_property(global_resource,
+                                      ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "online"),
+                                      DDM_DATA_UPDATE_REPLACE,
+                                      DDM_DATA_CARDINALITY_1,
+                                      FALSE, NULL,
+                                      &value);
+}
+
 DDMDataModel*
 ddm_static_file_model_new (const char *filename,
                            GError    **error)
@@ -353,6 +374,8 @@
 
     static_file_model = get_static_file_model(model);
     static_file_model->backend_model = ddm_data_model_new_no_backend();
+
+    setup_global_resource(model);
     
     if (!ddm_static_file_parse(filename, static_file_model->backend_model, error)) {
         /* FIXME: cleanup and free the models */

Modified: dumbhippo/trunk/client/common/ddm/test-data.xml
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-data.xml	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/test-data.xml	2007-11-09 23:06:59 UTC (rev 6890)
@@ -7,11 +7,13 @@
     </resource>
     <resource xmlns="http://mugshot.org/p/o/user"; m:resourceId="user/USER2">
 	<name m:type="+s">Sally Smith</name>
+	<aim m:type="s?">SSCoolJ</aim>
     </resource>
     <resource xmlns="http://mugshot.org/p/o/user"; m:resourceId="user/USER1">
 	<name m:type="+s">John Doe</name>
 	<externalAccounts m:type="+r?" m:defaultChildren="+" m:resourceId="externalAccount/USER1.MYSPACE"/>
 	<contacts m:type="r*" m:resourceId="user/USER2"/>
 	<contacters m:type="r*" m:update="clear"/>
+	<aim m:type="s?">JohnDoe1</aim>
     </resource>
 </m:model>

Modified: dumbhippo/trunk/client/common/ddm/test-local-data.xml
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-local-data.xml	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/test-local-data.xml	2007-11-09 23:06:59 UTC (rev 6890)
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?><!-- -*- sgml-indent-step: 4; fill-column: 120 -*- -->
 <m:model xmlns:m="http://mugshot.org/p/system"; m:resourceBase="online-desktop:/o/">
-    <resource xmlns="online-desktop://p/o/buddy" m:resourceId="pidgin-buddy/AIM.JohnDoe1">
-	<user m:type="r?" m:resourceId="http://mugshot.org/o/user/USER1"/>
+    <resource xmlns="online-desktop:/p/o/buddy" m:resourceId="pidgin-buddy/AIM.JohnDoe1">
+	<name m:type="s">JohnDoe1</name>
+	<protocol m:type="s">aim</protocol>
+        <!-- This is for testing multipart fetches without rules -->
+	<fixedUser m:type="r?" m:resourceId="http://mugshot.org/o/user/USER1"/>
     </resource>
 </m:model>

Modified: dumbhippo/trunk/client/common/ddm/test-multipart-fetch.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-multipart-fetch.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/test-multipart-fetch.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -1,85 +1,21 @@
 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 
-#include <string.h>
+#include "test-utils.h"
 
-#include "static-file-backend.h"
-#include "ddm-data-query.h"
-
-static void
-on_query_result (DDMDataResource *result,
-                 gpointer         user_data)
-{
-    DDMDataResource **result_location = user_data;
-    *result_location = result;
-}
-
-static void
-on_query_error (DDMDataError     error,
-                const char      *message,
-                gpointer         user_data)
-{
-    const char **message_location = user_data;
-    
-    g_assert(message != NULL);
-    *message_location = g_strdup(message);
-}
-
-static DDMDataResource *
-query_resource(DDMDataModel    *model,
-               const char      *resource_id,
-               const char      *fetch)
-{
-    DDMDataQuery *query;
-    DDMDataResource *result = NULL;
-    const char *error = NULL;
-    
-    query = ddm_data_model_query_resource_by_id(model, resource_id, fetch);
-    ddm_data_query_set_single_handler(query, on_query_result, &result);
-    ddm_data_query_set_error_handler(query, on_query_error, &error);
-
-    while (ddm_data_model_needs_flush(model))
-        ddm_data_model_flush(model);
-
-    if (error != NULL)
-        g_error("Error from getResource, resource_id=%s, fetch=%s: %s", resource_id, fetch, error);
-
-    return result;
-}
-
 int
 main(int argc, char **argv)
 {
-    GError *error = NULL;
     DDMDataModel *model;
-    const char *srcdir;
-    char *filename;
 
     DDMDataResource *result;
     DDMDataResource *user1;
     DDMDataResource *buddy1;
     DDMDataResource *user;
 
-    g_type_init();
+    model = test_init(TRUE);
+    
+    result = test_query_resource("online-desktop:/o/pidgin-buddy/AIM.JohnDoe1", "fixedUser name");
 
-    srcdir = g_getenv("DDM_SRCDIR");
-    if (srcdir == NULL)
-        g_error("DDM_SRCDIR is not set");
-
-    filename = g_build_filename(srcdir, "test-data.xml", NULL);
-    model = ddm_static_file_model_new(filename, &error);
-    if (model == NULL)
-        g_error("Failed to create test model: %s", error->message);
-
-    g_free(filename);
-
-    filename = g_build_filename(srcdir, "test-local-data.xml", NULL);
-    if (!ddm_static_load_local_file(filename, model, &error))
-        g_error("Failed to add_local data to test model: %s", error->message);
-
-    g_free(filename);
-
-    result = query_resource(model, "online-desktop:/o/pidgin-buddy/AIM.JohnDoe1", "user name");
-
     buddy1 = ddm_data_model_lookup_resource(model, "online-desktop:/o/pidgin-buddy/AIM.JohnDoe1");
     g_assert(buddy1 != NULL);
 
@@ -88,7 +24,7 @@
 
     user = NULL;
     ddm_data_resource_get(buddy1,
-                          "user", DDM_DATA_RESOURCE, &user,
+                          "fixedUxuser", DDM_DATA_RESOURCE, &user,
                           NULL);
 
     g_assert(user == user1);

Modified: dumbhippo/trunk/client/common/ddm/test-notification.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-notification.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/test-notification.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -1,60 +1,9 @@
 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 
 #include <string.h>
+#include "test-utils.h"
 
-#include "static-file-backend.h"
-#include "ddm-data-query.h"
-
-static DDMDataModel *model = NULL;
-
 static void
-on_query_result (DDMDataResource *result,
-                 gpointer         user_data)
-{
-    DDMDataResource **result_location = user_data;
-    *result_location = result;
-}
-
-static void
-on_query_error (DDMDataError     error,
-                const char      *message,
-                gpointer         user_data)
-{
-    const char **message_location = user_data;
-    
-    g_assert(message != NULL);
-    *message_location = g_strdup(message);
-}
-
-static void
-flush_model(DDMDataModel *model) {
-    while (ddm_data_model_needs_flush(model))
-        ddm_data_model_flush(model);
-
-}
-
-static DDMDataResource *
-query_resource(DDMDataModel    *model,
-               const char      *resource_id,
-               const char      *fetch)
-{
-    DDMDataQuery *query;
-    DDMDataResource *result = NULL;
-    const char *error = NULL;
-    
-    query = ddm_data_model_query_resource_by_id(model, resource_id, fetch);
-    ddm_data_query_set_single_handler(query, on_query_result, &result);
-    ddm_data_query_set_error_handler(query, on_query_error, &error);
-
-    flush_model(model);
-
-    if (error != NULL)
-        g_error("Error from getResource, resource_id=%s, fetch=%s: %s", resource_id, fetch, error);
-
-    return result;
-}
-
-static void
 on_buddy1_changed(DDMDataResource *resource,
                   GSList          *changed_properties,
                   gpointer         data)
@@ -68,7 +17,7 @@
 
     g_assert(strcmp(ddm_data_resource_get_resource_id(resource), "online-desktop:/o/pidgin-buddy/AIM.JohnDoe1") == 0);
 
-    user2 = ddm_data_model_lookup_resource(model, "http://mugshot.org/o/user/USER2";);
+    user2 = ddm_data_model_lookup_resource(test_get_model(), "http://mugshot.org/o/user/USER2";);
     g_assert(user2 != NULL);
 
     user = NULL;
@@ -89,10 +38,8 @@
 int
 main(int argc, char **argv)
 {
-    GError *error = NULL;
-    const char *srcdir;
-    char *filename;
-
+    DDMDataModel *model;
+    
     DDMDataResource *result;
     DDMDataResource *user1;
     DDMDataResource *user2;
@@ -103,27 +50,10 @@
 
     gboolean was_changed = FALSE;
 
-    g_type_init();
+    model = test_init(TRUE);
 
-    srcdir = g_getenv("DDM_SRCDIR");
-    if (srcdir == NULL)
-        g_error("DDM_SRCDIR is not set");
+    result = test_query_resource("online-desktop:/o/pidgin-buddy/AIM.JohnDoe1", "fixedUser name");
 
-    filename = g_build_filename(srcdir, "test-data.xml", NULL);
-    model = ddm_static_file_model_new(filename, &error);
-    if (model == NULL)
-        g_error("Failed to create test model: %s", error->message);
-
-    g_free(filename);
-
-    filename = g_build_filename(srcdir, "test-local-data.xml", NULL);
-    if (!ddm_static_load_local_file(filename, model, &error))
-        g_error("Failed to add_local data to test model: %s", error->message);
-
-    g_free(filename);
-
-    result = query_resource(model, "online-desktop:/o/pidgin-buddy/AIM.JohnDoe1", "user name");
-
     buddy1 = ddm_data_model_lookup_resource(model, "online-desktop:/o/pidgin-buddy/AIM.JohnDoe1");
     g_assert(buddy1 != NULL);
 
@@ -132,7 +62,7 @@
 
     user = NULL;
     ddm_data_resource_get(buddy1,
-                          "user", DDM_DATA_RESOURCE, &user,
+                          "fixedUser", DDM_DATA_RESOURCE, &user,
                           NULL);
 
     g_assert(user == user1);
@@ -149,12 +79,12 @@
     value.u.resource = user2;
 
     ddm_data_resource_update_property(buddy1,
-                                      ddm_qname_get("online-desktop://p/o/buddy", "user"),
+                                      ddm_qname_get("online-desktop://p/o/buddy", "fixedUser"),
                                       DDM_DATA_UPDATE_REPLACE, DDM_DATA_CARDINALITY_01,
                                       FALSE, NULL,
                                       &value);
 
-    flush_model(model);
+    test_flush();
 
     g_assert(was_changed);
 

Added: dumbhippo/trunk/client/common/ddm/test-rules.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-rules.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/test-rules.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -0,0 +1,96 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#include "test-utils.h"
+
+int
+main(int argc, char **argv)
+{
+    DDMDataModel *model;
+
+    DDMDataResource *user1;
+    DDMDataResource *user2;
+    DDMDataResource *buddy1;
+    DDMDataResource *user;
+    GSList *aimBuddies;
+
+    DDMDataValue value;
+
+    model = test_init(TRUE);
+
+    ddm_data_model_add_rule(model,
+                            "online-desktop:/p/o/buddy",
+                            "online-desktop:/p/o/buddy#user",
+                            "http://mugshot.org/p/o/user";,
+                            DDM_DATA_CARDINALITY_01, FALSE, NULL,
+                            "source.aim = target.name and target.protocol = 'aim'");
+    
+    ddm_data_model_add_rule(model,
+                            "online-desktop:/p/o/global",
+                            "online-desktop:/p/o/global#aimBuddies",
+                            "online-desktop:/p/o/buddy",
+                            DDM_DATA_CARDINALITY_N, FALSE, NULL,
+                            "source.protocol = 'aim'");
+
+    user1 = test_query_resource("http://mugshot.org/o/user/USER1";, "aim");
+    g_assert(user1 != NULL);
+    
+    user2 = test_query_resource("http://mugshot.org/o/user/USER2";, "aim");
+    g_assert(user2 != NULL);
+    
+    buddy1 = ddm_data_model_lookup_resource(model, "online-desktop:/o/pidgin-buddy/AIM.JohnDoe1");
+    g_assert(buddy1 != NULL);
+
+    /* Test that the rule was computed on initial data load */
+
+    ddm_data_resource_get(buddy1,
+                          "user", DDM_DATA_RESOURCE, &user,
+                          NULL);
+    g_assert(user == user1);
+
+    /* Try changing the source property, check that the rule-property gets unset */
+
+    value.type = DDM_DATA_STRING;
+    value.u.string = "JohnDoe2";
+
+    ddm_data_resource_update_property(buddy1,
+                                      ddm_qname_get("online-desktop://p/o/buddy", "name"),
+                                      DDM_DATA_UPDATE_REPLACE, DDM_DATA_CARDINALITY_1,
+                                      FALSE, NULL,
+                                      &value);
+
+    test_flush();
+
+    ddm_data_resource_get(buddy1,
+                          "user", DDM_DATA_RESOURCE, &user,
+                          NULL);
+    g_assert(user == NULL);
+    
+    /* Change it again in a way that will cause it to get reset */
+
+    value.type = DDM_DATA_STRING;
+    value.u.string = "SSCoolJ";
+
+    ddm_data_resource_update_property(buddy1,
+                                      ddm_qname_get("online-desktop://p/o/buddy", "name"),
+                                      DDM_DATA_UPDATE_REPLACE, DDM_DATA_CARDINALITY_1,
+                                      FALSE, NULL,
+                                      &value);
+
+    test_flush();
+
+    ddm_data_resource_get(buddy1,
+                          "user", DDM_DATA_RESOURCE, &user,
+                          NULL);
+    g_assert(user == user2);
+
+    /* Test our list valued rule */
+    
+    ddm_data_resource_get(ddm_data_model_get_global_resource(model),
+                          "aimBuddies", DDM_DATA_RESOURCE | DDM_DATA_LIST, &aimBuddies,
+                          NULL);
+
+    g_assert(g_slist_length(aimBuddies) == 1);
+    g_assert(aimBuddies->data == buddy1);
+    
+    return 0;
+}

Modified: dumbhippo/trunk/client/common/ddm/test-static-file-backend.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-static-file-backend.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/test-static-file-backend.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -2,57 +2,12 @@
 
 #include <string.h>
 
-#include "static-file-backend.h"
-#include "ddm-data-query.h"
+#include "test-utils.h"
 
-static void
-on_query_result (DDMDataResource *result,
-                 gpointer         user_data)
-{
-    DDMDataResource **result_location = user_data;
-    *result_location = result;
-}
-
-static void
-on_query_error (DDMDataError     error,
-                const char      *message,
-                gpointer         user_data)
-{
-    const char **message_location = user_data;
-    
-    g_assert(message != NULL);
-    *message_location = g_strdup(message);
-}
-
-static DDMDataResource *
-query_resource(DDMDataModel    *model,
-               const char      *resource_id,
-               const char      *fetch)
-{
-    DDMDataQuery *query;
-    DDMDataResource *result = NULL;
-    const char *error = NULL;
-    
-    query = ddm_data_model_query_resource_by_id(model, resource_id, fetch);
-    ddm_data_query_set_single_handler(query, on_query_result, &result);
-    ddm_data_query_set_error_handler(query, on_query_error, &error);
-
-    while (ddm_data_model_needs_flush(model))
-        ddm_data_model_flush(model);
-
-    if (error != NULL)
-        g_error("Error from getResource, resource_id=%s, fetch=%s: %s", resource_id, fetch, error);
-
-    return result;
-}
-
 int
 main(int argc, char **argv)
 {
-    GError *error = NULL;
     DDMDataModel *model;
-    const char *srcdir;
-    char *filename;
 
     DDMDataResource *result;
     DDMDataResource *user1;
@@ -61,21 +16,10 @@
     GSList *contacts;
     GSList *contacters;
 
-    g_type_init();
+    model = test_init(FALSE);
 
-    srcdir = g_getenv("DDM_SRCDIR");
-    if (srcdir == NULL)
-        g_error("DDM_SRCDIR is not set");
+    result = test_query_resource("http://mugshot.org/o/user/USER1";, "name;contacts;contacters");
 
-    filename = g_build_filename(srcdir, "test-data.xml", NULL);
-    model = ddm_static_file_model_new(filename, &error);
-    if (model == NULL)
-        g_error("Failed to create test model: %s", error->message);
-
-    g_free(filename);
-
-    result = query_resource(model, "http://mugshot.org/o/user/USER1";, "name;contacts;contacters");
-
     user1 = ddm_data_model_lookup_resource(model, "http://mugshot.org/o/user/USER1";);
     g_assert(user1 != NULL);
 

Added: dumbhippo/trunk/client/common/ddm/test-utils.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-utils.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/test-utils.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -0,0 +1,89 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#include "test-utils.h"
+#include "ddm-data-query.h"
+
+static DDMDataModel *model;
+
+DDMDataModel *
+test_init (gboolean load_local)
+{
+    GError *error = NULL;
+    const char *srcdir;
+    char *filename;
+
+    g_type_init();
+
+    srcdir = g_getenv("DDM_SRCDIR");
+    if (srcdir == NULL)
+        g_error("DDM_SRCDIR is not set");
+
+    filename = g_build_filename(srcdir, "test-data.xml", NULL);
+    model = ddm_static_file_model_new(filename, &error);
+    if (model == NULL)
+        g_error("Failed to create test model: %s", error->message);
+
+    g_free(filename);
+
+    if (load_local) {
+        filename = g_build_filename(srcdir, "test-local-data.xml", NULL);
+        if (!ddm_static_load_local_file(filename, model, &error))
+            g_error("Failed to add_local data to test model: %s", error->message);
+        
+        g_free(filename);
+    }
+
+    return model;
+}
+
+DDMDataModel *
+test_get_model(void)
+{
+    return model;
+}
+
+void
+test_flush (void)
+{
+    while (ddm_data_model_needs_flush(model))
+        ddm_data_model_flush(model);
+}
+
+static void
+on_query_result (DDMDataResource *result,
+                 gpointer         user_data)
+{
+    DDMDataResource **result_location = user_data;
+    *result_location = result;
+}
+
+static void
+on_query_error (DDMDataError     error,
+                const char      *message,
+                gpointer         user_data)
+{
+    const char **message_location = user_data;
+    
+    g_assert(message != NULL);
+    *message_location = g_strdup(message);
+}
+
+DDMDataResource *
+test_query_resource(const char   *resource_id,
+                    const char   *fetch)
+{
+    DDMDataQuery *query;
+    DDMDataResource *result = NULL;
+    const char *error = NULL;
+    
+    query = ddm_data_model_query_resource_by_id(model, resource_id, fetch);
+    ddm_data_query_set_single_handler(query, on_query_result, &result);
+    ddm_data_query_set_error_handler(query, on_query_error, &error);
+
+    test_flush();
+
+    if (error != NULL)
+        g_error("Error from getResource, resource_id=%s, fetch=%s: %s", resource_id, fetch, error);
+
+    return result;
+}

Added: dumbhippo/trunk/client/common/ddm/test-utils.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-utils.h	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/ddm/test-utils.h	2007-11-09 23:06:59 UTC (rev 6890)
@@ -0,0 +1,19 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#ifndef __TEST_UTILS_H__
+#define __TEST_UTILS_H__
+
+#include "static-file-backend.h"
+#include "ddm-data-query.h"
+
+G_BEGIN_DECLS
+
+DDMDataModel *   test_init           (gboolean    load_local);
+DDMDataModel *   test_get_model      (void);
+void             test_flush          (void);
+DDMDataResource *test_query_resource (const char *resource_id,
+				      const char *fetch);
+
+G_END_DECLS
+
+#endif /* __TEST_UTILS_H__ */

Modified: dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c
===================================================================
--- dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c	2007-11-09 23:06:59 UTC (rev 6890)
@@ -162,7 +162,7 @@
                                                            DDM_GLOBAL_RESOURCE, DDM_GLOBAL_RESOURCE_CLASS);
 
     value.type = DDM_DATA_BOOLEAN;
-    value.u.string = FALSE;
+    value.u.boolean = FALSE;
 
     ddm_data_resource_update_property(global_resource,
                                       ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "online"),

Modified: dumbhippo/trunk/client/linux/Makefile-libddm.am
===================================================================
--- dumbhippo/trunk/client/linux/Makefile-libddm.am	2007-11-09 21:25:03 UTC (rev 6889)
+++ dumbhippo/trunk/client/linux/Makefile-libddm.am	2007-11-09 23:06:59 UTC (rev 6890)
@@ -58,6 +58,7 @@
 	$(COMMONSRCDIR)/ddm/ddm-local-client.c			\
 	$(COMMONSRCDIR)/ddm/ddm-local-client.h			\
 	$(COMMONSRCDIR)/ddm/ddm-notification-set.c		\
+	$(COMMONSRCDIR)/ddm/ddm-rule.c				\
 	$(COMMONSRCDIR)/ddm/ddm-rule.h				\
 	$(COMMONSRCDIR)/ddm/ddm-qname.c				\
 	$(COMMONSRCDIR)/ddm/ddm-work-item.c			\
@@ -90,8 +91,11 @@
 nodist_libddm_test_la_SOURCES =				\
 	$(COMMONSRCDIR)/ddm/static-file-backend.c	\
 	$(COMMONSRCDIR)/ddm/static-file-backend.h	\
-	$(COMMONSRCDIR)/ddm/static-file-parser.c
+	$(COMMONSRCDIR)/ddm/static-file-parser.c	\
+	$(COMMONSRCDIR)/ddm/test-utils.c		\
+	$(COMMONSRCDIR)/ddm/test-utils.h
 
+
 ######################################################################
 # Test programs
 
@@ -101,6 +105,7 @@
 	test-condition-reduce			\
 	test-multipart-fetch			\
 	test-notification			\
+	test-rules				\
 	test-static-file-parser			\
 	test-static-file-backend
 
@@ -139,6 +144,11 @@
 
 nodist_test_notification_SOURCES=$(COMMONSRCDIR)/ddm/test-notification.c
 
+test_rules_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDDM_CFLAGS) -DDDM_COMPILATION=1
+test_rules_LDADD=libddm-1.la libddm-test.la
+
+nodist_test_rules_SOURCES=$(COMMONSRCDIR)/ddm/test-rules.c
+
 test_static_file_parser_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDDM_CFLAGS) -DDDM_COMPILATION=1
 test_static_file_parser_LDADD=libddm-1.la libddm-test.la
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]