//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:     genenum.cpp
//
//  Contents:     implementation of CEnumeratorTest object
//                This is the object that does all of the testing.
//
//  Classes:
//
//  Functions:
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//--------------------------------------------------------------------------

#include "headers.hxx"
#pragma hdrstop

//+-------------------------------------------------------------------------
//
//  Member:      CEnumeratorTest::CEnumeratorTest
//
//  Synopsis:    constructor
//
//  Arguments:   none
//
//  Returns:
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

CEnumeratorTest::CEnumeratorTest()
{
    m_pEnumTest      = NULL;
    m_ElementSize    = 0;
    m_ElementCount   = -1;
}

//+-------------------------------------------------------------------------
//
//  Member:     CEnumeratorTest::CEnumeratorTest
//
//  Synopsis:   constructor
//
//  Arguments:  [enumtest] --      The enumerator object to be tested
//              [elementsize] --   The size of one element from next
//              [elementcount] --  The number of elements expected to in
//                                 the enumerator. 0 if unknown.
//
//  Returns:
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

CEnumeratorTest::CEnumeratorTest(IGenEnum * enumtest, size_t elementsize, LONG elementcount)
{
    m_pEnumTest    = enumtest;
    m_ElementSize    = elementsize;
    m_ElementCount    = elementcount;
}


//+-------------------------------------------------------------------------
//
//  Function:   CEnumeratorTest::GetNext
//
//  Synopsis:   Internal Next Implementation. Does some basic checks on the
//              return values.
//
//  Effects:
//
//  Arguments:  [celt] --         the number of items to fetch
//              [pceltFetched] -- the number of items fetched
//              [phresult] --     the return from next
//
//  Requires:
//
//  Returns:    True if the basic tests passed, false if they didn't
//              The result of the next call itself is passed in param 3.
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:  Checks:
//                  That if s_ok is returned celt and pceltFetched are ==
//                  If a verify is provided it is called
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

BOOL CEnumeratorTest::GetNext(    ULONG   celt,
        ULONG*  pceltFetched,
        HRESULT* phresult
        )
{
    void*    prgelt;
    ULONG    ul;
    BOOL     fRet = TRUE;

    //
    // Allocate memory for the return elements
    //

    prgelt = new char[m_ElementSize * celt];

    if (prgelt == NULL)
    {
        printf("IEnumX::GetNext out of memory.\r\n");

        return(FALSE);
    }

    //
    // Call next
    //

    *phresult = m_pEnumTest->Next(celt, prgelt, pceltFetched);

    //
    // If the return result is S_OK make sure the numbers match
    //

    if (*phresult == S_OK)
    {
        if ((pceltFetched) && (celt != *pceltFetched))
        {
            printf("IEnumX::Next returned S_OK but celt"
                    " and pceltFetch mismatch.\r\n");

            fRet = FALSE;
        }
    }

    //
    // If false is returned then make sure celt is less than
    // the number actually fetched
    //

    if (*phresult == S_FALSE)
    {
        if ((pceltFetched) && (celt < *pceltFetched))
        {
            printf("IEnumX::Next return S_FALSE but celt is"
                   " less than pceltFetch.\r\n");

            fRet = FALSE;
        }
    }

    //
    // Call verify to make sure the elements are ok.
    //

    if ((*phresult == S_OK) || (*phresult == S_FALSE))
    {
        //
        // If we got S_FALSE back set celt to the number of elements
        // returned in pceltFetched.  If the user gave NULL for
        // pceltFetched and we got S_FALSE back then celt can only be
        // zero.
        //

        if (*phresult == S_FALSE)
        {
            if (pceltFetched)
            {
                celt = *pceltFetched;
            }
            else
            {
                celt = 0;
            }
        }

        //
        // loop through every returned element
        //

        for (ul=0; ul <= celt ; ul++)
        {
            if ((fRet == TRUE) &&
                (Verify(((char *)prgelt) + (ul * m_ElementSize)) == FALSE))
            {
                printf("Data element %d returned by IEnumX::Next is bad.\r\n", ul);

                fRet = FALSE;

                //
                // we keep looping anyway just to
                // free up resources.
                //
            }

            //
            // If the user supplied a cleanup function there is additional
            // memory that needs to be freed
            //
            // Math: cast prgelt to char* to it a one byte size and then scale
            // it by the index * the element size
            //

            Cleanup(((char *)prgelt) + (ul * m_ElementSize));

        }
    }

    delete prgelt;

    return fRet;
}

//+-------------------------------------------------------------------------
//
//  Method:     CEnumeratorTest::TestNext
//
//  Synopsis:   Test the next enumerator methods
//
//  Effects:
//
//  Arguments:    None.
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:      BUGBUG: This function should really be broken down into
//              smaller function.
//              Also, the return mechanism is unwieldy.
//
//--------------------------------------------------------------------------

HRESULT CEnumeratorTest::TestNext(void)
{
    ULONG    celtFetched;
    LONG     lInternalCount = 0;
    HRESULT  hresult;
    ULONG    i;
    void*    prgelt;

    //
    // First we want to count the element by doing a next on each one.
    //

    do {
        if (!GetNext(1, &celtFetched, &hresult))
        {
            return(E_FAIL);
        }
        if (hresult == S_OK)
        {
            lInternalCount++;
        }

    } while ( hresult == S_OK );

    //
    // If the user passed in an amount make sure it matches what we got
    //

    if ((m_ElementCount != -1) && (lInternalCount != m_ElementCount))
    {
        printf("IEnumX: enumerated count and passed count do not match!\r\n");

        return(E_FAIL);
    }
    else if (m_ElementCount == -1)
    {
        //
        //  If the user didn't pass in the element count let's set it here.
        //

        m_ElementCount = lInternalCount;
    }

    hresult = m_pEnumTest->Reset();

    if (hresult != S_OK)
    {
        printf("IEnumnX: Reset failed (%lx)\r\n", hresult );

        return(E_FAIL);
    }


    //
    // Make sure we fail on ...Next(celt>1, ...,NULL)
    //

    if (GetNext(2, NULL, &hresult))
    {
        if (SUCCEEDED(hresult))
        {
            printf("IEnumX: celt>1 pceltFetched==NULL returned success\r\n");

            return(E_FAIL);
        }
    }
    else
    {
        return(E_FAIL);
    }


    //
    // This next test will call next getting more each time
    //

    for (i = 1; i < (ULONG)m_ElementCount; i++)
    {
        hresult = m_pEnumTest->Reset();

        if (hresult != S_OK)
        {
            printf("IEnumnX: Reset failed (%lx)\r\n", hresult );

            return(E_FAIL);
        }

        if (!GetNext(i, &celtFetched, &hresult))
        {
            return(E_FAIL);
        }

        if ((hresult != S_OK) || (celtFetched != i))
        {
            printf("IEnumX: next/reset test failed!\r\n");

            return(E_FAIL);
        }
    }


    //
    // Now get more elements than we were supposed to
    // This should return S_FALSE with the max number in the number fetched
    //

    hresult = m_pEnumTest->Reset();

    if (hresult != S_OK)
    {
        printf("IEnumX: Reset failed (%lx)\r\n", hresult );

        return(E_FAIL);
    }

    if (!GetNext(m_ElementCount + 1, &celtFetched, &hresult))
    {
        return(E_FAIL);
    }

    if ((hresult != S_FALSE) || (lInternalCount != m_ElementCount))
    {
        printf("IEnumX: next/reset test failed!\r\n");

        return(E_FAIL);
    }

    //
    // Now verifyall.  We do it here after the object has been worked on a bit
    // since it is more likely to fail at this point
    //

    hresult = m_pEnumTest->Reset();

    if (hresult != S_OK)
    {
        printf("IEnumX: Reset failed (%lx)\r\n", hresult );

        return(E_FAIL);
    }

    //
    // Allocate memory for the return elements
    //

    prgelt = new char[m_ElementSize * m_ElementCount];

    if (prgelt == NULL)
    {
        printf("IEnumX: verifyall new failed\r\n");

        return(E_OUTOFMEMORY);
    }

    hresult = m_pEnumTest->Next(m_ElementCount, prgelt, &celtFetched);

    if ((hresult != S_OK) || (celtFetched != (ULONG)m_ElementCount))
    {
        printf("IEnumX: verifyall test: next failed (%lx)\r\n", hresult );
        delete prgelt;

        return(E_FAIL);
    }

    if (VerifyAll(prgelt, m_ElementCount) == FALSE)
    {
        printf("IEnumX: verifyall failed (%lx)\r\n", hresult );

        delete prgelt;

        return(E_FAIL);
    }

    delete prgelt;

    return(S_OK);
}

//+-------------------------------------------------------------------------
//
//  Method:     CEnumeratorTest::TestSkip
//
//  Synopsis:    This function calls all the tests
//
//  Effects:
//
//  Arguments:    None
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

HRESULT CEnumeratorTest::TestSkip(void)
{
    LONG        i;
    HRESULT        hresult;
    ULONG        celtFetched;

    //
    // Make sure we call TestNext to set the element count
    //

    if (m_ElementCount == -1)
    {
        TestNext();
    }

    //
    // Call Skip, reset and try to get one element
    //

    for (i = 0; i < (LONG)m_ElementCount; i++)
    {
        hresult = m_pEnumTest->Reset();

        if (hresult != S_OK)
        {
            printf("IEnumnX: Reset failed (%lx)\r\n", hresult );

            return(E_FAIL);
        }

        hresult = m_pEnumTest->Skip(i);

        if (hresult != S_OK)
        {
            printf("IEnumnX: Skip failed (%lx)\r\n", hresult );

            return(E_FAIL);
        }

        //
        //  Now one element to provide some check that the skip worked
        //

        if (!GetNext(1, &celtFetched, &hresult))
        {
            return(E_FAIL);
        }

        if (hresult != S_OK)
        {
            return(E_FAIL);
        }
    }

    //
    //  Reset the enumerator before we leave
    //

    hresult = m_pEnumTest->Reset();

    if (hresult != S_OK)
    {
        printf("IEnumnX: Reset failed (%lx)\r\n", hresult );
        return(E_FAIL);
    }

    return(S_OK);

}

//+-------------------------------------------------------------------------
//
//  Method:     CEnumeratorTest::TestRelease
//
//  Synopsis:    This function calls all the tests
//
//  Effects:
//
//  Arguments:    None
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

HRESULT CEnumeratorTest::TestRelease(void)
{
    return(S_OK);
}

//+-------------------------------------------------------------------------
//
//  Method:     CEnumeratorTest::TestClone
//
//  Synopsis:    This function calls all the tests
//
//  Effects:
//
//  Arguments:    None
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

HRESULT CEnumeratorTest::TestClone(void)
{
    return(S_OK);
}

//+-------------------------------------------------------------------------
//
//  Method:     CEnumeratorTest::TestAll
//
//  Synopsis:    This function calls all the tests
//
//  Effects:
//
//  Arguments:    None
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

HRESULT CEnumeratorTest::TestAll(void)
{
    HRESULT    hresult;

    hresult = TestNext();

    if (hresult == S_OK)
    {
        hresult = TestSkip();
    }

    if (hresult == S_OK)
    {
        hresult = TestClone();
    }

    if (hresult == S_OK)
    {
        hresult = TestRelease();
    }

    return(hresult);
}



//+-------------------------------------------------------------------------
//
//  Method:     CEnumeratorTest::VerifyAll
//
//  Synopsis:   Verify entire array of returned results.
//
//  Arguments:  None
//
//  Returns:    BOOL
//
//  Algorithm:  Just default to saying everything is ok
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

BOOL CEnumeratorTest::VerifyAll(void *pv, LONG cl)
{
        return TRUE;
}


//+-------------------------------------------------------------------------
//
//  Method:     CEnumeratorTest::Verify
//
//  Synopsis:   Verify one element
//
//  Arguments:  None
//
//  Returns:    BOOL
//
//  Algorithm:  Just default to saying everything is ok
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//  Notes:
//
//--------------------------------------------------------------------------

BOOL CEnumeratorTest::Verify(void *pv)
{
        return TRUE;
}




//+-------------------------------------------------------------------------
//
//  Method:     CEnumeratorTest::Cleanup
//
//  Synopsis:   Default implementation of cleanup
//
//  Arguments:  [pv] - pointer to entry enumerated
//
//  Algorithm:  If there is nothing special to free this implementation
//              can be used.
//
//  History:    dd-mmm-yy Author    Comment
//              24-May-94 kennethm  author
//
//--------------------------------------------------------------------------

void  CEnumeratorTest::Cleanup(void *pv)
{
    return;
}