Index: /trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp
===================================================================
--- /trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp	(revision 37902)
+++ /trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp	(revision 37903)
@@ -26,4 +26,8 @@
 #include <iprt/stream.h>
 
+#ifndef BYTE
+# define BYTE uint8_t
+#endif
+
 /** @todo Use original source of GuestCtrlImpl.cpp! */
 
@@ -37,8 +41,5 @@
 
 char pszUnterm1[] = { 'a', 's', 'd', 'f' };
-
-#ifndef BYTE
-# define BYTE uint8_t
-#endif
+char pszUnterm2[] = { 'f', 'o', 'o', '3', '=', 'b', 'a', 'r', '3' };
 
 static struct
@@ -46,5 +47,6 @@
     const char *pbData;
     size_t      cbData;
-    uint32_t    uOffset;
+    uint32_t    uOffsetStart;
+    uint32_t    uOffsetAfter;
     uint32_t    uMapElements;
     int         iResult;
@@ -52,18 +54,27 @@
 {
     /* Invalid stuff. */
-    { NULL,         0,                  0, 0, VERR_INVALID_POINTER },
-    { NULL,         512,                0, 0, VERR_INVALID_POINTER },
-    { "",           0,                  0, 0, VERR_INVALID_PARAMETER },
-    { "",           0,                  5, 0, VERR_INVALID_PARAMETER },
-    { "",           1,                  0, 0, VINF_SUCCESS },
-    { pszUnterm1,   5,                  0, 0, VINF_SUCCESS },
-    { "foo=bar",    0,                  0, 0, VERR_INVALID_PARAMETER },
-
-    /* Parsing stuff. */
-    { "foo",                    sizeof("foo"),                          0, 0, VINF_SUCCESS },
-    { "foo=",                   sizeof("foo="),                         0, 1, VINF_SUCCESS },
-    { "=bar",                   sizeof("=bar"),                         0, 0, VINF_SUCCESS },
-    { "foo=bar",                sizeof("foo=bar"),                      0, 1, VINF_SUCCESS },
-    { "foo=bar\0baz=boo",       16,                                     0, 2, VINF_SUCCESS }
+    { NULL,                             0,                                                 0,  0,       0, VERR_INVALID_POINTER },
+    { NULL,                             512,                                               0,  0,       0, VERR_INVALID_POINTER },
+    { "",                               0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
+    { "",                               0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
+    { "foo=bar1",                       0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
+    { "foo=bar2",                       0,                                                 50, 50,      0, VERR_INVALID_PARAMETER },
+    /* Incomplete buffer (missing \0 termination). */
+    { "",                               1,                                                 0, 0,       0, VERR_MORE_DATA },
+    { "\0",                             1,                                                 0, 0,       0, VERR_MORE_DATA },
+    { pszUnterm1,                       5,                                                 0, 0,       0, VERR_MORE_DATA },
+    { "foo1",                           sizeof("foo1"),                                    0, 0,       0, VERR_MORE_DATA },
+    { pszUnterm2,                       8,                                                 0, 0,       0, VERR_MORE_DATA },
+    /* Incomplete buffer (missing components). */
+    { "=bar\0",                         sizeof("=bar"),                                    0,  0,                                         0, VERR_MORE_DATA },
+    /* Last sequence is incomplete -- new offset should point to it. */
+    { "hug=sub\0incomplete",            sizeof("hug=sub\0incomplete"),                     0,  sizeof("hug=sub"),                         1, VERR_MORE_DATA },
+    { "boo=hoo\0baz=boo\0qwer",         sizeof("boo=hoo\0baz=boo\0qwer"),                  0,  sizeof("boo=hoo\0baz=boo"),                2, VERR_MORE_DATA },
+    /* Parsing good stuff. */
+    { "foo2=",                          sizeof("foo2="),                                   0,  sizeof("foo2="),                           1, VINF_SUCCESS },
+    { "har=hor",                        sizeof("har=hor"),                                 0,  sizeof("har=hor"),                         1, VINF_SUCCESS },
+    { "foo=bar\0baz=boo",               sizeof("foo=bar\0baz=boo"),                        0,  sizeof("foo=bar\0baz=boo"),                2, VINF_SUCCESS },
+    /* Parsing until a different block (two terminations, returning offset to next block). */
+    { "off=rab\0\0zab=oob",             sizeof("off=rab\0\0zab=oob"),                      0,  sizeof("zab=oob"),                         1, VERR_MORE_DATA }
 };
 
@@ -78,5 +89,5 @@
 
     size_t uCur = *puOffset;
-    for (uCur = 0; uCur < cbData; uCur++)
+    for (;uCur < cbData;)
     {
         const char *pszStart = (char*)&pbData[uCur];
@@ -84,13 +95,18 @@
 
         /* Search and of current pair (key=value\0). */
-        while (   *pszEnd != '\0'
-               && ++uCur < cbData)
-        {
+        while (uCur++ < cbData)
+        {
+            if (*pszEnd == '\0')
+                break;
             pszEnd++;
         }
 
-        size_t uLen = pszEnd - pszStart;
-        if (!uLen)
-            continue;
+        size_t uPairLen = pszEnd - pszStart;
+        if (   *pszEnd != '\0'
+            || !uPairLen)
+        {
+            rc = VERR_MORE_DATA;
+            break;
+        }
 
         const char *pszSep = pszStart;
@@ -104,5 +120,6 @@
             || pszSep == pszEnd)
         {
-            continue;
+            rc = VERR_MORE_DATA;
+            break;
         }
 
@@ -110,4 +127,5 @@
         size_t uValLen = pszEnd - (pszSep + 1);
 
+        /* Get key (if present). */
         if (uKeyLen)
         {
@@ -123,4 +141,5 @@
             mapBuf[RTCString(pszKey)].pszValue = NULL;
 
+            /* Get value (if present). */
             if (uValLen)
             {
@@ -138,4 +157,6 @@
 
             RTMemFree(pszKey);
+
+            *puOffset += uCur - *puOffset;
         }
     }
@@ -152,24 +173,47 @@
     RTTestBanner(hTest);
 
+    if (sizeof("sizecheck") != 10)
+        RTTestFailed(hTest, "Basic size test failed (%u <-> 10)", sizeof("sizecheck"));
+
     for (unsigned iTest = 0; iTest < RT_ELEMENTS(aTests); iTest++)
     {
-        GuestBufferMap map;
+        GuestBufferMap bufMap;
 
         int iResult = outputBufferParse((BYTE*)aTests[iTest].pbData, aTests[iTest].cbData,
-                                        &aTests[iTest].uOffset, map);
+                                        &aTests[iTest].uOffsetStart, bufMap);
+
+        RTTestIPrintf(RTTESTLVL_DEBUG, "=> Test #%u\n", iTest);
+
         if (iResult != aTests[iTest].iResult)
         {
-            RTTestFailed(hTest, "#%u: Returned %Rrc, expected %Rrc",
-                         iTest, iResult, aTests[iTest].iResult);
-        }
-        else if (map.size() != aTests[iTest].uMapElements)
-        {
-            RTTestFailed(hTest, "#%u: Map has %u elements, expected %u",
-                         iTest, map.size(), aTests[iTest].uMapElements);
-        }
-
-        if (map.size())
-            RTTestIPrintf(RTTESTLVL_DEBUG, "Output for test #%u\n", iTest);
-        for (GuestBufferMapIter it = map.begin(); it != map.end(); it++)
+            RTTestFailed(hTest, "\tReturned %Rrc, expected %Rrc",
+                         iResult, aTests[iTest].iResult);
+        }
+        else if (bufMap.size() != aTests[iTest].uMapElements)
+        {
+            RTTestFailed(hTest, "\tMap has %u elements, expected %u",
+                         bufMap.size(), aTests[iTest].uMapElements);
+        }
+        else if (aTests[iTest].uOffsetStart != aTests[iTest].uOffsetAfter)
+        {
+            RTTestFailed(hTest, "\tOffset %u wrong, expected %u",
+                         aTests[iTest].uOffsetStart, aTests[iTest].uOffsetAfter);
+        }
+        else if (iResult == VERR_MORE_DATA)
+        {
+            /* There is remaining data left in the buffer (which needs to be merged
+             * with a following buffer) -- print it. */
+            const char *pszRemaining = aTests[iTest].pbData;
+            size_t uOffsetNew = aTests[iTest].uOffsetStart;
+            size_t uToWrite = aTests[iTest].cbData - uOffsetNew;
+            if (pszRemaining && uOffsetNew)
+            {
+                RTTestIPrintf(RTTESTLVL_DEBUG, "\tRemaining (%u):\n", uToWrite);
+                RTStrmWriteEx(g_pStdOut, &aTests[iTest].pbData[uOffsetNew], uToWrite - 1, NULL);
+                RTTestIPrintf(RTTESTLVL_DEBUG, "\n");
+            }
+        }
+
+        for (GuestBufferMapIter it = bufMap.begin(); it != bufMap.end(); it++)
         {
             RTTestIPrintf(RTTESTLVL_DEBUG, "\t%s -> %s\n",
