From 58f9d655358bc0c68bfadbc2946b964c2ac774b7 Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Tue, 22 Feb 2011 19:08:41 +0200 Subject: [PATCH] Add lots of tests for pack/unpack code, fix bugs found Closes GH-12. --- doc/apiref.rst | 4 +- src/error.c | 28 +++-- src/jansson_private.h | 1 + src/pack_unpack.c | 137 ++++++++++++++-------- test/suites/api/test_pack.c | 84 ++++++++------ test/suites/api/test_unpack.c | 211 ++++++++++++++++++++++++++++++++-- test/suites/api/util.h | 43 ++++++- 7 files changed, 397 insertions(+), 111 deletions(-) diff --git a/doc/apiref.rst b/doc/apiref.rst index 79693fb..17ac715 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -841,8 +841,8 @@ denotes the C type that is expected as the corresponding argument. ``O`` (any value) [json_t \*] Like ``o``, but the argument's reference count is incremented. - This is useful if you pack and array an array or object and want - to keep the reference for the JSON value consumed by ``O`` to + This is useful if you pack into an array or object and want to + keep the reference for the JSON value consumed by ``O`` to yourself. ``[fmt]`` (array) diff --git a/src/error.c b/src/error.c index d113b8f..074a68e 100644 --- a/src/error.c +++ b/src/error.c @@ -5,21 +5,29 @@ void jsonp_error_init(json_error_t *error, const char *source) { if(error) { - size_t length; - error->text[0] = '\0'; error->line = -1; error->column = -1; error->position = 0; + if(source) + jsonp_error_set_source(error, source); + else + error->source[0] = '\0'; + } +} - length = strlen(source); - if(length < JSON_ERROR_SOURCE_LENGTH) - strcpy(error->source, source); - else { - size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; - strcpy(error->source, "..."); - strcpy(error->source + 3, source + extra); - } +void jsonp_error_set_source(json_error_t *error, const char *source) +{ + if(!error || !source) + return; + + size_t length = strlen(source); + if(length < JSON_ERROR_SOURCE_LENGTH) + strcpy(error->source, source); + else { + size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; + strcpy(error->source, "..."); + strcpy(error->source + 3, source + extra); } } diff --git a/src/jansson_private.h b/src/jansson_private.h index b4743f3..08731e8 100644 --- a/src/jansson_private.h +++ b/src/jansson_private.h @@ -68,6 +68,7 @@ typedef struct { const object_key_t *jsonp_object_iter_fullkey(void *iter); void jsonp_error_init(json_error_t *error, const char *source); +void jsonp_error_set_source(json_error_t *error, const char *source); void jsonp_error_set(json_error_t *error, int line, int column, size_t position, const char *msg, ...); void jsonp_error_vset(json_error_t *error, int line, int column, diff --git a/src/pack_unpack.c b/src/pack_unpack.c index 4fb8c1b..eb15b17 100644 --- a/src/pack_unpack.c +++ b/src/pack_unpack.c @@ -6,6 +6,7 @@ * it under the terms of the MIT license. See LICENSE for details. */ +#include #include #include "jansson_private.h" @@ -32,6 +33,19 @@ static const char *type_names[] = { #define type_name(x) type_names[json_typeof(x)] +static const char *unpack_value_starters = "{[siIbfFOon"; + + +static void scanner_init(scanner_t *s, json_error_t *error, + size_t flags, const char *fmt) +{ + s->error = error; + s->flags = flags; + s->fmt = s->start = fmt; + s->line = 1; + s->column = 0; +} + static void next_token(scanner_t *s) { const char *t = s->fmt; @@ -55,7 +69,7 @@ static void next_token(scanner_t *s) s->fmt = t; } -static void set_error(scanner_t *s, const char *fmt, ...) +static void set_error(scanner_t *s, const char *source, const char *fmt, ...) { va_list ap; size_t pos; @@ -64,6 +78,8 @@ static void set_error(scanner_t *s, const char *fmt, ...) pos = (size_t)(s->fmt - s->start); jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap); + jsonp_error_set_source(s->error, source); + va_end(ap); } @@ -79,18 +95,18 @@ static json_t *pack_object(scanner_t *s, va_list *ap) json_t *value; if(!s->token) { - set_error(s, "Unexpected end of format string"); + set_error(s, "", "Unexpected end of format string"); goto error; } if(s->token != 's') { - set_error(s, "Expected format 's', got '%c'\n", *s->fmt); + set_error(s, "", "Expected format 's', got '%c'", s->token); goto error; } key = va_arg(*ap, const char *); if(!key) { - set_error(s, "NULL object key"); + set_error(s, "", "NULL object key"); goto error; } @@ -101,7 +117,7 @@ static json_t *pack_object(scanner_t *s, va_list *ap) goto error; if(json_object_set_new(object, key, value)) { - set_error(s, "Unable to add key \"%s\"", key); + set_error(s, "", "Unable to add key \"%s\"", key); goto error; } @@ -124,7 +140,7 @@ static json_t *pack_array(scanner_t *s, va_list *ap) json_t *value; if(!s->token) { - set_error(s, "Unexpected end of format string"); + set_error(s, "", "Unexpected end of format string"); goto error; } @@ -133,7 +149,7 @@ static json_t *pack_array(scanner_t *s, va_list *ap) goto error; if(json_array_append_new(array, value)) { - set_error(s, "Unable to append to array"); + set_error(s, "", "Unable to append to array"); goto error; } @@ -159,7 +175,7 @@ static json_t *pack(scanner_t *s, va_list *ap) { const char *str = va_arg(*ap, const char *); if(!str) { - set_error(s, "NULL string"); + set_error(s, "", "NULL string argument"); return NULL; } return json_string(str); @@ -187,7 +203,8 @@ static json_t *pack(scanner_t *s, va_list *ap) return va_arg(*ap, json_t *); default: - set_error(s, "Unrecognized format character '%c'", s->token); + set_error(s, "", "Unexpected format character '%c'", + s->token); return NULL; } } @@ -207,12 +224,13 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) hashtable_t key_set; if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) { - set_error(s, "Out of memory"); + set_error(s, "", "Out of memory"); return -1; } if(!json_is_object(root)) { - set_error(s, "Expected object, got %s", type_name(root)); + set_error(s, "", "Expected object, got %s", + type_name(root)); goto out; } next_token(s); @@ -222,13 +240,13 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) json_t *value; if(strict != 0) { - set_error(s, "Expected '}' after '%c', got '%c'", + set_error(s, "", "Expected '}' after '%c', got '%c'", (strict == 1 ? '!' : '*'), s->token); goto out; } if(!s->token) { - set_error(s, "Unexpected end of format string"); + set_error(s, "", "Unexpected end of format string"); goto out; } @@ -239,19 +257,24 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) } if(s->token != 's') { - set_error(s, "Expected format 's', got '%c'\n", *s->fmt); + set_error(s, "", "Expected format 's', got '%c'", s->token); goto out; } key = va_arg(*ap, const char *); if(!key) { - set_error(s, "NULL object key"); + set_error(s, "", "NULL object key"); goto out; } next_token(s); value = json_object_get(root, key); + if(!value) { + set_error(s, "", "Object item not found: %s", key); + goto out; + } + if(unpack(s, value, ap)) goto out; @@ -264,7 +287,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) if(strict == 1 && key_set.size != json_object_size(root)) { long diff = (long)json_object_size(root) - (long)key_set.size; - set_error(s, "%li object items left unpacked", diff); + set_error(s, "", "%li object item(s) left unpacked", diff); goto out; } @@ -281,7 +304,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) int strict = 0; if(!json_is_array(root)) { - set_error(s, "Expected array, got %s", type_name(root)); + set_error(s, "", "Expected array, got %s", type_name(root)); return -1; } next_token(s); @@ -290,14 +313,14 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) json_t *value; if(strict != 0) { - set_error(s, "Expected ']' after '%c', got '%c'", + set_error(s, "", "Expected ']' after '%c', got '%c'", (strict == 1 ? '!' : '*'), s->token); return -1; } if(!s->token) { - set_error(s, "Unexpected end of format string"); + set_error(s, "", "Unexpected end of format string"); return -1; } @@ -307,9 +330,16 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) continue; } + if(!strchr(unpack_value_starters, s->token)) { + set_error(s, "", "Unexpected format character '%c'", + s->token); + return -1; + } + value = json_array_get(root, i); if(!value) { - set_error(s, "Array index %lu out of range", (unsigned long)i); + set_error(s, "", "Array index %lu out of range", + (unsigned long)i); return -1; } @@ -325,7 +355,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap) if(strict == 1 && i != json_array_size(root)) { long diff = (long)json_array_size(root) - (long)i; - set_error(s, "%li array items left upacked", diff); + set_error(s, "", "%li array item(s) left unpacked", diff); return -1; } @@ -344,7 +374,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 's': if(!json_is_string(root)) { - set_error(s, "Expected string, got %s", type_name(root)); + set_error(s, "", "Expected string, got %s", + type_name(root)); return -1; } @@ -353,7 +384,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) str = va_arg(*ap, const char **); if(!str) { - set_error(s, "NULL string"); + set_error(s, "", "NULL string argument"); return -1; } @@ -363,7 +394,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'i': if(!json_is_integer(root)) { - set_error(s, "Expected integer, got %s", type_name(root)); + set_error(s, "", "Expected integer, got %s", + type_name(root)); return -1; } @@ -374,7 +406,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'I': if(!json_is_integer(root)) { - set_error(s, "Expected integer, got %s", type_name(root)); + set_error(s, "", "Expected integer, got %s", + type_name(root)); return -1; } @@ -385,7 +418,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'b': if(!json_is_boolean(root)) { - set_error(s, "Expected true or false, got %s", type_name(root)); + set_error(s, "", "Expected true or false, got %s", + type_name(root)); return -1; } @@ -396,7 +430,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'f': if(!json_is_real(root)) { - set_error(s, "Expected real, got %s", type_name(root)); + set_error(s, "", "Expected real, got %s", + type_name(root)); return -1; } @@ -407,7 +442,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'F': if(!json_is_number(root)) { - set_error(s, "Expected real or integer, got %s", + set_error(s, "", "Expected real or integer, got %s", type_name(root)); return -1; } @@ -431,13 +466,15 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) case 'n': /* Never assign, just validate */ if(!json_is_null(root)) { - set_error(s, "Expected null, got %s", type_name(root)); + set_error(s, "", "Expected null, got %s", + type_name(root)); return -1; } return 0; default: - set_error(s, "Unknown format character '%c'", s->token); + set_error(s, "", "Unexpected format character '%c'", + s->token); return -1; } } @@ -449,28 +486,27 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags, va_list ap_copy; json_t *value; - jsonp_error_init(error, ""); - if(!fmt || !*fmt) { - jsonp_error_set(error, -1, -1, 0, "Null or empty format string"); + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); return NULL; } + jsonp_error_init(error, NULL); - s.error = error; - s.flags = flags; - s.fmt = s.start = fmt; - s.line = 1; - s.column = 0; - + scanner_init(&s, error, flags, fmt); next_token(&s); + va_copy(ap_copy, ap); value = pack(&s, &ap_copy); va_end(ap_copy); + if(!value) + return NULL; + next_token(&s); if(s.token) { json_decref(value); - set_error(&s, "Garbage after format string"); + set_error(&s, "", "Garbage after format string"); return NULL; } @@ -507,19 +543,20 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, scanner_t s; va_list ap_copy; - jsonp_error_init(error, ""); - - if(!fmt || !*fmt) { - jsonp_error_set(error, -1, -1, 0, "Null or empty format string"); + if(!root) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL root value"); return -1; } - s.error = error; - s.flags = flags; - s.fmt = s.start = fmt; - s.line = 1; - s.column = 0; + if(!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, "NULL or empty format string"); + return -1; + } + jsonp_error_init(error, NULL); + scanner_init(&s, error, flags, fmt); next_token(&s); va_copy(ap_copy, ap); @@ -531,7 +568,7 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, next_token(&s); if(s.token) { - set_error(&s, "Garbage after format string"); + set_error(&s, "", "Garbage after format string"); return -1; } diff --git a/test/suites/api/test_pack.c b/test/suites/api/test_pack.c index 772afb1..add5841 100644 --- a/test/suites/api/test_pack.c +++ b/test/suites/api/test_pack.c @@ -22,7 +22,7 @@ int main() */ /* true */ - value = json_pack_ex(&error, 0, "b", 1); + value = json_pack("b", 1); if(!json_is_true(value)) fail("json_pack boolean failed"); if(value->refcount != (ssize_t)-1) @@ -30,7 +30,7 @@ int main() json_decref(value); /* false */ - value = json_pack_ex(&error, 0, "b", 0); + value = json_pack("b", 0); if(!json_is_false(value)) fail("json_pack boolean failed"); if(value->refcount != (ssize_t)-1) @@ -38,7 +38,7 @@ int main() json_decref(value); /* null */ - value = json_pack_ex(&error, 0, "n"); + value = json_pack("n"); if(!json_is_null(value)) fail("json_pack null failed"); if(value->refcount != (ssize_t)-1) @@ -46,16 +46,23 @@ int main() json_decref(value); /* integer */ - value = json_pack_ex(&error, 0, "i", 1); + value = json_pack("i", 1); if(!json_is_integer(value) || json_integer_value(value) != 1) fail("json_pack integer failed"); if(value->refcount != (ssize_t)1) fail("json_pack integer refcount failed"); json_decref(value); + /* integer from json_int_t */ + value = json_pack("I", (json_int_t)555555); + if(!json_is_integer(value) || json_integer_value(value) != 555555) + fail("json_pack json_int_t failed"); + if(value->refcount != (ssize_t)1) + fail("json_pack integer refcount failed"); + json_decref(value); /* real */ - value = json_pack_ex(&error, 0, "f", 1.0); + value = json_pack("f", 1.0); if(!json_is_real(value) || json_real_value(value) != 1.0) fail("json_pack real failed"); if(value->refcount != (ssize_t)1) @@ -63,7 +70,7 @@ int main() json_decref(value); /* string */ - value = json_pack_ex(&error, 0, "s", "test"); + value = json_pack("s", "test"); if(!json_is_string(value) || strcmp("test", json_string_value(value))) fail("json_pack string failed"); if(value->refcount != (ssize_t)1) @@ -71,7 +78,7 @@ int main() json_decref(value); /* empty object */ - value = json_pack_ex(&error, 0, "{}", 1.0); + value = json_pack("{}", 1.0); if(!json_is_object(value) || json_object_size(value) != 0) fail("json_pack empty object failed"); if(value->refcount != (ssize_t)1) @@ -79,7 +86,7 @@ int main() json_decref(value); /* empty list */ - value = json_pack_ex(&error, 0, "[]", 1.0); + value = json_pack("[]", 1.0); if(!json_is_array(value) || json_array_size(value) != 0) fail("json_pack empty list failed"); if(value->refcount != (ssize_t)1) @@ -87,7 +94,7 @@ int main() json_decref(value); /* non-incref'd object */ - value = json_pack_ex(&error, 0, "o", json_integer(1)); + value = json_pack("o", json_integer(1)); if(!json_is_integer(value) || json_integer_value(value) != 1) fail("json_pack object failed"); if(value->refcount != (ssize_t)1) @@ -95,7 +102,7 @@ int main() json_decref(value); /* incref'd object */ - value = json_pack_ex(&error, 0, "O", json_integer(1)); + value = json_pack("O", json_integer(1)); if(!json_is_integer(value) || json_integer_value(value) != 1) fail("json_pack object failed"); if(value->refcount != (ssize_t)2) @@ -104,7 +111,7 @@ int main() json_decref(value); /* simple object */ - value = json_pack_ex(&error, 0, "{s:[]}", "foo"); + value = json_pack("{s:[]}", "foo"); if(!json_is_object(value) || json_object_size(value) != 1) fail("json_pack array failed"); if(!json_is_array(json_object_get(value, "foo"))) @@ -114,7 +121,7 @@ int main() json_decref(value); /* simple array */ - value = json_pack_ex(&error, 0, "[i,i,i]", 0, 1, 2); + value = json_pack("[i,i,i]", 0, 1, 2); if(!json_is_array(value) || json_array_size(value) != 3) fail("json_pack object failed"); for(i=0; i<3; i++) @@ -127,19 +134,19 @@ int main() json_decref(value); /* Whitespace; regular string */ - value = json_pack_ex(&error, 0, " s ", "test"); + value = json_pack(" s ", "test"); if(!json_is_string(value) || strcmp("test", json_string_value(value))) fail("json_pack string (with whitespace) failed"); json_decref(value); /* Whitespace; empty array */ - value = json_pack_ex(&error, 0, "[ ]"); + value = json_pack("[ ]"); if(!json_is_array(value) || json_array_size(value) != 0) fail("json_pack empty array (with whitespace) failed"); json_decref(value); /* Whitespace; array */ - value = json_pack_ex(&error, 0, "[ i , i, i ] ", 1, 2, 3); + value = json_pack("[ i , i, i ] ", 1, 2, 3); if(!json_is_array(value) || json_array_size(value) != 3) fail("json_pack array (with whitespace) failed"); json_decref(value); @@ -148,56 +155,67 @@ int main() * Invalid cases */ + /* newline in format string */ + if(json_pack_ex(&error, 0, "{\n\n1")) + fail("json_pack failed to catch invalid format '1'"); + check_error("Expected format 's', got '1'", "", 3, 1, 4); + /* mismatched open/close array/object */ if(json_pack_ex(&error, 0, "[}")) fail("json_pack failed to catch mismatched '}'"); - if(error.line != 1 || error.column != 2) - fail("json_pack didn't get the error coordinates right!"); + check_error("Unexpected format character '}'", "", 1, 2, 2); if(json_pack_ex(&error, 0, "{]")) fail("json_pack failed to catch mismatched ']'"); - if(error.line != 1 || error.column != 2) - fail("json_pack didn't get the error coordinates right!"); + check_error("Expected format 's', got ']'", "", 1, 2, 2); /* missing close array */ if(json_pack_ex(&error, 0, "[")) fail("json_pack failed to catch missing ']'"); - if(error.line != 1 || error.column != 2) - fail("json_pack didn't get the error coordinates right!"); + check_error("Unexpected end of format string", "", 1, 2, 2); /* missing close object */ if(json_pack_ex(&error, 0, "{")) fail("json_pack failed to catch missing '}'"); - if(error.line != 1 || error.column != 2) - fail("json_pack didn't get the error coordinates right!"); + check_error("Unexpected end of format string", "", 1, 2, 2); + + /* garbage after format string */ + if(json_pack_ex(&error, 0, "[i]a", 42)) + fail("json_pack failed to catch garbage after format string"); + check_error("Garbage after format string", "", 1, 4, 4); + + if(json_pack_ex(&error, 0, "ia", 42)) + fail("json_pack failed to catch garbage after format string"); + check_error("Garbage after format string", "", 1, 2, 2); /* NULL string */ if(json_pack_ex(&error, 0, "s", NULL)) fail("json_pack failed to catch null argument string"); - if(error.line != 1 || error.column != 1) - fail("json_pack didn't get the error coordinates right!"); + check_error("NULL string argument", "", 1, 1, 1); /* NULL format */ if(json_pack_ex(&error, 0, NULL)) fail("json_pack failed to catch NULL format string"); - if(error.line != -1 || error.column != -1) - fail("json_pack didn't get the error coordinates right!"); + check_error("NULL or empty format string", "", -1, -1, 0); + + /* NULL key */ + if(json_pack_ex(&error, 0, "{s:i}", NULL, 1)) + fail("json_pack failed to catch NULL key"); + check_error("NULL object key", "", 1, 2, 2); /* More complicated checks for row/columns */ if(json_pack_ex(&error, 0, "{ {}: s }", "foo")) fail("json_pack failed to catch object as key"); - if(error.line != 1 || error.column != 3) - fail("json_pack didn't get the error coordinates right!"); + check_error("Expected format 's', got '{'", "", 1, 3, 3); + if(json_pack_ex(&error, 0, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13)) fail("json_pack failed to catch missing ]"); - if(error.line != 1 || error.column != 19) - fail("json_pack didn't get the error coordinates right!"); + check_error("Unexpected format character '}'", "", 1, 19, 19); if(json_pack_ex(&error, 0, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]")) fail("json_pack failed to catch extra }"); - if(error.line != 1 || error.column != 21) - fail("json_pack didn't get the error coordinates right!"); + check_error("Unexpected format character '}'", "", 1, 21, 21); return 0; } diff --git a/test/suites/api/test_unpack.c b/test/suites/api/test_unpack.c index 992a611..b7935a8 100644 --- a/test/suites/api/test_unpack.c +++ b/test/suites/api/test_unpack.c @@ -15,6 +15,7 @@ int main() { json_t *j, *j2; int i1, i2, i3; + json_int_t I1; int rv; double f; char *s; @@ -26,62 +27,82 @@ int main() */ /* true */ - rv = json_unpack_ex(json_true(), &error, 0, "b", &i1); + rv = json_unpack(json_true(), "b", &i1); if(rv || !i1) fail("json_unpack boolean failed"); /* false */ - rv = json_unpack_ex(json_false(), &error, 0, "b", &i1); + rv = json_unpack(json_false(), "b", &i1); if(rv || i1) fail("json_unpack boolean failed"); /* null */ - if(json_unpack_ex(json_null(), &error, 0, "n")) + if(json_unpack(json_null(), "n")) fail("json_unpack null failed"); /* integer */ j = json_integer(42); - rv = json_unpack_ex(j, &error, 0, "i", &i1); + rv = json_unpack(j, "i", &i1); if(rv || i1 != 42) fail("json_unpack integer failed"); json_decref(j); + /* json_int_t */ + j = json_integer(5555555); + rv = json_unpack(j, "I", &I1); + if(rv || I1 != 5555555) + fail("json_unpack json_int_t failed"); + json_decref(j); + /* real */ j = json_real(1.7); - rv = json_unpack_ex(j, &error, 0, "f", &f); + rv = json_unpack(j, "f", &f); if(rv || f != 1.7) fail("json_unpack real failed"); json_decref(j); + /* number */ + j = json_integer(12345); + rv = json_unpack(j, "F", &f); + if(rv || f != 12345.0) + fail("json_unpack (real or) integer failed"); + json_decref(j); + + j = json_real(1.7); + rv = json_unpack(j, "F", &f); + if(rv || f != 1.7) + fail("json_unpack real (or integer) failed"); + json_decref(j); + /* string */ j = json_string("foo"); - rv = json_unpack_ex(j, &error, 0, "s", &s); + rv = json_unpack(j, "s", &s); if(rv || strcmp(s, "foo")) fail("json_unpack string failed"); json_decref(j); /* empty object */ j = json_object(); - if(json_unpack_ex(j, &error, 0, "{}")) + if(json_unpack(j, "{}")) fail("json_unpack empty object failed"); json_decref(j); /* empty list */ j = json_array(); - if(json_unpack_ex(j, &error, 0, "[]")) + if(json_unpack(j, "[]")) fail("json_unpack empty list failed"); json_decref(j); /* non-incref'd object */ j = json_object(); - rv = json_unpack_ex(j, &error, 0, "o", &j2); + rv = json_unpack(j, "o", &j2); if(j2 != j || j->refcount != 1) fail("json_unpack object failed"); json_decref(j); /* incref'd object */ j = json_object(); - rv = json_unpack_ex(j, &error, 0, "O", &j2); + rv = json_unpack(j, "O", &j2); if(j2 != j || j->refcount != 2) fail("json_unpack object failed"); json_decref(j); @@ -89,14 +110,14 @@ int main() /* simple object */ j = json_pack("{s:i}", "foo", 42); - rv = json_unpack_ex(j, &error, 0, "{s:i}", "foo", &i1); + rv = json_unpack(j, "{s:i}", "foo", &i1); if(rv || i1 != 42) fail("json_unpack simple object failed"); json_decref(j); /* simple array */ j = json_pack("[iii]", 1, 2, 3); - rv = json_unpack_ex(j, &error, 0, "[i,i,i]", &i1, &i2, &i3); + rv = json_unpack(j, "[i,i,i]", &i1, &i2, &i3); if(rv || i1 != 1 || i2 != 2 || i3 != 3) fail("json_unpack simple array failed"); json_decref(j); @@ -105,39 +126,205 @@ int main() * Invalid cases */ + j = json_integer(42); + if(!json_unpack_ex(j, &error, 0, "z")) + fail("json_unpack succeeded with invalid format character"); + check_error("Unexpected format character 'z'", "", 1, 1, 1); + + if(!json_unpack_ex(NULL, &error, 0, "[i]")) + fail("json_unpack succeeded with NULL root"); + check_error("NULL root value", "", -1, -1, 0); + /* mismatched open/close array/object */ j = json_pack("[]"); if(!json_unpack_ex(j, &error, 0, "[}")) fail("json_unpack failed to catch mismatched ']'"); + check_error("Unexpected format character '}'", "", 1, 2, 2); json_decref(j); j = json_pack("{}"); if(!json_unpack_ex(j, &error, 0, "{]")) fail("json_unpack failed to catch mismatched '}'"); + check_error("Expected format 's', got ']'", "", 1, 2, 2); json_decref(j); /* missing close array */ j = json_pack("[]"); if(!json_unpack_ex(j, &error, 0, "[")) fail("json_unpack failed to catch missing ']'"); + check_error("Unexpected end of format string", "", 1, 2, 2); json_decref(j); /* missing close object */ j = json_pack("{}"); if(!json_unpack_ex(j, &error, 0, "{")) fail("json_unpack failed to catch missing '}'"); + check_error("Unexpected end of format string", "", 1, 2, 2); json_decref(j); + /* garbage after format string */ + j = json_pack("[i]", 42); + if(!json_unpack_ex(j, &error, 0, "[i]a", &i1)) + fail("json_unpack failed to catch garbage after format string"); + check_error("Garbage after format string", "", 1, 4, 4); + + j = json_integer(12345); + if(!json_unpack_ex(j, &error, 0, "ia", &i1)) + fail("json_unpack failed to catch garbage after format string"); + check_error("Garbage after format string", "", 1, 2, 2); + /* NULL format string */ j = json_pack("[]"); if(!json_unpack_ex(j, &error, 0, NULL)) fail("json_unpack failed to catch null format string"); + check_error("NULL or empty format string", "", -1, -1, 0); json_decref(j); /* NULL string pointer */ j = json_string("foobie"); if(!json_unpack_ex(j, &error, 0, "s", NULL)) fail("json_unpack failed to catch null string pointer"); + check_error("NULL string argument", "", 1, 1, 1); + json_decref(j); + + /* invalid types */ + j = json_integer(42); + j2 = json_string("foo"); + if(!json_unpack_ex(j, &error, 0, "s")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected string, got integer", "", 1, 1, 1); + + if(!json_unpack_ex(j, &error, 0, "n")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected null, got integer", "", 1, 1, 1); + + if(!json_unpack_ex(j, &error, 0, "b")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected true or false, got integer", "", 1, 1, 1); + + if(!json_unpack_ex(j2, &error, 0, "i")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected integer, got string", "", 1, 1, 1); + + if(!json_unpack_ex(j2, &error, 0, "I")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected integer, got string", "", 1, 1, 1); + + if(!json_unpack_ex(j, &error, 0, "f")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected real, got integer", "", 1, 1, 1); + + if(!json_unpack_ex(j2, &error, 0, "F")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected real or integer, got string", "", 1, 1, 1); + + if(!json_unpack_ex(j, &error, 0, "[i]")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected array, got integer", "", 1, 1, 1); + + if(!json_unpack_ex(j, &error, 0, "{si}", "foo")) + fail("json_unpack failed to catch invalid type"); + check_error("Expected object, got integer", "", 1, 1, 1); + + json_decref(j); + json_decref(j2); + + /* Array index out of range */ + j = json_pack("[i]", 1); + if(!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2)) + fail("json_unpack failed to catch index out of array bounds"); + check_error("Array index 1 out of range", "", 1, 3, 3); + json_decref(j); + + /* NULL object key */ + j = json_pack("{si}", "foo", 42); + if(!json_unpack_ex(j, &error, 0, "{si}", NULL, &i1)) + fail("json_unpack failed to catch null string pointer"); + check_error("NULL object key", "", 1, 2, 2); + json_decref(j); + + /* Object key not found */ + j = json_pack("{si}", "foo", 42); + if(!json_unpack_ex(j, &error, 0, "{si}", "baz", &i1)) + fail("json_unpack failed to catch null string pointer"); + check_error("Object item not found: baz", "", 1, 3, 3); + json_decref(j); + + /* + * Strict validation + */ + + j = json_pack("[iii]", 1, 2, 3); + rv = json_unpack(j, "[iii!]", &i1, &i2, &i3); + if(rv || i1 != 1 || i2 != 2 || i3 != 3) + fail("json_unpack array with strict validation failed"); + json_decref(j); + + j = json_pack("[iii]", 1, 2, 3); + if(!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2)) + fail("json_unpack array with strict validation failed"); + check_error("1 array item(s) left unpacked", "", 1, 5, 5); + json_decref(j); + + /* Like above, but with JSON_STRICT instead of '!' format */ + j = json_pack("[iii]", 1, 2, 3); + if(!json_unpack_ex(j, &error, JSON_STRICT, "[ii]", &i1, &i2)) + fail("json_unpack array with strict validation failed"); + check_error("1 array item(s) left unpacked", "", 1, 4, 4); + json_decref(j); + + j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42); + rv = json_unpack(j, "{sssi!}", "foo", &s, "baz", &i1); + if(rv || strcmp(s, "bar") != 0 || i1 != 42) + fail("json_unpack object with strict validation failed"); + json_decref(j); + + /* Unpack the same item twice */ + j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42); + if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s)) + fail("json_unpack object with strict validation failed"); + check_error("1 object item(s) left unpacked", "", 1, 10, 10); + json_decref(j); + + j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4); + if(json_unpack_ex(j, NULL, JSON_STRICT | JSON_VALIDATE_ONLY, + "[i{sisn}[ii]]", "foo", "bar")) + fail("json_unpack complex value with strict validation failed"); + json_decref(j); + + /* ! and * must be last */ + j = json_pack("[ii]", 1, 2); + if(!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2)) + fail("json_unpack failed to catch ! in the middle of an array"); + check_error("Expected ']' after '!', got 'i'", "", 1, 4, 4); + + if(!json_unpack_ex(j, &error, 0, "[i*i]", &i1, &i2)) + fail("json_unpack failed to catch * in the middle of an array"); + check_error("Expected ']' after '*', got 'i'", "", 1, 4, 4); + json_decref(j); + + j = json_pack("{sssi}", "foo", "bar", "baz", 42); + if(!json_unpack_ex(j, &error, 0, "{ss!si}", "foo", &s, "baz", &i1)) + fail("json_unpack failed to catch ! in the middle of an object"); + check_error("Expected '}' after '!', got 's'", "", 1, 5, 5); + + if(!json_unpack_ex(j, &error, 0, "{ss*si}", "foo", &s, "baz", &i1)) + fail("json_unpack failed to catch ! in the middle of an object"); + check_error("Expected '}' after '*', got 's'", "", 1, 5, 5); + json_decref(j); + + /* Error in nested object */ + j = json_pack("{s{snsn}}", "foo", "bar", "baz"); + if(!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar")) + fail("json_unpack nested object with strict validation failed"); + check_error("1 object item(s) left unpacked", "", 1, 7, 7); + json_decref(j); + + /* Error in nested array */ + j = json_pack("[[ii]]", 1, 2); + if(!json_unpack_ex(j, &error, 0, "[[i!]]", &i1)) + fail("json_unpack nested array with strict validation failed"); + check_error("1 array item(s) left unpacked", "", 1, 5, 5); json_decref(j); return 0; diff --git a/test/suites/api/util.h b/test/suites/api/util.h index 986ef2e..83be721 100644 --- a/test/suites/api/util.h +++ b/test/suites/api/util.h @@ -5,16 +5,51 @@ * it under the terms of the MIT license. See LICENSE for details. */ -#ifndef TESTPROGS_UTIL_H -#define TESTPROGS_UTIL_H +#ifndef UTIL_H +#define UTIL_H +#include #include +#include + +#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__) #define fail(msg) \ do { \ - fprintf(stderr, "%s:%s:%d: %s\n", \ - __FILE__, __FUNCTION__, __LINE__, msg); \ + failhdr; \ + fprintf(stderr, "%s\n", msg); \ exit(1); \ } while(0) +/* Assumes json_error_t error */ +#define check_error(text_, source_, line_, column_, position_) \ + do { \ + if(strcmp(error.text, text_) != 0) { \ + failhdr; \ + fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \ + exit(1); \ + } \ + if(strcmp(error.source, source_) != 0) { \ + failhdr; \ + \ + fprintf(stderr, "source: \"%s\" != \"%s\"\n", error.source, source_); \ + exit(1); \ + } \ + if(error.line != line_) { \ + failhdr; \ + fprintf(stderr, "line: %d != %d\n", error.line, line_); \ + exit(1); \ + } \ + if(error.column != column_) { \ + failhdr; \ + fprintf(stderr, "column: %d != %d\n", error.column, column_); \ + exit(1); \ + } \ + if(error.position != position_) { \ + failhdr; \ + fprintf(stderr, "position: %d != %d\n", error.position, position_); \ + exit(1); \ + } \ + } while(0) + #endif