/*
 * @file test_printer_yin.c
 * @author: Fred Gan <ganshaolong@vip.qq.com>
 * @brief unit tests for functions from printer_yin.c
 *
 * Copyright (c) 2019-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 "common.h"
#include "context.h"
#include "out.h"
#include "printer_schema.h"
#include "tree_schema.h"

static void
test_module(void **state)
{
    struct lys_module *mod;

    char *orig = malloc(8096);

    strcpy(orig,
            "module all {\n"
            "    yang-version 1.1;\n"
            "    namespace \"urn:all\";\n"
            "    prefix all_mod;\n\n"
            "    import ietf-yang-types {\n"
            "        prefix yt;\n"
            "        revision-date 2013-07-15;\n"
            "        description\n"
            "            \"YANG types\";\n"
            "        reference\n"
            "            \"RFC reference\";\n"
            "    }\n\n"
            "    feature feat1 {\n"
            "        if-feature \"feat2\";\n"
            "        status obsolete;\n"
            "    }\n\n"
            "    feature feat2;\n"
            "    feature feat3;\n\n"
            "    identity ident2 {\n"
            "        base ident1;\n"
            "    }\n\n"
            "    identity ident1;\n\n"
            "    typedef tdef1 {\n"
            "        type tdef2 {\n"
            "            length \"3..9 | 30..40\";\n"
            "            pattern \"[ac]*\";\n"
            "        }\n"
            "        units \"none\";\n"
            "        default \"aaa\";\n"
            "    }\n\n"
            "    typedef tdef2 {\n"
            "        type string {\n"
            "            length \"2..10 | 20..50\";\n"
            "            pattern \"[ab]*\";\n"
            "        }\n"
            "    }\n\n"
            "    grouping group1 {\n"
            "        leaf leaf1 {\n"
            "            type int8;\n"
            "        }\n"
            "    }\n\n"
            "    container cont1 {\n"
            "        leaf leaf2 {\n"
            "            if-feature \"feat1\";\n"
            "            type int16;\n"
            "            status obsolete;\n"
            "        }\n\n"
            "        uses group1 {\n"
            "            if-feature \"feat2\";\n"
            "            refine \"leaf1\" {\n"
            "                if-feature \"feat3\";\n"
            "                must \"24 - 4 = number('20')\";\n"
            "                default \"25\";\n"
            "                config true;\n"
            "                mandatory false;\n"
            "                description\n"
            "                    \"dsc\";\n"
            "                reference\n"
            "                    \"none\";\n"
            "            }\n"
            "        }\n\n"
            "        leaf leaf3 {\n"
            "            type int32;\n"
            "        }\n\n"
            "        leaf leaf4 {\n"
            "            type int64 {\n"
            "                range \"1000 .. 50000\" {\n"
            "                    error-message\n"
            "                        \"Special error message.\";\n"
            "                    error-app-tag \"special-tag\";\n"
            "                }\n"
            "            }\n"
            "        }\n\n"
            "        leaf leaf5 {\n"
            "            type uint8;\n"
            "        }\n\n"
            "        leaf leaf6 {\n"
            "            type uint16;\n"
            "        }\n\n"
            "        leaf leaf7 {\n"
            "            type uint32;\n"
            "        }\n\n"
            "        leaf leaf8 {\n"
            "            type uint64;\n"
            "        }\n\n"
            "        choice choic1 {\n"
            "            default \"leaf9b\";\n"
            "            leaf leaf9a {\n"
            "                type decimal64 {\n"
            "                    fraction-digits 9;\n"
            "                }\n"
            "            }\n\n"
            "            leaf leaf9b {\n"
            "                type boolean;\n"
            "                default \"false\";\n"
            "            }\n"
            "        }\n\n"
            "        leaf leaf10 {\n"
            "            type boolean;\n"
            "        }\n\n");
    strcpy(orig + strlen(orig),
            "        leaf leaf11 {\n"
            "            type enumeration {\n"
            "                enum \"one\";\n"
            "                enum \"two\";\n"
            "                enum \"five\" {\n"
            "                    value 5;\n"
            "                }\n"
            "            }\n"
            "        }\n\n"
            "        leaf leaf12 {\n"
            "            type bits {\n"
            "                bit flag0 {\n"
            "                    position 0;\n"
            "                }\n"
            "                bit flag1;\n"
            "                bit flag2 {\n"
            "                    position 2;\n"
            "                }\n"
            "                bit flag3 {\n"
            "                    position 3;\n"
            "                }\n"
            "            }\n"
            "            default \"flag0 flag3\";\n"
            "        }\n\n"
            "        leaf leaf13 {\n"
            "            type binary;\n"
            "        }\n\n"
            "        leaf leaf14 {\n"
            "            type leafref {\n"
            "                path \"/cont1/leaf17\";\n"
            "            }\n"
            "        }\n\n"
            "        leaf leaf15 {\n"
            "            type empty;\n"
            "        }\n\n"
            "        leaf leaf16 {\n"
            "            type union {\n"
            "                type instance-identifier {\n"
            "                    require-instance true;\n"
            "                }\n"
            "                type int8;\n"
            "            }\n"
            "        }\n\n"
            "        list list1 {\n"
            "            key \"leaf18\";\n"
            "            unique \"leaf19\";\n"
            "            min-elements 1;\n"
            "            max-elements 20;\n"
            "            leaf leaf18 {\n"
            "                type string;\n"
            "            }\n\n"
            "            leaf leaf19 {\n"
            "                type uint32;\n"
            "            }\n\n"
            "            anyxml axml1;\n"
            "            anydata adata1;\n\n"
            "            action act1 {\n"
            "                input {\n"
            "                    leaf leaf24 {\n"
            "                        type string;\n"
            "                    }\n"
            "                }\n\n"
            "                output {\n"
            "                    leaf leaf25 {\n"
            "                        type string;\n"
            "                    }\n"
            "                }\n"
            "            }\n\n"
            "            notification notif1 {\n"
            "                leaf leaf26 {\n"
            "                    type string;\n"
            "                }\n"
            "            }\n"
            "        }\n\n"
            "        leaf-list llist1 {\n"
            "            type tdef1;\n"
            "            ordered-by user;\n"
            "        }\n\n"
            "        list list2 {\n"
            "            key \"leaf27 leaf28\";\n"
            "            leaf leaf27 {\n"
            "                type uint8;\n"
            "            }\n\n"
            "            leaf leaf28 {\n"
            "                type uint8;\n"
            "            }\n"
            "        }\n\n"
            "        leaf leaf29 {\n"
            "            type instance-identifier;\n"
            "        }\n\n"
            "        container must-deviations-container {\n"
            "            presence \"Allows deviations on the leaf\";\n"
            "            leaf leaf30 {\n"
            "                type string;\n"
            "            }\n"
            "        }\n\n"
            "        leaf leaf23 {\n"
            "            type empty;\n"
            "        }\n"
            "    }\n\n"
            "    augment \"/cont1\" {\n"
            "        leaf leaf17 {\n"
            "            type string;\n"
            "        }\n"
            "    }\n\n"
            "    rpc rpc1 {\n"
            "        input {\n"
            "            leaf leaf20 {\n"
            "                type tdef1;\n"
            "            }\n"
            "        }\n\n"
            "        output {\n"
            "            container cont2 {\n"
            "                leaf leaf21 {\n"
            "                    type empty;\n"
            "                }\n"
            "            }\n"
            "        }\n"
            "    }\n\n"
            "    container test-when {\n"
            "        leaf when-check {\n"
            "            type boolean;\n"
            "        }\n\n"
            "        leaf gated-data {\n"
            "            when \"../when-check = 'true'\";\n"
            "            type uint16;\n"
            "        }\n"
            "    }\n\n"
            "    extension c-define {\n"
            "        description\n"
            "            \"Takes as an argument a name string.\n"
            "            Makes the code generator use the given name\n"
            "            in the #define.\";\n"
            "        argument \"name\";\n"
            "    }\n"
            "}\n");

    char *ori_res = malloc(8096);

    strcpy(ori_res,
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<module name=\"all\"\n"
            "        xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
            "        xmlns:all_mod=\"urn:all\"\n"
            "        xmlns:yt=\"urn:ietf:params:xml:ns:yang:ietf-yang-types\">\n"
            "  <yang-version value=\"1.1\"/>\n"
            "  <namespace uri=\"urn:all\"/>\n"
            "  <prefix value=\"all_mod\"/>\n"
            "  <import module=\"ietf-yang-types\">\n"
            "    <prefix value=\"yt\"/>\n"
            "    <revision-date date=\"2013-07-15\"/>\n"
            "    <description>\n"
            "      <text>YANG types</text>\n"
            "    </description>\n"
            "    <reference>\n"
            "      <text>RFC reference</text>\n"
            "    </reference>\n"
            "  </import>\n\n"
            "  <extension name=\"c-define\">\n"
            "    <argument name=\"name\"/>\n"
            "    <description>\n"
            "      <text>Takes as an argument a name string.\n"
            "Makes the code generator use the given name\n"
            "in the #define.</text>\n"
            "    </description>\n"
            "  </extension>\n"
            "  <feature name=\"feat1\">\n"
            "    <if-feature name=\"feat2\"/>\n"
            "    <status value=\"obsolete\"/>\n"
            "  </feature>\n"
            "  <feature name=\"feat2\"/>\n"
            "  <feature name=\"feat3\"/>\n"
            "  <identity name=\"ident2\">\n"
            "    <base name=\"ident1\"/>\n"
            "  </identity>\n"
            "  <identity name=\"ident1\"/>\n"
            "  <typedef name=\"tdef1\">\n"
            "    <type name=\"tdef2\">\n"
            "      <length value=\"3..9 | 30..40\"/>\n"
            "      <pattern value=\"[ac]*\"/>\n"
            "    </type>\n"
            "    <units name=\"none\"/>\n"
            "    <default value=\"aaa\"/>\n"
            "  </typedef>\n"
            "  <typedef name=\"tdef2\">\n"
            "    <type name=\"string\">\n"
            "      <length value=\"2..10 | 20..50\"/>\n"
            "      <pattern value=\"[ab]*\"/>\n"
            "    </type>\n"
            "  </typedef>\n"
            "  <grouping name=\"group1\">\n"
            "    <leaf name=\"leaf1\">\n"
            "      <type name=\"int8\"/>\n"
            "    </leaf>\n"
            "  </grouping>\n"
            "  <container name=\"cont1\">\n"
            "    <leaf name=\"leaf2\">\n"
            "      <if-feature name=\"feat1\"/>\n"
            "      <type name=\"int16\"/>\n"
            "      <status value=\"obsolete\"/>\n"
            "    </leaf>\n"
            "    <uses name=\"group1\">\n"
            "      <if-feature name=\"feat2\"/>\n"
            "      <refine target-node=\"leaf1\">\n"
            "        <if-feature name=\"feat3\"/>\n"
            "        <must condition=\"24 - 4 = number('20')\"/>\n"
            "        <default value=\"25\"/>\n"
            "        <config value=\"true\"/>\n"
            "        <mandatory value=\"false\"/>\n"
            "        <description>\n"
            "          <text>dsc</text>\n"
            "        </description>\n"
            "        <reference>\n"
            "          <text>none</text>\n"
            "        </reference>\n"
            "      </refine>\n"
            "    </uses>\n"
            "    <leaf name=\"leaf3\">\n"
            "      <type name=\"int32\"/>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf4\">\n"
            "      <type name=\"int64\">\n"
            "        <range value=\"1000 .. 50000\">\n"
            "          <error-message>\n"
            "            <value>Special error message.</value>\n"
            "          </error-message>\n"
            "          <error-app-tag value=\"special-tag\"/>\n"
            "        </range>\n"
            "      </type>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf5\">\n"
            "      <type name=\"uint8\"/>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf6\">\n"
            "      <type name=\"uint16\"/>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf7\">\n"
            "      <type name=\"uint32\"/>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf8\">\n"
            "      <type name=\"uint64\"/>\n"
            "    </leaf>\n"
            "    <choice name=\"choic1\">\n"
            "      <default value=\"leaf9b\"/>\n"
            "      <leaf name=\"leaf9a\">\n"
            "        <type name=\"decimal64\">\n"
            "          <fraction-digits value=\"9\"/>\n"
            "        </type>\n"
            "      </leaf>\n"
            "      <leaf name=\"leaf9b\">\n"
            "        <type name=\"boolean\"/>\n"
            "        <default value=\"false\"/>\n"
            "      </leaf>\n"
            "    </choice>\n"
            "    <leaf name=\"leaf10\">\n"
            "      <type name=\"boolean\"/>\n"
            "    </leaf>\n");
    strcpy(ori_res + strlen(ori_res),
            "    <leaf name=\"leaf11\">\n"
            "      <type name=\"enumeration\">\n"
            "        <enum name=\"one\"/>\n"
            "        <enum name=\"two\"/>\n"
            "        <enum name=\"five\">\n"
            "          <value value=\"5\"/>\n"
            "        </enum>\n"
            "      </type>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf12\">\n"
            "      <type name=\"bits\">\n"
            "        <bit name=\"flag0\">\n"
            "          <position value=\"0\"/>\n"
            "        </bit>\n"
            "        <bit name=\"flag1\"/>\n"
            "        <bit name=\"flag2\">\n"
            "          <position value=\"2\"/>\n"
            "        </bit>\n"
            "        <bit name=\"flag3\">\n"
            "          <position value=\"3\"/>\n"
            "        </bit>\n"
            "      </type>\n"
            "      <default value=\"flag0 flag3\"/>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf13\">\n"
            "      <type name=\"binary\"/>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf14\">\n"
            "      <type name=\"leafref\">\n"
            "        <path value=\"/cont1/leaf17\"/>\n"
            "      </type>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf15\">\n"
            "      <type name=\"empty\"/>\n"
            "    </leaf>\n"
            "    <leaf name=\"leaf16\">\n"
            "      <type name=\"union\">\n"
            "        <type name=\"instance-identifier\">\n"
            "          <require-instance value=\"true\"/>\n"
            "        </type>\n"
            "        <type name=\"int8\"/>\n"
            "      </type>\n"
            "    </leaf>\n"
            "    <list name=\"list1\">\n"
            "      <key value=\"leaf18\"/>\n"
            "      <unique tag=\"leaf19\"/>\n"
            "      <min-elements value=\"1\"/>\n"
            "      <max-elements value=\"20\"/>\n"
            "      <leaf name=\"leaf18\">\n"
            "        <type name=\"string\"/>\n"
            "      </leaf>\n"
            "      <leaf name=\"leaf19\">\n"
            "        <type name=\"uint32\"/>\n"
            "      </leaf>\n"
            "      <anyxml name=\"axml1\"/>\n"
            "      <anydata name=\"adata1\"/>\n"
            "      <action name=\"act1\">\n"
            "        <input>\n"
            "          <leaf name=\"leaf24\">\n"
            "            <type name=\"string\"/>\n"
            "          </leaf>\n"
            "        </input>\n"
            "        <output>\n"
            "          <leaf name=\"leaf25\">\n"
            "            <type name=\"string\"/>\n"
            "          </leaf>\n"
            "        </output>\n"
            "      </action>\n"
            "      <notification name=\"notif1\">\n"
            "        <leaf name=\"leaf26\">\n"
            "          <type name=\"string\"/>\n"
            "        </leaf>\n"
            "      </notification>\n"
            "    </list>\n"
            "    <leaf-list name=\"llist1\">\n"
            "      <type name=\"tdef1\"/>\n"
            "      <ordered-by value=\"user\"/>\n"
            "    </leaf-list>\n"
            "    <list name=\"list2\">\n"
            "      <key value=\"leaf27 leaf28\"/>\n"
            "      <leaf name=\"leaf27\">\n"
            "        <type name=\"uint8\"/>\n"
            "      </leaf>\n"
            "      <leaf name=\"leaf28\">\n"
            "        <type name=\"uint8\"/>\n"
            "      </leaf>\n"
            "    </list>\n"
            "    <leaf name=\"leaf29\">\n"
            "      <type name=\"instance-identifier\"/>\n"
            "    </leaf>\n"
            "    <container name=\"must-deviations-container\">\n"
            "      <presence value=\"Allows deviations on the leaf\"/>\n"
            "      <leaf name=\"leaf30\">\n"
            "        <type name=\"string\"/>\n"
            "      </leaf>\n"
            "    </container>\n"
            "    <leaf name=\"leaf23\">\n"
            "      <type name=\"empty\"/>\n"
            "    </leaf>\n"
            "  </container>\n"
            "  <container name=\"test-when\">\n"
            "    <leaf name=\"when-check\">\n"
            "      <type name=\"boolean\"/>\n"
            "    </leaf>\n"
            "    <leaf name=\"gated-data\">\n"
            "      <when condition=\"../when-check = 'true'\"/>\n"
            "      <type name=\"uint16\"/>\n"
            "    </leaf>\n"
            "  </container>\n"
            "  <augment target-node=\"/cont1\">\n"
            "    <leaf name=\"leaf17\">\n"
            "      <type name=\"string\"/>\n"
            "    </leaf>\n"
            "  </augment>\n"
            "  <rpc name=\"rpc1\">\n"
            "    <input>\n"
            "      <leaf name=\"leaf20\">\n"
            "        <type name=\"tdef1\"/>\n"
            "      </leaf>\n"
            "    </input>\n"
            "    <output>\n"
            "      <container name=\"cont2\">\n"
            "        <leaf name=\"leaf21\">\n"
            "          <type name=\"empty\"/>\n"
            "        </leaf>\n"
            "      </container>\n"
            "    </output>\n"
            "  </rpc>\n"
            "</module>\n");

    char *printed;
    struct ly_out *out;

    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out));

    UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
    assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YIN, 0, 0));
    assert_int_equal(strlen(ori_res), ly_out_printed(out));
    assert_string_equal(printed, ori_res);

    ly_out_free(out, NULL, 1);
    free(orig);
    free(ori_res);
}

static LY_ERR
test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name),
        const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format,
        const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
{
    *module_data = user_data;
    *format = LYS_IN_YIN;
    *free_module_data = NULL;
    return LY_SUCCESS;
}

static void
test_submodule(void **state)
{
    struct lys_module *mod;

    const char *mod_yin =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<module name=\"a\"\n"
            "        xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
            "        xmlns:a_mod=\"urn:a\">\n"
            "  <yang-version value=\"1.1\"/>\n"
            "  <namespace uri=\"urn:a\"/>\n"
            "  <prefix value=\"a_mod\"/>\n"
            "  <include module=\"a-sub\"/>\n"
            "</module>\n";

    char *submod_yin =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<submodule name=\"a-sub\"\n"
            "        xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
            "        xmlns:a_mod=\"urn:a\"\n"
            "        xmlns:yt=\"urn:ietf:params:xml:ns:yang:ietf-yang-types\">\n"
            "  <yang-version value=\"1.1\"/>\n"
            "  <belongs-to module=\"a\">\n"
            "    <prefix value=\"a_mod\"/>\n"
            "  </belongs-to>\n"
            "  <import module=\"ietf-yang-types\">\n"
            "    <prefix value=\"yt\"/>\n"
            "    <revision-date date=\"2013-07-15\"/>\n"
            "  </import>\n\n"
            "  <description>\n"
            "    <text>YANG types</text>\n"
            "  </description>\n"
            "  <reference>\n"
            "    <text>RFC reference</text>\n"
            "  </reference>\n"
            "</submodule>\n";

    char *printed;
    struct ly_out *out;

    assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out));

    ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod_yin);

    UTEST_ADD_MODULE(mod_yin, LYS_IN_YIN, NULL, &mod);
    assert_int_equal(LY_SUCCESS, lys_print_submodule(out, mod->parsed->includes[0].submodule, LYS_OUT_YIN, 0, 0));
    assert_int_equal(strlen(submod_yin), ly_out_printed(out));
    assert_string_equal(printed, submod_yin);

    ly_out_free(out, NULL, 1);
}

int
main(void)
{
    const struct CMUnitTest tests[] = {
        UTEST(test_module),
        UTEST(test_submodule),
    };

    return cmocka_run_group_tests(tests, NULL, NULL);
}
