(file) Return to test_base.cpp CVS log (file) (dir) Up to [OMI] / omi / base / tests

File: [OMI] / omi / base / tests / Attic / test_base.cpp (download)
Revision: 1.1.1.1 (vendor branch), Wed May 30 21:47:49 2012 UTC (12 years, 1 month ago) by mike
Branch: TOG
CVS Tags: OMI_1_0_2_Branch, OMI_1_0_1_PRE, OMI_1_0_0
Changes since 1.1: +0 -0 lines
Initial Import

/*
**==============================================================================
**
** Open Management Infrastructure (OMI)
**
** Copyright (c) Microsoft Corporation
** 
** Licensed under the Apache License, Version 2.0 (the "License"); you may not 
** use this file except in compliance with the License. You may obtain a copy 
** of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
** MERCHANTABLITY OR NON-INFRINGEMENT. 
**
** See the Apache 2 License for the specific language governing permissions 
** and limitations under the License.
**
**==============================================================================
*/

#include <vector>
#include <algorithm>
#include <ut/ut.h>
#include <base/base.h>
#include <base/buf.h>
#include <base/batch.h>
#include <base/strings.h>
#include <base/paths.h>
#include <base/conf.h>
#include <base/dir.h>
#include <base/log.h>
#include <base/io.h>
#include <base/strarr.h>
#include <base/getopt.h>
#include <base/hashtable.h>
#include "MSFT_AllTypes.h"
#include "MSFT_Process.h"

using namespace std;
using namespace mi;

//#define ENABLE_PRINT

#if defined(_MSC_VER)
/* warning C6309: Argument '3' is null: this does not adhere to function specification of 'MI_Instance_AddElement' */
# pragma warning(disable : 6309)
#endif

extern "C" 
{
extern MI_SchemaDecl test_repos_classDecl;
}

static FILE* outStream = NULL;

static string prefix;

static void setUp()
{
#if defined(ENABLE_PRINT)
    outStream = stdout;
#else
    outStream = Fopen(NULL_FILE, "w");
#endif
    prefix = GetPath(ID_PREFIX);
}

static void cleanup()
{
#if defined(ENABLE_PRINT)
    outStream = NULL;
#else
    fclose(outStream);
    outStream = NULL;
#endif
}

static void MemTest(const char* data, char ch, size_t size)
{
    size_t i;

    for (i = 0; i < size; i++)
    {
        if (data[i] != ch)
            UT_ASSERT_FAILED_MSG("Memcheck failed");
    }
}

static void TestAllocator1()
{
    Batch a;
    char data[256];
    // make this member static, since pprefast does not like stack usage more 
    // than 16K
    static char* blocks[4096];
    const size_t N = sizeof(blocks) / sizeof(blocks[0]);
    size_t i;

    Batch_InitFromBuffer(&a, data, sizeof(data), BATCH_MAX_PAGES);

    for (i = 1; i < N; i++)
    {
        char* p = (char*)Batch_Get(&a, i);
        UT_ASSERT(p != NULL);

        memset(p, i % 256, i);
        blocks[i] = p;
    }

    // First chunk is 1 bytes as offset 0:
    UT_ASSERT(data[0] == 1);

    // Second chunk is 2 bytes as offset 8:
    UT_ASSERT(data[8+0] == 2);
    UT_ASSERT(data[8+1] == 2);

    // Third chunk is 3 bytes as offset 16:
    UT_ASSERT(data[16+0] == 3);
    UT_ASSERT(data[16+1] == 3);

    // Fourth chunk is 4 bytes as offset 24:
    UT_ASSERT(data[24+0] == 4);
    UT_ASSERT(data[24+1] == 4);

    // Fifth chunk is 5 bytes as offset 32:
    UT_ASSERT(data[32+0] == 5);
    UT_ASSERT(data[32+1] == 5);

    for (i = 1; i < N; i++)
    {
        char* p = blocks[i];
        UT_ASSERT(p != NULL);

        MemTest(p, i % 256, i);
    }

    Batch_Destroy(&a);
}

static void TestAllocator2()
{
    Batch batch = BATCH_INITIALIZER;
    char* blocks[512];
    const size_t N = sizeof(blocks) / sizeof(blocks[0]);
    size_t i;

    for (i = 1; i < N; i++)
    {
        char* p = (char*)Batch_Get(&batch, i);
        UT_ASSERT(p != NULL);

        memset(p, i % 256, i);
        blocks[i] = p;
    }

    for (i = 1; i < N; i++)
    {
        char* p = blocks[i];
        UT_ASSERT(p != NULL);

        MemTest(p, i % 256, i);
    }

    Batch_Destroy(&batch);
    memset(&batch, 0, sizeof(batch));
}

static void TestAllocator3()
{
    Batch* batch;
    char* blocks[512];
    const size_t N = sizeof(blocks) / sizeof(blocks[0]);
    size_t i;

    batch = Batch_New(8);
    UT_ASSERT(batch != NULL);

    for (i = 1; i < N; i++)
    {
        char* p = (char*)Batch_Get(batch, i);
        UT_ASSERT(p != NULL);

        memset(p, i % 256, i);
        blocks[i] = p;
    }

    for (i = 1; i < N; i++)
    {
        char* p = blocks[i];
        UT_ASSERT(p != NULL);

        MemTest(p, i % 256, i);
    }

    Batch_Delete(batch);
}

static void TestGetName()
{
    UT_ASSERT(strcmp(Type_NameOf(MI_BOOLEAN), "BOOLEAN") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_UINT8), "UINT8") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_SINT8), "SINT8") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_UINT16), "UINT16") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_SINT16), "SINT16") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_UINT32), "UINT32") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_SINT32), "SINT32") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_UINT64), "UINT64") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_SINT64), "SINT64") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_REAL32), "REAL32") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_REAL64), "REAL64") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_CHAR16), "CHAR16") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_DATETIME), "DATETIME") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_STRING), "STRING") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_REFERENCE), "REFERENCE") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_INSTANCE), "INSTANCE") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_BOOLEANA), "BOOLEANA") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_UINT8A), "UINT8A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_SINT8A), "SINT8A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_UINT16A), "UINT16A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_SINT16A), "SINT16A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_UINT32A), "UINT32A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_SINT32A), "SINT32A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_UINT64A), "UINT64A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_SINT64A), "SINT64A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_REAL32A), "REAL32A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_REAL64A), "REAL64A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_CHAR16A), "CHAR16A") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_DATETIMEA), "DATETIMEA") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_STRINGA), "STRINGA") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_REFERENCEA), "REFERENCEA") == 0);
    UT_ASSERT(strcmp(Type_NameOf(MI_INSTANCEA), "INSTANCEA") == 0);
}

static void TestGetSize()
{
    UT_ASSERT(Type_SizeOf(MI_BOOLEAN) == sizeof(MI_Boolean));
    UT_ASSERT(Type_SizeOf(MI_UINT8) == sizeof(MI_Uint8));
    UT_ASSERT(Type_SizeOf(MI_SINT8) == sizeof(MI_Sint8));
    UT_ASSERT(Type_SizeOf(MI_UINT16) == sizeof(MI_Uint16));
    UT_ASSERT(Type_SizeOf(MI_SINT16) == sizeof(MI_Sint16));
    UT_ASSERT(Type_SizeOf(MI_UINT32) == sizeof(MI_Uint32));
    UT_ASSERT(Type_SizeOf(MI_SINT32) == sizeof(MI_Sint32));
    UT_ASSERT(Type_SizeOf(MI_UINT64) == sizeof(MI_Uint64));
    UT_ASSERT(Type_SizeOf(MI_SINT64) == sizeof(MI_Sint64));
    UT_ASSERT(Type_SizeOf(MI_REAL32) == sizeof(MI_Real32));
    UT_ASSERT(Type_SizeOf(MI_REAL64) == sizeof(MI_Real64));
    UT_ASSERT(Type_SizeOf(MI_CHAR16) == sizeof(MI_Char16));
    UT_ASSERT(Type_SizeOf(MI_DATETIME) == sizeof(MI_Datetime));
    UT_ASSERT(Type_SizeOf(MI_STRING) == sizeof(MI_String));
    UT_ASSERT(Type_SizeOf(MI_REFERENCE) == sizeof(MI_Instance*));
    UT_ASSERT(Type_SizeOf(MI_INSTANCE) == sizeof(MI_Instance*));
    UT_ASSERT(Type_SizeOf(MI_BOOLEANA) == sizeof(MI_BooleanA));
    UT_ASSERT(Type_SizeOf(MI_UINT8A) == sizeof(MI_Uint8A));
    UT_ASSERT(Type_SizeOf(MI_SINT8A) == sizeof(MI_Sint8A));
    UT_ASSERT(Type_SizeOf(MI_UINT16A) == sizeof(MI_Uint16A));
    UT_ASSERT(Type_SizeOf(MI_SINT16A) == sizeof(MI_Sint16A));
    UT_ASSERT(Type_SizeOf(MI_UINT32A) == sizeof(MI_Uint32A));
    UT_ASSERT(Type_SizeOf(MI_SINT32A) == sizeof(MI_Sint32A));
    UT_ASSERT(Type_SizeOf(MI_UINT64A) == sizeof(MI_Uint64A));
    UT_ASSERT(Type_SizeOf(MI_SINT64A) == sizeof(MI_Sint64A));
    UT_ASSERT(Type_SizeOf(MI_REAL32A) == sizeof(MI_Real32A));
    UT_ASSERT(Type_SizeOf(MI_REAL64A) == sizeof(MI_Real64A));
    UT_ASSERT(Type_SizeOf(MI_CHAR16A) == sizeof(MI_Char16A));
    UT_ASSERT(Type_SizeOf(MI_DATETIMEA) == sizeof(MI_DatetimeA));
    UT_ASSERT(Type_SizeOf(MI_STRINGA) == sizeof(MI_StringA));
    UT_ASSERT(Type_SizeOf(MI_REFERENCEA) == sizeof(MI_ReferenceA));
    UT_ASSERT(Type_SizeOf(MI_INSTANCEA) == sizeof(MI_InstanceA));
}

static MI_Instance* NewPerson(
    MI_Uint32 key,
    const MI_Char* first,
    const MI_Char* last,
    Batch* batch)
{
    MI_Value value;
    MI_Instance* inst;
    MI_Result r;
    const MI_ClassDecl* cd;
    
    /* Find class decl for MSFT_Person */
    cd = SchemaDecl_FindClassDecl(&test_repos_classDecl, T("MSFT_Person"));
    UT_ASSERT(cd != NULL);

    /* Create instance of MSFT_Person */
    r = Instance_New(&inst, cd, batch);
    UT_ASSERT(r == MI_RESULT_OK);

    /* CIM_Person.Key */
    value.uint32 = key;
    r = MI_Instance_SetElement(inst, MI_T("Key"), &value, MI_UINT32, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    /* CIM_Person.First */
    value.string = (MI_Char*)first;
    r = MI_Instance_SetElement(inst, MI_T("First"), &value, MI_STRING, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    /* CIM_Person.Last */
    value.string = (MI_Char*)last;
    r = MI_Instance_SetElement(inst, MI_T("Last"), &value, MI_STRING, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    return inst;
}

static void TestStringArray1(void)
{
    Batch batch = BATCH_INITIALIZER;
    StringArray sa;
    MI_Result r;
    static const MI_Char* data[] = 
    {
        MI_T("Monday"), 
        MI_T("Tuesday"),
        MI_T("Wednesday"),
        MI_T("Thursday"),
        MI_T("Friday"),
        MI_T("Saturday"),
        MI_T("Sunday"),
    };
    static const size_t size = MI_COUNT(data);
    size_t i;

    StringArray_Init(&sa, size, &batch);

    for (i = 0; i < size; i++)
    {
        r = StringArray_Add(&sa, data[i]);
        UT_ASSERT(r == MI_RESULT_OK);
    }

    UT_ASSERT(sa.size == size);
    r = StringArray_Add(&sa, MI_T("Never"));
    UT_ASSERT(r == MI_RESULT_FAILED);

#if 0
    StringArray_Print(&sa, stdout);
#endif

    for (i = 0; i < size; i++)
    {
        UT_ASSERT(Zcasecmp(sa.data[i], data[i]) == 0);
    }

    Batch_Destroy(&batch);
}

static void TestStringArray2(void)
{
    Batch batch = BATCH_INITIALIZER;
    StringArray sa;
    static const size_t N = 12345;
    size_t i;

    StringArray_Init(&sa, N, &batch);

    for (i = 0; i < N; i++)
    {
        MI_Result r;
        MI_Char buf[32];
        Szprintf(buf, MI_COUNT(buf), MI_T("%d"), (int)i);
        r = StringArray_Add(&sa, buf);
        UT_ASSERT(r == MI_RESULT_OK);
    }

    UT_ASSERT(sa.size == N);

    for (i = 0; i < N; i++)
    {
        MI_Char buf[32];
        Szprintf(buf, MI_COUNT(buf), MI_T("%d"), (int)i);
        Zcasecmp(sa.data[i], buf);
    }

    Batch_Destroy(&batch);
}

void TestMessages()
{
    MI_Result r;
    MI_Instance* instanceName;
    GetInstanceReq* req;
    PostInstanceMsg* rsp;
    MI_Value value;
    MI_Uint64 msgID;
    StringArray propertySet;
    const MI_ClassDecl* cd;
    Batch* b = NULL;

    /* Set the msgID */
    msgID = 12345;

    /* Build GetInstance message */
    req = GetInstanceReq_New(msgID, BinaryProtocolFlag);
    UT_ASSERT(req);

    /* Use the message's batch for all allocations */
    b = req->base.batch;
    UT_ASSERT(b);

    /* Create an instance name */
    {
        cd = SchemaDecl_FindClassDecl(&test_repos_classDecl, 
            T("MSFT_Person"));
        UT_ASSERT(cd != NULL);

        r = Instance_New(&instanceName, cd, b);
        UT_ASSERT(r == MI_RESULT_OK);

        value.string = (MI_Char*)MI_T("CIM_Person");
        MI_Instance_SetElement(instanceName, MI_T("__CLASS"), &value, MI_STRING, 0);

        value.uint32 = 1234;
        MI_Instance_SetElement(instanceName, MI_T("Key"), &value, MI_UINT32, 0);
    }

    /* Build a property list */
    {
        StringArray_Init(&propertySet, 2, b);
        r = StringArray_Add(&propertySet, MI_T("First"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = StringArray_Add(&propertySet, MI_T("Last"));
        UT_ASSERT(r == MI_RESULT_OK);
    }

    req->instanceName = instanceName;
    req->propertySet = &propertySet;

    GetInstanceReq_Print(req, outStream);

    /* Create response */
    rsp = PostInstanceMsg_New(msgID);
    UT_ASSERT(rsp);

    rsp->instance = NewPerson(1, MI_T("George"), MI_T("Washington"), b);
    UT_ASSERT(rsp);

    PostInstanceMsg_Print(rsp, outStream);

    /* Destroy instances */
    MI_Instance_Delete(instanceName);
    MI_Instance_Delete(rsp->instance);

    /* Destroy the messages */
    GetInstanceReq_Release(req);
    PostInstanceMsg_Release(rsp);
}

void TestMessages2()
{
    EnumerateInstancesReq* req;
    MI_Uint64 msgID;
    StringArray propertySet;
    Batch* b = NULL;

    /* Set the msgID */
    msgID = 45678;

    /* EnumerateInstancesReq */
    req = EnumerateInstancesReq_New(msgID, BinaryProtocolFlag);
    UT_ASSERT(req);

    /* Use the messages's batch for all allocations */
    b = req->base.batch;

    /* EnumerateInstancesReq.className */
    req->className = (MI_Char*)Batch_Zdup(b, MI_T("CIM_Person"));

    /* EnumerateInstancesReq.deepInheritance */
    req->deepInheritance = MI_TRUE;

    /* EnumerateInstancesReq.propertySet */
    {
        MI_Result r;
        StringArray_Init(&propertySet, 3, b);
        r = StringArray_Add(&propertySet, MI_T("Key"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = StringArray_Add(&propertySet, MI_T("First"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = StringArray_Add(&propertySet, MI_T("Last"));
        UT_ASSERT(r == MI_RESULT_OK);
        req->propertySet = &propertySet;
    }

    EnumerateInstancesReq_Print(req, outStream);

    /* Destroy the message */
    EnumerateInstancesReq_Release(req);
}

static void TestStrings()
{
    char buf[1024];
    size_t n;

    memset(buf, 0xFF, sizeof(buf));
    n = Strlcpy(buf, "abc", 8);
    UT_ASSERT(strcmp(buf, "abc") == 0);
    UT_ASSERT(n == 3);

    memset(buf, 0xFF, sizeof(buf));
    n = Strlcpy(buf, "abc", 3);
    UT_ASSERT(strcmp(buf, "ab") == 0);
    UT_ASSERT(n == 3);

    memset(buf, 0xFF, sizeof(buf));
    n = Strlcpy(buf, "", 1);
    UT_ASSERT(strcmp(buf, "") == 0);
    UT_ASSERT(n == 0);

    memset(buf, 0xFF, sizeof(buf));
    n = Strlcpy(buf, "a", 2);
    UT_ASSERT(strcmp(buf, "a") == 0);
    UT_ASSERT(n == 1);

    memset(buf, 0xFF, sizeof(buf));
    n = Strlcpy(buf, "aaa", 8);
    n = Strlcat(buf, "bbb", 8);
    n = Strlcat(buf, "ccc", 8);
    UT_ASSERT(strcmp(buf, "aaabbbc") == 0);
    UT_ASSERT(n == 3);

    n = Strlcat(buf, "ccddd", 10);
    UT_ASSERT(strcmp(buf, "aaabbbccc") == 0);
    UT_ASSERT(n == 5);
}

typedef struct _Elem
{
    struct _Elem* next;
    struct _Elem* prev;
    int num;
}
Elem;

static void TestList()
{
    Elem* head = NULL;
    Elem* tail = NULL;
    Elem* p;

    /* Insert some elements */
    {
        Elem* e = (Elem*)malloc(sizeof(Elem));
        UT_ASSERT(e != NULL);
        e->num = 1;
        List_Prepend(
            (ListElem**)&head, (ListElem**)&tail, (ListElem*)e);
    }
    {
        Elem* e = (Elem*)malloc(sizeof(Elem));
        UT_ASSERT(e != NULL);
        e->num = 2;
        List_Prepend(
            (ListElem**)&head, (ListElem**)&tail, (ListElem*)e);
    }
    {
        Elem* e = (Elem*)malloc(sizeof(Elem));
        UT_ASSERT(e != NULL);
        e->num = 3;
        List_Append(
            (ListElem**)&head, (ListElem**)&tail, (ListElem*)e);
    }

    /* Test contents */
    {
        int i;

        for (i = 0, p = head; p; p = p->next, i++)
        {
            switch (i)
            {
                case 0:
                    UT_ASSERT(p->num == 2);
                    break;
                case 1:
                    UT_ASSERT(p->num == 1);
                    break;
                case 2:
                    UT_ASSERT(p->num == 3);
                    break;
                default:
                    UT_ASSERT_FAILED_MSG("unexpected");
            }
        }
    }

    /* Remove head */
    {
        Elem* tmp = head;
        List_Remove(
            (ListElem**)&head, (ListElem**)&tail, (ListElem*)head);
        free(tmp);
    }

    /* Test contents */
    {
        int i;

        for (i = 0, p = head; p; p = p->next, i++)
        {
            switch (i)
            {
                case 0:
                    UT_ASSERT(p->num == 1);
                    break;
                case 1:
                    UT_ASSERT(p->num == 3);
                    break;
                default:
                    UT_ASSERT_FAILED_MSG("unexpected");
            }
        }
    }

    /* Remove tail */
    {
        Elem* tmp = tail;
        List_Remove(
            (ListElem**)&head, (ListElem**)&tail, (ListElem*)tail);
        free(tmp);
    }

    /* Test contents */
    {
        int i;

        for (i = 0, p = head; p; p = p->next, i++)
        {
            switch (i)
            {
                case 0:
                    UT_ASSERT(p->num == 1);
                    break;
                default:
                    UT_ASSERT_FAILED_MSG("unexpected");
            }
        }
    }

    UT_ASSERT(head == tail);

    /* Remove final element */
    {
        Elem* tmp = head;
        List_Remove(
            (ListElem**)&head, (ListElem**)&tail, (ListElem*)head);
        free(tmp);
    }

    UT_ASSERT(head == tail);
    UT_ASSERT(head == NULL);
    UT_ASSERT(tail == NULL);
}

static void TestFindClassDecl()
{
    MI_ClassDecl* cd;
    MI_MethodDecl* md;

    cd = SchemaDecl_FindClassDecl(&test_repos_classDecl, T("MSFT_Person"));
    UT_ASSERT(cd != NULL);

    md = ClassDecl_FindMethodDecl(cd, T("NoSuchMethod"));
    UT_ASSERT(md == NULL);
    cd = SchemaDecl_FindClassDecl(&test_repos_classDecl, T("NoSuchClass"));
    UT_ASSERT(cd == NULL);

    md = ClassDecl_FindMethodDecl(cd, T("NoSuchMethod"));
    UT_ASSERT(md == NULL);

    md = ClassDecl_FindMethodDecl(0, 0);
    UT_ASSERT(md == NULL);
}

static MI_Result AddString(
    MI_Instance* inst, 
    const MI_Char* name, 
    const MI_Char* str)
{
    MI_Value value;
    value.string = (MI_Char*)str;

    return MI_Instance_AddElement(
        inst,
        name,
        &value,
        MI_STRING,
        0);
}

static MI_Instance* NewAllTypes(Batch* batch)
{
    MI_Instance* base;
    MSFT_AllTypes* inst;
    MI_ClassDecl* cd;
    MI_Result r;

    cd = SchemaDecl_FindClassDecl(
        &test_repos_classDecl, T("MSFT_AllTypes"));
    UT_ASSERT(cd != NULL);

    r = Instance_New(&base, cd, batch);
    UT_ASSERT(r == MI_RESULT_OK);
    inst = (MSFT_AllTypes*)base;

    /* Key*/
#if 0
    r = Instance_SetElementFromString(base, T("Key"), T("1234"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Key.exists == 1);
    UT_ASSERT(inst->Key.value == 1234);
#endif

    /* BooleanValue */
    r = Instance_SetElementFromString(base, T("BooleanValue"), T("TRUE"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->BooleanValue.exists == 1);
    UT_ASSERT(inst->BooleanValue.value == 1);

    /* BooleanValue */
    r = Instance_SetElementFromString(base, T("BooleanValue"), T("FALSE"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->BooleanValue.exists == 1);
    UT_ASSERT(inst->BooleanValue.value == 0);

    /* Uint8Value */
    r = Instance_SetElementFromString(base, T("Uint8Value"), T("8"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Uint8Value.exists == 1);
    UT_ASSERT(inst->Uint8Value.value == 8);

    /* Sint8Value */
    r = Instance_SetElementFromString(base, T("Sint8Value"), T("-8"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Sint8Value.exists == 1);
    UT_ASSERT(inst->Sint8Value.value == -8);

    /* Uint16Value */
    r = Instance_SetElementFromString(base, T("Uint16Value"), T("16"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Uint16Value.exists == 1);
    UT_ASSERT(inst->Uint16Value.value == 16);

    /* Sint16Value */
    r = Instance_SetElementFromString(base, T("Sint16Value"), T("-16"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Sint16Value.exists == 1);
    UT_ASSERT(inst->Sint16Value.value == -16);

    /* Uint32Value */
    r = Instance_SetElementFromString(base, T("Uint32Value"), T("32"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Uint32Value.exists == 1);
    UT_ASSERT(inst->Uint32Value.value == 32);

    /* Sint32Value */
    r = Instance_SetElementFromString(base, T("Sint32Value"), T("-32"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Sint32Value.exists == 1);
    UT_ASSERT(inst->Sint32Value.value == -32);

    /* Uint64Value */
    r = Instance_SetElementFromString(base, T("Uint64Value"), T("64"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Uint64Value.exists == 1);
    UT_ASSERT(inst->Uint64Value.value == 64);

    /* Sint64Value */
    r = Instance_SetElementFromString(base, T("Sint64Value"), T("-64"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Sint64Value.exists == 1);
    UT_ASSERT(inst->Sint64Value.value == -64);

    /* Real32Value */
    r = Instance_SetElementFromString(base, T("Real32Value"), T("32.32"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Real32Value.exists == 1);
    UT_ASSERT(inst->Real32Value.value > 32 && inst->Real32Value.value < 33);

    /* Real64Value */
    r = Instance_SetElementFromString(base, T("Real64Value"), T("64.64"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Real64Value.exists == 1);
    UT_ASSERT(inst->Real64Value.value > 64 && inst->Real64Value.value < 65);

    /* DatetimeValue */
    r = Instance_SetElementFromString(base, T("TimestampValue"), 
        T("20051225120000.123456-360"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->TimestampValue.exists == 1);

    /* Char16Value */
    r = Instance_SetElementFromString(base, T("Char16Value"), T("1234"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Char16Value.exists == 1);
    UT_ASSERT(inst->Char16Value.value == 1234);

    /* DatetimeValue */
    r = Instance_SetElementFromString(base, T("IntervalValue"), 
        T("12345678010101.123456:000"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->IntervalValue.exists == 1);

    /* StringValue */
    r = Instance_SetElementFromString(base, T("StringValue"), T("Hello"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->StringValue.exists == 1);
    UT_ASSERT(Zcmp(inst->StringValue.value, T("Hello")) == 0);

    /* Uint32Array */
    {
        MI_Uint32 data[] = { 10, 20, 30 };
        MI_Value value;
        value.uint32a.data = data;
        value.uint32a.size = MI_COUNT(data);
        r = MI_Instance_SetElement(base, T("Uint32Array"), &value, MI_UINT32A, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(inst->Uint32Array.value.data[0] == 10);
        UT_ASSERT(inst->Uint32Array.value.data[1] == 20);
        UT_ASSERT(inst->Uint32Array.value.data[2] == 30);
    }

    /* StringArray */
    {
        MI_Char* data[] = { (MI_Char*)T("RED"), (MI_Char*)T("GREEN"), (MI_Char*)T("BLUE") };
        MI_Value value;
        value.stringa.data = data;
        value.stringa.size = MI_COUNT(data);
        r = MI_Instance_SetElement(base, T("StringArray"), &value, MI_STRINGA, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(Zcmp(inst->StringArray.value.data[0], T("RED")) == 0);
        UT_ASSERT(Zcmp(inst->StringArray.value.data[1], T("GREEN")) == 0);
        UT_ASSERT(Zcmp(inst->StringArray.value.data[2], T("BLUE")) == 0);
    }

    return base;
}

static void TestAllTypes()
{
    MI_Instance* base;
    MSFT_AllTypes* inst;
    MI_ClassDecl* cd;
    MI_Result r;
    FILE* os;
    Batch batch = BATCH_INITIALIZER;

    cd = SchemaDecl_FindClassDecl(
        &test_repos_classDecl, T("MSFT_AllTypes"));
    UT_ASSERT(cd != NULL);

    r = Instance_New(&base, cd, &batch);
    UT_ASSERT(r == MI_RESULT_OK);
    inst = (MSFT_AllTypes*)base;

    /* BooleanValue */
    r = Instance_SetElementFromString(base, T("BooleanValue"), T("TRUE"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->BooleanValue.exists == 1);
    UT_ASSERT(inst->BooleanValue.value == 1);

    /* BooleanValue */
    r = Instance_SetElementFromString(base, T("BooleanValue"), T("FALSE"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->BooleanValue.exists == 1);
    UT_ASSERT(inst->BooleanValue.value == 0);

    /* Uint8Value */
    r = Instance_SetElementFromString(base, T("Uint8Value"), T("8"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Uint8Value.exists == 1);
    UT_ASSERT(inst->Uint8Value.value == 8);

    /* Sint8Value */
    r = Instance_SetElementFromString(base, T("Sint8Value"), T("-8"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Sint8Value.exists == 1);
    UT_ASSERT(inst->Sint8Value.value == -8);

    /* Uint16Value */
    r = Instance_SetElementFromString(base, T("Uint16Value"), T("16"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Uint16Value.exists == 1);
    UT_ASSERT(inst->Uint16Value.value == 16);

    /* Sint16Value */
    r = Instance_SetElementFromString(base, T("Sint16Value"), T("-16"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Sint16Value.exists == 1);
    UT_ASSERT(inst->Sint16Value.value == -16);

    /* Uint32Value */
    r = Instance_SetElementFromString(base, T("Uint32Value"), T("32"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Uint32Value.exists == 1);
    UT_ASSERT(inst->Uint32Value.value == 32);

    /* Sint32Value */
    r = Instance_SetElementFromString(base, T("Sint32Value"), T("-32"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Sint32Value.exists == 1);
    UT_ASSERT(inst->Sint32Value.value == -32);

    /* Uint64Value */
    r = Instance_SetElementFromString(base, T("Uint64Value"), T("64"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Uint64Value.exists == 1);
    UT_ASSERT(inst->Uint64Value.value == 64);

    /* Sint64Value */
    r = Instance_SetElementFromString(base, T("Sint64Value"), T("-64"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Sint64Value.exists == 1);
    UT_ASSERT(inst->Sint64Value.value == -64);

    /* Real32Value */
    r = Instance_SetElementFromString(base, T("Real32Value"), T("32.32"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Real32Value.exists == 1);
    UT_ASSERT(inst->Real32Value.value > 32 && inst->Real32Value.value < 33);

    /* Real64Value */
    r = Instance_SetElementFromString(base, T("Real64Value"), T("64.64"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Real64Value.exists == 1);
    UT_ASSERT(inst->Real64Value.value > 64 && inst->Real64Value.value < 65);

    /* DatetimeValue */
    r = Instance_SetElementFromString(base, T("TimestampValue"), 
        T("20051225120000.123456-360"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->TimestampValue.exists == 1);

    /* Char16Value */
    r = Instance_SetElementFromString(base, T("Char16Value"), T("1234"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->Char16Value.exists == 1);
    UT_ASSERT(inst->Char16Value.value == 1234);

    /* DatetimeValue */
    r = Instance_SetElementFromString(base, T("IntervalValue"), 
        T("12345678010101.123456:000"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->IntervalValue.exists == 1);

    /* StringValue */
    r = Instance_SetElementFromString(base, T("StringValue"), T("Hello"));
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst->StringValue.exists == 1);
    UT_ASSERT(Zcmp(inst->StringValue.value, T("Hello")) == 0);

    /* Uint32Array */
    {
        MI_Uint32 data[] = { 10, 20, 30 };
        MI_Value value;
        value.uint32a.data = data;
        value.uint32a.size = MI_COUNT(data);
        r = MI_Instance_SetElement(base, T("Uint32Array"), &value, MI_UINT32A, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(inst->Uint32Array.value.data[0] == 10);
        UT_ASSERT(inst->Uint32Array.value.data[1] == 20);
        UT_ASSERT(inst->Uint32Array.value.data[2] == 30);
    }

    /* StringArray */
    {
        MI_Char* data[] = { (MI_Char*)T("RED"), (MI_Char*)T("GREEN"), (MI_Char*)T("BLUE") };
        MI_Value value;
        value.stringa.data = data;
        value.stringa.size = MI_COUNT(data);
        r = MI_Instance_SetElement(base, T("StringArray"), &value, MI_STRINGA, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(Zcmp(inst->StringArray.value.data[0], T("RED")) == 0);
        UT_ASSERT(Zcmp(inst->StringArray.value.data[1], T("GREEN")) == 0);
        UT_ASSERT(Zcmp(inst->StringArray.value.data[2], T("BLUE")) == 0);
    }

    /* Print */
    os = outStream;
    UT_ASSERT(os);
    MI_Instance_Print(base, os, 0);

    /* Convert */
    {
        MSFT_AllTypes conv;
        r = Instance_InitConvert(
            (MI_Instance*)&conv, 
            cd, 
            base, 
            MI_FALSE,  /* Keys only */
            MI_FALSE, /* allow-keyless */
            MI_FALSE, /* copy */
            &batch);  
        UT_ASSERT(r == MI_RESULT_OK);
        MI_Instance_Destruct((MI_Instance*)&conv);
    }

    /* Convert */
    {
        MSFT_AllTypes conv;
        r = Instance_InitConvert(
            (MI_Instance*)&conv, 
            cd, 
            base, 
            MI_FALSE,  /* Keys only */
            MI_FALSE, /* allow-keyless */
            MI_TRUE, /* copy */
            &batch);  
        UT_ASSERT(r == MI_RESULT_OK);
        MI_Instance_Destruct((MI_Instance*)&conv);
    }

    {
        MI_Instance* dinst = NULL;
        MSFT_AllTypes sinst;
        memset(&sinst, 0, sizeof(sinst));

        /* Create a dynamic instance will all string properties */
        r = Instance_NewDynamic(&dinst, T("MSFT_AllTypes"), MI_FLAG_CLASS,
            &batch);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(dinst != NULL);

        r = AddString(dinst, T("Key"), T("999"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("BooleanValue"), T("FALSE"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Uint8Value"), T("8"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Sint8Value"), T("-8"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Uint16Value"), T("16"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Sint16Value"), T("-16"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Uint32Value"), T("32"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Sint32Value"), T("-32"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Uint64Value"), T("64"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Sint64Value"), T("-64"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Real32Value"), T("32.32"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Real64Value"), T("64.64"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("TimestampValue"), 
            T("20051225120000.123456-360"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("Char16Value"), T("1234"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("IntervalValue"), 
            T("12345678010101.123456:000"));
        UT_ASSERT(r == MI_RESULT_OK);
        r = AddString(dinst, T("StringValue"), T("Hello"));
        UT_ASSERT(r == MI_RESULT_OK);

#if 0
        MI_Instance_Print(dinst, stdout, 0);
#endif
        /* Convert to static instance */
        r = Instance_InitConvert(
            (MI_Instance*)&sinst, 
            cd, 
            dinst, 
            MI_FALSE, /* Keys only */
            MI_FALSE, /* allow-keyless */
            MI_TRUE,  /* copy */
            &batch);
        UT_ASSERT(r == MI_RESULT_OK);

#if 0
        MI_Instance_Print(&sinst.__instance, stdout, 0);
#endif

        UT_ASSERT(sinst.BooleanValue.exists == 1);
        UT_ASSERT(sinst.BooleanValue.value == 0);
        UT_ASSERT(sinst.Uint8Value.exists == 1);
        UT_ASSERT(sinst.Uint8Value.value == 8);
        UT_ASSERT(sinst.Sint8Value.exists == 1);
        UT_ASSERT(sinst.Sint8Value.value == -8);
        UT_ASSERT(sinst.Uint16Value.exists == 1);
        UT_ASSERT(sinst.Uint16Value.value == 16);
        UT_ASSERT(sinst.Sint16Value.exists == 1);
        UT_ASSERT(sinst.Sint16Value.value == -16);
        UT_ASSERT(sinst.Uint32Value.exists == 1);
        UT_ASSERT(sinst.Uint32Value.value == 32);
        UT_ASSERT(sinst.Sint32Value.exists == 1);
        UT_ASSERT(sinst.Sint32Value.value == -32);
        UT_ASSERT(sinst.Uint64Value.exists == 1);
        UT_ASSERT(sinst.Uint64Value.value == 64);
        UT_ASSERT(sinst.Sint64Value.exists == 1);
        UT_ASSERT(sinst.Sint64Value.value == -64);
        UT_ASSERT(sinst.Real32Value.exists == 1);
        UT_ASSERT(sinst.Real32Value.value > 32 && sinst.Real32Value.value < 33);
        UT_ASSERT(sinst.Real64Value.exists == 1);
        UT_ASSERT(sinst.Real64Value.value > 64 && sinst.Real64Value.value < 65);
        UT_ASSERT(sinst.TimestampValue.exists == 1);
        UT_ASSERT(sinst.Char16Value.exists == 1);
        UT_ASSERT(sinst.Char16Value.value == 1234);
        UT_ASSERT(sinst.IntervalValue.exists == 1);
        UT_ASSERT(sinst.StringValue.exists == 1);
        UT_ASSERT(Zcmp(sinst.StringValue.value, T("Hello")) == 0);

        MI_Instance_Destruct((MI_Instance*)&sinst);
        MI_Instance_Delete(dinst);
    }

    MI_Instance_Delete(base);

    Batch_Destroy(&batch);
}

#if 0
static bool Test(MI_Result r, const MI_Char* str)
{
    return Zcmp(Result_ToString(r), str) == 0;
}
#endif

#if 0
static void TestLib1()
{
    char path[MAX_PATH];

    Lib_Format(path, NULL, "PersonProvider");
#if defined(CONFIG_OS_LINUX)
    UT_ASSERT(strcmp(path, "libPersonProvider.so") == 0);
#elif defined(_MSC_VER)
    UT_ASSERT(strcmp(path, "PersonProvider.dll") == 0);
#endif
}

static void TestLib2()
{
#define DIRNAME "../" CONFIG_LIBDIR
    char path[MAX_PATH];
    void* handle;
    void* sym;
    MI_Result r;

    /* Format path */
    Lib_Format(path, DIRNAME, "PersonProvider");
#if defined(CONFIG_OS_LINUX)
    UT_ASSERT(strcmp(path, DIRNAME "/libPersonProvider.so") == 0);
#elif defined(_MSC_VER)
    UT_ASSERT(strcmp(path, DIRNAME "/PersonProvider.dll") == 0);
#endif

    /* Load library */
    handle = Lib_Open(path);

    if(!handle)
    {
        handle = Lib_Open("PersonProvider");
    }

    UT_ASSERT(handle != NULL);

    /* Get 'MI_Main' symbol */
    sym = Lib_Sym(handle, "MI_Main");
    UT_ASSERT(sym != NULL);

    /* Close the library */
    r = Lib_Close(handle);
    UT_ASSERT(r == MI_RESULT_OK);
}
#endif

void TestInstances()
{
    MI_Result r;
    MI_Instance* inst;
    MI_Value value;
    const MI_ClassDecl* cd;
    Batch batch = BATCH_INITIALIZER;
    
    /* Find class decl */
    cd = SchemaDecl_FindClassDecl(&test_repos_classDecl, T("MSFT_Process"));
    UT_ASSERT(cd != NULL);

    /* Create instance of MSFT_Numbers */
    r = Instance_New(&inst, cd, &batch);
    UT_ASSERT(r == MI_RESULT_OK);

    /* CIM_Person.Key */
    value.uint64 = 1234;
    r = MI_Instance_SetElement(inst, MI_T("WorkingSetSize"), &value, MI_UINT64, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    /* Check value */
    for (size_t i = 0; i < 1000000; i++)
    {
        MI_Value v;
        MI_Type t;
        MI_Uint32 f;
        r = MI_Instance_GetElement(inst, MI_T("WorkingSetSize"), &v, &t, &f, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(v.uint64 == 1234);
        UT_ASSERT(t == MI_UINT64);
        UT_ASSERT(!(f & MI_FLAG_NULL));
    }

    MI_Instance_Delete(inst);
    Batch_Destroy(&batch);
}

static void TestDynamicInstances()
{
    MI_Instance* inst;
    MI_Result r;
    MI_Value v;
    Batch batch = BATCH_INITIALIZER;
    Batch* b = &batch;

    r = Instance_NewDynamic(&inst, T("MSFT_Person"), MI_FLAG_CLASS, b);
    UT_ASSERT(r == MI_RESULT_OK);

    v.uint32 = 1;
    r = MI_Instance_AddElement(inst, T("Key"), &v, MI_UINT32, MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);

    v.string = (MI_Char*)T("George");
    r = MI_Instance_AddElement(inst, T("First"), &v, MI_STRING, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    v.string = (MI_Char*)T("Washington");
    r = MI_Instance_AddElement(inst, T("Last"), &v, MI_STRING, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    /* Check "Key" */
    {
        MI_Type t;
        MI_Uint32 f;
        r = MI_Instance_GetElement(inst, T("Key"), &v, &t, &f, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(v.uint32 == 1);
        UT_ASSERT(t == MI_UINT32);
        UT_ASSERT(!(f & MI_FLAG_NULL));
    }

    /* Check "First" */
    {
        MI_Type t;
        MI_Uint32 f;
        r = MI_Instance_GetElement(inst, T("First"), &v, &t, &f, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(Zcmp(v.string, T("George")) == 0);
        UT_ASSERT(t == MI_STRING);
        UT_ASSERT(!(f & MI_FLAG_NULL));
    }

    /* Check "Last" */
    {
        MI_Type t;
        MI_Uint32 f;
        r = MI_Instance_GetElement(inst, T("Last"), &v, &t, &f, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(Zcmp(v.string, T("Washington")) == 0);
        UT_ASSERT(t == MI_STRING);
        UT_ASSERT(!(f & MI_FLAG_NULL));
    }

#if 0
    MI_Instance_Print(inst, stdout, 0);
#endif

    // Test cloning.
    MI_Instance* clone = NULL;
    r = MI_Instance_Clone(inst, &clone);
    UT_ASSERT(r == MI_RESULT_OK);

#if 0
    MI_Instance_Print(clone, stdout, 0);
#endif

    /* Check "Key" */
    {
        MI_Type t;
        MI_Uint32 f;
        r = MI_Instance_GetElement(clone, T("Key"), &v, &t, &f, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(v.uint32 == 1);
        UT_ASSERT(t == MI_UINT32);
        UT_ASSERT(!(f & MI_FLAG_NULL));
    }

    /* Check "First" */
    {
        MI_Type t;
        MI_Uint32 f;
        r = MI_Instance_GetElement(clone, T("First"), &v, &t, &f, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(Zcmp(v.string, T("George")) == 0);
        UT_ASSERT(t == MI_STRING);
        UT_ASSERT(!(f & MI_FLAG_NULL));
    }

    /* Check "Last" */
    {
        MI_Type t;
        MI_Uint32 f;
        r = MI_Instance_GetElement(clone, T("Last"), &v, &t, &f, 0);
        UT_ASSERT(r == MI_RESULT_OK);
        UT_ASSERT(Zcmp(v.string, T("Washington")) == 0);
        UT_ASSERT(t == MI_STRING);
        UT_ASSERT(!(f & MI_FLAG_NULL));
    }


    /* Pack/unpack the instance */
    {
        Buf buf = BUF_INITIALIZER;
        MI_Instance* newInst;

        Buf_Init(&buf, 4096);

        /* Pack */
        r = Instance_Pack(inst, MI_FALSE, NULL, NULL, &buf);
        UT_ASSERT(r == MI_RESULT_OK);

        /* Unpack */
        r = Instance_Unpack(&newInst, &buf, b, MI_FALSE);
        UT_ASSERT(r == MI_RESULT_OK);

#if 0
        MI_Instance_Print(newInst, stdout, 0);
#endif

        MI_Instance_Delete(newInst);
        Buf_Destroy(&buf);
    }


    // Add many properties:
    for (size_t i = 0; i < 1000; i++)
    {
        MI_Char buf[32];
        Szprintf(buf, MI_COUNT(buf), T("P%d"), (int)i);
        v.string = (MI_Char*)T("Junk");
        r = MI_Instance_AddElement(inst, buf, &v, MI_STRING, 0);
        UT_ASSERT(r == MI_RESULT_OK);
    }

    // Test basd name:
    r = MI_Instance_AddElement(inst, T(""), NULL, MI_UINT32, 0);
    UT_ASSERT(r == MI_RESULT_INVALID_PARAMETER);

    // Test reference array:
    {
        MI_Instance* data[2];

        for (size_t i = 0; i < 2; i++)
        {
            MI_Instance* tmp = NULL;
            r = MI_Instance_Clone(inst, &tmp);
            UT_ASSERT(r == MI_RESULT_OK);
            UT_ASSERT(tmp != NULL);
            data[i] = tmp;
        }

        v.instancea.data = data;
        v.instancea.size = 2;
        r = MI_Instance_AddElement(inst, T("RefArray"), &v, MI_REFERENCEA, 0);
        UT_ASSERT(r == MI_RESULT_OK);

        for (size_t i = 0; i < 2; i++)
            MI_Instance_Delete(data[i]);
    }

    MI_Instance_Print(inst, outStream, 0);

    // Clone again
    {
        MI_Instance* tmp = NULL;
        r = MI_Instance_Clone(inst, &tmp);
        UT_ASSERT(r == MI_RESULT_OK);
        MI_Instance_Delete(tmp);
    }

    MI_Instance_Delete(clone);
    MI_Instance_Delete(inst);
    Batch_Destroy(b);
}

/*
**==============================================================================
**
** TestBuf()
**
**     This test excercises the Buf implementation. It packs and unpacks
**     integer values of lengths 8-bit, 16-bit, 32-bit, and 64-bit. The values
**     and the lengths are selected randomly. The unpacked values are compared
**     with the original packed values (the original values are save in a 
**     vector).
**
**==============================================================================
*/
static void TestBuf()
{
    Buf b;
    vector<MI_Uint8> buf8;
    vector<MI_Uint16> buf16;
    vector<MI_Uint32> buf32;
    vector<MI_Uint64> buf64;
    vector<MI_Uint32> indices;
    MI_Uint64 sum1 = 0;
    MI_Uint64 sum2 = 0;
    const size_t N = 100000;
    MI_Result r;

    Buf_Init(&b, 256);

    for (size_t i = 0; i < N; i++)
    {
        buf8.push_back(rand() % 0xFF);
        buf16.push_back(rand() % 0xFFFF);
        buf32.push_back(rand() % 0xFFFFFFFF);
        buf64.push_back(rand() % 0xFFFFFFFF);
        indices.push_back((MI_Uint32)rand() % 3);

        switch (indices[i])
        {
            case 0:
                r = Buf_PackU8(&b, buf8[i]);
                UT_ASSERT(r == MI_RESULT_OK);
                sum1 += buf8[i];
                break;
            case 1:
                r = Buf_PackU16(&b, buf16[i]);
                UT_ASSERT(r == MI_RESULT_OK);
                sum1 += buf16[i];
                break;
            case 2:
                r = Buf_PackU32(&b, buf32[i]);
                UT_ASSERT(r == MI_RESULT_OK);
                sum1 += buf32[i];
                break;
            case 3:
                r = Buf_PackU64(&b, buf64[i]);
                sum1 += buf64[i];
                UT_ASSERT(r == MI_RESULT_OK);
                break;
        }
    }

    for (size_t i = 0; i < N; i++)
    {
        switch (indices[i])
        {
            case 0:
            {
                MI_Uint8 x = 0;
                r = Buf_UnpackU8(&b, &x);
                UT_ASSERT(r == MI_RESULT_OK);
                UT_ASSERT(x == buf8[i]);
                sum2 += buf8[i];
                break;
            }
            case 1:
            {
                MI_Uint16 x = 0;
                r = Buf_UnpackU16(&b, &x);
                UT_ASSERT(r == MI_RESULT_OK);
                UT_ASSERT(x == buf16[i]);
                sum2 += buf16[i];
                break;
            }
            case 2:
            {
                MI_Uint32 x = 0;
                r = Buf_UnpackU32(&b, &x);
                UT_ASSERT(r == MI_RESULT_OK);
                UT_ASSERT(x == buf32[i]);
                sum2 += buf32[i];
                break;
            }
            case 3:
            {
                MI_Uint64 x = 0;
                r = Buf_UnpackU64(&b, &x);
                UT_ASSERT(r == MI_RESULT_OK);
                UT_ASSERT(x == buf64[i]);
                sum2 += buf64[i];
                break;
            }
        }
    }

    /* Verify that the check sums are the same */
    UT_ASSERT(sum1 == sum2);

    Buf_Destroy(&b);
}

/*
**==============================================================================
**
** TestBuf2()
**
**     This test excercises the string pack/unpack functions of Buf.
**     It packs N decimal formatted strings and then unpacks them, comparing
**     that backed and unpacked forms are consistent.
**
**==============================================================================
*/
static void TestBuf2()
{
    Buf b;
    MI_Result r;
    MI_Uint32 i;
    const MI_Uint32 N = 10000;

    Buf_Init(&b, 256);

    for (i = 0; i < N; i++)
    {
        MI_Char buf[32];
        Szprintf(buf, MI_COUNT(buf), T("%u"), i);
        r = Buf_PackStr(&b, buf);
        UT_ASSERT(r == MI_RESULT_OK);
    }

    for (i = 0; i < N; i++)
    {
        MI_Char buf[32];
        const MI_Char* str = NULL;

        Szprintf(buf, MI_COUNT(buf), T("%u"), i);
        r = Buf_UnpackStr(&b, &str);
        UT_ASSERT(r == MI_RESULT_OK);
    }

    Buf_Destroy(&b);
}

/*
**==============================================================================
**
** TestBuf3()
**
**     This test excercises the integer array pack/unpack functions of Buf.
**     It packs N arrays of various lengths and attempts to unpack them and
**     compare the results.
**
**==============================================================================
*/
static void TestBuf3()
{
    Buf b;
    MI_Uint32 i;
    const MI_Uint32 N = 100;
    MI_Result r;
    vector<MI_Uint8> a8;
    vector<MI_Uint16> a16;
    vector<MI_Uint32> a32;
    vector<MI_Uint64> a64;
    vector<MI_Uint8> b8;
    vector<MI_Uint16> b16;
    vector<MI_Uint32> b32;
    vector<MI_Uint64> b64;

    Buf_Init(&b, 256);

    for (i = 0; i < N; i++)
    {
        a8.push_back((MI_Uint8)i);
        a16.push_back((MI_Uint16)i);
        a32.push_back((MI_Uint32)i);
        a64.push_back((MI_Uint64)i);
        {
            r = Buf_PackU8A(&b, &a8[0], (MI_Uint32)a8.size());
            UT_ASSERT(r == MI_RESULT_OK);
        }
        {
            r = Buf_PackU16A(&b, &a16[0], (MI_Uint32)a16.size());
            UT_ASSERT(r == MI_RESULT_OK);
        }
        {
            r = Buf_PackU32A(&b, &a32[0], (MI_Uint32)a32.size());
            UT_ASSERT(r == MI_RESULT_OK);
        }
        {
            r = Buf_PackU64A(&b, &a64[0], (MI_Uint32)a64.size());
            UT_ASSERT(r == MI_RESULT_OK);
        }
    }

    for (i = 0; i < N; i++)
    {
        b8.push_back((MI_Uint8)i);
        b16.push_back((MI_Uint16)i);
        b32.push_back((MI_Uint32)i);
        b64.push_back((MI_Uint64)i);
        {
            const MI_Uint8* data;
            MI_Uint32 size;

            r = Buf_UnpackU8A(&b, &data, &size);
            UT_ASSERT(r == MI_RESULT_OK);
            UT_ASSERT((MI_Uint32)b8.size() == size);
            UT_ASSERT(memcmp(data, &b8[0], i * sizeof(MI_Uint8)) == 0);
        }
        {
            const MI_Uint16* data;
            MI_Uint32 size;

            r = Buf_UnpackU16A(&b, &data, &size);
            UT_ASSERT(r == MI_RESULT_OK);
            UT_ASSERT((MI_Uint32)b16.size() == size);
            UT_ASSERT(memcmp(data, &b16[0], i * sizeof(MI_Uint16)) == 0);
        }
        {
            const MI_Uint32* data;
            MI_Uint32 size;

            r = Buf_UnpackU32A(&b, &data, &size);
            UT_ASSERT(r == MI_RESULT_OK);
            UT_ASSERT((MI_Uint32)b32.size() == size);
            UT_ASSERT(memcmp(data, &b32[0], i * sizeof(MI_Uint32)) == 0);
        }
        {
            const MI_Uint64* data;
            MI_Uint32 size;

            r = Buf_UnpackU64A(&b, &data, &size);
            UT_ASSERT(r == MI_RESULT_OK);
            UT_ASSERT((MI_Uint32)b64.size() == size);
            UT_ASSERT(memcmp(data, &b64[0], i * sizeof(MI_Uint64)) == 0);
        }
    }

    Buf_Destroy(&b);
}

/*
**==============================================================================
**
** TestBuf4()
**
**     This test excercises the string array pack/unpack functions of Buf.
**
**==============================================================================
*/
static void TestBuf4()
{
    Buf b;
    MI_Result r;
    const MI_Char* DATA[] =
    {
        T("Sunday"),
        T("Monday"),
        T("Tuesday"),
        T("Wednesday"),
        T("Thursday"),
        T("Friday"),
        T("Saturday"),
    };
    const MI_Uint32 SIZE = MI_COUNT(DATA);
    const MI_Char** data;
    MI_Uint32 size;
    MI_Uint32 i;

    {
        Buf_Init(&b, 256);

        r = Buf_PackStrA(&b, DATA, SIZE);
        UT_ASSERT(r == MI_RESULT_OK);

        r = Buf_UnpackStrA(&b, &data, &size);
        UT_ASSERT(r == MI_RESULT_OK);

        UT_ASSERT(size == SIZE);

        for (i = 0; i < size; i++)
            UT_ASSERT(Zcmp(data[i], DATA[i]) == 0);

        Buf_Destroy(&b);
    }
    {
        Buf_Init(&b, 256);

        r = Buf_PackStrA(&b, DATA, 1);
        UT_ASSERT(r == MI_RESULT_OK);

        r = Buf_UnpackStrA(&b, &data, &size);
        UT_ASSERT(r == MI_RESULT_OK);

        UT_ASSERT(size == 1);

        for (i = 0; i < size; i++)
            UT_ASSERT(Zcmp(data[i], DATA[i]) == 0);

        Buf_Destroy(&b);
    }
    {
        Buf_Init(&b, 256);

        r = Buf_PackStrA(&b, DATA, 0);
        UT_ASSERT(r == MI_RESULT_OK);

        r = Buf_UnpackStrA(&b, &data, &size);
        UT_ASSERT(r == MI_RESULT_OK);

        UT_ASSERT(size == 0);

        for (i = 0; i < size; i++)
            UT_ASSERT(Zcmp(data[i], DATA[i]) == 0);

        Buf_Destroy(&b);
    }
    {
        MI_Uint32 j;
        const MI_Uint32 N = 100;

        Buf_Init(&b, 256);

        for (j = 0; j < N; j++)
        {
            r = Buf_PackStrA(&b, DATA, SIZE);
            UT_ASSERT(r == MI_RESULT_OK);
        }

        for (j = 0; j < N; j++)
        {
            r = Buf_UnpackStrA(&b, &data, &size);
            UT_ASSERT(r == MI_RESULT_OK);

            UT_ASSERT(size == SIZE);

            for (i = 0; i < size; i++)
                UT_ASSERT(Zcmp(data[i], DATA[i]) == 0);
        }

        Buf_Destroy(&b);
    }
}

static void TestLog()
{
    MI_Result r;
    MI_Char tmp[MAX_PATH_SIZE];

#if defined(CONFIG_OS_WINDOWS)
    int tmp_i = _mkdir(GetPath(ID_TMPDIR));
    MI_UNUSED(tmp_i);
#endif
    /* Open the log (into text.log) */
    r = Log_Open(ZSTempPath(tmp, "test2.log"));
    UT_ASSERT(r == MI_RESULT_OK);

    /* Set the log level to 'INFO' */
    Log_SetLevel(LOG_INFO);

    /* Write a log message of each level */
    LOGF((T("fatal: %u"), 11111));
    LOGE((T("error: %u"), 22222));
    LOGW((T("warning: %u"), 33333));
    LOGI((T("information: %u"), 44444));
    LOGD((T("debug: %u"), 55555));

    /* Close the log */
    Log_Close();

    /* verify that output was correct */
    std::vector< unsigned char > content;
    UT_ASSERT(ut::readFileContent(ut::StrToChar(tmp), content));

    ut::removeIfExist(ut::StrToChar(tmp).c_str());

    UT_ASSERT(!content.empty());

    {
        std::string s(reinterpret_cast<const char*>(&content[0]),
            reinterpret_cast<const char*>(&content[0]) + content.size());

        // expecting to find mentioning of all messages, excluding 'debug'
        UT_ASSERT(s.find("FATAL") != string::npos);
        UT_ASSERT(s.find("fatal: 11111") != string::npos);
        UT_ASSERT(s.find("ERROR") != string::npos);
        UT_ASSERT(s.find("error: 22222") != string::npos);
        UT_ASSERT(s.find("WARNING") != string::npos);
        UT_ASSERT(s.find("warning: 33333") != string::npos);
        UT_ASSERT(s.find("INFO") != string::npos);
        UT_ASSERT(s.find("information: 44444") != string::npos);

        // these should not be in the file
        UT_ASSERT(s.find("DEBUG") == string::npos);
        UT_ASSERT(s.find("debug: 55555") == string::npos);

    }

    /* Test loglevel conversions */
    {
        const char* _symbols[] = 
        {
            "Fatal",
            "Error",
            "Warning",
            "Info",
            "Debug",
        };
        const Log_Level _levels[] = 
        {
            LOG_FATAL,
            LOG_ERROR,
            LOG_WARNING,
            LOG_INFO,
            LOG_DEBUG
        };
        const size_t n = sizeof(_symbols) / sizeof(_symbols[0]);

        for (size_t i = 0; i < n; i++)
        {
            UT_ASSERT(Log_SetLevelFromString(_symbols[i]) == 0);

            UT_ASSERT(Strcasecmp(
                Log_GetLevelString(Log_GetLevel()), _symbols[i]) == 0);

            UT_ASSERT(Log_GetLevel() == _levels[i]);
        }
    }
}

static void TestPackInstance()
{
    MI_Instance* inst1 = NULL;
    MI_Instance* inst2 = NULL;
    Batch batch = BATCH_INITIALIZER;
    Buf buf = BUF_INITIALIZER;
    MI_Result r;

    /* Create new instance of MSFT_AllTypes */
    inst1 = NewAllTypes(&batch);
    UT_ASSERT(inst1 != NULL);

    /* Pack instance */
    r = Instance_Pack(inst1, MI_FALSE, NULL, NULL, &buf);
    UT_ASSERT(r == MI_RESULT_OK);

    /* Unpack instance */
    r = Instance_Unpack(&inst2, &buf, &batch, MI_FALSE);
    UT_ASSERT(r == MI_RESULT_OK);
    UT_ASSERT(inst2 != NULL);

#if 0
    MI_Instance_Print(inst1, stdout, 0);
    MI_Instance_Print(inst2, stdout, 0);
#endif

    /* Steal buffer memory and attach it to the batch. */
    {
        Page* page = Buf_StealPage(&buf);
        UT_ASSERT(page != NULL);
        Batch_AttachPage(&batch, page);
    }

    MI_Instance_Delete(inst1);
    MI_Instance_Delete(inst2);
    Buf_Destroy(&buf);
    Batch_Destroy(&batch);
}

static void TestPage()
{
    size_t n = sizeof (Page);
    UT_ASSERT(n % 8 == 0);
}

#define TestOneCppProp(prop,valueParam)             \
    {  \
    /* test auto-generated setters/getters/copy/assignment operators  */  \
    MSFT_AllTypes_Class inst1;                   \
                                                \
    UT_ASSERT(!inst1.prop().exists);            \
    inst1.prop##_value(valueParam);             \
    UT_ASSERT(inst1.prop().exists);             \
    UT_ASSERT(inst1.prop().value == valueParam);   \
                                                \
    /* copy ctor of instance */                 \
    MSFT_AllTypes_Class inst2( inst1 );          \
                                                \
    UT_ASSERT(inst1.prop() == inst2.prop());    \
    UT_ASSERT(inst1.prop##_value() == inst2.prop##_value());    \
                                                \
    /* assignment operator */                   \
    MSFT_AllTypes_Class inst3;                   \
                                                \
    UT_ASSERT(inst1.prop().exists != inst3.prop().exists);    \
    UT_ASSERT(!inst3.prop().exists);            \
                                                \
    inst3 = inst1;                              \
    UT_ASSERT(inst1.prop().exists == inst3.prop().exists);    \
    UT_ASSERT(inst1.prop().value == inst3.prop().value);    \
    UT_ASSERT(inst1.prop##_value() == inst3.prop##_value());    \
                                                \
    /* copy data using setters  */              \
    MSFT_AllTypes_Class inst4;                   \
                                                \
    UT_ASSERT(inst1.prop().exists != inst4.prop().exists);    \
                                                \
    inst4.prop(inst1.prop());                   \
    UT_ASSERT(inst1.prop() == inst4.prop());    \
    UT_ASSERT(inst1.prop##_value() == inst4.prop##_value());    \
                                                \
    /* test null assignment */                  \
    MSFT_AllTypes_Class inst_null;               \
                                                \
    UT_ASSERT(inst4.prop() != inst_null.prop());    \
    inst4 = inst_null;                          \
    UT_ASSERT(inst4.prop() == inst_null.prop());\
    }


static void TestBaseCppAllTypes()
{
    TestOneCppProp(BooleanValue,MI_TRUE);
    TestOneCppProp(BooleanValue,MI_FALSE);


    TestOneCppProp(Uint8Value,0);
    TestOneCppProp(Uint8Value,88);
    TestOneCppProp(Sint8Value,0);
    TestOneCppProp(Sint8Value,-88);
    TestOneCppProp(Uint16Value,0);
    TestOneCppProp(Uint16Value,1616);
    TestOneCppProp(Sint16Value,0);
    TestOneCppProp(Sint16Value,-1616);
    TestOneCppProp(Uint32Value,0);
    TestOneCppProp(Uint32Value,3232);
    TestOneCppProp(Sint32Value,0);
    TestOneCppProp(Sint32Value,-3232);
    TestOneCppProp(Uint64Value,0);
    TestOneCppProp(Uint64Value,6464);
    TestOneCppProp(Sint64Value,0);
    TestOneCppProp(Sint64Value,-6464);

    TestOneCppProp(StringValue,MI_T("123"));
    TestOneCppProp(StringValue,String(MI_T("123")));
    TestOneCppProp(StringValue,String());

    StringA sa;  sa.PushBack(MI_T("123")); sa.PushBack(MI_T("321"));

    //ATTN: add == in vector !! TestOneCppProp(StringArray,sa);
    //TestOneCppProp(StringArray,StringA());
}

static void TestDir()
{
    string path = prefix + "/base/tests/testdir";
    Dir* dir = Dir_Open(path.c_str());
    UT_ASSERT(dir != NULL);

    vector<string> names1;

    for (;;)
    {
        DirEnt* ent = Dir_Read(dir);
        if (!ent)
            break;

        if (strcmp(ent->name, "..") == 0 || strcmp(ent->name, ".") == 0)
            continue;

        if (strcmp(ent->name, "CVS") == 0)
            continue;

        names1.push_back(ent->name);
    }

    sort(names1.begin(), names1.end());

    vector<string> names2;
    names2.push_back("dir1");
    names2.push_back("dir2");
    names2.push_back("file1");
    names2.push_back("file2");
    names2.push_back("file3");

    UT_ASSERT(names1.size() == 5);
    UT_ASSERT(names2.size() == 5);
    UT_ASSERT(names1 == names2);

    Dir_Close(dir);
}

static void TestStrArr()
{
    char** data;
    
    data = StrArr();
    UT_ASSERT(StrArrLen(data) == 0);

    StrArrCat(&data, "Red");
    UT_ASSERT(StrArrLen(data) == 1);
    StrArrCat(&data, "Green");
    UT_ASSERT(StrArrLen(data) == 2);
    StrArrCat(&data, "Blue");
    UT_ASSERT(StrArrLen(data) == 3);

    UT_ASSERT(strcmp(data[0], "Red") == 0);
    UT_ASSERT(strcmp(data[1], "Green") == 0);
    UT_ASSERT(strcmp(data[2], "Blue") == 0);

    StrArrFree(data);
}

static void TestConf1()
{
    char path[MAX_PATH_SIZE];
    Conf* conf;
    size_t n = 1;

    Strlcpy(path, GetPath(ID_PREFIX), sizeof(path));
    Strlcat(path, "/base/tests/file1.conf", sizeof(path));

    /* Open configuration file */
    conf = Conf_Open(path);
    UT_ASSERT(conf != NULL);

    for (;;)
    {
        const char* key;
        const char* value;
        int r = Conf_Read(conf, &key, &value);

        if (r == 1)
            break;

        UT_ASSERT(r == 0);

        switch (n)
        {
            case 1:
                UT_ASSERT(strcmp(key, "Key1") == 0);
                UT_ASSERT(strcmp(value, "Value1") == 0);
                break;
            case 2:
                UT_ASSERT(strcmp(key, "Key2") == 0);
                UT_ASSERT(strcmp(value, "Value2") == 0);
                break;
            case 3:
                UT_ASSERT(strcmp(key, "Key3") == 0);
                UT_ASSERT(strcmp(value, "Value3") == 0);
                break;
            case 4:
                UT_ASSERT(strcmp(key, "Key4") == 0);
                UT_ASSERT(strcmp(value, "Value4") == 0);
                break;
            case 5:
                UT_ASSERT(strcmp(key, "Key5") == 0);
                UT_ASSERT(strcmp(value, "Value5") == 0);
                break;
            case 6:
                UT_ASSERT(strcmp(key, "Key6") == 0);
                UT_ASSERT(strcmp(value, "") == 0);
                break;
            case 7:
                UT_ASSERT(strcmp(key, "Key7") == 0);
                UT_ASSERT(strcmp(value, "") == 0);
                break;
            default:
                UT_ASSERT(UT_FALSE);
        }

        n++;
    }

    Conf_Close(conf);
}

static void TestConf2()
{
    char path[MAX_PATH_SIZE];
    Conf* conf;
    size_t n = 1;

    Strlcpy(path, GetPath(ID_PREFIX), sizeof(path));
    Strlcat(path, "/base/tests/file2.conf", sizeof(path));

    /* Open configuration file */
    conf = Conf_Open(path);
    UT_ASSERT(conf != NULL);

    for (;;)
    {
        const char* key;
        const char* value;
        int r = Conf_Read(conf, &key, &value);

        if (r == -1)
        {
            const char* err = Conf_Error(conf);

            if (n == 1)
            {
                UT_ASSERT(strcmp(err, "expected '='") == 0);
                UT_ASSERT(Conf_Line(conf) == 2);
            }
            else if (n == 2)
            {
                UT_ASSERT(strcmp(err, "expected keyword") == 0);
                UT_ASSERT(Conf_Line(conf) == 3);
            }
            else if (n == 3)
            {
                UT_ASSERT(strcmp(err, "expected keyword") == 0);
                UT_ASSERT(Conf_Line(conf) == 4);
            }
            else
                UT_ASSERT(UT_FALSE);
        }
        else if (r == 0)
        {
            UT_ASSERT(n == 4);
            UT_ASSERT(strcmp(key, "KEY1") == 0);
            UT_ASSERT(strcmp(value, "VALUE1") == 0);
        }
        else
            break;

        n++;
    }

    Conf_Close(conf);
}

static void TestMkdirhier()
{
    char tmp[MAX_PATH_SIZE];
    Mkdirhier(TempPath(tmp, "/aaa/bbb/ccc/ddd"), 0755);
    UT_ASSERT(Isdir(tmp));
}

static void TestIntToStr()
{
    for (MI_Uint32 i = 0; i < 1000; i++)
    {
        char buf1[11];
        size_t size1;
        const char* str = Uint32ToStr(buf1, i, &size1);

        char buf2[11];
        size_t size2 = Snprintf(buf2, sizeof(buf2), "%u", i);

        UT_ASSERT(str != NULL);
        UT_ASSERT(size1 == size2);
        UT_ASSERT(strcmp(str, buf2) == 0);
    }

    for (MI_Uint64 i = 0; i < 1000; i++)
    {
        char buf1[21];
        size_t size1;
        const char* str = Uint64ToStr(buf1, i, &size1);


        char buf2[21];
        size_t size2 = Snprintf(buf2, sizeof(buf2), UINT64_FMT, i);

        UT_ASSERT(str != NULL);
        UT_ASSERT(size1 == size2);
        UT_ASSERT(strcmp(str, buf2) == 0);
    }
}

static void TestGetOpt()
{
    GetOptState state = GETOPTSTATE_INITIALIZER;

    const char* argv[] =
    {
        "aaa",
        "bbb",
        "--help",
        "--infile=/tmp/infile",
        "--outfile",
        "/tmp/outfile",
        "-x=abc",
        "ccc",
        NULL,
    };
    int argc = sizeof(argv) / sizeof(argv[0]) - 1;

    static const char* opts[] =
    {
        "--help",
        "--infile:",
        "--outfile:",
        "-x:",
        NULL,
    };

    int r;
    bool help = false;
    string infile;
    string outfile;
    string x;

    for (;;)
    {
        r = GetOpt(&argc, argv, opts, &state);

        if (r == -1)
        {
            fprintf(stderr, "GetOpt() failed: %s\n", state.err);
            UT_ASSERT_FAILED_MSG("GetOpt failed");
        }

        if (r != 0)
            break;

        if (strcmp(state.opt, "--help") == 0)
            help = true;
        else if (strcmp(state.opt, "--infile") == 0)
            infile = state.arg;
        else if (strcmp(state.opt, "--outfile") == 0)
            outfile = state.arg;
        else if (strcmp(state.opt, "-x") == 0)
            x = state.arg;
    }

    UT_ASSERT(argc == 3);
    UT_ASSERT(strcmp(argv[0], "aaa") == 0);
    UT_ASSERT(strcmp(argv[1], "bbb") == 0);
    UT_ASSERT(strcmp(argv[2], "ccc") == 0);
    UT_ASSERT(argv[3] == NULL);
    UT_ASSERT(help == true);
    UT_ASSERT(infile == "/tmp/infile");
    UT_ASSERT(outfile == "/tmp/outfile");
    UT_ASSERT(x == "abc");
}

static void TestGetOptErr()
{
    GetOptState state = GETOPTSTATE_INITIALIZER;

    const char* argv[] =
    {
        "aaa",
        "--help",
        "bbb",
        "--outfile", /* missing option argument */
        NULL,
    };
    int argc = sizeof(argv) / sizeof(argv[0]) - 1;

    static const char* opts[] =
    {
        "--help",
        "--outfile:",
        NULL,
    };

    bool help = false;
    string outfile;
    string error;

    for (;;)
    {
        int r = GetOpt(&argc, argv, opts, &state);

        if (r == 1)
            break;

        if (r == 0)
        {
            if (strcmp(state.opt, "--help") == 0)
                help = true;
            else if (strcmp(state.opt, "--outfile") == 0)
                outfile = state.arg;
        }
        if (r == -1)
        {
            error = state.err;
            break;
        }
    }

    UT_ASSERT(help == true);
    UT_ASSERT(error == "missing option argument: --outfile");
    UT_ASSERT(outfile == "");
}

static void TestMatchKeys()
{
    MI_Result r;
    MI_Instance* inst1;
    MI_Instance* inst2;
    MI_Instance* ref;
    MI_Value value;

    r = Instance_NewDynamic(&ref, T("MyRef"), MI_FLAG_CLASS, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    value.uint32 = 57789;
    r = MI_Instance_AddElement(ref, T("Key1"), &value, MI_UINT32, MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);

    value.string = (MI_Char*)T("Bye");
    r = MI_Instance_AddElement(ref, T("Key2"), &value, MI_STRING, MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);
    
    r = Instance_NewDynamic(&inst1, T("MyClass"), MI_FLAG_ASSOCIATION, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    r = Instance_NewDynamic(&inst2, T("MyClass"), MI_FLAG_CLASS, 0);
    UT_ASSERT(r == MI_RESULT_OK);

    UT_ASSERT(Instance_MatchKeys(inst1, inst2) == MI_FALSE);

    value.uint32 = 1234;
    r = MI_Instance_AddElement(inst1, T("Key1"), &value,MI_UINT32, MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);

    value.string = (MI_Char*)T("Hello");
    r = MI_Instance_AddElement(inst1, T("Key2"), &value, MI_STRING,MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);

    UT_ASSERT(Instance_MatchKeys(inst1, inst2) == MI_FALSE);

    value.uint32 = 1234;
    r = MI_Instance_AddElement(inst2, T("Key1"), &value, MI_UINT32,MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);

    UT_ASSERT(Instance_MatchKeys(inst1, inst2) == MI_FALSE);

    value.string = (MI_Char*)T("Hello");
    r = MI_Instance_AddElement(inst2, T("Key2"), &value, MI_STRING,MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);

    UT_ASSERT(Instance_MatchKeys(inst1, inst2) == MI_TRUE);

    value.reference = ref;
    r = MI_Instance_AddElement(inst1, T("Key3"), &value, 
        MI_REFERENCE,MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);

    UT_ASSERT(Instance_MatchKeys(inst1, inst2) == MI_FALSE);

    value.reference = ref;
    r = MI_Instance_AddElement(inst2, T("Key3"), &value, 
        MI_REFERENCE,MI_FLAG_KEY);
    UT_ASSERT(r == MI_RESULT_OK);

    UT_ASSERT(Instance_MatchKeys(inst1, inst2) == MI_TRUE);

#if 0
    __MI_Instance_Print(inst1, stdout, 0);
    __MI_Instance_Print(inst2, stdout, 0);
#endif

    __MI_Instance_Delete(inst1);
    __MI_Instance_Delete(inst2);
    __MI_Instance_Delete(ref);
}

static void TestWSManDatetime()
{
    MI_Char buf[64];

    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("P5Y"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 1826);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 0);
        UT_ASSERT(x.u.interval.seconds == 0);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("P1826D")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("P1Y1M22D"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 417);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 0);
        UT_ASSERT(x.u.interval.seconds == 0);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("P417D")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("P1Y1M22DT10H11M12S"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 417);
        UT_ASSERT(x.u.interval.hours == 10);
        UT_ASSERT(x.u.interval.minutes == 11);
        UT_ASSERT(x.u.interval.seconds == 12);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("P417DT10H11M12S")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("PT31S"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 0);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 0);
        UT_ASSERT(x.u.interval.seconds == 31);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("PT31S")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("PT66S"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 0);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 1);
        UT_ASSERT(x.u.interval.seconds == 6);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("PT1M6S")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("PT60S"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 0);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 1);
        UT_ASSERT(x.u.interval.seconds == 0);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("PT1M")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("PT5.5S"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 0);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 0);
        UT_ASSERT(x.u.interval.seconds == 5);
        UT_ASSERT(x.u.interval.microseconds == 500000);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("PT5.500000S")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("PT5.123456S"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 0);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 0);
        UT_ASSERT(x.u.interval.seconds == 5);
        UT_ASSERT(x.u.interval.microseconds == 123456);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("PT5.123456S")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("P1Y2M3DT10H30M"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 429);
        UT_ASSERT(x.u.interval.hours == 10);
        UT_ASSERT(x.u.interval.minutes == 30);
        UT_ASSERT(x.u.interval.seconds == 0);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("P429DT10H30M")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("P1Y2MT"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 426);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 0);
        UT_ASSERT(x.u.interval.seconds == 0);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("P426D")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("PT0.000001S"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 0);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 0);
        UT_ASSERT(x.u.interval.seconds == 0);
        UT_ASSERT(x.u.interval.microseconds == 1);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("PT0.000001S")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("2010-12-31"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 2010);
        UT_ASSERT(x.u.timestamp.month == 12);
        UT_ASSERT(x.u.timestamp.day == 31);
        UT_ASSERT(x.u.timestamp.hour == 0);
        UT_ASSERT(x.u.timestamp.minute == 0);
        UT_ASSERT(x.u.timestamp.second == 0);
        UT_ASSERT(x.u.timestamp.microseconds == 0);
        UT_ASSERT(x.u.timestamp.utc == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("2010-12-31")) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("2010-1-1"), &x) == -1);
    }
    {
        MI_Datetime x;
        const MI_Char STR[] = T("2010-12-31+06:33");
        UT_ASSERT(ParseWSManDatetime(STR, &x) == 0);
        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 2010);
        UT_ASSERT(x.u.timestamp.month == 12);
        UT_ASSERT(x.u.timestamp.day == 31);
        UT_ASSERT(x.u.timestamp.hour == 0);
        UT_ASSERT(x.u.timestamp.minute == 0);
        UT_ASSERT(x.u.timestamp.second == 0);
        UT_ASSERT(x.u.timestamp.microseconds == 0);
        UT_ASSERT(x.u.timestamp.utc == 393);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, STR) == 0);
    }
    {
        MI_Datetime x;
        const MI_Char STR[] = T("2010-12-31-06:20");
        UT_ASSERT(ParseWSManDatetime(STR, &x) == 0);
        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 2010);
        UT_ASSERT(x.u.timestamp.month == 12);
        UT_ASSERT(x.u.timestamp.day == 31);
        UT_ASSERT(x.u.timestamp.hour == 0);
        UT_ASSERT(x.u.timestamp.minute == 0);
        UT_ASSERT(x.u.timestamp.second == 0);
        UT_ASSERT(x.u.timestamp.microseconds == 0);
        UT_ASSERT(x.u.timestamp.utc == -380);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, STR) == 0);
    }
    {
        MI_Datetime x;
        const MI_Char STR[] = T("2010-12-31");
        UT_ASSERT(ParseWSManDatetime(STR, &x) == 0);
        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 2010);
        UT_ASSERT(x.u.timestamp.month == 12);
        UT_ASSERT(x.u.timestamp.day == 31);
        UT_ASSERT(x.u.timestamp.hour == 0);
        UT_ASSERT(x.u.timestamp.minute == 0);
        UT_ASSERT(x.u.timestamp.second == 0);
        UT_ASSERT(x.u.timestamp.microseconds == 0);
        UT_ASSERT(x.u.timestamp.utc == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, STR) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime(T("2010-12-31T12:30:03Z"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 2010);
        UT_ASSERT(x.u.timestamp.month == 12);
        UT_ASSERT(x.u.timestamp.day == 31);
        UT_ASSERT(x.u.timestamp.hour == 12);
        UT_ASSERT(x.u.timestamp.minute == 30);
        UT_ASSERT(x.u.timestamp.second == 03);
        UT_ASSERT(x.u.timestamp.microseconds == 0);
        UT_ASSERT(x.u.timestamp.utc == 0);
    }
    {
        MI_Datetime x;
        const MI_Char STR[] = T("2010-12-31T12:30:03+06:23");
        UT_ASSERT(ParseWSManDatetime(STR, &x) == 0);

        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 2010);
        UT_ASSERT(x.u.timestamp.month == 12);
        UT_ASSERT(x.u.timestamp.day == 31);
        UT_ASSERT(x.u.timestamp.hour == 12);
        UT_ASSERT(x.u.timestamp.minute == 30);
        UT_ASSERT(x.u.timestamp.second == 03);
        UT_ASSERT(x.u.timestamp.microseconds == 0);
        UT_ASSERT(x.u.timestamp.utc == 383);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, STR) == 0);
    }
    {
        MI_Datetime x;
        const MI_Char STR[] = T("12:30:03+06:23");
        UT_ASSERT(ParseWSManDatetime(STR, &x) == 0);
        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 0);
        UT_ASSERT(x.u.timestamp.month == 0);
        UT_ASSERT(x.u.timestamp.day == 0);
        UT_ASSERT(x.u.timestamp.hour == 12);
        UT_ASSERT(x.u.timestamp.minute == 30);
        UT_ASSERT(x.u.timestamp.second == 03);
        UT_ASSERT(x.u.timestamp.microseconds == 0);
        UT_ASSERT(x.u.timestamp.utc == 383);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, STR) == 0);
    }
    {
        MI_Datetime x;
        const MI_Char STR[] = T("12:30:03.1234567+06:23");
        UT_ASSERT(ParseWSManDatetime(STR, &x) == 0);
        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 0);
        UT_ASSERT(x.u.timestamp.month == 0);
        UT_ASSERT(x.u.timestamp.day == 0);
        UT_ASSERT(x.u.timestamp.hour == 12);
        UT_ASSERT(x.u.timestamp.minute == 30);
        UT_ASSERT(x.u.timestamp.second == 03);
        UT_ASSERT(x.u.timestamp.microseconds == 123456);
        UT_ASSERT(x.u.timestamp.utc == 383);
        //PrintDatetime(&x);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("12:30:03.123456+06:23")) == 0);
    }
    {
        MI_Datetime x;
        const MI_Char STR[] = T("2010-12-31T12:30:03.123456+06:23");
        UT_ASSERT(ParseWSManDatetime(STR, &x) == 0);
        UT_ASSERT(x.isTimestamp == 1);
        UT_ASSERT(x.u.timestamp.year == 2010);
        UT_ASSERT(x.u.timestamp.month == 12);
        UT_ASSERT(x.u.timestamp.day == 31);
        UT_ASSERT(x.u.timestamp.hour == 12);
        UT_ASSERT(x.u.timestamp.minute == 30);
        UT_ASSERT(x.u.timestamp.second == 03);
        UT_ASSERT(x.u.timestamp.microseconds == 123456);
        UT_ASSERT(x.u.timestamp.utc == 383);
        //PrintDatetime(&x);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, STR) == 0);
    }
    {
        MI_Datetime x;
        UT_ASSERT(ParseWSManDatetime( T("P4294967295D"), &x) == 0);
        UT_ASSERT(x.isTimestamp == 0);
        UT_ASSERT(x.u.interval.days == 4294967295U);
        UT_ASSERT(x.u.interval.hours == 0);
        UT_ASSERT(x.u.interval.minutes == 0);
        UT_ASSERT(x.u.interval.seconds == 0);
        UT_ASSERT(x.u.interval.microseconds == 0);

        FormatWSManDatetime(&x, buf);
        UT_ASSERT(Zcmp(buf, T("P4294967295D")) == 0);
    }
}

typedef struct _TestBucket /* derives from HashBucket */
{
    struct _TestBucket* next;
    char* key;
    long data;
}
TestBucket;

static size_t TestHash(
    const HashBucket* bucket_)
{
    /* Note: this algorithm has a poor distribution */
    TestBucket* bucket = (TestBucket*)bucket_;
    size_t h = 0;
    char* key = bucket->key;

    while (*key)
    {
        h += 5 * *key++;
    }

    return h;
}

static int TestEqual(
    const HashBucket* bucket1_,
    const HashBucket* bucket2_)
{
    TestBucket* bucket1 = (TestBucket*)bucket1_;
    TestBucket* bucket2 = (TestBucket*)bucket2_;
    return strcmp(bucket1->key, bucket2->key) == 0;
}

static void TestRelease(
    HashBucket* bucket_)
{
    TestBucket* bucket = (TestBucket*)bucket_;

    free(bucket->key);
    free(bucket);
}

static void TestHashTable1()
{
    HashTable table;
    int r;
    TestBucket* b;

    r = HashTable_Init(&table, 1, TestHash, TestEqual, TestRelease);
    UT_ASSERT(r == 0);

    /* Insert some buckets */
    {
        /* Insert RED=1 */
        {
            b = (TestBucket*)calloc(1, sizeof(TestBucket));
            UT_ASSERT(b != 0);
            b->key = strdup("RED");
            b->data = 1;
            r = HashTable_Insert(&table, (HashBucket*)b);
            UT_ASSERT(r == 0);
        }

        /* Insert GREEN=2 */
        {
            b = (TestBucket*)calloc(1, sizeof(TestBucket));
            UT_ASSERT(b != 0);
            b->key = strdup("GREEN");
            b->data = 2;
            r = HashTable_Insert(&table, (HashBucket*)b);
            UT_ASSERT(r == 0);
        }

        /* Insert BLUE=3 */
        {
            b = (TestBucket*)calloc(1, sizeof(TestBucket));
            UT_ASSERT(b != 0);
            b->key = strdup("BLUE");
            b->data = 3;
            r = HashTable_Insert(&table, (HashBucket*)b);
            UT_ASSERT(r == 0);

            /* Insert BLUE=3 again (should fail) */
            r = HashTable_Insert(&table, (HashBucket*)b);
            UT_ASSERT(r == 1);
        }


        /* Find RED=1 */
        {
            TestBucket key;
            key.key = (char*)"RED";
            b = (TestBucket*)HashTable_Find(&table, (const HashBucket*)&key);
            UT_ASSERT(b != 0);
            UT_ASSERT(strcmp(b->key, "RED") == 0);
            UT_ASSERT(b->data == 1);
        }

        /* Find GREEN=2 */
        {
            TestBucket key;
            key.key = (char*)"GREEN";
            b = (TestBucket*)HashTable_Find(&table, (const HashBucket*)&key);
            UT_ASSERT(b != 0);
            UT_ASSERT(strcmp(b->key, "GREEN") == 0);
            UT_ASSERT(b->data == 2);
        }

        /* Find BLUE=3 */
        {
            TestBucket key;
            key.key = (char*)"BLUE";
            b = (TestBucket*)HashTable_Find(&table, (const HashBucket*)&key);
            UT_ASSERT(b != 0);
            UT_ASSERT(strcmp(b->key, "BLUE") == 0);
            UT_ASSERT(b->data == 3);
        }

        /* Find YELLOW=4 (should fail) */
        {
            TestBucket key;
            key.key = (char*)"YELLOW";
            b = (TestBucket*)HashTable_Find(&table, (const HashBucket*)&key);
            UT_ASSERT(b == 0);
        }

        /* Remove RED */
        {
            TestBucket key;
            key.key = (char*)"RED";
            r = HashTable_Remove(&table, (const HashBucket*)&key);
            UT_ASSERT(r == 0);

            /* Remove should fail now */
            r = HashTable_Remove(&table, (const HashBucket*)&key);
            UT_ASSERT(r == -1);
        }

        /* Remove GREEN */
        {
            TestBucket key;
            key.key = (char*)"GREEN";
            r = HashTable_Remove(&table, (const HashBucket*)&key);
            UT_ASSERT(r == 0);

            /* Remove should fail now */
            r = HashTable_Remove(&table, (const HashBucket*)&key);
            UT_ASSERT(r == -1);
        }

        /* Remove BLUE */
        {
            TestBucket key;
            key.key = (char*)"BLUE";
            r = HashTable_Remove(&table, (const HashBucket*)&key);
            UT_ASSERT(r == 0);

            /* Remove should fail now */
            r = HashTable_Remove(&table, (const HashBucket*)&key);
            UT_ASSERT(r == -1);
        }

        /* Remove YELLOW (should fail) */
        {
            TestBucket key;
            key.key = (char*)"YELLOW";
            r = HashTable_Remove(&table, (const HashBucket*)&key);
            UT_ASSERT(r == -1);
        }
    }

    /* Release all the memroy */
    HashTable_Destroy(&table);
}

static void TestHashTable2()
{
    HashTable table;
    int r;
    size_t i;
    const size_t N = 10000;

    /* Create the hash table */

    r = HashTable_Init(&table, 63, TestHash, TestEqual, TestRelease);
    UT_ASSERT(r == 0);

    /* Insert N buckets into hash table */

    for (i = 0; i < N; i++)
    {
        char buf[32];
        TestBucket* b;

        Szprintf(buf, sizeof(buf), "%u", (unsigned int)i);
        b = (TestBucket*)calloc(1, sizeof(TestBucket));
        UT_ASSERT(b != 0);
        b->key = strdup(buf);
        b->data = (long)i;
        r = HashTable_Insert(&table, (HashBucket*)b);
        UT_ASSERT(r == 0);
    }

    /* Verify that each number is in the hash table */

    for (i = 0; i < N; i++)
    {
        char buf[32];
        TestBucket* b;
        TestBucket kb;
        kb.key = buf;
        Szprintf(buf, sizeof(buf), "%u", (unsigned int)i);

        /* Find it */

        b = (TestBucket*)HashTable_Find(&table, (const HashBucket*)&kb);
        UT_ASSERT(b != 0);

        /* Check it */
        UT_ASSERT(strcmp(b->key, buf) == 0);
    }

    /* Delete all the buckets */

    for (i = 0; i < N; i++)
    {
        char buf[32];
        TestBucket kb;
        int r;

        kb.key = buf;
        Szprintf(buf, sizeof(buf), "%u", (unsigned int)i);

        /* Find it */

        r = HashTable_Remove(&table, (const HashBucket*)&kb);
        UT_ASSERT(r == 0);
    }

    /* Release all the memroy */
    HashTable_Destroy(&table);
}

static void RunTests()
{
    UT_TEST(TestHashTable1);
    UT_TEST(TestHashTable2);
    UT_TEST(TestAllocator1);
    UT_TEST(TestAllocator2);
    UT_TEST(TestAllocator3);
    UT_TEST(TestGetName);
    UT_TEST(TestGetSize);
    UT_TEST(TestStringArray1);
    UT_TEST(TestStringArray2);
    UT_TEST(TestMessages);
    UT_TEST(TestMessages2);
    UT_TEST(TestStrings);
    UT_TEST(TestList);
    UT_TEST(TestFindClassDecl);
    UT_TEST(TestAllTypes);
#if 0
    UT_TEST(TestLib1);
    UT_TEST(TestLib2);
#endif
    UT_TEST(TestInstances);
    UT_TEST(TestDynamicInstances);
    UT_TEST(TestBuf);
    UT_TEST(TestBuf2);
    UT_TEST(TestBuf3);
    UT_TEST(TestBuf4);
    UT_TEST(TestPackInstance);
    UT_TEST(TestPage);
    UT_TEST(TestBaseCppAllTypes);
    UT_TEST(TestDir);
    UT_TEST(TestStrArr);
    UT_TEST(TestConf1);
    UT_TEST(TestConf2);
    UT_TEST(TestMkdirhier);
    UT_TEST(TestLog);
    UT_TEST(TestIntToStr);
    UT_TEST(TestGetOpt);
    UT_TEST(TestGetOptErr);
    UT_TEST(TestMatchKeys);
    UT_ENTRY_POINT(TestWSManDatetime);
}

UT_ENTRY_POINT(RunTests);

ViewCVS 0.9.2