struct istream *value_stream, *seekable_stream;
+ struct json_tree *tree;
+ struct json_tree_node *tree_node;
+ unsigned int tree_node_level;
+
char *error;
bool opened:1;
}
static void
-json_istream_parse_list_open(void *context, void *parent_context ATTR_UNUSED,
+json_istream_parse_list_open(void *context, void *parent_context,
const char *name, bool object,
- void **list_context_r ATTR_UNUSED)
+ void **list_context_r)
{
struct json_istream *stream = context;
+ struct json_tree_node *parent = parent_context;
unsigned int node_level = stream->node_level;
i_assert(!stream->node_parsed);
stream->node_level++;
+ if (stream->tree != NULL) {
+ if (parent == NULL)
+ parent = stream->tree_node;
+ if (object) {
+ *list_context_r = (void *)
+ json_tree_node_add_object(parent, name);
+ } else {
+ *list_context_r = (void *)
+ json_tree_node_add_array(parent, name);
+ }
+ return;
+ }
+
if (node_level == stream->read_node_level) {
i_zero(&stream->node);
stream->node.name = name;
}
}
+ if (stream->tree != NULL) {
+ if (stream->node_level < stream->tree_node_level) {
+ stream->end_of_list = TRUE;
+ stream->node_parsed = TRUE;
+ json_parser_interrupt(stream->parser);
+ } else if (stream->node_level == stream->tree_node_level) {
+ if (!json_istream_parse_skip(stream)) {
+ stream->node_parsed = TRUE;
+ json_parser_interrupt(stream->parser);
+ }
+ }
+ return;
+ }
+
if (stream->node_level < stream->read_node_level) {
stream->end_of_list = TRUE;
if (!json_istream_parse_skip(stream)) {
if (stream->skip_to_end || stream->skip_nodes > 0)
return;
+ i_assert(stream->tree == NULL);
i_assert(stream->node_level >= stream->read_node_level);
if (stream->node_level != stream->read_node_level)
}
static void
-json_istream_parse_value(void *context, void *parent_context ATTR_UNUSED,
- const char *name, enum json_type type,
- const struct json_value *value)
+json_istream_parse_value(void *context, void *parent_context, const char *name,
+ enum json_type type, const struct json_value *value)
{
struct json_istream *stream = context;
+ struct json_tree_node *parent = parent_context;
i_assert(!stream->node_parsed);
i_assert(stream->node_level >= stream->read_node_level);
}
}
+ if (stream->tree != NULL) {
+ if (parent == NULL) {
+ /* just starting; parent is not in the syntax tree */
+ parent = stream->tree_node;
+ }
+ (void)json_tree_node_add_value(parent, name, type, value);
+ if (stream->node_level == stream->tree_node_level) {
+ stream->node_parsed = TRUE;
+ json_parser_interrupt(stream->parser);
+ }
+ return;
+ }
+
+ /* not parsing a full tree */
if (stream->node_level != stream->read_node_level)
return;
if (json_istream_parse_skip(stream))
}
json_parser_disable_string_stream(stream->parser);
}
+ if (stream->tree != NULL)
+ json_tree_unref(&stream->tree);
}
int json_istream_read(struct json_istream *stream, struct json_node *node_r)
static void json_istream_ascend_common(struct json_istream *stream)
{
+ if (stream->tree != NULL)
+ json_tree_unref(&stream->tree);
stream->skip_nodes = 0;
stream->node_parsed = FALSE;
stream->member_parsed = FALSE;
node_r);
return 1;
}
+
+/*
+ * Tree values
+ */
+
+static int json_istream_read_tree_common(struct json_istream *stream)
+{
+ const char *error;
+ int ret;
+
+ ret = json_istream_consume_value_stream(stream);
+ if (ret <= 0)
+ return ret;
+ ret = json_parse_more(stream->parser, &error);
+ if (ret < 0) {
+ json_istream_set_error(stream, error);
+ return ret;
+ }
+ if (stream->error != NULL)
+ return -1;
+ if (ret == 0 && !stream->node_parsed) {
+ return 0;
+ }
+ if (ret > 0) {
+ stream->end_of_input = TRUE;
+ if (!stream->node_parsed)
+ return -1;
+ }
+ return 1;
+}
+
+int json_istream_read_tree(struct json_istream *stream,
+ struct json_tree **tree_r)
+{
+ int ret;
+
+ i_assert(tree_r != NULL);
+
+ if (stream->closed) {
+ *tree_r = NULL;
+ return -1;
+ }
+ if (stream->end_of_input) {
+ *tree_r = NULL;
+ return -1;
+ }
+ if (stream->end_of_list) {
+ *tree_r = NULL;
+ return 1;
+ }
+
+ stream->member_parsed = FALSE;
+ if (stream->node_parsed) {
+ struct json_node root_node = stream->node;
+
+ if (stream->tree != NULL) {
+ *tree_r = stream->tree;
+ return 1;
+ }
+
+ i_assert(stream->node.type != JSON_TYPE_NONE);
+ i_assert(!json_node_is_end(&stream->node));
+
+ /* start tree with parsed node */
+ root_node.name = NULL;
+ stream->tree = json_tree_create();
+ stream->tree_node = json_tree_node_add(
+ json_tree_get_root(stream->tree), &root_node);
+
+ stream->node_parsed = FALSE;
+
+ if (json_node_is_singular(&root_node)) {
+ /* return tree with non-list item immediately */
+ *tree_r = stream->tree;
+ stream->tree = NULL;
+ return 1;
+ }
+
+ stream->tree_node_level = stream->read_node_level;
+
+ } else if (stream->tree == NULL) {
+ /* start blank tree */
+ stream->tree = json_tree_create();
+ stream->tree_node = json_tree_get_root(stream->tree);
+ stream->tree_node_level = stream->read_node_level;
+ }
+
+ ret = json_istream_read_tree_common(stream);
+ if (ret <= 0) {
+ *tree_r = NULL;
+ return ret;
+ }
+
+ if (stream->end_of_list) {
+ *tree_r = NULL;
+ return 1;
+ }
+
+ *tree_r = stream->tree;
+ stream->tree = NULL;
+ json_istream_skip(stream);
+ return 1;
+}
+
+int json_istream_read_into_tree_node(struct json_istream *stream,
+ struct json_tree_node *tree_node)
+{
+ int ret;
+
+ if (stream->tree != NULL) {
+ if (stream->node_parsed)
+ return 1;
+ } else {
+ if (!stream->node_parsed) {
+ ret = json_istream_read(stream, NULL);
+ if (ret <= 0 )
+ return ret;
+ }
+
+ struct json_node new_node = stream->node;
+
+ i_assert(new_node.type != JSON_TYPE_NONE);
+ i_assert(!json_node_is_end(&new_node));
+
+ /* start tree branch with parsed node */
+ stream->tree_node = json_tree_node_add(tree_node, &new_node);
+
+ stream->node_parsed = FALSE;
+
+ if (json_node_is_singular(&new_node)) {
+ stream->tree_node = NULL;
+ json_istream_skip(stream);
+ return 1;
+ }
+
+ stream->tree = json_tree_node_get_tree(tree_node);
+ json_tree_ref(stream->tree);
+
+ stream->tree_node_level = stream->read_node_level;
+ }
+
+ ret = json_istream_read_tree_common(stream);
+ if (ret <= 0)
+ return ret;
+
+ json_istream_skip(stream);
+ return 1;
+}
+
+int json_istream_read_into_tree(struct json_istream *stream,
+ struct json_tree *tree)
+{
+ return json_istream_read_into_tree_node(
+ stream, json_tree_get_root(tree));
+}
i_stream_unref(&input);
}
+/*
+ * Test: read tree
+ */
+
+static void test_json_istream_read_tree(void)
+{
+ struct istream *input;
+ struct json_istream *jinput;
+ const char *text;
+ struct json_tree *jtree;
+ struct json_tree_node *jtnode;
+ struct json_node jnode;
+ unsigned int pos, text_len, state;
+ int ret = 0;
+
+ /* number */
+ text = "2234234";
+ text_len = strlen(text);
+
+ input = test_istream_create_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream read tree - number");
+
+ pos = 0; state = 0; ret = 0;
+ while (ret >= 0 && state <= 1) {
+ if (pos <= text_len)
+ pos++;
+ test_istream_set_size(input, pos);
+ ret = json_istream_read_tree(jinput, &jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ json_tree_unref(&jtree);
+ state++;
+ }
+ test_assert(state == 1);
+ test_istream_set_size(input, text_len);
+ test_json_read_success(&jinput);
+
+ test_end();
+
+ json_istream_unref(&jinput);
+ i_stream_unref(&input);
+
+ /* string */
+ text = "\"frop\"";
+ text_len = strlen(text);
+
+ input = test_istream_create_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream read tree - string");
+
+ pos = 0; state = 0; ret = 0;
+ while (ret >= 0 && state <= 1) {
+ if (pos <= text_len)
+ pos++;
+ test_istream_set_size(input, pos);
+ ret = json_istream_read_tree(jinput, &jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_STRING);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ json_tree_unref(&jtree);
+ state++;
+ }
+ test_assert(state == 1);
+ test_istream_set_size(input, text_len);
+ test_json_read_success(&jinput);
+
+ test_end();
+
+ json_istream_unref(&jinput);
+ i_stream_unref(&input);
+
+ /* array */
+ text = "[\"frop\"]";
+ text_len = strlen(text);
+
+ input = test_istream_create_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream read tree - array");
+
+ pos = 0; state = 0; ret = 0;
+ while (ret >= 0 && state <= 1) {
+ if (pos <= text_len)
+ pos++;
+ test_istream_set_size(input, pos);
+ ret = json_istream_read_tree(jinput, &jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ jtnode = json_tree_node_get_child(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_STRING);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ jtnode = json_tree_node_get_parent(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ json_tree_unref(&jtree);
+ state++;
+ }
+ test_assert(state == 1);
+ test_istream_set_size(input, text_len);
+ test_json_read_success(&jinput);
+
+ test_end();
+
+ json_istream_unref(&jinput);
+ i_stream_unref(&input);
+
+ /* array */
+ text = "[\"frop\", {\"a\":1234, \"b\":[1, 2, 3, 4], "
+ "\"c\":1234}, \"frop\"]";
+ text_len = strlen(text);
+
+ input = test_istream_create_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream read tree - sequence");
+
+ pos = 0; state = 0; ret = 0;
+ while (ret >= 0 && state <= 4) {
+ if (pos <= text_len)
+ pos++;
+ test_istream_set_size(input, pos);
+ switch (state) {
+ case 0:
+ ret = json_istream_descend(jinput, &jnode);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ test_assert(json_node_is_array(&jnode));
+ state++;
+ break;
+ case 1:
+ ret = json_istream_read_tree(jinput, &jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_STRING);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ json_tree_unref(&jtree);
+ state++;
+ break;
+ case 2:
+ ret = json_istream_read_tree(jinput, &jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_OBJECT);
+ jtnode = json_tree_node_get_child(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ jtnode = json_tree_node_get_child(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ jtnode = json_tree_node_get_parent(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ jtnode = json_tree_node_get_parent(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_OBJECT);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ json_tree_unref(&jtree);
+ state++;
+ break;
+ case 3:
+ ret = json_istream_read_tree(jinput, &jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_STRING);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ json_tree_unref(&jtree);
+ json_istream_ascend(jinput);
+ state++;
+ break;
+ case 4:
+ ret = json_istream_read(jinput, &jnode);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ state++;
+ break;
+ }
+ }
+ test_assert(state == 4);
+ test_istream_set_size(input, text_len);
+ test_json_read_success(&jinput);
+
+ test_end();
+
+ json_istream_unref(&jinput);
+ i_stream_unref(&input);
+}
+
+/*
+ * Test: read into tree
+ */
+
+static void test_json_istream_read_into_tree(void)
+{
+ struct istream *input;
+ struct json_istream *jinput;
+ const char *text;
+ struct json_tree *jtree;
+ struct json_node jnode;
+ struct json_tree_node *jtnode;
+ unsigned int pos, text_len, state;
+ int ret = 0;
+
+ /* number */
+ jtree = json_tree_create();
+ text = "2234234";
+ text_len = strlen(text);
+
+ input = test_istream_create_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream read into tree - number");
+
+ pos = 0; state = 0; ret = 0;
+ while (ret >= 0 && state <= 1) {
+ if (pos <= text_len)
+ pos++;
+ test_istream_set_size(input, pos);
+ ret = json_istream_read_into_tree(jinput, jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ state++;
+ }
+ test_assert(state == 1);
+ test_istream_set_size(input, text_len);
+ test_json_read_success(&jinput);
+
+ test_end();
+
+ json_istream_unref(&jinput);
+ i_stream_unref(&input);
+ json_tree_unref(&jtree);
+
+ /* string */
+ jtree = json_tree_create();
+ text = "\"frop\"";
+ text_len = strlen(text);
+
+ input = test_istream_create_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream read into tree - string");
+
+ pos = 0; state = 0; ret = 0;
+ while (ret >= 0 && state <= 1) {
+ if (pos <= text_len)
+ pos++;
+ test_istream_set_size(input, pos);
+ ret = json_istream_read_into_tree(jinput, jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_STRING);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ state++;
+ }
+ test_assert(state == 1);
+ test_istream_set_size(input, text_len);
+ test_json_read_success(&jinput);
+
+ test_end();
+
+ json_istream_unref(&jinput);
+ i_stream_unref(&input);
+ json_tree_unref(&jtree);
+
+ /* array */
+ jtree = json_tree_create();
+ text = "[\"frop\"]";
+ text_len = strlen(text);
+
+ input = test_istream_create_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream read into tree - array");
+
+ pos = 0; state = 0; ret = 0;
+ while (ret >= 0 && state <= 1) {
+ if (pos <= text_len)
+ pos++;
+ test_istream_set_size(input, pos);
+ ret = json_istream_read_into_tree(jinput, jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ jtnode = json_tree_node_get_child(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_STRING);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ jtnode = json_tree_node_get_parent(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ state++;
+ }
+ test_assert(state == 1);
+ test_istream_set_size(input, text_len);
+ test_json_read_success(&jinput);
+
+ test_end();
+
+ json_istream_unref(&jinput);
+ i_stream_unref(&input);
+ json_tree_unref(&jtree);
+
+ /* sequence */
+ jtree = json_tree_create();
+ (void)json_tree_node_add_array(json_tree_get_root(jtree), NULL);
+ text = "[\"frop\", {\"a\":1234, \"b\":[1, 2, 3, 4], "
+ "\"c\":1234}, \"frop\"]";
+ text_len = strlen(text);
+
+ input = test_istream_create_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream read into tree - sequence");
+
+ pos = 0; state = 0; ret = 0;
+ while (ret >= 0 && state <= 4) {
+ if (pos <= text_len)
+ pos++;
+ test_istream_set_size(input, pos);
+ switch (state) {
+ case 0:
+ ret = json_istream_descend(jinput, &jnode);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ test_assert(json_node_is_array(&jnode));
+ state++;
+ break;
+ case 1:
+ ret = json_istream_read_into_tree(jinput, jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ state++;
+ break;
+ case 2:
+ ret = json_istream_read_into_tree(jinput, jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ state++;
+ break;
+ case 3:
+ ret = json_istream_read_into_tree(jinput, jtree);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+ state++;
+ break;
+ case 4:
+ ret = json_istream_read(jinput, &jnode);
+ if (ret == 0)
+ continue;
+ if (ret < 0)
+ break;
+
+ jtnode = json_tree_get_root(jtree);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ jtnode = json_tree_node_get_child(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_STRING);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_OBJECT);
+ jtnode = json_tree_node_get_child(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ test_assert_strcmp(json_tree_node_get_name(jtnode),
+ "a");
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ test_assert_strcmp(json_tree_node_get_name(jtnode),
+ "b");
+ jtnode = json_tree_node_get_child(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ jtnode = json_tree_node_get_parent(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_NUMBER);
+ test_assert_strcmp(json_tree_node_get_name(jtnode),
+ "c");
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ jtnode = json_tree_node_get_parent(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_OBJECT);
+ jtnode = json_tree_node_get_next(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_STRING);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+ jtnode = json_tree_node_get_parent(jtnode);
+ test_assert(json_tree_node_get_type(jtnode) ==
+ JSON_TYPE_ARRAY);
+ test_assert(json_tree_node_get_next(jtnode) == NULL);
+
+ json_istream_ascend(jinput);
+ state++;
+ break;
+ }
+ }
+ test_assert(state == 5);
+ test_istream_set_size(input, text_len);
+ test_json_read_success(&jinput);
+
+ test_end();
+
+ i_stream_unref(&input);
+ json_tree_unref(&jtree);
+}
+
/*
* Test: read stream
*/
struct json_istream *jinput;
const char *text, *error;
struct json_node jnode;
+ struct json_tree *jtree;
unsigned int text_len;
int ret = 0;
json_istream_unref(&jinput);
i_stream_unref(&input);
+ /* tree parse error */
+ text = "{\"a\":[0],\"b\":[1],\"c\":[2],\"d\":[\"unclosed array\"}";
+ text_len = strlen(text);
+
+ input = i_stream_create_from_data(text, text_len);
+ jinput = json_istream_create(input, 0, NULL, 0);
+
+ test_begin("json istream error - tree parse error");
+
+ ret = json_istream_read_tree(jinput, &jtree);
+ error = json_istream_get_error(jinput);
+ test_out_reason("read failure", (ret < 0 && error != NULL), error);
+
+ test_end();
+
+ json_istream_unref(&jinput);
+ i_stream_unref(&input);
+
/* spurious data at end of input */
text = "[\"data\"][\"junk\"]";
text_len = strlen(text);
test_json_istream_read_buffer,
test_json_istream_read_trickle,
test_json_istream_finish,
+ test_json_istream_read_tree,
+ test_json_istream_read_into_tree,
test_json_istream_read_stream,
test_json_istream_tokens_buffer,
test_json_istream_tokens_trickle,