diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f99316..420bfc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # Notes: # # Author: Paul Harris, June 2012 -# Additions: Joakim Soderberg, Febuary 2013 +# Additions: Joakim Soderberg, February 2013 # # Supports: building static/shared, release/debug/etc, can also build html docs # and some of the tests. @@ -35,7 +35,7 @@ # >> make test (to run the tests, if you enabled them) # # Brief description on how it works: -# There is a small heirachy of CMakeLists.txt files which define how the +# There is a small hierarchy of CMakeLists.txt files which define how the # project is built. # Header file detection etc is done, and the results are written into config.h # and jansson_config.h, which are generated from the corresponding @@ -100,7 +100,7 @@ include (CheckFunctionKeywords) include (CheckIncludeFiles) include (CheckTypeSize) -# supress format-truncation warning +# suppress format-truncation warning include (CheckCCompilerFlag) check_c_compiler_flag(-Wno-format-truncation HAS_NO_FORMAT_TRUNCATION) if (HAS_NO_FORMAT_TRUNCATION) @@ -215,7 +215,7 @@ else () set (JSON_UINT8 "unsigned char") endif () -# Check for ssize_t and SSIZE_T existance. +# Check for ssize_t and SSIZE_T existence. check_type_size(ssize_t SSIZE_T) check_type_size(SSIZE_T UPPERCASE_SSIZE_T) if(NOT HAVE_SSIZE_T) diff --git a/README.rst b/README.rst index 371e913..9570b8f 100644 --- a/README.rst +++ b/README.rst @@ -30,6 +30,18 @@ source distribution for details. Compilation and Installation ---------------------------- +You can download and install Jansson using the `vcpkg `_ dependency manager: + +.. code-block:: bash + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + vcpkg install jansson + +The Jansson port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please `create an issue or pull request `_ on the vcpkg repository. + If you obtained a `source tarball`_ from the "Releases" section of the main site just use the standard autotools commands:: diff --git a/cmake/CoverallsGenerateGcov.cmake b/cmake/CoverallsGenerateGcov.cmake index 0c4c2b0..c4da8fb 100644 --- a/cmake/CoverallsGenerateGcov.cmake +++ b/cmake/CoverallsGenerateGcov.cmake @@ -102,8 +102,8 @@ if (GIT_FOUND) message("Git branch: ${GIT_BRANCH}") message("Git author: ${GIT_AUTHOR_NAME}") message("Git e-mail: ${GIT_AUTHOR_EMAIL}") - message("Git commiter name: ${GIT_COMMITTER_NAME}") - message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}") + message("Git committer name: ${GIT_COMMITTER_NAME}") + message("Git committer e-mail: ${GIT_COMMITTER_EMAIL}") message("Git commit message: ${GIT_COMMIT_MESSAGE}") endif() @@ -265,7 +265,7 @@ foreach (GCOV_FILE ${GCOV_FILES}) # Instead of trying to parse the source from the # gcov file, simply read the file contents from the source file. # (Parsing it from the gcov is hard because C-code uses ; in many places - # which also happens to be the same as the CMake list delimeter). + # which also happens to be the same as the CMake list delimiter). file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE) string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake index 55539d4..3bf0a5d 100644 --- a/cmake/FindSphinx.cmake +++ b/cmake/FindSphinx.cmake @@ -241,27 +241,42 @@ endif () # ---------------------------------------------------------------------------- # determine Sphinx version +# some quick experiments by @ploxiln +# - sphinx 1.7 and later have the version output format like "sphinx-build 1.7.2" +# - sphinx 1.2 through 1.6 have the version output format like "Sphinx (sphinx-build) 1.2.2" +# - sphinx 1.1 and before do not have a "--version" flag, but it causes the help output like "-h" does which includes version like "Sphinx v1.0.2" if (Sphinx-build_EXECUTABLE) # intentionally use invalid -h option here as the help that is shown then # will include the Sphinx version information if (Sphinx_PYTHON_EXECUTABLE) execute_process ( - COMMAND "${Sphinx_PYTHON_EXECUTABLE}" ${Sphinx_PYTHON_OPTIONS} "${Sphinx-build_EXECUTABLE}" -h + COMMAND "${Sphinx_PYTHON_EXECUTABLE}" ${Sphinx_PYTHON_OPTIONS} "${Sphinx-build_EXECUTABLE}" --version OUTPUT_VARIABLE _Sphinx_VERSION ERROR_VARIABLE _Sphinx_VERSION ) elseif (UNIX) execute_process ( - COMMAND "${Sphinx-build_EXECUTABLE}" -h + COMMAND "${Sphinx-build_EXECUTABLE}" --version OUTPUT_VARIABLE _Sphinx_VERSION ERROR_VARIABLE _Sphinx_VERSION ) endif () # The sphinx version can also contain a "b" instead of the last dot. - # For example "Sphinx v1.2b1" so we cannot just split on "." - if (_Sphinx_VERSION MATCHES "Sphinx v([0-9]+\\.[0-9]+(\\.|b)[0-9]+)") + # For example "Sphinx v1.2b1" or "Sphinx 1.7.0b2" so we cannot just split on "." + if (_Sphinx_VERSION MATCHES "sphinx-build ([0-9]+\\.[0-9]+(\\.|a?|b?)([0-9]*)(b?)([0-9]*))") set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}") + set (_SPHINX_VERSION_FOUND) + elseif (_Sphinx_VERSION MATCHES "Sphinx v([0-9]+\\.[0-9]+(\\.|b?)([0-9]*)(b?)([0-9]*))") + set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}") + set (_SPHINX_VERSION_FOUND) + elseif (_Sphinx_VERSION MATCHES "Sphinx \\(sphinx-build\\) ([0-9]+\\.[0-9]+(\\.|a?|b?)([0-9]*)(b?)([0-9]*))") + set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}") + set (_SPHINX_VERSION_FOUND) + endif () +endif () + +if(_SPHINX_VERSION_FOUND) string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.|b)[0-9]+" "\\1" Sphinx_VERSION_MAJOR ${Sphinx_VERSION_STRING}) string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.|b)[0-9]+" "\\1" Sphinx_VERSION_MINOR ${Sphinx_VERSION_STRING}) string(REGEX REPLACE "[0-9]+\\.[0-9]+(\\.|b)([0-9]+)" "\\1" Sphinx_VERSION_PATCH ${Sphinx_VERSION_STRING}) @@ -270,7 +285,6 @@ if (Sphinx-build_EXECUTABLE) if (Sphinx_VERSION_PATCH EQUAL 0) string (REGEX REPLACE "\\.0$" "" Sphinx_VERSION_STRING "${Sphinx_VERSION_STRING}") endif () - endif() endif () # ---------------------------------------------------------------------------- diff --git a/doc/apiref.rst b/doc/apiref.rst index 5d8d41b..16f1ecb 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -726,6 +726,12 @@ allowed in object keys. The value of any existing key is not changed. Returns 0 on success or -1 on error. +.. function:: int json_object_update_recursive(json_t *object, json_t *other) + + Like :func:`json_object_update()`, but object values in *other* are + recursively merged with the corresponding values in *object* if they are also + objects, instead of overwriting them. Returns 0 on success or -1 on error. + .. function:: json_object_foreach(object, key, value) Iterate over every key-value pair of ``object``, running the block @@ -1351,7 +1357,11 @@ If no error or position information is needed, you can pass *NULL*. It is important to note that this function can only succeed on stream file descriptors (such as SOCK_STREAM). Using this function on a non-stream file descriptor will result in undefined behavior. For - non-stream file descriptors, see instead :func:`json_loadb()`. + non-stream file descriptors, see instead :func:`json_loadb()`. In + addition, please note that this function cannot be used on non-blocking + file descriptors (such as a non-blocking socket). Using this function + on non-blocking file descriptors has a high risk of data loss because + it does not support resuming. This function requires POSIX and fails on all non-POSIX systems. diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 56861c2..bb7a6c2 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -74,7 +74,7 @@ function:: static char *request(const char *url); -It takes the URL as a parameter, preforms a HTTP GET request, and +It takes the URL as a parameter, performs a HTTP GET request, and returns a newly allocated string that contains the response body. If the request fails, an error message is printed to stderr and the return value is *NULL*. For full details, refer to :download:`the code diff --git a/src/hashtable.h b/src/hashtable.h index c112834..00d44a9 100644 --- a/src/hashtable.h +++ b/src/hashtable.h @@ -55,7 +55,7 @@ typedef struct hashtable { * * Returns 0 on success, -1 on error (out of memory). */ -int hashtable_init(hashtable_t *hashtable) JANSSON_ATTRS(warn_unused_result); +int hashtable_init(hashtable_t *hashtable) JANSSON_ATTRS((warn_unused_result)); /** * hashtable_close - Release all resources used by a hashtable object diff --git a/src/jansson.def b/src/jansson.def index ed72829..55b39c8 100644 --- a/src/jansson.def +++ b/src/jansson.def @@ -41,6 +41,7 @@ EXPORTS json_object_update json_object_update_existing json_object_update_missing + json_object_update_recursive json_object_iter json_object_iter_at json_object_iter_next diff --git a/src/jansson.h b/src/jansson.h index 1aa3c2c..3436465 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -40,9 +40,9 @@ extern "C" { #endif #if defined(__GNUC__) || defined(__clang__) -#define JANSSON_ATTRS(...) __attribute__((__VA_ARGS__)) +#define JANSSON_ATTRS(x) __attribute__(x) #else -#define JANSSON_ATTRS(...) +#define JANSSON_ATTRS(x) #endif /* types */ @@ -191,7 +191,7 @@ static JSON_INLINE enum json_error_code json_error_code(const json_error_t *e) { void json_object_seed(size_t seed); size_t json_object_size(const json_t *object); -json_t *json_object_get(const json_t *object, const char *key) JANSSON_ATTRS(warn_unused_result); +json_t *json_object_get(const json_t *object, const char *key) JANSSON_ATTRS((warn_unused_result)); int json_object_set_new(json_t *object, const char *key, json_t *value); int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); int json_object_del(json_t *object, const char *key); @@ -199,6 +199,7 @@ int json_object_clear(json_t *object); int json_object_update(json_t *object, json_t *other); int json_object_update_existing(json_t *object, json_t *other); int json_object_update_missing(json_t *object, json_t *other); +int json_object_update_recursive(json_t *object, json_t *other); void *json_object_iter(json_t *object); void *json_object_iter_at(json_t *object, const char *key); void *json_object_key_to_iter(const char *key); @@ -267,7 +268,7 @@ int json_object_update_missing_new(json_t *object, json_t *other) } size_t json_array_size(const json_t *array); -json_t *json_array_get(const json_t *array, size_t index) JANSSON_ATTRS(warn_unused_result); +json_t *json_array_get(const json_t *array, size_t index) JANSSON_ATTRS((warn_unused_result)); int json_array_set_new(json_t *array, size_t index, json_t *value); int json_array_append_new(json_t *array, json_t *value); int json_array_insert_new(json_t *array, size_t index, json_t *value); @@ -308,9 +309,9 @@ int json_real_set(json_t *real, double value); /* pack, unpack */ -json_t *json_pack(const char *fmt, ...) JANSSON_ATTRS(warn_unused_result); -json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) JANSSON_ATTRS(warn_unused_result); -json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) JANSSON_ATTRS(warn_unused_result); +json_t *json_pack(const char *fmt, ...) JANSSON_ATTRS((warn_unused_result)); +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) JANSSON_ATTRS((warn_unused_result)); +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) JANSSON_ATTRS((warn_unused_result)); #define JSON_VALIDATE_ONLY 0x1 #define JSON_STRICT 0x2 @@ -321,8 +322,8 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char /* sprintf */ -json_t *json_sprintf(const char *fmt, ...) JANSSON_ATTRS(warn_unused_result, format(printf, 1, 2)); -json_t *json_vsprintf(const char *fmt, va_list ap) JANSSON_ATTRS(warn_unused_result, format(printf, 1, 0)); +json_t *json_sprintf(const char *fmt, ...) JANSSON_ATTRS((warn_unused_result, format(printf, 1, 2))); +json_t *json_vsprintf(const char *fmt, va_list ap) JANSSON_ATTRS((warn_unused_result, format(printf, 1, 0))); /* equality */ @@ -332,8 +333,8 @@ int json_equal(const json_t *value1, const json_t *value2); /* copying */ -json_t *json_copy(json_t *value) JANSSON_ATTRS(warn_unused_result); -json_t *json_deep_copy(const json_t *value) JANSSON_ATTRS(warn_unused_result); +json_t *json_copy(json_t *value) JANSSON_ATTRS((warn_unused_result)); +json_t *json_deep_copy(const json_t *value) JANSSON_ATTRS((warn_unused_result)); /* decoding */ @@ -346,12 +347,12 @@ json_t *json_deep_copy(const json_t *value) JANSSON_ATTRS(warn_unused_result); typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); -json_t *json_loads(const char *input, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); -json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); -json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); -json_t *json_loadfd(int input, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); -json_t *json_load_file(const char *path, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); -json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); +json_t *json_loads(const char *input, size_t flags, json_error_t *error) JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) JANSSON_ATTRS((warn_unused_result)); +json_t *json_loadfd(int input, size_t flags, json_error_t *error) JANSSON_ATTRS((warn_unused_result)); +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) JANSSON_ATTRS((warn_unused_result)); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error) JANSSON_ATTRS((warn_unused_result)); /* encoding */ @@ -369,7 +370,7 @@ json_t *json_load_callback(json_load_callback_t callback, void *data, size_t fla typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); -char *json_dumps(const json_t *json, size_t flags) JANSSON_ATTRS(warn_unused_result); +char *json_dumps(const json_t *json, size_t flags) JANSSON_ATTRS((warn_unused_result)); size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags); int json_dumpf(const json_t *json, FILE *output, size_t flags); int json_dumpfd(const json_t *json, int output, size_t flags); diff --git a/src/jansson_private.h b/src/jansson_private.h index c7ce11a..42882a8 100644 --- a/src/jansson_private.h +++ b/src/jansson_private.h @@ -84,11 +84,11 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out); int jsonp_dtostr(char *buffer, size_t size, double value, int prec); /* Wrappers for custom memory functions */ -void* jsonp_malloc(size_t size) JANSSON_ATTRS(warn_unused_result); +void* jsonp_malloc(size_t size) JANSSON_ATTRS((warn_unused_result)); void jsonp_free(void *ptr); -char *jsonp_strndup(const char *str, size_t length) JANSSON_ATTRS(warn_unused_result); -char *jsonp_strdup(const char *str) JANSSON_ATTRS(warn_unused_result); -char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS(warn_unused_result); +char *jsonp_strndup(const char *str, size_t length) JANSSON_ATTRS((warn_unused_result)); +char *jsonp_strdup(const char *str) JANSSON_ATTRS((warn_unused_result)); +char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS((warn_unused_result)); /* Circular reference check*/ /* Space for "0x", double the sizeof a pointer for the hex and a terminator. */ diff --git a/src/lookup3.h b/src/lookup3.h index 2fe4c25..7a67e03 100644 --- a/src/lookup3.h +++ b/src/lookup3.h @@ -243,7 +243,7 @@ static uint32_t hashlittle(const void *key, size_t length, uint32_t initval) * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). + * noticeably faster for short strings (like English words). */ #ifndef NO_MASKING_TRICK diff --git a/src/strbuffer.h b/src/strbuffer.h index a0276d4..75c02a2 100644 --- a/src/strbuffer.h +++ b/src/strbuffer.h @@ -16,7 +16,7 @@ typedef struct { size_t size; /* bytes allocated */ } strbuffer_t; -int strbuffer_init(strbuffer_t *strbuff) JANSSON_ATTRS(warn_unused_result); +int strbuffer_init(strbuffer_t *strbuff) JANSSON_ATTRS((warn_unused_result)); void strbuffer_close(strbuffer_t *strbuff); void strbuffer_clear(strbuffer_t *strbuff); diff --git a/src/value.c b/src/value.c index 7a000f8..e09a485 100644 --- a/src/value.c +++ b/src/value.c @@ -37,7 +37,7 @@ static JSON_INLINE int isnan(double x) { return x != x; } static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } #endif -json_t *do_deep_copy(const json_t *, hashtable_t *); +json_t *do_deep_copy(const json_t *json, hashtable_t *parents); static JSON_INLINE void json_init(json_t *json, json_type type) { @@ -214,6 +214,58 @@ int json_object_update_missing(json_t *object, json_t *other) return 0; } +int do_object_update_recursive(json_t *object, json_t *other, hashtable_t *parents) +{ + const char *key; + json_t *value; + char loop_key[LOOP_KEY_LEN]; + int res = 0; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + if(jsonp_loop_check(parents, other, loop_key, sizeof(loop_key))) + return -1; + + json_object_foreach(other, key, value) { + json_t *v = json_object_get(object, key); + + if(json_is_object(v) && json_is_object(value)) + { + if(do_object_update_recursive(v, value, parents)) + { + res = -1; + break; + } + } + else + { + if(json_object_set_nocheck(object, key, value)) + { + res = -1; + break; + } + } + } + + hashtable_del(parents, loop_key); + + return res; +} + +int json_object_update_recursive(json_t *object, json_t *other) +{ + int res; + hashtable_t parents_set; + + if (hashtable_init(&parents_set)) + return -1; + res = do_object_update_recursive(object, other, &parents_set); + hashtable_close(&parents_set); + + return res; +} + void *json_object_iter(json_t *json) { json_object_t *object; @@ -329,7 +381,7 @@ static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents) result = json_object(); if(!result) - return NULL; + goto out; /* Cannot use json_object_foreach because object has to be cast non-const */ @@ -348,6 +400,8 @@ static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents) } iter = json_object_iter_next((json_t *)object, iter); } + +out: hashtable_del(parents, loop_key); return result; @@ -648,7 +702,7 @@ static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents) result = json_array(); if(!result) - return NULL; + goto out; for(i = 0; i < json_array_size(array); i++) { @@ -659,6 +713,8 @@ static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents) break; } } + +out: hashtable_del(parents, loop_key); return result; diff --git a/test/suites/api/test_array.c b/test/suites/api/test_array.c index 9991fa0..a1a0934 100644 --- a/test/suites/api/test_array.c +++ b/test/suites/api/test_array.c @@ -186,7 +186,7 @@ static void test_insert(void) for(i = 0; i < 20; i++) { if(json_array_insert(array, 0, seven)) - fail("unable to insert value at the begining of an array"); + fail("unable to insert value at the beginning of an array"); } for(i = 0; i < 20; i++) { @@ -479,9 +479,9 @@ static void test_bad_args(void) if(!json_array_extend(num, arr)) fail("json_array_extend did not return error for first argument non-array"); if(!json_array_extend(arr, NULL)) - fail("json_array_extend did not return error for second arguemnt non-array"); + fail("json_array_extend did not return error for second argument non-array"); if(!json_array_extend(arr, num)) - fail("json_array_extend did not return error for second arguemnt non-array"); + fail("json_array_extend did not return error for second argument non-array"); if(num->refcount != 1) fail("unexpected reference count on num"); diff --git a/test/suites/api/test_dump.c b/test/suites/api/test_dump.c index cd4af63..c97a105 100644 --- a/test/suites/api/test_dump.c +++ b/test/suites/api/test_dump.c @@ -13,6 +13,10 @@ #include #endif #include "util.h" +#ifdef __MINGW32__ +#include +#define pipe(fds) _pipe(fds, 1024, _O_BINARY) +#endif static int encode_null_callback(const char *buffer, size_t size, void *data) { diff --git a/test/suites/api/test_equal.c b/test/suites/api/test_equal.c index 339bab6..ed1023f 100644 --- a/test/suites/api/test_equal.c +++ b/test/suites/api/test_equal.c @@ -159,7 +159,7 @@ static void test_equal_object() static void test_equal_complex() { - json_t *value1, *value2; + json_t *value1, *value2, *value3; const char *complex_json = "{" @@ -176,15 +176,25 @@ static void test_equal_complex() value1 = json_loads(complex_json, 0, NULL); value2 = json_loads(complex_json, 0, NULL); + value3 = json_loads(complex_json, 0, NULL); if(!value1 || !value2) fail("unable to parse JSON"); if(!json_equal(value1, value2)) - fail("json_equal fails for two inequal strings"); + fail("json_equal fails for two equal objects"); + + json_array_set_new(json_object_get(json_object_get(value2, "object"), + "array-in-object"), 1, json_false()); + if(json_equal(value1, value2)) + fail("json_equal fails for two inequal objects"); + + json_object_set_new(json_object_get(json_object_get(value3, "object"), + "object-in-object"), "foo", json_string("baz")); + if(json_equal(value1, value3)) + fail("json_equal fails for two inequal objects"); json_decref(value1); json_decref(value2); - - /* TODO: There's no negative test case here */ + json_decref(value3); } static void run_tests() diff --git a/test/suites/api/test_object.c b/test/suites/api/test_object.c index 0d320b3..5112fc7 100644 --- a/test/suites/api/test_object.c +++ b/test/suites/api/test_object.c @@ -59,7 +59,7 @@ static void test_update() /* update an empty object with an empty object */ if(json_object_update(object, other)) - fail("unable to update an emtpy object with an empty object"); + fail("unable to update an empty object with an empty object"); if(json_object_size(object) != 0) fail("invalid size after update"); @@ -270,6 +270,86 @@ static void test_conditional_updates() json_decref(other); } +static void test_recursive_updates() +{ + json_t *invalid, *object, *other, *barBefore, *barAfter; + + invalid = json_integer(42); + + object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2); + other = json_pack("{sisisi}", "foo", 3, "bar", 4, "baz", 5); + + if(!json_object_update_recursive(invalid, other)) + fail("json_object_update_recursive accepted non-object argument"); + + json_decref(invalid); + + if(json_object_update_recursive(object, other)) + fail("json_object_update_recursive failed"); + + if(json_object_size(object) != 3) + fail("invalid size after update"); + + if(json_integer_value(json_object_get(object, "foo")) != 3) + fail("json_object_update_recursive failed to update existing key"); + + if(json_integer_value(json_object_get(object, "bar")) != 4) + fail("json_object_update_recursive failed to overwrite object"); + + if(json_integer_value(json_object_get(object, "baz")) != 5) + fail("json_object_update_recursive didn't add new item"); + + json_decref(object); + json_decref(other); + + object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2); + other = json_pack("{s{si}}", "bar", "baz", 3); + barBefore = json_object_get(object, "bar"); + + if(!barBefore) + fail("can't get bar object before json_object_update_recursive"); + + if(json_object_update_recursive(object, other)) + fail("json_object_update_recursive failed"); + + if(json_object_size(object) != 2) + fail("invalid size after update"); + + if(!json_object_get(object, "foo")) + fail("json_object_update_recursive removed existing key"); + + if(json_integer_value(json_object_get(json_object_get(object, "bar"), "baz")) != 3) + fail("json_object_update_recursive failed to update nested value"); + + barAfter = json_object_get(object, "bar"); + if(!barAfter) + fail("can't get bar object after json_object_update_recursive"); + + if(barBefore != barAfter) + fail("bar object reference changed after json_object_update_recursive"); + + json_decref(object); + json_decref(other); + + /* check circular reference */ + object = json_pack("{s{s{s{si}}}}", "foo", "bar", "baz", "xxx", 2); + other = json_pack("{s{s{si}}}", "foo", "bar", "baz", 2); + json_object_set(json_object_get(json_object_get(other, "foo"), "bar"), "baz", + json_object_get(other, "foo")); + + if(!json_object_update_recursive(object, other)) + fail("json_object_update_recursive update a circular reference!"); + + json_object_set_new(json_object_get(json_object_get(other, "foo"), "bar"), "baz", + json_integer(1)); + + if(json_object_update_recursive(object, other)) + fail("json_object_update_recursive failed!"); + + json_decref(object); + json_decref(other); +} + static void test_circular() { json_t *object1, *object2; @@ -730,6 +810,7 @@ static void run_tests() test_update(); test_set_many_keys(); test_conditional_updates(); + test_recursive_updates(); test_circular(); test_set_nocheck(); test_iterators();