str_free(&buffer);
}
+static void test_json_ostream_write_tree(void)
+{
+ string_t *buffer;
+ struct istream *input;
+ struct ostream *output;
+ struct json_ostream *joutput;
+ struct json_tree *jtree, *jtree2;
+ struct json_tree_node *jtnode;
+ const char *data;
+ unsigned int state, pos;
+ int ret;
+
+ buffer = str_new(default_pool, 256);
+ output = o_stream_create_buffer(buffer);
+ o_stream_set_no_error_handling(output, TRUE);
+
+ /* number */
+ test_begin("json ostream write tree - number");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_number_int(
+ json_tree_get_root(jtree), NULL, 23423);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("23423", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* false */
+ test_begin("json ostream write tree - false");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_false(
+ json_tree_get_root(jtree), NULL);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("false", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* null */
+ test_begin("json ostream write tree - null");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_null(
+ json_tree_get_root(jtree), NULL);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("null", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* true */
+ test_begin("json ostream write tree - true");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_true(
+ json_tree_get_root(jtree), NULL);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("true", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* string */
+ test_begin("json ostream write tree - string");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_string(
+ json_tree_get_root(jtree), NULL, "frop");
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("\"frop\"", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* string stream */
+ test_begin("json ostream write tree - string stream");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(
+ json_tree_get_root(jtree), NULL, input);
+ i_stream_unref(&input);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp(
+ "\"AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ\"",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* array */
+ test_begin("json ostream write tree - array");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_array(
+ json_tree_get_root(jtree), NULL);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("[]", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* [ string ] */
+ test_begin("json ostream write tree - array [ string ]");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_array(
+ json_tree_get_root(jtree), NULL);
+ (void)json_tree_node_add_string(jtnode, NULL, "frop");
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("[\"frop\"]", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* [ string stream ] */
+ test_begin("json ostream write tree - array [ string stream ]");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_array(
+ json_tree_get_root(jtree), NULL);
+ data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(jtnode, NULL, input);
+ i_stream_unref(&input);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp(
+ "[\"AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ\"]",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* object */
+ test_begin("json ostream write tree - object");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("{}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { member: string } */
+ test_begin("json ostream write tree - object { member: string }");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ (void)json_tree_node_add_string(jtnode, "frop", "friep");
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("{\"frop\":\"friep\"}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { member: string stream } */
+ test_begin("json ostream write tree - object { member: string stream }");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(jtnode, "frop", input);
+ i_stream_unref(&input);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp(
+ "{\"frop\":"
+ "\"AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ\"}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */
+ test_begin("json ostream write tree - object "
+ "{ \"a\": [{\"d\": 1}], \"b\": [{\"e\": 2}], "
+ "\"c\": [{\"f\": 3}] }");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ jtnode = json_tree_node_add_array(jtnode, "a");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "d", 1);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_add_array(jtnode, "b");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "e", 2);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_add_array(jtnode, "c");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "f", 3);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp(
+ "{\"a\":[{\"d\":1}],\"b\":[{\"e\":2}],\"c\":[{\"f\":3}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */
+ test_begin("json ostream write tree - descended");
+ joutput = json_ostream_create(output, 0);
+ ret = json_ostream_descend_object(joutput, NULL);
+ i_assert(ret > 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "d", 1);
+ ret = json_ostream_write_tree(joutput, "a", jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "e", 2);
+ ret = json_ostream_write_tree(joutput, "b", jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "f", 3);
+ ret = json_ostream_write_tree(joutput, "c", jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_ascend_object(joutput);
+ i_assert(ret > 0);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp(
+ "{\"a\":[{\"d\":1}],\"b\":[{\"e\":2}],\"c\":[{\"f\":3}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* descended, strings */
+ test_begin("json ostream write tree - descended, strings");
+ joutput = json_ostream_create(output, 0);
+ ret = json_ostream_descend_object(joutput, NULL);
+ i_assert(ret > 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(jtnode, "d", "gggggggggg");
+ ret = json_ostream_write_tree(joutput, "a", jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(jtnode, "e", "hhhhhhhhhh");
+ ret = json_ostream_write_tree(joutput, "b", jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(jtnode, "f", "iiiiiiiiii");
+ ret = json_ostream_write_tree(joutput, "c", jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_ascend_object(joutput);
+ i_assert(ret > 0);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp("{\"a\":[{\"d\":\"gggggggggg\"}],"
+ "\"b\":[{\"e\":\"hhhhhhhhhh\"}],"
+ "\"c\":[{\"f\":\"iiiiiiiiii\"}]}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */
+ test_begin("json ostream write tree - nested trees");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "d", 1);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "a", jtree2);
+ json_tree_unref(&jtree2);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "e", 2);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "b", jtree2);
+ json_tree_unref(&jtree2);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "f", 3);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "c", jtree2);
+ json_tree_unref(&jtree2);
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret > 0);
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ i_assert(ret > 0);
+ json_ostream_unref(&joutput);
+ test_assert_strcmp(
+ "{\"a\":[{\"d\":1}],\"b\":[{\"e\":2}],\"c\":[{\"f\":3}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* trickle [1] */
+ test_begin("json ostream write tree - object, trickle[1]");
+ o_stream_set_max_buffer_size(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ jtnode = json_tree_node_add_array(jtnode, "aaaaaa");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "dddddd", 1);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_add_array(jtnode, "bbbbbb");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "eeeeee", 2);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_add_array(jtnode, "cccccc");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "ffffff", 3);
+ joutput = json_ostream_create(output, 0);
+ state = 0;
+ for (pos = 0; pos < 400 && state <= 1; pos++) {
+ o_stream_set_max_buffer_size(output, pos);
+ switch (state) {
+ case 0:
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 1:
+ ret = json_ostream_flush(joutput);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ state++;
+ continue;
+ }
+ }
+ json_ostream_unref(&joutput);
+ test_assert(state == 2);
+ test_assert_strcmp("{\"aaaaaa\":[{\"dddddd\":1}],"
+ "\"bbbbbb\":[{\"eeeeee\":2}],"
+ "\"cccccc\":[{\"ffffff\":3}]}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* trickle [2] */
+ test_begin("json ostream write tree - object, trickle[2]");
+ o_stream_set_max_buffer_size(output, 0);
+ joutput = json_ostream_create(output, 0);
+ state = 0;
+ for (pos = 0; pos < 400 && state <= 7; pos++) {
+ o_stream_set_max_buffer_size(output, pos);
+ switch (state) {
+ case 0:
+ ret = json_ostream_descend_object(joutput, NULL);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ state++;
+ continue;
+ case 1:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(
+ jtnode, "dddddd", 1);
+ state++;
+ /* fall through */
+ case 2:
+ ret = json_ostream_write_tree(joutput, "aaaaaa", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 3:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(
+ jtnode, "eeeeee", 2);
+ state++;
+ /* fall through */
+ case 4:
+ ret = json_ostream_write_tree(joutput, "bbbbbb", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 5:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(
+ jtnode, "ffffff", 3);
+ state++;
+ /* fall through */
+ case 6:
+ ret = json_ostream_write_tree(joutput, "cccccc", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 7:
+ ret = json_ostream_ascend_object(joutput);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ state++;
+ continue;
+ }
+ }
+ json_ostream_unref(&joutput);
+ test_assert(state == 8);
+ test_assert_strcmp("{\"aaaaaa\":[{\"dddddd\":1}],"
+ "\"bbbbbb\":[{\"eeeeee\":2}],"
+ "\"cccccc\":[{\"ffffff\":3}]}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* trickle[3] */
+ test_begin("json ostream write tree - object, trickle[3]");
+ o_stream_set_max_buffer_size(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "dddddd", 1);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "aaaaaa", jtree2);
+ json_tree_unref(&jtree2);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "eeeeee", 2);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "bbbbbb", jtree2);
+ json_tree_unref(&jtree2);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "ffffff", 3);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "cccccc", jtree2);
+ json_tree_unref(&jtree2);
+ joutput = json_ostream_create(output, 0);
+ state = 0;
+ for (pos = 0; pos < 400 && state <= 1; pos++) {
+ o_stream_set_max_buffer_size(output, pos);
+ switch (state) {
+ case 0:
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 1:
+ ret = json_ostream_flush(joutput);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ state++;
+ continue;
+ }
+ }
+ json_ostream_unref(&joutput);
+ test_assert(state == 2);
+ test_assert_strcmp("{\"aaaaaa\":[{\"dddddd\":1}],"
+ "\"bbbbbb\":[{\"eeeeee\":2}],"
+ "\"cccccc\":[{\"ffffff\":3}]}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* trickle [4] */
+ test_begin("json ostream write tree - object, trickle[4]");
+ o_stream_set_max_buffer_size(output, 0);
+ joutput = json_ostream_create(output, 0);
+ state = 0;
+ for (pos = 0; pos < 400 && state <= 7; pos++) {
+ o_stream_set_max_buffer_size(output, pos);
+ switch (state) {
+ case 0:
+ ret = json_ostream_descend_object(joutput, NULL);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ state++;
+ continue;
+ case 1:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(
+ jtnode, "dddddd", "gggggggggg");
+ state++;
+ /* fall through */
+ case 2:
+ ret = json_ostream_write_tree(joutput, "aaaaaa", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 3:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(
+ jtnode, "eeeeee", "hhhhhhhhhh");
+ state++;
+ /* fall through */
+ case 4:
+ ret = json_ostream_write_tree(joutput, "bbbbbb", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 5:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(
+ jtnode, "ffffff", "iiiiiiiiii");
+ state++;
+ /* fall through */
+ case 6:
+ ret = json_ostream_write_tree(joutput, "cccccc", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 7:
+ ret = json_ostream_ascend_object(joutput);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ state++;
+ continue;
+ }
+ }
+ json_ostream_unref(&joutput);
+ test_assert(state == 8);
+ test_assert_strcmp("{\"aaaaaa\":[{\"dddddd\":\"gggggggggg\"}],"
+ "\"bbbbbb\":[{\"eeeeee\":\"hhhhhhhhhh\"}],"
+ "\"cccccc\":[{\"ffffff\":\"iiiiiiiiii\"}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* trickle [5] */
+ test_begin("json ostream write tree - object, trickle[5]");
+ o_stream_set_max_buffer_size(output, 0);
+ joutput = json_ostream_create(output, 0);
+ state = 0;
+ for (pos = 0; pos < 400 && state <= 7; pos++) {
+ o_stream_set_max_buffer_size(output, pos);
+ switch (state) {
+ case 0:
+ ret = json_ostream_descend_object(joutput, NULL);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ state++;
+ continue;
+ case 1:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_text(
+ jtnode, "dddddd", "1234567");
+ state++;
+ /* fall through */
+ case 2:
+ ret = json_ostream_write_tree(joutput, "aaaaaa", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 3:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_text(
+ jtnode, "eeeeee", "[1,2,3,4,5,6,7]");
+ state++;
+ /* fall through */
+ case 4:
+ ret = json_ostream_write_tree(joutput, "bbbbbb", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 5:
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_text(
+ jtnode, "ffffff", "\"1234567\"");
+ state++;
+ /* fall through */
+ case 6:
+ ret = json_ostream_write_tree(joutput, "cccccc", jtree);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 7:
+ ret = json_ostream_ascend_object(joutput);
+ i_assert(ret >= 0);
+ if (ret == 0)
+ break;
+ state++;
+ continue;
+ }
+ }
+ json_ostream_unref(&joutput);
+ test_assert(state == 8);
+ test_assert_strcmp("{\"aaaaaa\":[{\"dddddd\":1234567}],"
+ "\"bbbbbb\":[{\"eeeeee\":[1,2,3,4,5,6,7]}],"
+ "\"cccccc\":[{\"ffffff\":\"1234567\"}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* trickle [6] */
+ test_begin("json ostream write tree - object, trickle[6]");
+ o_stream_set_max_buffer_size(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ jtnode = json_tree_node_add_array(jtnode, "aaaaaa");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ data = "AAAAA";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(jtnode, "dddddd", input);
+ i_stream_unref(&input);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_add_array(jtnode, "bbbbbb");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ data = "BBBBB";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(jtnode, "eeeeee", input);
+ i_stream_unref(&input);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_add_array(jtnode, "cccccc");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ data = "CCCCC";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(jtnode, "ffffff", input);
+ i_stream_unref(&input);
+ joutput = json_ostream_create(output, 0);
+ state = 0;
+ for (pos = 0; pos < 400 && state <= 1; pos++) {
+ o_stream_set_max_buffer_size(output, pos);
+ switch (state) {
+ case 0:
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ i_assert(ret >= 0);
+ if (ret == 0) break;
+ json_tree_unref(&jtree);
+ state++;
+ continue;
+ case 1:
+ ret = json_ostream_flush(joutput);
+ i_assert(ret >= 0);
+ if (ret == 0) break;
+ state++;
+ continue;
+ }
+ }
+ json_ostream_unref(&joutput);
+ test_assert(state == 2);
+ test_assert_strcmp("{\"aaaaaa\":[{\"dddddd\":\"AAAAA\"}],"
+ "\"bbbbbb\":[{\"eeeeee\":\"BBBBB\"}],"
+ "\"cccccc\":[{\"ffffff\":\"CCCCC\"}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ o_stream_destroy(&output);
+ str_free(&buffer);
+}
+
+static void test_json_ostream_nwrite_tree(void)
+{
+ string_t *buffer;
+ struct istream *input;
+ struct ostream *output;
+ struct json_ostream *joutput;
+ struct json_tree *jtree, *jtree2;
+ struct json_tree_node *jtnode;
+ const char *data;
+
+ buffer = str_new(default_pool, 256);
+ output = o_stream_create_buffer(buffer);
+ o_stream_set_no_error_handling(output, TRUE);
+
+ /* number */
+ test_begin("json ostream nwrite tree - number");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_number_int(
+ json_tree_get_root(jtree), NULL, 23423);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("23423", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* false */
+ test_begin("json ostream nwrite tree - false");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_false(
+ json_tree_get_root(jtree), NULL);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("false", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* null */
+ test_begin("json ostream nwrite tree - null");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_null(
+ json_tree_get_root(jtree), NULL);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("null", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* true */
+ test_begin("json ostream nwrite tree - true");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_true(
+ json_tree_get_root(jtree), NULL);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("true", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* string */
+ test_begin("json ostream nwrite tree - string");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_string(
+ json_tree_get_root(jtree), NULL, "frop");
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("\"frop\"", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* string stream */
+ test_begin("json ostream nwrite tree - string stream");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(
+ json_tree_get_root(jtree), NULL, input);
+ i_stream_unref(&input);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp(
+ "\"AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ\"",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* array */
+ test_begin("json ostream nwrite tree - array");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_array(
+ json_tree_get_root(jtree), NULL);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("[]", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* [ string ] */
+ test_begin("json ostream nwrite tree - array [ string ]");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_array(
+ json_tree_get_root(jtree), NULL);
+ (void)json_tree_node_add_string(jtnode, NULL, "frop");
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("[\"frop\"]", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* [ string stream ] */
+ test_begin("json ostream nwrite tree - array [ string stream ]");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_array(
+ json_tree_get_root(jtree), NULL);
+ data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(jtnode, NULL, input);
+ i_stream_unref(&input);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp(
+ "[\"AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ\"]",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* object */
+ test_begin("json ostream nwrite tree - object");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ (void)json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("{}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { member: string } */
+ test_begin("json ostream nwrite tree - object { member: string }");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ (void)json_tree_node_add_string(jtnode, "frop", "friep");
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("{\"frop\":\"friep\"}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { member: string stream } */
+ test_begin("json ostream nwrite tree - "
+ "object { member: string stream }");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ";
+ input = i_stream_create_from_data(data, strlen(data));
+ (void)json_tree_node_add_string_stream(jtnode, "frop", input);
+ i_stream_unref(&input);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp(
+ "{\"frop\":"
+ "\"AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ\"}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */
+ test_begin("json ostream nwrite tree - object "
+ "{ \"a\": [{\"d\": 1}], \"b\": [{\"e\": 2}], "
+ "\"c\": [{\"f\": 3}] }");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_node_add_object(
+ json_tree_get_root(jtree), NULL);
+ jtnode = json_tree_node_add_array(jtnode, "a");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "d", 1);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_add_array(jtnode, "b");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "e", 2);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_get_parent(jtnode);
+ jtnode = json_tree_node_add_array(jtnode, "c");
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "f", 3);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp(
+ "{\"a\":[{\"d\":1}],\"b\":[{\"e\":2}],\"c\":[{\"f\":3}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */
+ test_begin("json ostream nwrite tree - descended");
+ joutput = json_ostream_create(output, 0);
+ json_ostream_ndescend_object(joutput, NULL);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "d", 1);
+ json_ostream_nwrite_tree(joutput, "a", jtree);
+ json_tree_unref(&jtree);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "e", 2);
+ json_ostream_nwrite_tree(joutput, "b", jtree);
+ json_tree_unref(&jtree);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "f", 3);
+ json_ostream_nwrite_tree(joutput, "c", jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nascend_object(joutput);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp(
+ "{\"a\":[{\"d\":1}],\"b\":[{\"e\":2}],\"c\":[{\"f\":3}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* descended, strings */
+ test_begin("json ostream nwrite tree - descended, strings");
+ joutput = json_ostream_create(output, 0);
+ json_ostream_ndescend_object(joutput, NULL);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(jtnode, "d", "gggggggggg");
+ json_ostream_nwrite_tree(joutput, "a", jtree);
+ json_tree_unref(&jtree);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(jtnode, "e", "hhhhhhhhhh");
+ json_ostream_nwrite_tree(joutput, "b", jtree);
+ json_tree_unref(&jtree);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_string(jtnode, "f", "iiiiiiiiii");
+ json_ostream_nwrite_tree(joutput, "c", jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nascend_object(joutput);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp("{\"a\":[{\"d\":\"gggggggggg\"}],"
+ "\"b\":[{\"e\":\"hhhhhhhhhh\"}],"
+ "\"c\":[{\"f\":\"iiiiiiiiii\"}]}", str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */
+ test_begin("json ostream nwrite tree - nested trees");
+ joutput = json_ostream_create(output, 0);
+ jtree = json_tree_create();
+ jtnode = json_tree_get_root(jtree);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "d", 1);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "a", jtree2);
+ json_tree_unref(&jtree2);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "e", 2);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "b", jtree2);
+ json_tree_unref(&jtree2);
+ jtree2 = json_tree_create();
+ jtnode = json_tree_get_root(jtree2);
+ jtnode = json_tree_node_add_array(jtnode, NULL);
+ jtnode = json_tree_node_add_object(jtnode, NULL);
+ (void)json_tree_node_add_number_int(jtnode, "f", 3);
+ jtnode = json_tree_get_root(jtree);
+ json_tree_node_add_subtree(jtnode, "c", jtree2);
+ json_tree_unref(&jtree2);
+ json_ostream_nwrite_tree(joutput, NULL, jtree);
+ json_tree_unref(&jtree);
+ json_ostream_nfinish_destroy(&joutput);
+ test_assert_strcmp(
+ "{\"a\":[{\"d\":1}],\"b\":[{\"e\":2}],\"c\":[{\"f\":3}]}",
+ str_c(buffer));
+ test_end();
+ str_truncate(buffer, 0);
+ output->offset = 0;
+
+ o_stream_destroy(&output);
+ str_free(&buffer);
+}
+
int main(int argc, char *argv[])
{
int c;
static void (*test_functions[])(void) = {
test_json_ostream_write,
test_json_ostream_nwrite,
+ test_json_ostream_write_tree,
+ test_json_ostream_nwrite_tree,
NULL
};
--- /dev/null
+/* Copyright (c) 2017-2023 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "istream.h"
+#include "ostream.h"
+#include "test-common.h"
+
+#include "json-istream.h"
+#include "json-ostream.h"
+
+#include <unistd.h>
+
+static bool debug = FALSE;
+
+struct json_io_test {
+ const char *input;
+ const char *output;
+ struct json_limits limits;
+ enum json_parser_flags flags;
+};
+
+static const struct json_io_test
+tests[] = {
+ {
+ .input = "{\"kty\":\"EC\","
+ "\"crv\":\"P-256\","
+ "\"x\":\"Kp0Y4-Wpt-D9t_2XenFIj0LmvaZByLG69yOisek4aMI\","
+ "\"y\":\"wjEPB5BhH5SRPw1cCN5grWrLCphrW19fCFR8p7c9O5o\","
+ "\"use\":\"sig\","
+ "\"kid\":\"123\","
+ "\"d\":\"Po2z9rs86J2Qb_xWprr4idsWNPlgKf3G8-mftnE2ync\""
+ "}",
+ },
+ {
+ .input =
+ "{\r\n"
+ " \"$schema\": \"http://json-schema.org/draft-06/schema#\",\r\n"
+ " \"$id\": \"http://json-schema.org/draft-06/schema#\",\r\n"
+ " \"title\": \"Core schema meta-schema\",\r\n"
+ " \"definitions\": {\r\n"
+ " \"schemaArray\": {\r\n"
+ " \"type\": \"array\",\r\n"
+ " \"minItems\": 1,\r\n"
+ " \"items\": { \"$ref\": \"#\" }\r\n"
+ " },\r\n"
+ " \"nonNegativeInteger\": {\r\n"
+ " \"type\": \"integer\",\r\n"
+ " \"minimum\": 0\r\n"
+ " },\r\n"
+ " \"nonNegativeIntegerDefault0\": {\r\n"
+ " \"allOf\": [\r\n"
+ " { \"$ref\": \"#/definitions/nonNegativeInteger\" },\r\n"
+ " { \"default\": 0 }\r\n"
+ " ]\r\n"
+ " },\r\n"
+ " \"simpleTypes\": {\r\n"
+ " \"enum\": [\r\n"
+ " \"array\",\r\n"
+ " \"boolean\",\r\n"
+ " \"integer\",\r\n"
+ " \"null\",\r\n"
+ " \"number\",\r\n"
+ " \"object\",\r\n"
+ " \"string\"\r\n"
+ " ]\r\n"
+ " },\r\n"
+ " \"stringArray\": {\r\n"
+ " \"type\": \"array\",\r\n"
+ " \"items\": { \"type\": \"string\" },\r\n"
+ " \"uniqueItems\": true,\r\n"
+ " \"default\": []\r\n"
+ " }\r\n"
+ " },\r\n"
+ " \"type\": [\"object\", \"boolean\"],\r\n"
+ " \"properties\": {\r\n"
+ " \"$id\": {\r\n"
+ " \"type\": \"string\",\r\n"
+ " \"format\": \"uri-reference\"\r\n"
+ " },\r\n"
+ " \"$schema\": {\r\n"
+ " \"type\": \"string\",\r\n"
+ " \"format\": \"uri\"\r\n"
+ " },\r\n"
+ " \"$ref\": {\r\n"
+ " \"type\": \"string\",\r\n"
+ " \"format\": \"uri-reference\"\r\n"
+ " },\r\n"
+ " \"title\": {\r\n"
+ " \"type\": \"string\"\r\n"
+ " },\r\n"
+ " \"description\": {\r\n"
+ " \"type\": \"string\"\r\n"
+ " },\r\n"
+ " \"default\": {},\r\n"
+ " \"multipleOf\": {\r\n"
+ " \"type\": \"number\",\r\n"
+ " \"exclusiveMinimum\": 0\r\n"
+ " },\r\n"
+ " \"maximum\": {\r\n"
+ " \"type\": \"number\"\r\n"
+ " },\r\n"
+ " \"exclusiveMaximum\": {\r\n"
+ " \"type\": \"number\"\r\n"
+ " },\r\n"
+ " \"minimum\": {\r\n"
+ " \"type\": \"number\"\r\n"
+ " },\r\n"
+ " \"exclusiveMinimum\": {\r\n"
+ " \"type\": \"number\"\r\n"
+ " },\r\n"
+ " \"maxLength\": { \"$ref\": \"#/definitions/nonNegativeInteger\" },\r\n"
+ " \"minLength\": { \"$ref\": \"#/definitions/nonNegativeIntegerDefault0\" },\r\n"
+ " \"pattern\": {\r\n"
+ " \"type\": \"string\",\r\n"
+ " \"format\": \"regex\"\r\n"
+ " },\r\n"
+ " \"additionalItems\": { \"$ref\": \"#\" },\r\n"
+ " \"items\": {\r\n"
+ " \"anyOf\": [\r\n"
+ " { \"$ref\": \"#\" },\r\n"
+ " { \"$ref\": \"#/definitions/schemaArray\" }\r\n"
+ " ],\r\n"
+ " \"default\": {}\r\n"
+ " },\r\n"
+ " \"maxItems\": { \"$ref\": \"#/definitions/nonNegativeInteger\" },\r\n"
+ " \"minItems\": { \"$ref\": \"#/definitions/nonNegativeIntegerDefault0\" },\r\n"
+ " \"uniqueItems\": {\r\n"
+ " \"type\": \"boolean\",\r\n"
+ " \"default\": false\r\n"
+ " },\r\n"
+ " \"contains\": { \"$ref\": \"#\" },\r\n"
+ " \"maxProperties\": { \"$ref\": \"#/definitions/nonNegativeInteger\" },\r\n"
+ " \"minProperties\": { \"$ref\": \"#/definitions/nonNegativeIntegerDefault0\" },\r\n"
+ " \"required\": { \"$ref\": \"#/definitions/stringArray\" },\r\n"
+ " \"additionalProperties\": { \"$ref\": \"#\" },\r\n"
+ " \"definitions\": {\r\n"
+ " \"type\": \"object\",\r\n"
+ " \"additionalProperties\": { \"$ref\": \"#\" },\r\n"
+ " \"default\": {}\r\n"
+ " },\r\n"
+ " \"properties\": {\r\n"
+ " \"type\": \"object\",\r\n"
+ " \"additionalProperties\": { \"$ref\": \"#\" },\r\n"
+ " \"default\": {}\r\n"
+ " },\r\n"
+ " \"patternProperties\": {\r\n"
+ " \"type\": \"object\",\r\n"
+ " \"additionalProperties\": { \"$ref\": \"#\" },\r\n"
+ " \"default\": {}\r\n"
+ " },\r\n"
+ " \"dependencies\": {\r\n"
+ " \"type\": \"object\",\r\n"
+ " \"additionalProperties\": {\r\n"
+ " \"anyOf\": [\r\n"
+ " { \"$ref\": \"#\" },\r\n"
+ " { \"$ref\": \"#/definitions/stringArray\" }\r\n"
+ " ]\r\n"
+ " }\r\n"
+ " },\r\n"
+ " \"propertyNames\": { \"$ref\": \"#\" },\r\n"
+ " \"const\": {},\r\n"
+ " \"enum\": {\r\n"
+ " \"type\": \"array\",\r\n"
+ " \"minItems\": 1,\r\n"
+ " \"uniqueItems\": true\r\n"
+ " },\r\n"
+ " \"type\": {\r\n"
+ " \"anyOf\": [\r\n"
+ " { \"$ref\": \"#/definitions/simpleTypes\" },\r\n"
+ " {\r\n"
+ " \"type\": \"array\",\r\n"
+ " \"items\": { \"$ref\": \"#/definitions/simpleTypes\" },\r\n"
+ " \"minItems\": 1,\r\n"
+ " \"uniqueItems\": true\r\n"
+ " }\r\n"
+ " ]\r\n"
+ " },\r\n"
+ " \"format\": { \"type\": \"string\" },\r\n"
+ " \"allOf\": { \"$ref\": \"#/definitions/schemaArray\" },\r\n"
+ " \"anyOf\": { \"$ref\": \"#/definitions/schemaArray\" },\r\n"
+ " \"oneOf\": { \"$ref\": \"#/definitions/schemaArray\" },\r\n"
+ " \"not\": { \"$ref\": \"#\" }\r\n"
+ " },\r\n"
+ " \"default\": {}\r\n"
+ "}\r\n",
+ .output =
+ "{\"$schema\":\"http://json-schema.org/draft-06/schema#\","
+ "\"$id\":\"http://json-schema.org/draft-06/schema#\","
+ "\"title\":\"Core schema meta-schema\",\"definitions\":{"
+ "\"schemaArray\":{\"type\":\"array\",\"minItems\":1,"
+ "\"items\":{\"$ref\":\"#\"}},\"nonNegativeInteger\":{"
+ "\"type\":\"integer\",\"minimum\":0},"
+ "\"nonNegativeIntegerDefault0\":{\"allOf\":["
+ "{\"$ref\":\"#/definitions/nonNegativeInteger\"},"
+ "{\"default\":0}]},\"simpleTypes\":{\"enum\":["
+ "\"array\",\"boolean\",\"integer\",\"null\","
+ "\"number\",\"object\",\"string\"]},\"stringArray\":{"
+ "\"type\":\"array\",\"items\":{\"type\":\"string\"},"
+ "\"uniqueItems\":true,\"default\":[]}},"
+ "\"type\":[\"object\",\"boolean\"],"
+ "\"properties\":{\"$id\":{\"type\":\"string\","
+ "\"format\":\"uri-reference\"},\"$schema\":{"
+ "\"type\":\"string\",\"format\":\"uri\"},"
+ "\"$ref\":{\"type\":\"string\",\"format\":\"uri-reference\""
+ "},\"title\":{\"type\":\"string\"},\"description\":{"
+ "\"type\":\"string\"},\"default\":{},\"multipleOf\":{"
+ "\"type\":\"number\",\"exclusiveMinimum\":0},"
+ "\"maximum\":{\"type\":\"number\"},\"exclusiveMaximum\":{"
+ "\"type\":\"number\"},\"minimum\":{\"type\":\"number\""
+ "},\"exclusiveMinimum\":{\"type\":\"number\"},"
+ "\"maxLength\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},"
+ "\"minLength\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},"
+ "\"pattern\":{\"type\":\"string\",\"format\":\"regex\""
+ "},\"additionalItems\":{\"$ref\":\"#\"},\"items\":{"
+ "\"anyOf\":[{\"$ref\":\"#\"},{\"$ref\":\"#/definitions/schemaArray\"}"
+ "],\"default\":{}},"
+ "\"maxItems\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},"
+ "\"minItems\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},"
+ "\"uniqueItems\":{\"type\":\"boolean\",\"default\":false},"
+ "\"contains\":{\"$ref\":\"#\"},"
+ "\"maxProperties\":{\"$ref\":\"#/definitions/nonNegativeInteger\"},"
+ "\"minProperties\":{\"$ref\":\"#/definitions/nonNegativeIntegerDefault0\"},"
+ "\"required\":{\"$ref\":\"#/definitions/stringArray\"},"
+ "\"additionalProperties\":{\"$ref\":\"#\"},\"definitions\":{"
+ "\"type\":\"object\",\"additionalProperties\":{\"$ref\":\"#\"},"
+ "\"default\":{}},\"properties\":{\"type\":\"object\","
+ "\"additionalProperties\":{\"$ref\":\"#\"},\"default\":{}"
+ "},\"patternProperties\":{\"type\":\"object\","
+ "\"additionalProperties\":{\"$ref\":\"#\"},"
+ "\"default\":{}},\"dependencies\":{\"type\":\"object\","
+ "\"additionalProperties\":{\"anyOf\":[{\"$ref\":\"#\"},"
+ "{\"$ref\":\"#/definitions/stringArray\"}"
+ "]}},\"propertyNames\":{\"$ref\":\"#\"},\"const\":{},"
+ "\"enum\":{\"type\":\"array\",\"minItems\":1,\"uniqueItems\":true"
+ "},\"type\":{\"anyOf\":[{\"$ref\":\"#/definitions/simpleTypes\"},"
+ "{\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/simpleTypes\"},"
+ "\"minItems\":1,\"uniqueItems\":true}]},\"format\":{\"type\":\"string\"},"
+ "\"allOf\":{\"$ref\":\"#/definitions/schemaArray\"},"
+ "\"anyOf\":{\"$ref\":\"#/definitions/schemaArray\"},"
+ "\"oneOf\":{\"$ref\":\"#/definitions/schemaArray\"},"
+ "\"not\":{\"$ref\":\"#\"}},\"default\":{}}"
+ }
+};
+
+static const unsigned tests_count = N_ELEMENTS(tests);
+
+static void test_json_tree_stream_io(void)
+{
+ string_t *outbuf;
+ unsigned int i;
+
+ outbuf = str_new(default_pool, 1024);
+
+ for (i = 0; i < tests_count; i++) T_BEGIN {
+ const struct json_io_test *test;
+ const char *text, *text_out;
+ unsigned int pos, text_len;
+ struct istream *input;
+ struct ostream *output;
+ struct json_istream *jinput;
+ struct json_ostream *joutput;
+ struct json_tree *jtree;
+ const char *error = NULL;
+ int ret = 0;
+
+ test = &tests[i];
+ text = test->input;
+ text_out = test->output;
+ if (text_out == NULL)
+ text_out = test->input;
+ text_len = strlen(text);
+
+ test_begin(t_strdup_printf("json tree stream io [%d]", i));
+
+ buffer_set_used_size(outbuf, 0);
+
+ input = test_istream_create_data(text, text_len);
+ output = o_stream_create_buffer(outbuf);
+ o_stream_set_no_error_handling(output, TRUE);
+
+ jinput = json_istream_create(input, 0, NULL, 0);
+ joutput = json_ostream_create(output, 0);
+
+ o_stream_set_max_buffer_size(output, 0);
+ ret = 0;
+ for (pos = 0; pos <= text_len && ret == 0; pos++) {
+ test_istream_set_size(input, pos);
+ ret = json_istream_read_tree(jinput, &jtree);
+ }
+ test_assert(ret > 0);
+
+ test_istream_set_size(input, text_len);
+ ret = json_istream_finish(&jinput, &error);
+ test_out_reason_quiet("input stream ok (trickle)",
+ ret > 0, error);
+
+ ret = 0;
+ for (pos = 0; pos <= 65535 && ret == 0; pos++) {
+ o_stream_set_max_buffer_size(output, pos);
+ if (jtree != NULL) {
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ if (ret > 0)
+ json_tree_unref(&jtree);
+ }
+ if (jtree == NULL)
+ ret = json_ostream_flush(joutput);
+ }
+ json_ostream_unref(&joutput);
+ test_out_quiet("output stream ok (trickle)", ret > 0);
+
+ test_out_quiet("io match (trickle)",
+ strcmp(text_out, str_c(outbuf)) == 0);
+
+ if (debug) {
+ i_debug("OUT: >%s<", text_out);
+ i_debug("OUT: >%s<", str_c(outbuf));
+ }
+
+ json_tree_unref(&jtree);
+ i_stream_unref(&input);
+ o_stream_unref(&output);
+
+ buffer_set_used_size(outbuf, 0);
+
+ input = test_istream_create_data(text, text_len);
+ output = o_stream_create_buffer(outbuf);
+ o_stream_set_no_error_handling(output, TRUE);
+
+ jinput = json_istream_create(input, 0, NULL, 0);
+ joutput = json_ostream_create(output, 0);
+
+ ret = json_istream_read_tree(jinput, &jtree);
+ test_assert(ret > 0);
+
+ ret = json_istream_finish(&jinput, &error);
+ test_out_reason_quiet("input stream ok (buffer)",
+ ret > 0, error);
+
+ if (jtree != NULL) {
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ if (ret > 0) {
+ json_tree_unref(&jtree);
+ ret = json_ostream_flush(joutput);
+ }
+ }
+ json_ostream_unref(&joutput);
+ test_out_quiet("output stream ok (buffer)", ret > 0);
+
+ test_out_quiet("io match (buffer)",
+ strcmp(text_out, str_c(outbuf)) == 0);
+
+ if (debug) {
+ i_debug("OUT: >%s<", text_out);
+ i_debug("OUT: >%s<", str_c(outbuf));
+ }
+
+ json_tree_unref(&jtree);
+ i_stream_unref(&input);
+ o_stream_unref(&output);
+
+ test_end();
+
+ } T_END;
+
+ buffer_free(&outbuf);
+}
+
+static void test_json_tree_file(const char *file)
+{
+ struct istream *input;
+ struct ostream *output;
+ struct json_istream *jinput;
+ struct json_ostream *joutput;
+ struct json_tree *jtree;
+ int ret = 0;
+
+ input = i_stream_create_file(file, 1024);
+ output = o_stream_create_fd(1, 1024);
+ o_stream_set_no_error_handling(output, TRUE);
+
+ jinput = json_istream_create(input, 0, NULL,
+ JSON_PARSER_FLAG_NUMBERS_AS_STRING);
+ joutput = json_ostream_create(output, 0);
+
+ ret = 0;
+ while (ret == 0)
+ ret = json_istream_read_tree(jinput, &jtree);
+
+ if (ret < 0) {
+ i_fatal("Failed to read JSON: %s",
+ json_istream_get_error(jinput));
+ }
+
+ ret = 0;
+ while (ret == 0) {
+ if (jtree != NULL) {
+ ret = json_ostream_write_tree(joutput, NULL, jtree);
+ if (ret > 0)
+ json_tree_unref(&jtree);
+ }
+ if (jtree == NULL)
+ ret = json_ostream_flush(joutput);
+ }
+
+ if (ret < 0) {
+ i_fatal("Failed to write JSON: %s",
+ o_stream_get_error(output));
+ }
+
+ json_istream_unref(&jinput);
+ json_ostream_unref(&joutput);
+
+ o_stream_nsend_str(output, "\n");
+ i_stream_unref(&input);
+ o_stream_unref(&output);
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+
+ static void (*test_functions[])(void) = {
+ test_json_tree_stream_io,
+ NULL
+ };
+
+ while ((c = getopt(argc, argv, "D")) > 0) {
+ switch (c) {
+ case 'D':
+ debug = TRUE;
+ break;
+ default:
+ i_fatal("Usage: %s [-D]", argv[0]);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0) {
+ test_json_tree_file(argv[0]);
+ return 0;
+ }
+
+ return test_run(test_functions);
+}