From 09b28beef413a18d17c7cb99eadbc7c2ac729f5e Mon Sep 17 00:00:00 2001
From: Jakob Olsson <jakob.olsson@iopsys.eu>
Date: Mon, 30 Sep 2019 14:52:38 +0200
Subject: [PATCH] add object, string and integer manipulation via string format

---
 CMakeLists.txt                 |  56 +++++++
 api.c                          | 261 +++++++++++++++++++++++++++++++++
 api.h                          |  16 ++
 cmake/modules/FindCMocka.cmake |  26 ++++
 test/CMakeLists.txt            |  26 ++++
 test/api_test.c                | 257 ++++++++++++++++++++++++++++++++
 6 files changed, 642 insertions(+)
 create mode 100644 CMakeLists.txt
 create mode 100644 api.c
 create mode 100644 api.h
 create mode 100644 cmake/modules/FindCMocka.cmake
 create mode 100644 test/CMakeLists.txt
 create mode 100644 test/api_test.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..605d998
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,56 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
+PROJECT(json-editor)
+
+#set version
+SET(MAJOR_VERSION 1)
+SET(MINOR_VERSION 0)
+SET(PATCH_VERSION 0)
+#EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "log HEAD --format=format:%H -1" OUTPUT_VARIABLE GIT_SHA1)
+
+#ADD_DEFINITIONS(-D_PROJECT_NAME="${PROJECT_NAME}")
+#ADD_DEFINITIONS(-D_PROJECT_VERSION="${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
+#ADD_DEFINITIONS(-D_GIT_SHA1="${GIT_SHA1}")
+
+#set compile flags
+SET(DEFAULT_FLAGS "--std=gnu99")
+SET(RELEASE_FLAGS "-Wno-long-long -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Werror-implicit-function-declaration -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common -Wsign-compare -Wunused-result")
+
+SET(CMAKE_C_FLAGS_DEBUG          "${CMAKE_C_FLAGS_DEBUG} ${DEFAULT_FLAGS} -Wall")
+SET(CMAKE_C_FLAGS_RELEASE        "${CMAKE_C_FLAGS_RELEASE} -Os ${DEFAULT_FLAGS} ${RELEASE_FLAGS}")
+SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} ${DEFAULT_FLAGS} ${RELEASE_FLAGS}")
+SET(CMAKE_C_FLAGS_MINSIZEREL     "${CMAKE_C_FLAGS_MINSIZEREL}  ${DEFAULT_FLAGS} ${RELEASE_FLAGS}")
+
+#INCLUDE(LogOptions.cmake)
+
+#create executable
+FILE(GLOB SOURCES "*.c" "*.h")
+ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
+
+#link libraries
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules")
+find_library(JSON_LIBRARIES NAMES json-c)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${JSON_LIBRARIES})
+
+#testing
+IF(CMAKE_BUILD_TYPE STREQUAL Debug)
+	OPTION(ENABLE_BUILD_TESTS "Build tests" ON)
+#	OPTION(ENABLE_VALGRIND_TESTS "Build tests with valgrind" ON)
+ELSE()
+	OPTION(ENABLE_BUILD_TESTS "Build tests" OFF)
+#	OPTION(ENABLE_VALGRIND_TESTS "Build tests with valgrind" OFF)
+ENDIF()
+
+IF(ENABLE_BUILD_TESTS)
+	FIND_PACKAGE(CMocka)
+	if(CMOCKA_FOUND)
+		ADD_LIBRARY(${PROJECT_NAME}-api SHARED ${SOURCES})
+		MESSAGE("-- Building tests")
+		ENABLE_TESTING()
+		ADD_SUBDIRECTORY(test)
+	ELSE(CMOCKA_FOUND)
+		MESSAGE("-- CMocka not found")
+	ENDIF(CMOCKA_FOUND)
+ENDIF(ENABLE_BUILD_TESTS)
+
+#install
+#INSTALL(TARGETS imonitor RUNTIME DESTINATION bin)
diff --git a/api.c b/api.c
new file mode 100644
index 0000000..d697ed5
--- /dev/null
+++ b/api.c
@@ -0,0 +1,261 @@
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+#include <json-c/json.h>
+
+#include "api.h"
+
+/* will allocate memory! remember to free! */
+char *get_file(const char *path)
+{
+	FILE *f;
+	size_t len, nread;
+	char *buffer;
+
+	f = fopen(path, "r");
+	if (!f)
+		goto out;
+
+	if (fseek(f, 0, SEEK_END))
+		goto out_close;
+
+	len = ftell(f);
+	if (len < 0)
+		goto out_close;
+
+	if (fseek(f, 0, SEEK_SET))
+		goto out_close;
+
+	buffer = calloc(1, len);
+	if (!buffer)
+		goto out_close;
+
+	nread = fread(buffer, sizeof(char), len, f);
+	if (nread < len)
+		goto out_free;
+
+	fclose(f);
+	return buffer;
+out_free:
+	free(buffer);
+out_close:
+	fclose(f);
+out:
+	return NULL;
+}
+
+struct json_object *path_to_obj(const char *path)
+{
+	struct json_object *obj;
+	char *file_str;
+
+	file_str = get_file(path);
+	if (!file_str)
+		goto out;
+
+	obj = json_tokener_parse(file_str);
+
+	free(file_str);
+	return obj;
+out:
+	return NULL;
+}
+
+int set_by_string(char *fmt, struct json_object **src, char *val, enum json_type type)
+{
+	struct json_object *ptr, *tmp, *tar;
+	const char *delimiter = ".";
+	char buffer[1024] = {0};
+	char *p, *prev = fmt;
+
+	if (!*src) {
+		printf("lets allocate new object\n");
+		*src = json_object_new_object();
+	}
+
+	ptr = tmp = *src;
+
+	strcpy(buffer, fmt);
+
+	p = strtok(buffer,delimiter);
+	while (p != NULL) {
+		prev = p;
+		p = strtok(NULL, delimiter);
+
+		/* if next key exists, parse prev key, let switch-case add/alter last key */
+		if (p) {
+			json_object_object_get_ex(tmp, prev, &ptr);
+
+			printf("p=%s\n", p);
+
+			/* TODO: if prev contains [x], get idx of x */
+			char *open_brace = strchr(prev, '[');
+			printf("open_brace=%s\n", open_brace);
+			if (open_brace) {
+				char *close_brace = strchr(open_brace, ']');
+				if (close_brace) {
+					int len = close_brace - open_brace;
+					printf("len=%d\n", len);
+					char *idx_str = calloc(1, len);
+					strncpy(idx_str, open_brace + 1, len-1);
+					printf("idx_str = %s\n", idx_str);
+					int idx = atoi(idx_str);
+					printf("idx = %d\n", idx);
+					*open_brace = '\0';
+					printf("prev=%s\n", prev);
+
+					if (ptr && json_object_get_type(ptr) == json_type_array) {
+						ptr = json_object_array_get_idx(ptr, idx);
+					} else {
+						ptr = json_object_new_array();
+						json_object_object_add(tmp, prev, ptr);
+					}
+				}
+			}
+
+			/* create prev object if it does not exist */
+			if (!ptr) {
+				ptr = json_object_new_object();
+				json_object_object_add(tmp, prev, ptr);
+			}
+		}
+
+		tmp = ptr;
+	}
+
+	json_object_object_get_ex(ptr, prev, &tar);
+
+	switch(type) {
+	case json_type_object:
+	{
+		struct json_object *parsed_val = json_tokener_parse(val);
+
+		if (!parsed_val) {
+			fprintf(stderr, "Invalid object format\n");
+			return -1;
+		} else if (type != json_object_get_type(parsed_val)) {
+			fprintf(stderr, "Types don't match!\n");
+			return -1;
+		}
+
+		json_object_object_foreach(parsed_val, key1, val1)
+			json_object_object_add(*src, key1, val1);
+	}
+	break;
+	case json_type_string:
+		json_object_object_add(ptr, prev, json_object_new_string(val));
+	break;
+	case json_type_int:
+		json_object_object_add(ptr, prev, json_object_new_int(atoi(val)));
+	break;
+	case json_type_array:
+	{
+		struct json_object *parsed_val = json_tokener_parse(val);
+
+		if (!parsed_val) {
+			fprintf(stderr, "Invalid array format\n");
+			return -1;
+		} else if (type != json_object_get_type(parsed_val)) {
+			fprintf(stderr, "Types don't match!\n");
+			return -1;
+		}
+
+		json_object_object_add(ptr, prev, parsed_val);
+	}
+	break;
+	case json_type_double:
+		json_object_object_add(ptr, prev, json_object_new_int(atof(val)));
+	break;
+	case json_type_boolean:
+	{
+		bool bool_val = false;
+
+		if (strncasecmp(val, "true", 4) || atoi(val) == 1)
+			bool_val = true;
+
+
+		json_object_object_add(ptr, prev, json_object_new_boolean(bool_val));
+	}
+	break;
+	default:
+		fprintf(stderr, "Not valid input type!\n");
+	break;
+	}
+
+	return 0;
+}
+
+int set(char *fmt, struct json_object *src, struct json_object *val)
+{
+	const char *delimiter = ".";
+	char buffer[1024] = {0};
+	char *p;
+
+	if (json_object_get_type(val) != json_type_object) {
+		fprintf(stderr, "object must be of type object!\n");
+		return -1;
+	}
+
+	src = get(fmt, src);
+
+	strcpy(buffer, fmt);
+
+	/* TODO: put this in some separate function */
+	for (p = strtok(buffer,delimiter); p != NULL; p = strtok(NULL, delimiter)) {
+		struct json_object *ptr, *tmp = src;
+
+		json_object_object_get_ex(tmp, p, &ptr);
+		if (!ptr) {
+			ptr = json_object_new_object();
+			json_object_object_add(tmp, p, ptr);
+		}
+
+		tmp = ptr;
+	}
+
+	json_object_object_foreach(val, key, val1)
+		json_object_object_add(src, key, val1);
+
+	return 0;
+}
+
+struct json_object *get(char *fmt, struct json_object *src)
+{
+	struct json_object *ptr, *tmp = src;
+	const char *delimiter = ".";
+	char buffer[1024] = {0};
+
+	strcpy(buffer, fmt);
+
+	for (char *p = strtok(buffer,delimiter); p != NULL; p = strtok(NULL, delimiter)) {
+		json_object_object_get_ex(tmp, p, &ptr);
+		tmp = ptr;
+	}
+
+	return ptr;
+}
+
+int main()
+{
+	struct json_object *obj = path_to_obj("/home/jakob/git/json-editor-api/test.json");
+	struct json_object *ptr;
+
+	printf("%s %d obj=%s\n", __func__, __LINE__, json_object_get_string(obj));
+
+	ptr = get("nested", obj);
+
+	printf("%s\n", json_object_get_string(ptr));
+
+	set_by_string("nested.api[1].test", &obj, "1", json_type_string);
+
+	printf("%s\n", json_object_get_string(obj));
+
+	json_object_put(obj);
+	return 0;
+}
+
diff --git a/api.h b/api.h
new file mode 100644
index 0000000..418688c
--- /dev/null
+++ b/api.h
@@ -0,0 +1,16 @@
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+#include <json-c/json.h>
+
+char *get_file(const char *path);
+struct json_object *path_to_obj(const char *path);
+int set_by_string(char *fmt, struct json_object **src, char *val, enum json_type type);
+int set(char *fmt, struct json_object *src, struct json_object *val);
+
+struct json_object *get(char *fmt, struct json_object *src);
diff --git a/cmake/modules/FindCMocka.cmake b/cmake/modules/FindCMocka.cmake
new file mode 100644
index 0000000..65e12fd
--- /dev/null
+++ b/cmake/modules/FindCMocka.cmake
@@ -0,0 +1,26 @@
+#  CMOCKA_FOUND - System has CMocka
+#  CMOCKA_INCLUDE_DIRS - The CMocka include directories
+#  CMOCKA_LIBRARIES - The libraries needed to use CMocka
+#  CMOCKA_DEFINITIONS - Compiler switches required for using CMocka
+
+find_package(PkgConfig QUIET)
+if(PKG_CONFIG_FOUND)
+    pkg_check_modules(PC_CMOCKA QUIET cmocka)
+    set(CMOCKA_DEFINITIONS ${PC_CMOCKA_CFLAGS_OTHER})
+endif()
+
+find_path(CMOCKA_INCLUDE_DIR cmocka.h
+          HINTS ${PC_CMOCKA_INCLUDEDIR} ${PC_CMOCKA_INCLUDE_DIRS}
+          PATH_SUFFIXES cmocka)
+
+find_library(CMOCKA_LIBRARY NAMES cmocka
+             HINTS ${PC_CMOCKA_LIBDIR} ${PC_CMOCKA_LIBRARY_DIRS})
+
+set(CMOCKA_LIBRARIES ${CMOCKA_LIBRARY})
+set(CMOCKA_INCLUDE_DIRS ${CMOCKA_INCLUDE_DIR})
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(cmocka  DEFAULT_MSG
+                                  CMOCKA_LIBRARY CMOCKA_INCLUDE_DIR)
+
+mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARY)
\ No newline at end of file
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..9df0139
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,26 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
+#INCLUDE_DIRECTORIES ("${PROJECT_SOURCE_DIR}/src")
+INCLUDE_DIRECTORIES ("${PROJECT_SOURCE_DIR}")
+#FILE(COPY files DESTINATION .)
+
+find_library(JSON_LIBRARIES NAMES json-c)
+
+SET(api_tests api_test)
+FOREACH(test_name IN LISTS api_tests)
+    ADD_EXECUTABLE(${test_name} ${test_name}.c)
+	TARGET_LINK_LIBRARIES(
+		${test_name}
+		${CMOCKA_LIBRARIES}
+		json-editor-api
+		${JSON_LIBRARIES}
+	)
+	ADD_TEST(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
+ENDFOREACH(test_name)
+
+#SET(COVERAGE_EXCLUDES 'src/main.c' '/usr/include/*')
+
+#SETUP_TARGET_FOR_COVERAGE(
+#    NAME test_coverage
+#	EXECUTABLE ctest
+#	DEPENDENCIES owsd-api
+#)
diff --git a/test/api_test.c b/test/api_test.c
new file mode 100644
index 0000000..f7af316
--- /dev/null
+++ b/test/api_test.c
@@ -0,0 +1,257 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <json-c/json.h>
+#include <libwebsockets.h>
+
+#include "api.h"
+
+struct json_object *file_obj, *modify_obj;
+
+static void test_cfg_parse_success(void **state)
+{
+	(void) state; /* unused */
+
+	struct json_object *file = path_to_obj("/home/jakob/git/json-editor-api/test.json");
+
+    struct json_object *obj = json_object_new_object();
+    struct json_object *tot = json_object_new_object();
+
+    json_object_object_add(obj, "api", json_object_new_string("test2"));
+    json_object_object_add(tot, "nested", obj);
+    json_object_object_add(tot, "test", json_object_new_string("success"));
+
+    assert_int_equal(1, json_object_equal(file, tot));
+
+}
+
+static void test_cfg_parse_fail(void **state)
+{
+	(void) state; /* unused */
+
+	struct json_object *obj = path_to_obj("NON_EXISTENT_FILE");
+	assert_null(obj);
+	if (obj)
+		json_object_put(obj);
+}
+
+static void test_build_from_scratch(void **state)
+{
+	(void) state; /* unused */
+
+	struct json_object *file = path_to_obj("/home/jakob/git/json-editor-api/test.json");
+    struct json_object *jobj = NULL;
+
+    set_by_string("", &jobj, "{ \"test\":\"success\", \"nested\": { \"api\":\"test2\"} }", json_type_object);
+
+    assert_int_equal(1, json_object_equal(file, jobj));
+
+}
+
+static void test_json_add_object(void **state)
+{
+    (void) state;
+
+	//struct json_object *obj = json_object_new_object();
+    json_object_object_add(file_obj, "test2", json_object_new_string("success"));
+    //json_object_object_add(file_obj, "string", json_object_new_string("1"));
+
+    set_by_string("string", &modify_obj, "{\"test2\":\"success\"}", json_type_object);
+
+    printf("file_obj=%s\n", json_object_get_string(file_obj));
+    printf("modify_obj=%s\n", json_object_get_string(modify_obj));
+
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+}
+
+static void test_json_add_array(void **state)
+{
+    (void) state;
+
+	struct json_object *arr = json_object_new_array();
+
+    json_object_array_add(arr, json_object_new_int(1));
+    json_object_array_add(arr, json_object_new_int(2));
+    json_object_array_add(arr, json_object_new_int(3));
+    json_object_object_add(file_obj, "ints", arr);
+    //json_object_object_add(file_obj, "string", json_object_new_string("1"));
+
+    set_by_string("ints", &modify_obj, "[ 1, 2, 3 ]", json_type_array);
+
+    printf("file_obj=%s\n", json_object_get_string(file_obj));
+    printf("modify_obj=%s\n", json_object_get_string(modify_obj));
+
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+}
+
+static void test_json_add_multi_types(void **state)
+{
+    (void) state;
+
+	struct json_object *arr = json_object_new_array();
+    struct json_object *obj = json_object_new_object();
+    struct json_object *nested = json_object_new_object();
+
+    json_object_object_add(nested, "integer", json_object_new_int(1));
+    json_object_object_add(obj, "nested1", nested);
+    json_object_object_add(file_obj, "nested0", obj);
+
+
+    json_object_array_add(arr, json_object_new_int(1));
+    json_object_array_add(arr, json_object_new_int(2));
+    json_object_array_add(arr, json_object_new_int(3));
+    json_object_object_add(file_obj, "ints", arr);
+
+    json_object_object_add(file_obj, "string", json_object_new_string("1"));
+    json_object_object_add(file_obj, "integer", json_object_new_int(1));
+
+    set_by_string("nested0.nested1.integer", &modify_obj, "1", json_type_int);
+    set_by_string("ints", &modify_obj, "[ 1, 2, 3 ]", json_type_array);
+    set_by_string("string", &modify_obj, "1", json_type_string);
+    set_by_string("integer", &modify_obj, "1", json_type_int);
+
+    printf("file_obj=%s\n", json_object_get_string(file_obj));
+    printf("modify_obj=%s\n", json_object_get_string(modify_obj));
+
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+}
+
+static void test_json_add_multi_obj(void **state)
+{
+    (void) state;
+
+	struct json_object *arr = json_object_new_array();
+    struct json_object *obj = json_object_new_object();
+    struct json_object *nested = json_object_new_object();
+
+    json_object_object_add(nested, "integer", json_object_new_int(1));
+    json_object_object_add(obj, "nested1", nested);
+    json_object_object_add(file_obj, "nested0", obj);
+
+
+    json_object_array_add(arr, json_object_new_int(1));
+    json_object_array_add(arr, json_object_new_int(2));
+    json_object_array_add(arr, json_object_new_int(3));
+    json_object_object_add(file_obj, "ints", arr);
+
+    json_object_object_add(file_obj, "string", json_object_new_string("1"));
+    json_object_object_add(file_obj, "integer", json_object_new_int(1));
+
+    set_by_string("", &modify_obj, "{ \"nested0\": {\"nested1\": {\"integer\": 1}}, \"ints\": [1, 2, 3], \"string\":\"1\", \"integer\": 1}", json_type_object);
+
+    printf("file_obj=%s\n", json_object_get_string(file_obj));
+    printf("modify_obj=%s\n", json_object_get_string(modify_obj));
+
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+}
+
+static void test_json_add_string(void **state)
+{
+    (void) state;
+
+	//struct json_object *obj = json_object_new_object();
+
+    json_object_object_add(file_obj, "string", json_object_new_string("1"));
+
+    set_by_string("string", &modify_obj, "1", json_type_string);
+
+    printf("file_obj=%s\n", json_object_get_string(file_obj));
+    printf("modify_obj=%s\n", json_object_get_string(modify_obj));
+
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+}
+
+
+static void test_json_overwrite_string(void **state)
+{
+    (void) state;
+
+	//struct json_object *obj = json_object_new_object();
+
+    json_object_object_add(file_obj, "test", json_object_new_string("1"));
+    set_by_string("test", &modify_obj, "1", json_type_string);
+
+    printf("file_obj    = %s\n", json_object_get_string(file_obj));
+    printf("modify_obj  = %s\n", json_object_get_string(modify_obj));
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+
+    //json_object_object_add(file_obj, "test", json_object_new_string("1"));
+    struct json_object *obj, *nested;
+
+    json_object_object_get_ex(file_obj, "nested", &nested);
+    json_object_object_get_ex(nested, "api", &obj);
+    json_object_set_string(obj, "2");
+
+    set_by_string("nested.api", &modify_obj, "2", json_type_string);
+
+    printf("file_obj=%s\n", json_object_get_string(file_obj));
+    printf("modify_obj=%s\n", json_object_get_string(modify_obj));
+
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+}
+
+static void test_json_add_int(void **state)
+{
+    (void) state;
+
+	//struct json_object *obj = json_object_new_object();
+
+    json_object_object_add(file_obj, "integer", json_object_new_int(1));
+
+    set_by_string("integer", &modify_obj, "1", json_type_int);
+
+    printf("file_obj=%s\n", json_object_get_string(file_obj));
+    printf("modify_obj=%s\n", json_object_get_string(modify_obj));
+
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+}
+
+static void test_json_add_int_nested(void **state)
+{
+    (void) state;
+
+	struct json_object *obj = json_object_new_object();
+    struct json_object *nested = json_object_new_object();
+
+    json_object_object_add(nested, "integer", json_object_new_int(1));
+    json_object_object_add(obj, "nested1", nested);
+    json_object_object_add(file_obj, "nested0", obj);
+
+    set_by_string("nested0.nested1.integer", &modify_obj, "1", json_type_int);
+
+    printf("file_obj=%s\n", json_object_get_string(file_obj));
+    printf("modify_obj=%s\n", json_object_get_string(modify_obj));
+
+    assert_int_equal(1, json_object_equal(modify_obj, file_obj));
+}
+
+static int setup (void** state) {
+    file_obj = path_to_obj("/home/jakob/git/json-editor-api/test.json");
+    modify_obj = path_to_obj("/home/jakob/git/json-editor-api/test.json");
+	return 0;
+}
+
+static int teardown (void** state) {
+	json_object_put(file_obj);
+    json_object_put(modify_obj);
+	return 0;
+}
+
+int main(void) {
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test(test_cfg_parse_success),
+		cmocka_unit_test(test_cfg_parse_fail),
+        cmocka_unit_test(test_build_from_scratch),
+        cmocka_unit_test_setup_teardown(test_json_add_int, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_json_add_int_nested, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_json_add_string, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_json_add_object, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_json_add_array, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_json_add_multi_types, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_json_add_multi_obj, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_json_overwrite_string, setup, teardown),
+	};
+
+	return cmocka_run_group_tests(tests, NULL, NULL);
+}
-- 
GitLab