Add build system, remove dependency on glib, add TAP library
authorSimon Marchi <simon.marchi@efficios.com>
Wed, 4 Dec 2019 22:33:56 +0000 (17:33 -0500)
committerSimon Marchi <simon.marchi@efficios.com>
Wed, 4 Dec 2019 22:48:41 +0000 (17:48 -0500)
This commit...

 * adds an automake-based system.
 * removes glib dependencies from the library code (i.e. not from the tests),
   replacing them with home-grown code.
 * sets up the tests to run using the TAP library [1].

[1] https://github.com/shlomif/libtap-prev

Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Makefile.am [new file with mode: 0644]
argpar/Makefile.am [new file with mode: 0644]
argpar/argpar.c
argpar/argpar.h
configure.ac [new file with mode: 0644]
tests/Makefile.am [new file with mode: 0644]
tests/tap/Makefile.am [new file with mode: 0644]
tests/tap/tap.c [new file with mode: 0644]
tests/tap/tap.h [new file with mode: 0644]
tests/test_argpar.c

diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..6b36a14
--- /dev/null
@@ -0,0 +1,5 @@
+SUBDIRS = \
+       argpar \
+       tests
+
+ACLOCAL_AMFLAGS = -I m4
diff --git a/argpar/Makefile.am b/argpar/Makefile.am
new file mode 100644 (file)
index 0000000..175526a
--- /dev/null
@@ -0,0 +1,3 @@
+noinst_LTLIBRARIES = libargpar.la
+
+libargpar_la_SOURCES = argpar.c argpar.h
index 7f4e46b3d758332eff529b1396cfb3d4c578e59e..6a9e256e02854f32e9df08574f6f0600e8132e0e 100644 (file)
  * SOFTWARE.
  */
 
+#include <assert.h>
+#include <stdarg.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <glib.h>
-
-#include "common/assert.h"
-#include "common/common.h"
 
 #include "argpar.h"
 
+#define argpar_realloc(_ptr, _type, _nmemb) ((_type *) realloc(_ptr, (_nmemb) * sizeof(_type)))
+#define argpar_calloc(_type, _nmemb) ((_type *) calloc((_nmemb), sizeof(_type)))
+#define argpar_zalloc(_type) argpar_calloc(_type, 1)
+
+#define ARGPAR_ASSERT(_cond) assert(_cond)
+
+static
+char *argpar_vasprintf(const char *fmt, va_list args)
+{
+       int len1, len2;
+       char *str;
+       va_list args2;
+
+       va_copy(args2, args);
+
+       len1 = vsnprintf(NULL, 0, fmt, args);
+       if (len1 < 0) {
+               str = NULL;
+               goto end;
+       }
+
+       str = malloc(len1 + 1);
+       if (!str) {
+               goto end;
+       }
+
+       len2 = vsnprintf(str, len1 + 1, fmt, args2);
+
+       ARGPAR_ASSERT(len1 == len2);
+
+end:
+       return str;
+}
+
+
+static
+char *argpar_asprintf(const char *fmt, ...)
+{
+       va_list args;
+       char *str;
+       
+       va_start(args, fmt);
+       str = argpar_vasprintf(fmt, args);
+       va_end(args);
+
+       return str;
+}
+
+static
+bool argpar_string_append_printf(char **str, const char *fmt, ...)
+{
+       char *new_str = NULL;
+       char *addendum;
+       bool success;
+       va_list args;
+
+       ARGPAR_ASSERT(str);
+
+       va_start(args, fmt);
+       addendum = argpar_vasprintf(fmt, args);
+       va_end(args);
+
+       if (!addendum) {
+               success = false;
+               goto end;
+       }
+
+       new_str = argpar_asprintf("%s%s", *str ? *str : "", addendum);
+       if (!new_str) {
+               success = false;
+               goto end;
+       }
+       
+       free(*str);
+       *str = new_str;
+
+       success = true;
+
+end:
+       free(addendum);
+
+       return success;
+}
+
 static
 void destroy_item(struct bt_argpar_item * const item)
 {
@@ -40,22 +123,98 @@ void destroy_item(struct bt_argpar_item * const item)
        if (item->type == BT_ARGPAR_ITEM_TYPE_OPT) {
                struct bt_argpar_item_opt * const opt_item = (void *) item;
 
-               g_free((void *) opt_item->arg);
+               free((void *) opt_item->arg);
        }
 
-       g_free(item);
+       free(item);
 
 end:
        return;
 }
 
+static
+bool push_item(struct bt_argpar_item_array * const array,
+               struct bt_argpar_item * const item)
+{
+       bool success;
+
+       ARGPAR_ASSERT(array);
+       ARGPAR_ASSERT(item);
+
+       if (array->n_items == array->n_alloc) {
+               unsigned int new_n_alloc = array->n_alloc * 2;
+               struct bt_argpar_item **new_items;
+
+               new_items = argpar_realloc(array->items, 
+                       struct bt_argpar_item *, new_n_alloc);
+               if (!new_items) {
+                       success = false;
+                       goto end;
+               }
+
+               array->n_alloc = new_n_alloc;
+               array->items = new_items;
+       }
+
+       array->items[array->n_items] = item;
+       array->n_items++;
+
+       success = true;
+
+end:
+       return success;
+}
+
+static
+void destroy_item_array(struct bt_argpar_item_array * const array)
+{
+       if (array) {
+               unsigned int i;
+
+               for (i = 0; i < array->n_items; i++) {
+                       destroy_item(array->items[i]);
+               }
+
+               free(array->items);
+               free(array);
+       }
+}
+
+static
+struct bt_argpar_item_array *new_item_array(void)
+{
+       struct bt_argpar_item_array *ret;
+       const int initial_size = 10;
+
+       ret = argpar_zalloc(struct bt_argpar_item_array);
+       if (!ret) {
+               goto end;
+       }
+
+       ret->items = argpar_calloc(struct bt_argpar_item *, initial_size);
+       if (!ret->items) {
+               goto error;
+       }
+
+       ret->n_alloc = initial_size;
+
+       goto end;
+
+error:
+       destroy_item_array(ret);
+       ret = NULL;
+
+end:
+       return ret;
+}
+
 static
 struct bt_argpar_item_opt *create_opt_item(
                const struct bt_argpar_opt_descr * const descr,
                const char * const arg)
 {
        struct bt_argpar_item_opt *opt_item =
-               g_new0(struct bt_argpar_item_opt, 1);
+               argpar_zalloc(struct bt_argpar_item_opt);
 
        if (!opt_item) {
                goto end;
@@ -65,7 +224,7 @@ struct bt_argpar_item_opt *create_opt_item(
        opt_item->descr = descr;
 
        if (arg) {
-               opt_item->arg = g_strdup(arg);
+               opt_item->arg = strdup(arg);
                if (!opt_item->arg) {
                        goto error;
                }
@@ -87,7 +246,7 @@ struct bt_argpar_item_non_opt *create_non_opt_item(const char * const arg,
                const unsigned int non_opt_index)
 {
        struct bt_argpar_item_non_opt * const non_opt_item =
-               g_new0(struct bt_argpar_item_non_opt, 1);
+               argpar_zalloc(struct bt_argpar_item_non_opt);
 
        if (!non_opt_item) {
                goto end;
@@ -142,7 +301,7 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts,
        const char *short_opt_ch = short_opts;
 
        if (strlen(short_opts) == 0) {
-               g_string_append(parse_ret->error, "Invalid argument");
+               argpar_string_append_printf(&parse_ret->error, "Invalid argument");
                goto error;
        }
 
@@ -155,7 +314,7 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts,
                descr = find_descr(descrs, *short_opt_ch, NULL);
                if (!descr) {
                        ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT;
-                       g_string_append_printf(parse_ret->error,
+                       argpar_string_append_printf(&parse_ret->error,
                                "Unknown option `-%c`", *short_opt_ch);
                        goto error;
                }
@@ -176,7 +335,7 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts,
                         * expected.
                         */
                        if (!opt_arg || (short_opt_ch[1] && strlen(opt_arg) == 0)) {
-                               g_string_append_printf(parse_ret->error,
+                               argpar_string_append_printf(&parse_ret->error,
                                        "Missing required argument for option `-%c`",
                                        *short_opt_ch);
                                *used_next_orig_arg = false;
@@ -190,7 +349,9 @@ enum parse_orig_arg_opt_ret parse_short_opts(const char * const short_opts,
                        goto error;
                }
 
-               g_ptr_array_add(parse_ret->items, opt_item);
+               if (!push_item(parse_ret->items, &opt_item->base)) {
+                       goto error;
+               }
 
                if (descr->with_arg) {
                        /* Option has an argument: no more options */
@@ -237,7 +398,8 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
        const char *long_opt_name = long_opt_arg;
 
        if (strlen(long_opt_arg) == 0) {
-               g_string_append(parse_ret->error, "Invalid argument");
+               argpar_string_append_printf(&parse_ret->error,
+                       "Invalid argument");
                goto error;
        }
 
@@ -248,7 +410,7 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
 
                /* Isolate the option name */
                if (long_opt_name_size > max_len) {
-                       g_string_append_printf(parse_ret->error,
+                       argpar_string_append_printf(&parse_ret->error,
                                "Invalid argument `--%s`", long_opt_arg);
                        goto error;
                }
@@ -261,7 +423,7 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
        /* Find corresponding option descriptor */
        descr = find_descr(descrs, '\0', long_opt_name);
        if (!descr) {
-               g_string_append_printf(parse_ret->error,
+               argpar_string_append_printf(&parse_ret->error,
                        "Unknown option `--%s`", long_opt_name);
                ret = PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT;
                goto error;
@@ -275,7 +437,7 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
                } else {
                        /* `--long-opt arg` style */
                        if (!next_orig_arg) {
-                               g_string_append_printf(parse_ret->error,
+                               argpar_string_append_printf(&parse_ret->error,
                                        "Missing required argument for option `--%s`",
                                        long_opt_name);
                                goto error;
@@ -292,7 +454,10 @@ enum parse_orig_arg_opt_ret parse_long_opt(const char * const long_opt_arg,
                goto error;
        }
 
-       g_ptr_array_add(parse_ret->items, opt_item);
+       if (!push_item(parse_ret->items, &opt_item->base)) {
+               goto error;
+       }
+
        goto end;
 
 error:
@@ -313,7 +478,7 @@ enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg,
 {
        enum parse_orig_arg_opt_ret ret = PARSE_ORIG_ARG_OPT_RET_OK;
 
-       BT_ASSERT(orig_arg[0] == '-');
+       ARGPAR_ASSERT(orig_arg[0] == '-');
 
        if (orig_arg[1] == '-') {
                /* Long option */
@@ -331,29 +496,31 @@ enum parse_orig_arg_opt_ret parse_orig_arg_opt(const char * const orig_arg,
 }
 
 static
-void prepend_while_parsing_arg_to_error(GString * const error,
+bool prepend_while_parsing_arg_to_error(char **error,
                const unsigned int i, const char * const arg)
 {
-       /* 🙁 There's no g_string_prepend_printf()! */
-       GString * const tmp_str = g_string_new(NULL);
+       char *new_error;
+       bool success;
 
-       BT_ASSERT(error);
-       BT_ASSERT(arg);
+       ARGPAR_ASSERT(error);
+       ARGPAR_ASSERT(*error);
 
-       if (!tmp_str) {
+       new_error = argpar_asprintf("While parsing argument #%u (`%s`): %s",
+               i + 1, arg, *error);
+       if (!new_error) {
+               success = false;
                goto end;
        }
 
-       g_string_append_printf(tmp_str, "While parsing argument #%u (`%s`): %s",
-               i + 1, arg, error->str);
-       g_string_assign(error, tmp_str->str);
-       g_string_free(tmp_str, TRUE);
+       free(*error);
+       *error = new_error;
+       success = true;
 
 end:
-       return;
+       return success;
 }
 
-BT_HIDDEN
+ARGPAR_HIDDEN
 struct bt_argpar_parse_ret bt_argpar_parse(unsigned int argc,
                const char * const *argv,
                const struct bt_argpar_opt_descr * const descrs,
@@ -363,13 +530,7 @@ struct bt_argpar_parse_ret bt_argpar_parse(unsigned int argc,
        unsigned int i;
        unsigned int non_opt_index = 0;
 
-       parse_ret.error = g_string_new(NULL);
-       if (!parse_ret.error) {
-               goto error;
-       }
-
-       parse_ret.items = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) destroy_item);
+       parse_ret.items = new_item_array();
        if (!parse_ret.items) {
                goto error;
        }
@@ -391,7 +552,11 @@ struct bt_argpar_parse_ret bt_argpar_parse(unsigned int argc,
                        }
 
                        non_opt_index++;
-                       g_ptr_array_add(parse_ret.items, non_opt_item);
+
+                       if (!push_item(parse_ret.items, &non_opt_item->base)) {
+                               goto error;
+                       }
+
                        continue;
                }
 
@@ -402,11 +567,11 @@ struct bt_argpar_parse_ret bt_argpar_parse(unsigned int argc,
                case PARSE_ORIG_ARG_OPT_RET_OK:
                        break;
                case PARSE_ORIG_ARG_OPT_RET_ERROR_UNKNOWN_OPT:
-                       BT_ASSERT(!used_next_orig_arg);
+                       ARGPAR_ASSERT(!used_next_orig_arg);
 
                        if (fail_on_unknown_opt) {
                                prepend_while_parsing_arg_to_error(
-                                       parse_ret.error, i, orig_arg);
+                                       &parse_ret.error, i, orig_arg);
                                goto error;
                        }
 
@@ -416,15 +581,15 @@ struct bt_argpar_parse_ret bt_argpar_parse(unsigned int argc,
                         * unknown option.
                         */
                        parse_ret.ingested_orig_args = i;
-                       g_string_free(parse_ret.error, TRUE);
+                       free(parse_ret.error);
                        parse_ret.error = NULL;
                        goto end;
                case PARSE_ORIG_ARG_OPT_RET_ERROR:
                        prepend_while_parsing_arg_to_error(
-                               parse_ret.error, i, orig_arg);
+                               &parse_ret.error, i, orig_arg);
                        goto error;
                default:
-                       bt_common_abort();
+                       abort();
                }
 
                if (used_next_orig_arg) {
@@ -433,33 +598,27 @@ struct bt_argpar_parse_ret bt_argpar_parse(unsigned int argc,
        }
 
        parse_ret.ingested_orig_args = argc;
-       g_string_free(parse_ret.error, TRUE);
+       free(parse_ret.error);
        parse_ret.error = NULL;
        goto end;
 
 error:
-       if (parse_ret.items) {
-               /* That's how we indicate that an error occured */
-               g_ptr_array_free(parse_ret.items, TRUE);
-               parse_ret.items = NULL;
-       }
+       /* That's how we indicate that an error occured */
+       destroy_item_array(parse_ret.items);
+       parse_ret.items = NULL;
 
 end:
        return parse_ret;
 }
 
-BT_HIDDEN
+ARGPAR_HIDDEN
 void bt_argpar_parse_ret_fini(struct bt_argpar_parse_ret *ret)
 {
-       BT_ASSERT(ret);
+       ARGPAR_ASSERT(ret);
 
-       if (ret->items) {
-               g_ptr_array_free(ret->items, TRUE);
-               ret->items = NULL;
-       }
+       destroy_item_array(ret->items);
+       ret->items = NULL;
 
-       if (ret->error) {
-               g_string_free(ret->error, TRUE);
-               ret->error = NULL;
-       }
+       free(ret->error);
+       ret->error = NULL;
 }
index 85a663dc738d5a9401d5e3734a4fc4693e09b006..5732a88dd8671d15444f25a54b23cda0d75cf8ce 100644 (file)
  * SOFTWARE.
  */
 
-#include <glib.h>
 #include <stdbool.h>
 
-#include "common/macros.h"
-
 /* Sentinel for an option descriptor array */
 #define BT_ARGPAR_OPT_DESCR_SENTINEL   { -1, '\0', NULL, false }
 
+/*
+ * ARGPAR_HIDDEN: if argpar is used in some shared library, we don't want them
+ * to be exported by that library, so mark them as "hidden".
+ *
+ * On Windows, symbols are local unless explicitly exported,
+ * see https://gcc.gnu.org/wiki/Visibility
+ */
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define ARGPAR_HIDDEN
+#else
+#define ARGPAR_HIDDEN __attribute__((visibility("hidden")))
+#endif
+
 /* Option descriptor */
 struct bt_argpar_opt_descr {
        /* Numeric ID for this option */
@@ -88,13 +98,24 @@ struct bt_argpar_item_non_opt {
        unsigned int non_opt_index;
 };
 
+struct bt_argpar_item_array {
+       /* Array of `struct bt_argpar_item *`, or `NULL` on error */
+       struct bt_argpar_item **items;
+
+       /* Number of used slots in `data`. */
+       unsigned int n_items;
+
+       /* Number of allocated slots in `data`. */
+       unsigned int n_alloc;
+};
+
 /* What is returned by bt_argpar_parse() */
 struct bt_argpar_parse_ret {
        /* Array of `struct bt_argpar_item *`, or `NULL` on error */
-       GPtrArray *items;
+       struct bt_argpar_item_array *items;
 
        /* Error string, or `NULL` if none */
-       GString *error;
+       char *error;
 
        /* Number of original arguments (`argv`) ingested */
        unsigned int ingested_orig_args;
@@ -194,7 +215,7 @@ struct bt_argpar_parse_ret {
  * You can finalize the returned structure with
  * bt_argpar_parse_ret_fini().
  */
-BT_HIDDEN
+ARGPAR_HIDDEN
 struct bt_argpar_parse_ret bt_argpar_parse(unsigned int argc,
                const char * const *argv,
                const struct bt_argpar_opt_descr *descrs,
@@ -206,7 +227,7 @@ struct bt_argpar_parse_ret bt_argpar_parse(unsigned int argc,
  * It is safe to call bt_argpar_parse() multiple times with the same
  * structure.
  */
-BT_HIDDEN
+ARGPAR_HIDDEN
 void bt_argpar_parse_ret_fini(struct bt_argpar_parse_ret *ret);
 
 #endif /* BABELTRACE_ARGPAR_H */
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..01f70d7
--- /dev/null
@@ -0,0 +1,17 @@
+AC_INIT([argpar], [1])
+AM_INIT_AUTOMAKE([foreign])
+LT_INIT
+
+AC_CONFIG_MACRO_DIRS([m4])
+
+# Depend on glib just for the tests.
+PKG_CHECK_MODULES([GLIB], [glib-2.0])
+
+AC_CONFIG_FILES([
+       Makefile
+       argpar/Makefile
+       tests/Makefile
+       tests/tap/Makefile
+])
+
+AC_OUTPUT
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644 (file)
index 0000000..fa84fc1
--- /dev/null
@@ -0,0 +1,15 @@
+SUBDIRS = tap
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir) \
+       -I$(top_srcdir)/tests/tap \
+       $(GLIB_CFLAGS)
+
+noinst_PROGRAMS = test_argpar
+test_argpar_SOURCES = test_argpar.c
+test_argpar_LDADD = \
+       $(top_builddir)/tests/tap/libtap.la \
+       $(top_builddir)/argpar/libargpar.la \
+       $(GLIB_LIBS)
+
+TESTS = test_argpar
diff --git a/tests/tap/Makefile.am b/tests/tap/Makefile.am
new file mode 100644 (file)
index 0000000..7b1bc21
--- /dev/null
@@ -0,0 +1,3 @@
+noinst_LTLIBRARIES = libtap.la
+
+libtap_la_SOURCES = tap.c tap.h
diff --git a/tests/tap/tap.c b/tests/tap/tap.c
new file mode 100644 (file)
index 0000000..09ac2af
--- /dev/null
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tap.h"
+
+static int no_plan = 0;
+static int skip_all = 0;
+static int have_plan = 0;
+static unsigned int test_count = 0; /* Number of tests that have been run */
+static unsigned int e_tests = 0; /* Expected number of tests to run */
+static unsigned int failures = 0; /* Number of tests that failed */
+static char *todo_msg = NULL;
+static char *todo_msg_fixed = "libtap malloc issue";
+static int todo = 0;
+static int test_died = 0;
+static const  int exit_die  = 255;       /* exit-code on die() */
+
+
+/* Encapsulate the pthread code in a conditional.  In the absence of
+   libpthread the code does nothing */
+#if defined(LIBTAP_ENABLE_BROKEN_THREAD_SAFE) && defined(HAVE_LIBPTHREAD)
+#include <pthread.h>
+static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&M);
+# define UNLOCK pthread_mutex_unlock(&M);
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+static void _expected_tests(unsigned int);
+static void _tap_init(void);
+static void _cleanup(void);
+
+/*
+ * Generate a test result.
+ *
+ * ok -- boolean, indicates whether or not the test passed.
+ * test_name -- the name of the test, may be NULL
+ * test_comment -- a comment to print afterwards, may be NULL
+ */
+unsigned int
+_gen_result(int ok, const char *func, const char *file, unsigned int line,
+           const char *test_name, ...)
+{
+       va_list ap;
+       char *local_test_name = NULL;
+       char *c;
+       int name_is_digits;
+
+       LOCK;
+
+       test_count++;
+
+       /* Start by taking the test name and performing any printf()
+          expansions on it */
+       if(test_name != NULL) {
+               va_start(ap, test_name);
+               if (vasprintf(&local_test_name, test_name, ap) < 0)
+        {
+            local_test_name = NULL;
+        }
+               va_end(ap);
+
+               /* Make sure the test name contains more than digits
+                  and spaces.  Emit an error message and exit if it
+                  does */
+               if(local_test_name) {
+                       name_is_digits = 1;
+                       for(c = local_test_name; *c != '\0'; c++) {
+                               if(!isdigit(*c) && !isspace(*c)) {
+                                       name_is_digits = 0;
+                                       break;
+                               }
+                       }
+
+                       if(name_is_digits) {
+                               diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
+                               diag("    Very confusing.");
+                       }
+               }
+       }
+
+       if(!ok) {
+               printf("not ");
+               failures++;
+       }
+
+       printf("ok %d", test_count);
+
+       if(test_name != NULL) {
+               printf(" - ");
+
+               /* Print the test name, escaping any '#' characters it
+                  might contain */
+               if(local_test_name != NULL) {
+                       flockfile(stdout);
+                       for(c = local_test_name; *c != '\0'; c++) {
+                               if(*c == '#')
+                                       fputc('\\', stdout);
+                               fputc((int)*c, stdout);
+                       }
+                       funlockfile(stdout);
+               } else {        /* vasprintf() failed, use a fixed message */
+                       printf("%s", todo_msg_fixed);
+               }
+       }
+
+       /* If we're in a todo_start() block then flag the test as being
+          TODO.  todo_msg should contain the message to print at this
+          point.  If it's NULL then asprintf() failed, and we should
+          use the fixed message.
+
+          This is not counted as a failure, so decrement the counter if
+          the test failed. */
+       if(todo) {
+               printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
+               if(!ok)
+                       failures--;
+       }
+
+       printf("\n");
+
+       if(!ok)
+               diag("    Failed %stest (%s:%s() at line %d)",
+                    todo ? "(TODO) " : "", file, func, line);
+
+       free(local_test_name);
+
+       UNLOCK;
+
+       /* We only care (when testing) that ok is positive, but here we
+          specifically only want to return 1 or 0 */
+       return ok ? 1 : 0;
+}
+
+/*
+ * Initialise the TAP library.  Will only do so once, however many times it's
+ * called.
+ */
+void
+_tap_init(void)
+{
+       static int run_once = 0;
+
+       LOCK;
+
+       if(!run_once) {
+               atexit(_cleanup);
+
+               /* stdout needs to be unbuffered so that the output appears
+                  in the same place relative to stderr output as it does
+                  with Test::Harness */
+               setbuf(stdout, 0);
+               run_once = 1;
+       }
+
+       UNLOCK;
+}
+
+/*
+ * Note that there's no plan.
+ */
+int
+plan_no_plan(void)
+{
+
+       LOCK;
+
+       _tap_init();
+
+       if(have_plan != 0) {
+               fprintf(stderr, "You tried to plan twice!\n");
+               test_died = 1;
+               UNLOCK;
+               exit(exit_die);
+       }
+
+       have_plan = 1;
+       no_plan = 1;
+
+       UNLOCK;
+
+       return 1;
+}
+
+/*
+ * Note that the plan is to skip all tests
+ */
+int
+plan_skip_all(const char *reason)
+{
+
+       LOCK;
+
+       _tap_init();
+
+       skip_all = 1;
+
+       printf("1..0");
+
+       if(reason != NULL)
+               printf(" # SKIP %s", reason);
+
+       printf("\n");
+
+       UNLOCK;
+
+       exit(0);
+}
+
+/*
+ * Note the number of tests that will be run.
+ */
+int
+plan_tests(unsigned int tests)
+{
+
+       LOCK;
+
+       _tap_init();
+
+       if(have_plan != 0) {
+               fprintf(stderr, "You tried to plan twice!\n");
+               test_died = 1;
+               UNLOCK;
+               exit(exit_die);
+       }
+
+       if(tests == 0) {
+               fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
+               test_died = 1;
+               UNLOCK;
+               exit(exit_die);
+       }
+
+       have_plan = 1;
+
+       _expected_tests(tests);
+
+       UNLOCK;
+
+       return 1;
+}
+
+unsigned int
+diag(const char *fmt, ...)
+{
+       va_list ap;
+
+       LOCK;
+
+       fputs("# ", stderr);
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       fputs("\n", stderr);
+
+       UNLOCK;
+
+       return 0;
+}
+
+void
+_expected_tests(unsigned int tests)
+{
+
+       LOCK;
+
+       printf("1..%d\n", tests);
+       e_tests = tests;
+
+       UNLOCK;
+}
+
+int
+skip(unsigned int n, const char *fmt, ...)
+{
+       va_list ap;
+       char *skip_msg;
+
+       LOCK;
+
+       va_start(ap, fmt);
+    if (vasprintf(&skip_msg, fmt, ap) < 0)
+    {
+        skip_msg = NULL;
+    }
+       va_end(ap);
+
+       while(n-- > 0) {
+               test_count++;
+               printf("ok %d # skip %s\n", test_count,
+                      skip_msg != NULL ?
+                      skip_msg : "libtap():malloc() failed");
+       }
+
+       free(skip_msg);
+
+       UNLOCK;
+
+       return 1;
+}
+
+void
+todo_start(const char *fmt, ...)
+{
+       va_list ap;
+
+       LOCK;
+
+       va_start(ap, fmt);
+       if (vasprintf(&todo_msg, fmt, ap) < 0)
+    {
+        todo_msg = NULL;
+    }
+       va_end(ap);
+
+       todo = 1;
+
+       UNLOCK;
+}
+
+void
+todo_end(void)
+{
+
+       LOCK;
+
+       todo = 0;
+       free(todo_msg);
+
+       UNLOCK;
+}
+
+int
+exit_status(void)
+{
+       int r;
+
+       LOCK;
+
+       /* If there's no plan, just return the number of failures */
+       if(no_plan || !have_plan) {
+               UNLOCK;
+               return failures;
+       }
+
+       /* Ran too many tests?  Return the number of tests that were run
+          that shouldn't have been */
+       if(e_tests < test_count) {
+               r = test_count - e_tests;
+               UNLOCK;
+               return r;
+       }
+
+       /* Return the number of tests that failed + the number of tests
+          that weren't run */
+       r = failures + e_tests - test_count;
+       UNLOCK;
+
+       return r;
+}
+
+/*
+ * Cleanup at the end of the run, produce any final output that might be
+ * required.
+ */
+void
+_cleanup(void)
+{
+
+       LOCK;
+
+       /* If plan_no_plan() wasn't called, and we don't have a plan,
+          and we're not skipping everything, then something happened
+          before we could produce any output */
+       if(!no_plan && !have_plan && !skip_all) {
+               diag("Looks like your test died before it could output anything.");
+               UNLOCK;
+               return;
+       }
+
+       if(test_died) {
+               diag("Looks like your test exited with %d just after %d.", exit_die, test_count);
+               UNLOCK;
+               return;
+       }
+
+
+       /* No plan provided, but now we know how many tests were run, and can
+          print the header at the end */
+       if(!skip_all && (no_plan || !have_plan)) {
+               printf("1..%d\n", test_count);
+       }
+
+       if((have_plan && !no_plan) && e_tests < test_count) {
+               diag("Looks like you planned %d test%s but ran %d extra.",
+                    e_tests, e_tests == 1 ? "":"s", test_count - e_tests);
+               UNLOCK;
+               return;
+       }
+
+       if((have_plan || !no_plan) && e_tests > test_count) {
+               diag("Looks like you planned %d test%s but ran %d.",
+                    e_tests, e_tests == 1 ? "":"s", test_count);
+               UNLOCK;
+               return;
+       }
+
+       if(failures)
+               diag("Looks like you failed %d test%s of %d.",
+                    failures, failures == 1 ? "":"s", test_count);
+
+       UNLOCK;
+}
diff --git a/tests/tap/tap.h b/tests/tap/tap.h
new file mode 100644 (file)
index 0000000..c2dfb4b
--- /dev/null
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 2004 Nik Clayton
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * plan_tests - announce the number of tests you plan to run
+ * @tests: the number of tests
+ *
+ * This should be the first call in your test program: it allows tracing
+ * of failures which mean that not all tests are run.
+ *
+ * If you don't know how many tests will actually be run, assume all of them
+ * and use skip() if you don't actually run some tests.
+ *
+ * Example:
+ *     plan_tests(13);
+ */
+int plan_tests(unsigned int tests);
+static inline int plan(unsigned int tests)
+{
+    return plan_tests(tests);
+}
+#if (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) && !defined(__GNUC__)
+# error "Needs gcc or C99 compiler for variadic macros."
+#else
+
+/**
+ * ok1 - Simple conditional test
+ * @e: the expression which we expect to be true.
+ *
+ * This is the simplest kind of test: if the expression is true, the
+ * test passes.  The name of the test which is printed will simply be
+ * file name, line number, and the expression itself.
+ *
+ * Example:
+ *     ok1(init_subsystem() == 1);
+ */
+# define ok1(e) ((e) ?                                                 \
+                _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
+                _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
+
+/**
+ * ok - Conditional test with a name
+ * @e: the expression which we expect to be true.
+ * @...: the printf-style name of the test.
+ *
+ * If the expression is true, the test passes.  The name of the test will be
+ * the filename, line number, and the printf-style string.  This can be clearer
+ * than simply the expression itself.
+ *
+ * Example:
+ *     ok1(init_subsystem() == 1);
+ *     ok(init_subsystem() == 0, "Second initialization should fail");
+ */
+# define ok(e, ...) ((e) ?                                             \
+                    _gen_result(1, __func__, __FILE__, __LINE__,       \
+                                __VA_ARGS__) :                         \
+                    _gen_result(0, __func__, __FILE__, __LINE__,       \
+                                __VA_ARGS__))
+
+/**
+ * pass - Note that a test passed
+ * @...: the printf-style name of the test.
+ *
+ * For complicated code paths, it can be easiest to simply call pass() in one
+ * branch and fail() in another.
+ *
+ * Example:
+ *     x = do_something();
+ *     if (!checkable(x) || check_value(x))
+ *             pass("do_something() returned a valid value");
+ *     else
+ *             fail("do_something() returned an invalid value");
+ */
+# define pass(...) ok(1, __VA_ARGS__)
+
+/**
+ * fail - Note that a test failed
+ * @...: the printf-style name of the test.
+ *
+ * For complicated code paths, it can be easiest to simply call pass() in one
+ * branch and fail() in another.
+ */
+# define fail(...) ok(0, __VA_ARGS__)
+
+/* I don't find these to be useful. */
+# define skip_if(cond, n, ...)                         \
+       if (cond) skip((n), __VA_ARGS__);               \
+       else
+
+# define skip_start(test, n, ...)                      \
+       do {                                            \
+               if((test)) {                            \
+                       skip(n,  __VA_ARGS__);          \
+                       continue;                       \
+               }
+
+# define skip_end } while(0)
+
+#ifndef PRINTF_ATTRIBUTE
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+unsigned int _gen_result(int, const char *, const char *, unsigned int,
+   const char *, ...) PRINTF_ATTRIBUTE(5, 6);
+
+/**
+ * diag - print a diagnostic message (use instead of printf/fprintf)
+ * @fmt: the format of the printf-style message
+ *
+ * diag ensures that the output will not be considered to be a test
+ * result by the TAP test harness.  It will append '\n' for you.
+ *
+ * Example:
+ *     diag("Now running complex tests");
+ */
+unsigned int diag(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+
+/**
+ * skip - print a diagnostic message (use instead of printf/fprintf)
+ * @n: number of tests you're skipping.
+ * @fmt: the format of the reason you're skipping the tests.
+ *
+ * Sometimes tests cannot be run because the test system lacks some feature:
+ * you should explicitly document that you're skipping tests using skip().
+ *
+ * From the Test::More documentation:
+ *   If it's something the user might not be able to do, use SKIP.  This
+ *   includes optional modules that aren't installed, running under an OS that
+ *   doesn't have some feature (like fork() or symlinks), or maybe you need an
+ *   Internet connection and one isn't available.
+ *
+ * Example:
+ *     #ifdef HAVE_SOME_FEATURE
+ *     ok1(test_some_feature());
+ *     #else
+ *     skip(1, "Don't have SOME_FEATURE");
+ *     #endif
+ */
+int skip(unsigned int n, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3);
+
+/**
+ * todo_start - mark tests that you expect to fail.
+ * @fmt: the reason they currently fail.
+ *
+ * It's extremely useful to write tests before you implement the matching fix
+ * or features: surround these tests by todo_start()/todo_end().  These tests
+ * will still be run, but with additional output that indicates that they are
+ * expected to fail.
+ *
+ * This way, should a test start to succeed unexpectedly, tools like prove(1)
+ * will indicate this and you can move the test out of the todo block.  This
+ * is much more useful than simply commenting out (or '#if 0') the tests.
+ *
+ * From the Test::More documentation:
+ *   If it's something the programmer hasn't done yet, use TODO.  This is for
+ *   any code you haven't written yet, or bugs you have yet to fix, but want to
+ *   put tests in your testing script (always a good idea).
+ *
+ * Example:
+ *     todo_start("dwim() not returning true yet");
+ *     ok(dwim(), "Did what the user wanted");
+ *     todo_end();
+ */
+void todo_start(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+
+/**
+ * todo_end - end of tests you expect to fail.
+ *
+ * See todo_start().
+ */
+void todo_end(void);
+
+/**
+ * exit_status - the value that main should return.
+ *
+ * For maximum compatability your test program should return a particular exit
+ * code (ie. 0 if all tests were run, and every test which was expected to
+ * succeed succeeded).
+ *
+ * Example:
+ *     exit(exit_status());
+ */
+int exit_status(void);
+
+/**
+ * plan_no_plan - I have no idea how many tests I'm going to run.
+ *
+ * In some situations you may not know how many tests you will be running, or
+ * you are developing your test program, and do not want to update the
+ * plan_tests() call every time you make a change.  For those situations use
+ * plan_no_plan() instead of plan_tests().  It indicates to the test harness
+ * that an indeterminate number of tests will be run.
+ *
+ * Remember, if you fail to plan, you plan to fail.
+ *
+ * Example:
+ *     plan_no_plan();
+ *     while (random() % 2)
+ *             ok1(some_test());
+ *     exit(exit_status());
+ */
+int plan_no_plan(void);
+
+/**
+ * plan_skip_all - Indicate that you will skip all tests.
+ * @reason: the string indicating why you can't run any tests.
+ *
+ * If your test program detects at run time that some required functionality
+ * is missing (for example, it relies on a database connection which is not
+ * present, or a particular configuration option that has not been included
+ * in the running kernel) use plan_skip_all() instead of plan_tests().
+ *
+ * Example:
+ *     if (!have_some_feature) {
+ *             plan_skip_all("Need some_feature support");
+ *             exit(exit_status());
+ *     }
+ *     plan_tests(13);
+ */
+int plan_skip_all(const char *reason);
+
+#endif /* C99 or gcc */
index fa915d87a81a8082e670179fcc11f54c51db8c7a..4674d8984fc706cecab75cc5b3d032e25f4531ed 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
 
 #include "tap/tap.h"
-#include "common/assert.h"
-
 #include "argpar/argpar.h"
 
 /*
@@ -48,8 +47,8 @@ void test_succeed(const char *cmdline,
        gchar **argv = g_strsplit(cmdline, " ", 0);
        unsigned int i;
 
-       BT_ASSERT(argv);
-       BT_ASSERT(res_str);
+       assert(argv);
+       assert(res_str);
        parse_ret = bt_argpar_parse(g_strv_length(argv),
                (const char * const *) argv, descrs, false);
        ok(parse_ret.items,
@@ -70,8 +69,8 @@ void test_succeed(const char *cmdline,
                goto end;
        }
 
-       for (i = 0; i < parse_ret.items->len; i++) {
-               const struct bt_argpar_item *arg = parse_ret.items->pdata[i];
+       for (i = 0; i < parse_ret.items->n_items; i++) {
+               const struct bt_argpar_item *arg = parse_ret.items->items[i];
 
                switch (arg->type) {
                case BT_ARGPAR_ITEM_TYPE_OPT:
@@ -518,12 +517,12 @@ void test_fail(const char *cmdline, const char *expected_error,
                goto end;
        }
 
-       ok(strcmp(expected_error, parse_ret.error->str) == 0,
+       ok(strcmp(expected_error, parse_ret.error) == 0,
                "bt_argpar_parse() writes the expected error string "
                "for command line `%s`", cmdline);
-       if (strcmp(expected_error, parse_ret.error->str) != 0) {
+       if (strcmp(expected_error, parse_ret.error) != 0) {
                diag("Expected: `%s`", expected_error);
-               diag("Got:      `%s`", parse_ret.error->str);
+               diag("Got:      `%s`", parse_ret.error);
        }
 
 end:
This page took 0.064204 seconds and 4 git commands to generate.