From e17ae412245ba9afb33ff6a0f1f665b4d66d4da4 Mon Sep 17 00:00:00 2001 From: Jose Bollo Date: Thu, 12 Jul 2018 10:59:48 +0200 Subject: [PATCH] coverage and test: Add tests coverage values: - lines: 70.5 % - functions: 76.3 % Change-Id: Iaf802e84bbfa57502bbbac8c3b567b14c01608b6 Signed-off-by: Jose Bollo --- bindings/samples/hello3.c | 312 +++++++++++++++++++- coverage/.gitignore | 11 + coverage/Makefile | 7 +- coverage/bin/Makefile | 122 ++++++-- coverage/bin/bug.c | 270 ++++++++++++++++++ coverage/bin/fake/monitoring | 1 + coverage/bin/loc.txt | 0 coverage/bin/locales/en/loc.txt | 0 coverage/bin/locales/fr-FR/loc.txt | 0 coverage/bin/locales/fr/loc.txt | 0 coverage/bin/locales/jp/loc.txt | 0 coverage/ldpath/weak/bug.so | 1 - coverage/ldpath/weak/bugs | 1 + coverage/scripts/00-trace.sh | 7 +- coverage/scripts/01-http.sh | 28 +- coverage/scripts/02-hello.sh | 44 +++ coverage/scripts/{03-x-hello.sh => 03-auto-ws.sh} | 0 coverage/scripts/06-auto-so.sh | 35 +++ coverage/scripts/run-test.sh | 65 ++++- src/tests/wrap-json/CMakeLists.txt | 23 ++ src/tests/wrap-json/test-wrap-json.c | 331 ++++++++++++++++++++++ 21 files changed, 1211 insertions(+), 47 deletions(-) create mode 120000 coverage/bin/fake/monitoring create mode 100644 coverage/bin/loc.txt create mode 100644 coverage/bin/locales/en/loc.txt create mode 100644 coverage/bin/locales/fr-FR/loc.txt create mode 100644 coverage/bin/locales/fr/loc.txt create mode 100644 coverage/bin/locales/jp/loc.txt delete mode 120000 coverage/ldpath/weak/bug.so create mode 120000 coverage/ldpath/weak/bugs rename coverage/scripts/{03-x-hello.sh => 03-auto-ws.sh} (100%) create mode 100755 coverage/scripts/06-auto-so.sh create mode 100644 src/tests/wrap-json/CMakeLists.txt create mode 100644 src/tests/wrap-json/test-wrap-json.c diff --git a/bindings/samples/hello3.c b/bindings/samples/hello3.c index f49b3365..477e47bd 100644 --- a/bindings/samples/hello3.c +++ b/bindings/samples/hello3.c @@ -18,14 +18,24 @@ #include #include #include +#include +#include +#include +#include #include #define AFB_BINDING_VERSION 3 #include +#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, diff --git a/coverage/.gitignore b/coverage/.gitignore index 43d04cb6..69f6ea4f 100644 --- a/coverage/.gitignore +++ b/coverage/.gitignore @@ -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 diff --git a/coverage/Makefile b/coverage/Makefile index 3f5f3ce3..30477a70 100644 --- a/coverage/Makefile +++ b/coverage/Makefile @@ -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 diff --git a/coverage/bin/Makefile b/coverage/bin/Makefile index 19bf254a..f9d561ed 100644 --- a/coverage/bin/Makefile +++ b/coverage/bin/Makefile @@ -4,41 +4,102 @@ 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%,$@) diff --git a/coverage/bin/bug.c b/coverage/bin/bug.c index ddebdef7..62646cec 100644 --- a/coverage/bin/bug.c +++ b/coverage/bin/bug.c @@ -1,6 +1,276 @@ +#include +#include +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 +const struct afb_binding_v2 afbBindingV2; + +#endif +/**************************************************************************/ +#if defined(BUG2) /* incomplete exports: afbBindingV2 miss */ + +#define AFB_BINDING_VERSION 0 +#include +struct afb_binding_data_v2 afbBindingV2data; + +#endif +/**************************************************************************/ +#if defined(BUG3) /* zero filled structure */ + +#define AFB_BINDING_VERSION 0 +#include +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 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 +struct afb_api_x3 *afbBindingV3root; + +#endif +/**************************************************************************/ +#if defined(BUG13) /* no afbBindingV3root nor afbBindingV3entry */ + +#define AFB_BINDING_VERSION 0 +#include +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 + +const struct afb_binding_v3 afbBindingV3; + +#endif +/**************************************************************************/ +#if defined(BUG15) /* bad api name */ + +#define AFB_BINDING_VERSION 3 +#include + +const struct afb_binding_v3 afbBindingV3 = { + .api = "bug 15" +}; + +#endif +/**************************************************************************/ +#if defined(BUG16) /* both entry and preinit */ + +#define AFB_BINDING_VERSION 3 +#include + +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 + +int afbBindingV3entry(struct afb_api_x3 *rootapi) { errno = EAGAIN; return -1; } +#endif +/**************************************************************************/ +#if defined(BUG18) /* preinit fails */ + +#define AFB_BINDING_VERSION 3 +#include + +const struct afb_binding_v3 afbBindingV3 = { + .api = "bug18", + .preinit = (void*)err +}; + +#endif +/**************************************************************************/ +#if defined(BUG19) /* preinit SEGV */ + +#define AFB_BINDING_VERSION 3 +#include + +const struct afb_binding_v3 afbBindingV3 = { + .api = "bug19", + .preinit = (void*)bug +}; + +#endif +/**************************************************************************/ +#if defined(BUG20) /* init fails */ + +#define AFB_BINDING_VERSION 3 +#include + +const struct afb_binding_v3 afbBindingV3 = { + .api = "bug20", + .init = (void*)err +}; + +#endif +/**************************************************************************/ +#if defined(BUG21) /* init SEGV */ + +#define AFB_BINDING_VERSION 3 +#include + +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 index 00000000..8cdac10f --- /dev/null +++ b/coverage/bin/fake/monitoring @@ -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 index 00000000..e69de29b diff --git a/coverage/bin/locales/en/loc.txt b/coverage/bin/locales/en/loc.txt new file mode 100644 index 00000000..e69de29b diff --git a/coverage/bin/locales/fr-FR/loc.txt b/coverage/bin/locales/fr-FR/loc.txt new file mode 100644 index 00000000..e69de29b diff --git a/coverage/bin/locales/fr/loc.txt b/coverage/bin/locales/fr/loc.txt new file mode 100644 index 00000000..e69de29b diff --git a/coverage/bin/locales/jp/loc.txt b/coverage/bin/locales/jp/loc.txt new file mode 100644 index 00000000..e69de29b diff --git a/coverage/ldpath/weak/bug.so b/coverage/ldpath/weak/bug.so deleted file mode 120000 index 01e7c27a..00000000 --- a/coverage/ldpath/weak/bug.so +++ /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 index 00000000..6ed8e22c --- /dev/null +++ b/coverage/ldpath/weak/bugs @@ -0,0 +1 @@ +../../bin/bugs \ No newline at end of file diff --git a/coverage/scripts/00-trace.sh b/coverage/scripts/00-trace.sh index 34a19bb0..27edc5b1 100755 --- a/coverage/scripts/00-trace.sh +++ b/coverage/scripts/00-trace.sh @@ -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 < /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 index 00000000..7e69f742 --- /dev/null +++ b/src/tests/wrap-json/CMakeLists.txt @@ -0,0 +1,23 @@ +########################################################################### +# Copyright (C) 2017, 2018 "IoT.bzh" +# +# author: José Bollo +# +# 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 index 00000000..83ed3156 --- /dev/null +++ b/src/tests/wrap-json/test-wrap-json.c @@ -0,0 +1,331 @@ +/* + Copyright (C) 2016, 2017, 2018 "IoT.bzh" + + author: José Bollo + + 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 +#include +#include + +#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; +} + -- 2.16.6