summaryrefslogtreecommitdiffstats
path: root/libxml2/testOOMlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'libxml2/testOOMlib.c')
-rw-r--r--libxml2/testOOMlib.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/libxml2/testOOMlib.c b/libxml2/testOOMlib.c
new file mode 100644
index 0000000..34c4aa0
--- /dev/null
+++ b/libxml2/testOOMlib.c
@@ -0,0 +1,269 @@
+/*
+ * testOOM.c: Test out-of-memory handling
+ *
+ * See Copyright for the status of this software.
+ *
+ * Copyright 2003 Red Hat, Inc.
+ * Written by: hp@redhat.com
+ */
+
+#include "testOOMlib.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <string.h>
+
+#define _TEST_INT_MAX 2147483647
+#ifndef TRUE
+#define TRUE (1)
+#endif
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
+#include <libxml/xmlmemory.h>
+
+static int fail_alloc_counter = _TEST_INT_MAX;
+static int n_failures_per_failure = 1;
+static int n_failures_this_failure = 0;
+static int n_blocks_outstanding = 0;
+
+/**
+ * set_fail_alloc_counter:
+ * @until_next_fail: number of successful allocs before one fails
+ *
+ * Sets the number of allocations until we simulate a failed
+ * allocation. If set to 0, the next allocation to run
+ * fails; if set to 1, one succeeds then the next fails; etc.
+ * Set to _TEST_INT_MAX to not fail anything.
+ */
+static void
+set_fail_alloc_counter (int until_next_fail)
+{
+ fail_alloc_counter = until_next_fail;
+}
+
+/**
+ * get_fail_alloc_counter:
+ *
+ * Returns the number of successful allocs until we'll simulate
+ * a failed alloc.
+ */
+static int
+get_fail_alloc_counter (void)
+{
+ return fail_alloc_counter;
+}
+
+/**
+ * set_fail_alloc_failures:
+ * @failures_per_failure: number to fail
+ *
+ * Sets how many mallocs to fail when the fail alloc counter reaches
+ * 0.
+ *
+ */
+static void
+set_fail_alloc_failures (int failures_per_failure)
+{
+ n_failures_per_failure = failures_per_failure;
+}
+
+/**
+ * decrement_fail_alloc_counter:
+ *
+ * Called when about to alloc some memory; if
+ * it returns #TRUE, then the allocation should
+ * fail. If it returns #FALSE, then the allocation
+ * should not fail.
+ *
+ * returns #TRUE if this alloc should fail
+ */
+static int
+decrement_fail_alloc_counter (void)
+{
+ if (fail_alloc_counter <= 0)
+ {
+ n_failures_this_failure += 1;
+ if (n_failures_this_failure >= n_failures_per_failure)
+ {
+ fail_alloc_counter = _TEST_INT_MAX;
+
+ n_failures_this_failure = 0;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ fail_alloc_counter -= 1;
+ return FALSE;
+ }
+}
+
+/**
+ * test_get_malloc_blocks_outstanding:
+ *
+ * Get the number of outstanding malloc()'d blocks.
+ *
+ * Returns number of blocks
+ */
+int
+test_get_malloc_blocks_outstanding (void)
+{
+ return n_blocks_outstanding;
+}
+
+void*
+test_malloc (size_t bytes)
+{
+ if (decrement_fail_alloc_counter ())
+ {
+ /* FAIL the malloc */
+ return NULL;
+ }
+
+ if (bytes == 0) /* some system mallocs handle this, some don't */
+ return NULL;
+ else
+ {
+ void *mem;
+ mem = xmlMemMalloc (bytes);
+
+ if (mem)
+ n_blocks_outstanding += 1;
+
+ return mem;
+ }
+}
+
+void*
+test_realloc (void *memory,
+ size_t bytes)
+{
+ if (decrement_fail_alloc_counter ())
+ {
+ /* FAIL */
+ return NULL;
+ }
+
+ if (bytes == 0) /* guarantee this is safe */
+ {
+ test_free (memory);
+ return NULL;
+ }
+ else
+ {
+ void *mem;
+ mem = xmlMemRealloc (memory, bytes);
+
+ if (memory == NULL && mem != NULL)
+ n_blocks_outstanding += 1;
+
+ return mem;
+ }
+}
+
+void
+test_free (void *memory)
+{
+ if (memory) /* we guarantee it's safe to free (NULL) */
+ {
+ n_blocks_outstanding -= 1;
+
+ xmlMemFree (memory);
+ }
+}
+
+char*
+test_strdup (const char *str)
+{
+ int len;
+ char *copy;
+
+ if (str == NULL)
+ return NULL;
+
+ len = strlen (str);
+
+ copy = test_malloc (len + 1);
+ if (copy == NULL)
+ return NULL;
+
+ memcpy (copy, str, len + 1);
+
+ return copy;
+}
+
+static int
+run_failing_each_malloc (int n_mallocs,
+ TestMemoryFunction func,
+ void *data)
+{
+ n_mallocs += 10; /* fudge factor to ensure reallocs etc. are covered */
+
+ while (n_mallocs >= 0)
+ {
+ set_fail_alloc_counter (n_mallocs);
+
+ if (!(* func) (data))
+ return FALSE;
+
+ n_mallocs -= 1;
+ }
+
+ set_fail_alloc_counter (_TEST_INT_MAX);
+
+ return TRUE;
+}
+
+/**
+ * test_oom_handling:
+ * @func: function to call
+ * @data: data to pass to function
+ *
+ * Tests how well the given function responds to out-of-memory
+ * situations. Calls the function repeatedly, failing a different
+ * call to malloc() each time. If the function ever returns #FALSE,
+ * the test fails. The function should return #TRUE whenever something
+ * valid (such as returning an error, or succeeding) occurs, and #FALSE
+ * if it gets confused in some way.
+ *
+ * Returns #TRUE if the function never returns FALSE
+ */
+int
+test_oom_handling (TestMemoryFunction func,
+ void *data)
+{
+ int approx_mallocs;
+
+ /* Run once to see about how many mallocs are involved */
+
+ set_fail_alloc_counter (_TEST_INT_MAX);
+
+ if (!(* func) (data))
+ return FALSE;
+
+ approx_mallocs = _TEST_INT_MAX - get_fail_alloc_counter ();
+
+ set_fail_alloc_failures (1);
+ if (!run_failing_each_malloc (approx_mallocs, func, data))
+ return FALSE;
+
+ set_fail_alloc_failures (2);
+ if (!run_failing_each_malloc (approx_mallocs, func, data))
+ return FALSE;
+
+ set_fail_alloc_failures (3);
+ if (!run_failing_each_malloc (approx_mallocs, func, data))
+ return FALSE;
+
+ set_fail_alloc_counter (_TEST_INT_MAX);
+
+ return TRUE;
+}