aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael G. Martins <rafael@rafaelmartins.eng.br>2016-04-27 01:38:10 +0200
committerRafael G. Martins <rafael@rafaelmartins.eng.br>2016-04-27 02:19:37 +0200
commit74b2ee22a6b60d53b1241e0c52284b288e561599 (patch)
tree8d1b197f657af22912f8e6d611c6c9cccd9890d9
parent6153580a13e7e7c48e38fa446572c8adcae08084 (diff)
downloadblogc-74b2ee22a6b60d53b1241e0c52284b288e561599.tar.gz
blogc-74b2ee22a6b60d53b1241e0c52284b288e561599.tar.bz2
blogc-74b2ee22a6b60d53b1241e0c52284b288e561599.zip
moved squareball back to blogc source tree.
i don't have enough time to maintain a separated library at this point, and worry about soname bump, library stability, etc. all the code is trivial enough to be copied around, and all the utils are implemented as single file now, to make it easier to copy.
-rw-r--r--.gitmodules3
-rw-r--r--Makefile.am43
-rw-r--r--configure.ac24
m---------squareball0
-rw-r--r--src/utils.c612
-rw-r--r--src/utils.h102
-rw-r--r--tests/check_source_parser.c2
-rw-r--r--tests/check_utils.c959
8 files changed, 1694 insertions, 51 deletions
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 061fb99..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "squareball"]
- path = squareball
- url = https://github.com/rafaelmartins/squareball.git
diff --git a/Makefile.am b/Makefile.am
index a0559ea..8237508 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,16 +1,11 @@
## Autotools settings
-if INTERNAL_SQUAREBALL
-SUBDIRS = squareball
-endif
-
ACLOCAL_AMFLAGS = -I m4
AM_DISTCHECK_CONFIGURE_FLAGS = \
--enable-tests \
--enable-ronn \
--disable-valgrind \
- --with-squareball=internal \
$(NULL)
@@ -43,6 +38,7 @@ noinst_HEADERS = \
src/renderer.h \
src/source-parser.h \
src/template-parser.h \
+ src/utils.h \
$(NULL)
noinst_LTLIBRARIES = \
@@ -69,17 +65,16 @@ libblogc_la_SOURCES = \
src/renderer.c \
src/source-parser.c \
src/template-parser.c \
+ src/utils.c \
$(NULL)
libblogc_la_CFLAGS = \
$(AM_CFLAGS) \
-I$(top_srcdir)/src \
- $(SQUAREBALL_CFLAGS) \
$(NULL)
libblogc_la_LIBADD = \
$(LIBM) \
- $(SQUAREBALL_LIBS) \
$(NULL)
@@ -90,12 +85,10 @@ blogc_SOURCES = \
blogc_CFLAGS = \
$(AM_CFLAGS) \
-I$(top_srcdir)/src \
- $(SQUAREBALL_CFLAGS) \
$(NULL)
blogc_LDADD = \
libblogc.la \
- $(SQUAREBALL_LIBS) \
$(NULL)
@@ -170,6 +163,7 @@ check_PROGRAMS += \
tests/check_renderer \
tests/check_source_parser \
tests/check_template_parser \
+ tests/check_utils \
$(NULL)
tests_check_error_SOURCES = \
@@ -177,7 +171,6 @@ tests_check_error_SOURCES = \
$(NULL)
tests_check_error_CFLAGS = \
- $(SQUAREBALL_CFLAGS) \
$(CMOCKA_CFLAGS) \
$(NULL)
@@ -186,7 +179,6 @@ tests_check_error_LDFLAGS = \
$(NULL)
tests_check_error_LDADD = \
- $(SQUAREBALL_LIBS) \
$(CMOCKA_LIBS) \
libblogc.la \
$(NULL)
@@ -196,7 +188,6 @@ tests_check_loader_SOURCES = \
$(NULL)
tests_check_loader_CFLAGS = \
- $(SQUAREBALL_CFLAGS) \
$(CMOCKA_CFLAGS) \
$(NULL)
@@ -207,7 +198,6 @@ tests_check_loader_LDFLAGS = \
$(NULL)
tests_check_loader_LDADD = \
- $(SQUAREBALL_LIBS) \
$(CMOCKA_LIBS) \
libblogc.la \
$(NULL)
@@ -217,7 +207,6 @@ tests_check_content_parser_SOURCES = \
$(NULL)
tests_check_content_parser_CFLAGS = \
- $(SQUAREBALL_CFLAGS) \
$(CMOCKA_CFLAGS) \
$(NULL)
@@ -226,7 +215,6 @@ tests_check_content_parser_LDFLAGS = \
$(NULL)
tests_check_content_parser_LDADD = \
- $(SQUAREBALL_LIBS) \
$(CMOCKA_LIBS) \
libblogc.la \
$(NULL)
@@ -236,7 +224,6 @@ tests_check_datetime_parser_SOURCES = \
$(NULL)
tests_check_datetime_parser_CFLAGS = \
- $(SQUAREBALL_CFLAGS) \
$(CMOCKA_CFLAGS) \
$(NULL)
@@ -245,7 +232,6 @@ tests_check_datetime_parser_LDFLAGS = \
$(NULL)
tests_check_datetime_parser_LDADD = \
- $(SQUAREBALL_LIBS) \
$(CMOCKA_LIBS) \
libblogc.la \
$(NULL)
@@ -255,7 +241,6 @@ tests_check_renderer_SOURCES = \
$(NULL)
tests_check_renderer_CFLAGS = \
- $(SQUAREBALL_CFLAGS) \
$(CMOCKA_CFLAGS) \
$(NULL)
@@ -264,7 +249,6 @@ tests_check_renderer_LDFLAGS = \
$(NULL)
tests_check_renderer_LDADD = \
- $(SQUAREBALL_LIBS) \
$(CMOCKA_LIBS) \
libblogc.la \
$(NULL)
@@ -274,7 +258,6 @@ tests_check_source_parser_SOURCES = \
$(NULL)
tests_check_source_parser_CFLAGS = \
- $(SQUAREBALL_CFLAGS) \
$(CMOCKA_CFLAGS) \
$(NULL)
@@ -283,7 +266,6 @@ tests_check_source_parser_LDFLAGS = \
$(NULL)
tests_check_source_parser_LDADD = \
- $(SQUAREBALL_LIBS) \
$(CMOCKA_LIBS) \
libblogc.la \
$(NULL)
@@ -293,7 +275,6 @@ tests_check_template_parser_SOURCES = \
$(NULL)
tests_check_template_parser_CFLAGS = \
- $(SQUAREBALL_CFLAGS) \
$(CMOCKA_CFLAGS) \
$(NULL)
@@ -302,7 +283,23 @@ tests_check_template_parser_LDFLAGS = \
$(NULL)
tests_check_template_parser_LDADD = \
- $(SQUAREBALL_LIBS) \
+ $(CMOCKA_LIBS) \
+ libblogc.la \
+ $(NULL)
+
+tests_check_utils_SOURCES = \
+ tests/check_utils.c \
+ $(NULL)
+
+tests_check_utils_CFLAGS = \
+ $(CMOCKA_CFLAGS) \
+ $(NULL)
+
+tests_check_utils_LDFLAGS = \
+ -no-install \
+ $(NULL)
+
+tests_check_utils_LDADD = \
$(CMOCKA_LIBS) \
libblogc.la \
$(NULL)
diff --git a/configure.ac b/configure.ac
index 0bc1e19..1e7366e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -123,29 +123,6 @@ AC_CHECK_HEADERS([sys/types.h sys/stat.h time.h])
LT_LIB_M
-AC_ARG_WITH([squareball], [AS_HELP_STRING([--with-squareball=@<:@internal/system@:>@],
- [whether to use library squareball from system [default=internal]])])
-AS_IF([test "x$with_squareball" = "xsystem"], [
- SQUAREBALL="system"
- PKG_CHECK_MODULES([SQUAREBALL], [squareball >= 0.2.0], , [
- AC_MSG_ERROR([library squareball requested from system but not found or not new enough])
- ])
-], [
- SQUAREBALL="internal"
- SQUAREBALL_CFLAGS='-I$(top_srcdir)/squareball/src'
- SQUAREBALL_LIBS='$(top_builddir)/squareball/libsquareball.la'
- AC_SUBST(SQUAREBALL_LIBS)
- AC_SUBST(SQUAREBALL_CFLAGS)
- ac_configure_args_pre="$ac_configure_args"
- ac_configure_args_post="$ac_configure_args --enable-bundleme"
- ac_configure_args="$ac_configure_args_post"
- AC_CONFIG_COMMANDS_PRE([ac_configure_args="$ac_configure_args_pre"])
- AC_CONFIG_COMMANDS_POST([ac_configure_args="$ac_configure_args_post"])
- AC_CONFIG_SUBDIRS([squareball])
- ac_configure_args="$ac_configure_args_pre"
-])
-AM_CONDITIONAL(INTERNAL_SQUAREBALL, [test "x$with_squareball" != "xsystem"])
-
AC_CONFIG_FILES([
Makefile
blogc.spec
@@ -164,7 +141,6 @@ AS_ECHO("
cflags: ${CFLAGS}
ldflags: ${LDFLAGS}
- squareball: ${SQUAREBALL}
tests: ${TESTS}
ronn: ${RONN}
diff --git a/squareball b/squareball
deleted file mode 160000
-Subproject e2639b044e450ed54cf6e768ed2eb8b88e5c055
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..855b503
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,612 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#define SB_STRING_CHUNK_SIZE 128
+
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "utils.h"
+
+
+void*
+sb_malloc(size_t size)
+{
+ // simple things simple!
+ void *rv = malloc(size);
+ if (rv == NULL) {
+ fprintf(stderr, "fatal: Failed to allocate memory!\n");
+ abort();
+ }
+ return rv;
+}
+
+
+void*
+sb_realloc(void *ptr, size_t size)
+{
+ // simple things even simpler :P
+ void *rv = realloc(ptr, size);
+ if (rv == NULL && size != 0) {
+ fprintf(stderr, "fatal: Failed to reallocate memory!\n");
+ free(ptr);
+ abort();
+ }
+ return rv;
+}
+
+
+sb_slist_t*
+sb_slist_append(sb_slist_t *l, void *data)
+{
+ sb_slist_t *node = sb_malloc(sizeof(sb_slist_t));
+ node->data = data;
+ node->next = NULL;
+ if (l == NULL) {
+ l = node;
+ }
+ else {
+ sb_slist_t *tmp;
+ for (tmp = l; tmp->next != NULL; tmp = tmp->next);
+ tmp->next = node;
+ }
+ return l;
+}
+
+
+sb_slist_t*
+sb_slist_prepend(sb_slist_t *l, void *data)
+{
+ sb_slist_t *node = sb_malloc(sizeof(sb_slist_t));
+ node->data = data;
+ node->next = l;
+ l = node;
+ return l;
+}
+
+
+void
+sb_slist_free_full(sb_slist_t *l, sb_free_func_t free_func)
+{
+ while (l != NULL) {
+ sb_slist_t *tmp = l->next;
+ if ((free_func != NULL) && (l->data != NULL))
+ free_func(l->data);
+ free(l);
+ l = tmp;
+ }
+}
+
+
+void
+sb_slist_free(sb_slist_t *l)
+{
+ sb_slist_free_full(l, NULL);
+}
+
+
+size_t
+sb_slist_length(sb_slist_t *l)
+{
+ if (l == NULL)
+ return 0;
+ size_t i;
+ sb_slist_t *tmp;
+ for (tmp = l, i = 0; tmp != NULL; tmp = tmp->next, i++);
+ return i;
+}
+
+
+char*
+sb_strdup(const char *s)
+{
+ if (s == NULL)
+ return NULL;
+ size_t l = strlen(s);
+ char *tmp = malloc(l + 1);
+ if (tmp == NULL)
+ return NULL;
+ memcpy(tmp, s, l + 1);
+ return tmp;
+}
+
+
+char*
+sb_strndup(const char *s, size_t n)
+{
+ if (s == NULL)
+ return NULL;
+ size_t l = strnlen(s, n);
+ char *tmp = malloc(l + 1);
+ if (tmp == NULL)
+ return NULL;
+ memcpy(tmp, s, l);
+ tmp[l] = '\0';
+ return tmp;
+}
+
+
+char*
+sb_strdup_vprintf(const char *format, va_list ap)
+{
+ va_list ap2;
+ va_copy(ap2, ap);
+ int l = vsnprintf(NULL, 0, format, ap2);
+ va_end(ap2);
+ if (l < 0)
+ return NULL;
+ char *tmp = malloc(l + 1);
+ if (!tmp)
+ return NULL;
+ int l2 = vsnprintf(tmp, l + 1, format, ap);
+ if (l2 < 0) {
+ free(tmp);
+ return NULL;
+ }
+ return tmp;
+}
+
+
+char*
+sb_strdup_printf(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ char *tmp = sb_strdup_vprintf(format, ap);
+ va_end(ap);
+ return tmp;
+}
+
+
+bool
+sb_str_starts_with(const char *str, const char *prefix)
+{
+ int str_l = strlen(str);
+ int str_lp = strlen(prefix);
+ if (str_lp > str_l)
+ return false;
+ return strncmp(str, prefix, str_lp) == 0;
+}
+
+
+bool
+sb_str_ends_with(const char *str, const char *suffix)
+{
+ int str_l = strlen(str);
+ int str_ls = strlen(suffix);
+ if (str_ls > str_l)
+ return false;
+ return strcmp(str + str_l - str_ls, suffix) == 0;
+}
+
+
+char*
+sb_str_lstrip(char *str)
+{
+ if (str == NULL)
+ return NULL;
+ int i;
+ size_t str_len = strlen(str);
+ for (i = 0; i < str_len; i++) {
+ if ((str[i] != ' ') && (str[i] != '\t') && (str[i] != '\n') &&
+ (str[i] != '\r') && (str[i] != '\t') && (str[i] != '\f') &&
+ (str[i] != '\v'))
+ {
+ str += i;
+ break;
+ }
+ if (i == str_len - 1) {
+ str += str_len;
+ break;
+ }
+ }
+ return str;
+}
+
+
+char*
+sb_str_rstrip(char *str)
+{
+ if (str == NULL)
+ return NULL;
+ int i;
+ size_t str_len = strlen(str);
+ for (i = str_len - 1; i >= 0; i--) {
+ if ((str[i] != ' ') && (str[i] != '\t') && (str[i] != '\n') &&
+ (str[i] != '\r') && (str[i] != '\t') && (str[i] != '\f') &&
+ (str[i] != '\v'))
+ {
+ str[i + 1] = '\0';
+ break;
+ }
+ if (i == 0) {
+ str[0] = '\0';
+ break;
+ }
+ }
+ return str;
+}
+
+
+char*
+sb_str_strip(char *str)
+{
+ return sb_str_lstrip(sb_str_rstrip(str));
+}
+
+
+char**
+sb_str_split(const char *str, char c, unsigned int max_pieces)
+{
+ if (str == NULL)
+ return NULL;
+ char **rv = sb_malloc(sizeof(char*));
+ unsigned int i, start = 0, count = 0;
+ for (i = 0; i < strlen(str) + 1; i++) {
+ if (str[0] == '\0')
+ break;
+ if ((str[i] == c && (!max_pieces || count + 1 < max_pieces)) || str[i] == '\0') {
+ rv = sb_realloc(rv, (count + 1) * sizeof(char*));
+ rv[count] = sb_malloc(i - start + 1);
+ memcpy(rv[count], str + start, i - start);
+ rv[count++][i - start] = '\0';
+ start = i + 1;
+ }
+ }
+ rv = sb_realloc(rv, (count + 1) * sizeof(char*));
+ rv[count] = NULL;
+ return rv;
+}
+
+
+char*
+sb_str_replace(const char *str, const char search, const char *replace)
+{
+ char **pieces = sb_str_split(str, search, 0);
+ if (pieces == NULL)
+ return NULL;
+ char* rv = sb_strv_join(pieces, replace);
+ sb_strv_free(pieces);
+ if (rv == NULL)
+ return sb_strdup(str);
+ return rv;
+}
+
+
+void
+sb_strv_free(char **strv)
+{
+ if (strv == NULL)
+ return;
+ for (size_t i = 0; strv[i] != NULL; i++)
+ free(strv[i]);
+ free(strv);
+}
+
+
+char*
+sb_strv_join(char **strv, const char *separator)
+{
+ if (strv == NULL || separator == NULL)
+ return NULL;
+ sb_string_t *str = sb_string_new();
+ for (size_t i = 0; strv[i] != NULL; i++) {
+ str = sb_string_append(str, strv[i]);
+ if (strv[i + 1] != NULL)
+ str = sb_string_append(str, separator);
+ }
+ return sb_string_free(str, false);
+}
+
+
+size_t
+sb_strv_length(char **strv)
+{
+ if (strv == NULL)
+ return 0;
+ size_t i;
+ for (i = 0; strv[i] != NULL; i++);
+ return i;
+}
+
+
+sb_string_t*
+sb_string_new(void)
+{
+ sb_string_t* rv = sb_malloc(sizeof(sb_string_t));
+ rv->str = NULL;
+ rv->len = 0;
+ rv->allocated_len = 0;
+
+ // initialize with empty string
+ rv = sb_string_append(rv, "");
+
+ return rv;
+}
+
+
+char*
+sb_string_free(sb_string_t *str, bool free_str)
+{
+ if (str == NULL)
+ return NULL;
+ char *rv = NULL;
+ if (free_str)
+ free(str->str);
+ else
+ rv = str->str;
+ free(str);
+ return rv;
+}
+
+
+sb_string_t*
+sb_string_dup(sb_string_t *str)
+{
+ if (str == NULL)
+ return NULL;
+ sb_string_t* new = sb_string_new();
+ return sb_string_append_len(new, str->str, str->len);
+}
+
+
+sb_string_t*
+sb_string_append_len(sb_string_t *str, const char *suffix, size_t len)
+{
+ if (str == NULL)
+ return NULL;
+ if (suffix == NULL)
+ return str;
+ size_t old_len = str->len;
+ str->len += len;
+ if (str->len + 1 > str->allocated_len) {
+ str->allocated_len = (((str->len + 1) / SB_STRING_CHUNK_SIZE) + 1) * SB_STRING_CHUNK_SIZE;
+ str->str = sb_realloc(str->str, str->allocated_len);
+ }
+ memcpy(str->str + old_len, suffix, len);
+ str->str[str->len] = '\0';
+ return str;
+}
+
+
+sb_string_t*
+sb_string_append(sb_string_t *str, const char *suffix)
+{
+ if (str == NULL)
+ return NULL;
+ const char *my_suffix = suffix == NULL ? "" : suffix;
+ return sb_string_append_len(str, my_suffix, strlen(my_suffix));
+}
+
+
+sb_string_t*
+sb_string_append_c(sb_string_t *str, char c)
+{
+ if (str == NULL)
+ return NULL;
+ size_t old_len = str->len;
+ str->len += 1;
+ if (str->len + 1 > str->allocated_len) {
+ str->allocated_len = (((str->len + 1) / SB_STRING_CHUNK_SIZE) + 1) * SB_STRING_CHUNK_SIZE;
+ str->str = sb_realloc(str->str, str->allocated_len);
+ }
+ str->str[old_len] = c;
+ str->str[str->len] = '\0';
+ return str;
+}
+
+
+sb_string_t*
+sb_string_append_printf(sb_string_t *str, const char *format, ...)
+{
+ if (str == NULL)
+ return NULL;
+ va_list ap;
+ va_start(ap, format);
+ char *tmp = sb_strdup_vprintf(format, ap);
+ va_end(ap);
+ str = sb_string_append(str, tmp);
+ free(tmp);
+ return str;
+}
+
+
+sb_trie_t*
+sb_trie_new(sb_free_func_t free_func)
+{
+ sb_trie_t *trie = sb_malloc(sizeof(sb_trie_t));
+ trie->root = NULL;
+ trie->free_func = free_func;
+ return trie;
+}
+
+
+static void
+sb_trie_free_node(sb_trie_t *trie, sb_trie_node_t *node)
+{
+ if (trie == NULL || node == NULL)
+ return;
+ if (node->data != NULL && trie->free_func != NULL)
+ trie->free_func(node->data);
+ sb_trie_free_node(trie, node->next);
+ sb_trie_free_node(trie, node->child);
+ free(node);
+}
+
+
+void
+sb_trie_free(sb_trie_t *trie)
+{
+ if (trie == NULL)
+ return;
+ sb_trie_free_node(trie, trie->root);
+ free(trie);
+}
+
+
+void
+sb_trie_insert(sb_trie_t *trie, const char *key, void *data)
+{
+ if (trie == NULL || key == NULL || data == NULL)
+ return;
+
+ sb_trie_node_t *parent = NULL;
+ sb_trie_node_t *previous;
+ sb_trie_node_t *current;
+ sb_trie_node_t *tmp;
+
+ while (1) {
+
+ if (trie->root == NULL || (parent != NULL && parent->child == NULL)) {
+ current = sb_malloc(sizeof(sb_trie_node_t));
+ current->key = *key;
+ current->data = NULL;
+ current->next = NULL;
+ current->child = NULL;
+ if (trie->root == NULL)
+ trie->root = current;
+ else
+ parent->child = current;
+ parent = current;
+ goto clean;
+ }
+
+ tmp = parent == NULL ? trie->root : parent->child;
+ previous = NULL;
+
+ while (tmp != NULL && tmp->key != *key) {
+ previous = tmp;
+ tmp = tmp->next;
+ }
+
+ parent = tmp;
+
+ if (previous == NULL || parent != NULL)
+ goto clean;
+
+ current = sb_malloc(sizeof(sb_trie_node_t));
+ current->key = *key;
+ current->data = NULL;
+ current->next = NULL;
+ current->child = NULL;
+ previous->next = current;
+ parent = current;
+
+clean:
+ if (*key == '\0') {
+ if (parent->data != NULL && trie->free_func != NULL)
+ trie->free_func(parent->data);
+ parent->data = data;
+ break;
+ }
+ key++;
+ }
+}
+
+
+void*
+sb_trie_lookup(sb_trie_t *trie, const char *key)
+{
+ if (trie == NULL || trie->root == NULL || key == NULL)
+ return NULL;
+
+ sb_trie_node_t *parent = trie->root;
+ sb_trie_node_t *tmp;
+ while (1) {
+ for (tmp = parent; tmp != NULL; tmp = tmp->next) {
+
+ if (tmp->key == *key) {
+ if (tmp->key == '\0')
+ return tmp->data;
+ parent = tmp->child;
+ break;
+ }
+ }
+ if (tmp == NULL)
+ return NULL;
+
+ if (*key == '\0')
+ break;
+ key++;
+ }
+ return NULL;
+}
+
+
+static void
+sb_trie_size_node(sb_trie_node_t *node, size_t *count)
+{
+ if (node == NULL || count == NULL)
+ return;
+
+ if (node->key == '\0')
+ (*count)++;
+
+ sb_trie_size_node(node->next, count);
+ sb_trie_size_node(node->child, count);
+}
+
+
+size_t
+sb_trie_size(sb_trie_t *trie)
+{
+ if (trie == NULL)
+ return 0;
+
+ size_t count = 0;
+ sb_trie_size_node(trie->root, &count);
+ return count;
+}
+
+
+static void
+sb_trie_foreach_node(sb_trie_node_t *node, sb_string_t *str,
+ sb_trie_foreach_func_t func, void *user_data)
+{
+ if (node == NULL || str == NULL || func == NULL)
+ return;
+
+ if (node->key == '\0') {
+ char *tmp = sb_string_free(str, false);
+ func(tmp, node->data, user_data);
+ free(tmp);
+ }
+
+ if (node->child != NULL) {
+ sb_string_t *child = sb_string_dup(str);
+ child = sb_string_append_c(child, node->key);
+ sb_trie_foreach_node(node->child, child, func, user_data);
+ }
+
+ if (node->next != NULL)
+ sb_trie_foreach_node(node->next, str, func, user_data);
+
+ if (node->child != NULL && node->next == NULL)
+ sb_string_free(str, true);
+}
+
+
+void
+sb_trie_foreach(sb_trie_t *trie, sb_trie_foreach_func_t func,
+ void *user_data)
+{
+ if (trie == NULL || trie->root == NULL || func == NULL)
+ return;
+
+ sb_string_t *str = sb_string_new();
+ sb_trie_foreach_node(trie->root, str, func, user_data);
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..411295a
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,102 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+
+// memory
+
+typedef void (*sb_free_func_t) (void *ptr);
+
+void* sb_malloc(size_t size);
+void* sb_realloc(void *ptr, size_t size);
+
+
+// slist
+
+typedef struct _sb_slist_t {
+ struct _sb_slist_t *next;
+ void *data;
+} sb_slist_t;
+
+sb_slist_t* sb_slist_append(sb_slist_t *l, void *data);
+sb_slist_t* sb_slist_prepend(sb_slist_t *l, void *data);
+void sb_slist_free(sb_slist_t *l);
+void sb_slist_free_full(sb_slist_t *l, sb_free_func_t free_func);
+size_t sb_slist_length(sb_slist_t *l);
+
+
+// strfuncs
+
+char* sb_strdup(const char *s);
+char* sb_strndup(const char *s, size_t n);
+char* sb_strdup_vprintf(const char *format, va_list ap);
+char* sb_strdup_printf(const char *format, ...);
+bool sb_str_starts_with(const char *str, const char *prefix);
+bool sb_str_ends_with(const char *str, const char *suffix);
+char* sb_str_lstrip(char *str);
+char* sb_str_rstrip(char *str);
+char* sb_str_strip(char *str);
+char** sb_str_split(const char *str, char c, unsigned int max_pieces);
+char* sb_str_replace(const char *str, const char search, const char *replace);
+void sb_strv_free(char **strv);
+char* sb_strv_join(char **strv, const char *separator);
+size_t sb_strv_length(char **strv);
+
+
+// string
+
+typedef struct {
+ char *str;
+ size_t len;
+ size_t allocated_len;
+} sb_string_t;
+
+sb_string_t* sb_string_new(void);
+char* sb_string_free(sb_string_t *str, bool free_str);
+sb_string_t* sb_string_dup(sb_string_t *str);
+sb_string_t* sb_string_append_len(sb_string_t *str, const char *suffix, size_t len);
+sb_string_t* sb_string_append(sb_string_t *str, const char *suffix);
+sb_string_t* sb_string_append_c(sb_string_t *str, char c);
+sb_string_t* sb_string_append_printf(sb_string_t *str, const char *format, ...);
+
+
+// trie
+
+typedef struct _sb_trie_node_t {
+ char key;
+ void *data;
+ struct _sb_trie_node_t *next, *child;
+} sb_trie_node_t;
+
+struct _sb_trie_t {
+ sb_trie_node_t *root;
+ sb_free_func_t free_func;
+};
+
+typedef struct _sb_trie_t sb_trie_t;
+
+typedef void (*sb_trie_foreach_func_t)(const char *key, void *data,
+ void *user_data);
+
+sb_trie_t* sb_trie_new(sb_free_func_t free_func);
+void sb_trie_free(sb_trie_t *trie);
+void sb_trie_insert(sb_trie_t *trie, const char *key, void *data);
+void* sb_trie_lookup(sb_trie_t *trie, const char *key);
+size_t sb_trie_size(sb_trie_t *trie);
+void sb_trie_foreach(sb_trie_t *trie, sb_trie_foreach_func_t func,
+ void *user_data);
+
+#endif /* _UTILS_H */
diff --git a/tests/check_source_parser.c b/tests/check_source_parser.c
index 0f891dd..477399c 100644
--- a/tests/check_source_parser.c
+++ b/tests/check_source_parser.c
@@ -173,7 +173,7 @@ test_source_parse_with_description(void **state)
"# This is a test\n"
"\n"
"bola\n";
- sb_error_t *err = NULL;
+ blogc_error_t *err = NULL;
sb_trie_t *source = blogc_source_parse(a, strlen(a), &err);
assert_null(err);
assert_non_null(source);
diff --git a/tests/check_utils.c b/tests/check_utils.c
new file mode 100644
index 0000000..6a6ceca
--- /dev/null
+++ b/tests/check_utils.c
@@ -0,0 +1,959 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdlib.h>
+
+#include "../src/utils.h"
+
+#define SB_STRING_CHUNK_SIZE 128
+
+
+static void
+test_slist_append(void **state)
+{
+ sb_slist_t *l = NULL;
+ l = sb_slist_append(l, (void*) sb_strdup("bola"));
+ assert_non_null(l);
+ assert_string_equal(l->data, "bola");
+ assert_null(l->next);
+ l = sb_slist_append(l, (void*) sb_strdup("guda"));
+ assert_non_null(l);
+ assert_string_equal(l->data, "bola");
+ assert_non_null(l->next);
+ assert_string_equal(l->next->data, "guda");
+ assert_null(l->next->next);
+ sb_slist_free_full(l, free);
+}
+
+
+static void
+test_slist_prepend(void **state)
+{
+ sb_slist_t *l = NULL;
+ l = sb_slist_prepend(l, (void*) sb_strdup("bola"));
+ assert_non_null(l);
+ assert_string_equal(l->data, "bola");
+ assert_null(l->next);
+ l = sb_slist_prepend(l, (void*) sb_strdup("guda"));
+ assert_non_null(l);
+ assert_string_equal(l->data, "guda");
+ assert_non_null(l->next);
+ assert_string_equal(l->next->data, "bola");
+ assert_null(l->next->next);
+ sb_slist_free_full(l, free);
+}
+
+
+static void
+test_slist_free(void **state)
+{
+ sb_slist_t *l = NULL;
+ char *t1 = sb_strdup("bola");
+ char *t2 = sb_strdup("guda");
+ char *t3 = sb_strdup("chunda");
+ l = sb_slist_append(l, (void*) t1);
+ l = sb_slist_append(l, (void*) t2);
+ l = sb_slist_append(l, (void*) t3);
+ sb_slist_free(l);
+ assert_string_equal(t1, "bola");
+ assert_string_equal(t2, "guda");
+ assert_string_equal(t3, "chunda");
+ free(t1);
+ free(t2);
+ free(t3);
+}
+
+
+static void
+test_slist_length(void **state)
+{
+ sb_slist_t *l = NULL;
+ l = sb_slist_append(l, (void*) sb_strdup("bola"));
+ l = sb_slist_append(l, (void*) sb_strdup("guda"));
+ l = sb_slist_append(l, (void*) sb_strdup("chunda"));
+ assert_int_equal(sb_slist_length(l), 3);
+ sb_slist_free_full(l, free);
+ assert_int_equal(sb_slist_length(NULL), 0);
+}
+
+
+static void
+test_strdup(void **state)
+{
+ char *str = sb_strdup("bola");
+ assert_string_equal(str, "bola");
+ free(str);
+ str = sb_strdup(NULL);
+ assert_null(str);
+}
+
+
+static void
+test_strndup(void **state)
+{
+ char *str = sb_strndup("bolaguda", 4);
+ assert_string_equal(str, "bola");
+ free(str);
+ str = sb_strndup("bolaguda", 30);
+ assert_string_equal(str, "bolaguda");
+ free(str);
+ str = sb_strndup("bolaguda", 8);
+ assert_string_equal(str, "bolaguda");
+ free(str);
+ str = sb_strdup(NULL);
+ assert_null(str);
+}
+
+
+static void
+test_strdup_printf(void **state)
+{
+ char *str = sb_strdup_printf("bola");
+ assert_string_equal(str, "bola");
+ free(str);
+ str = sb_strdup_printf("bola, %s", "guda");
+ assert_string_equal(str, "bola, guda");
+ free(str);
+}
+
+
+static void
+test_str_starts_with(void **state)
+{
+ assert_true(sb_str_starts_with("bolaguda", "bola"));
+ assert_true(sb_str_starts_with("bola", "bola"));
+ assert_false(sb_str_starts_with("gudabola", "bola"));
+ assert_false(sb_str_starts_with("guda", "bola"));
+ assert_false(sb_str_starts_with("bola", "bolaguda"));
+}
+
+
+static void
+test_str_ends_with(void **state)
+{
+ assert_true(sb_str_ends_with("bolaguda", "guda"));
+ assert_true(sb_str_ends_with("bola", "bola"));
+ assert_false(sb_str_ends_with("gudabola", "guda"));
+ assert_false(sb_str_ends_with("guda", "bola"));
+ assert_false(sb_str_ends_with("bola", "gudabola"));
+}
+
+
+static void
+test_str_lstrip(void **state)
+{
+ char *str = sb_strdup(" \tbola\n \t");
+ assert_string_equal(sb_str_lstrip(str), "bola\n \t");
+ free(str);
+ str = sb_strdup("guda");
+ assert_string_equal(sb_str_lstrip(str), "guda");
+ free(str);
+ str = sb_strdup("\n");
+ assert_string_equal(sb_str_lstrip(str), "");
+ free(str);
+ str = sb_strdup("\t \n");
+ assert_string_equal(sb_str_lstrip(str), "");
+ free(str);
+ str = sb_strdup("");
+ assert_string_equal(sb_str_lstrip(str), "");
+ free(str);
+ assert_null(sb_str_lstrip(NULL));
+}
+
+
+static void
+test_str_rstrip(void **state)
+{
+ char *str = sb_strdup(" \tbola\n \t");
+ assert_string_equal(sb_str_rstrip(str), " \tbola");
+ free(str);
+ str = sb_strdup("guda");
+ assert_string_equal(sb_str_rstrip(str), "guda");
+ free(str);
+ str = sb_strdup("\n");
+ assert_string_equal(sb_str_rstrip(str), "");
+ free(str);
+ str = sb_strdup("\t \n");
+ assert_string_equal(sb_str_rstrip(str), "");
+ free(str);
+ str = sb_strdup("");
+ assert_string_equal(sb_str_rstrip(str), "");
+ free(str);
+ assert_null(sb_str_rstrip(NULL));
+}
+
+
+static void
+test_str_strip(void **state)
+{
+ char *str = sb_strdup(" \tbola\n \t");
+ assert_string_equal(sb_str_strip(str), "bola");
+ free(str);
+ str = sb_strdup("guda");
+ assert_string_equal(sb_str_strip(str), "guda");
+ free(str);
+ str = sb_strdup("\n");
+ assert_string_equal(sb_str_strip(str), "");
+ free(str);
+ str = sb_strdup("\t \n");
+ assert_string_equal(sb_str_strip(str), "");
+ free(str);
+ str = sb_strdup("");
+ assert_string_equal(sb_str_strip(str), "");
+ free(str);
+ assert_null(sb_str_strip(NULL));
+}
+
+
+static void
+test_str_split(void **state)
+{
+ char **strv = sb_str_split("bola:guda:chunda", ':', 0);
+ assert_string_equal(strv[0], "bola");
+ assert_string_equal(strv[1], "guda");
+ assert_string_equal(strv[2], "chunda");
+ assert_null(strv[3]);
+ sb_strv_free(strv);
+ strv = sb_str_split("bola:guda:chunda", ':', 2);
+ assert_string_equal(strv[0], "bola");
+ assert_string_equal(strv[1], "guda:chunda");
+ assert_null(strv[2]);
+ sb_strv_free(strv);
+ strv = sb_str_split("bola:guda:chunda", ':', 1);
+ assert_string_equal(strv[0], "bola:guda:chunda");
+ assert_null(strv[1]);
+ sb_strv_free(strv);
+ strv = sb_str_split("", ':', 1);
+ assert_null(strv[0]);
+ sb_strv_free(strv);
+ assert_null(sb_str_split(NULL, ':', 0));
+}
+
+
+static void
+test_str_replace(void **state)
+{
+ char *str = sb_str_replace("bolao", 'o', "zaz");
+ assert_string_equal(str, "bzazlazaz");
+ free(str);
+ str = sb_str_replace("bolao", 'b', "zaz");
+ assert_string_equal(str, "zazolao");
+ free(str);
+ str = sb_str_replace("bolao", 'b', NULL);
+ assert_string_equal(str, "bolao");
+ free(str);
+ assert_null(sb_str_replace(NULL, 'b', "zaz"));
+}
+
+
+static void
+test_strv_join(void **state)
+{
+ char *pieces[] = {"guda","bola", "chunda", NULL};
+ char *str = sb_strv_join(pieces, ":");
+ assert_string_equal(str, "guda:bola:chunda");
+ free(str);
+ char *pieces2[] = {NULL};
+ str = sb_strv_join(pieces2, ":");
+ assert_string_equal(str, "");
+ free(str);
+ assert_null(sb_strv_join(pieces, NULL));
+ assert_null(sb_strv_join(NULL, ":"));
+ assert_null(sb_strv_join(NULL, NULL));
+}
+
+
+static void
+test_strv_length(void **state)
+{
+ char *pieces[] = {"guda","bola", "chunda", NULL};
+ assert_int_equal(sb_strv_length(pieces), 3);
+ char *pieces2[] = {NULL};
+ assert_int_equal(sb_strv_length(pieces2), 0);
+ assert_int_equal(sb_strv_length(NULL), 0);
+}
+
+
+static void
+test_string_new(void **state)
+{
+ sb_string_t *str = sb_string_new();
+ assert_non_null(str);
+ assert_string_equal(str->str, "");
+ assert_int_equal(str->len, 0);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+}
+
+
+static void
+test_string_free(void **state)
+{
+ sb_string_t *str = sb_string_new();
+ free(str->str);
+ str->str = sb_strdup("bola");
+ str->len = 4;
+ str->allocated_len = SB_STRING_CHUNK_SIZE;
+ char *tmp = sb_string_free(str, false);
+ assert_string_equal(tmp, "bola");
+ free(tmp);
+ assert_null(sb_string_free(NULL, false));
+}
+
+
+static void
+test_string_dup(void **state)
+{
+ sb_string_t *str = sb_string_new();
+ free(str->str);
+ str->str = sb_strdup("bola");
+ str->len = 4;
+ str->allocated_len = SB_STRING_CHUNK_SIZE;
+ sb_string_t *new = sb_string_dup(str);
+ assert_non_null(new);
+ assert_string_equal(new->str, "bola");
+ assert_int_equal(new->len, 4);
+ assert_int_equal(new->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(new, true));
+ assert_null(sb_string_free(str, true));
+ assert_null(sb_string_dup(NULL));
+}
+
+
+static void
+test_string_append_len(void **state)
+{
+ sb_string_t *str = sb_string_new();
+ str = sb_string_append_len(str, "guda", 4);
+ assert_non_null(str);
+ assert_string_equal(str->str, "guda");
+ assert_int_equal(str->len, 4);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+ str = sb_string_new();
+ str = sb_string_append_len(str, "guda", 4);
+ str = sb_string_append_len(str, "bola", 4);
+ assert_non_null(str);
+ assert_string_equal(str->str, "gudabola");
+ assert_int_equal(str->len, 8);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+ str = sb_string_new();
+ str = sb_string_append_len(str, "guda", 3);
+ str = sb_string_append_len(str, "bola", 4);
+ assert_non_null(str);
+ assert_string_equal(str->str, "gudbola");
+ assert_int_equal(str->len, 7);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+ str = sb_string_new();
+ str = sb_string_append_len(str, "guda", 4);
+ str = sb_string_append_len(str,
+ "cwlwmwxxmvjnwtidmjehzdeexbxjnjowruxjrqpgpfhmvwgqeacdjissntmbtsjidzkcw"
+ "nnqhxhneolbwqlctcxmrsutolrjikpavxombpfpjyaqltgvzrjidotalcuwrwxtaxjiwa"
+ "xfhfyzymtffusoqywaruxpybwggukltspqqmghzpqstvcvlqbkhquihzndnrvkaqvevaz"
+ "dxrewtgapkompnviiyielanoyowgqhssntyvcvqqtfjmkphywbkvzfyttaalttywhqcec"
+ "hgrwzaglzogwjvqncjzodaqsblcbpcdpxmrtctzginvtkckhqvdplgjvbzrnarcxjrsbc"
+ "sbfvpylgjznsuhxcxoqbpxowmsrgwimxjgyzwwmryqvstwzkglgeezelvpvkwefqdatnd"
+ "dxntikgoqlidfnmdhxzevqzlzubvyleeksdirmmttqthhkvfjggznpmarcamacpvwsrnr"
+ "ftzfeyasjpxoevyptpdnqokswiondusnuymqwaryrmdgscbnuilxtypuynckancsfnwtg"
+ "okxhegoifakimxbbafkeannglvsxprqzfekdinssqymtfexf", 600);
+ str = sb_string_append_len(str, NULL, 0);
+ str = sb_string_append_len(str,
+ "cwlwmwxxmvjnwtidmjehzdeexbxjnjowruxjrqpgpfhmvwgqeacdjissntmbtsjidzkcw"
+ "nnqhxhneolbwqlctcxmrsutolrjikpavxombpfpjyaqltgvzrjidotalcuwrwxtaxjiwa"
+ "xfhfyzymtffusoqywaruxpybwggukltspqqmghzpqstvcvlqbkhquihzndnrvkaqvevaz"
+ "dxrewtgapkompnviiyielanoyowgqhssntyvcvqqtfjmkphywbkvzfyttaalttywhqcec"
+ "hgrwzaglzogwjvqncjzodaqsblcbpcdpxmrtctzginvtkckhqvdplgjvbzrnarcxjrsbc"
+ "sbfvpylgjznsuhxcxoqbpxowmsrgwimxjgyzwwmryqvstwzkglgeezelvpvkwefqdatnd"
+ "dxntikgoqlidfnmdhxzevqzlzubvyleeksdirmmttqthhkvfjggznpmarcamacpvwsrnr"
+ "ftzfeyasjpxoevyptpdnqokswiondusnuymqwaryrmdgscbnuilxtypuynckancsfnwtg"
+ "okxhegoifakimxbbafkeannglvsxprqzfekdinssqymtfexf", 600);
+ assert_non_null(str);
+ assert_string_equal(str->str,
+ "gudacwlwmwxxmvjnwtidmjehzdeexbxjnjowruxjrqpgpfhmvwgqeacdjissntmbtsjid"
+ "zkcwnnqhxhneolbwqlctcxmrsutolrjikpavxombpfpjyaqltgvzrjidotalcuwrwxtax"
+ "jiwaxfhfyzymtffusoqywaruxpybwggukltspqqmghzpqstvcvlqbkhquihzndnrvkaqv"
+ "evazdxrewtgapkompnviiyielanoyowgqhssntyvcvqqtfjmkphywbkvzfyttaalttywh"
+ "qcechgrwzaglzogwjvqncjzodaqsblcbpcdpxmrtctzginvtkckhqvdplgjvbzrnarcxj"
+ "rsbcsbfvpylgjznsuhxcxoqbpxowmsrgwimxjgyzwwmryqvstwzkglgeezelvpvkwefqd"
+ "atnddxntikgoqlidfnmdhxzevqzlzubvyleeksdirmmttqthhkvfjggznpmarcamacpvw"
+ "srnrftzfeyasjpxoevyptpdnqokswiondusnuymqwaryrmdgscbnuilxtypuynckancsf"
+ "nwtgokxhegoifakimxbbafkeannglvsxprqzfekdinssqymtfexfcwlwmwxxmvjnwtidm"
+ "jehzdeexbxjnjowruxjrqpgpfhmvwgqeacdjissntmbtsjidzkcwnnqhxhneolbwqlctc"
+ "xmrsutolrjikpavxombpfpjyaqltgvzrjidotalcuwrwxtaxjiwaxfhfyzymtffusoqyw"
+ "aruxpybwggukltspqqmghzpqstvcvlqbkhquihzndnrvkaqvevazdxrewtgapkompnvii"
+ "yielanoyowgqhssntyvcvqqtfjmkphywbkvzfyttaalttywhqcechgrwzaglzogwjvqnc"
+ "jzodaqsblcbpcdpxmrtctzginvtkckhqvdplgjvbzrnarcxjrsbcsbfvpylgjznsuhxcx"
+ "oqbpxowmsrgwimxjgyzwwmryqvstwzkglgeezelvpvkwefqdatnddxntikgoqlidfnmdh"
+ "xzevqzlzubvyleeksdirmmttqthhkvfjggznpmarcamacpvwsrnrftzfeyasjpxoevypt"
+ "pdnqokswiondusnuymqwaryrmdgscbnuilxtypuynckancsfnwtgokxhegoifakimxbba"
+ "fkeannglvsxprqzfekdinssqymtfexf");
+ assert_int_equal(str->len, 1204);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE * 10);
+ assert_null(sb_string_free(str, true));
+ str = sb_string_new();
+ str = sb_string_append_len(str, NULL, 0);
+ assert_non_null(str);
+ assert_string_equal(str->str, "");
+ assert_int_equal(str->len, 0);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+ assert_null(sb_string_append_len(NULL, "foo", 3));
+}
+
+
+static void
+test_string_append(void **state)
+{
+ sb_string_t *str = sb_string_new();
+ str = sb_string_append(str, "guda");
+ assert_non_null(str);
+ assert_string_equal(str->str, "guda");
+ assert_int_equal(str->len, 4);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+ str = sb_string_new();
+ str = sb_string_append(str, "guda");
+ str = sb_string_append(str, "bola");
+ assert_non_null(str);
+ assert_string_equal(str->str, "gudabola");
+ assert_int_equal(str->len, 8);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+ str = sb_string_new();
+ str = sb_string_append(str, "guda");
+ str = sb_string_append(str,
+ "cwlwmwxxmvjnwtidmjehzdeexbxjnjowruxjrqpgpfhmvwgqeacdjissntmbtsjidzkcw"
+ "nnqhxhneolbwqlctcxmrsutolrjikpavxombpfpjyaqltgvzrjidotalcuwrwxtaxjiwa"
+ "xfhfyzymtffusoqywaruxpybwggukltspqqmghzpqstvcvlqbkhquihzndnrvkaqvevaz"
+ "dxrewtgapkompnviiyielanoyowgqhssntyvcvqqtfjmkphywbkvzfyttaalttywhqcec"
+ "hgrwzaglzogwjvqncjzodaqsblcbpcdpxmrtctzginvtkckhqvdplgjvbzrnarcxjrsbc"
+ "sbfvpylgjznsuhxcxoqbpxowmsrgwimxjgyzwwmryqvstwzkglgeezelvpvkwefqdatnd"
+ "dxntikgoqlidfnmdhxzevqzlzubvyleeksdirmmttqthhkvfjggznpmarcamacpvwsrnr"
+ "ftzfeyasjpxoevyptpdnqokswiondusnuymqwaryrmdgscbnuilxtypuynckancsfnwtg"
+ "okxhegoifakimxbbafkeannglvsxprqzfekdinssqymtfexf");
+ str = sb_string_append(str, NULL);
+ str = sb_string_append(str,
+ "cwlwmwxxmvjnwtidmjehzdeexbxjnjowruxjrqpgpfhmvwgqeacdjissntmbtsjidzkcw"
+ "nnqhxhneolbwqlctcxmrsutolrjikpavxombpfpjyaqltgvzrjidotalcuwrwxtaxjiwa"
+ "xfhfyzymtffusoqywaruxpybwggukltspqqmghzpqstvcvlqbkhquihzndnrvkaqvevaz"
+ "dxrewtgapkompnviiyielanoyowgqhssntyvcvqqtfjmkphywbkvzfyttaalttywhqcec"
+ "hgrwzaglzogwjvqncjzodaqsblcbpcdpxmrtctzginvtkckhqvdplgjvbzrnarcxjrsbc"
+ "sbfvpylgjznsuhxcxoqbpxowmsrgwimxjgyzwwmryqvstwzkglgeezelvpvkwefqdatnd"
+ "dxntikgoqlidfnmdhxzevqzlzubvyleeksdirmmttqthhkvfjggznpmarcamacpvwsrnr"
+ "ftzfeyasjpxoevyptpdnqokswiondusnuymqwaryrmdgscbnuilxtypuynckancsfnwtg"
+ "okxhegoifakimxbbafkeannglvsxprqzfekdinssqymtfexf");
+ assert_non_null(str);
+ assert_string_equal(str->str,
+ "gudacwlwmwxxmvjnwtidmjehzdeexbxjnjowruxjrqpgpfhmvwgqeacdjissntmbtsjid"
+ "zkcwnnqhxhneolbwqlctcxmrsutolrjikpavxombpfpjyaqltgvzrjidotalcuwrwxtax"
+ "jiwaxfhfyzymtffusoqywaruxpybwggukltspqqmghzpqstvcvlqbkhquihzndnrvkaqv"
+ "evazdxrewtgapkompnviiyielanoyowgqhssntyvcvqqtfjmkphywbkvzfyttaalttywh"
+ "qcechgrwzaglzogwjvqncjzodaqsblcbpcdpxmrtctzginvtkckhqvdplgjvbzrnarcxj"
+ "rsbcsbfvpylgjznsuhxcxoqbpxowmsrgwimxjgyzwwmryqvstwzkglgeezelvpvkwefqd"
+ "atnddxntikgoqlidfnmdhxzevqzlzubvyleeksdirmmttqthhkvfjggznpmarcamacpvw"
+ "srnrftzfeyasjpxoevyptpdnqokswiondusnuymqwaryrmdgscbnuilxtypuynckancsf"
+ "nwtgokxhegoifakimxbbafkeannglvsxprqzfekdinssqymtfexfcwlwmwxxmvjnwtidm"
+ "jehzdeexbxjnjowruxjrqpgpfhmvwgqeacdjissntmbtsjidzkcwnnqhxhneolbwqlctc"
+ "xmrsutolrjikpavxombpfpjyaqltgvzrjidotalcuwrwxtaxjiwaxfhfyzymtffusoqyw"
+ "aruxpybwggukltspqqmghzpqstvcvlqbkhquihzndnrvkaqvevazdxrewtgapkompnvii"
+ "yielanoyowgqhssntyvcvqqtfjmkphywbkvzfyttaalttywhqcechgrwzaglzogwjvqnc"
+ "jzodaqsblcbpcdpxmrtctzginvtkckhqvdplgjvbzrnarcxjrsbcsbfvpylgjznsuhxcx"
+ "oqbpxowmsrgwimxjgyzwwmryqvstwzkglgeezelvpvkwefqdatnddxntikgoqlidfnmdh"
+ "xzevqzlzubvyleeksdirmmttqthhkvfjggznpmarcamacpvwsrnrftzfeyasjpxoevypt"
+ "pdnqokswiondusnuymqwaryrmdgscbnuilxtypuynckancsfnwtgokxhegoifakimxbba"
+ "fkeannglvsxprqzfekdinssqymtfexf");
+ assert_int_equal(str->len, 1204);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE * 10);
+ assert_null(sb_string_free(str, true));
+ str = sb_string_new();
+ str = sb_string_append(str, NULL);
+ assert_non_null(str);
+ assert_string_equal(str->str, "");
+ assert_int_equal(str->len, 0);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+ assert_null(sb_string_append(NULL, "asd"));
+ assert_null(sb_string_append(NULL, NULL));
+}
+
+
+static void
+test_string_append_c(void **state)
+{
+ sb_string_t *str = sb_string_new();
+ str = sb_string_append_len(str, "guda", 4);
+ for (int i = 0; i < 600; i++)
+ str = sb_string_append_c(str, 'c');
+ assert_non_null(str);
+ assert_string_equal(str->str,
+ "gudaccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+ "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+ "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+ "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+ "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+ "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+ "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+ "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
+ "cccccccccccccccccccccccccccccccccccccccccccccccccccc");
+ assert_int_equal(str->len, 604);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE * 5);
+ assert_null(sb_string_free(str, true));
+ assert_null(sb_string_append_c(NULL, 0));
+}
+
+
+static void
+test_string_append_printf(void **state)
+{
+ sb_string_t *str = sb_string_new();
+ str = sb_string_append_printf(str, "guda: %s %d", "bola", 1);
+ assert_non_null(str);
+ assert_string_equal(str->str, "guda: bola 1");
+ assert_int_equal(str->len, 12);
+ assert_int_equal(str->allocated_len, SB_STRING_CHUNK_SIZE);
+ assert_null(sb_string_free(str, true));
+ assert_null(sb_string_append_printf(NULL, "asd"));
+}
+
+
+static void
+test_trie_new(void **state)
+{
+ sb_trie_t *trie = sb_trie_new(free);
+ assert_non_null(trie);
+ assert_null(trie->root);
+ assert_true(trie->free_func == free);
+ sb_trie_free(trie);
+}
+
+
+static void
+test_trie_insert(void **state)
+{
+ sb_trie_t *trie = sb_trie_new(free);
+
+ sb_trie_insert(trie, "bola", sb_strdup("guda"));
+ assert_true(trie->root->key == 'b');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'o');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'l');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == 'a');
+ assert_null(trie->root->child->child->child->data);
+ assert_true(trie->root->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->child->data, "guda");
+
+
+ sb_trie_insert(trie, "chu", sb_strdup("nda"));
+ assert_true(trie->root->key == 'b');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'o');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'l');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == 'a');
+ assert_null(trie->root->child->child->child->data);
+ assert_true(trie->root->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->child->data, "guda");
+
+ assert_true(trie->root->next->key == 'c');
+ assert_null(trie->root->next->data);
+ assert_true(trie->root->next->child->key == 'h');
+ assert_null(trie->root->next->child->data);
+ assert_true(trie->root->next->child->child->key == 'u');
+ assert_null(trie->root->next->child->child->data);
+ assert_true(trie->root->next->child->child->child->key == '\0');
+ assert_string_equal(trie->root->next->child->child->child->data, "nda");
+
+
+ sb_trie_insert(trie, "bote", sb_strdup("aba"));
+ assert_true(trie->root->key == 'b');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'o');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'l');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == 'a');
+ assert_null(trie->root->child->child->child->data);
+ assert_true(trie->root->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->child->data, "guda");
+
+ assert_true(trie->root->next->key == 'c');
+ assert_null(trie->root->next->data);
+ assert_true(trie->root->next->child->key == 'h');
+ assert_null(trie->root->next->child->data);
+ assert_true(trie->root->next->child->child->key == 'u');
+ assert_null(trie->root->next->child->child->data);
+ assert_true(trie->root->next->child->child->child->key == '\0');
+ assert_string_equal(trie->root->next->child->child->child->data, "nda");
+
+ assert_true(trie->root->child->child->next->key == 't');
+ assert_null(trie->root->child->child->next->data);
+ assert_true(trie->root->child->child->next->child->key == 'e');
+ assert_null(trie->root->child->child->next->child->data);
+ assert_true(trie->root->child->child->next->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->next->child->child->data, "aba");
+
+
+ sb_trie_insert(trie, "bo", sb_strdup("haha"));
+ assert_true(trie->root->key == 'b');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'o');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'l');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == 'a');
+ assert_null(trie->root->child->child->child->data);
+ assert_true(trie->root->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->child->data, "guda");
+
+ assert_true(trie->root->next->key == 'c');
+ assert_null(trie->root->next->data);
+ assert_true(trie->root->next->child->key == 'h');
+ assert_null(trie->root->next->child->data);
+ assert_true(trie->root->next->child->child->key == 'u');
+ assert_null(trie->root->next->child->child->data);
+ assert_true(trie->root->next->child->child->child->key == '\0');
+ assert_string_equal(trie->root->next->child->child->child->data, "nda");
+
+ assert_true(trie->root->child->child->next->key == 't');
+ assert_null(trie->root->child->child->next->data);
+ assert_true(trie->root->child->child->next->child->key == 'e');
+ assert_null(trie->root->child->child->next->child->data);
+ assert_true(trie->root->child->child->next->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->next->child->child->data, "aba");
+
+ assert_true(trie->root->child->child->next->next->key == '\0');
+ assert_string_equal(trie->root->child->child->next->next->data, "haha");
+
+ sb_trie_free(trie);
+
+
+ trie = sb_trie_new(free);
+
+ sb_trie_insert(trie, "chu", sb_strdup("nda"));
+ assert_true(trie->root->key == 'c');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'h');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'u');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->data, "nda");
+
+
+ sb_trie_insert(trie, "bola", sb_strdup("guda"));
+ assert_true(trie->root->key == 'c');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'h');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'u');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->data, "nda");
+
+ assert_true(trie->root->next->key == 'b');
+ assert_null(trie->root->next->data);
+ assert_true(trie->root->next->child->key == 'o');
+ assert_null(trie->root->next->child->data);
+ assert_true(trie->root->next->child->child->key == 'l');
+ assert_null(trie->root->next->child->child->data);
+ assert_true(trie->root->next->child->child->child->key == 'a');
+ assert_null(trie->root->next->child->child->child->data);
+ assert_true(trie->root->next->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->next->child->child->child->child->data, "guda");
+
+
+ sb_trie_insert(trie, "bote", sb_strdup("aba"));
+ assert_true(trie->root->key == 'c');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'h');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'u');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->data, "nda");
+
+ assert_true(trie->root->next->key == 'b');
+ assert_null(trie->root->next->data);
+ assert_true(trie->root->next->child->key == 'o');
+ assert_null(trie->root->next->child->data);
+ assert_true(trie->root->next->child->child->key == 'l');
+ assert_null(trie->root->next->child->child->data);
+ assert_true(trie->root->next->child->child->child->key == 'a');
+ assert_null(trie->root->next->child->child->child->data);
+ assert_true(trie->root->next->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->next->child->child->child->child->data, "guda");
+
+ assert_true(trie->root->next->child->child->next->key == 't');
+ assert_null(trie->root->next->child->child->next->data);
+ assert_true(trie->root->next->child->child->next->child->key == 'e');
+ assert_null(trie->root->next->child->child->next->child->data);
+ assert_true(trie->root->next->child->child->next->child->child->key == '\0');
+ assert_string_equal(trie->root->next->child->child->next->child->child->data, "aba");
+
+
+ sb_trie_insert(trie, "bo", sb_strdup("haha"));
+ assert_true(trie->root->key == 'c');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'h');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'u');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->data, "nda");
+
+ assert_true(trie->root->next->key == 'b');
+ assert_null(trie->root->next->data);
+ assert_true(trie->root->next->child->key == 'o');
+ assert_null(trie->root->next->child->data);
+ assert_true(trie->root->next->child->child->key == 'l');
+ assert_null(trie->root->next->child->child->data);
+ assert_true(trie->root->next->child->child->child->key == 'a');
+ assert_null(trie->root->next->child->child->child->data);
+ assert_true(trie->root->next->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->next->child->child->child->child->data, "guda");
+
+ assert_true(trie->root->next->child->child->next->key == 't');
+ assert_null(trie->root->next->child->child->next->data);
+ assert_true(trie->root->next->child->child->next->child->key == 'e');
+ assert_null(trie->root->next->child->child->next->child->data);
+ assert_true(trie->root->next->child->child->next->child->child->key == '\0');
+ assert_string_equal(trie->root->next->child->child->next->child->child->data, "aba");
+
+ assert_true(trie->root->next->child->child->next->next->key == '\0');
+ assert_string_equal(trie->root->next->child->child->next->next->data, "haha");
+
+ sb_trie_free(trie);
+}
+
+
+static void
+test_trie_insert_duplicated(void **state)
+{
+ sb_trie_t *trie = sb_trie_new(free);
+
+ sb_trie_insert(trie, "bola", sb_strdup("guda"));
+ assert_true(trie->root->key == 'b');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'o');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'l');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == 'a');
+ assert_null(trie->root->child->child->child->data);
+ assert_true(trie->root->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->child->data, "guda");
+
+ sb_trie_insert(trie, "bola", sb_strdup("asdf"));
+ assert_true(trie->root->key == 'b');
+ assert_null(trie->root->data);
+ assert_true(trie->root->child->key == 'o');
+ assert_null(trie->root->child->data);
+ assert_true(trie->root->child->child->key == 'l');
+ assert_null(trie->root->child->child->data);
+ assert_true(trie->root->child->child->child->key == 'a');
+ assert_null(trie->root->child->child->child->data);
+ assert_true(trie->root->child->child->child->child->key == '\0');
+ assert_string_equal(trie->root->child->child->child->child->data, "asdf");
+
+ sb_trie_free(trie);
+
+ trie = NULL;
+ sb_trie_insert(trie, "bola", NULL);
+ assert_null(trie);
+}
+
+
+static void
+test_trie_keep_data(void **state)
+{
+ sb_trie_t *trie = sb_trie_new(NULL);
+
+ char *t1 = "guda";
+ char *t2 = "nda";
+ char *t3 = "aba";
+ char *t4 = "haha";
+
+ sb_trie_insert(trie, "bola", t1);
+ sb_trie_insert(trie, "chu", t2);
+ sb_trie_insert(trie, "bote", t3);
+ sb_trie_insert(trie, "bo", t4);
+
+ sb_trie_free(trie);
+
+ assert_string_equal(t1, "guda");
+ assert_string_equal(t2, "nda");
+ assert_string_equal(t3, "aba");
+ assert_string_equal(t4, "haha");
+}
+
+
+static void
+test_trie_lookup(void **state)
+{
+ sb_trie_t *trie = sb_trie_new(free);
+
+ sb_trie_insert(trie, "bola", sb_strdup("guda"));
+ sb_trie_insert(trie, "chu", sb_strdup("nda"));
+ sb_trie_insert(trie, "bote", sb_strdup("aba"));
+ sb_trie_insert(trie, "bo", sb_strdup("haha"));
+
+ assert_string_equal(sb_trie_lookup(trie, "bola"), "guda");
+ assert_string_equal(sb_trie_lookup(trie, "chu"), "nda");
+ assert_string_equal(sb_trie_lookup(trie, "bote"), "aba");
+ assert_string_equal(sb_trie_lookup(trie, "bo"), "haha");
+
+ assert_null(sb_trie_lookup(trie, "arcoiro"));
+
+ sb_trie_free(trie);
+
+ trie = sb_trie_new(free);
+
+ sb_trie_insert(trie, "chu", sb_strdup("nda"));
+ sb_trie_insert(trie, "bola", sb_strdup("guda"));
+ sb_trie_insert(trie, "bote", sb_strdup("aba"));
+ sb_trie_insert(trie, "bo", sb_strdup("haha"));
+ sb_trie_insert(trie, "copa", sb_strdup("bu"));
+ sb_trie_insert(trie, "b", sb_strdup("c"));
+ sb_trie_insert(trie, "test", sb_strdup("asd"));
+
+ assert_string_equal(sb_trie_lookup(trie, "bola"), "guda");
+ assert_string_equal(sb_trie_lookup(trie, "chu"), "nda");
+ assert_string_equal(sb_trie_lookup(trie, "bote"), "aba");
+ assert_string_equal(sb_trie_lookup(trie, "bo"), "haha");
+
+ assert_null(sb_trie_lookup(trie, "arcoiro"));
+
+ sb_trie_free(trie);
+
+ assert_null(sb_trie_lookup(NULL, "bola"));
+}
+
+
+static void
+test_trie_size(void **state)
+{
+ sb_trie_t *trie = sb_trie_new(free);
+
+ sb_trie_insert(trie, "bola", sb_strdup("guda"));
+ sb_trie_insert(trie, "chu", sb_strdup("nda"));
+ sb_trie_insert(trie, "bote", sb_strdup("aba"));
+ sb_trie_insert(trie, "bo", sb_strdup("haha"));
+
+ assert_int_equal(sb_trie_size(trie), 4);
+ assert_int_equal(sb_trie_size(NULL), 0);
+
+ sb_trie_free(trie);
+
+ trie = sb_trie_new(free);
+
+ sb_trie_insert(trie, "chu", sb_strdup("nda"));
+ sb_trie_insert(trie, "bola", sb_strdup("guda"));
+ sb_trie_insert(trie, "bote", sb_strdup("aba"));
+ sb_trie_insert(trie, "bo", sb_strdup("haha"));
+ sb_trie_insert(trie, "copa", sb_strdup("bu"));
+ sb_trie_insert(trie, "b", sb_strdup("c"));
+ sb_trie_insert(trie, "test", sb_strdup("asd"));
+
+ assert_int_equal(sb_trie_size(trie), 7);
+ assert_int_equal(sb_trie_size(NULL), 0);
+
+ sb_trie_free(trie);
+}
+
+
+static unsigned int counter;
+static char *expected_keys[] = {"chu", "copa", "bola", "bote", "bo", "b", "test"};
+static char *expected_datas[] = {"nda", "bu", "guda", "aba", "haha", "c", "asd"};
+
+static void
+mock_foreach(const char *key, void *data, void *user_data)
+{
+ assert_string_equal(user_data, "foo");
+ assert_string_equal(key, expected_keys[counter]);
+ assert_string_equal((char*) data, expected_datas[counter++]);
+}
+
+
+static void
+test_trie_foreach(void **state)
+{
+ sb_trie_t *trie = sb_trie_new(free);
+
+ sb_trie_insert(trie, "chu", sb_strdup("nda"));
+ sb_trie_insert(trie, "bola", sb_strdup("guda"));
+ sb_trie_insert(trie, "bote", sb_strdup("aba"));
+ sb_trie_insert(trie, "bo", sb_strdup("haha"));
+ sb_trie_insert(trie, "copa", sb_strdup("bu"));
+ sb_trie_insert(trie, "b", sb_strdup("c"));
+ sb_trie_insert(trie, "test", sb_strdup("asd"));
+
+ counter = 0;
+ sb_trie_foreach(trie, mock_foreach, "foo");
+ sb_trie_foreach(NULL, mock_foreach, "foo");
+ sb_trie_foreach(trie, NULL, "foo");
+ sb_trie_foreach(NULL, NULL, "foo");
+
+ sb_trie_free(trie);
+}
+
+
+int
+main(void)
+{
+ const UnitTest tests[] = {
+
+ // slist
+ unit_test(test_slist_append),
+ unit_test(test_slist_prepend),
+ unit_test(test_slist_free),
+ unit_test(test_slist_length),
+
+ // strfuncs
+ unit_test(test_strdup),
+ unit_test(test_strndup),
+ unit_test(test_strdup_printf),
+ unit_test(test_str_starts_with),
+ unit_test(test_str_ends_with),
+ unit_test(test_str_lstrip),
+ unit_test(test_str_rstrip),
+ unit_test(test_str_strip),
+ unit_test(test_str_split),
+ unit_test(test_str_replace),
+ unit_test(test_strv_join),
+ unit_test(test_strv_length),
+
+ // string
+ unit_test(test_string_new),
+ unit_test(test_string_free),
+ unit_test(test_string_dup),
+ unit_test(test_string_append_len),
+ unit_test(test_string_append),
+ unit_test(test_string_append_c),
+ unit_test(test_string_append_printf),
+
+ // trie
+ unit_test(test_trie_new),
+ unit_test(test_trie_insert),
+ unit_test(test_trie_insert_duplicated),
+ unit_test(test_trie_keep_data),
+ unit_test(test_trie_lookup),
+ unit_test(test_trie_size),
+ unit_test(test_trie_foreach),
+ };
+ return run_tests(tests);
+}