Add lots of tests for pack/unpack code, fix bugs found

Closes GH-12.
This commit is contained in:
Petri Lehtinen 2011-02-22 19:08:41 +02:00
parent a33c3628da
commit 58f9d65535
7 changed files with 397 additions and 111 deletions

View File

@ -841,8 +841,8 @@ denotes the C type that is expected as the corresponding argument.
``O`` (any value) [json_t \*] ``O`` (any value) [json_t \*]
Like ``o``, but the argument's reference count is incremented. Like ``o``, but the argument's reference count is incremented.
This is useful if you pack and array an array or object and want This is useful if you pack into an array or object and want to
to keep the reference for the JSON value consumed by ``O`` to keep the reference for the JSON value consumed by ``O`` to
yourself. yourself.
``[fmt]`` (array) ``[fmt]`` (array)

View File

@ -5,14 +5,23 @@ void jsonp_error_init(json_error_t *error, const char *source)
{ {
if(error) if(error)
{ {
size_t length;
error->text[0] = '\0'; error->text[0] = '\0';
error->line = -1; error->line = -1;
error->column = -1; error->column = -1;
error->position = 0; error->position = 0;
if(source)
jsonp_error_set_source(error, source);
else
error->source[0] = '\0';
}
}
length = strlen(source); 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) if(length < JSON_ERROR_SOURCE_LENGTH)
strcpy(error->source, source); strcpy(error->source, source);
else { else {
@ -20,7 +29,6 @@ void jsonp_error_init(json_error_t *error, const char *source)
strcpy(error->source, "..."); strcpy(error->source, "...");
strcpy(error->source + 3, source + extra); strcpy(error->source + 3, source + extra);
} }
}
} }
void jsonp_error_set(json_error_t *error, int line, int column, void jsonp_error_set(json_error_t *error, int line, int column,

View File

@ -68,6 +68,7 @@ typedef struct {
const object_key_t *jsonp_object_iter_fullkey(void *iter); const object_key_t *jsonp_object_iter_fullkey(void *iter);
void jsonp_error_init(json_error_t *error, const char *source); 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, void jsonp_error_set(json_error_t *error, int line, int column,
size_t position, const char *msg, ...); size_t position, const char *msg, ...);
void jsonp_error_vset(json_error_t *error, int line, int column, void jsonp_error_vset(json_error_t *error, int line, int column,

View File

@ -6,6 +6,7 @@
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
*/ */
#include <string.h>
#include <jansson.h> #include <jansson.h>
#include "jansson_private.h" #include "jansson_private.h"
@ -32,6 +33,19 @@ static const char *type_names[] = {
#define type_name(x) type_names[json_typeof(x)] #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) static void next_token(scanner_t *s)
{ {
const char *t = s->fmt; const char *t = s->fmt;
@ -55,7 +69,7 @@ static void next_token(scanner_t *s)
s->fmt = t; 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; va_list ap;
size_t pos; size_t pos;
@ -64,6 +78,8 @@ static void set_error(scanner_t *s, const char *fmt, ...)
pos = (size_t)(s->fmt - s->start); pos = (size_t)(s->fmt - s->start);
jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap); jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap);
jsonp_error_set_source(s->error, source);
va_end(ap); va_end(ap);
} }
@ -79,18 +95,18 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
json_t *value; json_t *value;
if(!s->token) { if(!s->token) {
set_error(s, "Unexpected end of format string"); set_error(s, "<format>", "Unexpected end of format string");
goto error; goto error;
} }
if(s->token != 's') { if(s->token != 's') {
set_error(s, "Expected format 's', got '%c'\n", *s->fmt); set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
goto error; goto error;
} }
key = va_arg(*ap, const char *); key = va_arg(*ap, const char *);
if(!key) { if(!key) {
set_error(s, "NULL object key"); set_error(s, "<args>", "NULL object key");
goto error; goto error;
} }
@ -101,7 +117,7 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
goto error; goto error;
if(json_object_set_new(object, key, value)) { if(json_object_set_new(object, key, value)) {
set_error(s, "Unable to add key \"%s\"", key); set_error(s, "<internal>", "Unable to add key \"%s\"", key);
goto error; goto error;
} }
@ -124,7 +140,7 @@ static json_t *pack_array(scanner_t *s, va_list *ap)
json_t *value; json_t *value;
if(!s->token) { if(!s->token) {
set_error(s, "Unexpected end of format string"); set_error(s, "<format>", "Unexpected end of format string");
goto error; goto error;
} }
@ -133,7 +149,7 @@ static json_t *pack_array(scanner_t *s, va_list *ap)
goto error; goto error;
if(json_array_append_new(array, value)) { if(json_array_append_new(array, value)) {
set_error(s, "Unable to append to array"); set_error(s, "<internal>", "Unable to append to array");
goto error; goto error;
} }
@ -159,7 +175,7 @@ static json_t *pack(scanner_t *s, va_list *ap)
{ {
const char *str = va_arg(*ap, const char *); const char *str = va_arg(*ap, const char *);
if(!str) { if(!str) {
set_error(s, "NULL string"); set_error(s, "<args>", "NULL string argument");
return NULL; return NULL;
} }
return json_string(str); return json_string(str);
@ -187,7 +203,8 @@ static json_t *pack(scanner_t *s, va_list *ap)
return va_arg(*ap, json_t *); return va_arg(*ap, json_t *);
default: default:
set_error(s, "Unrecognized format character '%c'", s->token); set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
return NULL; return NULL;
} }
} }
@ -207,12 +224,13 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
hashtable_t key_set; hashtable_t key_set;
if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) { if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) {
set_error(s, "Out of memory"); set_error(s, "<internal>", "Out of memory");
return -1; return -1;
} }
if(!json_is_object(root)) { if(!json_is_object(root)) {
set_error(s, "Expected object, got %s", type_name(root)); set_error(s, "<validation>", "Expected object, got %s",
type_name(root));
goto out; goto out;
} }
next_token(s); next_token(s);
@ -222,13 +240,13 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
json_t *value; json_t *value;
if(strict != 0) { if(strict != 0) {
set_error(s, "Expected '}' after '%c', got '%c'", set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
(strict == 1 ? '!' : '*'), s->token); (strict == 1 ? '!' : '*'), s->token);
goto out; goto out;
} }
if(!s->token) { if(!s->token) {
set_error(s, "Unexpected end of format string"); set_error(s, "<format>", "Unexpected end of format string");
goto out; goto out;
} }
@ -239,19 +257,24 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
} }
if(s->token != 's') { if(s->token != 's') {
set_error(s, "Expected format 's', got '%c'\n", *s->fmt); set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
goto out; goto out;
} }
key = va_arg(*ap, const char *); key = va_arg(*ap, const char *);
if(!key) { if(!key) {
set_error(s, "NULL object key"); set_error(s, "<args>", "NULL object key");
goto out; goto out;
} }
next_token(s); next_token(s);
value = json_object_get(root, key); value = json_object_get(root, key);
if(!value) {
set_error(s, "<validation>", "Object item not found: %s", key);
goto out;
}
if(unpack(s, value, ap)) if(unpack(s, value, ap))
goto out; 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)) { if(strict == 1 && key_set.size != json_object_size(root)) {
long diff = (long)json_object_size(root) - (long)key_set.size; long diff = (long)json_object_size(root) - (long)key_set.size;
set_error(s, "%li object items left unpacked", diff); set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
goto out; goto out;
} }
@ -281,7 +304,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
int strict = 0; int strict = 0;
if(!json_is_array(root)) { if(!json_is_array(root)) {
set_error(s, "Expected array, got %s", type_name(root)); set_error(s, "<validation>", "Expected array, got %s", type_name(root));
return -1; return -1;
} }
next_token(s); next_token(s);
@ -290,14 +313,14 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
json_t *value; json_t *value;
if(strict != 0) { if(strict != 0) {
set_error(s, "Expected ']' after '%c', got '%c'", set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
(strict == 1 ? '!' : '*'), (strict == 1 ? '!' : '*'),
s->token); s->token);
return -1; return -1;
} }
if(!s->token) { if(!s->token) {
set_error(s, "Unexpected end of format string"); set_error(s, "<format>", "Unexpected end of format string");
return -1; return -1;
} }
@ -307,9 +330,16 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
continue; continue;
} }
if(!strchr(unpack_value_starters, s->token)) {
set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
return -1;
}
value = json_array_get(root, i); value = json_array_get(root, i);
if(!value) { if(!value) {
set_error(s, "Array index %lu out of range", (unsigned long)i); set_error(s, "<validation>", "Array index %lu out of range",
(unsigned long)i);
return -1; 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)) { if(strict == 1 && i != json_array_size(root)) {
long diff = (long)json_array_size(root) - (long)i; long diff = (long)json_array_size(root) - (long)i;
set_error(s, "%li array items left upacked", diff); set_error(s, "<validation>", "%li array item(s) left unpacked", diff);
return -1; return -1;
} }
@ -344,7 +374,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 's': case 's':
if(!json_is_string(root)) { if(!json_is_string(root)) {
set_error(s, "Expected string, got %s", type_name(root)); set_error(s, "<validation>", "Expected string, got %s",
type_name(root));
return -1; return -1;
} }
@ -353,7 +384,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
str = va_arg(*ap, const char **); str = va_arg(*ap, const char **);
if(!str) { if(!str) {
set_error(s, "NULL string"); set_error(s, "<args>", "NULL string argument");
return -1; return -1;
} }
@ -363,7 +394,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'i': case 'i':
if(!json_is_integer(root)) { if(!json_is_integer(root)) {
set_error(s, "Expected integer, got %s", type_name(root)); set_error(s, "<validation>", "Expected integer, got %s",
type_name(root));
return -1; return -1;
} }
@ -374,7 +406,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'I': case 'I':
if(!json_is_integer(root)) { if(!json_is_integer(root)) {
set_error(s, "Expected integer, got %s", type_name(root)); set_error(s, "<validation>", "Expected integer, got %s",
type_name(root));
return -1; return -1;
} }
@ -385,7 +418,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'b': case 'b':
if(!json_is_boolean(root)) { if(!json_is_boolean(root)) {
set_error(s, "Expected true or false, got %s", type_name(root)); set_error(s, "<validation>", "Expected true or false, got %s",
type_name(root));
return -1; return -1;
} }
@ -396,7 +430,8 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'f': case 'f':
if(!json_is_real(root)) { if(!json_is_real(root)) {
set_error(s, "Expected real, got %s", type_name(root)); set_error(s, "<validation>", "Expected real, got %s",
type_name(root));
return -1; return -1;
} }
@ -407,7 +442,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'F': case 'F':
if(!json_is_number(root)) { if(!json_is_number(root)) {
set_error(s, "Expected real or integer, got %s", set_error(s, "<validation>", "Expected real or integer, got %s",
type_name(root)); type_name(root));
return -1; return -1;
} }
@ -431,13 +466,15 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
case 'n': case 'n':
/* Never assign, just validate */ /* Never assign, just validate */
if(!json_is_null(root)) { if(!json_is_null(root)) {
set_error(s, "Expected null, got %s", type_name(root)); set_error(s, "<validation>", "Expected null, got %s",
type_name(root));
return -1; return -1;
} }
return 0; return 0;
default: default:
set_error(s, "Unknown format character '%c'", s->token); set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
return -1; return -1;
} }
} }
@ -449,28 +486,27 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags,
va_list ap_copy; va_list ap_copy;
json_t *value; json_t *value;
jsonp_error_init(error, "");
if(!fmt || !*fmt) { if(!fmt || !*fmt) {
jsonp_error_set(error, -1, -1, 0, "Null or empty format string"); jsonp_error_init(error, "<format>");
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
return NULL; return NULL;
} }
jsonp_error_init(error, NULL);
s.error = error; scanner_init(&s, error, flags, fmt);
s.flags = flags;
s.fmt = s.start = fmt;
s.line = 1;
s.column = 0;
next_token(&s); next_token(&s);
va_copy(ap_copy, ap); va_copy(ap_copy, ap);
value = pack(&s, &ap_copy); value = pack(&s, &ap_copy);
va_end(ap_copy); va_end(ap_copy);
if(!value)
return NULL;
next_token(&s); next_token(&s);
if(s.token) { if(s.token) {
json_decref(value); json_decref(value);
set_error(&s, "Garbage after format string"); set_error(&s, "<format>", "Garbage after format string");
return NULL; return NULL;
} }
@ -507,19 +543,20 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
scanner_t s; scanner_t s;
va_list ap_copy; va_list ap_copy;
jsonp_error_init(error, ""); if(!root) {
jsonp_error_init(error, "<root>");
if(!fmt || !*fmt) { jsonp_error_set(error, -1, -1, 0, "NULL root value");
jsonp_error_set(error, -1, -1, 0, "Null or empty format string");
return -1; return -1;
} }
s.error = error; if(!fmt || !*fmt) {
s.flags = flags; jsonp_error_init(error, "<format>");
s.fmt = s.start = fmt; jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
s.line = 1; return -1;
s.column = 0; }
jsonp_error_init(error, NULL);
scanner_init(&s, error, flags, fmt);
next_token(&s); next_token(&s);
va_copy(ap_copy, ap); 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); next_token(&s);
if(s.token) { if(s.token) {
set_error(&s, "Garbage after format string"); set_error(&s, "<format>", "Garbage after format string");
return -1; return -1;
} }

View File

@ -22,7 +22,7 @@ int main()
*/ */
/* true */ /* true */
value = json_pack_ex(&error, 0, "b", 1); value = json_pack("b", 1);
if(!json_is_true(value)) if(!json_is_true(value))
fail("json_pack boolean failed"); fail("json_pack boolean failed");
if(value->refcount != (ssize_t)-1) if(value->refcount != (ssize_t)-1)
@ -30,7 +30,7 @@ int main()
json_decref(value); json_decref(value);
/* false */ /* false */
value = json_pack_ex(&error, 0, "b", 0); value = json_pack("b", 0);
if(!json_is_false(value)) if(!json_is_false(value))
fail("json_pack boolean failed"); fail("json_pack boolean failed");
if(value->refcount != (ssize_t)-1) if(value->refcount != (ssize_t)-1)
@ -38,7 +38,7 @@ int main()
json_decref(value); json_decref(value);
/* null */ /* null */
value = json_pack_ex(&error, 0, "n"); value = json_pack("n");
if(!json_is_null(value)) if(!json_is_null(value))
fail("json_pack null failed"); fail("json_pack null failed");
if(value->refcount != (ssize_t)-1) if(value->refcount != (ssize_t)-1)
@ -46,16 +46,23 @@ int main()
json_decref(value); json_decref(value);
/* integer */ /* integer */
value = json_pack_ex(&error, 0, "i", 1); value = json_pack("i", 1);
if(!json_is_integer(value) || json_integer_value(value) != 1) if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack integer failed"); fail("json_pack integer failed");
if(value->refcount != (ssize_t)1) if(value->refcount != (ssize_t)1)
fail("json_pack integer refcount failed"); fail("json_pack integer refcount failed");
json_decref(value); 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 */ /* 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) if(!json_is_real(value) || json_real_value(value) != 1.0)
fail("json_pack real failed"); fail("json_pack real failed");
if(value->refcount != (ssize_t)1) if(value->refcount != (ssize_t)1)
@ -63,7 +70,7 @@ int main()
json_decref(value); json_decref(value);
/* string */ /* 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))) if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string failed"); fail("json_pack string failed");
if(value->refcount != (ssize_t)1) if(value->refcount != (ssize_t)1)
@ -71,7 +78,7 @@ int main()
json_decref(value); json_decref(value);
/* empty object */ /* 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) if(!json_is_object(value) || json_object_size(value) != 0)
fail("json_pack empty object failed"); fail("json_pack empty object failed");
if(value->refcount != (ssize_t)1) if(value->refcount != (ssize_t)1)
@ -79,7 +86,7 @@ int main()
json_decref(value); json_decref(value);
/* empty list */ /* 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) if(!json_is_array(value) || json_array_size(value) != 0)
fail("json_pack empty list failed"); fail("json_pack empty list failed");
if(value->refcount != (ssize_t)1) if(value->refcount != (ssize_t)1)
@ -87,7 +94,7 @@ int main()
json_decref(value); json_decref(value);
/* non-incref'd object */ /* 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) if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack object failed"); fail("json_pack object failed");
if(value->refcount != (ssize_t)1) if(value->refcount != (ssize_t)1)
@ -95,7 +102,7 @@ int main()
json_decref(value); json_decref(value);
/* incref'd object */ /* 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) if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack object failed"); fail("json_pack object failed");
if(value->refcount != (ssize_t)2) if(value->refcount != (ssize_t)2)
@ -104,7 +111,7 @@ int main()
json_decref(value); json_decref(value);
/* simple object */ /* 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) if(!json_is_object(value) || json_object_size(value) != 1)
fail("json_pack array failed"); fail("json_pack array failed");
if(!json_is_array(json_object_get(value, "foo"))) if(!json_is_array(json_object_get(value, "foo")))
@ -114,7 +121,7 @@ int main()
json_decref(value); json_decref(value);
/* simple array */ /* 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) if(!json_is_array(value) || json_array_size(value) != 3)
fail("json_pack object failed"); fail("json_pack object failed");
for(i=0; i<3; i++) for(i=0; i<3; i++)
@ -127,19 +134,19 @@ int main()
json_decref(value); json_decref(value);
/* Whitespace; regular string */ /* 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))) if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string (with whitespace) failed"); fail("json_pack string (with whitespace) failed");
json_decref(value); json_decref(value);
/* Whitespace; empty array */ /* Whitespace; empty array */
value = json_pack_ex(&error, 0, "[ ]"); value = json_pack("[ ]");
if(!json_is_array(value) || json_array_size(value) != 0) if(!json_is_array(value) || json_array_size(value) != 0)
fail("json_pack empty array (with whitespace) failed"); fail("json_pack empty array (with whitespace) failed");
json_decref(value); json_decref(value);
/* Whitespace; array */ /* 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) if(!json_is_array(value) || json_array_size(value) != 3)
fail("json_pack array (with whitespace) failed"); fail("json_pack array (with whitespace) failed");
json_decref(value); json_decref(value);
@ -148,56 +155,67 @@ int main()
* Invalid cases * 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'", "<format>", 3, 1, 4);
/* mismatched open/close array/object */ /* mismatched open/close array/object */
if(json_pack_ex(&error, 0, "[}")) if(json_pack_ex(&error, 0, "[}"))
fail("json_pack failed to catch mismatched '}'"); fail("json_pack failed to catch mismatched '}'");
if(error.line != 1 || error.column != 2) check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
fail("json_pack didn't get the error coordinates right!");
if(json_pack_ex(&error, 0, "{]")) if(json_pack_ex(&error, 0, "{]"))
fail("json_pack failed to catch mismatched ']'"); fail("json_pack failed to catch mismatched ']'");
if(error.line != 1 || error.column != 2) check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
fail("json_pack didn't get the error coordinates right!");
/* missing close array */ /* missing close array */
if(json_pack_ex(&error, 0, "[")) if(json_pack_ex(&error, 0, "["))
fail("json_pack failed to catch missing ']'"); fail("json_pack failed to catch missing ']'");
if(error.line != 1 || error.column != 2) check_error("Unexpected end of format string", "<format>", 1, 2, 2);
fail("json_pack didn't get the error coordinates right!");
/* missing close object */ /* missing close object */
if(json_pack_ex(&error, 0, "{")) if(json_pack_ex(&error, 0, "{"))
fail("json_pack failed to catch missing '}'"); fail("json_pack failed to catch missing '}'");
if(error.line != 1 || error.column != 2) check_error("Unexpected end of format string", "<format>", 1, 2, 2);
fail("json_pack didn't get the error coordinates right!");
/* 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", "<format>", 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", "<format>", 1, 2, 2);
/* NULL string */ /* NULL string */
if(json_pack_ex(&error, 0, "s", NULL)) if(json_pack_ex(&error, 0, "s", NULL))
fail("json_pack failed to catch null argument string"); fail("json_pack failed to catch null argument string");
if(error.line != 1 || error.column != 1) check_error("NULL string argument", "<args>", 1, 1, 1);
fail("json_pack didn't get the error coordinates right!");
/* NULL format */ /* NULL format */
if(json_pack_ex(&error, 0, NULL)) if(json_pack_ex(&error, 0, NULL))
fail("json_pack failed to catch NULL format string"); fail("json_pack failed to catch NULL format string");
if(error.line != -1 || error.column != -1) check_error("NULL or empty format string", "<format>", -1, -1, 0);
fail("json_pack didn't get the error coordinates right!");
/* 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", "<args>", 1, 2, 2);
/* More complicated checks for row/columns */ /* More complicated checks for row/columns */
if(json_pack_ex(&error, 0, "{ {}: s }", "foo")) if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
fail("json_pack failed to catch object as key"); fail("json_pack failed to catch object as key");
if(error.line != 1 || error.column != 3) check_error("Expected format 's', got '{'", "<format>", 1, 3, 3);
fail("json_pack didn't get the error coordinates right!");
if(json_pack_ex(&error, 0, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13)) if(json_pack_ex(&error, 0, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13))
fail("json_pack failed to catch missing ]"); fail("json_pack failed to catch missing ]");
if(error.line != 1 || error.column != 19) check_error("Unexpected format character '}'", "<format>", 1, 19, 19);
fail("json_pack didn't get the error coordinates right!");
if(json_pack_ex(&error, 0, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]")) if(json_pack_ex(&error, 0, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"))
fail("json_pack failed to catch extra }"); fail("json_pack failed to catch extra }");
if(error.line != 1 || error.column != 21) check_error("Unexpected format character '}'", "<format>", 1, 21, 21);
fail("json_pack didn't get the error coordinates right!");
return 0; return 0;
} }

View File

@ -15,6 +15,7 @@ int main()
{ {
json_t *j, *j2; json_t *j, *j2;
int i1, i2, i3; int i1, i2, i3;
json_int_t I1;
int rv; int rv;
double f; double f;
char *s; char *s;
@ -26,62 +27,82 @@ int main()
*/ */
/* true */ /* true */
rv = json_unpack_ex(json_true(), &error, 0, "b", &i1); rv = json_unpack(json_true(), "b", &i1);
if(rv || !i1) if(rv || !i1)
fail("json_unpack boolean failed"); fail("json_unpack boolean failed");
/* false */ /* false */
rv = json_unpack_ex(json_false(), &error, 0, "b", &i1); rv = json_unpack(json_false(), "b", &i1);
if(rv || i1) if(rv || i1)
fail("json_unpack boolean failed"); fail("json_unpack boolean failed");
/* null */ /* null */
if(json_unpack_ex(json_null(), &error, 0, "n")) if(json_unpack(json_null(), "n"))
fail("json_unpack null failed"); fail("json_unpack null failed");
/* integer */ /* integer */
j = json_integer(42); j = json_integer(42);
rv = json_unpack_ex(j, &error, 0, "i", &i1); rv = json_unpack(j, "i", &i1);
if(rv || i1 != 42) if(rv || i1 != 42)
fail("json_unpack integer failed"); fail("json_unpack integer failed");
json_decref(j); 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 */ /* real */
j = json_real(1.7); 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) if(rv || f != 1.7)
fail("json_unpack real failed"); fail("json_unpack real failed");
json_decref(j); 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 */ /* string */
j = json_string("foo"); j = json_string("foo");
rv = json_unpack_ex(j, &error, 0, "s", &s); rv = json_unpack(j, "s", &s);
if(rv || strcmp(s, "foo")) if(rv || strcmp(s, "foo"))
fail("json_unpack string failed"); fail("json_unpack string failed");
json_decref(j); json_decref(j);
/* empty object */ /* empty object */
j = json_object(); j = json_object();
if(json_unpack_ex(j, &error, 0, "{}")) if(json_unpack(j, "{}"))
fail("json_unpack empty object failed"); fail("json_unpack empty object failed");
json_decref(j); json_decref(j);
/* empty list */ /* empty list */
j = json_array(); j = json_array();
if(json_unpack_ex(j, &error, 0, "[]")) if(json_unpack(j, "[]"))
fail("json_unpack empty list failed"); fail("json_unpack empty list failed");
json_decref(j); json_decref(j);
/* non-incref'd object */ /* non-incref'd object */
j = json_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) if(j2 != j || j->refcount != 1)
fail("json_unpack object failed"); fail("json_unpack object failed");
json_decref(j); json_decref(j);
/* incref'd object */ /* incref'd object */
j = json_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) if(j2 != j || j->refcount != 2)
fail("json_unpack object failed"); fail("json_unpack object failed");
json_decref(j); json_decref(j);
@ -89,14 +110,14 @@ int main()
/* simple object */ /* simple object */
j = json_pack("{s:i}", "foo", 42); 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) if(rv || i1 != 42)
fail("json_unpack simple object failed"); fail("json_unpack simple object failed");
json_decref(j); json_decref(j);
/* simple array */ /* simple array */
j = json_pack("[iii]", 1, 2, 3); 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) if(rv || i1 != 1 || i2 != 2 || i3 != 3)
fail("json_unpack simple array failed"); fail("json_unpack simple array failed");
json_decref(j); json_decref(j);
@ -105,39 +126,205 @@ int main()
* Invalid cases * 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'", "<format>", 1, 1, 1);
if(!json_unpack_ex(NULL, &error, 0, "[i]"))
fail("json_unpack succeeded with NULL root");
check_error("NULL root value", "<root>", -1, -1, 0);
/* mismatched open/close array/object */ /* mismatched open/close array/object */
j = json_pack("[]"); j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, "[}")) if(!json_unpack_ex(j, &error, 0, "[}"))
fail("json_unpack failed to catch mismatched ']'"); fail("json_unpack failed to catch mismatched ']'");
check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
json_decref(j); json_decref(j);
j = json_pack("{}"); j = json_pack("{}");
if(!json_unpack_ex(j, &error, 0, "{]")) if(!json_unpack_ex(j, &error, 0, "{]"))
fail("json_unpack failed to catch mismatched '}'"); fail("json_unpack failed to catch mismatched '}'");
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
json_decref(j); json_decref(j);
/* missing close array */ /* missing close array */
j = json_pack("[]"); j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, "[")) if(!json_unpack_ex(j, &error, 0, "["))
fail("json_unpack failed to catch missing ']'"); fail("json_unpack failed to catch missing ']'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
json_decref(j); json_decref(j);
/* missing close object */ /* missing close object */
j = json_pack("{}"); j = json_pack("{}");
if(!json_unpack_ex(j, &error, 0, "{")) if(!json_unpack_ex(j, &error, 0, "{"))
fail("json_unpack failed to catch missing '}'"); fail("json_unpack failed to catch missing '}'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
json_decref(j); 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", "<format>", 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", "<format>", 1, 2, 2);
/* NULL format string */ /* NULL format string */
j = json_pack("[]"); j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, NULL)) if(!json_unpack_ex(j, &error, 0, NULL))
fail("json_unpack failed to catch null format string"); fail("json_unpack failed to catch null format string");
check_error("NULL or empty format string", "<format>", -1, -1, 0);
json_decref(j); json_decref(j);
/* NULL string pointer */ /* NULL string pointer */
j = json_string("foobie"); j = json_string("foobie");
if(!json_unpack_ex(j, &error, 0, "s", NULL)) if(!json_unpack_ex(j, &error, 0, "s", NULL))
fail("json_unpack failed to catch null string pointer"); fail("json_unpack failed to catch null string pointer");
check_error("NULL string argument", "<args>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<args>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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", "<validation>", 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'", "<format>", 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'", "<format>", 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'", "<format>", 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'", "<format>", 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", "<validation>", 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", "<validation>", 1, 5, 5);
json_decref(j); json_decref(j);
return 0; return 0;

View File

@ -5,16 +5,51 @@
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
*/ */
#ifndef TESTPROGS_UTIL_H #ifndef UTIL_H
#define TESTPROGS_UTIL_H #define UTIL_H
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <jansson.h>
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
#define fail(msg) \ #define fail(msg) \
do { \ do { \
fprintf(stderr, "%s:%s:%d: %s\n", \ failhdr; \
__FILE__, __FUNCTION__, __LINE__, msg); \ fprintf(stderr, "%s\n", msg); \
exit(1); \ exit(1); \
} while(0) } 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 #endif