/*
 * libfyaml-test-core.c - libfyaml core testing harness
 *
 * Copyright (c) 2019 Pantelis Antoniou <pantelis.antoniou@konsulko.com>
 *
 * SPDX-License-Identifier: MIT
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>

#include <check.h>

#include <libfyaml.h>

#include "fy-check.h"

START_TEST(doc_build_simple)
{
	struct fy_document *fyd;

	/* setup */
	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	/* cleanup */
	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_build_parse_check)
{
	struct fy_document *fyd;
	char *buf;

	/* build document (with comments, newlines etc) */
	fyd = fy_document_build_from_string(NULL, "#comment\n[ 42,  \n  12 ] # comment\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* convert to string */
	buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* compare with expected result */
	ck_assert_str_eq(buf, "[42, 12]\n");

	free(buf);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_build_scalar)
{
	struct fy_document *fyd;

	/* build document */
	fyd = fy_document_build_from_string(NULL, "plain scalar # comment", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* compare with expected result */
	ck_assert_str_eq(fy_node_get_scalar0(fy_document_root(fyd)), "plain scalar");

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_build_sequence)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	int count;
	void *iter;

	/* build document */
	fyd = fy_document_build_from_string(NULL, "[ 10, 11, foo ] # comment", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* check for correct count value */
	count = fy_node_sequence_item_count(fy_document_root(fyd));
	ck_assert_int_eq(count, 3);

	/* try forward iterator first */
	iter = NULL;

	/* 0 */
	fyn = fy_node_sequence_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "10");

	/* 1 */
	fyn = fy_node_sequence_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "11");

	/* 2 */
	fyn = fy_node_sequence_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "foo");

	/* final, iterator must return NULL */
	fyn = fy_node_sequence_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_eq(fyn, NULL);

	/* reverse iterator */
	iter = NULL;

	/* 2 */
	fyn = fy_node_sequence_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "foo");

	/* 1 */
	fyn = fy_node_sequence_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "11");

	/* 0 */
	fyn = fy_node_sequence_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "10");

	/* final, iterator must return NULL */
	fyn = fy_node_sequence_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_eq(fyn, NULL);

	/* do forward index based accesses */

	/* 0 */
	fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), 0);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "10");

	/* 1 */
	fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), 1);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "11");

	/* 2 */
	fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), 2);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "foo");

	/* 3, it must not exist */
	fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), 3);
	ck_assert_ptr_eq(fyn, NULL);

	/* do backward index based accesses */

	/* 2 */
	fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), -1);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "foo");

	/* 1 */
	fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), -2);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "11");

	/* 0 */
	fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), -3);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "10");

	/* -1, it must not exist */
	fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), -4);
	ck_assert_ptr_eq(fyn, NULL);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_build_mapping)
{
	struct fy_document *fyd;
	struct fy_node_pair *fynp;
	int count;
	void *iter;

	fyd = fy_document_build_from_string(NULL, "{ foo: 10, bar : 20, baz: [100, 101], [frob, 1]: boo }", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* check for correct count value */
	count = fy_node_mapping_item_count(fy_document_root(fyd));
	ck_assert_int_eq(count, 4);

	/* forward iterator first */
	iter = NULL;

	/* 0 */
	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "foo");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "10");

	/* 1 */
	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "bar");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "20");

	/* 2 */
	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "baz");
	ck_assert_int_eq(fy_node_sequence_item_count(fy_node_pair_value(fynp)), 2);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_value(fynp), 0)), "100");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_value(fynp), 1)), "101");

	/* 3 */
	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_int_eq(fy_node_sequence_item_count(fy_node_pair_key(fynp)), 2);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_key(fynp), 0)), "frob");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_key(fynp), 1)), "1");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "boo");

	/* 4, it must not exist */
	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_eq(fynp, NULL);

	/* reverse iterator */
	iter = NULL;

	/* 3 */
	fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_int_eq(fy_node_sequence_item_count(fy_node_pair_key(fynp)), 2);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_key(fynp), 0)), "frob");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_key(fynp), 1)), "1");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "boo");

	/* 2 */
	fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "baz");
	ck_assert_int_eq(fy_node_sequence_item_count(fy_node_pair_value(fynp)), 2);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_value(fynp), 0)), "100");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_value(fynp), 1)), "101");

	/* 1 */
	fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "bar");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "20");

	/* 0 */
	fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "foo");
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "10");

	/* -1, it must not exist */
	fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_eq(fynp, NULL);

	/* key lookups (note how only the contents are compared) */
	ck_assert(fy_node_compare_string(fy_node_mapping_lookup_by_string(fy_document_root(fyd), "foo", FY_NT), "10", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_mapping_lookup_by_string(fy_document_root(fyd), "bar", FY_NT), "20", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_mapping_lookup_by_string(fy_document_root(fyd), "baz", FY_NT), "- 100\n- 101", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_mapping_lookup_by_string(fy_document_root(fyd), "- 'frob'\n- \"\x31\"", FY_NT), "boo", FY_NT) == true);

	fy_document_destroy(fyd);
	fyd = NULL;
}
END_TEST

START_TEST(doc_path_access)
{
	struct fy_document *fyd;
	struct fy_node *fyn;

	/* build document */
	fyd = fy_document_build_from_string(NULL, "{ "
		"foo: 10, bar : 20, baz:{ frob: boo }, "
		"frooz: [ seq1, { key: value} ], \"zero\\0zero\" : 0, "
		"{ key2: value2 }: { key3: value3 } "
		"}", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* check that getting root node works */
	fyn = fy_node_by_path(fy_document_root(fyd), "/", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fy_document_root(fyd));

	/* check access to scalars */
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW), "10", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "bar", FY_NT, FYNWF_DONT_FOLLOW), "20", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "baz/frob", FY_NT, FYNWF_DONT_FOLLOW), "boo", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/frooz/0", FY_NT, FYNWF_DONT_FOLLOW), "seq1", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/frooz/1/key", FY_NT, FYNWF_DONT_FOLLOW), "value", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "\"zero\\0zero\"", FY_NT, FYNWF_DONT_FOLLOW), "0", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/{ key2: value2 }/key3", FY_NT, FYNWF_DONT_FOLLOW), "value3", FY_NT) == true);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_path_node)
{
	struct fy_document *fyd;
	char *path;

	/* build document */
	fyd = fy_document_build_from_string(NULL, "{ "
		"foo: 10, bar : 20, baz:{ frob: boo }, "
		"frooz: [ seq1, { key: value} ], \"zero\\0zero\" : 0, "
		"{ key2: value2 }: { key3: value3 } "
		"}", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	path = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/", FY_NT, FYNWF_DONT_FOLLOW));
	ck_assert_str_eq(path, "/");
	free(path);

	path = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/frooz", FY_NT, FYNWF_DONT_FOLLOW));
	ck_assert_str_eq(path, "/frooz");
	free(path);

	path = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/frooz/0", FY_NT, FYNWF_DONT_FOLLOW));
	ck_assert_str_eq(path, "/frooz/0");
	free(path);

	path = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/{ key2: value2 }/key3", FY_NT, FYNWF_DONT_FOLLOW));
	ck_assert_str_eq(path, "/{key2: value2}/key3");
	free(path);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_path_parent)
{
	struct fy_document *fyd;
	struct fy_node *fyn_root, *fyn_foo, *fyn_bar, *fyn_baz, *fyn_frob, *fyn_ten;
	struct fy_node *fyn_deep, *fyn_deeper;
	char *path;

	/* build document */
	fyd = fy_document_build_from_string(NULL, "{ "
		"foo: 10, bar : [ ten, 20 ], baz:{ frob: boo, deep: { deeper: yet } }, "
		"}", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_root, NULL);

	fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_foo, NULL);

	fyn_bar = fy_node_by_path(fyn_root, "/bar", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_bar, NULL);

	fyn_baz = fy_node_by_path(fyn_root, "/baz", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_baz, NULL);

	fyn_frob = fy_node_by_path(fyn_root, "/baz/frob", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_frob, NULL);

	fyn_ten = fy_node_by_path(fyn_root, "/bar/0", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_ten, NULL);

	fyn_deep = fy_node_by_path(fyn_root, "/baz/deep", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_deep, NULL);

	fyn_deeper = fy_node_by_path(fyn_root, "/baz/deep/deeper", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_deeper, NULL);

	/* check parent paths of foo, frob, ten */
	path = fy_node_get_parent_address(fyn_foo);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "foo");
	free(path);

	path = fy_node_get_parent_address(fyn_frob);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "frob");
	free(path);

	path = fy_node_get_parent_address(fyn_ten);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "0");
	free(path);

	/* check relative paths to root */
	path = fy_node_get_path_relative_to(NULL, fyn_foo);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "foo");
	free(path);

	path = fy_node_get_path_relative_to(fyn_root, fyn_foo);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "foo");
	free(path);

	path = fy_node_get_path_relative_to(fyn_root, fyn_frob);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "baz/frob");
	free(path);

	path = fy_node_get_path_relative_to(fyn_root, fyn_ten);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "bar/0");
	free(path);

	/* check relative paths to other parents */
	path = fy_node_get_path_relative_to(fyn_baz, fyn_frob);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "frob");
	free(path);

	path = fy_node_get_path_relative_to(fyn_bar, fyn_ten);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "0");
	free(path);

	path = fy_node_get_path_relative_to(fyn_baz, fyn_deeper);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "deep/deeper");
	free(path);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_short_path)
{
	struct fy_document *fyd;
	struct fy_node *fyn_root, *fyn_foo, *fyn_notfoo, *fyn_bar, *fyn_baz;
	const char *str;

	/* build document */
	fyd = fy_document_build_from_string(NULL,
		"--- &r\n"
		"  foo: &f\n"
		"    bar: [ 0, two, baz: what ]\n"
		"    frob: true\n"
		"  notfoo: false\n"
		, FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_root, NULL);

	fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_foo, NULL);

	fyn_notfoo = fy_node_by_path(fyn_root, "/notfoo", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_notfoo, NULL);

	fyn_bar = fy_node_by_path(fyn_root, "/foo/bar", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_bar, NULL);

	fyn_baz = fy_node_by_path(fyn_root, "/foo/bar/2/baz", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_baz, NULL);

	str = fy_node_get_short_path_alloca(fyn_root);
	ck_assert_ptr_ne(str, NULL);
	ck_assert_str_eq(str, "*r");
	ck_assert_ptr_eq(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_root);

	str = fy_node_get_short_path_alloca(fyn_foo);
	ck_assert_ptr_ne(str, NULL);
	ck_assert_str_eq(str, "*f");
	ck_assert_ptr_eq(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_foo);

	str = fy_node_get_short_path_alloca(fyn_notfoo);
	ck_assert_ptr_ne(str, NULL);
	ck_assert_str_eq(str, "*r/notfoo");
	ck_assert_ptr_eq(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_notfoo);

	str = fy_node_get_short_path_alloca(fyn_bar);
	ck_assert_ptr_ne(str, NULL);
	ck_assert_str_eq(str, "*f/bar");
	ck_assert_ptr_eq(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_bar);

	str = fy_node_get_short_path_alloca(fyn_baz);
	ck_assert_ptr_ne(str, NULL);
	ck_assert_str_eq(str, "*f/bar/2/baz");
	ck_assert_ptr_eq(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_baz);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_scalar_path)
{
	struct fy_document *fyd;
	struct fy_node *fyn_root, *fyn_foo;

	/* build document */
	fyd = fy_document_build_from_string(NULL, "--- foo\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_root, NULL);

	/* get the scalar root and verify */
	fyn_foo = fy_node_by_path(fyn_root, "/", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_foo, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fyn_foo), "foo");

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_scalar_path_array)
{
	struct fy_document *fyd;
	struct fy_node *fyn_root, *fynt;

	/* build document */
	fyd = fy_document_build_from_string(NULL, "--- [ foo, bar, baz ]\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_root, NULL);

	/* get the scalars in the array and verify */
	fynt = fy_node_by_path(fyn_root, "/0", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fynt, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fynt), "foo");

	fynt = fy_node_by_path(fyn_root, "/1", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fynt, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fynt), "bar");

	fynt = fy_node_by_path(fyn_root, "/2", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fynt, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fynt), "baz");

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_nearest_anchor)
{
	struct fy_document *fyd;
	struct fy_node *fyn, *fyn_root, *fyn_foo, *fyn_notfoo, *fyn_bar, *fyn_baz;

	/* build document */
	fyd = fy_document_build_from_string(NULL,
		"--- &r\n"
		"  foo: &f\n"
		"    bar: [ 0, two, baz: what ]\n"
		"    frob: true\n"
		"  notfoo: false\n"
		, FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_root, NULL);

	fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_foo, NULL);

	fyn_notfoo = fy_node_by_path(fyn_root, "/notfoo", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_notfoo, NULL);

	fyn_bar = fy_node_by_path(fyn_root, "/foo/bar", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_bar, NULL);

	fyn_baz = fy_node_by_path(fyn_root, "/foo/bar/2/baz", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_baz, NULL);

	/* get nearest anchor of root (is root) */
	fyn = fy_anchor_node(fy_node_get_nearest_anchor(fyn_root));
	ck_assert_ptr_eq(fyn, fyn_root);

	/* get nearest anchor of notfoo (is root) */
	fyn = fy_anchor_node(fy_node_get_nearest_anchor(fyn_notfoo));
	ck_assert_ptr_eq(fyn, fyn_root);

	/* get nearest anchor of baz (is foo) */
	fyn = fy_anchor_node(fy_node_get_nearest_anchor(fyn_baz));
	ck_assert_ptr_eq(fyn, fyn_foo);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_references)
{
	struct fy_document *fyd;
	struct fy_node *fyn, *fyn_ref, *fyn_root, *fyn_foo, *fyn_notfoo, *fyn_bar, *fyn_baz;
	char *path;

	/* build document */
	fyd = fy_document_build_from_string(NULL,
		"---\n"
		"  foo: &f\n"
		"    bar: [ 0, two, baz: what ]\n"
		"    frob: true\n"
		"  notfoo: false\n"
		, FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_root, NULL);

	fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_foo, NULL);

	fyn_notfoo = fy_node_by_path(fyn_root, "/notfoo", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_notfoo, NULL);

	fyn_bar = fy_node_by_path(fyn_root, "/foo/bar", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_bar, NULL);

	fyn_baz = fy_node_by_path(fyn_root, "/foo/bar/2/baz", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_baz, NULL);

	/* get reference to root */
	path = fy_node_get_reference(fyn_root);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "*/");
	free(path);

	/* get reference to /foo */
	path = fy_node_get_reference(fyn_foo);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "*f");
	free(path);

	/* get reference to /notfoo */
	path = fy_node_get_reference(fyn_notfoo);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "*/notfoo");
	free(path);

	/* get reference to /foo/bar/2/baz */
	path = fy_node_get_reference(fyn_baz);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "*/foo/bar/2/baz");
	free(path);

	/* create reference to root and verify it points there */
	fyn_ref = fy_node_create_reference(fyn_root);
	ck_assert_ptr_ne(fyn_ref, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_root);
	fy_node_free(fyn_ref);

	/* get reference to /foo */
	fyn_ref = fy_node_create_reference(fyn_foo);
	ck_assert_ptr_ne(path, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_foo);
	fy_node_free(fyn_ref);

	/* get reference to /notfoo */
	fyn_ref = fy_node_create_reference(fyn_notfoo);
	ck_assert_ptr_ne(path, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_notfoo);
	fy_node_free(fyn_ref);

	/* get reference to /bar */
	fyn_ref = fy_node_create_reference(fyn_bar);
	ck_assert_ptr_ne(path, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_bar);
	fy_node_free(fyn_ref);

	/* get reference to /baz */
	fyn_ref = fy_node_create_reference(fyn_baz);
	ck_assert_ptr_ne(path, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_baz);
	fy_node_free(fyn_ref);

	/* get relative reference to /foo starting at / */
	path = fy_node_get_relative_reference(fyn_root, fyn_foo);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "*/foo");
	free(path);

	/* get relative reference to /foo/bar/2/baz starting at / */
	path = fy_node_get_relative_reference(fyn_root, fyn_baz);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "*/foo/bar/2/baz");
	free(path);

	/* get relative reference to /foo/bar/2/baz starting at /foo */
	path = fy_node_get_relative_reference(fyn_foo, fyn_baz);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "*f/bar/2/baz");
	free(path);

	/* get relative reference to /notfoo at /foo (will return absolute) */
	path = fy_node_get_relative_reference(fyn_foo, fyn_notfoo);
	ck_assert_ptr_ne(path, NULL);
	ck_assert_str_eq(path, "*/notfoo");
	free(path);

	/* create relative reference to /foo starting at / */
	fyn_ref = fy_node_create_relative_reference(fyn_root, fyn_foo);
	ck_assert_ptr_ne(fyn_ref, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_foo);
	fy_node_free(fyn_ref);

	/* create relative reference to /foo/bar/2/baz starting at / */
	fyn_ref = fy_node_create_relative_reference(fyn_root, fyn_baz);
	ck_assert_ptr_ne(fyn_ref, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_baz);
	fy_node_free(fyn_ref);

	/* create relative reference to /foo/bar/2/baz starting at /foo */
	fyn_ref = fy_node_create_relative_reference(fyn_foo, fyn_baz);
	ck_assert_ptr_ne(fyn_ref, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_baz);
	fy_node_free(fyn_ref);

	/* create relative reference to /notfoo starting at /foo (will use absolute) */
	fyn_ref = fy_node_create_relative_reference(fyn_foo, fyn_notfoo);
	ck_assert_ptr_ne(fyn_ref, NULL);
	fyn = fy_node_resolve_alias(fyn_ref);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_notfoo);
	fy_node_free(fyn_ref);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_nearest_child_of)
{
	struct fy_document *fyd;
	struct fy_node *fyn, *fyn_root, *fyn_foo, *fyn_bar, *fyn_baz;

	/* build document */
	fyd = fy_document_build_from_string(NULL,
		"foo:\n"
		"  bar:\n"
		"    barz: [ zero, baz: true ]\n"
		"  frooz: notfoo\n"
		, FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_root, NULL);

	fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_foo, NULL);

	fyn_bar = fy_node_by_path(fyn_root, "/foo/bar", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_bar, NULL);

	fyn_baz = fy_node_by_path(fyn_root, "/foo/bar/barz/1/baz", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_baz, NULL);

	/* nearest child to the root of /foo is /foo */
	fyn = fy_node_get_nearest_child_of(fyn_root, fyn_foo);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_foo);

	/* nearest child to the root of /foo/bar/barz/1/baz is /foo */
	fyn = fy_node_get_nearest_child_of(fyn_root, fyn_baz);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_foo);

	/* nearest child to foo of /foo/bar/barz/1/baz is /foo/bar */
	fyn = fy_node_get_nearest_child_of(fyn_foo, fyn_baz);
	ck_assert_ptr_ne(fyn, NULL);
	ck_assert_ptr_eq(fyn, fyn_bar);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_create_empty_seq1)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	char *buf;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_build_from_string(fyd, "[ ]", FY_NT);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);

	/* convert to string */
	buf = fy_emit_node_to_string(fy_document_root(fyd), FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* compare with expected result */
	ck_assert_str_eq(buf, "[]");

	free(buf);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_create_empty_seq2)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	char *buf;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_create_sequence(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);

	/* convert to string */
	buf = fy_emit_node_to_string(fy_document_root(fyd), FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* compare with expected result */
	ck_assert_str_eq(buf, "[]");

	free(buf);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_create_empty_map1)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	char *buf;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_build_from_string(fyd, "{ }", FY_NT);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);

	/* convert to string */
	buf = fy_emit_node_to_string(fy_document_root(fyd), FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* compare with expected result */
	ck_assert_str_eq(buf, "{}");

	free(buf);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_create_empty_map2)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	char *buf;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_create_mapping(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);

	/* convert to string */
	buf = fy_emit_node_to_string(fy_document_root(fyd), FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* compare with expected result */
	ck_assert_str_eq(buf, "{}");

	free(buf);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_create_test_seq1)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	int ret;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_create_sequence(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	ret = fy_node_sequence_append(fyn, fy_node_create_scalar(fyd, "foo", FY_NT));
	ck_assert_int_eq(ret, 0);

	ret = fy_node_sequence_append(fyn, fy_node_create_scalar(fyd, "bar", FY_NT));
	ck_assert_int_eq(ret, 0);

	ret = fy_node_sequence_append(fyn, fy_node_build_from_string(fyd, "{ baz: frooz }", FY_NT));
	ck_assert_int_eq(ret, 0);

	fy_document_set_root(fyd, fyn);

	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/0", FY_NT, FYNWF_DONT_FOLLOW), "foo", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/1", FY_NT, FYNWF_DONT_FOLLOW), "bar", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/2/baz", FY_NT, FYNWF_DONT_FOLLOW), "frooz", FY_NT) == true);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_create_test_map1)
{
	struct fy_document *fyd;
	struct fy_node *fyn, *fyn1, *fyn2, *fyn3;
	int ret;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_create_mapping(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	ret = fy_node_mapping_append(fyn,
			fy_node_build_from_string(fyd, "seq", FY_NT),
			fy_node_build_from_string(fyd, "[ zero, one ]", FY_NT));
	ck_assert_int_eq(ret, 0);

	ret = fy_node_mapping_append(fyn, NULL,
			fy_node_build_from_string(fyd, "value-of-null-key", FY_NT));
	ck_assert_int_eq(ret, 0);

	ret = fy_node_mapping_append(fyn,
			fy_node_build_from_string(fyd, "key-of-null-value", FY_NT), NULL);
	ck_assert_int_eq(ret, 0);

	fy_document_set_root(fyd, fyn);

	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/seq/0", FY_NT, FYNWF_DONT_FOLLOW), "zero", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/seq/1", FY_NT, FYNWF_DONT_FOLLOW), "one", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/''", FY_NT, FYNWF_DONT_FOLLOW), "value-of-null-key", FY_NT) == true);

	fyn1 = fy_node_by_path(fy_document_root(fyd), "/key-of-null-value", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_eq(fyn1, NULL);

	/* try to append duplicate key (it should fail) */
	fyn2 = fy_node_build_from_string(fyd, "seq", FY_NT);
	ck_assert_ptr_ne(fyn2, NULL);
	fyn3 = fy_node_create_scalar(fyd, "dupl", FY_NT);
	ck_assert_ptr_ne(fyn3, NULL);
	ret = fy_node_mapping_append(fyn, fyn2, fyn3);
	ck_assert_int_ne(ret, 0);

	fy_node_free(fyn3);
	fy_node_free(fyn2);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_insert_remove_seq)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	int ret;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fy_document_set_root(fyd, fy_node_build_from_string(fyd, "[ one, two, four ]", FY_NT));

	/* check that the order is correct */
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/0", FY_NT, FYNWF_DONT_FOLLOW), "one", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/1", FY_NT, FYNWF_DONT_FOLLOW), "two", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/2", FY_NT, FYNWF_DONT_FOLLOW), "four", FY_NT) == true);

	ret = fy_node_sequence_append(fy_document_root(fyd), fy_node_build_from_string(fyd, "five", FY_NT));
	ck_assert_int_eq(ret, 0);

	ret = fy_node_sequence_prepend(fy_document_root(fyd), fy_node_build_from_string(fyd, "zero", FY_NT));
	ck_assert_int_eq(ret, 0);

	ret = fy_node_sequence_insert_after(fy_document_root(fyd),
			fy_node_by_path(fy_document_root(fyd), "/2", FY_NT, FYNWF_DONT_FOLLOW),
			fy_node_build_from_string(fyd, "three", FY_NT));
	ck_assert_int_eq(ret, 0);

	ret = fy_node_sequence_insert_before(fy_document_root(fyd),
			fy_node_by_path(fy_document_root(fyd), "/3", FY_NT, FYNWF_DONT_FOLLOW),
			fy_node_build_from_string(fyd, "two-and-a-half", FY_NT));
	ck_assert_int_eq(ret, 0);

	fyn = fy_node_sequence_remove(fy_document_root(fyd),
			fy_node_by_path(fy_document_root(fyd), "/3", FY_NT, FYNWF_DONT_FOLLOW));
	ck_assert_ptr_ne(fyn, NULL);

	fy_node_free(fyn);

	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/0", FY_NT, FYNWF_DONT_FOLLOW), "zero", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/1", FY_NT, FYNWF_DONT_FOLLOW), "one", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/2", FY_NT, FYNWF_DONT_FOLLOW), "two", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/3", FY_NT, FYNWF_DONT_FOLLOW), "three", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/4", FY_NT, FYNWF_DONT_FOLLOW), "four", FY_NT) == true);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_insert_remove_map)
{
	struct fy_document *fyd;
	struct fy_node *fyn, *fyn_key;
	int ret;

	fyd = fy_document_build_from_string(NULL, "{ one: 1, two: 2, four: 4 }", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* check that the order is correct */
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/one", FY_NT, FYNWF_DONT_FOLLOW), "1", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/two", FY_NT, FYNWF_DONT_FOLLOW), "2", FY_NT) == true);
	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/four", FY_NT, FYNWF_DONT_FOLLOW), "4", FY_NT) == true);

	ret = fy_node_mapping_append(fy_document_root(fyd),
			fy_node_build_from_string(fyd, "three", FY_NT),
			fy_node_build_from_string(fyd, "3", FY_NT));
	ck_assert_int_eq(ret, 0);

	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/three", FY_NT, FYNWF_DONT_FOLLOW), "3", FY_NT) == true);

	ret = fy_node_mapping_prepend(fy_document_root(fyd),
			fy_node_build_from_string(fyd, "zero", FY_NT),
			fy_node_build_from_string(fyd, "0", FY_NT));
	ck_assert_int_eq(ret, 0);

	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/zero", FY_NT, FYNWF_DONT_FOLLOW), "0", FY_NT) == true);

	ret = fy_node_mapping_append(fy_document_root(fyd),
			fy_node_build_from_string(fyd, "two-and-a-half", FY_NT),
			fy_node_build_from_string(fyd, "2.5", FY_NT));
	ck_assert_int_eq(ret, 0);

	ck_assert(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/two-and-a-half", FY_NT, FYNWF_DONT_FOLLOW), "2.5", FY_NT) == true);

	fyn_key = fy_node_build_from_string(fyd, "two-and-a-half", FY_NT);
	ck_assert_ptr_ne(fyn_key, NULL);

	fyn = fy_node_mapping_remove_by_key(fy_document_root(fyd), fyn_key);
	ck_assert_ptr_ne(fyn, NULL);

	fy_node_free(fyn_key);
	fy_node_free(fyn);

	/* it must be removed */
	fyn = fy_node_by_path(fy_document_root(fyd), "/two-and-a-half", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_eq(fyn, NULL);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_sort)
{
	struct fy_document *fyd;
	void *fynp;
	void *iter;
	int ret, count;

	fyd = fy_document_build_from_string(NULL, "{ a: 5, { z: bar }: 1, z: 7, "
				      "[ a, b, c] : 3, { a: whee } : 2 , "
				      "b: 6, [ z ]: 4 }", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	ret = fy_node_sort(fy_document_root(fyd), NULL, NULL);
	ck_assert_int_eq(ret, 0);

	/* check for correct count value */
	count = fy_node_mapping_item_count(fy_document_root(fyd));
	ck_assert_int_eq(count, 7);

	/* forward iterator first */
	iter = NULL;

	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "1");

	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "2");

	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "3");

	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "4");

	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "5");

	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "6");

	fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_value(fynp)), "7");

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_mapping_sort)
{
	struct fy_document *fyd;
	struct fy_node *fyn_root, *fyn_inner;
	void *fynp;
	void *iter;
	int ret;

	/* outer keys: c, a, b — inner mapping under "a" has keys z, x, y */
	fyd = fy_document_build_from_string(NULL,
		"{ c: 3, a: { z: 1, x: 2, y: 3 }, b: 2 }", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);

	ret = fy_node_mapping_sort(fyn_root, NULL, NULL);
	ck_assert_int_eq(ret, 0);

	/* outer mapping should now be sorted: a, b, c */
	iter = NULL;

	fynp = fy_node_mapping_iterate(fyn_root, &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "a");

	fynp = fy_node_mapping_iterate(fyn_root, &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "b");

	fynp = fy_node_mapping_iterate(fyn_root, &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "c");

	/* inner mapping under "a" should still be unsorted: z, x, y */
	fyn_inner = fy_node_by_path(fyn_root, "/a", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_inner, NULL);

	iter = NULL;

	fynp = fy_node_mapping_iterate(fyn_inner, &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "z");

	fynp = fy_node_mapping_iterate(fyn_inner, &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "x");

	fynp = fy_node_mapping_iterate(fyn_inner, &iter);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_pair_key(fynp)), "y");

	fy_document_destroy(fyd);
}
END_TEST

static int seq_sort_cmp_scalar(struct fy_node *a, struct fy_node *b, void *arg)
{
	const char *sa = fy_node_get_scalar0(a);
	const char *sb = fy_node_get_scalar0(b);
	if (!sa) sa = "";
	if (!sb) sb = "";
	return strcmp(sa, sb);
}

START_TEST(doc_sequence_sort_basic)
{
	struct fy_document *fyd;
	struct fy_node *fyn_seq;
	struct fy_node *fyn;
	int ret;

	fyd = fy_document_build_from_string(NULL, "[c, a, b]", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_seq = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_seq, NULL);

	ret = fy_node_sequence_sort(fyn_seq, seq_sort_cmp_scalar, NULL);
	ck_assert_int_eq(ret, 0);

	fyn = fy_node_sequence_get_by_index(fyn_seq, 0);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "a");

	fyn = fy_node_sequence_get_by_index(fyn_seq, 1);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "b");

	fyn = fy_node_sequence_get_by_index(fyn_seq, 2);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "c");

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_sequence_sort_already_sorted)
{
	struct fy_document *fyd;
	struct fy_node *fyn_seq, *fyn;
	int ret;

	fyd = fy_document_build_from_string(NULL, "[a, b, c]", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_seq = fy_document_root(fyd);
	ret = fy_node_sequence_sort(fyn_seq, seq_sort_cmp_scalar, NULL);
	ck_assert_int_eq(ret, 0);

	fyn = fy_node_sequence_get_by_index(fyn_seq, 0);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "a");
	fyn = fy_node_sequence_get_by_index(fyn_seq, 1);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "b");
	fyn = fy_node_sequence_get_by_index(fyn_seq, 2);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "c");

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_sequence_sort_single)
{
	struct fy_document *fyd;
	struct fy_node *fyn_seq, *fyn;
	int ret;

	fyd = fy_document_build_from_string(NULL, "[a]", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_seq = fy_document_root(fyd);
	ret = fy_node_sequence_sort(fyn_seq, seq_sort_cmp_scalar, NULL);
	ck_assert_int_eq(ret, 0);

	fyn = fy_node_sequence_get_by_index(fyn_seq, 0);
	ck_assert_str_eq(fy_node_get_scalar0(fyn), "a");

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_sequence_sort_empty)
{
	struct fy_document *fyd;
	struct fy_node *fyn_seq;
	int ret;

	fyd = fy_document_build_from_string(NULL, "[]", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_seq = fy_document_root(fyd);
	ret = fy_node_sequence_sort(fyn_seq, seq_sort_cmp_scalar, NULL);
	ck_assert_int_eq(ret, 0);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_sequence_sort_non_sequence)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	int ret;

	fyd = fy_document_build_from_string(NULL, "{a: 1}", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_document_root(fyd);
	ret = fy_node_sequence_sort(fyn, seq_sort_cmp_scalar, NULL);
	ck_assert_int_eq(ret, -1);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_sequence_sort_null)
{
	int ret;

	ret = fy_node_sequence_sort(NULL, seq_sort_cmp_scalar, NULL);
	ck_assert_int_eq(ret, -1);
}
END_TEST

START_TEST(doc_sequence_sort_flow_style_preserved)
{
	struct fy_document *fyd;
	struct fy_node *fyn_seq;
	char *output;
	int ret;

	fyd = fy_document_build_from_string(NULL, "[c, a, b]", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_seq = fy_document_root(fyd);
	ret = fy_node_sequence_sort(fyn_seq, seq_sort_cmp_scalar, NULL);
	ck_assert_int_eq(ret, 0);

	output = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(output, NULL);
	ck_assert_str_eq(output, "[a, b, c]\n");
	free(output);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_sequence_sort_block_style_preserved)
{
	struct fy_document *fyd;
	struct fy_node *fyn_seq;
	char *output;
	int ret;

	fyd = fy_document_build_from_string(NULL,
		"- c\n- a\n- b\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_seq = fy_document_root(fyd);
	ret = fy_node_sequence_sort(fyn_seq, seq_sort_cmp_scalar, NULL);
	ck_assert_int_eq(ret, 0);

	output = fy_emit_document_to_string(fyd, FYECF_MODE_BLOCK);
	ck_assert_ptr_ne(output, NULL);
	ck_assert_str_eq(output, "- a\n- b\n- c\n");
	free(output);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_insert_at)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	int rc;

	/* build document */
	fyd = fy_document_build_from_string(NULL,
		"apiVersion: apps/v1\n"
		"kind: Deployment\n"
		"metadata:\n"
		"  annotations:\n"
		"    x: 2024-06-12t14:50:43z\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* verify that x exists */
	fyn = fy_node_by_path(fy_document_root(fyd), "/metadata/annotations/x", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn, NULL);

	/* delete the x node */
	rc = fy_document_insert_at(fyd, "/metadata/annotations/x", FY_NT, NULL);
	ck_assert_int_eq(rc, 0);

	/* verify that x now gone */
	fyn = fy_node_by_path(fy_document_root(fyd), "/metadata/annotations/x", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_eq(fyn, NULL);

	/* verify that y does not exist */
	fyn = fy_node_by_path(fy_document_root(fyd), "/metadata/annotations/y", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_eq(fyn, NULL);

	/* insert a y node */
	rc = fy_document_insert_at(fyd, "/metadata/annotations", FY_NT,
			fy_node_buildf(fyd, "y: 2024-06-12t14:50:43z"));
	ck_assert_int_eq(rc, 0);

	/* verify that y exists now */
	fyn = fy_node_by_path(fy_document_root(fyd), "/metadata/annotations/y", FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_destroy(fyd);
}
END_TEST

static char *join_docs(const char *tgt_text, const char *tgt_path,
		       const char *src_text, const char *src_path,
		       const char *emit_path)
{
	struct fy_document *fyd_tgt, *fyd_src;
	struct fy_node *fyn_tgt, *fyn_src, *fyn_emit;
	char *output;
	int ret;

	/* insert which overwrites root ( <map> <- <scalar> ) */
	fyd_tgt = fy_document_build_from_string(NULL, tgt_text, FY_NT);
	ck_assert_ptr_ne(fyd_tgt, NULL);

	fyd_src = fy_document_build_from_string(NULL, src_text, FY_NT);
	ck_assert_ptr_ne(fyd_src, NULL);

	fyn_tgt = fy_node_by_path(fy_document_root(fyd_tgt), tgt_path, FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_tgt, NULL);

	fyn_src = fy_node_by_path(fy_document_root(fyd_src), src_path, FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_src, NULL);

	ret = fy_node_insert(fyn_tgt, fyn_src);
	ck_assert_int_eq(ret, 0);

	ret = fy_document_set_parent(fyd_tgt, fyd_src);
	ck_assert_int_eq(ret, 0);

	fyn_emit = fy_node_by_path(fy_document_root(fyd_tgt), emit_path, FY_NT, FYNWF_DONT_FOLLOW);
	ck_assert_ptr_ne(fyn_emit, NULL);

	output = fy_emit_node_to_string(fyn_emit, FYECF_MODE_FLOW_ONELINE | FYECF_WIDTH_INF);
	ck_assert_ptr_ne(output, NULL);

	fy_document_destroy(fyd_tgt);

	return output;
}

START_TEST(doc_join_scalar_to_scalar)
{
	char *output;

	output = join_docs(
			"foo", "/",	/* target */
			"bar", "/",	/* source */
			"/");		/* emit path */

	ck_assert_str_eq(output, "bar");
	free(output);
}
END_TEST

START_TEST(doc_join_scalar_to_map)
{
	char *output;

	output = join_docs(
			"{ foo: baz }", "/",	/* target */
			"bar", "/",		/* source */
			"/");			/* emit path */

	ck_assert_str_eq(output, "bar");
	free(output);
}
END_TEST

START_TEST(doc_join_scalar_to_seq)
{
	char *output;

	output = join_docs(
			"[ foo, baz ]", "/",	/* target */
			"bar", "/",		/* source */
			"/");			/* emit path */

	ck_assert_str_eq(output, "bar");
	free(output);
}
END_TEST

START_TEST(doc_join_map_to_scalar)
{
	char *output;

	output = join_docs(
			"foo", "/",		/* target */
			"{bar: baz}", "/",		/* source */
			"/");			/* emit path */

	ck_assert_str_eq(output, "{bar: baz}");
	free(output);
}
END_TEST

START_TEST(doc_join_map_to_seq)
{
	char *output;

	output = join_docs(
			"[foo, frooz]", "/",	/* target */
			"{bar: baz}", "/",	/* source */
			"/");			/* emit path */

	ck_assert_str_eq(output, "{bar: baz}");
	free(output);
}
END_TEST

START_TEST(doc_join_map_to_map)
{
	char *output;

	output = join_docs(
			"{foo: frooz}", "/",	/* target */
			"{bar: baz}", "/",	/* source */
			"/");			/* emit path */

	ck_assert_str_eq(output, "{foo: frooz, bar: baz}");
	free(output);
}
END_TEST

START_TEST(doc_join_seq_to_scalar)
{
	char *output;

	output = join_docs(
			"foo", "/",		/* target */
			"[bar, baz]", "/",		/* source */
			"/");			/* emit path */

	ck_assert_str_eq(output, "[bar, baz]");
	free(output);
}
END_TEST

START_TEST(doc_join_seq_to_seq)
{
	char *output;

	output = join_docs(
			"[foo, frooz]", "/",	/* target */
			"[bar, baz]", "/",	/* source */
			"/");			/* emit path */

	ck_assert_str_eq(output, "[foo, frooz, bar, baz]");
	free(output);
}
END_TEST

START_TEST(doc_join_seq_to_map)
{
	char *output;

	output = join_docs(
			"{foo: frooz}", "/",	/* target */
			"[bar, baz]", "/",	/* source */
			"/");			/* emit path */

	ck_assert_str_eq(output, "[bar, baz]");
	free(output);
}
END_TEST

START_TEST(doc_join_tags)
{
	char *output;

	output = join_docs(
		"%TAG !a! tag:a.com,2019:\n"
		"---\n"
		"- !a!foo\n"
		"  foo: bar\n", "/",
		"%TAG !b! tag:b.com,2019:\n"
		"---\n"
		"- !b!bar\n"
		"  something: other\n", "/",
		"/");

	ck_assert_str_eq(output, "[!a!foo {foo: bar}, !b!bar {something: other}]");
	free(output);
}
END_TEST

START_TEST(doc_build_with_tags)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	struct fy_token *fyt;
	char *buf;
	int rc;

	/* build document */
	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	/* create a sequence and set it as root */
	fyn = fy_node_create_sequence(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);
	fyn = NULL;

	/* create a node, containing a new tag */
	fyn = fy_node_build_from_string(fyd, "%TAG !e! tag:example.com,2000:app/\n---\n- foo\n- !e!foo bar\n", FY_NT);
	ck_assert_ptr_ne(fyn, NULL);

	/* append it to the root of the document */
	rc = fy_node_sequence_append(fy_document_root(fyd), fyn);
	ck_assert_int_eq(rc, 0);
	fyn = NULL;

	/* there must be a new tag */
	fyt = fy_document_tag_directive_lookup(fyd, "!e!");
	ck_assert_ptr_ne(fyt, NULL);

	/* try to build another, but with a different !e! prefix, it must fail */
	fyn = fy_node_build_from_string(fyd, "%TAG !e! tag:example.com,2019:app/\n---\n- foo\n- !e!foo bar\n", FY_NT);
	ck_assert_ptr_eq(fyn, NULL);

	/* manually add a tag */
	rc = fy_document_tag_directive_add(fyd, "!f!", "tag:example.com,2019:f/");
	ck_assert_int_eq(rc, 0);

	/* build a node with a tag that's already in the document */
	fyn = fy_node_build_from_string(fyd, "!f!whiz frooz\n", FY_NT);
	ck_assert_ptr_ne(fyn, NULL);

	/* append it to the root of the document */
	rc = fy_node_sequence_append(fy_document_root(fyd), fyn);
	ck_assert_int_eq(rc, 0);
	fyn = NULL;

	/* convert to string */
	buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	free(buf);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(doc_attach_check)
{
	struct fy_document *fyd;
	struct fy_node *fyn, *fyn_seq, *fyn_map;
	struct fy_node *fyn_foo, *fyn_foo2, *fyn_bar, *fyn_baz;
	struct fy_node_pair *fynp;
	int rc;

	/* build document */
	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	/* create a sequence */
	fyn_seq = fy_node_create_sequence(fyd);
	ck_assert_ptr_ne(fyn_seq, NULL);

	/* create a mapping */
	fyn_map = fy_node_create_mapping(fyd);
	ck_assert_ptr_ne(fyn_map, NULL);

	/* create a simple scalar node foo */
	fyn_foo = fy_node_build_from_string(fyd, "foo", FY_NT);
	ck_assert_ptr_ne(fyn_foo, NULL);

	/* create another simple scalar node bar */
	fyn_bar = fy_node_build_from_string(fyd, "bar", FY_NT);
	ck_assert_ptr_ne(fyn_bar, NULL);

	/* create another simple scalar node baz */
	fyn_baz = fy_node_build_from_string(fyd, "baz", FY_NT);
	ck_assert_ptr_ne(fyn_baz, NULL);

	/* create a scalar node with the same content as foo */
	fyn_foo2 = fy_node_build_from_string(fyd, "foo", FY_NT);
	ck_assert_ptr_ne(fyn_foo2, NULL);

	/* set the root as the sequence */
	rc = fy_document_set_root(fyd, fyn_seq);
	ck_assert_int_eq(rc, 0);

	/* should fail since fyn_seq is now attached */
	rc = fy_document_set_root(fyd, fyn_seq);
	ck_assert_int_ne(rc, 0);

	/* freeing should fail, since it's attached too */
	rc = fy_node_free(fyn_seq);
	ck_assert_int_ne(rc, 0);

	/* append it to the sequence */
	rc = fy_node_sequence_append(fyn_seq, fyn_foo);
	ck_assert_int_eq(rc, 0);

	/* freeing should fail, since it's attached to the seq */
	rc = fy_node_free(fyn_foo);
	ck_assert_int_ne(rc, 0);

	/* trying to append it to the sequence again should fail */
	rc = fy_node_sequence_append(fyn_seq, fyn_foo);
	ck_assert_int_ne(rc, 0);

	/* append the mapping to the sequence */
	rc = fy_node_sequence_append(fyn_seq, fyn_map);
	ck_assert_int_eq(rc, 0);

	/* this should fail, since foo is attached to the sequence */
	rc = fy_node_mapping_append(fyn_map, fyn_foo, fyn_bar);
	ck_assert_int_ne(rc, 0);

	/* this should be OK, since foo2 is not attached */
	rc = fy_node_mapping_append(fyn_map, fyn_foo2, fyn_bar);
	ck_assert_int_eq(rc, 0);

	/* remove foo from the sequence */
	fyn = fy_node_sequence_remove(fyn_seq, fyn_foo);
	ck_assert_ptr_eq(fyn, fyn_foo);

	/* trying to append the same key should fail */
	rc = fy_node_mapping_append(fyn_map, fyn_foo, NULL);
	ck_assert_int_ne(rc, 0);

	/* append the baz: NULL mapping */
	rc = fy_node_mapping_append(fyn_map, fyn_baz, NULL);
	ck_assert_int_eq(rc, 0);

	/* get the baz: null node pair */
	fynp = fy_node_mapping_lookup_pair(fyn_map, fyn_baz);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_ptr_eq(fy_node_pair_key(fynp), fyn_baz);
	ck_assert_ptr_eq(fy_node_pair_value(fynp), NULL);

	/* trying to set the same key in the mapping should fail */
	rc = fy_node_pair_set_key(fynp, fyn_foo);
	ck_assert_int_ne(rc, 0);

	/* get the foo: bar node pair */
	fynp = fy_node_mapping_lookup_pair(fyn_map, fyn_foo);
	ck_assert_ptr_ne(fynp, NULL);
	ck_assert_ptr_eq(fy_node_pair_key(fynp), fyn_foo2);
	ck_assert_ptr_eq(fy_node_pair_value(fynp), fyn_bar);

	/* we're setting the same key to the mapping, but that's OK
	 * since the key is replaced */
	rc = fy_node_pair_set_key(fynp, fyn_foo);
	ck_assert_int_eq(rc, 0);

	/* fyn_foo has been freed */
	fyn_foo = NULL;

	/* convert to string */
	rc = fy_emit_document_to_fp(fyd, FYECF_MODE_FLOW_ONELINE, stderr);
	ck_assert_int_eq(rc, 0);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(manual_scalar_esc)
{
#undef MANUAL_SCALAR_ESC
#undef MANUAL_SCALAR_ESC_TXT
#define MANUAL_SCALAR_ESC "\\\"\0\a\b\t\v\f\r\x1b\xc2\x85\xc2\xa0\xe2\x80\xa8\xe2\x80\xa9"
#define MANUAL_SCALAR_ESC_TXT "\"\\\\\\\"\\0\\a\\b\\t\\v\\f\\r\\e\\N\\_\\L\\P\""
	const char *what = MANUAL_SCALAR_ESC;
	size_t what_sz = sizeof(MANUAL_SCALAR_ESC) - 1;
	struct fy_document *fyd;
	struct fy_node *fyn;
	char *buf;
	const char *buf2;
	size_t sz2;
	int rc;

	/* build document */
	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	/* create a manual scalar with all the escapes */
	fyn = fy_node_create_scalar(fyd, what, what_sz);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);
	fyn = NULL;

	/* emit to a buffer */
	buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* destroy the old document */
	fy_document_destroy(fyd);
	fyd = NULL;

	/* verify that the resulting document is in the escaped form */
	ck_assert_str_eq(buf, MANUAL_SCALAR_ESC_TXT "\n");

	/* now load the result back and verify that it contains exactly the same */
	fyd = fy_document_build_from_string(NULL, buf, FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* get the scalar content */
	buf2 = fy_node_get_scalar(fy_document_root(fyd), &sz2);
	ck_assert_ptr_ne(buf2, NULL);

	/* sizes must match */
	ck_assert_int_eq(what_sz, sz2);

	/* and the strings too */
	rc = memcmp(what, buf2, what_sz);
	ck_assert_int_eq(rc, 0);

	/* free the document */
	fy_document_destroy(fyd);
	fyd = NULL;

	free(buf);
}
END_TEST

START_TEST(manual_scalar_quoted)
{
#undef MANUAL_SCALAR_QUOTED
#undef MANUAL_SCALAR_QUOTED_TXT
#define MANUAL_SCALAR_QUOTED "&foo"
#define MANUAL_SCALAR_QUOTED_TXT "\"&foo\""
	const char *what = MANUAL_SCALAR_QUOTED;
	size_t what_sz = sizeof(MANUAL_SCALAR_QUOTED) - 1;
	struct fy_document *fyd;
	struct fy_node *fyn;
	char *buf;
	const char *buf2;
	size_t sz2;
	int rc;

	/* build document */
	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	/* create a manual scalar with all the escapes */
	fyn = fy_node_create_scalar(fyd, what, what_sz);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);
	fyn = NULL;

	/* emit to a buffer */
	buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* destroy the old document */
	fy_document_destroy(fyd);
	fyd = NULL;

	/* verify that the resulting document is in the escaped form */
	ck_assert_str_eq(buf, MANUAL_SCALAR_QUOTED_TXT "\n");

	/* now load the result back and verify that it contains exactly the same */
	fyd = fy_document_build_from_string(NULL, buf, FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* get the scalar content */
	buf2 = fy_node_get_scalar(fy_document_root(fyd), &sz2);
	ck_assert_ptr_ne(buf2, NULL);

	/* sizes must match */
	ck_assert_int_eq(what_sz, sz2);

	/* and the strings too */
	rc = memcmp(what, buf2, what_sz);
	ck_assert_int_eq(rc, 0);

	/* free the document */
	fy_document_destroy(fyd);
	fyd = NULL;

	free(buf);
}
END_TEST

START_TEST(manual_scalar_copy)
{
#undef MANUAL_SCALAR_COPY
#define MANUAL_SCALAR_COPY "foo"
	const char *what = MANUAL_SCALAR_COPY;
	size_t what_sz = sizeof(MANUAL_SCALAR_COPY) - 1;
	char *what_copy;
	struct fy_document *fyd;
	struct fy_node *fyn;
	char *buf;

	/* build document */
	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	what_copy = malloc(what_sz);
	ck_assert_ptr_ne(what_copy, NULL);
	memcpy(what_copy, what, what_sz);

	/* create a manual scalar with all the escapes */
	fyn = fy_node_create_scalar_copy(fyd, what_copy, what_sz);
	ck_assert_ptr_ne(fyn, NULL);

	/* free the data */
	free(what_copy);

	fy_document_set_root(fyd, fyn);
	fyn = NULL;

	/* emit to a buffer */
	buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* verify that the resulting document is the one we used + '\n' */
	ck_assert_str_eq(buf, MANUAL_SCALAR_COPY "\n");

	/* destroy the old document */
	fy_document_destroy(fyd);
	fyd = NULL;

	free(buf);

}
END_TEST

START_TEST(manual_scalarf)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	char *buf;

	/* build document */
	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	/* create a manual scalar using the printf interface */
	fyn = fy_node_create_scalarf(fyd, "foo%d", 13);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);
	fyn = NULL;

	/* emit to a buffer */
	buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* verify that the resulting document is the one we used + '\n' */
	ck_assert_str_eq(buf, "foo13" "\n");

	/* destroy the old document */
	fy_document_destroy(fyd);
	fyd = NULL;

	free(buf);
}
END_TEST

START_TEST(manual_valid_anchor)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	int ret;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_create_sequence(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);

	fyn = fy_node_create_scalar(fyd, "foo", FY_NT);
	ck_assert_ptr_ne(fyn, NULL);

	ret = fy_node_sequence_append(fy_document_root(fyd), fyn);
	ck_assert_int_eq(ret, 0);

	/* create a valid anchor */
	ret = fy_node_set_anchor(fyn, "foo", FY_NT);
	ck_assert_int_eq(ret, 0);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(manual_invalid_anchor)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	int ret;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_create_sequence(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);

	fyn = fy_node_create_scalar(fyd, "foo", FY_NT);
	ck_assert_ptr_ne(fyn, NULL);

	ret = fy_node_sequence_append(fy_document_root(fyd), fyn);
	ck_assert_int_eq(ret, 0);

	/* create an invalid anchor */
	ret = fy_node_set_anchor(fyn, "*foo", FY_NT);
	ck_assert_int_ne(ret, 0);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(manual_anchor_removal)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	int ret;

	fyd = fy_document_create(NULL);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_node_create_sequence(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fy_document_set_root(fyd, fyn);

	fyn = fy_node_create_scalar(fyd, "foo", FY_NT);
	ck_assert_ptr_ne(fyn, NULL);

	ret = fy_node_sequence_append(fy_document_root(fyd), fyn);
	ck_assert_int_eq(ret, 0);

	/* create a valid anchor */
	ret = fy_node_set_anchor(fyn, "foo", FY_NT);
	ck_assert_int_eq(ret, 0);

	fprintf(stderr, "---\n# with anchor\n");
	fy_emit_document_to_fp(fyd, FYECF_MODE_FLOW_ONELINE, stderr);

	/* should fail (an anchor already exists) */
	ret = fy_node_set_anchor(fyn, "bar", FY_NT);
	ck_assert_int_ne(ret, 0);

	/* should succeed */
	ret = fy_node_remove_anchor(fyn);
	ck_assert_int_eq(ret, 0);

	fprintf(stderr, "---\n# without anchor\n");
	fy_emit_document_to_fp(fyd, FYECF_MODE_FLOW_ONELINE, stderr);

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(manual_block_flow_mix)
{
	struct fy_document *fyd;
	struct fy_node *fyn_mapping, *fyn_key, *fyn_value;
	char *buf;
	int ret;

	fyd = fy_document_build_from_string(NULL, "--- &root\n{\n}\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_mapping = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_mapping, NULL);

	ck_assert(fy_node_is_mapping(fyn_mapping) == true);

	fyn_key = fy_node_create_scalar(fyd, "key", FY_NT);
	ck_assert_ptr_ne(fyn_key, NULL);

	fyn_value = fy_node_build_from_string(fyd, "|\n  literal\n", FY_NT);
	ck_assert_ptr_ne(fyn_value, NULL);

	ret = fy_node_mapping_append(fyn_mapping, fyn_key, fyn_value);
	ck_assert_int_eq(ret, 0);

	/* emit document to buffer */
	buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);

	/* destroy the first document */
	fy_document_destroy(fyd);
	fyd = NULL;

	/* read the emitted document back */
	fyd = fy_document_build_from_string(NULL, buf, FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* compare with expected result */
	ck_assert_str_eq(fy_node_get_scalar0(fy_node_by_path(fy_document_root(fyd), "/key", FY_NT, FYNWF_DONT_FOLLOW)), "literal\n");

	/* destroy the second document */
	fy_document_destroy(fyd);
	fyd = NULL;

	free(buf);

}
END_TEST

START_TEST(alloca_check)
{
	struct fy_document *fyd;
	char *buf;
	const char *abuf;

	/* build document */
	fyd = fy_document_build_from_string(NULL, "{ "
		"foo: 10, bar : [ ten, 20 ], baz:{ frob: boo, deep: { deeper: yet } }, "
		"}", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	/* fy_emit_document_to_string*() */
	buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);
	abuf = fy_emit_document_to_string_alloca(fyd, FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(abuf, NULL);
	ck_assert_str_eq(buf, abuf);
	free(buf);

	/* fy_emit_node_to_string*() */
	buf = fy_emit_node_to_string(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW), FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(buf, NULL);
	abuf = fy_emit_node_to_string_alloca(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW), FYECF_MODE_FLOW_ONELINE);
	ck_assert_ptr_ne(abuf, NULL);
	ck_assert_str_eq(buf, abuf);
	free(buf);

	/* path check eq */
	buf = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW));
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "/foo");
	abuf = fy_node_get_path_alloca(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW));
	ck_assert_ptr_ne(abuf, NULL);
	ck_assert_str_eq(abuf, "/foo");
	ck_assert_str_eq(buf, abuf);
	free(buf);

	/* check that a bad path is "" */
	abuf = fy_node_get_path_alloca(NULL);
	ck_assert_ptr_ne(abuf, NULL);
	ck_assert_str_eq(abuf, "");

	fy_document_destroy(fyd);

}
END_TEST

START_TEST(scanf_check)
{
	struct fy_document *fyd;
	struct fy_node *fyn_root;
	int ret, ival;
	char sval[256];

	/* build document */
	fyd = fy_document_build_from_string(NULL, "{ "
		"foo: 10, bar : 20, baz:{ frob: boo }, "
		"frooz: [ 1, { key: value }, three ]"
		"}", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn_root = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn_root, NULL);

	/* check scanf accesses to scalars */
	ret = fy_node_scanf(fyn_root, "/foo %d", &ival);
	ck_assert_int_eq(ret, 1);
	ck_assert_int_eq(ival, 10);

	ret = fy_node_scanf(fyn_root, "/bar %d", &ival);
	ck_assert_int_eq(ret, 1);
	ck_assert_int_eq(ival, 20);

	ret = fy_node_scanf(fyn_root, "/baz/frob %s", sval);
	ck_assert_int_eq(ret, 1);
	ck_assert_str_eq(sval, "boo");

	ret = fy_node_scanf(fyn_root, "/frooz/0 %d", &ival);
	ck_assert_int_eq(ret, 1);
	ck_assert_int_eq(ival, 1);

	ret = fy_node_scanf(fyn_root, "/frooz/1/key %s", sval);
	ck_assert_int_eq(ret, 1);
	ck_assert_str_eq(sval, "value");

	ret = fy_node_scanf(fyn_root, "/frooz/2 %s", sval);
	ck_assert_int_eq(ret, 1);
	ck_assert_str_eq(sval, "three");

	fy_document_destroy(fyd);
}
END_TEST

START_TEST(token_test)
{
        struct fy_document *fyd;
        struct fy_node *fyn_sequence, *fyn_mapping, *fyn_scalar;
        struct fy_token *fyn_sequence_start, *fyn_sequence_end,
            *fyn_mapping_start, *fyn_mapping_end, *fyn_scalar_token;
        struct fy_parse_cfg cfg = {
            .flags =  FYPCF_PARSE_COMMENTS
        };

        /* build document */

        fyd = fy_document_build_from_string(&cfg, "- name: key\n"
                                                     "  value: value\n", FY_NT);


        /* Root (sequence) */
        fyn_sequence = fy_document_root(fyd);
        ck_assert_ptr_ne(fyn_sequence, NULL);

        fyn_sequence_start = fy_node_get_start_token(fyn_sequence);
        ck_assert_ptr_ne(fyn_sequence_start, NULL);

        ck_assert_ptr_ne(fy_token_start_mark(fyn_sequence_start), NULL);

        ck_assert_int_eq(fy_token_start_mark(fyn_sequence_start)->line, 0);
        ck_assert_int_eq(fy_token_start_mark(fyn_sequence_start)->column, 0);

        fyn_sequence_end = fy_node_get_end_token(fyn_sequence);
        ck_assert_ptr_ne(fyn_sequence_end, NULL);

        ck_assert_int_eq(fy_token_end_mark(fyn_sequence_end)->line, 2);
        ck_assert_int_eq(fy_token_end_mark(fyn_sequence_end)->column, 0);

        /* Mapping (sequence item) */
        fyn_mapping = fy_node_sequence_get_by_index(fyn_sequence, 0);
        ck_assert_ptr_ne(fyn_mapping, NULL);

        fyn_mapping_start = fy_node_get_start_token(fyn_mapping);
        ck_assert_ptr_ne(fyn_mapping_start, NULL);

        ck_assert_ptr_ne(fy_token_start_mark(fyn_mapping_start), NULL);

        ck_assert_int_eq(fy_token_start_mark(fyn_mapping_start)->line, 0);
        ck_assert_int_eq(fy_token_start_mark(fyn_mapping_start)->column, 2);

        fyn_mapping_end = fy_node_get_end_token(fyn_mapping);
        ck_assert_ptr_ne(fyn_mapping_end, NULL);

        ck_assert_ptr_ne(fy_token_start_mark(fyn_mapping_end), NULL);

        ck_assert_int_eq(fy_token_start_mark(fyn_mapping_end)->line, 2);
        ck_assert_int_eq(fy_token_start_mark(fyn_mapping_end)->column, 0);

        /* Scalar (key) */
        fyn_scalar = fy_node_pair_key(fy_node_mapping_get_by_index(fyn_mapping, 0));
        ck_assert_ptr_ne(fyn_scalar, NULL);

        fyn_scalar_token = fy_node_get_start_token(fyn_scalar);
        ck_assert_ptr_ne(fyn_scalar_token, NULL);
        ck_assert_ptr_eq(fyn_scalar_token, fy_node_get_end_token(fyn_scalar));

        ck_assert_ptr_ne(fy_token_start_mark(fyn_scalar_token), NULL);
        ck_assert_int_eq(fy_token_start_mark(fyn_scalar_token)->line, 0);
        ck_assert_int_eq(fy_token_start_mark(fyn_scalar_token)->column, 2);

        ck_assert_ptr_ne(fy_token_end_mark(fyn_scalar_token), NULL);
        ck_assert_int_eq(fy_token_end_mark(fyn_scalar_token)->line, 0);
        ck_assert_int_eq(fy_token_end_mark(fyn_scalar_token)->column, 6);

       /* cleanup */
       fy_document_destroy(fyd);
}
END_TEST

/* test style change on a plain */
START_TEST(style_set_plain_scalar)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	enum fy_node_style style;
	char *buf;

	fyd = fy_document_build_from_string(NULL, "foo\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fprintf(stderr, "--------------\n");
	fprintf(stderr, "-- original --\n");
	fprintf(stderr, "--------------\n");
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	fprintf(stderr, "%s", buf);
	free(buf);
	fprintf(stderr, "--------------\n");

	fprintf(stderr, "-----------\n");
	fprintf(stderr, "-- plain --\n");
	fprintf(stderr, "-----------\n");
	style = fy_node_set_style(fyn, FYNS_PLAIN);
	ck_assert(style == FYNS_PLAIN);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "foo\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	fprintf(stderr, "-------------------\n");
	fprintf(stderr, "-- single quoted --\n");
	fprintf(stderr, "-------------------\n");
	style = fy_node_set_style(fyn, FYNS_SINGLE_QUOTED);
	ck_assert(style == FYNS_SINGLE_QUOTED);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "'foo'\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	fprintf(stderr, "-------------------\n");
	fprintf(stderr, "-- double quoted --\n");
	fprintf(stderr, "-------------------\n");
	style = fy_node_set_style(fyn, FYNS_DOUBLE_QUOTED);
	ck_assert(style == FYNS_DOUBLE_QUOTED);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "\"foo\"\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	fprintf(stderr, "-------------\n");
	fprintf(stderr, "-- literal --\n");
	fprintf(stderr, "-------------\n");
	style = fy_node_set_style(fyn, FYNS_LITERAL);
	ck_assert(style == FYNS_LITERAL);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "|-\n  foo\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	/* NOTE we don't try folded, because we never support emitting one */

	fy_document_destroy(fyd);
}
END_TEST

/* test style change on a double quoted scalar which ends with colon */
START_TEST(style_set_double_quoted_scalar_colon)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	enum fy_node_style style;
	char *buf;

	/* simple double quoted scalar that can't be a plain */
	fyd = fy_document_build_from_string(NULL, "\"foo:\"\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fprintf(stderr, "--------------\n");
	fprintf(stderr, "-- original --\n");
	fprintf(stderr, "--------------\n");
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	fprintf(stderr, "%s", buf);
	free(buf);
	fprintf(stderr, "--------------\n");

	/* we can't convert to a plain, it ends with : */
	fprintf(stderr, "-----------\n");
	fprintf(stderr, "-- plain --\n");
	fprintf(stderr, "-----------\n");
	style = fy_node_set_style(fyn, FYNS_PLAIN);
	ck_assert(style != FYNS_PLAIN);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	fprintf(stderr, "%s", buf);
	free(buf);

	fprintf(stderr, "-------------------\n");
	fprintf(stderr, "-- single quoted --\n");
	fprintf(stderr, "-------------------\n");
	style = fy_node_set_style(fyn, FYNS_SINGLE_QUOTED);
	ck_assert(style == FYNS_SINGLE_QUOTED);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "'foo:'\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	fprintf(stderr, "-------------------\n");
	fprintf(stderr, "-- double quoted --\n");
	fprintf(stderr, "-------------------\n");
	style = fy_node_set_style(fyn, FYNS_DOUBLE_QUOTED);
	ck_assert(style == FYNS_DOUBLE_QUOTED);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "\"foo:\"\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	/* literal has no problem with it */
	fprintf(stderr, "-------------\n");
	fprintf(stderr, "-- literal --\n");
	fprintf(stderr, "-------------\n");
	style = fy_node_set_style(fyn, FYNS_LITERAL);
	ck_assert(style == FYNS_LITERAL);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "|-\n  foo:\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	/* NOTE we don't try folded, because we never support emitting one */

	fy_document_destroy(fyd);
}
END_TEST

/* test style change on a double quoted scalar which contains a \0 */
START_TEST(style_set_double_quoted_scalar_0)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	enum fy_node_style style;
	char *buf;

	/* simple double quoted scalar that can't be anything else */
	fyd = fy_document_build_from_string(NULL, "\"foo\\0\"\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fprintf(stderr, "--------------\n");
	fprintf(stderr, "-- original --\n");
	fprintf(stderr, "--------------\n");
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	fprintf(stderr, "%s", buf);
	free(buf);
	fprintf(stderr, "--------------\n");

	/* we can't convert to a plain */
	fprintf(stderr, "-----------\n");
	fprintf(stderr, "-- plain --\n");
	fprintf(stderr, "-----------\n");
	style = fy_node_set_style(fyn, FYNS_PLAIN);
	ck_assert(style != FYNS_PLAIN);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	fprintf(stderr, "%s", buf);
	free(buf);

	fprintf(stderr, "-------------------\n");
	fprintf(stderr, "-- single quoted --\n");
	fprintf(stderr, "-------------------\n");
	style = fy_node_set_style(fyn, FYNS_SINGLE_QUOTED);
	ck_assert(style != FYNS_SINGLE_QUOTED);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	fprintf(stderr, "%s", buf);
	free(buf);

	fprintf(stderr, "-------------------\n");
	fprintf(stderr, "-- double quoted --\n");
	fprintf(stderr, "-------------------\n");
	style = fy_node_set_style(fyn, FYNS_DOUBLE_QUOTED);
	ck_assert(style == FYNS_DOUBLE_QUOTED);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "\"foo\\0\"\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	/* literal has no problem with it */
	fprintf(stderr, "-------------\n");
	fprintf(stderr, "-- literal --\n");
	fprintf(stderr, "-------------\n");
	style = fy_node_set_style(fyn, FYNS_LITERAL);
	ck_assert(style != FYNS_LITERAL);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	fprintf(stderr, "%s", buf);
	free(buf);

	/* NOTE we don't try folded, because we never support emitting one */

	fy_document_destroy(fyd);
}
END_TEST

/* test style change on a collection */
START_TEST(style_set_collection)
{
	struct fy_document *fyd;
	struct fy_node *fyn;
	enum fy_node_style style;
	char *buf;

	fyd = fy_document_build_from_string(NULL, "- one\n- two\n", FY_NT);
	ck_assert_ptr_ne(fyd, NULL);

	fyn = fy_document_root(fyd);
	ck_assert_ptr_ne(fyn, NULL);

	fprintf(stderr, "--------------\n");
	fprintf(stderr, "-- original --\n");
	fprintf(stderr, "--------------\n");
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	fprintf(stderr, "%s", buf);
	free(buf);
	fprintf(stderr, "--------------\n");

	fprintf(stderr, "----------\n");
	fprintf(stderr, "-- flow --\n");
	fprintf(stderr, "----------\n");
	style = fy_node_set_style(fyn, FYNS_FLOW);
	ck_assert(style == FYNS_FLOW);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "[\n  one,\n  two\n]\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	fprintf(stderr, "-----------\n");
	fprintf(stderr, "-- block --\n");
	fprintf(stderr, "-----------\n");
	style = fy_node_set_style(fyn, FYNS_BLOCK);
	ck_assert(style == FYNS_BLOCK);
	buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT);
	ck_assert_ptr_ne(buf, NULL);
	ck_assert_str_eq(buf, "- one\n- two\n");
	fprintf(stderr, "%s", buf);
	free(buf);

	fy_document_destroy(fyd);
}
END_TEST

void libfyaml_case_core(struct fy_check_suite *cs)
{
	struct fy_check_testcase *ctc;

	ctc = fy_check_suite_add_test_case(cs, "core");

	fy_check_testcase_add_test(ctc, doc_build_simple);
	fy_check_testcase_add_test(ctc, doc_build_parse_check);
	fy_check_testcase_add_test(ctc, doc_build_scalar);
	fy_check_testcase_add_test(ctc, doc_build_sequence);
	fy_check_testcase_add_test(ctc, doc_build_mapping);

	fy_check_testcase_add_test(ctc, doc_path_access);
	fy_check_testcase_add_test(ctc, doc_path_node);
	fy_check_testcase_add_test(ctc, doc_path_parent);
	fy_check_testcase_add_test(ctc, doc_short_path);
	fy_check_testcase_add_test(ctc, doc_scalar_path);
	fy_check_testcase_add_test(ctc, doc_scalar_path_array);

	fy_check_testcase_add_test(ctc, doc_nearest_anchor);
	fy_check_testcase_add_test(ctc, doc_references);
	fy_check_testcase_add_test(ctc, doc_nearest_child_of);

	fy_check_testcase_add_test(ctc, doc_create_empty_seq1);
	fy_check_testcase_add_test(ctc, doc_create_empty_seq2);
	fy_check_testcase_add_test(ctc, doc_create_empty_map1);
	fy_check_testcase_add_test(ctc, doc_create_empty_map2);

	fy_check_testcase_add_test(ctc, doc_create_test_seq1);
	fy_check_testcase_add_test(ctc, doc_create_test_map1);

	fy_check_testcase_add_test(ctc, doc_insert_remove_seq);
	fy_check_testcase_add_test(ctc, doc_insert_remove_map);

	fy_check_testcase_add_test(ctc, doc_sort);
	fy_check_testcase_add_test(ctc, doc_mapping_sort);
	fy_check_testcase_add_test(ctc, doc_sequence_sort_basic);
	fy_check_testcase_add_test(ctc, doc_sequence_sort_already_sorted);
	fy_check_testcase_add_test(ctc, doc_sequence_sort_single);
	fy_check_testcase_add_test(ctc, doc_sequence_sort_empty);
	fy_check_testcase_add_test(ctc, doc_sequence_sort_non_sequence);
	fy_check_testcase_add_test(ctc, doc_sequence_sort_null);
	fy_check_testcase_add_test(ctc, doc_sequence_sort_flow_style_preserved);
	fy_check_testcase_add_test(ctc, doc_sequence_sort_block_style_preserved);
	fy_check_testcase_add_test(ctc, doc_insert_at);

	fy_check_testcase_add_test(ctc, doc_join_scalar_to_scalar);
	fy_check_testcase_add_test(ctc, doc_join_scalar_to_map);
	fy_check_testcase_add_test(ctc, doc_join_scalar_to_seq);

	fy_check_testcase_add_test(ctc, doc_join_map_to_scalar);
	fy_check_testcase_add_test(ctc, doc_join_map_to_seq);
	fy_check_testcase_add_test(ctc, doc_join_map_to_map);

	fy_check_testcase_add_test(ctc, doc_join_seq_to_scalar);
	fy_check_testcase_add_test(ctc, doc_join_seq_to_seq);
	fy_check_testcase_add_test(ctc, doc_join_seq_to_map);

	fy_check_testcase_add_test(ctc, doc_join_tags);

	fy_check_testcase_add_test(ctc, doc_build_with_tags);

	fy_check_testcase_add_test(ctc, doc_attach_check);

	fy_check_testcase_add_test(ctc, manual_scalar_esc);
	fy_check_testcase_add_test(ctc, manual_scalar_quoted);
	fy_check_testcase_add_test(ctc, manual_scalar_copy);
	fy_check_testcase_add_test(ctc, manual_scalarf);

	fy_check_testcase_add_test(ctc, manual_valid_anchor);
	fy_check_testcase_add_test(ctc, manual_invalid_anchor);
	fy_check_testcase_add_test(ctc, manual_anchor_removal);

	fy_check_testcase_add_test(ctc, manual_block_flow_mix);

	fy_check_testcase_add_test(ctc, alloca_check);

	fy_check_testcase_add_test(ctc, scanf_check);

        fy_check_testcase_add_test(ctc, token_test);

	fy_check_testcase_add_test(ctc, style_set_plain_scalar);
	fy_check_testcase_add_test(ctc, style_set_double_quoted_scalar_colon);
	fy_check_testcase_add_test(ctc, style_set_double_quoted_scalar_0);
	fy_check_testcase_add_test(ctc, style_set_collection);
}
