[glib: 2/6] gerror: Add support for extended errors
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 2/6] gerror: Add support for extended errors
- Date: Wed, 6 Jan 2021 16:02:32 +0000 (UTC)
commit ae72f9de352af9a84679f98f81b4462f7a9cc2fa
Author: Krzesimir Nowak <qdlacz gmail com>
Date: Wed Dec 25 13:07:32 2019 +0100
gerror: Add support for extended errors
This commit adds a G_DEFINE_EXTENDED_ERROR macro and
g_error_domain_register() functions to register extended error
domains.
docs/reference/glib/glib-sections.txt | 7 +
glib/gerror.c | 359 +++++++++++++++++++++++++++++++++-
glib/gerror.h | 137 +++++++++++++
glib/glib-init.c | 1 +
glib/glib-init.h | 1 +
glib/tests/error.c | 100 ++++++++++
6 files changed, 595 insertions(+), 10 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 690c0de9c..eed764893 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -754,6 +754,13 @@ g_propagate_error
g_clear_error
g_prefix_error
g_propagate_prefixed_error
+<SUBSECTION>
+GErrorInitFunc
+GErrorCopyFunc
+GErrorClearFunc
+G_DEFINE_EXTENDED_ERROR
+g_error_domain_register_static
+g_error_domain_register
</SECTION>
<SECTION>
diff --git a/glib/gerror.c b/glib/gerror.c
index 129a82f2b..dd69283eb 100644
--- a/glib/gerror.c
+++ b/glib/gerror.c
@@ -372,27 +372,338 @@
* to add a check at the top of your function that the error return
* location is either %NULL or contains a %NULL error (e.g.
* `g_return_if_fail (error == NULL || *error == NULL);`).
+ *
+ * Since GLib 2.68 it is possible to extend the #GError type. This is
+ * done with the G_DEFINE_EXTENDED_ERROR() macro. To create an
+ * extended #GError type do something like this in the header file:
+ * |[<!-- language="C" -->
+ * typedef enum
+ * {
+ * MY_ERROR_BAD_REQUEST,
+ * } MyError;
+ * #define MY_ERROR (my_error_quark ())
+ * GQuark my_error_quark (void);
+ * int
+ * my_error_get_parse_error_id (GError *error);
+ * const char *
+ * my_error_get_bad_request_details (GError *error);
+ * ]|
+ * and in implementation:
+ * |[<!-- language="C" -->
+ * typedef struct
+ * {
+ * int parse_error_id;
+ * char *bad_request_details;
+ * } MyErrorPrivate;
+ *
+ * static void
+ * my_error_private_init (MyErrorPrivate *priv)
+ * {
+ * priv->parse_error_id = -1;
+ * // No need to set priv->bad_request_details to NULL,
+ * // the struct is initialized with zeros.
+ * }
+ *
+ * static void
+ * my_error_private_copy (const MyErrorPrivate *src_priv, MyErrorPrivate *dest_priv)
+ * {
+ * dest_priv->parse_error_id = src_priv->parse_error_id;
+ * dest_priv->bad_request_details = g_strdup (src_priv->bad_request_details);
+ * }
+ *
+ * static void
+ * my_error_private_clear (MyErrorPrivate *priv)
+ * {
+ * g_free (priv->bad_request_details);
+ * }
+ *
+ * // This defines the my_error_get_private and my_error_quark functions.
+ * G_DEFINE_EXTENDED_ERROR (MyError, my_error)
+ *
+ * int
+ * my_error_get_parse_error_id (GError *error)
+ * {
+ * MyErrorPrivate *priv = my_error_get_private (error);
+ * g_return_val_if_fail (priv != NULL, -1);
+ * return priv->parse_error_id;
+ * }
+ *
+ * const char *
+ * my_error_get_bad_request_details (GError *error)
+ * {
+ * MyErrorPrivate *priv = my_error_get_private (error);
+ * g_return_val_if_fail (priv != NULL, NULL);
+ * g_return_val_if_fail (error->code != MY_ERROR_BAD_REQUEST, NULL);
+ * return priv->bad_request_details;
+ * }
+ *
+ * static void
+ * my_error_set_bad_request (GError **error,
+ * const char *reason,
+ * int error_id,
+ * const char *details)
+ * {
+ * MyErrorPrivate *priv;
+ * g_set_error (error, MY_ERROR, MY_ERROR_BAD_REQUEST, "Invalid request: %s", reason);
+ * if (error != NULL && *error != NULL)
+ * {
+ * priv = my_error_get_private (error);
+ * g_return_val_if_fail (priv != NULL, NULL);
+ * priv->parse_error_id = error_id;
+ * priv->bad_request_details = g_strdup (details);
+ * }
+ * }
+ * ]|
+ * An example of use of the error could be:
+ * |[<!-- language="C" -->
+ * gboolean
+ * send_request (GBytes *request, GError **error)
+ * {
+ * ParseFailedStatus *failure = validate_request (request);
+ * if (failure != NULL)
+ * {
+ * my_error_set_bad_request (error, failure->reason, failure->error_id, failure->details);
+ * parse_failed_status_free (failure);
+ * return FALSE;
+ * }
+ *
+ * return send_one (request, error);
+ * }
+ * ]|
+ *
+ * Please note that if you are a library author and your library
+ * exposes an existing error domain, then you can't make this error
+ * domain an extended one without breaking ABI. This is because
+ * earlier it was possible to create an error with this error domain
+ * on the stack and then copy it with g_error_copy(). If the new
+ * version of your library makes the error domain an extended one,
+ * then g_error_copy() called by code that allocated the error on the
+ * stack will try to copy more data than it used to, which will lead
+ * to undefined behavior. You must not stack-allocate errors with an
+ * extended error domain, and it is bad practice to stack-allocate any
+ * other #GErrors.
+ *
+ * Extended error domains in unloadable plugins/modules are not
+ * supported.
*/
#include "config.h"
+#include <string.h>
+
#include "gerror.h"
+#include "ghash.h"
+#include "glib-init.h"
#include "gslice.h"
#include "gstrfuncs.h"
#include "gtestutils.h"
+#include "gthread.h"
+
+static GRWLock error_domain_global;
+/* error_domain_ht must be accessed with error_domain_global
+ * locked.
+ */
+static GHashTable *error_domain_ht = NULL;
+void
+g_error_init (void)
+{
+ error_domain_ht = g_hash_table_new (NULL, NULL);
+}
+
+typedef struct
+{
+ /* private_size is already aligned. */
+ gsize private_size;
+ GErrorInitFunc init;
+ GErrorCopyFunc copy;
+ GErrorClearFunc clear;
+} ErrorDomainInfo;
+
+/* Must be called with error_domain_global locked.
+ */
+static inline ErrorDomainInfo *
+error_domain_lookup (GQuark domain)
+{
+ return g_hash_table_lookup (error_domain_ht,
+ GUINT_TO_POINTER (domain));
+}
+
+/* Copied from gtype.c. */
+#define STRUCT_ALIGNMENT (2 * sizeof (gsize))
+#define ALIGN_STRUCT(offset) \
+ ((offset + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT)
+
+static void
+error_domain_register (GQuark error_quark,
+ gsize error_type_private_size,
+ GErrorInitFunc error_type_init,
+ GErrorCopyFunc error_type_copy,
+ GErrorClearFunc error_type_clear)
+{
+ g_rw_lock_writer_lock (&error_domain_global);
+ if (error_domain_lookup (error_quark) == NULL)
+ {
+ ErrorDomainInfo *info = g_new (ErrorDomainInfo, 1);
+ info->private_size = ALIGN_STRUCT (error_type_private_size);
+ info->init = error_type_init;
+ info->copy = error_type_copy;
+ info->clear = error_type_clear;
+
+ g_hash_table_insert (error_domain_ht,
+ GUINT_TO_POINTER (error_quark),
+ info);
+ }
+ else
+ {
+ const char *name = g_quark_to_string (error_quark);
+
+ g_critical ("Attempted to register an extended error domain for %s more than once", name);
+ }
+ g_rw_lock_writer_unlock (&error_domain_global);
+}
+
+/**
+ * g_error_domain_register_static:
+ * @error_type_name: static string to create a #GQuark from
+ * @error_type_private_size: size of the private error data in bytes
+ * @error_type_init: function initializing fields of the private error data
+ * @error_type_copy: function copying fields of the private error data
+ * @error_type_clear: function freeing fields of the private error data
+ *
+ * This function registers an extended #GError domain.
+ *
+ * @error_type_name should not be freed. @error_type_private_size must
+ * be greater than 0.
+ *
+ * @error_type_init receives an initialized #GError and should then initialize
+ * the private data.
+ *
+ * @error_type_copy is a function that receives both original and a copy
+ * #GError and should copy the fields of the private error data. The standard
+ * #GError fields are already handled.
+ *
+ * @error_type_clear receives the pointer to the error, and it should free the
+ * fields of the private error data. It should not free the struct itself though.
+ *
+ * Normally, it is better to use G_DEFINE_EXTENDED_ERROR(), as it
+ * already takes care of passing valid information to this function.
+ *
+ * Returns: #GQuark representing the error domain
+ * Since: 2.68
+ */
+GQuark
+g_error_domain_register_static (const char *error_type_name,
+ gsize error_type_private_size,
+ GErrorInitFunc error_type_init,
+ GErrorCopyFunc error_type_copy,
+ GErrorClearFunc error_type_clear)
+{
+ GQuark error_quark;
+
+ g_return_val_if_fail (error_type_name != NULL, 0);
+ g_return_val_if_fail (error_type_private_size > 0, 0);
+ g_return_val_if_fail (error_type_init != NULL, 0);
+ g_return_val_if_fail (error_type_copy != NULL, 0);
+ g_return_val_if_fail (error_type_clear != NULL, 0);
+
+ error_quark = g_quark_from_static_string (error_type_name);
+ error_domain_register (error_quark,
+ error_type_private_size,
+ error_type_init,
+ error_type_copy,
+ error_type_clear);
+ return error_quark;
+}
+
+/**
+ * g_error_domain_register:
+ * @error_type_name: string to create a #GQuark from
+ * @error_type_private_size: size of the private error data in bytes
+ * @error_type_init: function initializing fields of the private error data
+ * @error_type_copy: function copying fields of the private error data
+ * @error_type_clear: function freeing fields of the private error data
+ *
+ * This function registers an extended #GError domain.
+ * @error_type_name will be duplicated. Otherwise does the same as
+ * g_error_domain_register_static().
+ *
+ * Returns: #GQuark representing the error domain
+ * Since: 2.68
+ */
+GQuark
+g_error_domain_register (const char *error_type_name,
+ gsize error_type_private_size,
+ GErrorInitFunc error_type_init,
+ GErrorCopyFunc error_type_copy,
+ GErrorClearFunc error_type_clear)
+{
+ GQuark error_quark;
+
+ g_return_val_if_fail (error_type_name != NULL, 0);
+ g_return_val_if_fail (error_type_private_size > 0, 0);
+ g_return_val_if_fail (error_type_init != NULL, 0);
+ g_return_val_if_fail (error_type_copy != NULL, 0);
+ g_return_val_if_fail (error_type_clear != NULL, 0);
+
+ error_quark = g_quark_from_string (error_type_name);
+ error_domain_register (error_quark,
+ error_type_private_size,
+ error_type_init,
+ error_type_copy,
+ error_type_clear);
+ return error_quark;
+}
+
+static GError *
+g_error_allocate (GQuark domain, ErrorDomainInfo *out_info)
+{
+ guint8 *allocated;
+ GError *error;
+ ErrorDomainInfo *info;
+ gsize private_size;
+
+ g_rw_lock_reader_lock (&error_domain_global);
+ info = error_domain_lookup (domain);
+ if (info != NULL)
+ {
+ if (out_info != NULL)
+ *out_info = *info;
+ private_size = info->private_size;
+ g_rw_lock_reader_unlock (&error_domain_global);
+ }
+ else
+ {
+ g_rw_lock_reader_unlock (&error_domain_global);
+ if (out_info != NULL)
+ memset (out_info, 0, sizeof (*out_info));
+ private_size = 0;
+ }
+ allocated = g_slice_alloc0 (private_size + sizeof (GError));
+ error = (GError *) (allocated + private_size);
+ return error;
+}
+
+/* This function takes ownership of @message. */
static GError *
-g_error_new_steal (GQuark domain,
- gint code,
- gchar *message)
+g_error_new_steal (GQuark domain,
+ gint code,
+ gchar *message,
+ ErrorDomainInfo *out_info)
{
- GError *error = g_slice_new (GError);
+ ErrorDomainInfo info;
+ GError *error = g_error_allocate (domain, &info);
error->domain = domain;
error->code = code;
error->message = message;
+ if (info.init != NULL)
+ info.init (error);
+ if (out_info != NULL)
+ *out_info = info;
+
return error;
}
@@ -424,7 +735,7 @@ g_error_new_valist (GQuark domain,
g_warn_if_fail (domain != 0);
g_warn_if_fail (format != NULL);
- return g_error_new_steal (domain, code, g_strdup_vprintf (format, args));
+ return g_error_new_steal (domain, code, g_strdup_vprintf (format, args), NULL);
}
/**
@@ -479,7 +790,7 @@ g_error_new_literal (GQuark domain,
g_return_val_if_fail (message != NULL, NULL);
g_return_val_if_fail (domain != 0, NULL);
- return g_error_new_steal (domain, code, g_strdup (message));
+ return g_error_new_steal (domain, code, g_strdup (message), NULL);
}
/**
@@ -491,11 +802,31 @@ g_error_new_literal (GQuark domain,
void
g_error_free (GError *error)
{
+ gsize private_size;
+ ErrorDomainInfo *info;
+ guint8 *allocated;
+
g_return_if_fail (error != NULL);
- g_free (error->message);
+ g_rw_lock_reader_lock (&error_domain_global);
+ info = error_domain_lookup (error->domain);
+ if (info != NULL)
+ {
+ GErrorClearFunc clear = info->clear;
- g_slice_free (GError, error);
+ private_size = info->private_size;
+ g_rw_lock_reader_unlock (&error_domain_global);
+ clear (error);
+ }
+ else
+ {
+ g_rw_lock_reader_unlock (&error_domain_global);
+ private_size = 0;
+ }
+
+ g_free (error->message);
+ allocated = ((guint8 *) error) - private_size;
+ g_slice_free1 (private_size + sizeof (GError), allocated);
}
/**
@@ -509,14 +840,22 @@ g_error_free (GError *error)
GError*
g_error_copy (const GError *error)
{
+ GError *copy;
+ ErrorDomainInfo info;
+
g_return_val_if_fail (error != NULL, NULL);
/* See g_error_new_valist for why these don't return */
g_warn_if_fail (error->domain != 0);
g_warn_if_fail (error->message != NULL);
- return g_error_new_steal (error->domain,
+ copy = g_error_new_steal (error->domain,
error->code,
- g_strdup (error->message));
+ g_strdup (error->message),
+ &info);
+ if (info.copy != NULL)
+ info.copy (error, copy);
+
+ return copy;
}
/**
diff --git a/glib/gerror.h b/glib/gerror.h
index 8ecff04e1..8773a1023 100644
--- a/glib/gerror.h
+++ b/glib/gerror.h
@@ -47,6 +47,143 @@ struct _GError
gchar *message;
};
+/**
+ * G_DEFINE_EXTENDED_ERROR:
+ * @ErrorType: name to return a #GQuark for
+ * @error_type: prefix for the function name
+ *
+ * A convenience macro which defines two functions. First, returning
+ * the #GQuark for the extended error type @ErrorType; it is called
+ * `@error_type_quark()`. Second, returning the private data from a
+ * passed #GError; it is called `@error_type_get_private()`.
+ *
+ * For this macro to work, a type named `@ErrorTypePrivate` should be
+ * defined, `@error_type_private_init()`, `@error_type_private_copy()`
+ * and `@error_type_private_clear()` functions need to be either
+ * declared or defined. The functions should be similar to
+ * #GErrorInitFunc, #GErrorCopyFunc and #GErrorClearFunc,
+ * respectively, but they should receive the private data type instead
+ * of #GError.
+ *
+ * Since: 2.68
+ */
+#define G_DEFINE_EXTENDED_ERROR(ErrorType, error_type) \
+static inline ErrorType ## Private * \
+error_type ## _get_private (const GError *error) \
+{ \
+ /* Copied from gtype.c (STRUCT_ALIGNMENT and ALIGN_STRUCT macros). */ \
+ const gsize sa = 2 * sizeof (gsize); \
+ const gsize as = (sizeof (ErrorType ## Private) + (sa - 1)) & -sa; \
+ g_return_val_if_fail (error != NULL, NULL); \
+ g_return_val_if_fail (error->domain == error_type ## _quark (), NULL); \
+ return (ErrorType ## Private *) (((guint8 *)error) - as); \
+} \
+ \
+static void \
+g_error_with_ ## error_type ## _private_init (GError *error) \
+{ \
+ ErrorType ## Private *priv = error_type ## _get_private (error); \
+ error_type ## _private_init (priv); \
+} \
+ \
+static void \
+g_error_with_ ## error_type ## _private_copy (const GError *src_error, \
+ GError *dest_error) \
+{ \
+ const ErrorType ## Private *src_priv = error_type ## _get_private (src_error); \
+ ErrorType ## Private *dest_priv = error_type ## _get_private (dest_error); \
+ error_type ## _private_copy (src_priv, dest_priv); \
+} \
+ \
+static void \
+g_error_with_ ## error_type ## _private_clear (GError *error) \
+{ \
+ ErrorType ## Private *priv = error_type ## _get_private (error); \
+ error_type ## _private_clear (priv); \
+} \
+ \
+GQuark \
+error_type ## _quark (void) \
+{ \
+ static GQuark q; \
+ static gsize initialized = 0; \
+ \
+ if (g_once_init_enter (&initialized)) \
+ { \
+ q = g_error_domain_register_static (#ErrorType, \
+ sizeof (ErrorType ## Private), \
+ g_error_with_ ## error_type ## _private_init, \
+ g_error_with_ ## error_type ## _private_copy, \
+ g_error_with_ ## error_type ## _private_clear); \
+ g_once_init_leave (&initialized, 1); \
+ } \
+ \
+ return q; \
+}
+
+/**
+ * GErrorInitFunc:
+ * @error: extended error
+ *
+ * Specifies the type of function which is called just after an
+ * extended error instance is created and its fields filled. It should
+ * only initialize the fields in the private data, which can be
+ * received with the generated `*_get_private()` function.
+ *
+ * Normally, it is better to use G_DEFINE_EXTENDED_ERROR(), as it
+ * already takes care of getting the private data from @error.
+ *
+ * Since: 2.68
+ */
+typedef void (*GErrorInitFunc) (GError *error);
+
+/**
+ * GErrorCopyFunc:
+ * @src_error: source extended error
+ * @dest_error: destination extended error
+ *
+ * Specifies the type of function which is called when an extended
+ * error instance is copied. It is passed the pointer to the
+ * destination error and source error, and should copy only the fields
+ * of the private data from @src_error to @dest_error.
+ *
+ * Normally, it is better to use G_DEFINE_EXTENDED_ERROR(), as it
+ * already takes care of getting the private data from @src_error and
+ * @dest_error.
+ *
+ * Since: 2.68
+ */
+typedef void (*GErrorCopyFunc) (const GError *src_error, GError *dest_error);
+
+/**
+ * GErrorClearFunc:
+ * @error: extended error to clear
+ *
+ * Specifies the type of function which is called when an extended
+ * error instance is freed. It is passed the error pointer about to be
+ * freed, and should free the error's private data fields.
+ *
+ * Normally, it is better to use G_DEFINE_EXTENDED_ERROR(), as it
+ * already takes care of getting the private data from @error.
+ *
+ * Since: 2.68
+ */
+typedef void (*GErrorClearFunc) (GError *error);
+
+GLIB_AVAILABLE_IN_2_68
+GQuark g_error_domain_register_static (const char *error_type_name,
+ gsize error_type_private_size,
+ GErrorInitFunc error_type_init,
+ GErrorCopyFunc error_type_copy,
+ GErrorClearFunc error_type_clear);
+
+GLIB_AVAILABLE_IN_2_68
+GQuark g_error_domain_register (const char *error_type_name,
+ gsize error_type_private_size,
+ GErrorInitFunc error_type_init,
+ GErrorCopyFunc error_type_copy,
+ GErrorClearFunc error_type_clear);
+
GLIB_AVAILABLE_IN_ALL
GError* g_error_new (GQuark domain,
gint code,
diff --git a/glib/glib-init.c b/glib/glib-init.c
index 902e18f4f..2958fb5be 100644
--- a/glib/glib-init.c
+++ b/glib/glib-init.c
@@ -337,6 +337,7 @@ glib_init (void)
g_messages_prefixed_init ();
g_debug_init ();
g_quark_init ();
+ g_error_init ();
}
#if defined (G_OS_WIN32)
diff --git a/glib/glib-init.h b/glib/glib-init.h
index f44b9a4d2..c25cbb0a1 100644
--- a/glib/glib-init.h
+++ b/glib/glib-init.h
@@ -27,6 +27,7 @@ extern GLogLevelFlags g_log_msg_prefix;
void glib_init (void);
void g_quark_init (void);
+void g_error_init (void);
#ifdef G_OS_WIN32
#include <windows.h>
diff --git a/glib/tests/error.c b/glib/tests/error.c
index 44e501506..c2e35d898 100644
--- a/glib/tests/error.c
+++ b/glib/tests/error.c
@@ -82,6 +82,105 @@ test_copy (void)
g_error_free (copy);
}
+typedef struct
+{
+ int init_called;
+ int copy_called;
+ int free_called;
+} TestErrorCheck;
+
+static TestErrorCheck *init_check;
+
+GQuark test_error_quark (void);
+#define TEST_ERROR (test_error_quark ())
+
+typedef struct
+{
+ int foo;
+ TestErrorCheck *check;
+} TestErrorPrivate;
+
+static void
+test_error_private_init (TestErrorPrivate *priv)
+{
+ priv->foo = 13;
+ /* If that triggers, it's test bug.
+ */
+ g_assert_nonnull (init_check);
+ /* Using global init_check, because error->check is still nil at
+ * this point.
+ */
+ init_check->init_called++;
+}
+
+static void
+test_error_private_copy (const TestErrorPrivate *src_priv,
+ TestErrorPrivate *dest_priv)
+{
+ dest_priv->foo = src_priv->foo;
+ dest_priv->check = src_priv->check;
+
+ dest_priv->check->copy_called++;
+}
+
+static void
+test_error_private_clear (TestErrorPrivate *priv)
+{
+ priv->check->free_called++;
+}
+
+G_DEFINE_EXTENDED_ERROR (TestError, test_error)
+
+static TestErrorPrivate *
+fill_test_error (GError *error, TestErrorCheck *check)
+{
+ TestErrorPrivate *test_error = test_error_get_private (error);
+
+ test_error->check = check;
+
+ return test_error;
+}
+
+static void
+test_extended (void)
+{
+ TestErrorCheck check = { 0, 0, 0 };
+ GError *error;
+ TestErrorPrivate *test_priv;
+ GError *copy_error;
+ TestErrorPrivate *copy_test_priv;
+
+ init_check = ✓
+ error = g_error_new_literal (TEST_ERROR, 0, "foo");
+ test_priv = fill_test_error (error, &check);
+
+ g_assert_cmpint (check.init_called, ==, 1);
+ g_assert_cmpint (check.copy_called, ==, 0);
+ g_assert_cmpint (check.free_called, ==, 0);
+
+ g_assert_cmpuint (error->domain, ==, TEST_ERROR);
+ g_assert_cmpint (test_priv->foo, ==, 13);
+
+ copy_error = g_error_copy (error);
+ g_assert_cmpint (check.init_called, ==, 2);
+ g_assert_cmpint (check.copy_called, ==, 1);
+ g_assert_cmpint (check.free_called, ==, 0);
+
+ g_assert_cmpuint (error->domain, ==, copy_error->domain);
+ g_assert_cmpint (error->code, ==, copy_error->code);
+ g_assert_cmpstr (error->message, ==, copy_error->message);
+
+ copy_test_priv = test_error_get_private (copy_error);
+ g_assert_cmpint (test_priv->foo, ==, copy_test_priv->foo);
+
+ g_error_free (error);
+ g_error_free (copy_error);
+
+ g_assert_cmpint (check.init_called, ==, 2);
+ g_assert_cmpint (check.copy_called, ==, 1);
+ g_assert_cmpint (check.free_called, ==, 2);
+}
+
int
main (int argc, char *argv[])
{
@@ -91,6 +190,7 @@ main (int argc, char *argv[])
g_test_add_func ("/error/prefix", test_prefix);
g_test_add_func ("/error/literal", test_literal);
g_test_add_func ("/error/copy", test_copy);
+ g_test_add_func ("/error/extended", test_extended);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]