/**
 * @file test_lyb.c
 * @author Michal Vasko <mvasko@cesnet.cz>
 * @brief Cmocka tests for LYB binary data format.
 *
 * Copyright (c) 2020 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */
#define _UTEST_MAIN_
#include "utests.h"

#include "hash_table.h"
#include "libyang.h"

#define CHECK_PARSE_LYD(INPUT, OUT_NODE) \
                CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, OUT_NODE)

#define CHECK_LYD_STRING(MODEL, TEXT) \
                CHECK_LYD_STRING_PARAM(MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)

#define CHECK_PRINT_THEN_PARSE(DATA_XML) \
    { \
        struct lyd_node *tree_1; \
        struct lyd_node *tree_2; \
        char *xml_out; \
        CHECK_PARSE_LYD(DATA_XML, tree_1); \
        assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
        assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
        assert_non_null(tree_2); \
        CHECK_LYD(tree_1, tree_2); \
        free(xml_out); \
        lyd_free_all(tree_1); \
        lyd_free_all(tree_2); \
    }

static int
setup(void **state)
{
    UTEST_SETUP;
    assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG));

    return 0;
}

static void
tests_leaflist(void **state)
{
    const char *mod;
    const char *data_xml;

    mod =
            "module mod { namespace \"urn:test-leaflist\"; prefix m;"
            "  container cont {"
            "    presence \"\";"
            "    leaf-list ll {"
            "      type uint8;"
            "    }"
            "  }"
            "}";
    UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL);

    data_xml =
            "<cont xmlns=\"urn:test-leaflist\">\n"
            "</cont>\n";
    CHECK_PRINT_THEN_PARSE(data_xml);

    data_xml =
            "<cont xmlns=\"urn:test-leaflist\">\n"
            "  <ll>1</ll>\n"
            "</cont>\n";
    CHECK_PRINT_THEN_PARSE(data_xml);

    data_xml =
            "<cont xmlns=\"urn:test-leaflist\">\n"
            "  <ll>1</ll>\n"
            "  <ll>2</ll>\n"
            "</cont>\n";
    CHECK_PRINT_THEN_PARSE(data_xml);
}

static void
tests_list(void **state)
{
    const char *mod;
    const char *data_xml;

    mod =
            "module mod { namespace \"urn:test-list\"; prefix m;"
            "  container cont {"
            "    presence \"\";"
            "    list lst {"
            "      key \"lf\";"
            "      leaf lf {"
            "        type uint8;"
            "      }"
            "    }"
            "  }"
            "}";
    UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL);

    data_xml =
            "<cont xmlns=\"urn:test-list\">\n"
            "  <lst>"
            "    <lf>1</lf>"
            "  </lst>"
            "</cont>\n";
    CHECK_PRINT_THEN_PARSE(data_xml);

    data_xml =
            "<cont xmlns=\"urn:test-list\">\n"
            "  <lst>"
            "    <lf>1</lf>"
            "    <lf>2</lf>"
            "  </lst>"
            "</cont>\n";
    CHECK_PRINT_THEN_PARSE(data_xml);
}

static void
tests_any(void **state)
{
    const char *mod;
    const char *data_xml;

    mod =
            "module mod { namespace \"urn:test-any\"; prefix m;"
            "  container cont {"
            "    presence \"\";"
            "    anyxml anxml;\n"
            "  }"
            "}";
    UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL);

    data_xml =
            "<cont xmlns=\"urn:test-any\">\n"
            "</cont>\n";
    CHECK_PRINT_THEN_PARSE(data_xml);

    data_xml =
            "<cont xmlns=\"urn:test-any\">\n"
            "  <anxml><node>value</node></anxml>\n"
            "</cont>\n";
    CHECK_PRINT_THEN_PARSE(data_xml);

    data_xml =
            "<cont xmlns=\"urn:test-any\">\n"
            "  <anxml><node1>value</node1></anxml>\n"
            "  <anxml><node2>value</node2></anxml>\n"
            "</cont>\n";
    CHECK_PRINT_THEN_PARSE(data_xml);
}

static void
test_ietf_interfaces(void **state)
{
    const char *data_xml =
            "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n"
            "    <interface>\n"
            "        <name>eth0</name>\n"
            "        <description>Ethernet 0</description>\n"
            "        <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
            "        <enabled>true</enabled>\n"
            "        <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n"
            "            <enabled>true</enabled>\n"
            "            <mtu>1500</mtu>\n"
            "            <address>\n"
            "                <ip>192.168.2.100</ip>\n"
            "                <prefix-length>24</prefix-length>\n"
            "            </address>\n"
            "        </ipv4>\n"
            "    </interface>\n"
            "    <interface>\n"
            "        <name>eth1</name>\n"
            "        <description>Ethernet 1</description>\n"
            "        <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
            "        <enabled>true</enabled>\n"
            "        <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n"
            "            <enabled>true</enabled>\n"
            "            <mtu>1500</mtu>\n"
            "            <address>\n"
            "                <ip>10.10.1.5</ip>\n"
            "                <prefix-length>16</prefix-length>\n"
            "            </address>\n"
            "        </ipv4>\n"
            "    </interface>\n"
            "    <interface>\n"
            "        <name>gigaeth0</name>\n"
            "        <description>GigabitEthernet 0</description>\n"
            "        <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n"
            "        <enabled>false</enabled>\n"
            "    </interface>\n"
            "</interfaces>\n";

    assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-ip", NULL, NULL));
    assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "iana-if-type", NULL, NULL));

    CHECK_PRINT_THEN_PARSE(data_xml);
}

static void
test_origin(void **state)
{
    const char *origin_yang =
            "module test-origin {"
            "   namespace \"urn:test-origin\";"
            "   prefix to;"
            "   import ietf-origin {"
            "       prefix or;"
            "   }"
            ""
            "   container cont {"
            "       leaf leaf1 {"
            "           type string;"
            "       }"
            "       leaf leaf2 {"
            "           type string;"
            "       }"
            "       leaf leaf3 {"
            "           type uint8;"
            "       }"
            "   }"
            "}";
    const char *data_xml =
            "<cont xmlns=\"urn:test-origin\">\n"
            "  <leaf1 xmlns:or=\"urn:ietf:params:xml:ns:yang:ietf-origin\" or:origin=\"or:default\">value1</leaf1>\n"
            "  <leaf2>value2</leaf2>\n"
            "  <leaf3 xmlns:or=\"urn:ietf:params:xml:ns:yang:ietf-origin\" or:origin=\"or:system\">125</leaf3>\n"
            "</cont>\n";

    UTEST_ADD_MODULE(origin_yang, LYS_IN_YANG, NULL, NULL);
    assert_int_equal(LY_SUCCESS, lys_set_implemented(ly_ctx_get_module_latest(UTEST_LYCTX, "ietf-origin"), NULL));

    CHECK_PRINT_THEN_PARSE(data_xml);
}

static void
test_statements(void **state)
{
    const char *links_yang =
            "module links {\n"
            "    yang-version 1.1;\n"
            "    namespace \"urn:module2\";\n"
            "    prefix mod2;\n"
            "\n"
            "    identity just-another-identity;\n"
            "\n"
            "    leaf one-leaf {\n"
            "        type string;\n"
            "    }\n"
            "\n"
            "    list list-for-augment {\n"
            "        key keyleaf;\n"
            "\n"
            "        leaf keyleaf {\n"
            "            type string;\n"
            "        }\n"
            "\n"
            "        leaf just-leaf {\n"
            "            type int32;\n"
            "        }\n"
            "    }\n"
            "\n"
            "    leaf rleaf {\n"
            "        type string;\n"
            "    }\n"
            "\n"
            "    leaf-list llist {\n"
            "        type string;\n"
            "        min-elements 0;\n"
            "        max-elements 100;\n"
            "        ordered-by user;\n"
            "    }\n"
            "\n"
            "    grouping rgroup {\n"
            "        leaf rg1 {\n"
            "            type string;\n"
            "        }\n"
            "\n"
            "        leaf rg2 {\n"
            "            type string;\n"
            "        }\n"
            "    }\n"
            "}\n";

    const char *statements_yang =
            "module statements {\n"
            "    namespace \"urn:module\";\n"
            "    prefix mod;\n"
            "    yang-version 1.1;\n"
            "\n"
            "    import links {\n"
            "        prefix mod2;\n"
            "    }\n"
            "\n"
            "    identity random-identity {\n"
            "        base \"mod2:just-another-identity\";\n"
            "        base \"another-identity\";\n"
            "    }\n"
            "\n"
            "    identity another-identity {\n"
            "        base \"mod2:just-another-identity\";\n"
            "    }\n"
            "\n"
            "    typedef percent {\n"
            "        type uint8 {\n"
            "            range \"0 .. 100\";\n"
            "        }\n"
            "        units percent;\n"
            "    }\n"
            "\n"
            "    container ice-cream-shop {\n"
            "        container employees {\n"
            "            list employee {\n"
            "                config true;\n"
            "                key id;\n"
            "                unique name;\n"
            "                min-elements 0;\n"
            "                max-elements 100;\n"
            "\n"
            "                leaf id {\n"
            "                    type uint64;\n"
            "                    mandatory true;\n"
            "                }\n"
            "\n"
            "                leaf name {\n"
            "                    type string;\n"
            "                }\n"
            "\n"
            "                leaf age {\n"
            "                    type uint32;\n"
            "                }\n"
            "            }\n"
            "        }\n"
            "    }\n"
            "\n"
            "    container random {\n"
            "        choice switch {\n"
            "            case a {\n"
            "                leaf aleaf {\n"
            "                    type string;\n"
            "                    default aaa;\n"
            "                }\n"
            "            }\n"
            "\n"
            "            case c {\n"
            "                leaf cleaf {\n"
            "                    type string;\n"
            "                }\n"
            "            }\n"
            "        }\n"
            "\n"
            "        anyxml xml-data;\n"
            "        anydata any-data;\n"
            "        leaf-list leaflist {\n"
            "            type string;\n"
            "            min-elements 0;\n"
            "            max-elements 20;\n"
            "            ordered-by system;\n"
            "        }\n"
            "\n"
            "        grouping group {\n"
            "            leaf g1 {\n"
            "                mandatory false;\n"
            "                type percent;\n"
            "            }\n"
            "\n"
            "            leaf g2 {\n"
            "                type string;\n"
            "            }\n"
            "        }\n"
            "\n"
            "        uses group;\n"
            "        uses mod2:rgroup;\n"
            "\n"
            "        leaf lref {\n"
            "            type leafref {\n"
            "                path \"/mod2:one-leaf\";\n"
            "            }\n"
            "        }\n"
            "\n"
            "        leaf iref {\n"
            "            type identityref {\n"
            "                base \"mod2:just-another-identity\";\n"
            "            }\n"
            "        }\n"
            "    }\n"
            "\n"
            "    notification notif;\n"
            "\n"
            "    augment \"/random\" {\n"
            "        leaf aug-leaf {\n"
            "            type string;\n"
            "        }\n"
            "    }\n"
            "}\n";

    const char *data_xml =
            "<ice-cream-shop xmlns=\"urn:module\">\n"
            "  <employees>\n"
            "    <employee>\n"
            "      <id>0</id>\n"
            "      <name>John Doe</name>\n"
            "      <age>28</age>\n"
            "    </employee>\n"
            "    <employee>\n"
            "      <id>1</id>\n"
            "      <name>Dohn Joe</name>\n"
            "      <age>20</age>\n"
            "    </employee>\n"
            "  </employees>\n"
            "</ice-cream-shop>\n"
            "<one-leaf xmlns=\"urn:module2\">reference leaf</one-leaf>\n"
            "<random xmlns=\"urn:module\">\n"
            "  <aleaf>string</aleaf>\n"
            "  <xml-data><anyxml>data</anyxml></xml-data>\n"
            "  <any-data><notif/></any-data>\n"
            "  <leaflist>l0</leaflist>\n"
            "  <leaflist>l1</leaflist>\n"
            "  <leaflist>l2</leaflist>\n"
            "  <g1>40</g1>\n"
            "  <g2>string</g2>\n"
            "  <aug-leaf>string</aug-leaf>\n"
            "  <rg1>string</rg1>\n"
            "  <rg2>string</rg2>\n"
            "  <lref>reference leaf</lref>\n"
            "  <iref>random-identity</iref>\n"
            "</random>\n";

    UTEST_ADD_MODULE(links_yang, LYS_IN_YANG, NULL, NULL);
    UTEST_ADD_MODULE(statements_yang, LYS_IN_YANG, NULL, NULL);

    CHECK_PRINT_THEN_PARSE(data_xml);
}

static void
test_opaq(void **state)
{
    const char *nc_feats[] = {"writable-running", NULL};
    const char *data_xml =
            "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
            "  <target>\n"
            "    <running/>\n"
            "  </target>\n"
            "  <config>\n"
            "    <top xmlns=\"urn:ed\">\n"
            "      <first>TestFirst</first>\n"
            "    </top>\n"
            "  </config>\n"
            "</edit-config>\n";
    struct ly_in *in;
    struct lyd_node *tree_1;
    struct lyd_node *tree_2;
    char *xml_out; /* tree_2 */
    LY_ERR rc;

    assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", NULL, nc_feats));

    ly_in_new_memory(data_xml, &in);
    rc = lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree_1, NULL);
    ly_in_free(in, 0);
    assert_int_equal(rc, LY_SUCCESS);

    assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0);

    ly_in_new_memory(xml_out, &in);
    rc = lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_LYB, LYD_TYPE_RPC_YANG, &tree_2, NULL);
    ly_in_free(in, 0);
    assert_int_equal(rc, LY_SUCCESS);

    /* compare models */
    CHECK_LYD(tree_1, tree_2);

    /* clean */
    free(xml_out);
    lyd_free_all(tree_1);
    lyd_free_all(tree_2);
}

#if 0

static void
test_types(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "types", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/types.xml", LYD_XML, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_annotations(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/annotations.xml", LYD_XML, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_similar_annot_names(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/similar-annot-names.xml", LYD_XML, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_many_child_annot(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/many-childs-annot.xml", LYD_XML, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_union(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "union", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/union.xml", LYD_XML, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_union2(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "statements", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/union2.xml", LYD_XML, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_collisions(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/collisions.xml", LYD_XML, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_anydata(void **state)
{
    struct state *st = (*state);
    const struct lys_module *mod;
    int ret;
    const char *test_anydata =
            "module test-anydata {"
            "   namespace \"urn:test-anydata\";"
            "   prefix ya;"
            ""
            "   container cont {"
            "       anydata ntf;"
            "   }"
            "}";

    assert_non_null(ly_ctx_load_module(st->ctx, "ietf-netconf-notifications", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/ietf-netconf-notifications.json", LYD_JSON, LYD_OPT_NOTIF | LYD_OPT_TRUSTED, NULL);
    assert_ptr_not_equal(st->dt1, NULL);

    / *get notification in LYB format to set as anydata content * /
    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    lyd_free_withsiblings(st->dt1);
    st->dt1 = NULL;

    / *now comes the real test, test anydata * /
    mod = lys_parse_mem(st->ctx, test_anydata, LYS_YANG);
    assert_non_null(mod);

    st->dt1 = lyd_new(NULL, mod, "cont");
    assert_non_null(st->dt1);

    assert_non_null(lyd_new_anydata(st->dt1, NULL, "ntf", st->mem, LYD_ANYDATA_LYBD));
    st->mem = NULL;

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    ret = lyd_validate(&st->dt1, LYD_OPT_CONFIG, NULL);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);

    /* and also test the embedded notification itself */
    free(st->mem);
    ret = lyd_lyb_data_length(((struct lyd_node_anydata *)st->dt1->child)->value.mem);
    st->mem = malloc(ret);
    memcpy(st->mem, ((struct lyd_node_anydata *)st->dt1->child)->value.mem, ret);

    lyd_free_withsiblings(st->dt2);
    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_NOTIF | LYD_OPT_STRICT | LYD_OPT_NOEXTDEPS, NULL);
    assert_ptr_not_equal(st->dt2, NULL);

    /* parse the JSON again for this comparison */
    lyd_free_withsiblings(st->dt1);
    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/ietf-netconf-notifications.json", LYD_JSON, LYD_OPT_NOTIF | LYD_OPT_TRUSTED, NULL);
    assert_ptr_not_equal(st->dt1, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_submodule_feature(void **state)
{
    struct state *st = (*state);
    const struct lys_module *mod;
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    mod = ly_ctx_load_module(st->ctx, "feature-submodule-main", NULL);
    assert_non_null(mod);
    assert_int_equal(lys_features_enable(mod, "test-submodule-feature"), 0);

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/test-submodule-feature.json", LYD_JSON, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_coliding_augments(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "augment-target", NULL));
    assert_non_null(ly_ctx_load_module(st->ctx, "augment0", NULL));
    assert_non_null(ly_ctx_load_module(st->ctx, "augment1", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/augment.xml", LYD_XML, LYD_OPT_CONFIG);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

static void
test_leafrefs(void **state)
{
    struct state *st = (*state);
    int ret;

    ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files");
    assert_non_null(ly_ctx_load_module(st->ctx, "leafrefs2", NULL));

    st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/leafrefs2.json", LYD_JSON, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt1, NULL);

    ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS);
    assert_int_equal(ret, 0);

    st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT);
    assert_ptr_not_equal(st->dt2, NULL);

    check_data_tree(st->dt1, st->dt2);
}

#endif

int
main(void)
{
    const struct CMUnitTest tests[] = {
        UTEST(tests_leaflist),
        UTEST(tests_list),
        UTEST(tests_any),
        UTEST(test_ietf_interfaces, setup),
        UTEST(test_origin, setup),
        UTEST(test_statements, setup),
        UTEST(test_opaq, setup),
#if 0
        cmocka_unit_test_setup_teardown(test_types, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_annotations, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_similar_annot_names, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_many_child_annot, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_union, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_union2, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_collisions, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_anydata, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_submodule_feature, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_coliding_augments, setup_f, teardown_f),
        cmocka_unit_test_setup_teardown(test_leafrefs, setup_f, teardown_f),
#endif
    };

    return cmocka_run_group_tests(tests, NULL, NULL);
}
