/* Copyright (C) 2014 Trend Micro Inc.
 * All rights reserved.
 *
 * This program is a free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation
 */

#include <check.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include "../os_xml/os_xml.h"
#include "../os_xml/os_xml_internal.h"

Suite *test_suite(void);


static void create_xml_file(const char *str, char file_name[], size_t length)
{
    strncpy(file_name, "/tmp/tmp_file-XXXXXX", length);
    int fd = mkstemp(file_name);

    write(fd, str, strlen(str));
    close(fd);
}

static void nodecat(XML_NODE node, OS_XML *xml, char *buffer)
{
    int i = 0;
    /* write node */
    while (node[i]) {
        strncat(buffer, "<", 1);
        ck_assert_ptr_ne(node[i]->element, NULL);
        strncat(buffer, node[i]->element, strlen(node[i]->element));
        /* write attributes */
        if (node[i]->attributes) {
            ck_assert_ptr_ne(node[i]->values, NULL);
            int j = 0;
            while (node[i]->attributes[j]) {
                strncat(buffer, " ", 1);
                ck_assert_ptr_ne(node[i]->values[j], NULL);
                strncat(buffer, node[i]->attributes[j], strlen(node[i]->attributes[j]));
                strncat(buffer, "=", 1);
                strncat(buffer, "\"", 1);
                strncat(buffer, node[i]->values[j], strlen(node[i]->values[j]));
                strncat(buffer, "\"", 1);
                j++;
            }
            ck_assert_ptr_eq(node[i]->values[j], NULL);
        } else {
            ck_assert_ptr_eq(node[i]->values, NULL);
        }
        strncat(buffer, ">", 1);
        ck_assert_ptr_ne(node[i]->content, NULL);
        strncat(buffer, node[i]->content, strlen(node[i]->content));

        /* write children */
        XML_NODE child = OS_GetElementsbyNode(xml, node[i]);
        if (child != NULL) {
            nodecat(child, xml, buffer);
            OS_ClearNode(child);
        }

        /* close node */
        strncat(buffer, "</", 2);
        strncat(buffer, node[i]->element, strlen(node[i]->element));
        strncat(buffer, ">", 1);
        i++;
    }
}

static void assert_os_xml_eq_str(OS_XML *xml, const char *xml_str)
{
    XML_NODE node = OS_GetElementsbyNode(xml, NULL);
    if (node == NULL) {
        ck_assert_str_eq(xml_str, "");
        return;
    }

    char *buffer = (char *) malloc(6144 * sizeof(char));
    buffer[0] = '\0';
    nodecat(node, xml, buffer);
    OS_ClearNode(node);

    ck_assert_str_eq(buffer, xml_str);

    free(buffer);
}

#define assert_os_xml_eq(PARSE_STR, XML_STR) do { \
    char _ck_xml_file_name[256]; \
    create_xml_file(PARSE_STR, _ck_xml_file_name, 256); \
    OS_XML _ck_xml; \
    ck_assert_int_eq(OS_ReadXML(_ck_xml_file_name, &_ck_xml), 0); \
    ck_assert_int_eq(OS_ApplyVariables(&_ck_xml), 0); \
    assert_os_xml_eq_str(&_ck_xml, XML_STR); \
    OS_ClearXML(&_ck_xml); \
    unlink(_ck_xml_file_name); \
} while (0)

START_TEST(test_simplenodes)
{
    assert_os_xml_eq("", "");
    assert_os_xml_eq("<root></root>", "<root></root>");
    assert_os_xml_eq("<root/>", "<root></root>");
    assert_os_xml_eq("<root />", "<root></root>");
}
END_TEST


START_TEST(test_multiplenodes)
{
    assert_os_xml_eq(
        "<root1></root1>"
        "<root2></root2>"
        "<root3/>",
        "<root1></root1>"
        "<root2></root2>"
        "<root3></root3>");
}
END_TEST

START_TEST(test_children)
{
    assert_os_xml_eq(
        "<root1>"
        "<child1></child1>"
        "</root1>"
        "<root2>"
        "<child1></child1>"
        "<child2/>"
        "<child3>"
        "<child3.1></child3.1>"
        "</child3>"
        "</root2>"
        "<root3></root3>",
        "<root1>"
        "<child1></child1>"
        "</root1>"
        "<root2>"
        "<child1></child1>"
        "<child2></child2>"
        "<child3>"
        "<child3.1></child3.1>"
        "</child3>"
        "</root2>"
        "<root3></root3>");
}
END_TEST

START_TEST(test_multiplecontent)
{
    assert_os_xml_eq(
        "<root>"
        "value1"
        "<child/>"
        "</root>",
        "<root><child></child></root>");
    assert_os_xml_eq(
        "<root>"
        "value1"
        "<child/>"
        "value2"
        "</root>",
        "<root>value2<child></child></root>");
}
END_TEST


START_TEST(test_attributes)
{
    assert_os_xml_eq(
        "<root attr1=\"test\" attr2=\"1\"></root>",
        "<root attr1=\"test\" attr2=\"1\"></root>");

    assert_os_xml_eq(
        "<root attr1=\"test\"\nattr2=\"test\"\tattr3=\"test\"></root>",
        "<root attr1=\"test\" attr2=\"test\" attr3=\"test\"></root>");

    assert_os_xml_eq(
        "<root attr1=\"test/test\"></root>",
        "<root attr1=\"test/test\"></root>");

    assert_os_xml_eq(
        "<root attr1=\"test1\"></root><root attr1=\"test2\"></root>",
        "<root attr1=\"test1\"></root><root attr1=\"test2\"></root>");

    assert_os_xml_eq(
        "<root attr1=\"test\"\n\t  \t\n  \n\t  \nattr2=\"test\"></root>",
        "<root attr1=\"test\" attr2=\"test\"></root>");

    assert_os_xml_eq(
        "<root\n\t  \t\n  \n\t  \nattr1=\"test\"></root>",
        "<root attr1=\"test\"></root>");

    assert_os_xml_eq(
        "<root attr1=\n\t  \t\n  \n\t  \n\"test\"></root>",
        "<root attr1=\"test\"></root>");

    assert_os_xml_eq(
        "<root attr=\"test\" />",
        "<root attr=\"test\"></root>");

    assert_os_xml_eq(
        "<root attr=\"test\"/>",
        "<root attr=\"test\"></root>");
}
END_TEST

START_TEST(test_variables)
{
    assert_os_xml_eq(
        "<var name=\"var1\">value1</var>"
        "<var name=\"var2\">value2</var>"
        "<root attr1=\"$var1\" attr2=\"1\">$var2</root>"
        "<root attr1=\"blah$var1\" attr2=\"1\">blah$var2</root>"
        "<root attr1=\"blah$var1$var2 blah\" attr2=\"1\">blah$var2$var1 blah</root>",
        "<root attr1=\"value1\" attr2=\"1\">value2</root>"
        "<root attr1=\"blahvalue1\" attr2=\"1\">blahvalue2</root>"
        "<root attr1=\"blahvalue1value2 blah\" attr2=\"1\">blahvalue2value1 blah</root>");
}
END_TEST

START_TEST(test_comments)
{
    assert_os_xml_eq(
        "<root1/><!comment!><root2/>",
        "<root1></root1><root2></root2>");
    assert_os_xml_eq(
        "<root1/><!--comment--><root2/>",
        "<root1></root1><root2></root2>");

    assert_os_xml_eq(
        "<root1/><! comment with ! !><root2/>",
        "<root1></root1><root2></root2>");

    assert_os_xml_eq(
        "<root1/><! comment with - --><root2/>",
        "<root1></root1><root2></root2>");
}
END_TEST

START_TEST(test_specialchars)
{
    assert_os_xml_eq(
        "<var name=\"var1\">value1</var>"
        "<root1>\\</root1\\></root1>",
        "<root1>\\</root1\\></root1>");
}
END_TEST

START_TEST(test_linecounter)
{
    char xml_file_name[256];
    create_xml_file("<root1/>\n<root2/>\n<root3/>" , xml_file_name, 256);
    OS_XML xml;
    XML_NODE node;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    ck_assert_ptr_ne(node = OS_GetElementsbyNode(&xml, NULL), NULL);
    ck_assert_int_eq(xml.ln[0], 1);
    ck_assert_int_eq(xml.ln[1], 2);
    ck_assert_int_eq(xml.ln[2], 3);

    OS_ClearNode(node);
    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_invalidfile)
{
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML("invalid_file.inv", &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: File 'invalid_file.inv' not found.");
    ck_assert_int_eq(xml.err_line, 0);

    OS_ClearXML(&xml);
}
END_TEST

START_TEST(test_unclosednode)
{
    char xml_file_name[256];
    create_xml_file("<root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: End of file and some elements were not closed.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_unclosednode2)
{
    char xml_file_name[256];
    create_xml_file("<root></root2>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Element 'root' not closed.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_unclosedcomment)
{
    char xml_file_name[256];
    create_xml_file("<!-- comment", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Comment not closed.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_nodenotopened)
{
    char xml_file_name[256];
    create_xml_file("</root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Element not opened.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_unclosedattribute)
{
    char xml_file_name[256];
    create_xml_file("<root attr=\"attribute></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Attribute 'attr' not closed.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_unquotedattribute)
{
    char xml_file_name[256];
    create_xml_file("<root attr=attribute></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Attribute 'attr' not followed by a \" or '.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_infiniteattribute)
{
    char xml_file_name[256];
    create_xml_file("<root attr", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: End of file while reading an attribute.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_invalidvariablename)
{
    char xml_file_name[256];
    create_xml_file("<var test=\"test\"></var>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_ne(OS_ApplyVariables(&xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Only \"name\" is allowed as an attribute for a variable.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_invalidvariable)
{
    char xml_file_name[256];
    char overflow_string[XML_VARIABLE_MAXSIZE + 10];
    memset(overflow_string, 'c', XML_VARIABLE_MAXSIZE + 9);
    overflow_string[XML_VARIABLE_MAXSIZE + 9] = '\0';

    char xml_string[2 * XML_VARIABLE_MAXSIZE];
    snprintf(xml_string, 2 * XML_VARIABLE_MAXSIZE - 1, "<var name=\"%s\">test</var><root/>", overflow_string);
    create_xml_file(xml_string, xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_ne(OS_ApplyVariables(&xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Invalid variable name size.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_invalidvariable2)
{
    char xml_file_name[256];
    char overflow_string[XML_VARIABLE_MAXSIZE + 10];
    memset(overflow_string, 'c', XML_VARIABLE_MAXSIZE + 9);
    overflow_string[XML_VARIABLE_MAXSIZE + 9] = '\0';

    char xml_string[3 * XML_VARIABLE_MAXSIZE];
    snprintf(xml_string, 3 * XML_VARIABLE_MAXSIZE - 1, "<var name=\"%s\">test</var><test>$%s</test>", overflow_string, overflow_string);
    create_xml_file(xml_string, xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_ne(OS_ApplyVariables(&xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Invalid variable name size.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_unknownvariable1)
{
    char xml_file_name[256];
    create_xml_file("<var name=\"test\">content</var><root>$var</root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_ne(OS_ApplyVariables(&xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Unknown variable: 'var'.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_unknownvariable2)
{
    assert_os_xml_eq("<root>$var</root>",
                     "<root>$var</root>");
}
END_TEST

START_TEST(test_infiniteattribute2)
{
    char xml_file_name[256];
    create_xml_file("<root attr=", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Attribute 'attr' not followed by a \" or '.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_invalidattributestart)
{
    char xml_file_name[256];
    create_xml_file("<root attr=  test\"test\"/>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Attribute 'attr' not followed by a \" or '.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_invalidattributeclosing)
{
    char xml_file_name[256];
    create_xml_file("<root attr=''", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Bad attribute closing for 'attr'=''.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_invalidattributeclosing2)
{
    char xml_file_name[256];
    create_xml_file("<root attr='test'test/>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Bad attribute closing for 'attr'='test'.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_infiniteattribute3)
{
    char xml_file_name[256];
    create_xml_file("<root attr='", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: End of file while reading an attribute.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_duplicateattribute)
{
    char xml_file_name[256];
    create_xml_file("<root attr='test' attr2='test' attr='test123'></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Attribute 'attr' already defined.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_noattributevalue)
{
    char xml_file_name[256];
    create_xml_file("<root attr></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Attribute 'attr' has no value.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_noattributevalue2)
{
    char xml_file_name[256];
    create_xml_file("<root attr attr2='test'></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Attribute 'attr' has no value.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

//TODO
/*START_TEST(test_unknownvariable)
{
    char xml_file_name[256];
    create_xml_file("<root>$var</root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_ne(OS_ApplyVariables(&xml), 0);
    ck_assert_str_eq(xml.err, "XML_ERR: Unknown variable: var");
    ck_assert_int_eq(xml.err_line, 0);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST*/

START_TEST(test_oselementsexists)
{
    char xml_file_name[256];
    create_xml_file("<root></root><root1/><root/>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    ck_assert_int_eq(OS_RootElementExist(&xml, "root"), 2);
    ck_assert_int_eq(OS_RootElementExist(&xml, "root1"), 1);
    ck_assert_int_eq(OS_RootElementExist(&xml, "root2"), 0);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_osgetonecontentforelement)
{
    char xml_file_name[256];
    create_xml_file("<root><child>test</child><child>test2</child><child2>test</child2></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    const char *xml_path1[] = { "root", "child", NULL };
    const char *xml_path2[] = { "root", "child2", NULL };
    const char *xml_path3[] = { "root", "child3", NULL };
    char *content1, *content2;
    ck_assert_str_eq(content1 = OS_GetOneContentforElement(&xml, xml_path1), "test");
    ck_assert_str_eq(content2 = OS_GetOneContentforElement(&xml, xml_path2), "test");
    ck_assert_ptr_eq(OS_GetOneContentforElement(&xml, xml_path3), NULL);

    free(content1);
    free(content2);
    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

static void assert_ox_xml_write_eq(const char *xml_str_old, const char *xml_str_new, const char **xml_path, const char *oldval, const char *newval)
{
    char xml_in_file_name[256];
    create_xml_file(xml_str_old, xml_in_file_name, 256);
    char xml_out_file_name[256];
    create_xml_file("", xml_out_file_name, 256);

    ck_assert_int_eq(OS_WriteXML(xml_in_file_name, xml_out_file_name, xml_path, oldval, newval), 0);

    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_out_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    assert_os_xml_eq_str(&xml, xml_str_new);

    OS_ClearXML(&xml);
    unlink(xml_in_file_name);
    unlink(xml_out_file_name);
}

START_TEST(test_oswritexml_success)
{
    const char *xml_path[] = { "root", "child", NULL };
    assert_ox_xml_write_eq(
        "<root><child>test</child></root>",
        "<root><child>test_new</child></root>",
        xml_path, "test", "test_new");

    assert_ox_xml_write_eq(
        "<root><child>test</child></root>",
        "<root><child>test</child></root>",
        xml_path, "test", "test");

    assert_ox_xml_write_eq(
        "<root><child></child></root>",
        "<root><child>test</child></root>",
        xml_path, "test", "test");

    assert_ox_xml_write_eq(
        "<root2><child></child></root2>",
        "<root2><child></child></root2><root>\n <child>test</child></root>",
        xml_path, NULL, "test");
}
END_TEST

START_TEST(test_oswritexml_failures)
{
    char xml_in_file_name[256];
    create_xml_file("<root><child>test</child></root>", xml_in_file_name, 256);
    char xml_out_file_name[256];
    create_xml_file("", xml_out_file_name, 256);
    const char *xml_path[] = { "root", "child", NULL };

    ck_assert_int_eq(OS_WriteXML("invalid", xml_out_file_name, xml_path, "test", "test_new"), XMLW_NOIN);
    ck_assert_int_eq(OS_WriteXML(xml_in_file_name, "??invalid<<!!\"\"//\\\\", xml_path, "test", "test_new"), XMLW_NOOUT);

    unlink(xml_in_file_name);
    unlink(xml_out_file_name);
}
END_TEST

START_TEST(test_osgetattributecontent)
{
    char xml_file_name[256];
    create_xml_file("<root attr=\"value\" attr2=\"value1\"></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    const char *xml_path1[] = { "root", NULL };
    const char *xml_path2[] = { NULL };
    char *content1, *content2, *content3, *content4;
    ck_assert_str_eq(content1 = OS_GetAttributeContent(&xml, xml_path1, "attr"), "value");
    ck_assert_str_eq(content2 = OS_GetAttributeContent(&xml, xml_path1, "attr2"), "value1");
    ck_assert_str_eq(content3 = OS_GetAttributeContent(&xml, xml_path1, "attr3"), "");
    ck_assert_str_eq(content4 = OS_GetAttributeContent(&xml, xml_path1, NULL), "");
    ck_assert_ptr_eq(OS_GetAttributeContent(&xml, xml_path2, NULL), NULL);


    free(content1);
    free(content2);
    free(content3);
    free(content4);
    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_osgetcontents)
{
    char xml_file_name[256];
    create_xml_file("<root>value</root><root>value2</root><root2>value</root2>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    const char *xml_path1[] = { "root", NULL };
    const char *xml_path2[] = { "root2", NULL };
    char **content1, **content2;
    ck_assert_ptr_ne(content1 = OS_GetContents(&xml, xml_path1), NULL);
    ck_assert_str_eq(content1[0], "value");
    ck_assert_str_eq(content1[1], "value2");
    ck_assert_ptr_eq(content1[2], NULL);

    ck_assert_ptr_ne(content2 = OS_GetContents(&xml, xml_path2), NULL);
    ck_assert_str_eq(content2[0], "value");
    ck_assert_ptr_eq(content2[1], NULL);

    ck_assert_ptr_eq(OS_GetContents(&xml, NULL), NULL);

    int i = 0;
    while (content1[i]) {
        free(content1[i++]);
    }
    free(content1);

    i = 0;
    while (content2[i]) {
        free(content2[i++]);
    }
    free(content2);
    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_osgetelementcontent)
{
    char xml_file_name[256];
    create_xml_file("<root>value</root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    const char *xml_path[] = { "root", NULL };
    char **content;
    ck_assert_ptr_ne(content = OS_GetElementContent(&xml, xml_path), NULL);
    ck_assert_str_eq(content[0], "value");
    ck_assert_ptr_eq(content[1], NULL);

    int i = 0;
    while (content[i]) {
        free(content[i++]);
    }
    free(content);
    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_osgetelements)
{
    char xml_file_name[256];
    create_xml_file("<root><child1/><child2/></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    const char *xml_path[] = { "root", NULL };
    char **content1, **content2;
    ck_assert_ptr_ne(content1 = OS_GetElements(&xml, xml_path), NULL);
    ck_assert_str_eq(content1[0], "child1");
    ck_assert_str_eq(content1[1], "child2");
    ck_assert_ptr_eq(content1[2], NULL);
    ck_assert_ptr_ne(content2 = OS_GetElements(&xml, NULL), NULL);
    ck_assert_str_eq(content2[0], "root");
    ck_assert_ptr_eq(content2[1], NULL);
    const char *xml_path2[] = { NULL };
    ck_assert_ptr_eq(OS_GetElements(&xml,  xml_path2), NULL);

    int i = 0;
    while (content1[i]) {
        free(content1[i++]);
    }
    free(content1);
    i = 0;
    while (content2[i]) {
        free(content2[i++]);
    }
    free(content2);
    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_osgetattributes)
{
    char xml_file_name[256];
    create_xml_file("<root attr1=\"1\" attr2=\"2\"></root>", xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_eq(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_int_eq(OS_ApplyVariables(&xml), 0);
    const char *xml_path[] = { "root", NULL };
    char **content;
    ck_assert_ptr_ne(content = OS_GetAttributes(&xml, xml_path), NULL);
    ck_assert_str_eq(content[0], "attr1");
    ck_assert_str_eq(content[1], "attr2");
    ck_assert_ptr_eq(content[2], NULL);

    int i = 0;
    while (content[i]) {
        free(content[i++]);
    }
    free(content);
    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_osclearnode)
{
    OS_ClearNode(NULL);
}
END_TEST

START_TEST(test_stringoverflow)
{
    char xml_file_name[256];
    char overflow_string[XML_MAXSIZE + 10];
    memset(overflow_string, 'c', XML_MAXSIZE + 9);
    overflow_string[XML_MAXSIZE + 9] = '\0';

    char xml_string[2 * XML_MAXSIZE];
    snprintf(xml_string, 2 * XML_MAXSIZE - 1, "<%s/>", overflow_string);
    create_xml_file(xml_string, xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: String overflow.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_stringoverflow2)
{
    char xml_file_name[256];
    char overflow_string[XML_MAXSIZE + 10];
    memset(overflow_string, 'c', XML_MAXSIZE + 9);
    overflow_string[XML_MAXSIZE + 9] = '\0';

    char xml_string[2 * XML_MAXSIZE];
    snprintf(xml_string, 2 * XML_MAXSIZE - 1, "<test>%s</test>", overflow_string);
    create_xml_file(xml_string, xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: String overflow.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_stringoverflow3)
{
    char xml_file_name[256];
    char overflow_string[XML_MAXSIZE + 10];
    memset(overflow_string, 'c', XML_MAXSIZE + 9);
    overflow_string[XML_MAXSIZE + 9] = '\0';

    char xml_string[2 * XML_MAXSIZE];
    snprintf(xml_string, 2 * XML_MAXSIZE - 1, "<test %s=\"test\">test</test>", overflow_string);
    create_xml_file(xml_string, xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Overflow attempt at attribute 'cccccccccccccccccccc'.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

START_TEST(test_stringoverflow4)
{
    char xml_file_name[256];
    char overflow_string[XML_MAXSIZE + 10];
    memset(overflow_string, 'c', XML_MAXSIZE + 9);
    overflow_string[XML_MAXSIZE + 9] = '\0';

    char xml_string[2 * XML_MAXSIZE];
    snprintf(xml_string, 2 * XML_MAXSIZE - 1, "<test test=\"%s\">test</test>", overflow_string);
    create_xml_file(xml_string, xml_file_name, 256);
    OS_XML xml;
    ck_assert_int_ne(OS_ReadXML(xml_file_name, &xml), 0);
    ck_assert_str_eq(xml.err, "XMLERR: Overflow attempt at attribute 'test'.");
    ck_assert_int_eq(xml.err_line, 1);

    OS_ClearXML(&xml);
    unlink(xml_file_name);
}
END_TEST

Suite *test_suite(void)
{
    Suite *s = suite_create("os_xml");

    /* Core test case */
    TCase *tc_core = tcase_create("Core");
    tcase_add_test(tc_core, test_simplenodes);
    tcase_add_test(tc_core, test_multiplenodes);
    tcase_add_test(tc_core, test_multiplecontent);
    tcase_add_test(tc_core, test_children);
    tcase_add_test(tc_core, test_attributes);
    tcase_add_test(tc_core, test_variables);
    tcase_add_test(tc_core, test_comments);
    tcase_add_test(tc_core, test_specialchars);
    tcase_add_test(tc_core, test_linecounter);
    tcase_add_test(tc_core, test_invalidfile);
    tcase_add_test(tc_core, test_unclosednode);
    tcase_add_test(tc_core, test_unclosednode2);
    tcase_add_test(tc_core, test_unclosedcomment);
    tcase_add_test(tc_core, test_nodenotopened);
    tcase_add_test(tc_core, test_unclosedattribute);
    tcase_add_test(tc_core, test_infiniteattribute);
    tcase_add_test(tc_core, test_unquotedattribute);
    tcase_add_test(tc_core, test_invalidvariablename);
    tcase_add_test(tc_core, test_invalidvariable);
    tcase_add_test(tc_core, test_invalidvariable2);
    tcase_add_test(tc_core, test_unknownvariable1);
    tcase_add_test(tc_core, test_unknownvariable2);
    tcase_add_test(tc_core, test_infiniteattribute2);
    tcase_add_test(tc_core, test_invalidattributestart);
    tcase_add_test(tc_core, test_invalidattributeclosing);
    tcase_add_test(tc_core, test_invalidattributeclosing2);
    tcase_add_test(tc_core, test_infiniteattribute3);
    tcase_add_test(tc_core, test_duplicateattribute);
    tcase_add_test(tc_core, test_noattributevalue);
    tcase_add_test(tc_core, test_noattributevalue2);
    tcase_add_test(tc_core, test_oselementsexists);
    tcase_add_test(tc_core, test_osgetonecontentforelement);
    tcase_add_test(tc_core, test_oswritexml_success);
    tcase_add_test(tc_core, test_oswritexml_failures);
    tcase_add_test(tc_core, test_osgetattributecontent);
    tcase_add_test(tc_core, test_osgetcontents);
    tcase_add_test(tc_core, test_osgetelementcontent);
    tcase_add_test(tc_core, test_osgetelements);
    tcase_add_test(tc_core, test_osgetattributes);
    tcase_add_test(tc_core, test_osclearnode);
    tcase_add_test(tc_core, test_stringoverflow);
    tcase_add_test(tc_core, test_stringoverflow2);
    tcase_add_test(tc_core, test_stringoverflow3);
    tcase_add_test(tc_core, test_stringoverflow4);
    suite_add_tcase(s, tc_core);

    return (s);
}

int main(void)
{
    Suite *s = test_suite();
    SRunner *sr = srunner_create(s);
    srunner_run_all(sr, CK_NORMAL);
    int number_failed = srunner_ntests_failed(sr);
    srunner_free(sr);

    return ((number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
