diff --git a/tests/Makefile.am b/tests/Makefile.am index 8ca27d8..f99e985 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,6 +8,8 @@ TESTS_DEFAULT= TESTS_DEFAULT+= ucs_copyright_char.test TESTS_DEFAULT+= test_float.test TESTS_DEFAULT+= test_dump_buffered.test +TESTS_DEFAULT+= test_json_util.test +TESTS_DEFAULT+= test_array_api.test TESTS_DEFAULT+= test1.test TESTS_DEFAULT+= test2.test TESTS_DEFAULT+= test4.test @@ -22,11 +24,6 @@ TESTS_DEFAULT+= test_obj_iter-del.test TESTS_DEFAULT+= test_object_object_add_ex.test TESTS_DEFAULT+= test_many_subobj.test TESTS_DEFAULT+= test_obj_obj_get_ex-null.test -# we officially do NOT support NUL bytes (however, we may -# later add a workaround to at least transparently pass them -# through, thus I keep this as reference). -#TESTS_DEFAULT+= test_null.test - TESTS = $(TESTS_DEFAULT) check_PROGRAMS= @@ -81,7 +78,6 @@ EXTRA_DIST += test_dump_buffered.expected EXTRA_DIST += test_cast.expected EXTRA_DIST += test_charcase.expected EXTRA_DIST += test_locale.expected -EXTRA_DIST += test_null.expected EXTRA_DIST += test_parse.expected EXTRA_DIST += test_parse_int64.expected EXTRA_DIST += test_printbuf.expected @@ -93,6 +89,8 @@ EXTRA_DIST += test_object_object_add_exFormatted_pretty.expected EXTRA_DIST += test_object_object_add_exFormatted_spaced.expected EXTRA_DIST += test_many_subobj.expected EXTRA_DIST += test_obj_obj_get_ex-null.expected +EXTRA_DIST += test_json_util.expected +EXTRA_DIST += test_array_api.expected testsubdir=testSubDir TESTS_ENVIRONMENT = top_builddir=$(top_builddir) diff --git a/tests/test_array_api.c b/tests/test_array_api.c new file mode 100644 index 0000000..9b7f539 --- /dev/null +++ b/tests/test_array_api.c @@ -0,0 +1,89 @@ +#include "config.h" + +#include +#include + +#include "../json.h" + +#define CHK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", \ + __FILE__, __LINE__, #condition); \ + exit(1); \ + } \ +} while (0) + +static int +compare_ints(const void *left, const void *right) +{ + const struct fjson_object *const left_object = + *(const struct fjson_object *const *) left; + const struct fjson_object *const right_object = + *(const struct fjson_object *const *) right; + const int left_value = fjson_object_get_int((struct fjson_object *) left_object); + const int right_value = fjson_object_get_int((struct fjson_object *) right_object); + + return (left_value > right_value) - (left_value < right_value); +} + +int +main(void) +{ + struct fjson_object *array = fjson_object_new_array(); + struct fjson_object *non_array = fjson_object_new_int(1); + struct fjson_object *key; + struct fjson_object *found; + + CHK(array != NULL); + CHK(non_array != NULL); + CHK(fjson_object_get_array(array) != NULL); + CHK(fjson_object_get_array(non_array) == NULL); + fjson_object_put(non_array); + + CHK(fjson_object_array_add(array, fjson_object_new_int(30)) == 0); + CHK(fjson_object_array_add(array, fjson_object_new_int(10)) == 0); + CHK(fjson_object_array_add(array, fjson_object_new_int(20)) == 0); + CHK(fjson_object_array_length(array) == 3); + + fjson_object_array_sort(array, compare_ints); + CHK(fjson_object_get_int(fjson_object_array_get_idx(array, 0)) == 10); + CHK(fjson_object_get_int(fjson_object_array_get_idx(array, 1)) == 20); + CHK(fjson_object_get_int(fjson_object_array_get_idx(array, 2)) == 30); + + key = fjson_object_new_int(20); + found = fjson_object_array_bsearch(key, array, compare_ints); + CHK(found != NULL); + CHK(fjson_object_get_int(found) == 20); + fjson_object_put(key); + + key = fjson_object_new_int(25); + CHK(fjson_object_array_bsearch(key, array, compare_ints) == NULL); + fjson_object_put(key); + + CHK(fjson_object_array_put_idx(array, 1, fjson_object_new_int(25)) == 0); + CHK(fjson_object_array_length(array) == 3); + CHK(fjson_object_get_int(fjson_object_array_get_idx(array, 1)) == 25); + fjson_object_array_del_idx(array, 1); + CHK(fjson_object_array_length(array) == 2); + CHK(fjson_object_get_int(fjson_object_array_get_idx(array, 1)) == 30); + fjson_object_array_del_idx(array, -1); + fjson_object_array_del_idx(array, 99); + CHK(fjson_object_array_length(array) == 2); + + CHK(fjson_object_array_put_idx(array, 4, fjson_object_new_int(50)) == 0); + CHK(fjson_object_array_length(array) == 5); + CHK(fjson_object_array_get_idx(array, 2) == NULL); + CHK(fjson_object_array_get_idx(array, 3) == NULL); + CHK(fjson_object_get_int(fjson_object_array_get_idx(array, 4)) == 50); + CHK(fjson_object_array_get_idx(array, 5) == NULL); + + fjson_object_array_del_idx(array, 0); + CHK(fjson_object_array_length(array) == 4); + CHK(fjson_object_get_int(fjson_object_array_get_idx(array, 0)) == 30); + fjson_object_array_del_idx(array, 3); + CHK(fjson_object_array_length(array) == 3); + + fjson_object_put(array); + puts("OK"); + return 0; +} diff --git a/tests/test_array_api.expected b/tests/test_array_api.expected new file mode 100644 index 0000000..d86bac9 --- /dev/null +++ b/tests/test_array_api.expected @@ -0,0 +1 @@ +OK diff --git a/tests/test_array_api.test b/tests/test_array_api.test new file mode 100755 index 0000000..40fd360 --- /dev/null +++ b/tests/test_array_api.test @@ -0,0 +1,6 @@ +#!/bin/sh + +. "${srcdir-.}/test-defs.sh" + +run_output_test test_array_api +exit $? diff --git a/tests/test_dump_buffered.c b/tests/test_dump_buffered.c index 158fd20..e6df408 100644 --- a/tests/test_dump_buffered.c +++ b/tests/test_dump_buffered.c @@ -2,38 +2,155 @@ * Released under ASL 2.0 */ #include "config.h" #include +#include #include #include "../json_object.h" +#define CHK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", \ + __FILE__, __LINE__, #condition); \ + exit(1); \ + } \ +} while (0) + struct output { - char data[32]; + char data[256]; size_t filled; + size_t calls; + size_t max_write; + int reject; + int overflow; }; static size_t append(void *ptr, const char *buffer, size_t size) { struct output *output = ptr; + size_t accepted = size; + + ++output->calls; + if (output->reject) + return 0; + if (output->max_write > 0 && accepted > output->max_write) + accepted = output->max_write; + if (accepted > sizeof(output->data) - output->filled) { + output->overflow = 1; + return 0; + } + memcpy(output->data + output->filled, buffer, accepted); + output->filled += accepted; + return accepted; +} + +static struct fjson_object * +new_document(void) +{ + struct fjson_object *document = fjson_object_new_object(); + struct fjson_object *values = fjson_object_new_array(); - memcpy(output->data + output->filled, buffer, size); - output->filled += size; - return size; + CHK(document != NULL); + CHK(values != NULL); + CHK(fjson_object_array_add(values, fjson_object_new_int(1)) == 0); + CHK(fjson_object_array_add(values, fjson_object_new_boolean(1)) == 0); + CHK(fjson_object_array_add(values, NULL) == 0); + fjson_object_object_add( + document, "name", fjson_object_new_string("a\"b\n")); + fjson_object_object_add(document, "values", values); + return document; } -static void dump(const char *name, char *temp, size_t size, struct fjson_object *json) +static void +check_buffer_size(struct fjson_object *document, const char *expected, size_t size) { - struct output output = {{0}, 0}; + char temp[256]; + struct output output = {{0}, 0, 0, 0, 0, 0}; + const size_t written = fjson_object_dump_buffered( + document, FJSON_TO_STRING_PLAIN, + size == 0 ? NULL : temp, size, append, &output); - fjson_object_dump_buffered(json, FJSON_TO_STRING_PLAIN, temp, size, append, &output); - printf("%s:%.*s\n", name, (int)output.filled, output.data); + CHK(!output.overflow); + CHK(written == strlen(expected)); + CHK(output.filled == strlen(expected)); + CHK(memcmp(output.data, expected, output.filled) == 0); + CHK(output.calls > 0); +} + +static void +check_file_write(struct fjson_object *document, int flags, const char *expected) +{ + char actual[256]; + FILE *file = tmpfile(); + size_t written; + + CHK(file != NULL); + if (flags == FJSON_TO_STRING_SPACED) + written = fjson_object_write(document, file); + else + written = fjson_object_write_ext(document, flags, file); + CHK(written == strlen(expected)); + CHK(fflush(file) == 0); + CHK(fseek(file, 0, SEEK_SET) == 0); + CHK(fread(actual, 1, sizeof(actual), file) == strlen(expected)); + CHK(memcmp(actual, expected, strlen(expected)) == 0); + CHK(fclose(file) == 0); } int main(void) { - struct fjson_object *json = fjson_object_new_int64(123456789012345); - char small[2]; + static const char plain[] = + "{\"name\":\"a\\\"b\\n\",\"values\":[1,true,null]}"; + static const char spaced[] = + "{ \"name\": \"a\\\"b\\n\", \"values\": [ 1, true, null ] }"; + const size_t sizes[] = { + 0, 1, 2, 3, 4, 7, 8, + sizeof(plain) - 2, sizeof(plain) - 1, sizeof(plain) + }; + struct fjson_object *document = new_document(); + struct output output = {{0}, 0, 0, 0, 0, 0}; + char temp[4]; + size_t written; + + for (size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) + check_buffer_size(document, plain, sizes[i]); + + written = fjson_object_dump_ext( + document, FJSON_TO_STRING_PLAIN, append, &output); + CHK(written == strlen(plain)); + CHK(output.filled == strlen(plain)); + CHK(memcmp(output.data, plain, output.filled) == 0); + + memset(&output, 0, sizeof(output)); + written = fjson_object_dump(document, append, &output); + CHK(written == strlen(spaced)); + CHK(output.filled == strlen(spaced)); + CHK(memcmp(output.data, spaced, output.filled) == 0); + CHK(fjson_object_size_ext( + document, FJSON_TO_STRING_PLAIN) == strlen(plain)); + CHK(fjson_object_size(document) == strlen(spaced)); + + check_file_write(document, FJSON_TO_STRING_PLAIN, plain); + check_file_write(document, FJSON_TO_STRING_SPACED, spaced); + + memset(&output, 0, sizeof(output)); + output.max_write = 1; + written = fjson_object_dump_buffered( + document, FJSON_TO_STRING_PLAIN, + temp, sizeof(temp), append, &output); + CHK(!output.overflow); + CHK(output.calls > 1); + CHK(written == output.filled); + CHK(written < strlen(plain)); + + memset(&output, 0, sizeof(output)); + output.reject = 1; + written = fjson_object_dump_buffered( + document, FJSON_TO_STRING_PLAIN, + temp, sizeof(temp), append, &output); + CHK(written == 0); + CHK(output.filled == 0); + CHK(output.calls > 0); - dump("zero", NULL, 0, json); - dump("small", small, sizeof(small), json); - fjson_object_put(json); + fjson_object_put(document); + puts("OK"); return 0; } diff --git a/tests/test_dump_buffered.expected b/tests/test_dump_buffered.expected index 30ff064..d86bac9 100644 --- a/tests/test_dump_buffered.expected +++ b/tests/test_dump_buffered.expected @@ -1,2 +1 @@ -zero:123456789012345 -small:123456789012345 +OK diff --git a/tests/test_json_util.c b/tests/test_json_util.c new file mode 100644 index 0000000..b26ce2a --- /dev/null +++ b/tests/test_json_util.c @@ -0,0 +1,87 @@ +#include "config.h" + +#include +#include +#include +#include +#include + +#include "../json.h" + +#define CHK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", \ + __FILE__, __LINE__, #condition); \ + exit(1); \ + } \ +} while (0) + +static struct fjson_object * +new_document(void) +{ + struct fjson_object *document = fjson_object_new_object(); + struct fjson_object *items = fjson_object_new_array(); + + CHK(document != NULL); + CHK(items != NULL); + CHK(fjson_object_array_add(items, fjson_object_new_int(3)) == 0); + CHK(fjson_object_array_add(items, fjson_object_new_int(1)) == 0); + CHK(fjson_object_array_add(items, fjson_object_new_int(2)) == 0); + fjson_object_object_add(document, "name", fjson_object_new_string("value")); + fjson_object_object_add(document, "items", items); + return document; +} + +static void +check_document(struct fjson_object *document) +{ + const char *const expected = "{\"name\":\"value\",\"items\":[3,1,2]}"; + + CHK(document != NULL); + CHK(strcmp(fjson_object_to_json_string_ext( + document, FJSON_TO_STRING_PLAIN), expected) == 0); +} + +int +main(void) +{ + const char *const plain_path = "test_json_util-plain.json"; + const char *const pretty_path = "test_json_util-pretty.json"; + const char *const fd_path = "test_json_util-fd.json"; + const char *const serialized = "{\"name\":\"value\",\"items\":[3,1,2]}"; + struct fjson_object *document = new_document(); + struct fjson_object *parsed; + int fd; + + CHK(fjson_object_to_file(plain_path, document) == 0); + parsed = fjson_object_from_file(plain_path); + check_document(parsed); + fjson_object_put(parsed); + + CHK(fjson_object_to_file_ext( + pretty_path, document, FJSON_TO_STRING_PRETTY) == 0); + parsed = fjson_object_from_file(pretty_path); + check_document(parsed); + fjson_object_put(parsed); + + fd = open(fd_path, O_WRONLY | O_CREAT | O_TRUNC, 0600); + CHK(fd >= 0); + CHK(write(fd, "xxx", 3) == 3); + CHK(write(fd, serialized, strlen(serialized)) == (ssize_t) strlen(serialized)); + CHK(close(fd) == 0); + + fd = open(fd_path, O_RDONLY); + CHK(fd >= 0); + CHK(lseek(fd, 3, SEEK_SET) == 3); + parsed = fjson_object_from_fd(fd); + CHK(close(fd) == 0); + check_document(parsed); + fjson_object_put(parsed); + + CHK(unlink(plain_path) == 0); + CHK(unlink(pretty_path) == 0); + CHK(unlink(fd_path) == 0); + fjson_object_put(document); + puts("OK"); + return 0; +} diff --git a/tests/test_json_util.expected b/tests/test_json_util.expected new file mode 100644 index 0000000..d86bac9 --- /dev/null +++ b/tests/test_json_util.expected @@ -0,0 +1 @@ +OK diff --git a/tests/test_json_util.test b/tests/test_json_util.test new file mode 100755 index 0000000..8db2845 --- /dev/null +++ b/tests/test_json_util.test @@ -0,0 +1,6 @@ +#!/bin/sh + +. "${srcdir-.}/test-defs.sh" + +run_output_test test_json_util +exit $? diff --git a/tests/test_null.c b/tests/test_null.c deleted file mode 100644 index 7ee26b0..0000000 --- a/tests/test_null.c +++ /dev/null @@ -1,57 +0,0 @@ -/* -* Tests if binary strings are supported. -*/ - -#include "config.h" -#include -#include - -#include "json_inttypes.h" -#include "json_object.h" -#include "json_tokener.h" - -int main() -{ - // this test has a space after the null character. check that it's still included - const char *input = " \0 "; - const char *expected = "\" \\u0000 \""; - struct fjson_object *string = fjson_object_new_string_len(input, 3); - const char *json = fjson_object_to_json_string(string); - - int strings_match = !strcmp( expected, json); - int retval = 0; - if (strings_match) - { - printf("JSON write result is correct: %s\n", json); - printf("PASS\n"); - } else { - printf("JSON write result doesn't match expected string\n"); - printf("expected string: "); - printf("%s\n", expected); - printf("parsed string: "); - printf("%s\n", json); - printf("FAIL\n"); - retval=1; - } - fjson_object_put(string); - - struct fjson_object *parsed_str = fjson_tokener_parse(expected); - if (parsed_str) - { - int parsed_len = fjson_object_get_string_len(parsed_str); - const char *parsed_cstr = fjson_object_get_string(parsed_str); - int ii; - printf("Re-parsed object string len=%d, chars=[", parsed_len); - for (ii = 0; ii < parsed_len ; ii++) - { - printf("%s%d", (ii ? ", " : ""), (int)parsed_cstr[ii]); - } - printf("]\n"); - fjson_object_put(parsed_str); - } - else - { - printf("ERROR: failed to parse\n"); - } - return retval; -} diff --git a/tests/test_null.expected b/tests/test_null.expected deleted file mode 100644 index 52d2890..0000000 --- a/tests/test_null.expected +++ /dev/null @@ -1,3 +0,0 @@ -JSON write result is correct: " \u0000 " -PASS -Re-parsed object string len=3, chars=[32, 0, 32] diff --git a/tests/test_null.test b/tests/test_null.test deleted file mode 100755 index 469ec64..0000000 --- a/tests/test_null.test +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -# Common definitions -if test -z "$srcdir"; then - srcdir="${0%/*}" - test "$srcdir" = "$0" && srcdir=. - test -z "$srcdir" && srcdir=. -fi -. "$srcdir/test-defs.sh" - -run_output_test test_null -exit $? diff --git a/tests/test_set_serializer.expected b/tests/test_set_serializer.expected deleted file mode 100644 index b546fcc..0000000 --- a/tests/test_set_serializer.expected +++ /dev/null @@ -1,10 +0,0 @@ -Test setting, then resetting a custom serializer: -my_object.to_string(standard)={ "abc": 12, "foo": "bar" } -my_object.to_string(custom serializer)=Custom Output -Next line of output should be from the custom freeit function: -freeit, value=123 -my_object.to_string(standard)={ "abc": 12, "foo": "bar" } -Check that the custom serializer isn't free'd until the last fjson_object_put: -my_object.to_string(custom serializer)=Custom Output -Next line of output should be from the custom freeit function: -freeit, value=123 diff --git a/tests/test_set_serializer.test b/tests/test_set_serializer.test deleted file mode 100755 index 728dfed..0000000 --- a/tests/test_set_serializer.test +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -# Common definitions -if test -z "$srcdir"; then - srcdir="${0%/*}" - test "$srcdir" = "$0" && srcdir=. - test -z "$srcdir" && srcdir=. -fi -. "$srcdir/test-defs.sh" - -run_output_test test_set_serializer -exit $?