#include "pico_config.h"
#include "pico_stack.h"
#include "pico_addressing.h"
#include "pico_socket.h"
#include "pico_ipv4.h"
#include "pico_ipv6.h"
#include "pico_dns_common.h"
#include "pico_tree.h"
#include "modules/pico_dns_common.c"
#include "check.h"

Suite *pico_suite(void);

START_TEST(tc_dns_rdata_cmp) /* MARK: dns_rdata_cmp */
{
    uint8_t rdata1[10] = {
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    };
    uint8_t rdata2[10] = {
        1, 2, 3, 3, 5, 6, 7, 8, 9, 10
    };
    uint8_t rdata3[1] = {
        2
    };
    uint8_t rdata4[1] = {
        1
    };
    uint8_t rdata5[11] = {
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9
    };
    uint8_t rdata6[12] = {
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
    };

    uint8_t rdata7[5] = {
        72, 69, 76, 76, 79
    };

    uint8_t rdata8[5] = {
        104, 101, 108, 108, 111
    };

    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    /* Check equal data and size */
    ret = pico_dns_rdata_cmp(rdata1, rdata1, 10, 10, 0);
    fail_unless(!ret, "dns_rdata_cmp failed with equal data and size, case-sensitive!\n");

    /* Check smaller data and equal size */
    ret = pico_dns_rdata_cmp(rdata1, rdata2, 10, 10, 0);
    fail_unless(ret > 0, "dns_rdata_cmp failed with smaller data and equal size, case-sensitive!\n");

    /* Check larger data and smaller size */
    ret = pico_dns_rdata_cmp(rdata1, rdata3, 10, 1, 0);
    fail_unless(ret < 0, "dns_rdata_cmp failed with larger data and smaller size, case-sensitive!\n");

    /* Check equal data and smaller size */
    ret = pico_dns_rdata_cmp(rdata1, rdata4, 10, 1, 0);
    fail_unless(ret > 0, "dns_rdata_cmp failed with equal data and smaller size, case-sensitive!\n");

    /* Check smaller data and larger size */
    ret = pico_dns_rdata_cmp(rdata1, rdata5, 10, 11, 0);
    fail_unless(ret < 0, "dns_rdata_cmp failed with equal data and larger size, case-sensitive!\n");

    /* Check larger data and larger size */
    ret = pico_dns_rdata_cmp(rdata1, rdata6, 10, 12, 0);
    fail_unless(ret < 0, "dns_rdata_cmp failed with larger data and larger size, case-sensitive!\n");

    /* Check for tolower effect */
    ret = pico_dns_rdata_cmp(rdata7, rdata8, 5, 5, 0);
    fail_unless(ret < 0, "dns_rdata_cmp failed with check for tolower effect, case-sensitive!\n");

    /* now check with case-insensitive */

    /* Check equal data and size */
    ret = pico_dns_rdata_cmp(rdata1, rdata1, 10, 10, 1);
    fail_unless(!ret, "dns_rdata_cmp failed with equal data and size, case-insensitive!\n");

    /* Check smaller data and equal size */
    ret = pico_dns_rdata_cmp(rdata1, rdata2, 10, 10, 1);
    fail_unless(ret > 0, "dns_rdata_cmp failed with smaller data and equal size, case-insensitive!\n");

    /* Check larger data and smaller size */
    ret = pico_dns_rdata_cmp(rdata1, rdata3, 10, 1, 1);
    fail_unless(ret < 0, "dns_rdata_cmp failed with larger data and smaller size, case-insensitive!\n");

    /* Check equal data and smaller size */
    ret = pico_dns_rdata_cmp(rdata1, rdata4, 10, 1, 1);
    fail_unless(ret > 0, "dns_rdata_cmp failed with equal data and smaller size, case-insensitive!\n");

    /* Check smaller data and larger size */
    ret = pico_dns_rdata_cmp(rdata1, rdata5, 10, 11, 1);
    fail_unless(ret < 0, "dns_rdata_cmp failed with equal data and larger size, case-insensitive!\n");

    /* Check larger data and larger size */
    ret = pico_dns_rdata_cmp(rdata1, rdata6, 10, 12, 1);
    fail_unless(ret < 0, "dns_rdata_cmp failed with larger data and larger size, case-insensitive!\n");

    /* Check for tolower effect */
    ret = pico_dns_rdata_cmp(rdata7, rdata8, 5, 5, 1);
    fail_unless(ret == 0, "dns_rdata_cmp failed with check for tolower effect, case-insensitive!\n");

    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_dns_question_cmp) /* MARK: dns_question_cmp */
{
    struct pico_dns_question *a = NULL, *b = NULL;
    const char *url1 = "host (2).local";
    const char *url3 = "host.local";
    const char *url2 = "192.168.2.1";
    uint16_t len = 0;
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    a = pico_dns_question_create(url1, &len, PICO_PROTO_IPV4, PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(!a, "Question A could not be created!\n");
    b = pico_dns_question_create(url3, &len, PICO_PROTO_IPV4, PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(!b, "Question B could not be created!\n");

    ret = pico_dns_question_cmp((void *)a, (void *)b);
    fail_unless(ret > 0, "Question is lexicographically smaller");
    pico_dns_question_delete((void **)&a);
    pico_dns_question_delete((void **)&b);

    a = pico_dns_question_create(url2, &len, PICO_PROTO_IPV4, PICO_DNS_TYPE_PTR,
                                 PICO_DNS_CLASS_IN, 1);
    fail_if(!a, "Question A could not be created!\n");
    b = pico_dns_question_create(url2, &len, PICO_PROTO_IPV4, PICO_DNS_TYPE_PTR,
                                 PICO_DNS_CLASS_IN, 1);
    fail_if(!b, "Question B could not be created!\n");

    ret = pico_dns_question_cmp((void *)a, (void *)b);
    fail_unless(!ret, "Question A and B should be equal!\n");

    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_dns_qtree_insert) /* MARK: dns_qtree_insert*/
{
    const char *url = "host.local";
    const char *url2 = "host (2).local";
    const char *url3 = "host (3).local";
    struct pico_dns_question *a = NULL, *b = NULL, *c = NULL;
    uint16_t qlen = 0;
    PICO_DNS_QTREE_DECLARE(qtree);
    PICO_DNS_QTREE_DECLARE(qtree2);

    printf("*********************** starting %s * \n", __func__);

    a = pico_dns_question_create(url, &qlen, PICO_PROTO_IPV4, PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(!a || !(a->qname) || !(a->qsuffix), "Could not create question A!\n");
    b = pico_dns_question_create(url2, &qlen, PICO_PROTO_IPV4, PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(!b || !(b->qname) || !(b->qsuffix), "Coud not create question B!\n");

    pico_tree_insert(&qtree, a);
    fail_unless(pico_tree_count(&qtree) == 1,
                "pico_tree_insert failed with tree 1 question A!\n");

    pico_tree_insert(&qtree, b);
    fail_unless(2 == pico_tree_count(&qtree),
                "pico_tree_insert failed with tree 1 question B!\n");

    PICO_DNS_QTREE_DESTROY(&qtree);
    fail_unless(0 == pico_tree_count(&qtree),
                "Question tree not properly destroyed!\n");
    c = pico_dns_question_create(url3, &qlen, PICO_PROTO_IPV4, PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(!c || !(c->qname) || !(c->qsuffix), "Coud not create question B!\n");
    pico_tree_insert(&qtree2, c);
    fail_unless(1 == pico_tree_count(&qtree2),
                "pico_tree_insert failed with tree 2 question B!\n");
    PICO_DNS_QTREE_DESTROY(&qtree2);

    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_dns_record_cmp) /* MARK: dns_record_cmp */
{
    struct pico_dns_record *a = NULL;
    struct pico_dns_record *b = NULL;
    const char *url1 = "foo.local";
    const char *url3 = "a.local";
    struct pico_ip4 rdata = {
        0
    };
    uint16_t len = 0;
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    /* Create test records */
    a = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!a, "Record A could not be created!\n");
    b = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!b, "Record B could not be created!\n");

    /* Try to compare equal records */
    ret = pico_dns_record_cmp((void *) a, (void *) b);
    fail_unless(!ret, "dns_record_cmp failed with equal records - %d!\n", ret);
    pico_dns_record_delete((void **)&a);
    pico_dns_record_delete((void **)&b);

    /* Create different test records */
    a = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_AAAA,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!a, "Record A could not be created!\n");
    b = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!b, "Record B could not be created!\n");

    /* Try to compare records with equal rname but different type */
    ret = pico_dns_record_cmp((void *) a, (void *) b);
    fail_unless(ret > 0, "dns_record_cmp failed with same name, different types!\n");
    pico_dns_record_delete((void **)&a);
    pico_dns_record_delete((void **)&b);

    /* Create different test records */
    a  = pico_dns_record_create(url3, &rdata, 4, &len, PICO_DNS_TYPE_A,
                                PICO_DNS_CLASS_IN, 0);
    fail_if(!a, "Record A could not be created!\n");
    b  = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_A,
                                PICO_DNS_CLASS_IN, 0);
    fail_if(!b, "Record B could not be created!\n");

    /* Try to compare records with different rname but equal type */
    ret = pico_dns_record_cmp((void *) a, (void *) b);
    fail_unless(ret < 0, "mdns_cmp failed with different name, same types!\n");
    pico_dns_record_delete((void **)&a);
    pico_dns_record_delete((void **)&b);

    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_dns_rtree_insert) /* MARK: dns_rtree_insert*/
{
    PICO_DNS_RTREE_DECLARE(rtree);
    PICO_DNS_RTREE_DECLARE(rtree2);
    struct pico_dns_record *a = NULL;
    struct pico_dns_record *b = NULL, *c = NULL;
    const char *url1 = "foo.local";
    struct pico_ip4 rdata = {
        0
    };
    uint16_t len = 0;

    printf("*********************** starting %s * \n", __func__);

    /* Create test records */
    a = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_AAAA,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!a, "Record A could not be created!\n");
    b = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!b, "Record B could not be created!\n");

    pico_tree_insert(&rtree, a);
    pico_tree_insert(&rtree, b);

    PICO_DNS_RTREE_DESTROY(&rtree);
    fail_unless(pico_tree_count(&rtree) == 0,
                "Record tree not properly destroyed!\n");

    c = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!c, "Record C could not be created!\n");
    pico_tree_insert(&rtree2, c);

    PICO_DNS_RTREE_DESTROY(&rtree2);
    fail_unless(pico_tree_count(&rtree2) == 0,
                "Record tree not properly destroyed!\n");

    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_dns_record_cmp_name_type) /* MARK: dns_record_cmp_name_type */
{
    struct pico_dns_record *a = NULL;
    struct pico_dns_record *b = NULL;
    const char *url1 = "foo.local";
    const char *url3 = "a.local";
    struct pico_ip4 rdata = {
        0
    };
    uint16_t len = 0;
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    /* Create different test records */
    a = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_AAAA,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!a, "Record A could not be created!\n");
    b = pico_dns_record_create(url1, &rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 0);
    fail_if(!b, "Record B could not be created!\n");

    /* Try to compare records with equal rname but different type */
    ret = pico_dns_record_cmp_name_type((void *) a, (void *) b);
    fail_unless(ret > 0, "dns_record_cmp failed with same name, different types!\n");
    pico_dns_record_delete((void **)&a);
    pico_dns_record_delete((void **)&b);

    /* Create exactly the same test records */
    a  = pico_dns_record_create(url3, &rdata, 4, &len, PICO_DNS_TYPE_A,
                                PICO_DNS_CLASS_IN, 0);
    fail_if(!a, "Record A could not be created!\n");
    b  = pico_dns_record_create(url3, &rdata, 4, &len, PICO_DNS_TYPE_A,
                                PICO_DNS_CLASS_IN, 0);
    fail_if(!b, "Record B could not be created!\n");

    /* Try to compare records with different rname but equal type */
    ret = pico_dns_record_cmp_name_type((void *) a, (void *) b);
    fail_unless(!ret, "dns_record_cmp_name_type failed with same names, same types!\n");
    pico_dns_record_delete((void **)&a);
    pico_dns_record_delete((void **)&b);

    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_fill_packet_header) /* MARK: dns_fill_packet_header */
{
    struct pico_dns_header *header = NULL;
    uint8_t answer_buf[12] = {
        0x00, 0x00,
        0x85, 0x00,
        0x00, 0x00,
        0x00, 0x01,
        0x00, 0x01,
        0x00, 0x01
    };
    uint8_t query_buf[12] = {
        0x00, 0x00,
        0x01, 0x00,
        0x00, 0x01,
        0x00, 0x01,
        0x00, 0x01,
        0x00, 0x01
    };
    int i = 0;

    printf("*********************** starting %s * \n", __func__);

    header = (struct pico_dns_header *)
             PICO_ZALLOC(sizeof(struct pico_dns_header));

    fail_if(NULL == header, "Not enough space!\n");

    /* Create a query header */
    pico_dns_fill_packet_header(header, 1, 1, 1, 1);

    for (i = 0; i < 12; i++)
        printf("### %02x :: %02x\n", ((uint8_t*)header)[i], query_buf[i]);
    fail_unless(0 == memcmp((void *)header, (void *)query_buf, 12),
                "Comparing query header failed!\n");

    /* Create a answer header */
    pico_dns_fill_packet_header(header, 0, 1, 1, 1);

    fail_unless(0 == memcmp((void *)header, (void *)answer_buf, 12),
                "Comparing answer header failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_fill_packet_rr_section) /* MARK: dns_fill_packet_rr_section */
{
    printf("*********************** starting %s * \n", __func__);

    /* TODO: Insert test here */

    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_fill_packet_rr_sections) /* MARK: dns_fill_packet_rr_sections */
{
    pico_dns_packet *packet = NULL;
    PICO_DNS_QTREE_DECLARE(qtree);
    PICO_DNS_RTREE_DECLARE(antree);
    PICO_DNS_RTREE_DECLARE(nstree);
    PICO_DNS_RTREE_DECLARE(artree);
    struct pico_dns_record *record = NULL;
    const char *rname = "picotcp.com";
    uint8_t rdata[4] = {
        10, 10, 0, 1
    };
    uint8_t cmp_buf[39] = {
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x00u, 0x00u, 0x00u, 0x78u,
        0x00u, 0x04u,
        10u, 10u, 0u, 1u
    };
    uint16_t len = 0;
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    /* Create a new A record */
    record = pico_dns_record_create(rname, rdata, 4, &len, PICO_DNS_TYPE_A,
                                    PICO_DNS_CLASS_IN, 120);
    fail_if(!record, "dns_record_create failed!\n");

    /* Add the record to a tree */
    pico_tree_insert(&antree, record);

    /* Try to fill the rr sections with packet as a NULL-pointer */
    ret = pico_dns_fill_packet_rr_sections(packet, &qtree, &antree,
                                           &nstree, &artree);
    fail_unless(ret, "Checking of params failed!\n");

    len = (uint16_t)sizeof(struct pico_dns_header);
    pico_tree_size(&qtree, &len, &pico_dns_question_size);
    pico_tree_size(&antree, &len, &pico_dns_record_size);
    pico_tree_size(&nstree, &len, &pico_dns_record_size);
    pico_tree_size(&artree, &len, &pico_dns_record_size);
    printf("Packet len: %d\n", len);

    /* Allocate the packet with the right size */
    packet = (pico_dns_packet *)PICO_ZALLOC((size_t)len);
    fail_if(NULL == packet, "Allocating packet failed!\n");
    fail_if(pico_dns_fill_packet_rr_sections(packet, &qtree, &antree, &nstree,
                                             &artree),
            "Filling of rr sections failed!\n");

    fail_unless(memcmp((void *)packet, (void *)cmp_buf, 39) == 0,
                "Filling of rr sections went wrong!\n");
    PICO_FREE(packet);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_fill_packet_question_section) /* MARK: dns_fill_packet_question_section */
{
    pico_dns_packet *packet = NULL;
    PICO_DNS_QTREE_DECLARE(qtree);
    struct pico_dns_question *a = NULL, *b = NULL;
    const char *qurl = "picotcp.com";
    uint8_t cmp_buf[45] = {
        0x00u, 0x00u,                     /* 2 */
        0x00u, 0x00u,                     /* 2 */
        0x00u, 0x00u,                     /* 2 */
        0x00u, 0x00u,                     /* 2 */
        0x00u, 0x00u,                     /* 2 */
        0x00u, 0x00u,                     /* 2 //12 */
        0x06u, 'g', 'o', 'o', 'g', 'l', 'e',                /* 7 */
        0x03u, 'c', 'o', 'm',                   /* 4 */
        0x00u,                     /* 1 //12 */
        0x00u, 0x01u,
        0x00u, 0x01u,                     /* 4 */
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',               /* 8 */
        0x03u, 'c', 'o', 'm',                   /* 4 */
        0x00u,                     /* 1 //13 */
        0x00u, 0x01u,
        0x00u, 0x01u
    };                                     /* 4 */
    uint16_t len = 0;

    printf("*********************** starting %s * \n", __func__);

    /* Create DNS questions and a vector of them */
    a = pico_dns_question_create(qurl, &len, PICO_PROTO_IPV4, PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(NULL == a, "dns_question_create failed!\n");
    b = pico_dns_question_create("google.com", &len, PICO_PROTO_IPV4,
                                 PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(NULL == b, "dns_question_create failed!\n");

    pico_tree_insert(&qtree, a);
    pico_tree_insert(&qtree, b);

    /* Determine the length of the packet and provide space */
    len = (uint16_t)sizeof(struct pico_dns_header);
    pico_tree_size(&qtree, &len, &pico_dns_question_size);
    printf("Packet len: %d - 45\n", len);
    packet = (pico_dns_packet *)PICO_ZALLOC((size_t)len);

    fail_if(NULL == packet, "Allocating packet failed!\n");
    fail_if(pico_dns_fill_packet_question_section(packet, &qtree),
            "Filling of rr sections failed!\n");

    fail_unless(memcmp((void *)packet, (void *)cmp_buf, 45) == 0,
                "Filling of question sesction went wrong!\n");
    PICO_FREE(packet);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_packet_compress_find_ptr) /* MARK: dns_packet_compress_find_ptr */
{
    uint8_t data[] = "abcdef\5local\0abcdef\4test\5local";
    uint8_t *name = (uint8_t *)(data + 24);
    uint16_t len = 31;
    uint8_t *ptr = NULL;

    printf("*********************** starting %s * \n", __func__);

    ptr = pico_dns_packet_compress_find_ptr(name, data, len);
    fail_unless(ptr == (data + 6), "Finding compression ptr failed %p - %p!\n", ptr,
                data + 6);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_packet_compress_name) /* MARK: dns_packet_compress_name */
{
    uint8_t buf[46] = {
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u
    };

    uint8_t *name = buf + 29u;
    uint16_t len = 46;
    int ret = 0;
    printf("*********************** starting %s * \n", __func__);

    ret = pico_dns_packet_compress_name(name, buf, &len);
    fail_unless(ret == 0, "dns_packet_compress_name returned error!\n");
    fail_unless(len == (46 - 11), "packet_compress_name return wrong length!\n");
    fail_unless(memcmp(name, "\xc0\x0c", 2) == 0, "packet_compress_name failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_packet_compress) /* MARK: dns_packet_compress */
{
    uint8_t buf[83] = {
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x01u,
        0x00u, 0x00u,
        0x00u, 0x02u,
        0x00u, 0x00u,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x00u, 0x00u, 0x00, 0x0A,
        0x00u, 0x04u,
        0x0Au, 0x0Au, 0x0A, 0x0A,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x00u, 0x00u, 0x00, 0x0A,
        0x00u, 0x04u,
        0x0Au, 0x0Au, 0x0A, 0x0A
    };
    uint8_t cmp_buf[61] = {
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x01u,
        0x00u, 0x00u,
        0x00u, 0x02u,
        0x00u, 0x00u,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0xC0u, 0x0Cu,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x00u, 0x00u, 0x00, 0x0A,
        0x00u, 0x04u,
        0x0Au, 0x0Au, 0x0A, 0x0A,
        0xC0u, 0x0Cu,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x00u, 0x00u, 0x00, 0x0A,
        0x00u, 0x04u,
        0x0Au, 0x0Au, 0x0A, 0x0A
    };
    pico_dns_packet *packet = (pico_dns_packet *)buf;
    uint16_t len = 83;
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    ret = pico_dns_packet_compress(packet, &len);

    fail_unless(ret == 0, "dns_packet_compress returned error!\n");
    fail_unless(len == (83 - 22), "packet_compress returned length %u!\n", len);
    fail_unless(memcmp(packet, cmp_buf, 61) == 0, "packet_compress_name failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_question_fill_qsuffix) /* MARK: dns_question_fill_suffix */
{
    struct pico_dns_question_suffix suffix;
    printf("*********************** starting %s * \n", __func__);

    pico_dns_question_fill_suffix(&suffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN);

    fail_unless((suffix.qtype == short_be(PICO_DNS_TYPE_A)) &&
                (suffix.qclass == short_be(PICO_DNS_CLASS_IN)),
                "Filling qsuffix failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_question_delete) /* MARK: dns_question_delete */
{
    const char *qurl = "picotcp.com";
    uint16_t len = 0;
    int ret = 0;
    struct pico_dns_question *a = pico_dns_question_create(qurl, &len,
                                                           PICO_PROTO_IPV4,
                                                           PICO_DNS_TYPE_A,
                                                           PICO_DNS_CLASS_IN,
                                                           0);
    printf("*********************** starting %s * \n", __func__);

    ret = pico_dns_question_delete((void **)&a);

    fail_unless(ret == 0, "dns_question_delete returned error!\n");
    fail_unless(a == NULL, "dns_question_delete failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_question_create) /* MARK: dns_quesiton_create */
{
    const char *qurl = "picotcp.com";
    const char *qurl2 = "1.2.3.4";
    const char *qurl3 = "2001:0db8:0000:0000:0000:0000:0000:0000";
    char buf[13] = {
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03u, 'c', 'o', 'm',
        0x00u
    };
    char buf2[22] = {
        0x01u, '4',
        0x01u, '3',
        0x01u, '2',
        0x01u, '1',
        0x07u, 'i', 'n', '-', 'a', 'd', 'd', 'r',
        0x04u, 'a', 'r', 'p', 'a',
        0x00u
    };
    char buf3[74] = {
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '8', 0x01u, 'b', 0x01u, 'd', 0x01u, '0',
        0x01u, '1', 0x01u, '0', 0x01u, '0', 0x01u, '2',
        0x03u, 'I', 'P', '6',
        0x04u, 'A', 'R', 'P', 'A',
        0x00u
    };
    uint16_t len = 0;
    struct pico_dns_question *a = NULL;

    printf("*********************** starting %s * \n", __func__);

    /* First, plain A record */
    a = pico_dns_question_create(qurl, &len,
                                 PICO_PROTO_IPV4,
                                 PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN,
                                 0);
    fail_if(a == NULL, "dns_question_created returned NULL!\n");
    fail_unless(strcmp(a->qname, buf) == 0, "url not converted correctly!\n");
    fail_unless(short_be(a->qsuffix->qtype) == PICO_DNS_TYPE_A,
                "qtype not properly set!\n");
    fail_unless(short_be(a->qsuffix->qclass) == PICO_DNS_CLASS_IN,
                "qclass not properly set!\n");
    pico_dns_question_delete((void **)&a);

    /* Reverse PTR record for IPv4 address */
    a = pico_dns_question_create(qurl2, &len, PICO_PROTO_IPV4,
                                 PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN, 1);
    fail_unless(strcmp(a->qname, buf2) == 0, "url2 not converted correctly! %s\n", a->qname);
    fail_unless(short_be(a->qsuffix->qtype) == PICO_DNS_TYPE_PTR,
                "qtype2 not properly set!\n");
    fail_unless(short_be(a->qsuffix->qclass) == PICO_DNS_CLASS_IN,
                "qclass2 not properly set!\n");
    pico_dns_question_delete((void **)&a);

    /* Reverse PTR record for IPv6 address */
    a = pico_dns_question_create(qurl3, &len, PICO_PROTO_IPV6,
                                 PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN, 1);
    fail_unless(strcmp(a->qname, buf3) == 0, "url3 not converted correctly!\n");
    fail_unless(short_be(a->qsuffix->qtype) == PICO_DNS_TYPE_PTR,
                "qtype3 not properly set!\n");
    fail_unless(short_be(a->qsuffix->qclass) == PICO_DNS_CLASS_IN,
                "qclass3 not properly set!\n");
    pico_dns_question_delete((void **)&a);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_query_create) /* MARK: dns_query_create */
{
    pico_dns_packet *packet = NULL;
    PICO_DNS_QTREE_DECLARE(qtree);
    const char *qurl = "picotcp.com";
    const char *qurl2 = "google.com";
    uint8_t buf[42] = {
        0x00u, 0x00u,
        0x01u, 0x00u,
        0x00u, 0x02u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x06u, 'g', 'o', 'o', 'g', 'l', 'e',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0xc0u, 0x13u,
        0x00u, 0x01u,
        0x00u, 0x01u
    };
    uint16_t len = 0;
    int ret = 0;
    struct pico_dns_question *a = NULL, *b = NULL;

    printf("*********************** starting %s * \n", __func__);


    a = pico_dns_question_create(qurl, &len, PICO_PROTO_IPV4, PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(!a, "dns_question_create failed!\n");
    fail_unless(ret == 0, "dns_question_vector_add returned error!\n");
    b = pico_dns_question_create(qurl2, &len, PICO_PROTO_IPV4, PICO_DNS_TYPE_A,
                                 PICO_DNS_CLASS_IN, 0);
    fail_if(!b, "dns_question_create failed!\n");
    fail_unless(ret == 0, "dns_question_vector_add returned error!\n");

    pico_tree_insert(&qtree, a);
    pico_tree_insert(&qtree, b);

    packet = pico_dns_query_create(&qtree, NULL, NULL, NULL, &len);
    fail_if(packet == NULL, "dns_query_create returned NULL!\n");
    fail_unless(0 == memcmp(buf, (void *)packet, 42),
                "dns_query_created failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_record_fill_suffix) /* MARK: dns_record_fill_suffix */
{
    struct pico_dns_record_suffix *suffix = NULL;

    printf("*********************** starting %s * \n", __func__);

    pico_dns_record_fill_suffix(&suffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN,
                                120, 4);

    fail_unless((suffix->rtype == short_be(PICO_DNS_TYPE_A) &&
                 suffix->rclass == short_be(PICO_DNS_CLASS_IN) &&
                 suffix->rttl == long_be(120) &&
                 suffix->rdlength == short_be(4)),
                "Filling rsuffix failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_record_copy_flat) /* MARK: dns_record_copy_flat */
{
    struct pico_dns_record *record = NULL;
    const char *url = "picotcp.com";
    uint8_t rdata[4] = {
        10, 10, 0, 1
    };
    uint8_t buf[128] = {
        0
    };
    uint8_t *ptr = NULL;
    uint8_t cmp_buf[27] = {
        0x07, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03, 'c', 'o', 'm',
        0x00,
        0x00, 0x01,
        0x00, 0x01,
        0x00, 0x00, 0x00, 0x78,
        0x00, 0x04,
        0x0A, 0x0A, 0x00, 0x01
    };
    uint16_t len = 0;
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    record = pico_dns_record_create(url, (void *)rdata, 4,
                                    &len, PICO_DNS_TYPE_A,
                                    PICO_DNS_CLASS_IN, 120);
    fail_if(!record, "dns_record_create failed!\n");

    ptr = buf + 20;

    /* Try to copy the record to a flat buffer */
    ret = pico_dns_record_copy_flat(record, &ptr);

    fail_unless(ret == 0, "dns_record_copy_flat returned error!\n");
    fail_unless(memcmp(buf + 20, cmp_buf, 27) == 0,
                "dns_record_copy_flat failed!\n");

    /* FREE memory */
    pico_dns_record_delete((void **)&record);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_record_copy) /* MARK: dns_record_copy */
{
    struct pico_dns_record *a = NULL, *b = NULL;
    const char *url = "picotcp.com";
    uint8_t rdata[4] = {
        10, 10, 0, 1
    };
    uint16_t len = 0;

    a = pico_dns_record_create(url, (void *)rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 120);
    fail_if(!a, "dns_record_create failed!\n");

    printf("*********************** starting %s * \n", __func__);

    /* Try to copy the first DNS record */
    b = pico_dns_record_copy(a);
    fail_unless(b != NULL, "dns_record_copy returned NULL!\n");
    fail_unless(a != b, "pointers point to same struct!\n");
    fail_unless(strcmp(a->rname, b->rname) == 0,
                "dns_record_copy failed copying names!\n");
    fail_unless(a->rsuffix->rtype == b->rsuffix->rtype,
                "dns_record_copy failed copying rtype!\n");
    fail_unless(a->rsuffix->rclass == b->rsuffix->rclass,
                "dns_record_copy failed copying rclass!\n");
    fail_unless(a->rsuffix->rttl == b->rsuffix->rttl,
                "dns_record_copy failed copying rttl!\n");
    fail_unless(a->rsuffix->rdlength == b->rsuffix->rdlength,
                "dns_record_copy failed copying rdlenth!\n");
    fail_unless(memcmp(a->rdata, b->rdata, short_be(b->rsuffix->rdlength)) == 0,
                "dns_record_copy failed copying rdata!\n");

    /* FREE memory */
    pico_dns_record_delete((void **)&a);
    pico_dns_record_delete((void **)&b);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_record_delete) /* MARK: dns_record_delete */
{
    struct pico_dns_record *a = NULL;
    const char *url = "picotcp.com";
    uint8_t rdata[4] = {
        10, 10, 0, 1
    };
    uint16_t len = 0;
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);


    a = pico_dns_record_create(url, (void *)rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 120);
    fail_if(!a, "dns_record_create failed!\n");

    /* Try to delete the created record */
    ret = pico_dns_record_delete((void **)&a);
    fail_unless(ret == 0, "pico_dns_record_delete returned NULL!\n");
    fail_unless(a == NULL, "pico_dns_record_delete failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_record_create) /* MARK: dns_record_create */
{
    struct pico_dns_record *a = NULL;
    const char *url = "picotcp.com";
    uint8_t rdata[4] = {
        10, 10, 0, 1
    };
    uint16_t len = 0;

    printf("*********************** starting %s * \n", __func__);

    a = pico_dns_record_create(url, (void *)rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 120);
    fail_if(!a, "dns_record_create returned NULL!\n");
    fail_unless(strcmp(a->rname, "\x7picotcp\x3com"),
                "dns_record_create didn't convert url %s properly!\n",
                a->rname);
    fail_unless(a->rsuffix->rtype == short_be(PICO_DNS_TYPE_A),
                "dns_record_create failed setting rtype!\n");
    fail_unless(a->rsuffix->rclass == short_be(PICO_DNS_CLASS_IN),
                "dns_record_create failed setting rclass!\n");
    fail_unless(a->rsuffix->rttl == long_be(120),
                "dns_record_create failed setting rttl!\n");
    fail_unless(a->rsuffix->rdlength == short_be(4),
                "dns_record_create failed setting rdlenth!\n");
    fail_unless(memcmp(a->rdata, rdata, 4) == 0,
                "dns_record_create failed setting rdata!\n");

    /* TODO: Test PTR records */

    pico_dns_record_delete((void **)&a);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_answer_create) /* MARK: dns_answer_create */
{
    pico_dns_packet *packet = NULL;
    PICO_DNS_RTREE_DECLARE(rtree);
    struct pico_dns_record *a = NULL, *b = NULL;
    const char *url = "picotcp.com";
    const char *url2 = "google.com";
    uint8_t rdata[4] = {
        10, 10, 0, 1
    };
    uint16_t len = 0;
    uint8_t buf[62] = {
        0x00u, 0x00u,
        0x85u, 0x00u,
        0x00u, 0x00u,
        0x00u, 0x02u,
        0x00u, 0x00u,
        0x00u, 0x00u,
        0x06u, 'g', 'o', 'o', 'g', 'l', 'e',
        0x03u, 'c', 'o', 'm',
        0x00u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x00u, 0x00u, 0x00u, 0x78u,
        0x00u, 0x04u,
        0x0Au, 0x0Au, 0x00u, 0x01u,
        0x07u, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0xc0u, 0x13u,
        0x00u, 0x01u,
        0x00u, 0x01u,
        0x00u, 0x00u, 0x00u, 0x78u,
        0x00u, 0x04u,
        0x0Au, 0x0Au, 0x00u, 0x01u
    };

    printf("*********************** starting %s * \n", __func__);

    a = pico_dns_record_create(url, (void *)rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 120);
    fail_if(!a, "dns_record_create returned NULL!\n");
    b = pico_dns_record_create(url2, (void *)rdata, 4, &len, PICO_DNS_TYPE_A,
                               PICO_DNS_CLASS_IN, 120);
    fail_if(!a, "dns_record_create returned NULL!\n");

    pico_tree_insert(&rtree, a);
    pico_tree_insert(&rtree, b);

    /* Try to create an answer packet */
    packet = pico_dns_answer_create(&rtree, NULL, NULL, &len);
    fail_if (packet == NULL, "dns_answer_create returned NULL!\n");
    fail_unless(0 == memcmp((void *)packet, (void *)buf, len),
                "dns_answer_create failed!\n");

    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_namelen_comp) /* MARK: dns_namelen_comp */
{
    char name[] = "\3www\4tass\2be\0";
    char name_comp[] = "\3www\4tass\2be\xc0\x02";  /* two bytes ofset from start of buf */
    unsigned int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    /* name without compression */
    ret = pico_dns_namelen_comp(name);
    fail_unless(ret == 12, "Namelength is wrong!\n");

    /* name with compression */
    ret = pico_dns_namelen_comp(name_comp);
    fail_unless(ret == 13, "Namelength is wrong!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_decompress_name) /* MARK: dns_decompress_name */
{
    char name[] = "\4mail\xc0\x02";
    char name2[] = "\xc0\x02";
    char buf[] = "00\6google\3com";
    char *ret;

    printf("*********************** starting %s * \n", __func__);

    /* Test normal DNS name compression */
    ret = pico_dns_decompress_name(name, (pico_dns_packet *)buf);

    /* Fail conditions */
    fail_unless(ret != NULL, "Name ptr returned is NULL");
    fail_unless(strcmp(ret, "\4mail\6google\3com") == 0, "Not correctly decompressed: '%s'!\n", ret);

    /* Free memory */
    PICO_FREE(ret);
    ret = NULL;

    /* Test when there is only a pointer */
    ret = pico_dns_decompress_name(name2, (pico_dns_packet *)buf);

    /* Fail conditions */
    fail_unless(ret != NULL, "Name ptr returned is NULL");
    fail_unless(strcmp(ret, "\6google\3com") == 0, "Not correctly decompressed: '%s'!\n", ret);

    /* Free memory */
    PICO_FREE(ret);
    ret = NULL;
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_url_get_reverse_len) /* MARK: dns_url_get_reverse_len */
{
    const char *url_ipv4 = "10.10.0.1";
    const char *url_ipv6 = "2001:0db8:0000:0000:0000:0000:0000:0000";
    uint16_t arpalen = 0;
    uint16_t len = 0;

    printf("*********************** starting %s * \n", __func__);

    /* Try to determine the reverse length of the IPv4 URL */
    len = pico_dns_url_get_reverse_len(url_ipv4, &arpalen, PICO_PROTO_IPV4);
    fail_unless(len == (9 + 2) && arpalen == 13,
                "dns_url_get_reverse_len failed with IPv4 URL!\n");

    /* Try to determine the reverse length of the IPv6 URL */
    len = pico_dns_url_get_reverse_len(url_ipv6, &arpalen, PICO_PROTO_IPV6);
    fail_unless(len == (63 + 2) && arpalen == 9,
                "dns_url_get_reverse_len failed with IPv4 URL!\n");

    len = pico_dns_url_get_reverse_len(NULL, NULL, PICO_PROTO_IPV4);
    fail_unless(len == 0, "dns_url_get_reverse_len with NULL-ptrs failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_url_to_reverse_qname) /* MARK: dns_url_to_reverse_qname */
{
    const char *url_ipv4 = "10.10.0.1";
    const char *url_ipv6 = "2001:0db8:0000:0000:0000:0000:0000:0000";
    char *qname = NULL;
    char cmp_buf1[24] = {
        0x01, '1',
        0x01, '0',
        0x02, '1', '0',
        0x02, '1', '0',
        0x07, 'i', 'n', '-', 'a', 'd', 'd', 'r',
        0x04, 'a', 'r', 'p', 'a',
        0x00
    };
    char cmp_buf[74] = {
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '0', 0x01u, '0', 0x01u, '0', 0x01u, '0',
        0x01u, '8', 0x01u, 'b', 0x01u, 'd', 0x01u, '0',
        0x01u, '1', 0x01u, '0', 0x01u, '0', 0x01u, '2',
        0x03u, 'I', 'P', '6',
        0x04u, 'A', 'R', 'P', 'A',
        0x00u
    };

    printf("*********************** starting %s * \n", __func__);

    /* Try to reverse IPv4 URL */
    qname = pico_dns_url_to_reverse_qname(url_ipv4, PICO_PROTO_IPV4);
    fail_unless(qname != NULL, "dns_url_to_reverse_qname returned NULL!\n");
    fail_unless(strcmp(qname, cmp_buf1) == 0,
                "dns_url_to_reverse_qname failed with IPv4 %s!\n", qname);
    PICO_FREE(qname);

    /* Try to reverse IPv6 URL */
    qname = pico_dns_url_to_reverse_qname(url_ipv6, PICO_PROTO_IPV6);
    fail_unless(qname != NULL, "dns_url_to_reverse_qname returned NULL!\n");
    fail_unless(strcmp(qname, cmp_buf) == 0,
                "dns_url_to_reverse_qname failed with IPv6!\n");
    PICO_FREE(qname);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_qname_to_url) /* MARK: dns_qname_to_url */
{
    char qname[24] = {
        0x01, '1',
        0x01, '0',
        0x02, '1', '0',
        0x02, '1', '0',
        0x07, 'i', 'n', '-', 'a', 'd', 'd', 'r',
        0x04, 'a', 'r', 'p', 'a',
        0x00
    };
    char qname2[13] = {
        0x07, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03, 'c', 'o', 'm',
        0x00
    };
    char qname3[14] = {
        0x08, 'p', 'i', 'c', 'o', '.', 't', 'c', 'p',
        0x03, 'c', 'o', 'm',
        0x00
    };
    char *url = NULL;

    printf("*********************** starting %s * \n", __func__);

    /* Try to convert qname to url */
    url = pico_dns_qname_to_url(qname);
    fail_unless(url != NULL, "dns_qname_to_url returned NULL!\n");
    fail_unless(strcmp(url, "1.0.10.10.in-addr.arpa") == 0,
                "dns_qname_to_url failed %s!\n", url);
    PICO_FREE(url);

    /* Try to convert qname2 to url */
    url = pico_dns_qname_to_url(qname2);
    fail_unless(url != NULL, "dns_qname_to_url returned NULL!\n");
    fail_unless(strcmp(url, "picotcp.com") == 0,
                "dns_qname_to_url failed %s!\n", url);
    PICO_FREE(url);

    /* Try to convert qname2 to url */
    url = pico_dns_qname_to_url(qname3);
    fail_unless(url != NULL, "dns_qname_to_url returned NULL!\n");
    fail_unless(strcmp(url, "pico.tcp.com") == 0,
                "dns_qname_to_url failed %s!\n", url);
    PICO_FREE(url);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_url_to_qname) /* MARK: dns_url_to_qname */
{
    char qname1[24] = {
        0x01, '1',
        0x01, '0',
        0x02, '1', '0',
        0x02, '1', '0',
        0x07, 'i', 'n', '-', 'a', 'd', 'd', 'r',
        0x04, 'a', 'r', 'p', 'a',
        0x00
    };
    char qname2[13] = {
        0x07, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03, 'c', 'o', 'm',
        0x00
    };
    char *qname = NULL;

    printf("*********************** starting %s * \n", __func__);

    /* Try to convert url to qname1 */
    qname = pico_dns_url_to_qname("1.0.10.10.in-addr.arpa");
    fail_unless(qname != NULL, "dns_url_to_qname returned NULL!\n");
    fail_unless(strcmp(qname, qname1) == 0,
                "dns_url_to_qname failed %s!\n", qname);
    PICO_FREE(qname);

    /* Try to convert url to qname2 */
    qname = pico_dns_url_to_qname("picotcp.com");
    fail_unless(qname != NULL, "dns_url_to_qname returned NULL!\n");
    fail_unless(strcmp(qname, qname2) == 0,
                "dns_url_to_qname failed %s!\n", qname);
    PICO_FREE(qname);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_name_to_dns_notation) /* MARK: dns_name_to_dns_notation */
{
    char qname1[13] = {
        0x07, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03, 'c', 'o', 'm',
        0x00
    };
    char url1[13] = {
        0, 'p', 'i', 'c', 'o', 't', 'c', 'p', '.', 'c', 'o', 'm', 0x00
    };
    char url2[13] = {
        'a', 'p', 'i', 'c', 'o', 't', 'c', 'p', '.', 'c', 'o', 'm', 0x00
    };
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    ret = pico_dns_name_to_dns_notation(url1, (uint16_t)strlen(url1));
    fail_unless(ret == -1, "dns_name_to_dns_notation didn't check correct!\n");

    ret = pico_dns_name_to_dns_notation(url2, (uint16_t)strlen(url2));
    fail_unless(ret == 0, "dns_name_to_dns_notation returned error!\n");
    fail_unless(strcmp(url2, qname1) == 0,
                "dns_name_to_dns_notation failed! %s\n", url2);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_notation_to_name) /* MARK: dns_notation_to_name */
{
    char qname1[13] = {
        0x07, 'p', 'i', 'c', 'o', 't', 'c', 'p',
        0x03, 'c', 'o', 'm',
        0x00
    };
    char url1[13] = {
        '.', 'p', 'i', 'c', 'o', 't', 'c', 'p', '.', 'c', 'o', 'm', 0x00
    };
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    ret = pico_dns_notation_to_name(qname1, (uint16_t)strlen(qname1));
    fail_unless(ret == 0, "dns_notation_to_name returned error!\n");
    fail_unless(strcmp(url1, qname1) == 0,
                "dns_notation_to_name failed! %s\n", qname1);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_mirror_addr) /* MARK: dns_mirror_addr */
{
    char url[12] = "192.168.0.1";
    int ret = 0;

    printf("*********************** starting %s * \n", __func__);

    ret = pico_dns_mirror_addr(url);
    fail_unless(ret == 0, "dns_mirror_addr returned error!\n");
    fail_unless(strcmp(url, "1.0.168.192") == 0,
                "dns_mirror_addr failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_dns_ptr_ip6_nibble_lo) /* MARK: dns_ptr_ip6_nibble_lo */
{
    uint8_t byte = 0x34;
    char nibble_lo = 0;

    printf("*********************** starting %s * \n", __func__);

    nibble_lo = dns_ptr_ip6_nibble_lo(byte);
    fail_unless(nibble_lo == '4', "dns_ptr_ip6_nibble_lo failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_dns_ptr_ip6_nibble_hi) /* MARK: dns_ptr_ip6_nibble_hi */
{
    uint8_t byte = 0x34;
    char nibble_hi = 0;

    printf("*********************** starting %s * \n", __func__);

    nibble_hi = dns_ptr_ip6_nibble_hi(byte);
    fail_unless(nibble_hi == '3', "dns_ptr_ip6_nibble_hi failed! '%c'\n",
                nibble_hi);
    printf("*********************** ending %s * \n", __func__);
}
END_TEST
START_TEST(tc_pico_dns_ipv6_set_ptr) /* MARK: dns_ipv6_set_ptr */
{
    const char *url_ipv6 = "2001:0db8:0000:0000:0000:0000:0000:0000";

    char cmpbuf[65] = {
        '0', '.', '0', '.', '0', '.', '0', '.',
        '0', '.', '0', '.', '0', '.', '0', '.',
        '0', '.', '0', '.', '0', '.', '0', '.',
        '0', '.', '0', '.', '0', '.', '0', '.',
        '0', '.', '0', '.', '0', '.', '0', '.',
        '0', '.', '0', '.', '0', '.', '0', '.',
        '8', '.', 'b', '.', 'd', '.', '0', '.',
        '1', '.', '0', '.', '0', '.', '2', '.', 0x00
    };
    char buf[65] = {};

    printf("*********************** starting %s * \n", __func__);

    pico_dns_ipv6_set_ptr(url_ipv6, buf);
    fail_unless(strcmp(buf, cmpbuf) == 0,
                "dns_ipv6_set_ptr failed!\n");
    printf("*********************** ending %s * \n", __func__);
}
END_TEST

Suite *pico_suite(void)
{
    Suite *s = suite_create("PicoTCP");

    TCase *TCase_dns_rdata_cmp = tcase_create("Unit test for dns_rdata_cmp");
    TCase *TCase_dns_question_cmp = tcase_create("Unit test for dns_question_cmp");
    TCase *TCase_dns_qtree_insert = tcase_create("Unit test for dns_qtree_insert");
    TCase *TCase_dns_record_cmp = tcase_create("Unit test for dns_record_cmp");
    TCase *TCase_dns_rtree_insert = tcase_create("Unit test for dns_rtree_insert");
    TCase *TCase_dns_record_cmp_name_type = tcase_create("Unit test for dns_record_cmp_name_type");

    /* DNS packet section filling */
    TCase *TCase_pico_dns_fill_packet_header = tcase_create("Unit test for 'pico_dns_fill_packet_header'");
    TCase *TCase_pico_dns_fill_packet_rr_sections = tcase_create("Unit test for 'pico_dns_fill_packet_rr_sections'");
    TCase *TCase_pico_dns_fill_packet_rr_section = tcase_create("Unit test for 'pico_dns_fill_packet_rr_section'");
    TCase *TCase_pico_dns_fill_packet_question_section = tcase_create("Unit test for 'pico_dns_fill_packet_question_sections'");

    /* DNS packet compression */
    TCase *TCase_pico_dns_packet_compress_find_ptr = tcase_create("Unit test for 'pico_dns_packet_compress_find_ptr'");
    TCase *TCase_pico_dns_packet_compress_name = tcase_create("Unit test for 'pico_dns_packet_compress_name'");
    TCase *TCase_pico_dns_packet_compress = tcase_create("Unit test for 'pico_dns_packet_compress'");

    /* DNS question functions */
    TCase *TCase_pico_dns_question_fill_qsuffix = tcase_create("Unit test for 'pico_dns_question_fill_qsuffix'");
    TCase *TCase_pico_dns_question_delete = tcase_create("Unit test for 'pico_dns_question_delete'");
    TCase *TCase_pico_dns_question_create = tcase_create("Unit test for 'pico_dns_question_create'");

    /* DNS query packet creation */
    TCase *TCase_pico_dns_query_create = tcase_create("Unit test for 'pico_dns_query_create'");

    /* DNS resource record functions */
    TCase *TCase_pico_dns_record_fill_suffix = tcase_create("Unit test for 'pico_dns_record_fill_suffix'");
    TCase *TCase_pico_dns_record_copy_flat = tcase_create("Unit test for 'pico_dns_record_copy_flat'");
    TCase *TCase_pico_dns_record_copy = tcase_create("Unit test for 'pico_dns_record_copy'");
    TCase *TCase_pico_dns_record_delete = tcase_create("Unit test for 'pico_dns_record_delete'");
    TCase *TCAse_pico_dns_record_create = tcase_create("Unit test for 'pico_dns_record_create'");

    /* DNS answer packet creation */
    TCase *TCase_pico_dns_answer_create = tcase_create("Unit test for 'pico_dns_answer_create'");

    /* Name conversion and compression function */
    TCase *TCase_pico_dns_namelen_comp = tcase_create("Unit test for 'pico_dns_namelen_comp'");
    TCase *TCase_pico_dns_decompress_name = tcase_create("Unit test for 'pico_dns_decompress_name'");
    TCase *TCase_pico_dns_url_get_reverse_len = tcase_create("Unit test for 'pico_dns_url_get_reverse_len'");
    TCase *TCase_pico_dns_url_to_reverse_qname = tcase_create("Unit test for 'pico_dns_url_to_reverse_qname'");
    TCase *TCase_pico_dns_qname_to_url = tcase_create("Unit test for 'pico_dns_qname_to_url'");
    TCase *TCase_pico_dns_url_to_qname = tcase_create("Unit test for 'pico_dns_url_to_qname'");
    TCase *TCase_pico_dns_name_to_dns_notation = tcase_create("Unit test for 'pico_dns_name_to_dns_notation'");
    TCase *TCase_pico_dns_notation_to_name = tcase_create("Unit test for 'pico_dns_notation_to_name'");
    TCase *TCase_pico_dns_mirror_addr = tcase_create("Unit test for 'pico_dns_mirror_addr'");
    TCase *TCase_dns_ptr_ip6_nibble_lo = tcase_create("Unit test for 'dns_ptr_ip6_nibble_lo'");
    TCase *TCase_dns_ptr_ip6_nibble_hi = tcase_create("Unit test for 'dns_ptr_ip6_nibble_hi'");
    TCase *TCase_pico_dns_ipv6_set_ptr = tcase_create("Unit test for 'pico_dns_ipv6_set_ptr'");

    tcase_add_test(TCase_dns_rdata_cmp, tc_dns_rdata_cmp);
    tcase_add_test(TCase_dns_question_cmp, tc_dns_question_cmp);
    tcase_add_test(TCase_dns_qtree_insert, tc_dns_qtree_insert);
    tcase_add_test(TCase_dns_record_cmp, tc_dns_record_cmp);
    tcase_add_test(TCase_dns_rtree_insert, tc_dns_rtree_insert);
    tcase_add_test(TCase_dns_record_cmp_name_type, tc_dns_record_cmp_name_type);
    tcase_add_test(TCase_pico_dns_fill_packet_header, tc_pico_dns_fill_packet_header);
    tcase_add_test(TCase_pico_dns_fill_packet_rr_section, tc_pico_dns_fill_packet_rr_section);
    tcase_add_test(TCase_pico_dns_fill_packet_rr_sections, tc_pico_dns_fill_packet_rr_sections);
    tcase_add_test(TCase_pico_dns_fill_packet_question_section, tc_pico_dns_fill_packet_question_section);
    tcase_add_test(TCase_pico_dns_packet_compress_find_ptr, tc_pico_dns_packet_compress_find_ptr);
    tcase_add_test(TCase_pico_dns_packet_compress_name, tc_pico_dns_packet_compress_name);
    tcase_add_test(TCase_pico_dns_packet_compress, tc_pico_dns_packet_compress);
    tcase_add_test(TCase_pico_dns_question_fill_qsuffix, tc_pico_dns_question_fill_qsuffix);
    tcase_add_test(TCase_pico_dns_question_delete, tc_pico_dns_question_delete);
    tcase_add_test(TCase_pico_dns_question_create, tc_pico_dns_question_create);
    tcase_add_test(TCase_pico_dns_query_create, tc_pico_dns_query_create);
    tcase_add_test(TCase_pico_dns_record_fill_suffix, tc_pico_dns_record_fill_suffix);
    tcase_add_test(TCase_pico_dns_record_copy_flat, tc_pico_dns_record_copy_flat);
    tcase_add_test(TCase_pico_dns_record_copy, tc_pico_dns_record_copy);
    tcase_add_test(TCase_pico_dns_record_delete, tc_pico_dns_record_delete);
    tcase_add_test(TCAse_pico_dns_record_create, tc_pico_dns_record_create);
    tcase_add_test(TCase_pico_dns_answer_create, tc_pico_dns_answer_create);
    tcase_add_test(TCase_pico_dns_namelen_comp, tc_pico_dns_namelen_comp);
    tcase_add_test(TCase_pico_dns_decompress_name, tc_pico_dns_decompress_name);
    tcase_add_test(TCase_pico_dns_url_get_reverse_len, tc_pico_dns_url_get_reverse_len);
    tcase_add_test(TCase_pico_dns_url_to_reverse_qname, tc_pico_dns_url_to_reverse_qname);
    tcase_add_test(TCase_pico_dns_qname_to_url, tc_pico_dns_qname_to_url);
    tcase_add_test(TCase_pico_dns_url_to_qname, tc_pico_dns_url_to_qname);
    tcase_add_test(TCase_pico_dns_name_to_dns_notation, tc_pico_dns_name_to_dns_notation);
    tcase_add_test(TCase_pico_dns_notation_to_name, tc_pico_dns_notation_to_name);
    tcase_add_test(TCase_pico_dns_mirror_addr, tc_pico_dns_mirror_addr);
    tcase_add_test(TCase_dns_ptr_ip6_nibble_lo, tc_dns_ptr_ip6_nibble_lo);
    tcase_add_test(TCase_dns_ptr_ip6_nibble_hi, tc_dns_ptr_ip6_nibble_hi);
    tcase_add_test(TCase_pico_dns_ipv6_set_ptr, tc_pico_dns_ipv6_set_ptr);

    suite_add_tcase(s, TCase_dns_rdata_cmp);
    suite_add_tcase(s, TCase_dns_question_cmp);
    suite_add_tcase(s, TCase_dns_qtree_insert);
    suite_add_tcase(s, TCase_dns_record_cmp);
    suite_add_tcase(s, TCase_dns_rtree_insert);
    suite_add_tcase(s, TCase_dns_record_cmp_name_type);
    suite_add_tcase(s, TCase_pico_dns_fill_packet_header);
    suite_add_tcase(s, TCase_pico_dns_fill_packet_rr_section);
    suite_add_tcase(s, TCase_pico_dns_fill_packet_rr_sections);
    suite_add_tcase(s, TCase_pico_dns_fill_packet_question_section);
    suite_add_tcase(s, TCase_pico_dns_packet_compress_find_ptr);
    suite_add_tcase(s, TCase_pico_dns_packet_compress_name);
    suite_add_tcase(s, TCase_pico_dns_packet_compress);
    suite_add_tcase(s, TCase_pico_dns_question_fill_qsuffix);
    suite_add_tcase(s, TCase_pico_dns_question_delete);
    suite_add_tcase(s, TCase_pico_dns_question_create);
    suite_add_tcase(s, TCase_pico_dns_query_create);
    suite_add_tcase(s, TCase_pico_dns_record_fill_suffix);
    suite_add_tcase(s, TCase_pico_dns_record_copy);
    suite_add_tcase(s, TCase_pico_dns_record_delete);
    suite_add_tcase(s, TCAse_pico_dns_record_create);
    suite_add_tcase(s, TCase_pico_dns_answer_create);
    suite_add_tcase(s, TCase_pico_dns_namelen_comp);
    suite_add_tcase(s, TCase_pico_dns_decompress_name);
    suite_add_tcase(s, TCase_pico_dns_url_get_reverse_len);
    suite_add_tcase(s, TCase_pico_dns_url_to_reverse_qname);
    suite_add_tcase(s, TCase_pico_dns_qname_to_url);
    suite_add_tcase(s, TCase_pico_dns_url_to_qname);
    suite_add_tcase(s, TCase_pico_dns_name_to_dns_notation);
    suite_add_tcase(s, TCase_pico_dns_notation_to_name);
    suite_add_tcase(s, TCase_pico_dns_mirror_addr);
    suite_add_tcase(s, TCase_dns_ptr_ip6_nibble_lo);
    suite_add_tcase(s, TCase_dns_ptr_ip6_nibble_hi);
    suite_add_tcase(s, TCase_pico_dns_ipv6_set_ptr);

    return s;
}

int main(void)
{
    int fails;
    Suite *s = pico_suite();
    SRunner *sr = srunner_create(s);
    srunner_run_all(sr, CK_NORMAL);
    fails = srunner_ntests_failed(sr);
    srunner_free(sr);
    return fails;
}

