//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include <stdlib.h>
#include <stdio.h>
#ifndef UNDER_CE
#include <limits.h>
#include <float.h>
#include <objbase.h>
#endif
#include <windows.h>
#include <tchar.h>
#include <msxml2.h>
#include <stressutils.h>

#undef srand
#undef rand

#define XML_FILEPATH (TEXT("\\release\\s2_domxpathxqlxslt.xml"))

#define THREAD_WAIT_INTERVAL    (60*1000)
#define ONEKB                   1024

HANDLE  g_hTimesUp  = NULL;

CRITICAL_SECTION    g_csResults;

UINT DoStressIteration(void);
DWORD WINAPI TestThreadProc(LPVOID pv);

int
wmain(
    int     argc,
    WCHAR   **argv
    ){
    int             retValue = 0;  // Module return value.  You should return 0 unless you had to abort.
    MODULE_PARAMS   mp;            // Cmd line args arranged here.  Used to init the stress utils.
    STRESS_RESULTS  results;       // Cumulative test results for this module.

    int             i, n;

    DWORD           dwEnd;         // End time   
    DWORD           dwStartTime = GetTickCount();

    ///////////////////////////////////////////////////////
    // Initialization
    //

    ZeroMemory((void*)&mp,sizeof(MODULE_PARAMS));
    ZeroMemory((void*)&results,sizeof(STRESS_RESULTS));


    // Read the cmd line args into a module params struct
    // You must call this before InitializeStressUtils()

    // Be sure to use the correct version, depending on your 
    // module entry point.

    if (!ParseCmdLine_wmain(argc,argv,&mp)){
        LogFail (_T("Failure parsing the command line!  exiting ..."));
        return CESTRESS_ABORT; // Use the abort return code
    }


    // You must call this before using any stress utils 

    InitializeStressUtils(
        _T("s2_xmldomxpathxql.exe"),                // Module name to be used in logging
        LOGZONE(SLOG_SPACE_IE,SLOG_XML),            // Logging zones used by default
        &mp                                         // Module params passed on the cmd line
        );

    // Note on Logging Zones: 
    //
    // Logging is filtered at two different levels: by "logging space" and
    // by "logging zones".  The 16 logging spaces roughly corresponds to the major
    // feature areas in the system (including apps).  Each space has 16 sub-zones
    // for a finer filtering granularity.
    //
    // Use the LOGZONE(space, zones) macro to indicate the logging space
    // that your module will log under (only one allowed) and the zones
    // in that space that you will log under when the space is enabled
    // (may be multiple OR'd values).
    //
    // See \test\stress\stress\inc\logging.h for a list of available
    // logging spaces and the zones that are defined under each space


    LogVerbose (_T("Starting at %d"), dwStartTime);


    // Module-specific parsing of the 'user' portion of the cmd line.
    //
    // In this case, the user info in the cmd line represents the
    // number of threads that we want to run in this test.

    int nThreads = 1;//mp.tszUser ? min(4, max(1, _ttoi(mp.tszUser))) : 1;


    ///////////////////////////////////////////////////////
    // Main test loop
    //


    HANDLE  *hThread    = new HANDLE [nThreads];
    DWORD   *dwThreadId = new DWORD [nThreads];

    if(NULL == hThread || NULL == dwThreadId){
        LogWarn2(_T("Memory allocation failed.  Aborting."));
        retValue = CESTRESS_ABORT;
        goto cleanup;
    }

    InitializeCriticalSection (&g_csResults);


    // Spin one or more worker threads to do the actually testing.
    LogVerbose(_T("Creating %i worker threads"),nThreads);

    g_hTimesUp = CreateEvent (NULL, TRUE, FALSE, NULL);

    for (i = 0; i < nThreads; i++){
        hThread[i] = CreateThread (NULL, 0, TestThreadProc, &results, 0, &dwThreadId[i]);
        if (hThread[i]){
            LogVerbose(_T("Thread %i (0x%x) successfully created"),i,hThread[i]);
        }
        else{
            LogFail (_T("Failed to create thread %i! Error = %d"),i,GetLastError());
        }
    }

    // Main thread is now the timer
    Sleep (1000/*mp.dwDuration*/);

    // Signal the worker threads that they must finish up
    SetEvent (g_hTimesUp);


    ///////////////////////////////////////////////////////
    // Clean-up, report results, and exit
    //
    
    
    // Wait for all of the workers to return.
    
    // NOTE: Don't terminate the threads yourself.  Go ahead and let them hang.
    //       The harness will decide whether to let your module hang or
    //       to terminate the process.

    LogComment (_T("Time's up signaled.  Waiting for threads to finish ..."));


    bool bAllThreadsClosed;
    do {
        //assume they will all close fine
        bAllThreadsClosed = true;
        for (n = 0; n < nThreads; n++){
            if (hThread[n]){
                if (WAIT_OBJECT_0 == WaitForSingleObject (hThread[n], THREAD_WAIT_INTERVAL)){
                    LogVerbose (_T("Thread %i (0x%x) finished"), n, hThread[n]);

                    CloseHandle(hThread[n]);
                    hThread[n] = NULL;
                }
                else{
                    //one of the threads didn't close, so we need to retry to close the threads
                    bAllThreadsClosed = false;
                }
            }
        }
    }while ( ! bAllThreadsClosed );

    DeleteCriticalSection(&g_csResults);



    // Finish
    
    dwEnd = GetTickCount();
    LogVerbose(
        _T("Leaving at %d, duration (ms) = %d, (sec) = %d, (min) = %d"),
        dwEnd,
        dwEnd - dwStartTime,
        (dwEnd - dwStartTime) / 1000,
        (dwEnd - dwStartTime) / 60000
        );

cleanup:

    if(g_hTimesUp)
        CloseHandle(g_hTimesUp);

    if(hThread)
        delete [] hThread;

    if(dwThreadId)
        delete [] dwThreadId;

    // DON'T FORGET:  You must report your results to the harness before exiting
    ReportResults (&results);

    return retValue;
}

typedef struct{
    LPTSTR  lpszDesc;
    LPWSTR  lpwszQuery;
    LPWSTR  lpwszChecksum;
}VARIATION,*LPVARIATION;

VARIATION g_lpXPathVariation[] = {
    {
        TEXT("XPath 1.0: 2.2 Axes - Axis ancestor-or-self::book should return all books."),
        L"book[1]/author/parent::book",
        L"348.34348486798"
    },
    {
        TEXT("XPath 1.0: 2.2 Axes - Axis ancestor::book should return all book nodes."),
        L"book[1]/title/ancestor::book",
        L"348.34348486798"
    },
    {
        TEXT("XPath 1.0: 2.2 Axes - Axis ancestor::* should return all ancestor nodes."),
        L"book[1]/title/ancestor::*",
        L"453.76833223897"
    },
    {
        TEXT("XPath 1.0: 2.2 Axes - Axis ancestor-or-self::book should return all books."),
        L"book[1]/ancestor-or-self::book",
        L"348.34348486798"
    },
    {
        TEXT("XPath 1.0: 2.2 Axes - Axis ancestor-or-self::title should return title."),
        L"book[1]/title/ancestor-or-self::title",
        L"267.61249609090"
    }
};

int g_nXPathVariation = sizeof(g_lpXPathVariation)/sizeof(VARIATION);

VARIATION g_lpXQLVariation[] = {
    {
        TEXT("XQL: Collections - ./first-name - Find all first-name elements"),
        L"book[1]/author/./first-name",
        L"259.58892054159"
    },
    {
        TEXT("XQL: Children and Descendants - Find all title elements  from current context."),
        L"book[1]/.//title",
        L"264.99281847876"
    },
    {
        TEXT("XQL: Attributes - Return the current book element."),
        L"book[0]/@style",
        L"252.80279109929"
    },
    {
        TEXT("XQL: Comparisons - Return all author elements whose last names greater than &quot;M*&quot;"),
        L"book/author[last-name $gt$ 'M']",
        L"361.64971707202"
    }
};

int g_nXQLVariation = sizeof(g_lpXQLVariation)/sizeof(VARIATION);

VARIATION g_lpXSLTVariation[] = {
    {
        TEXT("Attribute Value Template, Base Spec Case - every place an Attribute Value Template is allowed"),
        L"<?xml version=\'1.0\'?><xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\"> \
        <xsl:output method=\"xml\" omit-xml-declaration=\"yes\" indent=\"yes\"/>\
        <xsl:template match=\"/\"> \
        <xsl:for-each select=\"bookstore/magazine\"> \
        <xsl:value-of  select=\"./title\"/> \
        <xslTITLE name=\"{title}\"></xslTITLE> \
        </xsl:for-each> \
        </xsl:template> \
        </xsl:stylesheet>",
        L"328.51129007756"
    }
};

int g_nXSLTVariation = sizeof(g_lpXSLTVariation)/sizeof(VARIATION);


BOOL
DOMLoaded(
    IXMLDOMDocument2    *pXML
    ){
    IXMLDOMParseError   *pXMLError  = NULL;
    BSTR                bstrError   = NULL;
    LONG                errorCode   = E_FAIL;
    if(pXML){
        if(SUCCEEDED(pXML->get_parseError(&pXMLError)) && pXMLError){
            pXMLError->get_errorCode(&errorCode);
            pXMLError->get_srcText(&bstrError);
            pXMLError->Release();
            pXMLError   = NULL;
        }
#ifdef UNICODE
        LogComment(TEXT("errorCode = %ld, errorDescr = %s"),errorCode,bstrError);
#else
        LogComment(TEXT("errorCode = %ld, errorDescr = %S"),errorCode,bstrError);
#endif
    }
    return (errorCode == 0) ? TRUE : FALSE;
}

BOOL
CoCreateAndLoad(
    IXMLDOMDocument2    **ppXML,
    BSTR                bstrFileToLoad
    ){
    HRESULT         hRes;
    VARIANT         varFileToLoad;
    VARIANT_BOOL    isPass = VARIANT_FALSE;
    BOOL            bCreatedAndLoaded;
    bCreatedAndLoaded = FALSE;
    VariantInit(&varFileToLoad);
    varFileToLoad.vt        = VT_BSTR;
    varFileToLoad.bstrVal   = SysAllocString(bstrFileToLoad);

    if(SUCCEEDED(hRes = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument2, (void**)ppXML))){
        LogComment(TEXT("obtained IXMLDOMDocument2 pointer"));
#ifdef UNICODE
        LogComment(TEXT("Trying to load %s"),bstrFileToLoad);
#else
        LogComment(TEXT("Trying to load %S"),bstrFileToLoad);
#endif
        (*ppXML)->put_async(VARIANT_FALSE);
        hRes = (*ppXML)->load(varFileToLoad,&isPass);
        if(SUCCEEDED(hRes) && (isPass == VARIANT_TRUE) && DOMLoaded(*ppXML)){
            LogComment(_T("Loaded file"));
            bCreatedAndLoaded = TRUE;
        }
        else{
            LogFail(_T("Could not load file, load failed with hRes = 0x%08x"),hRes);
        }
    }
    else{
        LogFail(_T("Could not obtain IXMLDOMDocument2 pointer, CoCreateInstance failed with hRes = 0x%08x"),hRes);
    }

    VariantClear(&varFileToLoad);

    return bCreatedAndLoaded;
}

BOOL
CoCreateAndLoadXMLString(
    IXMLDOMDocument2    **ppXML,
    BSTR                bstrXML
    ){
    HRESULT         hRes;
    VARIANT_BOOL    isPass;
    BOOL            bCreatedAndLoaded;
    bCreatedAndLoaded = FALSE;
    if(SUCCEEDED(hRes = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument2, (void**)ppXML))){
        LogComment(TEXT("obtained IXMLDOMDocument2 pointer"));
        (*ppXML)->put_async(VARIANT_FALSE);
        isPass  = VARIANT_FALSE;
#ifdef UNICODE
        LogComment(TEXT("Trying to load XML %s"),bstrXML);
#else
        LogComment(TEXT("Trying to load XML %S"),bstrXML);
#endif
        hRes = (*ppXML)->loadXML(bstrXML,&isPass);
        if(SUCCEEDED(hRes) && (isPass == VARIANT_TRUE) && DOMLoaded(*ppXML)){
            LogComment(_T("Loaded XML"));
            bCreatedAndLoaded = TRUE;
        }
        else{
            LogFail(_T("Could not load XML, loadXML failed with hRes = 0x%08x"),hRes);
        }
    }
    else{
        LogFail(_T("Could not obtain IXMLDOMDocument2 pointer, CoCreateInstance failed with hRes = 0x%08x"),hRes);
    }
    return bCreatedAndLoaded;
}

BOOL
CoCreateAndLoadRemoteBooks(
    IXMLDOMDocument2    **ppXML
    ){
    BSTR    bstrFileToLoad = SysAllocString(L"http://alotweb01/msxml/msxml50/tests/Xpath/XMLFiles/books.xml");
    BOOL    bCreatedAndLoaded = FALSE;
    bCreatedAndLoaded = CoCreateAndLoad(ppXML,bstrFileToLoad);
    SysFreeString(bstrFileToLoad);
    return bCreatedAndLoaded;
}

BOOL
CoCreateAndLoadLocalBooks(
    IXMLDOMDocument2    **ppXML
    ){
    BOOL bCreatedAndLoaded  = FALSE;
    //check if the file exists
    if ( GetFileAttributes( XML_FILEPATH ) != -1 ){
        BSTR bstrFileToLoad = SysAllocString(XML_FILEPATH);
        bCreatedAndLoaded = CoCreateAndLoad(ppXML,bstrFileToLoad);
        SysFreeString(bstrFileToLoad);
    }
    else
    {
        LogWarn1(TEXT("Missing File: %s"), XML_FILEPATH);
    }
    return bCreatedAndLoaded;
}

BOOL
CoCreateAndLoadBooks(
    IXMLDOMDocument2    **ppXML
    ){
    if(!ppXML)
        return FALSE;

    return CoCreateAndLoadLocalBooks(ppXML) ? TRUE : CoCreateAndLoadRemoteBooks(ppXML);
}

double
CalcCheckSum(
    BSTR    bstrVal
    ){
    long    lCtr        = 0,
            lLen        = 0;
    double  dblCheckSum = 0.0;
    if(!bstrVal)
        return dblCheckSum;
    lLen    = wcslen(bstrVal)*2;
    for(lCtr = 0;lCtr < lLen; lCtr++){
        dblCheckSum += ((char *)bstrVal)[lCtr]/(double)(lCtr+1);
    }
    return dblCheckSum;
}

void
ConvertToString(
    IXMLDOMNodeList *pNodeList,
    BSTR            *pbstrNodeList
    ){
    IXMLDOMNode *pNode          = NULL;
    WCHAR       *pwszNodeList   = NULL;
    WCHAR       *pwszTemp       = NULL;
    size_t      size            = 0;
    BSTR        bstrTemp        = NULL;
    LONG        len             = 0;
    int         nExtra          = 6,
                nNodes          = 0,
                nOldLen         = 0,
                nNodeLen        = 0,
                nTotalLen       = 0,
                nSize           = ONEKB;

    *pbstrNodeList  = NULL;

    if(!pNodeList || !pbstrNodeList){
        LogFail(_T("ConvertToString: got invalid arguments"));
        return;
    }

    pwszNodeList    = (WCHAR *)malloc((size_t)(nSize * sizeof(WCHAR)));
    if(!pwszNodeList){
        LogFail(_T("ConvertToString: alloc fail"));
        return;
    }
    pNodeList->get_length(&len);
    swprintf(pwszNodeList,L"len = %ld\n",len);
    /*
    logInfo(ECHO,TEXT("ConvertToString: len = %ld"),len);
    while(SUCCEEDED(pNodeList->nextNode(&pNode)) && pNode){
        nNodes++;
        pNode->Release();
        pNode   = NULL;
    }
    logInfo(ECHO,TEXT("ConvertToString: nNodes = %ld"),nNodes);
    */
    pNodeList->reset();
    while(SUCCEEDED(pNodeList->nextNode(&pNode)) && pNode){
        pNode->get_xml(&bstrTemp);
        if(bstrTemp && (wcscmp(bstrTemp,L"") != 0)){
            nOldLen     = wcslen(pwszNodeList);
            nNodeLen    = wcslen(bstrTemp);
            nTotalLen   = nOldLen+nNodeLen+nExtra;
            if(nTotalLen >= nSize){
                pwszTemp    = (WCHAR *)realloc(pwszNodeList,(size_t)((nTotalLen+ONEKB)*sizeof(WCHAR)));
                if(!pwszTemp){
                    break;
                }
                else{
                    pwszNodeList    = pwszTemp;
                    nSize           = nTotalLen+ONEKB;
                    wcscat(pwszNodeList,L"Node:");
                    wcscat(pwszNodeList,bstrTemp);
                    wcscat(pwszNodeList,L"\n");
                }           
            }
            else{
                wcscat(pwszNodeList,L"Node:");
                wcscat(pwszNodeList,bstrTemp);
                wcscat(pwszNodeList,L"\n");
            }
        }
        pNode->Release();
        pNode   = NULL;
        SysFreeString(bstrTemp);
    }

    *pbstrNodeList  = SysAllocString(pwszNodeList);

    free(pwszNodeList);    
}

void
SetSelectionLanguage(
    IXMLDOMDocument2    *pXML,
    WCHAR               *pwszLanguage
    ){
    VARIANT varTemp;
    BSTR    bstrPropName = SysAllocString(L"SelectionLanguage");
    if(!pXML || !pwszLanguage)
        return;
    VariantInit(&varTemp);
    varTemp.vt      = VT_BSTR;
    varTemp.bstrVal = SysAllocString(pwszLanguage);
    pXML->setProperty(bstrPropName,varTemp);
    VariantClear(&varTemp);
    SysFreeString(bstrPropName);
}

UINT
DoXPathIteration
    (
    void
    ){
    int                 nCases          = 0;
    int                 nPass           = 0;
    double              dblCheckSum     = 0.0;
    IXMLDOMDocument2    *pXML           = NULL;
    IXMLDOMElement      *pElem          = NULL;
    IXMLDOMNodeList     *pNodeList      = NULL;
    BSTR                bstrQuery       = NULL,
                        bstrNodeList    = NULL;
    WCHAR               wszTemp[1024];
    HRESULT             hRes;
    LPVARIATION         lpVariation     = g_lpXPathVariation;

    if(CoCreateAndLoadBooks(&pXML) && (NULL != pXML)){
        SetSelectionLanguage(pXML,L"XPath");
        for(nCases=0;nCases < g_nXPathVariation;nCases++){
            LogComment(_T("Trying to get the documentElement"));
            hRes    = pXML->get_documentElement(&pElem);
            if(SUCCEEDED(hRes) && (NULL != pElem)){
                LogComment(_T("Obtained the documentElement"));
#ifdef UNICODE
                LogComment(_T("Trying to apply the XPath query %s"),lpVariation->lpwszQuery);
#else
                LogComment(_T("Trying to apply the XPath query %S"),lpVariation->lpwszQuery);
#endif
                bstrQuery = SysAllocString(lpVariation->lpwszQuery);
                hRes = pElem->selectNodes(bstrQuery,&pNodeList);
                SysFreeString(bstrQuery);
                pElem->Release();
                pElem   = NULL;
            }
            else{
                LogFail(_T("get_documentElement failed with hRes = 0x%08x"),hRes);
            }
            if(SUCCEEDED(hRes) && (NULL != pNodeList)){
                LogComment(_T("Applied the XPath query"));
                ConvertToString(pNodeList,&bstrNodeList);
                dblCheckSum = CalcCheckSum(bstrNodeList);
                swprintf(wszTemp,L"%0.11f",dblCheckSum);
                if(wcscmp(wszTemp,lpVariation->lpwszChecksum) == 0){
                    nPass++;
                    LogComment(_T("We were able to obtain the expected checksum of %s"),lpVariation->lpwszChecksum);
                }
                else{
                    LogFail(_T("Could not obtain the expected checksum"));
#ifdef UNICODE
                    LogFail(_T("Computed CheckSum = %s, Expected Checksum = %s"),wszTemp,lpVariation->lpwszChecksum);
#else
                    LogFail(_T("Computed CheckSum = %S, Expected Checksum = %S"),wszTemp,lpVariation->lpwszChecksum);
#endif
                }
                SysFreeString(bstrNodeList);
            }
            else{
                LogFail(_T("selectNodes failed with hRes = 0x%08x"),hRes);
            }
            if(pNodeList){
                pNodeList->Release();
                pNodeList   = NULL;
            }
            lpVariation++;
        }
        pXML->Release();
        pXML = NULL;
    }
    else{
        LogFail(_T("CoCreateAndLoadBooks returned FALSE"));
    }
    return (nPass == g_nXPathVariation) ? CESTRESS_PASS : CESTRESS_FAIL;
}


UINT
DoXQLIteration
    (
    void
    ){
    int                 nCases          = 0;
    int                 nPass           = 0;
    double              dblCheckSum     = 0.0;
    IXMLDOMDocument2    *pXML           = NULL;
    IXMLDOMElement      *pElem          = NULL;
    IXMLDOMNodeList     *pNodeList      = NULL;
    BSTR                bstrQuery       = NULL,
                        bstrNodeList    = NULL;
    WCHAR               wszTemp[1024];
    HRESULT             hRes;
    LPVARIATION         lpVariation     = g_lpXQLVariation;

    if(CoCreateAndLoadBooks(&pXML) && (NULL != pXML)){
        for(nCases=0;nCases < g_nXQLVariation;nCases++){
            //Apply the query
            LogComment(_T("Trying to get the documentElement"));
            hRes    = pXML->get_documentElement(&pElem);
            if(SUCCEEDED(hRes) && (NULL != pElem)){
                LogComment(_T("Obtained the documentElement"));
#ifdef UNICODE
                LogComment(_T("Trying to apply the XQL query %s"),lpVariation->lpwszQuery);
#else
                LogComment(_T("Trying to apply the XQL query %S"),lpVariation->lpwszQuery);
#endif
                bstrQuery = SysAllocString(lpVariation->lpwszQuery);
                hRes = pElem->selectNodes(bstrQuery,&pNodeList);
                SysFreeString(bstrQuery);
                pElem->Release();
                pElem   = NULL;
            }
            else{
                LogFail(_T("get_documentElement failed with hRes = 0x%08x"),hRes);
            }
            if(SUCCEEDED(hRes) && (NULL != pNodeList)){
                LogComment(_T("Applied the XPath query"));
                ConvertToString(pNodeList,&bstrNodeList);
                dblCheckSum = CalcCheckSum(bstrNodeList);
                swprintf(wszTemp,L"%0.11f",dblCheckSum);
                if(wcscmp(wszTemp,lpVariation->lpwszChecksum) == 0){
                    nPass++;
                    LogComment(_T("We were able to obtain the expected checksum of %s"),lpVariation->lpwszChecksum);
                }
                else{
                    LogFail(_T("Could not obtain the expected checksum"));
#ifdef UNICODE
                    LogFail(_T("Computed CheckSum = %s, Expected Checksum = %s"),wszTemp,lpVariation->lpwszChecksum);
#else
                    LogFail(_T("Computed CheckSum = %S, Expected Checksum = %S"),wszTemp,lpVariation->lpwszChecksum);
#endif
                }
                SysFreeString(bstrNodeList);
            }
            else{
                LogFail(_T("selectNodes failed with hRes = 0x%08x"),hRes);
            }
            if(pNodeList){
                pNodeList->Release();
                pNodeList   = NULL;
            }
            lpVariation++;
        }
        pXML->Release();
        pXML = NULL;
    }
    else{
        LogFail(_T("CoCreateAndLoadBooks returned FALSE"));
    }
    return (nPass == g_nXQLVariation) ? CESTRESS_PASS : CESTRESS_FAIL;
}

UINT
DoXSLTIteration
    (
    void
    ){
    int                 nCases          = 0;
    int                 nPass           = 0;
    double              dblCheckSum     = 0.0;
    IXMLDOMDocument2    *pXML           = NULL;
    IXMLDOMDocument2    *pXSL           = NULL;
    IXMLDOMElement      *pElem          = NULL;
    IXMLDOMNodeList     *pNodeList      = NULL;
    BSTR                bstrQuery       = NULL,
                        bstrNodeList    = NULL;
    WCHAR               wszTemp[1024];
    HRESULT             hRes;
    LPVARIATION         lpVariation     = g_lpXSLTVariation;

    for(nCases=0;nCases < g_nXSLTVariation;nCases++){
        if(CoCreateAndLoadBooks(&pXML) && (NULL != pXML)){
            bstrQuery = SysAllocString(lpVariation->lpwszQuery);
            if(CoCreateAndLoadXMLString(&pXSL,bstrQuery) && (NULL != pXSL)){
#ifdef UNICODE
                LogComment(_T("Trying to apply the XSLT %s"),lpVariation->lpwszQuery);
#else
                LogComment(_T("Trying to apply the XSLT %S"),lpVariation->lpwszQuery);
#endif
                hRes = pXML->transformNode(pXSL,&bstrNodeList);
                if(SUCCEEDED(hRes)){
                    LogComment(_T("Applied the XSLT"));
                    dblCheckSum = CalcCheckSum(bstrNodeList);
                    swprintf(wszTemp,L"%0.11f",dblCheckSum);
                    if(wcscmp(wszTemp,lpVariation->lpwszChecksum) == 0){
                        nPass++;
                        LogComment(_T("We were able to obtain the expected checksum of %s"),lpVariation->lpwszChecksum);
                    }
                    else{
                        LogFail(_T("Could not obtain the expected checksum"));
    #ifdef UNICODE
                        LogFail(_T("Computed CheckSum = %s, Expected Checksum = %s"),wszTemp,lpVariation->lpwszChecksum);
    #else
                        LogFail(_T("Computed CheckSum = %S, Expected Checksum = %S"),wszTemp,lpVariation->lpwszChecksum);
    #endif
                    }
                    SysFreeString(bstrNodeList);
                }
                else{
                    LogFail(_T("transformNode failed with hRes = 0x%08x"),hRes);
                }
                pXSL->Release();
                pXSL = NULL;
            }
            else{
                LogFail(_T("CoCreateAndLoadXMLString returned FALSE"));
            }
            SysFreeString(bstrQuery);
            pXML->Release();
            pXML = NULL;
        }
        else{
            LogFail(_T("CoCreateAndLoadBooks returned FALSE"));
        }
        lpVariation++;
    }
    return (nPass == g_nXSLTVariation) ? CESTRESS_PASS : CESTRESS_FAIL;
}

///////////////////////////////////////////////
//
// Worker thread to do the acutal testing
//
DWORD
WINAPI
TestThreadProc
    (
    LPVOID  pv
    ){
    HRESULT         hRes;
    UINT            ret;
    int             nIterations = 0;
    STRESS_RESULTS  res;
    STRESS_RESULTS  *pRes = (STRESS_RESULTS*)pv; // get a ptr to the module results struct

    ZeroMemory((void*)&res,sizeof(STRESS_RESULTS));

    if(FAILED(hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED))){
        LogFail (_T("CoInitializeEx = 0x%08x"),hRes);
    }

    // Do our testing until time's up is signaled
    while(WAIT_OBJECT_0 != WaitForSingleObject(g_hTimesUp,0)){

        nIterations++;

        switch(nIterations%3){
        case 0:
            ret = DoXPathIteration();
            break;
        case 1:
            ret = DoXQLIteration();
            break;
        case 2:
            ret = DoXSLTIteration();
            break;
        default:
            break;
        }

        if (ret == CESTRESS_ABORT)
            return CESTRESS_ABORT;

        RecordIterationResults(&res,ret);
    }

    // Add this thread's results to the module tally
    EnterCriticalSection(&g_csResults);

    AddResults(pRes,&res);

    LeaveCriticalSection(&g_csResults);

    CoUninitialize();

    return 0;
}


