VirtualBox

Changeset 99392 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Apr 13, 2023 4:48:07 PM (23 months ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
156830
Message:

Guest Control: Completely revamped / overhauled the (now legacy) stream output parsing code and added lots of documentation to it. This way it should be a lot clearer what it's supposed to be doing.

Also, this now should fix some nasty bugs in that area we had in the past especially with some Linux guests (i.e. OL6), which sometimes send output data in a very unsteady manner.

Also overhauled the testcases while at it.

Luckily, this is all host-based code, so older Guest Additions also will benefit from this.

Location:
trunk/src/VBox/Main
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h

    r99262 r99392  
    973973
    974974    GuestToolboxStreamValue(void) { }
    975     GuestToolboxStreamValue(const char *pszValue)
    976         : mValue(pszValue) {}
     975    GuestToolboxStreamValue(const char *pszValue, size_t cwcValue = RTSTR_MAX)
     976        : mValue(pszValue, cwcValue) {}
    977977
    978978    GuestToolboxStreamValue(const GuestToolboxStreamValue& aThat)
     
    996996typedef std::map < Utf8Str, GuestToolboxStreamValue >::const_iterator GuestCtrlStreamPairMapIterConst;
    997997
     998class GuestToolboxStream;
     999
    9981000/**
    9991001 * Class representing a block of stream pairs (key=value). Each block in a raw guest
     
    10011003 * end of a guest stream is marked by "\0\0\0\0".
    10021004 *
     1005 * An empty stream block will be treated as being incomplete.
     1006 *
    10031007 * Only used for the busybox-like toolbox commands within VBoxService.
    10041008 * Deprecated, do not use anymore.
     
    10061010class GuestToolboxStreamBlock
    10071011{
     1012    friend GuestToolboxStream;
     1013
    10081014public:
    10091015
     
    10291035    int32_t     GetInt32(const char *pszKey, int32_t iDefault = 0) const;
    10301036
    1031     bool        IsEmpty(void) { return mPairs.empty(); }
    1032 
     1037    bool        IsComplete(void) const { return !m_mapPairs.empty() && m_fComplete; }
     1038    bool        IsEmpty(void) const { return m_mapPairs.empty(); }
     1039
     1040    int         SetValueEx(const char *pszKey, size_t cwcKey, const char *pszValue, size_t cwcValue, bool fOverwrite = false);
    10331041    int         SetValue(const char *pszKey, const char *pszValue);
    10341042
    10351043protected:
    10361044
    1037     GuestCtrlStreamPairMap mPairs;
     1045    /** Wheter the stream block is marked as complete.
     1046     *  An empty stream block is considered as incomplete. */
     1047    bool                   m_fComplete;
     1048    /** Map of stream pairs this block contains.*/
     1049    GuestCtrlStreamPairMap m_mapPairs;
    10381050};
    10391051
     
    10431055typedef std::vector< GuestToolboxStreamBlock >::const_iterator GuestCtrlStreamObjectsIterConst;
    10441056
     1057/** Defines a single terminator as a single char. */
     1058#define GUESTTOOLBOX_STRM_TERM                      '\0'
     1059/** Defines a single terminator as a string. */
     1060#define GUESTTOOLBOX_STRM_TERM_STR                  "\0"
     1061/** Defines the termination sequence for a single key/value pair. */
     1062#define GUESTTOOLBOX_STRM_TERM_PAIR_STR             GUESTTOOLBOX_STRM_TERM_STR
     1063/** Defines the termination sequence for a single stream block. */
     1064#define GUESTTOOLBOX_STRM_TERM_BLOCK_STR            GUESTTOOLBOX_STRM_TERM_STR GUESTTOOLBOX_STRM_TERM_STR
     1065/** Defines the termination sequence for the stream. */
     1066#define GUESTTOOLBOX_STRM_TERM_STREAM_STR           GUESTTOOLBOX_STRM_TERM_STR GUESTTOOLBOX_STRM_TERM_STR GUESTTOOLBOX_STRM_TERM_STR GUESTTOOLBOX_STRM_TERM_STR
     1067/** Defines how many consequtive terminators a key/value pair has. */
     1068#define GUESTTOOLBOX_STRM_PAIR_TERM_CNT             1
     1069/** Defines how many consequtive terminators a stream block has. */
     1070#define GUESTTOOLBOX_STRM_BLK_TERM_CNT              2
     1071/** Defines how many consequtive terminators a stream has. */
     1072#define GUESTTOOLBOX_STRM_TERM_CNT                  4
     1073
    10451074/**
    10461075 * Class for parsing machine-readable guest process output by VBoxService'
     
    10681097#endif
    10691098
    1070     size_t GetOffset() { return m_offBuffer; }
    1071 
    1072     size_t GetSize() { return m_cbUsed; }
     1099    size_t GetOffset(void) const { return m_offBuf; }
     1100
     1101    size_t GetSize(void) const { return m_cbUsed; }
     1102
     1103    size_t GetBlocks(void) const { return m_cBlocks; }
    10731104
    10741105    int ParseBlock(GuestToolboxStreamBlock &streamBlock);
     
    10841115    size_t m_cbUsed;
    10851116    /** Current byte offset within the internal stream buffer. */
    1086     size_t m_offBuffer;
     1117    size_t m_offBuf;
    10871118    /** Internal stream buffer. */
    10881119    BYTE  *m_pbBuffer;
     1120    /** How many completed stream blocks already were processed. */
     1121    size_t m_cBlocks;
    10891122};
    10901123
  • trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp

    r99011 r99392  
    535535
    536536GuestToolboxStreamBlock::GuestToolboxStreamBlock(void)
    537 {
    538 
    539 }
     537    : m_fComplete(false) { }
    540538
    541539GuestToolboxStreamBlock::~GuestToolboxStreamBlock()
     
    549547void GuestToolboxStreamBlock::Clear(void)
    550548{
    551     mPairs.clear();
     549    m_fComplete = false;
     550    m_mapPairs.clear();
    552551}
    553552
     
    558557void GuestToolboxStreamBlock::DumpToLog(void) const
    559558{
    560     LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
    561                  this, mPairs.size()));
    562 
    563     for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
    564          it != mPairs.end(); ++it)
     559    LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items, fComplete=%RTbool):\n",
     560                 this, m_mapPairs.size(), m_fComplete));
     561
     562    for (GuestCtrlStreamPairMapIterConst it = m_mapPairs.begin();
     563         it != m_mapPairs.end(); ++it)
    565564    {
    566565        LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
     
    610609size_t GuestToolboxStreamBlock::GetCount(void) const
    611610{
    612     return mPairs.size();
     611    return m_mapPairs.size();
    613612}
    614613
     
    645644    try
    646645    {
    647         GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(pszKey);
    648         if (itPairs != mPairs.end())
     646        GuestCtrlStreamPairMapIterConst itPairs = m_mapPairs.find(pszKey);
     647        if (itPairs != m_mapPairs.end())
    649648            return itPairs->second.mValue.c_str();
    650649    }
     
    711710
    712711/**
     712 * Sets a value to a key or deletes a key by setting a NULL value. Extended version.
     713 *
     714 * @return  VBox status code.
     715 * @param   pszKey              Key name to process.
     716 * @param   cwcKey              Maximum characters of \a pszKey to process.
     717 * @param   pszValue            Value to set. Set NULL for deleting the key.
     718 * @param   cwcValue            Maximum characters of \a pszValue to process.
     719 * @param   fOverwrite          Whether a key can be overwritten with a new value if it already exists. Will assert otherwise.
     720 */
     721int GuestToolboxStreamBlock::SetValueEx(const char *pszKey, size_t cwcKey, const char *pszValue, size_t cwcValue,
     722                                        bool fOverwrite /* = false */)
     723{
     724    AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
     725    AssertReturn(cwcKey, VERR_INVALID_PARAMETER);
     726
     727    int vrc = VINF_SUCCESS;
     728    try
     729    {
     730        Utf8Str const strKey(pszKey, cwcKey);
     731
     732        /* Take a shortcut and prevent crashes on some funny versions
     733         * of STL if map is empty initially. */
     734        if (!m_mapPairs.empty())
     735        {
     736            GuestCtrlStreamPairMapIter it = m_mapPairs.find(strKey);
     737            if (it != m_mapPairs.end())
     738            {
     739                if (pszValue == NULL)
     740                    m_mapPairs.erase(it);
     741                else if (!fOverwrite)
     742                    AssertMsgFailedReturn(("Key '%*s' already exists! Value is '%s'\n", cwcKey, pszKey, m_mapPairs[strKey].mValue.c_str()),
     743                                          VERR_ALREADY_EXISTS);
     744            }
     745        }
     746
     747        if (pszValue)
     748        {
     749            GuestToolboxStreamValue val(pszValue, cwcValue);
     750            Log3Func(("strKey='%s', strValue='%s'\n", strKey.c_str(), val.mValue.c_str()));
     751            m_mapPairs[strKey] = val;
     752        }
     753    }
     754    catch (const std::exception &)
     755    {
     756        /** @todo set vrc?   */
     757    }
     758    return vrc;
     759}
     760
     761/**
    713762 * Sets a value to a key or deletes a key by setting a NULL value.
    714763 *
     
    719768int GuestToolboxStreamBlock::SetValue(const char *pszKey, const char *pszValue)
    720769{
    721     AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
    722 
    723     int vrc = VINF_SUCCESS;
    724     try
    725     {
    726         Utf8Str const strKey(pszKey);
    727 
    728         /* Take a shortcut and prevent crashes on some funny versions
    729          * of STL if map is empty initially. */
    730         if (!mPairs.empty())
    731         {
    732             GuestCtrlStreamPairMapIter it = mPairs.find(strKey);
    733             if (it != mPairs.end())
    734                  mPairs.erase(it);
    735         }
    736 
    737         if (pszValue)
    738         {
    739             GuestToolboxStreamValue val(pszValue);
    740             mPairs[strKey] = val;
    741         }
    742     }
    743     catch (const std::exception &)
    744     {
    745         /** @todo set vrc?   */
    746     }
    747     return vrc;
     770    return SetValueEx(pszKey, RTSTR_MAX, pszValue, RTSTR_MAX);
    748771}
    749772
     
    754777    , m_cbAllocated(0)
    755778    , m_cbUsed(0)
    756     , m_offBuffer(0)
    757     , m_pbBuffer(NULL) { }
     779    , m_offBuf(0)
     780    , m_pbBuffer(NULL)
     781    , m_cBlocks(0) { }
    758782
    759783GuestToolboxStream::~GuestToolboxStream(void)
     
    778802
    779803    /* Rewind the buffer if it's empty. */
    780     size_t     cbInBuf   = m_cbUsed - m_offBuffer;
     804    size_t     cbInBuf   = m_cbUsed - m_offBuf;
    781805    bool const fAddToSet = cbInBuf == 0;
    782806    if (fAddToSet)
    783         m_cbUsed = m_offBuffer = 0;
     807        m_cbUsed = m_offBuf = 0;
    784808
    785809    /* Try and see if we can simply append the data. */
     
    792816    {
    793817        /* Move any buffered data to the front. */
    794         cbInBuf = m_cbUsed - m_offBuffer;
     818        cbInBuf = m_cbUsed - m_offBuf;
    795819        if (cbInBuf == 0)
    796             m_cbUsed = m_offBuffer = 0;
    797         else if (m_offBuffer) /* Do we have something to move? */
    798         {
    799             memmove(m_pbBuffer, &m_pbBuffer[m_offBuffer], cbInBuf);
     820            m_cbUsed = m_offBuf = 0;
     821        else if (m_offBuf) /* Do we have something to move? */
     822        {
     823            memmove(m_pbBuffer, &m_pbBuffer[m_offBuf], cbInBuf);
    800824            m_cbUsed = cbInBuf;
    801             m_offBuffer = 0;
     825            m_offBuf = 0;
    802826        }
    803827
     
    851875    m_cbAllocated = 0;
    852876    m_cbUsed = 0;
    853     m_offBuffer = 0;
     877    m_offBuf = 0;
     878    m_cBlocks = 0;
    854879}
    855880
     
    864889{
    865890    LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
    866                  m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuffer, pszFile));
     891                 m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuf, pszFile));
    867892
    868893    RTFILE hFile;
     
    874899    }
    875900}
    876 #endif
    877 
    878 /**
    879  * Tries to parse the next upcoming pair block within the internal
    880  * buffer.
    881  *
    882  * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
    883  * completely parsed already.
    884  *
    885  * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
    886  * stored in stream block) but still contains incomplete (unterminated)
    887  * data.
    888  *
    889  * Returns VINF_SUCCESS if current block was parsed until the next upcoming
    890  * block (with zero or more pairs stored in stream block).
     901#endif /* DEBUG */
     902
     903/**
     904 * Tries to parse the next upcoming pair block within the internal buffer.
     905 *
     906 * Parsing behavior:
     907 * - A stream can contain one or multiple blocks and is terminated by four (4) "\0".
     908 * - A block (or "object") contains one or multiple key=value pairs and is terminated with two (2) "\0".
     909 * - Each key=value pair is terminated by a single (1) "\0".
     910 *
     911 * As new data can arrive at a later time eventually completing a pair / block / stream,
     912 * the algorithm needs to be careful not intepreting its current data too early. So only skip termination
     913 * sequences if we really know that the termination sequence is complete. See comments down below.
     914 *
     915 * No locking done.
    891916 *
    892917 * @return VBox status code.
    893  * @param streamBlock               Reference to guest stream block to fill.
     918 * @retval VINF_EOF if the stream reached its end.
     919 * @param  streamBlock              Reference to guest stream block to fill
    894920 */
    895921int GuestToolboxStream::ParseBlock(GuestToolboxStreamBlock &streamBlock)
    896922{
     923    AssertMsgReturn(streamBlock.m_fComplete == false, ("Block object already marked as being completed\n"), VERR_WRONG_ORDER);
     924
    897925    if (   !m_pbBuffer
    898926        || !m_cbUsed)
    899         return VERR_NO_DATA;
    900 
    901     AssertReturn(m_offBuffer <= m_cbUsed, VERR_INVALID_PARAMETER);
    902     if (m_offBuffer == m_cbUsed)
    903         return VERR_NO_DATA;
    904 
    905     int          vrc       = VINF_SUCCESS;
    906     char * const pszOff    = (char *)&m_pbBuffer[m_offBuffer];
    907     size_t       cbLeft    = m_offBuffer < m_cbUsed ? m_cbUsed - m_offBuffer : 0;
    908     char        *pszStart  = pszOff;
    909     while (cbLeft > 0 && *pszStart != '\0')
    910     {
    911         char * const pszPairEnd = RTStrEnd(pszStart, cbLeft);
    912         if (!pszPairEnd)
    913         {
    914             vrc = VERR_MORE_DATA;
    915             break;
    916         }
    917         size_t const cchPair = (size_t)(pszPairEnd - pszStart);
    918         char *pszSep = (char *)memchr(pszStart, '=', cchPair);
    919         if (pszSep)
    920             *pszSep = '\0'; /* Terminate the separator so that we can  use pszStart as our key from now on. */
    921         else
    922         {
    923             vrc = VERR_MORE_DATA; /** @todo r=bird: This is BOGUS because we'll be stuck here if the guest feeds us bad data! */
    924             break;
    925         }
    926         char const * const pszVal = pszSep + 1;
    927 
    928         vrc = streamBlock.SetValue(pszStart, pszVal);
     927        return VINF_EOF;
     928
     929    AssertReturn(m_offBuf <= m_cbUsed, VERR_INVALID_PARAMETER);
     930    if (m_offBuf == m_cbUsed)
     931        return VINF_EOF;
     932
     933    char * const  pszStart = (char *)&m_pbBuffer[m_offBuf];
     934
     935    size_t        cbLeftParsed    = m_offBuf < m_cbUsed ? m_cbUsed - m_offBuf : 0;
     936    size_t        cbLeftLookAhead = cbLeftParsed;
     937
     938    char         *pszLookAhead = pszStart; /* Look ahead pointer to count terminators. */
     939    char         *pszParsed    = pszStart; /* Points to data considered as being parsed already. */
     940
     941    Log4Func(("Current @ %zu/%zu:\n%.*RhXd\n", m_offBuf, m_cbUsed, RT_MIN(cbLeftParsed, _1K), pszStart));
     942
     943    size_t cTerm = 0;
     944
     945    /*
     946     * We have to be careful when handling single terminators ('\0') here, as we might not know yet
     947     * if it's part of a multi-terminator seqeuence.
     948     *
     949     * So handle and skip those *only* when we hit a non-terminator char again.
     950     */
     951    int vrc = VINF_SUCCESS;
     952    while (cbLeftLookAhead)
     953    {
     954        /* Count consequtive terminators. */
     955        if (*pszLookAhead == GUESTTOOLBOX_STRM_TERM)
     956        {
     957            cTerm++;
     958            pszLookAhead++;
     959            cbLeftLookAhead--;
     960            continue;
     961        }
     962
     963        pszParsed    = pszLookAhead;
     964        cbLeftParsed = cbLeftLookAhead;
     965
     966        /* We hit a non-terminator (again); now interpret where we are, and
     967         * bail out if we need to. */
     968        if (cTerm >= 2)
     969        {
     970            Log2Func(("Hit end of termination sequence (%zu)\n", cTerm));
     971            break;
     972        }
     973
     974        cTerm = 0; /* Reset consequtive counter. */
     975
     976        char * const pszPairEnd = RTStrEnd(pszParsed, cbLeftParsed);
     977        if (!pszPairEnd) /* No zero terminator found (yet), try next time. */
     978            break;
     979
     980        Log3Func(("Pair '%s' (%u)\n", pszParsed, strlen(pszParsed)));
     981
     982        Assert(pszPairEnd != pszParsed);
     983        size_t const cbPair = (size_t)(pszPairEnd - pszParsed);
     984        Assert(cbPair);
     985        const char  *pszSep = (const char *)memchr(pszParsed, '=', cbPair);
     986        if (!pszSep) /* No separator found (yet), try next time. */
     987            break;
     988
     989        /* Skip the separator so that pszSep points to the actual value. */
     990        pszSep++;
     991
     992        char const * const pszKey = pszParsed;
     993        char const * const pszVal = pszSep;
     994
     995        vrc = streamBlock.SetValueEx(pszKey, pszSep - pszKey - 1, pszVal, pszPairEnd - pszVal);
    929996        if (RT_FAILURE(vrc))
    930997            return vrc;
    931998
    932         /* Next pair. */
    933         pszStart = pszPairEnd + 1;
    934         cbLeft  -= cchPair    + 1;
    935     }
    936 
    937     /* If we did not do any movement but we have stuff left
    938      * in our buffer just skip the current termination so that
    939      * we can try next time. */
    940     size_t cbDistance = (pszStart - pszOff);
    941     if (   !cbDistance
    942         && cbLeft > 0
    943         && *pszStart == '\0'
    944         && m_offBuffer < m_cbUsed)
    945         cbDistance++;
    946     m_offBuffer += cbDistance;
     999        if (cbPair >= cbLeftParsed)
     1000            break;
     1001
     1002        /* Accounting for next iteration. */
     1003        pszParsed       = pszPairEnd;
     1004        Assert(cbLeftParsed >= cbPair);
     1005        cbLeftParsed   -= cbPair;
     1006
     1007        pszLookAhead    = pszPairEnd;
     1008        cbLeftLookAhead = cbLeftParsed;
     1009
     1010        if (cbLeftParsed)
     1011            Log4Func(("Next iteration @ %zu:\n%.*RhXd\n", pszParsed - pszStart, cbLeftParsed, pszParsed));
     1012    }
     1013
     1014    if (cbLeftParsed)
     1015        Log4Func(("Done @ %zu:\n%.*RhXd\n", pszParsed - pszStart, cbLeftParsed, pszParsed));
     1016
     1017    m_offBuf += pszParsed - pszStart; /* Only account really parsed content. */
     1018    Assert(m_offBuf <= m_cbUsed);
     1019
     1020    /* Did we hit a block or stream termination sequence? */
     1021    if (cTerm >= GUESTTOOLBOX_STRM_BLK_TERM_CNT)
     1022    {
     1023        if (!streamBlock.IsEmpty()) /* Only account and complete blocks which have values in it. */
     1024        {
     1025            m_cBlocks++;
     1026            streamBlock.m_fComplete = true;
     1027#ifdef DEBUG
     1028            streamBlock.DumpToLog();
     1029#endif
     1030        }
     1031
     1032        if (cTerm >= GUESTTOOLBOX_STRM_TERM_CNT)
     1033        {
     1034            m_offBuf = m_cbUsed;
     1035            vrc = VINF_EOF;
     1036        }
     1037    }
     1038
     1039    LogFlowThisFunc(("cbLeft=%zu, offBuffer=%zu / cbUsed=%zu, cBlocks=%zu, cTerm=%zu -> current block has %RU64 pairs (complete = %RTbool), rc=%Rrc\n",
     1040                     cbLeftParsed, m_offBuf, m_cbUsed, m_cBlocks, cTerm, streamBlock.GetCount(), streamBlock.IsComplete(), vrc));
    9471041
    9481042    return vrc;
  • trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp

    r99120 r99392  
    23522352 *
    23532353 * @returns VBox status code.
     2354 * @retval  VINF_EOF if the stream reached its end.
    23542355 * @param   uHandle             Guest process file handle to get current block for.
    23552356 * @param   strmBlock           Where to return the stream block on success.
     
    23572358int GuestProcessToolbox::getCurrentBlock(uint32_t uHandle, GuestToolboxStreamBlock &strmBlock)
    23582359{
    2359     const GuestToolboxStream *pStream = NULL;
     2360    GuestToolboxStream *pStream = NULL;
    23602361    if (uHandle == GUEST_PROC_OUT_H_STDOUT)
    23612362        pStream = &mStdOut;
     
    23662367        return VERR_INVALID_PARAMETER;
    23672368
    2368     /** @todo Why not using pStream down below and hardcode to mStdOut? */
    2369 
    2370     int vrc;
    2371     do
    2372     {
    2373         /* Try parsing the data to see if the current block is complete. */
    2374         vrc = mStdOut.ParseBlock(strmBlock);
    2375         if (strmBlock.GetCount())
    2376             break;
    2377     } while (RT_SUCCESS(vrc));
    2378 
    2379     LogFlowThisFunc(("vrc=%Rrc, %RU64 pairs\n", vrc, strmBlock.GetCount()));
     2369    int vrc = pStream->ParseBlock(strmBlock);
     2370
     2371    LogFlowThisFunc(("vrc=%Rrc, currently %RU64 pairs\n", vrc, strmBlock.GetCount()));
    23802372    return vrc;
    23812373}
     
    25942586    LogFlowThisFunc(("fToolWaitFlags=0x%x, pStreamBlock=%p, pvrcGuest=%p\n", fToolWaitFlags, pStrmBlkOut, pvrcGuest));
    25952587
    2596     /* Can we parse the next block without waiting? */
    25972588    int vrc;
     2589
     2590    /* Is the next block complete without waiting for new data from the guest? */
    25982591    if (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK)
    25992592    {
    26002593        AssertPtr(pStrmBlkOut);
    26012594        vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
    2602         if (RT_SUCCESS(vrc))
     2595        if (   RT_SUCCESS(vrc)
     2596            && pStrmBlkOut->IsComplete())
    26032597            return vrc;
    26042598        /* else do the waiting below. */
  • trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp

    r98526 r99392  
    11/* $Id$ */
    22/** @file
    3  * Output stream parsing test cases.
     3 * Tests for VBoxService toolbox output streams.
    44 */
    55
     
    3939
    4040#include <iprt/env.h>
     41#include <iprt/file.h>
    4142#include <iprt/test.h>
     43#include <iprt/rand.h>
    4244#include <iprt/stream.h>
    4345
     
    5052*   Defined Constants And Macros                                                                                                 *
    5153*********************************************************************************************************************************/
    52 #define STR_SIZE(a_sz) RT_STR_TUPLE(a_sz)
     54/** Defines a test entry string size (in bytes). */
     55#define TST_STR_BYTES(a_sz)          (sizeof(a_sz) - 1)
     56/** Defines a test entry string, followed by its size (in bytes). */
     57#define TST_STR_AND_BYTES(a_sz)      a_sz, (sizeof(a_sz) - 1)
     58/** Defines the termination sequence for a single key/value pair. */
     59#define TST_STR_VAL_TRM              GUESTTOOLBOX_STRM_TERM_PAIR_STR
     60/** Defines the termination sequence for a single stream block. */
     61#define TST_STR_BLK_TRM              GUESTTOOLBOX_STRM_TERM_BLOCK_STR
     62/** Defines the termination sequence for the stream. */
     63#define TST_STR_STM_TRM              GUESTTOOLBOX_STRM_TERM_STREAM_STR
    5364
    5465
     
    7182char szUnterm2[] = { 'f', 'o', 'o', '3', '=', 'b', 'a', 'r', '3' };
    7283
     84PRTLOGGER g_pLog = NULL;
     85
     86/**
     87 * Tests single block parsing.
     88 */
    7389static struct
    7490{
     
    8197} g_aTestBlocks[] =
    8298{
    83     /*
    84      * Single object parsing.
    85      * An object is represented by one or multiple key=value pairs which are
    86      * separated by a single "\0". If this termination is missing it will be assumed
    87      * that we need to collect more data to do a successful parsing.
    88      */
    8999    /* Invalid stuff. */
    90     { NULL,                             0,          0,  0,                                         0, VERR_INVALID_POINTER },
    91     { NULL,                             512,        0,  0,                                         0, VERR_INVALID_POINTER },
    92     { "",                               0,          0,  0,                                         0, VERR_INVALID_PARAMETER },
    93     { "",                               0,          0,  0,                                         0, VERR_INVALID_PARAMETER },
    94     { "foo=bar1",                       0,          0,  0,                                         0, VERR_INVALID_PARAMETER },
    95     { "foo=bar2",                       0,          50, 50,                                        0, VERR_INVALID_PARAMETER },
    96     /* Empty buffers. */
    97     { "",                               1,          0,  1,                                         0, VINF_SUCCESS },
    98     { "\0",                             1,          0,  1,                                         0, VINF_SUCCESS },
    99     /* Unterminated values (missing "\0"). */
    100     { STR_SIZE("test1"),                            0,  0,                                         0, VERR_MORE_DATA },
    101     { STR_SIZE("test2="),                           0,  0,                                         0, VERR_MORE_DATA },
    102     { STR_SIZE("test3=test3"),                      0,  0,                                         0, VERR_MORE_DATA },
    103     { STR_SIZE("test4=test4\0t41"),                 0,  sizeof("test4=test4\0") - 1,               1, VERR_MORE_DATA },
    104     { STR_SIZE("test5=test5\0t51=t51"),             0,  sizeof("test5=test5\0") - 1,               1, VERR_MORE_DATA },
    105     /* Next block unterminated. */
    106     { STR_SIZE("t51=t51\0t52=t52\0\0t53=t53"),      0,  sizeof("t51=t51\0t52=t52\0") - 1,          2, VINF_SUCCESS },
    107     { STR_SIZE("test6=test6\0\0t61=t61"),           0,  sizeof("test6=test6\0") - 1,               1, VINF_SUCCESS },
    108     /* Good stuff. */
    109     { STR_SIZE("test61=\0test611=test611\0"),       0,  sizeof("test61=\0test611=test611\0") - 1,  2, VINF_SUCCESS },
    110     { STR_SIZE("test7=test7\0\0"),                  0,  sizeof("test7=test7\0") - 1,               1, VINF_SUCCESS },
    111     { STR_SIZE("test8=test8\0t81=t81\0\0"),         0,  sizeof("test8=test8\0t81=t81\0") - 1,      2, VINF_SUCCESS },
     100    { NULL,                             0,   0,  0,                             0, VERR_INVALID_POINTER },
     101    { NULL,                             512, 0,  0,                             0, VERR_INVALID_POINTER },
     102    { "",                               0,   0,  0,                             0, VERR_INVALID_PARAMETER },
     103    { "",                               0,   0,  0,                             0, VERR_INVALID_PARAMETER },
     104    { "foo=bar1",                       0,   0,  0,                             0, VERR_INVALID_PARAMETER },
     105    { "foo=bar2",                       0,   50, 50,                            0, VERR_INVALID_PARAMETER },
     106    /* Has a empty key (not allowed). */
     107    { TST_STR_AND_BYTES("=test2" TST_STR_VAL_TRM), 0, TST_STR_BYTES(""),        0, VERR_INVALID_PARAMETER },
     108    /* Empty buffers, i.e. nothing to process. */
     109    /* Index 6*/
     110    { "",                               1, 0,  0,                               0, VINF_SUCCESS },
     111    { TST_STR_VAL_TRM,                  1, 0,  0,                               0, VINF_SUCCESS },
     112    /* Stream termination sequence. */
     113    { TST_STR_AND_BYTES(TST_STR_STM_TRM),                                       0,
     114      TST_STR_BYTES    (TST_STR_STM_TRM),                                       0, VINF_EOF },
     115    /* Trash after stream termination sequence (skipped / ignored). */
     116    { TST_STR_AND_BYTES(TST_STR_STM_TRM "trash"),                               0,
     117      TST_STR_BYTES    (TST_STR_STM_TRM "trash"),                               0, VINF_EOF },
     118    { TST_STR_AND_BYTES("a=b" TST_STR_STM_TRM),                                 0,
     119      TST_STR_BYTES    ("a=b" TST_STR_STM_TRM),                                 1, VINF_EOF },
     120    { TST_STR_AND_BYTES("a=b" TST_STR_VAL_TRM "c=d" TST_STR_STM_TRM),           0,
     121      TST_STR_BYTES    ("a=b" TST_STR_VAL_TRM "c=d" TST_STR_STM_TRM),           2, VINF_EOF },
     122    /* Unterminated values (missing separator, i.e. no valid pair). */
     123    { TST_STR_AND_BYTES("test1"), 0,  0,                                        0, VINF_SUCCESS },
     124    /* Has a NULL value (allowed). */
     125    { TST_STR_AND_BYTES("test2=" TST_STR_VAL_TRM),                              0,
     126      TST_STR_BYTES    ("test2="),                                              1, VINF_SUCCESS },
     127    /* One completed pair only. */
     128    { TST_STR_AND_BYTES("test3=test3" TST_STR_VAL_TRM),                         0,
     129      TST_STR_BYTES    ("test3=test3"),                                         1, VINF_SUCCESS },
     130    /* One completed pair, plus an unfinished pair (separator + terminator missing). */
     131    { TST_STR_AND_BYTES("test4=test4" TST_STR_VAL_TRM "t41"),                   0,
     132      TST_STR_BYTES    ("test4=test4" TST_STR_VAL_TRM),                         1, VINF_SUCCESS },
     133    /* Two completed pairs. */
     134    { TST_STR_AND_BYTES("test5=test5" TST_STR_VAL_TRM "t51=t51" TST_STR_VAL_TRM), 0,
     135      TST_STR_BYTES    ("test5=test5" TST_STR_VAL_TRM "t51=t51"),               2, VINF_SUCCESS },
     136    /* One complete block, next block unterminated. */
     137    { TST_STR_AND_BYTES("a51=b51" TST_STR_VAL_TRM "c52=d52" TST_STR_BLK_TRM "e53=f53"), 0,
     138      TST_STR_BYTES    ("a51=b51" TST_STR_VAL_TRM "c52=d52" TST_STR_BLK_TRM),           2, VINF_SUCCESS },
     139    /* Ditto. */
     140    { TST_STR_AND_BYTES("test6=test6" TST_STR_BLK_TRM "t61=t61"),               0,
     141      TST_STR_BYTES    ("test6=test6" TST_STR_BLK_TRM),                         1, VINF_SUCCESS },
     142    /* Two complete pairs with a complete stream. */
     143    { TST_STR_AND_BYTES("test61=" TST_STR_VAL_TRM "test611=test612" TST_STR_STM_TRM), 0,
     144      TST_STR_BYTES    ("test61=" TST_STR_VAL_TRM "test611=test612" TST_STR_STM_TRM), 2, VINF_EOF },
     145    /* One complete block. */
     146    { TST_STR_AND_BYTES("test7=test7" TST_STR_BLK_TRM),                         0,
     147      TST_STR_BYTES     ("test7=test7"),                                        1, VINF_SUCCESS },
     148    /* Ditto. */
     149    { TST_STR_AND_BYTES("test81=test82" TST_STR_VAL_TRM "t81=t82" TST_STR_BLK_TRM), 0,
     150      TST_STR_BYTES    ("test81=test82" TST_STR_VAL_TRM "t81=t82"),             2, VINF_SUCCESS },
    112151    /* Good stuff, but with a second block -- should be *not* taken into account since
    113152     * we're only interested in parsing/handling the first object. */
    114     { STR_SIZE("t9=t9\0t91=t91\0\0t92=t92\0\0"),    0,  sizeof("t9=t9\0t91=t91\0") - 1,            2, VINF_SUCCESS },
     153    { TST_STR_AND_BYTES("t91=t92" TST_STR_VAL_TRM "t93=t94" TST_STR_BLK_TRM "t95=t96" TST_STR_BLK_TRM), 0,
     154      TST_STR_BYTES    ("t91=t92" TST_STR_VAL_TRM "t93=t94" TST_STR_BLK_TRM),   2, VINF_SUCCESS },
    115155    /* Nasty stuff. */
    116156        /* iso 8859-1 encoding (?) of 'aou' all with diaeresis '=f' and 'ao' with diaeresis. */
    117     { STR_SIZE("\xe4\xf6\xfc=\x66\xe4\xf6\0\0"),    0,  sizeof("\xe4\xf6\xfc=\x66\xe4\xf6\0") - 1, 1, VINF_SUCCESS },
     157    { TST_STR_AND_BYTES("1\xe4\xf6\xfc=\x66\xe4\xf6" TST_STR_BLK_TRM),          0,
     158      TST_STR_BYTES    ("1\xe4\xf6\xfc=\x66\xe4\xf6"),                          1, VINF_SUCCESS },
    118159        /* Like above, but after the first '\0' it adds 'ooo=aaa' all letters with diaeresis. */
    119     { STR_SIZE("\xe4\xf6\xfc=\x66\xe4\xf6\0\xf6\xf6\xf6=\xe4\xe4\xe4"),
    120                                                     0,  sizeof("\xe4\xf6\xfc=\x66\xe4\xf6\0") - 1, 1, VERR_MORE_DATA },
    121     /* Some "real world" examples. */
    122     { STR_SIZE("hdr_id=vbt_stat\0hdr_ver=1\0name=foo.txt\0\0"), 0, sizeof("hdr_id=vbt_stat\0hdr_ver=1\0name=foo.txt\0") - 1,
    123                                                                                                                                           3, VINF_SUCCESS }
     160    { TST_STR_AND_BYTES("2\xe4\xf6\xfc=\x66\xe4\xf6" TST_STR_VAL_TRM "\xf6\xf6\xf6=\xe4\xe4\xe4"), 0,
     161      TST_STR_BYTES    ("2\xe4\xf6\xfc=\x66\xe4\xf6" TST_STR_VAL_TRM),                             1, VINF_SUCCESS },
     162    /* Some "real world" examples from VBoxService toolbox. */
     163    { TST_STR_AND_BYTES("hdr_id=vbt_stat" TST_STR_VAL_TRM "hdr_ver=1" TST_STR_VAL_TRM "name=foo.txt" TST_STR_BLK_TRM), 0,
     164      TST_STR_BYTES    ("hdr_id=vbt_stat" TST_STR_VAL_TRM "hdr_ver=1" TST_STR_VAL_TRM "name=foo.txt"),                 3, VINF_SUCCESS }
    124165};
    125166
     167/**
     168 * Tests parsing multiple stream blocks.
     169 *
     170 * Same parsing behavior as for the tests above apply.
     171 */
    126172static struct
    127173{
     174    /** Stream data. */
    128175    const char *pbData;
     176    /** Size of stream data (in bytes). */
    129177    size_t      cbData;
    130178    /** Number of data blocks retrieved. These are separated by "\0\0". */
     
    135183{
    136184    /* No blocks. */
    137     { "\0\0\0\0",                                      sizeof("\0\0\0\0"),                                0, VERR_NO_DATA },
     185    { "", sizeof(""), 0, VINF_SUCCESS },
     186    /* Empty block (no key/value pairs), will not be accounted. */
     187    { TST_STR_STM_TRM,
     188      TST_STR_BYTES(TST_STR_STM_TRM),                                           0, VINF_EOF },
    138189    /* Good stuff. */
    139     { "\0b1=b1\0\0",                                   sizeof("\0b1=b1\0\0"),                             1, VERR_NO_DATA },
    140     { "b1=b1\0\0",                                     sizeof("b1=b1\0\0"),                               1, VERR_NO_DATA },
    141     { "b1=b1\0b2=b2\0\0",                              sizeof("b1=b1\0b2=b2\0\0"),                        1, VERR_NO_DATA },
    142     { "b1=b1\0b2=b2\0\0\0",                            sizeof("b1=b1\0b2=b2\0\0\0"),                      1, VERR_NO_DATA }
     190    { TST_STR_AND_BYTES(TST_STR_VAL_TRM "b1=b2" TST_STR_STM_TRM),               1, VINF_EOF },
     191    { TST_STR_AND_BYTES("b3=b31" TST_STR_STM_TRM),                              1, VINF_EOF },
     192    { TST_STR_AND_BYTES("b4=b41" TST_STR_BLK_TRM "b51=b61" TST_STR_STM_TRM),    2, VINF_EOF },
     193    { TST_STR_AND_BYTES("b5=b51" TST_STR_VAL_TRM "b61=b71" TST_STR_STM_TRM),    1, VINF_EOF }
    143194};
    144195
    145 int manualTest(void)
    146 {
    147     int rc = VINF_SUCCESS;
    148     static struct
     196/**
     197 * Reads and parses the stream from a given file.
     198 *
     199 * @returns RTEXITCODE
     200 * @param   pszFile             Absolute path to file to parse.
     201 */
     202static int tstReadFromFile(const char *pszFile)
     203{
     204    RTFILE fh;
     205    int rc = RTFileOpen(&fh, pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
     206    AssertRCReturn(rc, RTEXITCODE_FAILURE);
     207
     208    size_t cbFileSize;
     209    rc = RTFileQuerySize(fh, &cbFileSize);
     210    AssertRCReturn(rc, RTEXITCODE_FAILURE);
     211
     212    GuestToolboxStream      stream;
     213    GuestToolboxStreamBlock block;
     214
     215    size_t cPairs = 0;
     216    size_t cBlocks = 0;
     217
     218    unsigned aToRead[] = { 256, 23, 13 };
     219    unsigned i = 0;
     220
     221    size_t cbToRead = cbFileSize;
     222
     223    for (unsigned a = 0; a < 32; a++)
    149224    {
    150         const char *pbData;
    151         size_t      cbData;
    152         uint32_t    offStart;
    153         uint32_t    offAfter;
    154         uint32_t    cMapElements;
    155         int         iResult;
    156     } const s_aTest[] =
    157     {
    158         { "test5=test5\0t51=t51",           sizeof("test5=test5\0t51=t51"),                            0,  sizeof("test5=test5\0") - 1,                   1, VERR_MORE_DATA },
    159         { "\0\0test5=test5\0t51=t51",       sizeof("\0\0test5=test5\0t51=t51"),                        0,  sizeof("\0\0test5=test5\0") - 1,               1, VERR_MORE_DATA },
    160     };
    161 
    162     for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aTest); iTest++)
    163     {
    164         RTTestIPrintf(RTTESTLVL_DEBUG, "Manual test #%d\n", iTest);
    165 
    166         GuestToolboxStream stream;
    167         rc = stream.AddData((BYTE *)s_aTest[iTest].pbData, s_aTest[iTest].cbData);
    168 
    169         for (;;)
     225        uint8_t buf[_64K];
     226        do
    170227        {
    171             GuestToolboxStreamBlock block;
     228            size_t cbChunk = RT_MIN(cbToRead, i < RT_ELEMENTS(aToRead) ? aToRead[i++] : RTRandU64Ex(8, RT_MIN(sizeof(buf), 64)));
     229            if (cbChunk > cbToRead)
     230                cbChunk = cbToRead;
     231            if (cbChunk)
     232            {
     233                RTTestIPrintf(RTTESTLVL_DEBUG, "Reading %zu bytes (of %zu left) ...\n", cbChunk, cbToRead);
     234
     235                size_t cbRead;
     236                rc = RTFileRead(fh, &buf, cbChunk, &cbRead);
     237                AssertRCBreak(rc);
     238
     239                if (!cbRead)
     240                    continue;
     241
     242                cbToRead -= cbRead;
     243
     244                rc = stream.AddData((BYTE *)buf, cbRead);
     245                AssertRCBreak(rc);
     246            }
     247
    172248            rc = stream.ParseBlock(block);
    173             RTTestIPrintf(RTTESTLVL_DEBUG, "\tReturned with rc=%Rrc, numItems=%ld\n",
    174                           rc, block.GetCount());
    175 
    176             if (block.GetCount())
    177                 break;
    178         }
     249            Assert(rc != VERR_INVALID_PARAMETER);
     250            RTTestIPrintf(RTTESTLVL_DEBUG, "Parsing ended with %Rrc\n", rc);
     251            if (block.IsComplete())
     252            {
     253                /* Sanity checks; disable this if you parse anything else but fsinfo output from VBoxService toolbox. */
     254                //Assert(block.GetString("name") != NULL);
     255
     256                cPairs += block.GetCount();
     257                cBlocks = stream.GetBlocks();
     258                block.Clear();
     259            }
     260        } while (VINF_SUCCESS == rc /* Might also be VINF_EOF when finished */);
     261
     262        RTTestIPrintf(RTTESTLVL_ALWAYS, "Total %zu blocks + %zu pairs\n", cBlocks, cPairs);
     263
     264        /* Reset. */
     265        RTFileSeek(fh, 0, RTFILE_SEEK_BEGIN, NULL);
     266        cbToRead = cbFileSize;
     267        cPairs = 0;
     268        cBlocks = 0;
     269        block.Clear();
     270        stream.Destroy();
    179271    }
    180272
    181     return rc;
     273    int rc2 = RTFileClose(fh);
     274    if (RT_SUCCESS(rc))
     275        rc = rc2;
     276
     277    return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
    182278}
    183279
    184 int main()
     280int main(int argc, char **argv)
    185281{
    186282    RTTEST hTest;
     
    189285        return rcExit;
    190286    RTTestBanner(hTest);
     287
     288#ifdef DEBUG
     289    RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
     290#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
     291    fFlags |= RTLOGFLAGS_USECRLF;
     292#endif
     293    static const char * const s_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
     294    int rc = RTLogCreate(&g_pLog, fFlags, "guest_control.e.l.l2.l3.f", NULL,
     295                         RT_ELEMENTS(s_apszLogGroups), s_apszLogGroups, RTLOGDEST_STDOUT, NULL /*"vkat-release.log"*/);
     296    AssertRCReturn(rc, rc);
     297    RTLogSetDefaultInstance(g_pLog);
     298#endif
     299
     300    if (argc > 1)
     301        return tstReadFromFile(argv[1]);
    191302
    192303    RTTestIPrintf(RTTESTLVL_DEBUG, "Initializing COM...\n");
     
    198309    }
    199310
    200 #ifdef DEBUG_andy
    201     int rc = manualTest();
    202     if (RT_FAILURE(rc))
    203         return RTEXITCODE_FAILURE;
    204 #endif
    205 
    206     AssertCompile(sizeof("sizecheck")   == 10);
    207     AssertCompile(sizeof("off=rab")     == 8);
    208     AssertCompile(sizeof("off=rab\0\0") == 10);
    209 
    210     RTTestSub(hTest, "Lines");
     311    AssertCompile(TST_STR_BYTES("1")           == 1);
     312    AssertCompile(TST_STR_BYTES("sizecheck")   == 9);
     313    AssertCompile(TST_STR_BYTES("off=rab")     == 7);
     314    AssertCompile(TST_STR_BYTES("off=rab\0\0") == 9);
     315
     316    RTTestSub(hTest, "Blocks");
     317
     318    RTTestDisableAssertions(hTest);
     319
    211320    for (unsigned iTest = 0; iTest < RT_ELEMENTS(g_aTestBlocks); iTest++)
    212321    {
    213         RTTestIPrintf(RTTESTLVL_DEBUG, "=> Test #%u\n", iTest);
     322        RTTestIPrintf(RTTESTLVL_DEBUG, "=> Block test #%u:\n'%.*RhXd\n", iTest, g_aTestBlocks[iTest].cbData, g_aTestBlocks[iTest].pbData);
    214323
    215324        GuestToolboxStream stream;
    216         if (RT_FAILURE(g_aTestBlocks[iTest].iResult))
    217             RTTestDisableAssertions(hTest);
    218325        int iResult = stream.AddData((BYTE *)g_aTestBlocks[iTest].pbData, g_aTestBlocks[iTest].cbData);
    219         if (RT_FAILURE(g_aTestBlocks[iTest].iResult))
    220             RTTestRestoreAssertions(hTest);
    221326        if (RT_SUCCESS(iResult))
    222327        {
     
    224329            iResult = stream.ParseBlock(curBlock);
    225330            if (iResult != g_aTestBlocks[iTest].iResult)
    226                 RTTestFailed(hTest, "Block #%u: Returned %Rrc, expected %Rrc", iTest, iResult, g_aTestBlocks[iTest].iResult);
     331                RTTestFailed(hTest, "Block #%u: Returned %Rrc, expected %Rrc\n", iTest, iResult, g_aTestBlocks[iTest].iResult);
    227332            else if (stream.GetOffset() != g_aTestBlocks[iTest].offAfter)
    228                 RTTestFailed(hTest, "Block #%uOffset %zu wrong, expected %u\n",
    229                              iTest, stream.GetOffset(), g_aTestBlocks[iTest].offAfter);
     333                RTTestFailed(hTest, "Block #%u: Offset %zu wrong ('%#x'), expected %u ('%#x')\n",
     334                             iTest, stream.GetOffset(), g_aTestBlocks[iTest].pbData[stream.GetOffset()],
     335                             g_aTestBlocks[iTest].offAfter, g_aTestBlocks[iTest].pbData[g_aTestBlocks[iTest].offAfter]);
    230336            else if (iResult == VERR_MORE_DATA)
    231337                RTTestIPrintf(RTTESTLVL_DEBUG, "\tMore data (Offset: %zu)\n", stream.GetOffset());
     
    249355                    RTStrmWriteEx(g_pStdOut, &g_aTestBlocks[iTest].pbData[off], cbToWrite - 1, NULL);
    250356            }
     357
     358            if (RTTestIErrorCount())
     359                break;
    251360        }
    252361    }
    253362
    254     RTTestSub(hTest, "Blocks");
     363    RTTestSub(hTest, "Streams");
     364
    255365    for (unsigned iTest = 0; iTest < RT_ELEMENTS(g_aTestStream); iTest++)
    256366    {
    257         RTTestIPrintf(RTTESTLVL_DEBUG, "=> Block test #%u\n", iTest);
     367        RTTestIPrintf(RTTESTLVL_DEBUG, "=> Stream test #%u\n%.*RhXd\n",
     368                      iTest, g_aTestStream[iTest].cbData, g_aTestStream[iTest].pbData);
    258369
    259370        GuestToolboxStream stream;
     
    261372        if (RT_SUCCESS(iResult))
    262373        {
    263             uint32_t cBlocks = 0;
    264             uint8_t uSafeCouunter = 0;
     374            uint32_t cBlocksComplete = 0;
     375            uint8_t  cSafety = 0;
    265376            do
    266377            {
    267378                GuestToolboxStreamBlock curBlock;
    268379                iResult = stream.ParseBlock(curBlock);
    269                 RTTestIPrintf(RTTESTLVL_DEBUG, "Block #%u: Returned with %Rrc", iTest, iResult);
    270                 if (RT_SUCCESS(iResult))
    271                 {
    272                     /* Only count block which have at least one pair. */
    273                     if (curBlock.GetCount())
    274                         cBlocks++;
    275                 }
    276                 if (uSafeCouunter++ > 32)
     380                RTTestIPrintf(RTTESTLVL_DEBUG, "Stream #%u: Returned with %Rrc\n", iTest, iResult);
     381                if (cSafety++ > 8)
    277382                    break;
    278             } while (RT_SUCCESS(iResult));
     383                if (curBlock.IsComplete())
     384                    cBlocksComplete++;
     385            } while (iResult != VINF_EOF);
    279386
    280387            if (iResult != g_aTestStream[iTest].iResult)
    281                 RTTestFailed(hTest, "Block #%uReturned %Rrc, expected %Rrc", iTest, iResult, g_aTestStream[iTest].iResult);
    282             else if (cBlocks != g_aTestStream[iTest].cBlocks)
    283                 RTTestFailed(hTest, "Block #%uReturned %u blocks, expected %u", iTest, cBlocks, g_aTestStream[iTest].cBlocks);
     388                RTTestFailed(hTest, "Stream #%u: Returned %Rrc, expected %Rrc\n", iTest, iResult, g_aTestStream[iTest].iResult);
     389            else if (cBlocksComplete != g_aTestStream[iTest].cBlocks)
     390                RTTestFailed(hTest, "Stream #%u: Returned %u blocks, expected %u\n", iTest, cBlocksComplete, g_aTestStream[iTest].cBlocks);
    284391        }
    285392        else
    286             RTTestFailed(hTest, "Block #%u: Adding data failed with %Rrc", iTest, iResult);
     393            RTTestFailed(hTest, "Stream #%u: Adding data failed with %Rrc\n", iTest, iResult);
     394
     395        if (RTTestIErrorCount())
     396            break;
    287397    }
     398
     399    RTTestRestoreAssertions(hTest);
    288400
    289401    RTTestIPrintf(RTTESTLVL_DEBUG, "Shutting down COM...\n");
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette