coverage and test: Add tests 59/15659/1
authorJose Bollo <jose.bollo@iot.bzh>
Thu, 12 Jul 2018 08:59:48 +0000 (10:59 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Tue, 24 Jul 2018 14:28:32 +0000 (16:28 +0200)
coverage values:
  - lines:     70.5 %
  - functions: 76.3 %

Change-Id: Iaf802e84bbfa57502bbbac8c3b567b14c01608b6
Signed-off-by: Jose Bollo <jose.bollo@iot.bzh>
21 files changed:
bindings/samples/hello3.c
coverage/.gitignore
coverage/Makefile
coverage/bin/Makefile
coverage/bin/bug.c
coverage/bin/fake/monitoring [new symlink]
coverage/bin/loc.txt [new file with mode: 0644]
coverage/bin/locales/en/loc.txt [new file with mode: 0644]
coverage/bin/locales/fr-FR/loc.txt [new file with mode: 0644]
coverage/bin/locales/fr/loc.txt [new file with mode: 0644]
coverage/bin/locales/jp/loc.txt [new file with mode: 0644]
coverage/ldpath/weak/bug.so [deleted symlink]
coverage/ldpath/weak/bugs [new symlink]
coverage/scripts/00-trace.sh
coverage/scripts/01-http.sh
coverage/scripts/02-hello.sh
coverage/scripts/03-auto-ws.sh [moved from coverage/scripts/03-x-hello.sh with 100% similarity]
coverage/scripts/06-auto-so.sh [new file with mode: 0755]
coverage/scripts/run-test.sh
src/tests/wrap-json/CMakeLists.txt [new file with mode: 0644]
src/tests/wrap-json/test-wrap-json.c [new file with mode: 0644]

index f49b336..477e47b 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <pthread.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <json-c/json.h>
 
 #define AFB_BINDING_VERSION 3
 #include <afb/afb-binding.h>
 
+#if !defined(APINAME)
+#define APINAME "hello3"
+#endif
+
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
+/**************************************************************************/
+
 struct event
 {
        struct event *next;
@@ -116,6 +126,30 @@ static int event_broadcast(struct json_object *args, const char *tag)
        return e ? afb_event_broadcast(e->event, json_object_get(args)) : -1;
 }
 
+/**************************************************************************/
+
+struct api
+{
+       struct api *next;
+       afb_api_t api;
+       char name[1];
+};
+
+static struct api *apis = 0;
+
+/* search the api of name */
+static struct api *searchapi(const char *name, struct api ***previous)
+{
+       struct api *a, **p = &apis;
+       while((a = *p) && strcmp(a->name, name))
+               p = &a->next;
+       if (previous)
+               *previous = p;
+       return a;
+}
+
+/**************************************************************************/
+
 // Sample Generic Ping Debug API
 static void ping(afb_req_t request, json_object *jresp, const char *tag)
 {
@@ -451,23 +485,107 @@ static void info (afb_req_t request)
        afb_req_reply(request, afb_req_get_client_info(request), NULL, NULL);
 }
 
-static int preinit(afb_api_t api)
+static void eventloop (afb_req_t request)
 {
-       AFB_NOTICE("hello binding comes to live");
-       return 0;
+       afb_api_t api = afb_req_get_api(request);
+       struct sd_event *ev = afb_api_get_event_loop(api);
+       afb_req_reply(request, NULL, ev ? NULL : "no-event-loop", NULL);
 }
 
-static int init(afb_api_t api)
+static void dbus (afb_req_t request)
 {
-       AFB_NOTICE("hello binding starting");
-       return 0;
+       afb_api_t api = afb_req_get_api(request);
+       json_object *json = afb_req_json(request);
+       struct sd_bus *bus = (json_object_get_boolean(json) ? afb_api_get_system_bus : afb_api_get_user_bus)(api);
+       afb_req_reply(request, NULL, bus ? NULL : "no-bus", NULL);
 }
 
-static void onevent(afb_api_t api, const char *event, struct json_object *object)
+static void replycount (afb_req_t request)
 {
-       AFB_NOTICE("received event %s(%s)", event, json_object_to_json_string(object));
+       json_object *json = afb_req_json(request);
+       int count = json_object_get_int(json);
+       while (count-- > 0)
+               afb_req_reply(request, NULL, NULL, NULL);
 }
 
+static void get(afb_req_t request)
+{
+       struct afb_arg arg = afb_req_get(request, "name");
+       const char *name, *value, *path;
+
+       if (!arg.name || !arg.value)
+               afb_req_reply(request, NULL, "invalid", "the parameter 'name' is missing");
+       else {
+               name = arg.name;
+               value = afb_req_value(request, name);
+               path = afb_req_path(request, name);
+               afb_req_reply_f(request, NULL, NULL, "found for '%s': %s", name, value ?: path ?: "NULL");
+       }
+}
+
+static void ref(afb_req_t request)
+{
+       afb_req_addref(request);
+       afb_req_reply(request, NULL, NULL, NULL);
+       afb_req_unref(request);
+}
+
+static void rootdir (afb_req_t request)
+{
+       ssize_t s;
+       afb_api_t api = afb_req_get_api(request);
+       int fd = afb_api_rootdir_get_fd(api);
+       char buffer[150], root[1025];
+       sprintf(buffer, "/proc/self/fd/%d", fd);
+       s = readlink(buffer, root, sizeof root - 1);
+       if (s < 0)
+               afb_req_reply_f(request, NULL, "error", "can't readlink %s: %m", buffer);
+       else {
+               root[s] = 0;
+               afb_req_reply(request, json_object_new_string(root), NULL, NULL);
+       }
+}
+
+static void locale (afb_req_t request)
+{
+       char buffer[150], root[1025];
+       const char *lang, *file;
+       ssize_t s;
+       json_object *json = afb_req_json(request), *x;
+       afb_api_t api = afb_req_get_api(request);
+       int fd;
+
+       lang = NULL;
+       if (json_object_is_type(json, json_type_string))
+               file = json_object_get_string(json);
+       else {
+               if (!json_object_object_get_ex(json, "file", &x)) {
+                       afb_req_reply(request, NULL, "invalid", "no file");
+                       return;
+               }
+               file = json_object_get_string(x);
+               if (json_object_object_get_ex(json, "lang", &x))
+                       lang = json_object_get_string(x);
+       }
+
+       fd = afb_api_rootdir_open_locale(api, file, O_RDONLY, lang);
+       if (fd < 0)
+               afb_req_reply_f(request, NULL, "error", "can't open %s [%s]: %m", file?:"NULL", lang?:"NULL");
+       else {
+               sprintf(buffer, "/proc/self/fd/%d", fd);
+               s = readlink(buffer, root, sizeof root - 1);
+               if (s < 0)
+                       afb_req_reply_f(request, NULL, "error", "can't readlink %s: %m", buffer);
+               else {
+                       root[s] = 0;
+                       afb_req_reply(request, json_object_new_string(root), NULL, NULL);
+               }
+               close(fd);
+       }
+}
+
+static void api (afb_req_t request);
+
 // NOTE: this sample does not use session to keep test a basic as possible
 //       in real application most APIs should be protected with AFB_SESSION_CHECK
 static const struct afb_verb_v3 verbs[]= {
@@ -499,12 +617,186 @@ static const struct afb_verb_v3 verbs[]= {
   { .verb="setctxif",    .callback=setctx, .vcbdata = (void*)(intptr_t)0 },
   { .verb="getctx",      .callback=getctx },
   { .verb="info",        .callback=info },
+  { .verb="eventloop",   .callback=eventloop },
+  { .verb="dbus",        .callback=dbus },
+  { .verb="reply-count", .callback=replycount },
+  { .verb="get",         .callback=get},
+  { .verb="ref",         .callback=ref},
+  { .verb="rootdir",     .callback=rootdir},
+  { .verb="locale",      .callback=locale},
+  { .verb="api",         .callback=api},
   { .verb=NULL}
 };
 
-#if !defined(APINAME)
-#define APINAME "hello3"
+static void pingSample2 (struct afb_req_x1 req)
+{
+       pingSample(req.closure);
+}
+
+static const struct afb_verb_v2 apiverbs2[]= {
+  { .verb="ping",        .callback=pingSample2 },
+  { .verb="ping2",        .callback=pingSample2 },
+  { .verb=NULL }
+};
+
+static int apipreinit(void *closure, afb_api_t api)
+{
+       afb_api_set_verbs_v2(api, apiverbs2);
+       afb_api_set_verbs_v3(api, verbs);
+       return 0;
+}
+
+static void apiverb (afb_req_t request)
+{
+       afb_req_reply_f(request, json_object_get(afb_req_json(request)), NULL, "api: %s, verb: %s",
+               afb_req_get_called_api(request), afb_req_get_called_verb(request));
+}
+
+static void apievhndl(void *closure, const char *event, struct json_object *args, afb_api_t api)
+{
+       struct json_object *obj = closure;
+       afb_api_verbose(api, 0, NULL, 0, NULL, "the handler of closure(%s) received the event %s(%s)",
+               json_object_get_string(obj), event, json_object_get_string(args));
+}
+
+static void api (afb_req_t request)
+{
+       struct api *sapi, **psapi;
+       const char *action, *apiname, *verbname, *pattern;
+       json_object *json = afb_req_json(request), *x, *closure;
+       afb_api_t api = afb_req_get_api(request), oapi;
+
+       /* get the action */
+       if (!json_object_object_get_ex(json, "action", &x)) {
+               afb_req_reply(request, NULL, "invalid", "no action");
+               goto end;
+       }
+       action = json_object_get_string(x);
+
+       /* get the verb */
+       verbname = json_object_object_get_ex(json, "verb", &x) ?
+               json_object_get_string(x) : NULL;
+
+       /* get the pattern */
+       pattern = json_object_object_get_ex(json, "pattern", &x) ?
+               json_object_get_string(x) : NULL;
+
+       /* get the closure */
+       closure = NULL;
+       json_object_object_get_ex(json, "closure", &closure);
+
+       /* get the api */
+       if (json_object_object_get_ex(json, "api", &x)) {
+               apiname = json_object_get_string(x);
+               sapi = searchapi(apiname, &psapi);
+               oapi = sapi ? sapi->api : NULL;
+       } else {
+               oapi = api;
+               apiname = afb_api_name(api);
+               sapi = searchapi(apiname, &psapi);
+       }
+
+       /* search the sapi */
+       if (!strcasecmp(action, "create")) {
+               if (!apiname) {
+                       afb_req_reply(request, NULL, "invalid", "no api");
+                       goto end;
+               }
+               if (sapi) {
+                       afb_req_reply(request, NULL, "already-exist", NULL);
+                       goto end;
+               }
+               sapi = malloc (sizeof * sapi + strlen(apiname));
+               if (!sapi) {
+                       afb_req_reply(request, NULL, "out-of-memory", NULL);
+                       goto end;
+               }
+               sapi->api = afb_api_new_api(api, apiname, NULL, 1, apipreinit, NULL);
+               if (!sapi->api) {
+                       afb_req_reply_f(request, NULL, "cant-create", "%m");
+                       goto end;
+               }
+               strcpy(sapi->name, apiname);
+               sapi->next = NULL;
+               *psapi = sapi;
+       } else {
+               if (!oapi) {
+                       afb_req_reply(request, NULL, "cant-find-api", NULL);
+                       goto end;
+               }
+               if (!strcasecmp(action, "destroy")) {
+                       if (!sapi) {
+                               afb_req_reply(request, NULL, "cant-destroy", NULL);
+                               goto end;
+                       }
+                       afb_api_delete_api(oapi);
+                       *psapi = sapi->next;
+                       free(sapi);
+               } else if (!strcasecmp(action, "addverb")) {
+                       if (!verbname){
+                               afb_req_reply(request, NULL, "invalid", "no verb");
+                               goto end;
+                       }
+                       afb_api_add_verb(oapi, verbname, NULL, apiverb, NULL, NULL, 0, !!strchr(verbname, '*'));
+               } else if (!strcasecmp(action, "delverb")) {
+                       if (!verbname){
+                               afb_req_reply(request, NULL, "invalid", "no verb");
+                               goto end;
+                       }
+                       afb_api_del_verb(oapi, verbname, NULL);
+               } else if (!strcasecmp(action, "addhandler")) {
+                       if (!pattern){
+                               afb_req_reply(request, NULL, "invalid", "no pattern");
+                               goto end;
+                       }
+                       afb_api_event_handler_add(oapi, pattern, apievhndl, json_object_get(closure));
+               } else if (!strcasecmp(action, "delhandler")) {
+                       if (!pattern){
+                               afb_req_reply(request, NULL, "invalid", "no pattern");
+                               goto end;
+                       }
+                       closure = NULL;
+                       afb_api_event_handler_del(oapi, pattern, (void**)&closure);
+                       json_object_put(closure);
+               } else if (!strcasecmp(action, "seal")) {
+                       afb_api_seal(oapi);
+               } else {
+                       afb_req_reply_f(request, NULL, "invalid", "unknown action %s", action ?: "NULL");
+                       goto end;
+               }
+       }
+       afb_req_reply(request, NULL, NULL, NULL);
+end:   return;
+}
+
+/*************************************************************/
+
+static int preinit(afb_api_t api)
+{
+       AFB_NOTICE("hello binding comes to live");
+#if defined(PREINIT_PROVIDE_CLASS)
+       afb_api_provide_class(api, PREINIT_PROVIDE_CLASS);
+#endif
+#if defined(PREINIT_REQUIRE_CLASS)
+       afb_api_require_class(api, PREINIT_REQUIRE_CLASS);
+#endif
+       return 0;
+}
+
+static int init(afb_api_t api)
+{
+       AFB_NOTICE("hello binding starting");
+#if defined(INIT_REQUIRE_API)
+       afb_api_require_api(api, INIT_REQUIRE_API, 1);
 #endif
+       afb_api_add_alias(api, api->apiname, "fakename");
+       return 0;
+}
+
+static void onevent(afb_api_t api, const char *event, struct json_object *object)
+{
+       AFB_NOTICE("received event %s(%s)", event, json_object_to_json_string(object));
+}
 
 const struct afb_binding_v3 afbBindingV3 = {
        .api = APINAME,
index 43d04cb..69f6ea4 100644 (file)
@@ -1,3 +1,14 @@
 lcov-out.info
 report/
 valgrind.out
+bin/bugs
+run-test.output
+bin/afb-client
+bin/afb-daemon-cov
+bin/test-apiset
+bin/test-session
+bin/test-wrap-json
+*.o
+*.so
+*.gcda
+*.gcno
index 3f5f3ce..30477a7 100644 (file)
@@ -14,7 +14,8 @@ clean:
        make -C bin -w clean
 
 cleanall: clean
-       rm -rf lcov-out.info report valgrind.out
+       @echo remove all outputs and reports
+       @rm -rf lcov-out.info report valgrind.out 2>/dev/null || true
 
 .PHONY: test
 
@@ -22,14 +23,12 @@ test: binaries
        @echo -----------------------------------------
        @echo -- BEGIN TEST
        @echo -----------------------------------------
-       @scripts/run-test.sh
+       @scripts/run-test.sh 2>&1 | tee run-test.output
        @echo -----------------------------------------
        @echo -- END TEST
        @echo -----------------------------------------
 
 report: test
-       @echo generating LCOV report
-       @lcov -c -d bin -o lcov-out.info
        @echo generating report
        @genhtml -s -o report lcov-out.info
        @echo ready: xdg-open report/index.html
index 19bf254..f9d561e 100644 (file)
 heredir = .
 basedir = ../..
 
-targets = afb-daemon-cov  afb-client hi3.so hello.so salut.so salam.so shalom.so demat.so bug.so hellov2.so
+bindings = \
+       hi3.so \
+       hello.so \
+       salut.so \
+       salam.so \
+       shalom.so \
+       demat.so \
+       hellov2.so
+
+bugs = $(foreach i,\
+       1 2 3 4 5 6 7 8 9 \
+       10 11 12 13 14 15 16 17 18 19 \
+       20 21, \
+       bugs/bug$i.so)
+
+tests = \
+       test-apiset \
+       test-session \
+       test-wrap-json
+
+targets = \
+       afb-daemon-cov \
+       afb-client \
+       $(tests) \
+       $(bindings) \
+       $(bugs)
 
 binaries: $(targets)
 
 clean:
-       @rm $(targets) *.gcno *.gcda
+       @echo remove all binaries
+       @rm $(targets) *.gcno *.gcda *.o  2>/dev/null || true
 
 #======================================================================================
-# creates the targets
+# definitions
 #======================================================================================
 
 incdir = $(basedir)/include
 srcdir = $(basedir)/src
+tstdir = $(basedir)/src/tests
 samdir = $(basedir)/bindings/samples
 
 bindir = $(heredir)/bin
 
-cflags = -I$(incdir) \
-       $(shell pkg-config --cflags --libs openssl libmicrohttpd json-c libsystemd uuid) \
-       -ldl -lrt -lpthread 
+deps = openssl libmicrohttpd json-c libsystemd uuid
+
+ccflags = \
+       -g \
+       -I$(incdir) \
+       $(shell pkg-config --cflags $(deps))
+
+ldflags = -ldl -lrt -lpthread \
+       $(shell pkg-config --libs $(deps))
+
+cflags = $(ccflags) $(ldflags)
+
+defs =         -DAGL_DEVEL \
+       -DWITH_MONITORING_OPTION \
+       -DWITH_SUPERVISION \
+       -DAFB_VERSION=\"cov\" \
+       -DBINDING_INSTALL_DIR=\"$(shell pwd)/fake\"
 
 afb_lib_src = $(shell ls $(srcdir)/*.c | egrep -v '/afs-|/main-' )
-afb_clib_src = $(shell ls $(srcdir)/*.c | egrep -v '/afs-|/main-' )
+afb_lib_obj = $(patsubst $(srcdir)/%.c,%.o,$(afb_lib_src))
+afb_lib = afb-lib.a
+afb_lib_defs = $(defs)
+
+afb_daemon_srcs = $(srcdir)/main-afb-daemon.c $(afb_lib_obj)
+afb_daemon_defs = $(afb_lib_defs)
 
-afb_daemon_srcs = $(srcdir)/main-afb-daemon.c $(afb_lib_src)
-afb_daemon_defs = '-DAFB_VERSION="cov"' -DAGL_DEVEL -DWITH_MONITORING_OPTION '-DBINDING_INSTALL_DIR="fake"'
+afb_client_srcs = $(srcdir)/main-afb-client-demo.c $(afb_lib_src)
+afb_client_defs = $(defs)
 
-afb_client_srcs = $(srcdir)/main-afb-client-demo.c $(afb_clib_src)
-afb_client_defs = '-DAFB_VERSION="cov"' '-DBINDING_INSTALL_DIR="fake"'
+tst_defs = $(defs)
+tst_flags = $(cflags) \
+       -I$(srcdir) \
+       $(shell pkg-config --cflags --libs check)
+
+tst_defs = '-DAFB_VERSION="cov"' '-DBINDING_INSTALL_DIR="fake"'
+tst_flags = $(cflags) \
+       -I$(srcdir) \
+       $(shell pkg-config --cflags --libs check)
 
 hello2_src = $(samdir)/hello2.c
 hello3_src = $(samdir)/hello3.c
 hi_src = $(samdir)/hi3.c
 binding_flags = -shared -fPIC -Wl,--version-script=$(samdir)/export.map
 
+#======================================================================================
+# creates the targets
+#======================================================================================
+
+%.o: $(srcdir)/%.c
+       @echo creation of $@
+       @gcc -c -o $@ $< --coverage $(afb_lib_defs) $(ccflags)
+
 afb-daemon-cov: $(afb_daemon_srcs)
        @echo creation of $@
        @gcc -o $@ $(afb_daemon_srcs) --coverage $(afb_daemon_defs) $(cflags)
@@ -47,21 +108,41 @@ afb-client: $(afb_client_srcs)
        @echo creation of $@
        @gcc -o $@ $(afb_client_srcs) $(afb_client_defs) $(cflags)
 
+#======================================================================================
+# create test
+#======================================================================================
+
+test-apiset: $(tstdir)/apiset/test-apiset.c $(afb_lib_obj)
+       @echo creation of $@
+       @gcc -o $@ $(tstdir)/apiset/test-apiset.c $(afb_lib_obj) --coverage $(tst_defs) $(tst_flags)
+
+test-session: $(tstdir)/session/test-session.c $(afb_lib_obj)
+       @echo creation of $@
+       @gcc -o $@ $(tstdir)/session/test-session.c $(afb_lib_obj) --coverage $(tst_defs) $(tst_flags)
+
+test-wrap-json: $(tstdir)/session/test-session.c $(afb_lib_obj)
+       @echo creation of $@
+       @gcc -o $@ $(tstdir)/wrap-json/test-wrap-json.c $(afb_lib_obj) --coverage $(tst_defs) $(tst_flags)
+
+#======================================================================================
+# create bindings
+#======================================================================================
+
 hi3.so: $(hi3_src)
        @echo creation of $@
        @gcc -o $@ $(hi3_src) $(binding_flags) $(cflags)
 
 hello.so: $(hello3_src)
        @echo creation of $@
-       @gcc -o $@ $(hello3_src) '-DAPINAME="hello"' $(binding_flags) $(cflags)
+       @gcc -o $@ $(hello3_src) '-DAPINAME="hello"' '-DPREINIT_PROVIDE_CLASS="class1 class2"' $(binding_flags) $(cflags)
 
 salut.so: $(hello3_src)
        @echo creation of $@
-       @gcc -o $@ $(hello3_src) '-DAPINAME="salut"' $(binding_flags) $(cflags)
+       @gcc -o $@ $(hello3_src) '-DAPINAME="salut"' '-DPREINIT_REQUIRE_CLASS="class2"' $(binding_flags) $(cflags)
 
 salam.so: $(hello3_src)
        @echo creation of $@
-       @gcc -o $@ $(hello3_src) '-DAPINAME="salam"' $(binding_flags) $(cflags)
+       @gcc -o $@ $(hello3_src) '-DAPINAME="salam"' '-DINIT_REQUIRE_API="hello salut"' $(binding_flags) $(cflags)
 
 shalom.so: $(hello3_src)
        @echo creation of $@
@@ -75,7 +156,14 @@ hellov2.so: $(hello2_src)
        @echo creation of $@
        @gcc -o $@ $(hello2_src) '-DAPINAME="hello-v2"' $(binding_flags) $(cflags)
 
-bug.so: bug.c
-       @echo creation of $@
-       @gcc -o $@ bug.c $(binding_flags) $(cflags)
+#======================================================================================
+# create bugs
+#======================================================================================
+
+bugs:
+       @echo creation of directory bugs
+       @mkdir bugs
 
+bugs/bug%.so: bug.c bugs
+       @echo creation of $@
+       @gcc -o $@ bug.c $(binding_flags) $(cflags) -D$(patsubst bugs/bug%.so,BUG%,$@)
index ddebdef..62646ce 100644 (file)
@@ -1,6 +1,276 @@
+#include <errno.h>
+#include <stdint.h>
+static int ok()
+{
+       return 0;
+}
+static int bug()
+{
+       errno = 0;
+       return ((int(*)())(intptr_t)0)();
+}
+static int err()
+{
+       errno = EAGAIN;
+       return -1;
+}
+/**************************************************************************/
+/**************************************************************************/
+/***           BINDINGS V2                                              ***/
+/**************************************************************************/
+/**************************************************************************/
+#if defined(BUG1)  /* incomplete exports: afbBindingV2data miss */
+
+#define AFB_BINDING_VERSION 0
+#include <afb/afb-binding.h>
+const struct afb_binding_v2 afbBindingV2;
+
+#endif
+/**************************************************************************/
+#if defined(BUG2)  /* incomplete exports: afbBindingV2 miss */
+
+#define AFB_BINDING_VERSION 0
+#include <afb/afb-binding.h>
+struct afb_binding_data_v2 afbBindingV2data;
+
+#endif
+/**************************************************************************/
+#if defined(BUG3)  /* zero filled structure */
+
+#define AFB_BINDING_VERSION 0
+#include <afb/afb-binding.h>
+const struct afb_binding_v2 afbBindingV2;
+struct afb_binding_data_v2 afbBindingV2data;
+
+#endif
+/**************************************************************************/
+#if defined(BUG4)  /* no verb definition */
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+const struct afb_binding_v2 afbBindingV2 = {
+       .api = "bug4",
+        .preinit = (void*)ok,
+        .init = (void*)ok
+};
+#endif
+/**************************************************************************/
+#if defined(BUG5)  /* preinit buggy */
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+struct afb_verb_v2 verbs[] = {
+       { NULL }
+};
+const struct afb_binding_v2 afbBindingV2 = {
+       .api = "bug5",
+       .verbs = verbs,
+        .preinit = (void*)bug,
+        .init = (void*)ok
+};
+#endif
+/**************************************************************************/
+#if defined(BUG6)  /* buggy init */
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+struct afb_verb_v2 verbs[] = {
+       { NULL }
+};
+const struct afb_binding_v2 afbBindingV2 = {
+       .api = "bug6",
+       .verbs = verbs,
+        .preinit = (void*)ok,
+        .init = (void*)bug
+};
+#endif
+/**************************************************************************/
+#if defined(BUG7)  /* error in preinit */
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+struct afb_verb_v2 verbs[] = {
+       { NULL }
+};
+const struct afb_binding_v2 afbBindingV2 = {
+       .api = "bug7",
+       .verbs = verbs,
+        .preinit = (void*)err,
+        .init = (void*)ok
+};
+#endif
+/**************************************************************************/
+#if defined(BUG8)  /* error in init */
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+struct afb_verb_v2 verbs[] = {
+       { NULL }
+};
+const struct afb_binding_v2 afbBindingV2 = {
+       .api = "bug8",
+       .verbs = verbs,
+        .preinit = (void*)ok,
+        .init = (void*)err
+};
+#endif
+/**************************************************************************/
+#if defined(BUG9)  /* no api name */
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+struct afb_verb_v2 verbs[] = {
+       { NULL }
+};
+const struct afb_binding_v2 afbBindingV2 = {
+       .verbs = verbs,
+        .preinit = (void*)ok,
+        .init = (void*)ok
+};
+#endif
+/**************************************************************************/
+#if defined(BUG10)  /* bad api name */
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+struct afb_verb_v2 verbs[] = {
+       { NULL }
+};
+const struct afb_binding_v2 afbBindingV2 = {
+       .api = "bug 10",
+       .verbs = verbs,
+        .preinit = (void*)ok,
+        .init = (void*)err
+};
+#endif
+/**************************************************************************/
+/**************************************************************************/
+/***           BINDINGS V3                                              ***/
+/**************************************************************************/
+/**************************************************************************/
+#if defined(BUG11) /* make a SEGV */
+
 #define AFB_BINDING_VERSION 3
 #include <afb/afb-binding.h>
 int afbBindingEntry(afb_api_t api)
 {
        return ((int(*)())(intptr_t)0)();
 }
+#endif
+/**************************************************************************/
+#if defined(BUG12) /* no afbBindingV3 nor afbBindingV3entry */
+
+#define AFB_BINDING_VERSION 0
+#include <afb/afb-binding.h>
+struct afb_api_x3 *afbBindingV3root;
+
+#endif
+/**************************************************************************/
+#if defined(BUG13) /* no afbBindingV3root nor afbBindingV3entry */
+
+#define AFB_BINDING_VERSION 0
+#include <afb/afb-binding.h>
+const struct afb_binding_v3 afbBindingV3;
+int afbBindingV3entry(struct afb_api_x3 *rootapi) { return 0; }
+
+#endif
+/**************************************************************************/
+#if defined(BUG14) /* no api name */
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+const struct afb_binding_v3 afbBindingV3;
+
+#endif
+/**************************************************************************/
+#if defined(BUG15) /* bad api name */
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+const struct afb_binding_v3 afbBindingV3 = {
+       .api = "bug 15"
+};
+
+#endif
+/**************************************************************************/
+#if defined(BUG16) /* both entry and preinit */
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+int afbBindingV3entry(struct afb_api_x3 *rootapi) { return 0; }
+const struct afb_binding_v3 afbBindingV3 = {
+       .api = "bug16",
+       .preinit = afbBindingV3entry
+};
+
+#endif
+/**************************************************************************/
+#if defined(BUG17) /* entry fails */
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+int afbBindingV3entry(struct afb_api_x3 *rootapi) { errno = EAGAIN; return -1; }
+#endif
+/**************************************************************************/
+#if defined(BUG18) /* preinit fails */
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+const struct afb_binding_v3 afbBindingV3 = {
+       .api = "bug18",
+       .preinit = (void*)err
+};
+
+#endif
+/**************************************************************************/
+#if defined(BUG19) /* preinit SEGV */
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+const struct afb_binding_v3 afbBindingV3 = {
+       .api = "bug19",
+       .preinit = (void*)bug
+};
+
+#endif
+/**************************************************************************/
+#if defined(BUG20) /* init fails */
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+const struct afb_binding_v3 afbBindingV3 = {
+       .api = "bug20",
+       .init = (void*)err
+};
+
+#endif
+/**************************************************************************/
+#if defined(BUG21) /* init SEGV */
+
+#define AFB_BINDING_VERSION 3
+#include <afb/afb-binding.h>
+
+const struct afb_binding_v3 afbBindingV3 = {
+       .api = "bug21",
+       .init = (void*)bug,
+       .provide_class = "a b c",
+       .require_class = "x y z",
+       .require_api = "bug4 bug5",
+};
+
+#endif
+/**************************************************************************/
diff --git a/coverage/bin/fake/monitoring b/coverage/bin/fake/monitoring
new file mode 120000 (symlink)
index 0000000..8cdac10
--- /dev/null
@@ -0,0 +1 @@
+../../../test/monitoring
\ No newline at end of file
diff --git a/coverage/bin/loc.txt b/coverage/bin/loc.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/coverage/bin/locales/en/loc.txt b/coverage/bin/locales/en/loc.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/coverage/bin/locales/fr-FR/loc.txt b/coverage/bin/locales/fr-FR/loc.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/coverage/bin/locales/fr/loc.txt b/coverage/bin/locales/fr/loc.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/coverage/bin/locales/jp/loc.txt b/coverage/bin/locales/jp/loc.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/coverage/ldpath/weak/bug.so b/coverage/ldpath/weak/bug.so
deleted file mode 120000 (symlink)
index 01e7c27..0000000
+++ /dev/null
@@ -1 +0,0 @@
-sub/bug.so
\ No newline at end of file
diff --git a/coverage/ldpath/weak/bugs b/coverage/ldpath/weak/bugs
new file mode 120000 (symlink)
index 0000000..6ed8e22
--- /dev/null
@@ -0,0 +1 @@
+../../bin/bugs
\ No newline at end of file
index 34a19bb..27edc5b 100755 (executable)
@@ -1,4 +1,7 @@
 #!/bin/sh
 
-$R/bin/afb-client -k $WSURL monitor trace '{"add":{"api":"*","request":"*","event":"*","session":"*","global":"*"}}' &
-
+$R/bin/afb-client -k $WSURL <<EOC &
+monitor trace {"add":{"tag":"fun","api":"*","request":"*","event":"*","session":"*","global":"*"}}
+monitor trace {"add":{"tag":"T","api":"*","request":"*","event":"*","session":"*","global":"*"}}
+monitor trace {"drop":{"tag":"fun"}}
+EOC
index 191318c..9ffd640 100755 (executable)
@@ -1,17 +1,29 @@
 #!/bin/sh
 
 
-curl $URL/index.html
-curl $URL/marrus-orthocanna.jpg
-curl $URL/test.js
-curl $URL/icons/marrus-orthocanna.jpg
+curl -s -o /dev/null $URL/index.html
+curl -s -o /dev/null $URL/marrus-orthocanna.jpg
+curl -s -o /dev/null $URL/test.js
+curl -s -o /dev/null $URL/icons/marrus-orthocanna.jpg
 
-curl $URL/fake-file.html
+curl -s -o /dev/null $URL/fake-file.html
 
-curl "$URL/api/salut/ping?arg1=null&arg1=%22a+string%22"
-curl "$URL/api/hello/ping" \
+curl -s "$URL/api/salut/ping?arg1=null&arg1=%22a+string%22"
+curl -s "$URL/api/hello/ping" \
        -F image=@$R/www/marrus-orthocanna.jpg \
        -F name=test
-curl -X POST "$URL/api/hello/ping" \
+curl -s -X POST "$URL/api/hello/ping" \
        --header 'content-type: application/json' \
        --data-binary '[null,3,{"hello":false,"salut":4.5},true]'
+
+curl -s "$URL/api/hello/get?name=something&something=nothing"
+
+curl -s -F name=file -F file=@$R/www/marrus-orthocanna.jpg "$URL/api/hello/get"
+
+curl -s -X HEAD -o /dev/null $URL/index.html
+#curl -s -X CONNECT -o /dev/null $URL/index.html
+curl -s -X DELETE -o /dev/null $URL/index.html
+curl -s -X OPTIONS -o /dev/null $URL/index.html
+curl -s -X PATCH -o /dev/null $URL/index.html
+curl -s -X PUT -o /dev/null $URL/index.html
+curl -s -X TRACE -o /dev/null $URL/index.html
index fe9040c..9e3a9b0 100755 (executable)
@@ -48,5 +48,49 @@ hello setctx "some-text-2"
 hello getctx
 hello info
 hello verbose {"level":2,"message":"hello"}
+hello eventloop
+hello dbus false
+hello dbus true
+hello reply-count 0
+hello reply-count 2
+hello get null
+hello get {"name":"toto"}
+hello get {"name":"toto","toto":5}
+hello ref null
+hello rootdir null
+hello eventadd {"tag":"EVENT","name":"EVENT"}
+hello locale {"file":"loc.txt","lang":"ru,de,jp-JP,fr"}
+hello locale "loc.txt"
+hello locale "i don't exist"
+hello api {"action":"create","api":"_extra_"}
+hello api {"action":"addverb","api":"_extra_","verb":"ping"}
+hello api {"action":"seal","api":"_extra_"}
+hello api {"action":"addverb","api":"_extra_","verb":"ping"}
+hello api {"action":"destroy","api":"_extra_"}
+_extra_ ping2 {"a":true}
+_extra_ ping3 {"b":false}
+_extra_ ping {"c":[1,2,3]}
+hello api {"action":"create","api":"extra"}
+extra api {"action":"addverb","verb":"blablabla"}
+extra api {"action":"addverb","verb":"ping"}
+extra api {"action":"addverb","verb":"ping2"}
+extra api {"action":"addverb","verb":"ping3"}
+extra api {"action":"addverb","verb":"q*"}
+extra api {"action":"delverb","verb":"blablabla"}
+extra api {"action":"addhandler","pattern":"*","closure":"*"}
+extra api {"action":"addhandler","pattern":"hello/*","closure":"hello/*"}
+extra call {"api":"hello","verb":"eventsub","args":{"tag":"EVENT"}}
+hello eventpush {"tag":"EVENT","data":[1,2,"hello"]}
+hello api {"action":"delhandler","api":"extra","pattern":"hello/*"}
+hello eventpush {"tag":"EVENT","data":[1,2,"hello"]}
+extra ping2 {"a":true}
+extra ping3 {"b":false}
+extra ping {"c":[1,2,3]}
+extra query {"c":[1,2,3]}
+extra blablabla {"c":[1,2,3]}
+extra api {"action":"addverb","verb":"ping"}
+extra ping {"c":[1,2,3]}
+hello api {"action":"destroy","api":"extra"}
+extra ping {"c":[1,2,3]}
 EOC
 
diff --git a/coverage/scripts/06-auto-so.sh b/coverage/scripts/06-auto-so.sh
new file mode 100755 (executable)
index 0000000..36214ee
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+$R/bin/afb-client -s -e $WSURL <<EOC
+salam ping true
+x-HELLO PING false
+salam pIngNull true
+salam PingBug true
+salam PiNgJsOn {"well":"formed","json":[1,2,3,4.5,true,false,null,"oups"]}
+salam subcall {"api":"salam","verb":"pingjson","args":[{"key1":"value1"}]}
+salam subcall {"api":"salam","verb":"subcall","args":{"api":"salam","verb":"pingjson","args":[{"key1":"value1"}]}}
+salam subcallsync {"api":"salam","verb":"pingjson","args":[{"key1":"value1"}]}
+salam subcallsync {"api":"salam","verb":"subcall","args":{"api":"salam","verb":"pingjson","args":[{"key1":"value1"}]}}
+salam subcall {"api":"salam","verb":"subcallsync","args":{"api":"salam","verb":"pingjson","args":[{"key1":"value1"}]}}
+salam subcallsync {"api":"salam","verb":"subcallsync","args":{"api":"salam","verb":"pingjson","args":[{"key1":"value1"}]}}
+salam eventadd {"tag":"ev1","name":"event-A"}
+salam eventadd {"tag":"ev2","name":"event-B"}
+salam eventpush {"tag":"ev1","data":[1,2,"salam"]}
+salam eventpush {"tag":"ev2","data":{"item":0}}
+salam eventsub {"tag":"ev2"}
+salam eventpush {"tag":"ev1","data":[1,2,"salam"]}
+salam eventpush {"tag":"ev2","data":{"item":0}}
+salam eventsub {"tag":"ev1"}
+salam subcall {"api":"salam","verb":"eventpush","args":{"tag":"ev1","data":[1,2,"salam"]}}
+salam subcall {"api":"salam","verb":"eventpush","args":{"tag":"ev2","data":{"item":0}}}
+salam subcallsync {"api":"salam","verb":"eventpush","args":{"tag":"ev1","data":[1,2,"salam"]}}
+salam subcallsync {"api":"salam","verb":"eventpush","args":{"tag":"ev2","data":{"item":0}}}
+salam eventunsub {"tag":"ev2"}
+salam eventpush {"tag":"ev1","data":[1,2,"salam"]}
+salam eventpush {"tag":"ev2","data":{"item":0}}
+salam eventdel {"tag":"ev1"}
+salam eventpush {"tag":"ev1","data":[1,2,"salam"]}
+salam eventpush {"tag":"ev2","data":{"item":0}}
+salam eventdel {"tag":"ev2"}
+EOC
+
index 34c2d30..bac4da5 100755 (executable)
@@ -3,18 +3,72 @@
 export R=$(realpath $(dirname $0)/..)
 export PATH="$R/bin:$R/scripts:$PATH"
 
-$R/bin/afb-daemon-cov --help > /dev/null
+cd $R/bin
 
-$R/bin/afb-daemon-cov --version > /dev/null
+lcov -c -i -d $R/bin -o $R/lcov-out.info
 
-$R/bin/afb-daemon-cov --fake-option > /dev/null
+mk() {
+       echo
+       echo "*******************************************************************"
+       echo "** $*"
+       echo "*******************************************************************"
+       lcov -c -i -d $R/bin -o $R/fake.info
+       "$@"
+       lcov -c -d $R/bin -o $R/tmp.info
+       mv $R/lcov-out.info $R/previous.info
+       lcov -a $R/tmp.info -a $R/previous.info -o $R/lcov-out.info
+       rm $R/previous.info $R/fake.info  $R/tmp.info
+}
 
+mkdir /tmp/ldpaths
+export AFB_LDPATHS=/tmp/ldpaths
+export AFB_TRACEAPI=no
+
+##########################################################
+# test to check options
+##########################################################
+mk $R/bin/afb-daemon-cov --help
+
+mk $R/bin/afb-daemon-cov --version
+
+mk $R/bin/afb-daemon-cov --no-httpd --fake-option
+
+mk $R/bin/afb-daemon-cov --daemon --session-max
+
+mk $R/bin/afb-daemon-cov --ws-client fake --session-max toto
+
+mk $R/bin/afb-daemon-cov --foreground --port -55
+
+mk $R/bin/afb-daemon-cov --foreground --port 9999999
+
+mk $R/bin/afb-daemon-cov --no-ldpath --traceapi fake
+
+mk $R/bin/afb-daemon-cov --traceditf all --tracesvc all --log error,alarm
+
+LISTEN_FDNAMES=toto,demat LISTEN_FDS=5 mk $R/bin/afb-daemon-cov --no-ldpath --binding $R/bin/demat.so --ws-server sd:demat --call "demat/exit:0"
+
+mk $R/bin/afb-daemon-cov --weak-ldpaths $R/ldpath/weak --binding $R/bin/demat.so --ws-server sd:demat --call "demat/exit:0"
+
+##########################################################
+# test of the bench
+##########################################################
+mk $R/bin/test-apiset
+
+mk $R/bin/test-session
+
+mk $R/bin/test-wrap-json
+
+##########################################################
+# true life test
+##########################################################
+mk \
 valgrind \
        --log-file=$R/valgrind.out \
        --trace-children=no \
        --track-fds=yes \
        --leak-check=full \
-        --show-leak-kinds=all \
+       --show-leak-kinds=all \
+       --num-callers=50 \
 $R/bin/afb-daemon-cov \
        --verbose \
        --verbose \
@@ -29,6 +83,7 @@ $R/bin/afb-daemon-cov \
        --log error,warning,notice,info,debug,critical,alert-error,warning,notice,info,debug,critical,alert+error,warning,notice,info,debug,critical,alert \
        --foreground \
        --name binder-cov \
+       --port 8888 \
        --roothttp $R/www \
        --rootbase /opx \
        --rootapi /api \
@@ -41,7 +96,6 @@ $R/bin/afb-daemon-cov \
        --rootdir . \
        --ldpaths $R/ldpath/strong \
        --binding $R/bin/demat.so \
-       --weak-ldpaths $R/ldpath/weak \
        --auto-api $R/apis/auto \
        --token HELLO \
        --random-token \
@@ -50,6 +104,7 @@ $R/bin/afb-daemon-cov \
        --traceapi all \
        --traceses all \
        --traceevt all \
+       --monitoring \
        --call demat/ping:true \
        --ws-server unix:$R/apis/ws/hello \
        --ws-server unix:$R/apis/ws/salut \
diff --git a/src/tests/wrap-json/CMakeLists.txt b/src/tests/wrap-json/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7e69f74
--- /dev/null
@@ -0,0 +1,23 @@
+###########################################################################
+# Copyright (C) 2017, 2018 "IoT.bzh"
+#
+# author: José Bollo <jose.bollo@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+add_executable(test-wrap-json test-wrap-json.c)
+target_include_directories(test-wrap-json PRIVATE ../..)
+target_link_libraries(test-wrap-json afb-lib ${link_libraries})
+add_test(NAME wrap-json COMMAND test-wrap-json)
+
diff --git a/src/tests/wrap-json/test-wrap-json.c b/src/tests/wrap-json/test-wrap-json.c
new file mode 100644 (file)
index 0000000..83ed315
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ Copyright (C) 2016, 2017, 2018 "IoT.bzh"
+
+ author: José Bollo <jose.bollo@iot.bzh>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "wrap-json.h"
+
+
+void tclone(struct json_object *object)
+{
+       struct json_object *o;
+
+       o = wrap_json_clone(object);
+       if (!wrap_json_equal(object, o))
+               printf("ERROR in clone or equal: %s VERSUS %s\n", json_object_to_json_string(object), json_object_to_json_string(o));
+       json_object_put(o);
+
+       o = wrap_json_clone_deep(object);
+       if (!wrap_json_equal(object, o))
+               printf("ERROR in clone_deep or equal: %s VERSUS %s\n", json_object_to_json_string(object), json_object_to_json_string(o));
+       json_object_put(o);
+}
+
+void p(const char *desc, ...)
+{
+       int rc;
+       va_list args;
+       struct json_object *result;
+
+       va_start(args, desc);
+       rc = wrap_json_vpack(&result, desc, args);
+       va_end(args);
+       if (!rc)
+               printf("  SUCCESS %s\n\n", json_object_to_json_string(result));
+       else
+               printf("  ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc));
+       tclone(result);
+       json_object_put(result);
+}
+
+const char *xs[10];
+int *xi[10];
+int64_t *xI[10];
+double *xf[10];
+struct json_object *xo[10];
+size_t xz[10];
+uint8_t *xy[10];
+
+void u(const char *value, const char *desc, ...)
+{
+       unsigned m, k;
+       int rc;
+       va_list args;
+       struct json_object *object, *o;
+
+       memset(xs, 0, sizeof xs);
+       memset(xi, 0, sizeof xi);
+       memset(xI, 0, sizeof xI);
+       memset(xf, 0, sizeof xf);
+       memset(xo, 0, sizeof xo);
+       memset(xy, 0, sizeof xy);
+       memset(xz, 0, sizeof xz);
+       object = json_tokener_parse(value);
+       va_start(args, desc);
+       rc = wrap_json_vunpack(object, desc, args);
+       va_end(args);
+       if (rc)
+               printf("  ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc));
+       else {
+               value = NULL;
+               printf("  SUCCESS");
+               va_start(args, desc);
+               k = m = 0;
+               while(*desc) {
+                       switch(*desc) {
+                       case '{': m = (m << 1) | 1; k = 1; break;
+                       case '}': m = m >> 1; k = m&1; break;
+                       case '[': m = m << 1; k = 0; break;
+                       case ']': m = m >> 1; k = m&1; break;
+                       case 's': printf(" s:%s", k ? va_arg(args, const char*) : *(va_arg(args, const char**)?:&value)); k ^= m&1; break;
+                       case '%': printf(" %%:%zu", *va_arg(args, size_t*)); k = m&1; break;
+                       case 'n': printf(" n"); k = m&1; break;
+                       case 'b': printf(" b:%d", *va_arg(args, int*)); k = m&1; break;
+                       case 'i': printf(" i:%d", *va_arg(args, int*)); k = m&1; break;
+                       case 'I': printf(" I:%lld", *va_arg(args, int64_t*)); k = m&1; break;
+                       case 'f': printf(" f:%f", *va_arg(args, double*)); k = m&1; break;
+                       case 'F': printf(" F:%f", *va_arg(args, double*)); k = m&1; break;
+                       case 'o': printf(" o:%s", json_object_to_json_string(*va_arg(args, struct json_object**))); k = m&1; break;
+                       case 'O': o = *va_arg(args, struct json_object**); printf(" O:%s", json_object_to_json_string(o)); json_object_put(o); k = m&1; break;
+                       case 'y':
+                       case 'Y': {
+                               uint8_t *p = *va_arg(args, uint8_t**);
+                               size_t s = *va_arg(args, size_t*);
+                               printf(" y/%d:%.*s", (int)s, (int)s, (char*)p);
+                               k ^= m&1;
+                               break;
+                               }
+                       default: break;
+                       }
+                       desc++;
+               }
+               va_end(args);
+               printf("\n\n");
+       }
+       tclone(object);
+       json_object_put(object);
+}
+
+void c(const char *sx, const char *sy, int e, int c)
+{
+       int re, rc;
+       struct json_object *jx, *jy;
+
+       jx = json_tokener_parse(sx);
+       jy = json_tokener_parse(sy);
+
+       re = wrap_json_cmp(jx, jy);
+       rc = wrap_json_contains(jx, jy);
+
+       printf("compare(%s)(%s)\n", sx, sy);
+       printf("   -> %d / %d\n", re, rc);
+
+       if (!re != !!e)
+               printf("  ERROR should be %s\n", e ? "equal" : "different");
+       if (!rc != !c)
+               printf("  ERROR should %scontain\n", c ? "" : "not ");
+
+       printf("\n");
+}
+
+#define P(...) do{ printf("pack(%s)\n",#__VA_ARGS__); p(__VA_ARGS__); } while(0)
+#define U(...) do{ printf("unpack(%s)\n",#__VA_ARGS__); u(__VA_ARGS__); } while(0)
+
+int main()
+{
+       char buffer[4] = {'t', 'e', 's', 't'};
+
+       P("n");
+       P("b", 1);
+       P("b", 0);
+       P("i", 1);
+       P("I", (uint64_t)0x123456789abcdef);
+       P("f", 3.14);
+       P("s", "test");
+       P("s?", "test");
+       P("s?", NULL);
+       P("s#", "test asdf", 4);
+       P("s%", "test asdf", (size_t)4);
+       P("s#", buffer, 4);
+       P("s%", buffer, (size_t)4);
+       P("s++", "te", "st", "ing");
+       P("s#+#+", "test", 1, "test", 2, "test");
+       P("s%+%+", "test", (size_t)1, "test", (size_t)2, "test");
+       P("{}", 1.0);
+       P("[]", 1.0);
+       P("o", json_object_new_int(1));
+       P("o?", json_object_new_int(1));
+       P("o?", NULL);
+       P("O", json_object_new_int(1));
+       P("O?", json_object_new_int(1));
+       P("O?", NULL);
+       P("{s:[]}", "foo");
+       P("{s+#+: []}", "foo", "barbar", 3, "baz");
+       P("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL);
+       P("{s:**}", "a", NULL);
+       P("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL);
+       P("[i,i,i]", 0, 1, 2);
+       P("[s,o,O]", NULL, NULL, NULL);
+       P("[**]", NULL);
+       P("[s*,o*,O*]", NULL, NULL, NULL);
+       P(" s ", "test");
+       P("[ ]");
+       P("[ i , i,  i ] ", 1, 2, 3);
+       P("{\n\n1");
+       P("[}");
+       P("{]");
+       P("[");
+       P("{");
+       P("[i]a", 42);
+       P("ia", 42);
+       P("s", NULL);
+       P("+", NULL);
+       P(NULL);
+       P("{s:i}", NULL, 1);
+       P("{ {}: s }", "foo");
+       P("{ s: {},  s:[ii{} }", "foo", "bar", 12, 13);
+       P("[[[[[   [[[[[  [[[[ }]]]] ]]]] ]]]]]");
+       P("y", "???????hello>>>>>>>", (size_t)19);
+       P("Y", "???????hello>>>>>>>", (size_t)19);
+       P("{sy?}", "foo", "hi", (size_t)2);
+       P("{sy?}", "foo", NULL, 0);
+       P("{sy*}", "foo", "hi", (size_t)2);
+       P("{sy*}", "foo", NULL, 0);
+
+       U("true", "b", &xi[0]);
+       U("false", "b", &xi[0]);
+       U("null", "n");
+       U("42", "i", &xi[0]);
+       U("123456789", "I", &xI[0]);
+       U("3.14", "f", &xf[0]);
+       U("12345", "F", &xf[0]);
+       U("3.14", "F", &xf[0]);
+       U("\"foo\"", "s", &xs[0]);
+       U("\"foo\"", "s%", &xs[0], &xz[0]);
+       U("{}", "{}");
+       U("[]", "[]");
+       U("{}", "o", &xo[0]);
+       U("{}", "O", &xo[0]);
+       U("{\"foo\":42}", "{si}", "foo", &xi[0]);
+       U("[1,2,3]", "[i,i,i]", &xi[0], &xi[1], &xi[2]);
+       U("{\"a\":1,\"b\":2,\"c\":3}", "{s:i, s:i, s:i}", "a", &xi[0], "b", &xi[1], "c", &xi[2]);
+       U("42", "z");
+       U("null", "[i]");
+       U("[]", "[}");
+       U("{}", "{]");
+       U("[]", "[");
+       U("{}", "{");
+       U("[42]", "[i]a", &xi[0]);
+       U("42", "ia", &xi[0]);
+       U("[]", NULL);
+       U("\"foo\"", "s", NULL);
+       U("42", "s", NULL);
+       U("42", "n");
+       U("42", "b", NULL);
+       U("42", "f", NULL);
+       U("42", "[i]", NULL);
+       U("42", "{si}", "foo", NULL);
+       U("\"foo\"", "n");
+       U("\"foo\"", "b", NULL);
+       U("\"foo\"", "i", NULL);
+       U("\"foo\"", "I", NULL);
+       U("\"foo\"", "f", NULL);
+       U("\"foo\"", "F", NULL);
+       U("true", "s", NULL);
+       U("true", "n");
+       U("true", "i", NULL);
+       U("true", "I", NULL);
+       U("true", "f", NULL);
+       U("true", "F", NULL);
+       U("[42]", "[ii]", &xi[0], &xi[1]);
+       U("{\"foo\":42}", "{si}", NULL, &xi[0]);
+       U("{\"foo\":42}", "{si}", "baz", &xi[0]);
+       U("[1,2,3]", "[iii!]", &xi[0], &xi[1], &xi[2]);
+       U("[1,2,3]", "[ii!]", &xi[0], &xi[1]);
+       U("[1,2,3]", "[ii]", &xi[0], &xi[1]);
+       U("[1,2,3]", "[ii*]", &xi[0], &xi[1]);
+       U("{\"foo\":42,\"baz\":45}", "{sisi}", "baz", &xi[0], "foo", &xi[1]);
+       U("{\"foo\":42,\"baz\":45}", "{sisi*}", "baz", &xi[0], "foo", &xi[1]);
+       U("{\"foo\":42,\"baz\":45}", "{sisi!}", "baz", &xi[0], "foo", &xi[1]);
+       U("{\"foo\":42,\"baz\":45}", "{si}", "baz", &xi[0], "foo", &xi[1]);
+       U("{\"foo\":42,\"baz\":45}", "{si*}", "baz", &xi[0], "foo", &xi[1]);
+       U("{\"foo\":42,\"baz\":45}", "{si!}", "baz", &xi[0], "foo", &xi[1]);
+       U("[1,{\"foo\":2,\"bar\":null},[3,4]]", "[i{sisn}[ii]]", &xi[0], "foo", &xi[1], "bar", &xi[2], &xi[3]);
+       U("[1,2,3]", "[ii!i]", &xi[0], &xi[1], &xi[2]);
+       U("[1,2,3]", "[ii*i]", &xi[0], &xi[1], &xi[2]);
+       U("{\"foo\":1,\"bar\":2}", "{si!si}", "foo", &xi[1], "bar", &xi[2]);
+       U("{\"foo\":1,\"bar\":2}", "{si*si}", "foo", &xi[1], "bar", &xi[2]);
+       U("{\"foo\":{\"baz\":null,\"bar\":null}}", "{s{sn!}}", "foo", "bar");
+       U("[[1,2,3]]", "[[ii!]]", &xi[0], &xi[1]);
+       U("{}", "{s?i}", "foo", &xi[0]);
+       U("{\"foo\":1}", "{s?i}", "foo", &xi[0]);
+       U("{}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]);
+       U("{\"foo\":[1,2]}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]);
+       U("{\"bar\":{\"baz\":{\"quux\":15}}}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]);
+       U("{\"foo\":{\"bar\":4}}", "{s?{s?i}}", "foo", "bar", &xi[0]);
+       U("{\"foo\":{}}", "{s?{s?i}}", "foo", "bar", &xi[0]);
+       U("{}", "{s?{s?i}}", "foo", "bar", &xi[0]);
+       U("{\"foo\":42,\"baz\":45}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]);
+       U("{\"foo\":42}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]);
+
+       U("\"Pz8_Pz8_P2hlbGxvPj4-Pj4-Pg\"", "y", &xy[0], &xz[0]);
+       U("\"\"", "y", &xy[0], &xz[0]);
+       U("null", "y", &xy[0], &xz[0]);
+       U("{\"foo\":\"Pz8_Pz8_P2hlbGxvPj4-Pj4-Pg\"}", "{s?y}", "foo", &xy[0], &xz[0]);
+       U("{\"foo\":\"\"}", "{s?y}", "foo", &xy[0], &xz[0]);
+       U("{}", "{s?y}", "foo", &xy[0], &xz[0]);
+
+       c("null", "null", 1, 1);
+       c("true", "true", 1, 1);
+       c("false", "false", 1, 1);
+       c("1", "1", 1, 1);
+       c("1.0", "1.0", 1, 1);
+       c("\"\"", "\"\"", 1, 1);
+       c("\"hi\"", "\"hi\"", 1, 1);
+       c("{}", "{}", 1, 1);
+       c("{\"a\":true,\"b\":false}", "{\"b\":false,\"a\":true}", 1, 1);
+       c("[]", "[]", 1, 1);
+       c("[1,true,null]", "[1,true,null]", 1, 1);
+
+       c("null", "true", 0, 0);
+       c("null", "false", 0, 0);
+       c("0", "1", 0, 0);
+       c("1", "0", 0, 0);
+       c("0", "true", 0, 0);
+       c("0", "false", 0, 0);
+       c("0", "null", 0, 0);
+
+       c("\"hi\"", "\"hello\"", 0, 0);
+       c("\"hello\"", "\"hi\"", 0, 0);
+
+       c("{}", "null", 0, 0);
+       c("{}", "true", 0, 0);
+       c("{}", "1", 0, 0);
+       c("{}", "1.0", 0, 0);
+       c("{}", "[]", 0, 0);
+       c("{}", "\"x\"", 0, 0);
+
+       c("[1,true,null]", "[1,true]", 0, 1);
+       c("{\"a\":true,\"b\":false}", "{\"a\":true}", 0, 1);
+       c("{\"a\":true,\"b\":false}", "{\"a\":true,\"c\":false}", 0, 0);
+       c("{\"a\":true,\"c\":false}", "{\"a\":true,\"b\":false}", 0, 0);
+       return 0;
+}
+