summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2011-05-30 09:39:16 +0200
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2011-05-30 09:39:16 +0200
commit6a34a59d39e7c18c343b19f212b576d7ef2dd8bb (patch)
tree55a28d90ba7689b1da0630c06bccce00550f8c1e
parent396d10c4d48f9b36822ec4a34f104473d7e013c7 (diff)
parent62aac19d9b3b3cf5c56d39cb14c3b98361cb31ea (diff)
Merge branch 'balance-feature-36334'
-rw-r--r--docs/reference/telepathy-glib-sections.txt4
-rw-r--r--telepathy-glib/connection-internal.h5
-rw-r--r--telepathy-glib/connection.c340
-rw-r--r--telepathy-glib/connection.h8
-rw-r--r--telepathy-glib/signals-marshal.list1
-rw-r--r--tests/dbus/Makefile.am3
-rw-r--r--tests/dbus/connection-balance.c428
7 files changed, 789 insertions, 0 deletions
diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt
index fc49aac6..927e554a 100644
--- a/docs/reference/telepathy-glib-sections.txt
+++ b/docs/reference/telepathy-glib-sections.txt
@@ -3453,6 +3453,7 @@ TP_CONNECTION_FEATURE_CONNECTED
TP_CONNECTION_FEATURE_CAPABILITIES
TP_CONNECTION_FEATURE_AVATAR_REQUIREMENTS
TP_CONNECTION_FEATURE_CONTACT_INFO
+TP_CONNECTION_FEATURE_BALANCE
tp_connection_run_until_ready
TpConnectionWhenReadyCb
tp_connection_call_when_ready
@@ -3485,6 +3486,8 @@ tp_connection_get_detailed_error
tp_connection_add_client_interest
tp_connection_add_client_interest_by_id
tp_connection_bind_connection_status_to_property
+tp_connection_get_balance
+tp_connection_get_balance_uri
<SUBSECTION Standard>
tp_errors_disconnected_quark
tp_connection_get_type
@@ -3505,6 +3508,7 @@ tp_connection_get_feature_quark_connected
tp_connection_get_feature_quark_capabilities
tp_connection_get_feature_quark_avatar_requirements
tp_connection_get_feature_quark_contact_info
+tp_connection_get_feature_quark_balance
<SUBSECTION avatar-requirements>
TP_TYPE_AVATAR_REQUIREMENTS
TpAvatarRequirements
diff --git a/telepathy-glib/connection-internal.h b/telepathy-glib/connection-internal.h
index 92dddfa4..51773416 100644
--- a/telepathy-glib/connection-internal.h
+++ b/telepathy-glib/connection-internal.h
@@ -65,6 +65,11 @@ struct _TpConnectionPrivate {
TpContactInfoFlags contact_info_flags;
GList *contact_info_supported_fields;
+ gint balance;
+ guint balance_scale;
+ gchar *balance_currency;
+ gchar *balance_uri;
+
TpProxyPendingCall *introspection_call;
unsigned ready:1;
diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c
index d4debb2f..2d3e6369 100644
--- a/telepathy-glib/connection.c
+++ b/telepathy-glib/connection.c
@@ -43,6 +43,7 @@
#include "telepathy-glib/debug-internal.h"
#include "telepathy-glib/proxy-internal.h"
#include "telepathy-glib/util-internal.h"
+#include "telepathy-glib/_gen/signals-marshal.h"
#include "_gen/tp-cli-connection-body.h"
@@ -173,6 +174,31 @@ tp_connection_get_feature_quark_capabilities (void)
}
/**
+ * TP_CONNECTION_FEATURE_BALANCE:
+ *
+ * Expands to a call to a function that returns a #GQuark representing the
+ * "balance" feature.
+ *
+ * When this feature is prepared, the Balance.AccountBalance and
+ * Balance.ManageCreditURI properties of the Connection have been retrieved.
+ * In particular, the %TpConnection:balance, %TpConnection:balance-scale,
+ * %TpConnection:balance-currency and %TpConnection:balance-uri properties
+ * have been set and the TpConnection::balance-changed: will be emitted
+ * when they are changed.
+ *
+ * One can ask for a feature to be prepared using the
+ * tp_proxy_prepare_async() function, and waiting for it to callback.
+ *
+ * Since: UNRELEASED
+ */
+
+GQuark
+tp_connection_get_feature_quark_balance (void)
+{
+ return g_quark_from_static_string ("tp-connection-feature-balance");
+}
+
+/**
* TP_ERRORS_DISCONNECTED:
*
* #GError domain representing a Telepathy connection becoming disconnected.
@@ -242,9 +268,21 @@ enum
PROP_SELF_CONTACT,
PROP_SELF_HANDLE,
PROP_CAPABILITIES,
+ PROP_BALANCE,
+ PROP_BALANCE_SCALE,
+ PROP_BALANCE_CURRENCY,
+ PROP_BALANCE_URI,
N_PROPS
};
+enum {
+ SIGNAL_BALANCE_CHANGED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+
G_DEFINE_TYPE (TpConnection,
tp_connection,
TP_TYPE_PROXY)
@@ -283,6 +321,18 @@ tp_connection_get_property (GObject *object,
case PROP_CAPABILITIES:
g_value_set_object (value, self->priv->capabilities);
break;
+ case PROP_BALANCE:
+ g_value_set_int (value, self->priv->balance);
+ break;
+ case PROP_BALANCE_SCALE:
+ g_value_set_uint (value, self->priv->balance_scale);
+ break;
+ case PROP_BALANCE_CURRENCY:
+ g_value_set_string (value, self->priv->balance_currency);
+ break;
+ case PROP_BALANCE_URI:
+ g_value_set_string (value, self->priv->balance_uri);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -290,6 +340,126 @@ tp_connection_get_property (GObject *object,
}
static void
+tp_connection_unpack_balance (TpConnection *self,
+ GValueArray *balance_s)
+{
+ gint balance = 0;
+ guint scale = G_MAXUINT32;
+ const char *currency = "";
+ gboolean changed = FALSE;
+
+ if (balance_s == NULL)
+ goto finally;
+
+ tp_value_array_unpack (balance_s, 3,
+ &balance, &scale, &currency);
+
+finally:
+
+ g_object_freeze_notify ((GObject *) self);
+
+ if (self->priv->balance != balance)
+ {
+ self->priv->balance = balance;
+ g_object_notify ((GObject *) self, "balance");
+ changed = TRUE;
+ }
+
+ if (self->priv->balance_scale != scale)
+ {
+ self->priv->balance_scale = scale;
+ g_object_notify ((GObject *) self, "balance-scale");
+ changed = TRUE;
+ }
+
+ if (tp_strdiff (self->priv->balance_currency, currency))
+ {
+ g_free (self->priv->balance_currency);
+ self->priv->balance_currency = g_strdup (currency);
+ g_object_notify ((GObject *) self, "balance-currency");
+ changed = TRUE;
+ }
+
+ g_object_thaw_notify ((GObject *) self);
+
+ if (changed)
+ {
+ g_signal_emit (self, signals[SIGNAL_BALANCE_CHANGED], 0,
+ balance, scale, currency);
+ }
+}
+
+static void
+tp_connection_get_balance_cb (TpProxy *proxy,
+ GHashTable *props,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *weak_obj)
+{
+ TpConnection *self = (TpConnection *) proxy;
+ GSimpleAsyncResult *result = user_data;
+ GValueArray *balance = NULL;
+
+ if (in_error != NULL)
+ {
+ DEBUG ("Failed to get Balance properties: %s", in_error->message);
+ g_simple_async_result_set_from_error (result, in_error);
+ goto finally;
+ }
+
+ balance =
+ tp_asv_get_boxed (props, "AccountBalance", TP_STRUCT_TYPE_CURRENCY_AMOUNT);
+ self->priv->balance_uri =
+ g_strdup (tp_asv_get_string (props, "ManageCreditURI"));
+
+ g_object_freeze_notify ((GObject *) self);
+
+ tp_connection_unpack_balance (self, balance);
+
+ _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_BALANCE,
+ TRUE);
+
+ g_object_notify ((GObject *) self, "balance-uri");
+
+ g_object_thaw_notify ((GObject *) self);
+
+finally:
+ g_simple_async_result_complete (result);
+}
+
+static void
+tp_connection_balance_changed_cb (TpConnection *self,
+ const GValueArray *balance,
+ gpointer user_data,
+ GObject *weak_obj)
+{
+ tp_connection_unpack_balance (self, (GValueArray *) balance);
+}
+
+static void
+tp_connection_prepare_balance_async (TpProxy *proxy,
+ const TpProxyFeature *feature,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ TpConnection *self = (TpConnection *) proxy;
+ GSimpleAsyncResult *result;
+
+ result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+ tp_connection_prepare_balance_async);
+
+ g_assert (self->priv->balance_currency == NULL);
+
+ tp_cli_dbus_properties_call_get_all (self, -1,
+ TP_IFACE_CONNECTION_INTERFACE_BALANCE,
+ tp_connection_get_balance_cb, result, g_object_unref, NULL);
+
+ tp_cli_connection_interface_balance_connect_to_balance_changed (self,
+ tp_connection_balance_changed_cb,
+ NULL, NULL, NULL, NULL);
+}
+
+static void
tp_connection_get_rcc_cb (TpProxy *proxy,
const GValue *value,
const GError *error,
@@ -1202,6 +1372,9 @@ tp_connection_finalize (GObject *object)
tp_contact_info_spec_list_free (self->priv->contact_info_supported_fields);
self->priv->contact_info_supported_fields = NULL;
+ tp_clear_pointer (&self->priv->balance_currency, g_free);
+ tp_clear_pointer (&self->priv->balance_uri, g_free);
+
((GObjectClass *) tp_connection_parent_class)->finalize (object);
}
@@ -1271,6 +1444,7 @@ enum {
FEAT_CAPABILITIES,
FEAT_AVATAR_REQUIREMENTS,
FEAT_CONTACT_INFO,
+ FEAT_BALANCE,
N_FEAT
};
@@ -1281,6 +1455,7 @@ tp_connection_list_features (TpProxyClass *cls G_GNUC_UNUSED)
static GQuark need_requests[2] = {0, 0};
static GQuark need_avatars[2] = {0, 0};
static GQuark need_contact_info[2] = {0, 0};
+ static GQuark need_balance[2] = {0, 0};
if (G_LIKELY (features[0].name != 0))
return features;
@@ -1308,6 +1483,11 @@ tp_connection_list_features (TpProxyClass *cls G_GNUC_UNUSED)
need_contact_info[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO;
features[FEAT_CONTACT_INFO].interfaces_needed = need_contact_info;
+ features[FEAT_BALANCE].name = TP_CONNECTION_FEATURE_BALANCE;
+ features[FEAT_BALANCE].prepare_async = tp_connection_prepare_balance_async;
+ need_balance[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_BALANCE;
+ features[FEAT_BALANCE].interfaces_needed = need_balance;
+
/* assert that the terminator at the end is there */
g_assert (features[N_FEAT].name == 0);
@@ -1486,6 +1666,96 @@ tp_connection_class_init (TpConnectionClass *klass)
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_CAPABILITIES,
param_spec);
+
+ /**
+ * TpConnection:balance:
+ *
+ * The Amount field of the Balance.AccountBalance property.
+ *
+ * For this property to be valid, you must first call
+ * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
+ *
+ * See Also: tp_connection_get_balance()
+ */
+ param_spec = g_param_spec_int ("balance", "Balance Amount",
+ "The Amount field of the Account Balance",
+ G_MININT32, G_MAXINT32, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_BALANCE,
+ param_spec);
+
+ /**
+ * TpConnection:balance-scale:
+ *
+ * The Scale field of the Balance.AccountBalance property.
+ *
+ * For this property to be valid, you must first call
+ * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
+ *
+ * See Also: tp_connection_get_balance()
+ */
+ param_spec = g_param_spec_uint ("balance-scale", "Balance Scale",
+ "The Scale field of the Account Balance",
+ 0, G_MAXUINT32, G_MAXUINT32,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_BALANCE_SCALE,
+ param_spec);
+
+ /**
+ * TpConnection:balance-currency:
+ *
+ * The Currency field of the Balance.AccountBalance property.
+ *
+ * For this property to be valid, you must first call
+ * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
+ *
+ * See Also: tp_connection_get_balance()
+ */
+ param_spec = g_param_spec_string ("balance-currency", "Balance Currency",
+ "The Currency field of the Account Balance",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_BALANCE_CURRENCY,
+ param_spec);
+
+ /**
+ * TpConnection:balance-uri:
+ *
+ * The Balance.ManageCreditURI property.
+ *
+ * For this property to be valid, you must first call
+ * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
+ */
+ param_spec = g_param_spec_string ("balance-uri", "Balance URI",
+ "The URI for managing the account balance",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_BALANCE_URI,
+ param_spec);
+
+ /**
+ * TpConnection::balance-changed:
+ * @self: a channel
+ * @balance: the value of the #TpConnection:balance property
+ * @balance_scale: the value of the #TpConnection:balance-scale property
+ * @balance_currency: the value of the #TpConnection:balance-currency property
+ *
+ * Emitted when at least one of the #TpConnection:balance,
+ * #TpConnection:balance-scale or #TpConnection:balance-currency
+ * property is changed.
+ *
+ * For this signal to be emitted, you must first call
+ * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
+ *
+ * Since: 0.15.UNRELEASED
+ */
+ signals[SIGNAL_BALANCE_CHANGED] = g_signal_new ("balance-changed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ _tp_marshal_VOID__INT_UINT_STRING,
+ G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_STRING);
}
/**
@@ -2560,3 +2830,73 @@ tp_connection_bind_connection_status_to_property (TpConnection *self,
_tp_bind_connection_status_to_boolean,
NULL, GUINT_TO_POINTER (invert), NULL);
}
+
+/**
+ * tp_connection_get_balance:
+ * @self: a #TpConnection
+ * @balance: (out): a pointer to store the account balance (or %NULL)
+ * @scale: (out): a pointer to store the balance scale (or %NULL)
+ * @currency: (out) (transfer none): a pointer to store the balance
+ * currency (or %NULL)
+ *
+ * If @self has a valid account balance, returns %TRUE and sets the variables
+ * pointed to by @balance, @scale and @currency to the appropriate fields
+ * of the Balance.AccountBalance property.
+ *
+ * The monetary value of the balance is expressed as a fixed-point number,
+ * @balance, with a decimal scale defined by @scale; for instance a @balance
+ * of 1234 with @scale of 2 represents a value of "12.34" in the currency
+ * represented by @currency.
+ *
+ * Requires %TP_CONNECTION_FEATURE_BALANCE to be prepared.
+ *
+ * Returns: %TRUE if the balance is valid (and the values set), %FALSE if the
+ * balance is invalid.
+ * Since: UNRELEASED
+ */
+gboolean
+tp_connection_get_balance (TpConnection *self,
+ gint *balance,
+ guint *scale,
+ const gchar **currency)
+{
+ g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
+
+ if (self->priv->balance_currency == NULL)
+ return FALSE;
+
+ if (self->priv->balance == 0 &&
+ self->priv->balance_scale == G_MAXUINT32 &&
+ tp_str_empty (self->priv->balance_currency))
+ return FALSE;
+
+ if (balance != NULL)
+ *balance = self->priv->balance;
+
+ if (scale != NULL)
+ *scale = self->priv->balance_scale;
+
+ if (currency != NULL)
+ *currency = self->priv->balance_currency;
+
+ return TRUE;
+}
+
+/**
+ * tp_connection_get_balance_uri:
+ * @self: a #TpConnection
+ *
+ * The value of Balance.ManageCreditURI.
+ *
+ * Requires %TP_CONNECTION_FEATURE_BALANCE to be prepared.
+ *
+ * Returns: (transfer none): the #TpConnection:balance-uri property.
+ * Since: UNRELEASED
+ */
+const gchar *
+tp_connection_get_balance_uri (TpConnection *self)
+{
+ g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
+
+ return self->priv->balance_uri;
+}
diff --git a/telepathy-glib/connection.h b/telepathy-glib/connection.h
index 8e625bfd..38a9b3bc 100644
--- a/telepathy-glib/connection.h
+++ b/telepathy-glib/connection.h
@@ -274,6 +274,14 @@ GQuark tp_connection_get_feature_quark_avatar_requirements (void) G_GNUC_CONST;
TpAvatarRequirements * tp_connection_get_avatar_requirements (
TpConnection *self);
+#define TP_CONNECTION_FEATURE_BALANCE \
+ (tp_connection_get_feature_quark_balance ())
+GQuark tp_connection_get_feature_quark_balance (void) G_GNUC_CONST;
+
+gboolean tp_connection_get_balance (TpConnection *self,
+ gint *balance, guint *scale, const gchar **currency);
+const gchar * tp_connection_get_balance_uri (TpConnection *self);
+
G_END_DECLS
#include <telepathy-glib/_gen/tp-cli-connection.h>
diff --git a/telepathy-glib/signals-marshal.list b/telepathy-glib/signals-marshal.list
index e675efd7..cb31434e 100644
--- a/telepathy-glib/signals-marshal.list
+++ b/telepathy-glib/signals-marshal.list
@@ -16,3 +16,4 @@ VOID:UINT,UINT,UINT,STRING,BOXED
VOID:VOID
VOID:BOXED,UINT,INT,STRING
VOID:OBJECT,UINT,STRING
+VOID:INT,UINT,STRING
diff --git a/tests/dbus/Makefile.am b/tests/dbus/Makefile.am
index cf0d3e33..670a0b0f 100644
--- a/tests/dbus/Makefile.am
+++ b/tests/dbus/Makefile.am
@@ -17,6 +17,7 @@ noinst_PROGRAMS = \
test-cm \
test-cm-message \
test-connection \
+ test-connection-balance \
test-connection-bug-18845 \
test-connection-error \
test-connection-handles \
@@ -107,6 +108,8 @@ test_contact_lists_LDADD = \
$(LDADD) \
$(top_builddir)/examples/cm/contactlist/libexample-cm-contactlist.la
+test_connection_balance_SOURCES = connection-balance.c
+
test_connection_bug_18845_SOURCES = connection-bug-18845.c
test_connection_handles_SOURCES = connection-handles.c
diff --git a/tests/dbus/connection-balance.c b/tests/dbus/connection-balance.c
new file mode 100644
index 00000000..34d679b1
--- /dev/null
+++ b/tests/dbus/connection-balance.c
@@ -0,0 +1,428 @@
+/* Feature test for Conn.I.Balance
+ *
+ * Copyright © 2007-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright © 2007-2008 Nokia Corporation
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.
+ */
+
+#include <telepathy-glib/connection.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/debug.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/proxy-subclass.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "tests/lib/simple-conn.h"
+#include "tests/lib/util.h"
+
+#define BALANCE 1234
+#define BALANCE_SCALE 2
+#define BALANCE_CURRENCY "BDD" /* badger dollars */
+#define MANAGE_CREDIT_URI "http://chat.badger.net/topup"
+
+/* -- BalancedConnection -- */
+typedef TpTestsSimpleConnection BalancedConnection;
+typedef TpTestsSimpleConnectionClass BalancedConnectionClass;
+
+#define TYPE_BALANCED_CONNECTION (balanced_connection_get_type ())
+static GType balanced_connection_get_type (void);
+
+G_DEFINE_TYPE_WITH_CODE (BalancedConnection,
+ balanced_connection,
+ TP_TESTS_TYPE_SIMPLE_CONNECTION,
+
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_BALANCE, NULL))
+
+enum
+{
+ PROP_0,
+ PROP_ACCOUNT_BALANCE,
+ PROP_MANAGE_CREDIT_URI
+};
+
+static void
+balanced_connection_get_property (GObject *self G_GNUC_UNUSED,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_ACCOUNT_BALANCE:
+ /* known balance */
+ g_value_take_boxed (value, tp_value_array_build (3,
+ G_TYPE_INT, BALANCE,
+ G_TYPE_UINT, BALANCE_SCALE,
+ G_TYPE_STRING, BALANCE_CURRENCY,
+ G_TYPE_INVALID));
+ break;
+
+ case PROP_MANAGE_CREDIT_URI:
+ g_value_set_static_string (value, MANAGE_CREDIT_URI);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+balanced_connection_init (BalancedConnection *self G_GNUC_UNUSED)
+{
+}
+
+static void
+balanced_connection_class_init (BalancedConnectionClass *cls)
+{
+ GObjectClass *object_class = (GObjectClass *) cls;
+ TpBaseConnectionClass *base_class = TP_BASE_CONNECTION_CLASS (cls);
+
+ static TpDBusPropertiesMixinPropImpl balance_props[] = {
+ { "AccountBalance", "account-balance", NULL },
+ { "ManageCreditURI", "manage-credit-uri", NULL },
+ { NULL }
+ };
+
+ static const gchar *interfaces[] = {
+ TP_IFACE_CONNECTION_INTERFACE_BALANCE,
+ NULL
+ };
+
+ object_class->get_property = balanced_connection_get_property;
+
+ base_class->interfaces_always_present = interfaces;
+
+ g_object_class_install_property (object_class, PROP_ACCOUNT_BALANCE,
+ g_param_spec_boxed ("account-balance", "", "",
+ TP_STRUCT_TYPE_CURRENCY_AMOUNT,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_MANAGE_CREDIT_URI,
+ g_param_spec_string ("manage-credit-uri", "", "",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ tp_dbus_properties_mixin_implement_interface (object_class,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_BALANCE,
+ tp_dbus_properties_mixin_getter_gobject_properties, NULL,
+ balance_props);
+}
+
+/* -- UnbalancedConnection -- */
+typedef TpTestsSimpleConnection UnbalancedConnection;
+typedef TpTestsSimpleConnectionClass UnbalancedConnectionClass;
+
+#define TYPE_UNBALANCED_CONNECTION (unbalanced_connection_get_type ())
+static GType unbalanced_connection_get_type (void);
+
+G_DEFINE_TYPE (UnbalancedConnection,
+ unbalanced_connection,
+ TYPE_BALANCED_CONNECTION)
+
+static void
+unbalanced_connection_get_property (GObject *self G_GNUC_UNUSED,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ case PROP_ACCOUNT_BALANCE:
+ /* unknown balance */
+ g_value_take_boxed (value, tp_value_array_build (3,
+ G_TYPE_INT, 0,
+ G_TYPE_UINT, G_MAXUINT32,
+ G_TYPE_STRING, "",
+ G_TYPE_INVALID));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+unbalanced_connection_init (UnbalancedConnection *self G_GNUC_UNUSED)
+{
+}
+
+static void
+unbalanced_connection_class_init (UnbalancedConnectionClass *cls)
+{
+ GObjectClass *object_class = (GObjectClass *) cls;
+
+ object_class->get_property = unbalanced_connection_get_property;
+
+ g_object_class_override_property (object_class, PROP_ACCOUNT_BALANCE,
+ "account-balance");
+}
+
+/* -- Tests -- */
+typedef struct {
+ GMainLoop *mainloop;
+ TpDBusDaemon *dbus;
+ DBusConnection *client_libdbus;
+ DBusGConnection *client_dbusglib;
+ TpDBusDaemon *client_bus;
+ TpTestsSimpleConnection *service_conn;
+ TpBaseConnection *service_conn_as_base;
+ gchar *conn_name;
+ gchar *conn_path;
+ TpConnection *conn;
+
+ gboolean cwr_ready;
+ GError *cwr_error /* initialized in setup */;
+
+ GAsyncResult *prepare_result;
+
+ GError *error /* initialized where needed */;
+ gint wait;
+} Test;
+
+static void
+connection_prepared_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ Test *test = user_data;
+
+ g_message ("%p prepared", object);
+ g_assert (test->prepare_result == NULL);
+ test->prepare_result = g_object_ref (res);
+}
+
+static void
+run_prepare_proxy (Test *test,
+ GQuark *features)
+{
+ GError *error = NULL;
+
+ tp_proxy_prepare_async (test->conn, features, connection_prepared_cb, test);
+ g_assert (test->prepare_result == NULL);
+
+ while (test->prepare_result == NULL)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_assert (tp_proxy_prepare_finish (test->conn, test->prepare_result,
+ &error));
+ g_assert_no_error (error);
+
+ tp_clear_object (&test->prepare_result);
+}
+
+static void
+setup (Test *test,
+ gconstpointer data)
+{
+ GError *error = NULL;
+ GQuark features[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 };
+ GType conn_type = GPOINTER_TO_UINT (data);
+
+ g_type_init ();
+ tp_debug_set_flags ("all");
+ test->dbus = tp_tests_dbus_daemon_dup_or_die ();
+
+ test->mainloop = g_main_loop_new (NULL, FALSE);
+ test->error = NULL;
+
+ test->client_libdbus = dbus_bus_get_private (DBUS_BUS_STARTER, NULL);
+ g_assert (test->client_libdbus != NULL);
+ dbus_connection_setup_with_g_main (test->client_libdbus, NULL);
+ dbus_connection_set_exit_on_disconnect (test->client_libdbus, FALSE);
+ test->client_dbusglib = dbus_connection_get_g_connection (
+ test->client_libdbus);
+ dbus_g_connection_ref (test->client_dbusglib);
+ test->client_bus = tp_dbus_daemon_new (test->client_dbusglib);
+ g_assert (test->client_bus != NULL);
+
+ test->service_conn = tp_tests_object_new_static_class (
+ conn_type,
+ "account", "me@example.com",
+ "protocol", "simple-protocol",
+ NULL);
+ test->service_conn_as_base = TP_BASE_CONNECTION (test->service_conn);
+ g_assert (test->service_conn != NULL);
+ g_assert (test->service_conn_as_base != NULL);
+
+ g_assert (tp_base_connection_register (test->service_conn_as_base, "simple",
+ &test->conn_name, &test->conn_path, &error));
+ g_assert_no_error (error);
+
+ test->cwr_ready = FALSE;
+ test->cwr_error = NULL;
+
+ test->conn = tp_connection_new (test->client_bus, test->conn_name,
+ test->conn_path, &error);
+ g_assert (test->conn != NULL);
+ g_assert_no_error (error);
+
+ tp_cli_connection_call_connect (test->conn, -1, NULL, NULL, NULL, NULL);
+
+ g_assert (!tp_proxy_is_prepared (test->conn, TP_CONNECTION_FEATURE_CORE));
+ g_assert (!tp_proxy_is_prepared (test->conn,
+ TP_CONNECTION_FEATURE_CONNECTED));
+ g_assert (!tp_proxy_is_prepared (test->conn, TP_CONNECTION_FEATURE_BALANCE));
+
+ run_prepare_proxy (test, features);
+}
+
+static void
+teardown (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ TpConnection *conn;
+ gboolean ok;
+ GError *error = NULL;
+
+ g_clear_error (&test->error);
+ tp_clear_pointer (&test->mainloop, g_main_loop_unref);
+ tp_clear_object (&test->conn);
+
+ /* disconnect the connection so we don't leak it */
+ conn = tp_connection_new (test->dbus, test->conn_name, test->conn_path,
+ &error);
+ g_assert (conn != NULL);
+ g_assert_no_error (error);
+
+ ok = tp_cli_connection_run_disconnect (conn, -1, &error, NULL);
+ g_assert (ok);
+ g_assert_no_error (error);
+
+ g_assert (!tp_connection_run_until_ready (conn, FALSE, &error, NULL));
+ g_assert_error (error, TP_ERRORS, TP_ERROR_CANCELLED);
+ g_clear_error (&error);
+
+ test->service_conn_as_base = NULL;
+ g_object_unref (test->service_conn);
+ g_free (test->conn_name);
+ g_free (test->conn_path);
+
+ g_object_unref (test->dbus);
+ test->dbus = NULL;
+ g_object_unref (test->client_bus);
+ test->client_bus = NULL;
+
+ dbus_g_connection_unref (test->client_dbusglib);
+ dbus_connection_close (test->client_libdbus);
+ dbus_connection_unref (test->client_libdbus);
+}
+
+static void
+balance_changed_cb (TpConnection *conn,
+ gint balance,
+ guint scale,
+ const gchar *currency,
+ Test *test)
+{
+ g_assert_cmpint (balance, ==, BALANCE * 2);
+ g_assert_cmpuint (scale, ==, BALANCE_SCALE);
+ g_assert_cmpstr (currency, ==, BALANCE_CURRENCY);
+
+ test->wait--;
+ if (test->wait <= 0)
+ g_main_loop_quit (test->mainloop);
+}
+
+static void
+test_balance (Test *test,
+ gconstpointer nil G_GNUC_UNUSED)
+{
+ GQuark features[] = { TP_CONNECTION_FEATURE_BALANCE, 0 };
+ gint balance;
+ guint scale;
+ const gchar *currency, *uri;
+ gchar *currency_alloc, *uri_alloc;
+ GValueArray *v;
+
+ g_assert (!tp_proxy_is_prepared (test->conn, TP_CONNECTION_FEATURE_BALANCE));
+
+ run_prepare_proxy (test, features);
+
+ g_assert (tp_connection_get_balance (test->conn,
+ &balance, &scale, &currency));
+
+ g_assert_cmpint (balance, ==, BALANCE);
+ g_assert_cmpuint (scale, ==, BALANCE_SCALE);
+ g_assert_cmpstr (currency, ==, BALANCE_CURRENCY);
+
+ uri = tp_connection_get_balance_uri (test->conn);
+
+ g_assert_cmpstr (uri, ==, MANAGE_CREDIT_URI);
+
+ g_object_get (test->conn,
+ "balance", &balance,
+ "balance-scale", &scale,
+ "balance-currency", &currency_alloc,
+ "balance-uri", &uri_alloc,
+ NULL);
+
+ g_assert_cmpint (balance, ==, BALANCE);
+ g_assert_cmpuint (scale, ==, BALANCE_SCALE);
+ g_assert_cmpstr (currency_alloc, ==, BALANCE_CURRENCY);
+ g_assert_cmpstr (uri_alloc, ==, MANAGE_CREDIT_URI);
+
+ v = tp_value_array_build (3,
+ G_TYPE_INT, BALANCE * 2,
+ G_TYPE_UINT, BALANCE_SCALE,
+ G_TYPE_STRING, BALANCE_CURRENCY,
+ G_TYPE_INVALID);
+
+ tp_svc_connection_interface_balance_emit_balance_changed (
+ test->service_conn_as_base, v);
+
+ g_signal_connect (test->conn, "balance-changed",
+ G_CALLBACK (balance_changed_cb), test);
+
+ test->wait = 1;
+ g_main_loop_run (test->mainloop);
+ g_assert_no_error (test->error);
+
+ g_free (currency_alloc);
+ g_free (uri_alloc);
+}
+
+static void
+test_balance_unknown (Test *test,
+ gconstpointer nil G_GNUC_UNUSED)
+{
+ GQuark features[] = { TP_CONNECTION_FEATURE_BALANCE, 0 };
+ gint balance;
+ guint scale;
+ const gchar *currency;
+
+ g_assert (!tp_proxy_is_prepared (test->conn, TP_CONNECTION_FEATURE_BALANCE));
+
+ run_prepare_proxy (test, features);
+
+ g_assert (!tp_connection_get_balance (test->conn,
+ &balance, &scale, &currency));
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_type_init ();
+
+ tp_tests_abort_after (5);
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/conn/balance", Test,
+ GUINT_TO_POINTER (TYPE_BALANCED_CONNECTION),
+ setup, test_balance, teardown);
+ g_test_add ("/conn/balance-unknown", Test,
+ GUINT_TO_POINTER (TYPE_UNBALANCED_CONNECTION),
+ setup, test_balance_unknown, teardown);
+ g_test_add ("/conn/balance-unimplemented", Test,
+ GUINT_TO_POINTER (TP_TESTS_TYPE_SIMPLE_CONNECTION),
+ setup, test_balance_unknown, teardown);
+
+ return g_test_run ();
+}