summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPhilip Withnall <philip.withnall@collabora.co.uk>2014-11-17 11:43:13 +0000
committerPhilip Withnall <philip.withnall@collabora.co.uk>2014-11-17 11:46:16 +0000
commit983fa59d9b13c050f58e3f5716d89600551532fa (patch)
tree00b0ba9795579ce95cf6e51194714adbe79d6593 /tests
parent6faaa0a0f28bf18e4ff261c2b80ce7a23935a8df (diff)
gerror: Add a GError checker
This is a path-sensitive checker which detects various invalid usages of the GError API from GLib, such as overwriting GErrors which have not been cleared to NULL, or double-freeing GErrors. It can statically detect all run time errors from preconditions in GError methods. Includes a full unit test suite with > 95% coverage.
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/gerror-api.c665
-rw-r--r--tests/gerror.head.c16
-rw-r--r--tests/gerror.tail.c17
4 files changed, 701 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 3fc9e91..e14fb6e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -16,6 +16,7 @@ c_tests = \
gvariant-lookup.c \
gvariant-new.c \
nonnull.c \
+ gerror-api.c \
$(NULL)
templates = \
@@ -25,6 +26,8 @@ templates = \
assertion-return.tail.c \
generic.head.c \
generic.tail.c \
+ gerror.head.c \
+ gerror.tail.c \
gsignal.head.c \
gsignal.tail.c \
gvariant.head.c \
diff --git a/tests/gerror-api.c b/tests/gerror-api.c
new file mode 100644
index 0000000..6d03b1a
--- /dev/null
+++ b/tests/gerror-api.c
@@ -0,0 +1,665 @@
+/* Template: gerror */
+
+/*
+ * No error
+ */
+{
+ GError *some_error = g_error_new (G_IO_ERROR,
+ G_IO_ERROR_FAILED, "bah!");
+ g_error_free (some_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *some_error = g_error_new_literal (G_IO_ERROR,
+ G_IO_ERROR_FAILED, "bah!");
+ g_error_free (some_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *some_error;
+
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+
+ if (some_error == NULL) {
+ // Should never be reached, so we should never get an error
+ // about a double-free.
+ g_error_free (some_error);
+ }
+
+ g_error_free (some_error);
+}
+
+/*
+ * warning: Freeing non-set GError
+ * g_error_free (NULL);
+ * ^~~~~~~~~~~~~~~~~~~
+ */
+{
+ g_error_free (NULL);
+}
+
+/*
+ * warning: Freeing non-set GError
+ * g_error_free (some_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *some_error = NULL;
+ g_error_free (some_error);
+}
+
+/*
+ * warning: Using uninitialized GError
+ * g_error_free (some_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *some_error; // uninitialised
+ g_error_free (some_error);
+}
+
+/*
+ * warning: Freeing non-set GError
+ * g_error_free (some_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *some_error = NULL;
+
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_clear_error (&some_error);
+
+ g_error_free (some_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *some_error = NULL;
+
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_error_free (some_error);
+
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "new");
+ g_error_free (some_error);
+}
+
+/*
+ * warning: Overwriting already-freed GError
+ * g_set_error (&some_error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *some_error = NULL;
+
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_error_free (some_error);
+
+ g_set_error (&some_error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ g_clear_error (&some_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *some_error = NULL;
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_error_free (some_error);
+}
+
+/*
+ * warning: Freeing already-freed GError
+ * g_error_free (some_error); // double free
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *some_error = NULL;
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_error_free (some_error);
+ g_error_free (some_error); // double free
+}
+
+/*
+ * No error
+ */
+{
+ GError *some_error; // don’t explicitly initialise
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_error_free (some_error);
+}
+
+/*
+ * warning: Freeing non-set GError
+ * g_error_free (some_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *some_error = NULL;
+ if (some_cond) {
+ // Bug: @some_error is not set on the FALSE path.
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ }
+ g_error_free (some_error);
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ * ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *some_error = NULL;
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ * ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *some_error = NULL;
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ some_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ g_error_free (some_error);
+}
+
+/*
+ * No error
+ */
+{
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+}
+
+/*
+ * No error
+ */
+{
+ g_set_error (NULL, G_IO_ERROR, G_IO_ERROR_FAILED, "no-op");
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error = NULL;
+
+ g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+
+ if (sub_error == NULL) {
+ // Should never be reached, so we should never get an error
+ // about double-freeing.
+ g_error_free (sub_error);
+ }
+
+ g_error_free (sub_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *child_error = NULL;
+ g_set_error (&child_error, G_IO_ERROR, G_IO_ERROR_FAILED, "no-op");
+ g_error_free (child_error);
+}
+
+/*
+ * warning: Using uninitialized GError
+ * g_set_error (&child_error, G_IO_ERROR, G_IO_ERROR_FAILED, "non-clear");
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *child_error; // uninitialised
+ g_set_error (&child_error, G_IO_ERROR, G_IO_ERROR_FAILED, "non-clear");
+ g_error_free (child_error);
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ g_clear_error (error);
+}
+
+/*
+ * No error
+ */
+{
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_clear_error (error);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah again!");
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING, "Pending error");
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ guint i;
+ GError *child_error = NULL;
+
+ for (i = 0; i < 5; i++) {
+ some_failing_func (&child_error);
+ // ^-- bug: can overwrite @child_error when i > 0
+ }
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * g_propagate_error (error, sub_error2);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error1, *sub_error2;
+
+ sub_error1 = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure 1");
+ sub_error2 = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure 2");
+
+ g_propagate_error (error, sub_error1);
+ g_propagate_error (error, sub_error2);
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * g_propagate_error (error, sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ guint i;
+
+ for (i = 0; i < 5; i++) {
+ GError *sub_error = NULL;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failure");
+ g_propagate_error (error, sub_error);
+ // ^-- bug: will overwrite @error when i > 0
+ }
+}
+
+/*
+ * warning: Freeing already-freed GError
+ * g_error_free (main_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failure");
+ g_propagate_error (error, sub_error);
+ g_error_free (sub_error);
+ // ^-- sort-of-bug: This is the first time @sub_error is freed, but it
+ // means a dangling pointer is returned to the caller in @error
+ // FIXME: The dangling pointer should be detected and warned about
+ // more specifically.
+}
+
+/*
+ * warning: Freeing already-freed GError
+ * g_error_free (main_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_error (error, sub_error);
+ g_clear_error (&sub_error);
+ // ^-- sort-of-bug: This is the first time @sub_error is freed, but it
+ // means a dangling pointer is returned to the caller in @error
+ // FIXME: The dangling pointer should be detected and warned about
+ // more specifically.
+}
+
+/*
+ * warning: Freeing already-freed GError
+ * g_clear_error (&sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_error (NULL, sub_error);
+ g_clear_error (&sub_error);
+ // ^-- bug: @sub_error has already effectively been freed
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * sub_error = NULL;
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_error (error, sub_error);
+ // FIXME: false positive on this line; should be no error:
+ sub_error = NULL;
+ g_clear_error (&sub_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_prefixed_error (error, sub_error, "prefix: ");
+}
+
+/*
+ * warning: Freeing already-freed GError
+ * g_clear_error (&sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_prefixed_error (NULL, sub_error, "prefix: ");
+ g_clear_error (&sub_error);
+ // ^-- bug: @sub_error has already effectively been freed
+}
+
+/*
+ * warning: Freeing non-set GError
+ * g_propagate_error (error, sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error = NULL;
+ g_propagate_error (error, sub_error);
+}
+
+/*
+ * warning: Freeing already-freed GError
+ * g_propagate_error (error, sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_error_free (sub_error);
+
+ g_propagate_error (error, sub_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_error (NULL, sub_error);
+}
+
+/*
+ * warning: Using uninitialized GError
+ * g_propagate_error (&dest_error, sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error;
+ GError *dest_error; // uninitialised
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_error (&dest_error, sub_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error;
+ GError *dest_error = NULL;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_error (&dest_error, sub_error);
+
+ if (dest_error == NULL) {
+ // Should never be reached, so this double-free should never be
+ // a warning.
+ g_error_free (sub_error);
+ }
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Failure");
+ g_propagate_error (error, sub_error);
+
+ if (error != NULL && *error == NULL) {
+ // Should never be reached, so this double-free should never be
+ // a warning.
+ g_error_free (sub_error);
+ }
+}
+
+/*
+ * warning: Using uninitialized GError
+ * g_clear_error (&sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error;
+ g_clear_error (&sub_error);
+}
+
+/*
+ * No error
+ */
+{
+ g_clear_error (NULL);
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error = NULL;
+ g_clear_error (&sub_error);
+}
+
+/*
+ * warning: Freeing already-freed GError
+ * g_clear_error (&sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error = NULL;
+
+ g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_error_free (sub_error);
+ g_clear_error (&sub_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error = NULL;
+
+ g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_clear_error (&sub_error);
+
+ if (sub_error != NULL) {
+ // Should never be reached, so we should never get an error.
+ g_error_free (sub_error);
+ }
+}
+
+/*
+ * No error
+ */
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bah!");
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error = NULL;
+
+ if (rand ()) {
+ g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "blah");
+ }
+
+ g_clear_error (&sub_error);
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error = NULL;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original");
+
+ if (rand ()) {
+ g_set_error (&sub_error, G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ }
+
+ g_clear_error (&sub_error);
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error = NULL;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original");
+
+ if (rand ()) {
+ g_propagate_error (error, sub_error);
+ } else {
+ g_error_free (sub_error);
+ }
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * g_propagate_error (&sub_error, sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error = NULL;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original");
+
+ if (rand ()) {
+ g_propagate_error (&sub_error, sub_error);
+ } else {
+ g_error_free (sub_error);
+ }
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error = NULL;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original");
+
+ if (rand ()) {
+ g_clear_error (&sub_error);
+ }
+
+ g_clear_error (&sub_error);
+}
+
+/*
+ * warning: Freeing already-freed GError
+ * g_clear_error (&sub_error);
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error = NULL;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "original");
+ g_error_free (sub_error);
+
+ if (rand ()) {
+ g_clear_error (&sub_error);
+ }
+}
+
+/*
+ * No error
+ */
+{
+ GError *sub_error = NULL;
+
+ if (rand ()) {
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "1");
+ } else {
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "2");
+ }
+
+ g_error_free (sub_error);
+}
+
+/*
+ * warning: Overwriting already-set GError
+ * sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ * ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+{
+ GError *sub_error = NULL;
+
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "1");
+
+ if (rand ()) {
+ sub_error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "dupe");
+ }
+
+ g_error_free (sub_error);
+}
diff --git a/tests/gerror.head.c b/tests/gerror.head.c
new file mode 100644
index 0000000..8aa7e4c
--- /dev/null
+++ b/tests/gerror.head.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+static void
+some_failing_func (GError **error)
+{
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING, "Pending error");
+}
+
+static void
+some_failable_func (gboolean some_cond, GError **error)
+{
diff --git a/tests/gerror.tail.c b/tests/gerror.tail.c
new file mode 100644
index 0000000..647ca4a
--- /dev/null
+++ b/tests/gerror.tail.c
@@ -0,0 +1,17 @@
+ /* End of some_failable_func(). */
+}
+
+int
+main (void)
+{
+ GError *main_error = NULL;
+
+ some_failable_func (rand (), &main_error);
+
+ if (main_error != NULL) {
+ g_error_free (main_error);
+ return 1;
+ }
+
+ return 0;
+}