VirtualBox

source: vbox/trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp@ 95140

Last change on this file since 95140 was 94494, checked in by vboxsync, 3 years ago

Main/tstGuestCtrlParseBuffer: Buggy string length. Better test failure reporting. Use RTTestSub instead of RTTestPrintf.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* $Id: tstGuestCtrlParseBuffer.cpp 94494 2022-04-06 13:49:44Z vboxsync $ */
2/** @file
3 * Output stream parsing test cases.
4 */
5
6/*
7 * Copyright (C) 2011-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN
23#include <VBox/err.h>
24#include <VBox/log.h>
25
26#include "../include/GuestCtrlImplPrivate.h"
27
28using namespace com;
29
30#include <iprt/env.h>
31#include <iprt/test.h>
32#include <iprt/stream.h>
33
34#ifndef BYTE
35# define BYTE uint8_t
36#endif
37
38
39/*********************************************************************************************************************************
40* Defined Constants And Macros *
41*********************************************************************************************************************************/
42#define STR_SIZE(a_sz) RT_STR_TUPLE(a_sz)
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48typedef struct VBOXGUESTCTRL_BUFFER_VALUE
49{
50 char *pszValue;
51} VBOXGUESTCTRL_BUFFER_VALUE, *PVBOXGUESTCTRL_BUFFER_VALUE;
52typedef std::map< RTCString, VBOXGUESTCTRL_BUFFER_VALUE > GuestBufferMap;
53typedef std::map< RTCString, VBOXGUESTCTRL_BUFFER_VALUE >::iterator GuestBufferMapIter;
54typedef std::map< RTCString, VBOXGUESTCTRL_BUFFER_VALUE >::const_iterator GuestBufferMapIterConst;
55
56
57/*********************************************************************************************************************************
58* Global Variables *
59*********************************************************************************************************************************/
60char szUnterm1[] = { 'a', 's', 'd', 'f' };
61char szUnterm2[] = { 'f', 'o', 'o', '3', '=', 'b', 'a', 'r', '3' };
62
63static struct
64{
65 const char *pbData;
66 size_t cbData;
67 uint32_t offStart;
68 uint32_t offAfter;
69 uint32_t cMapElements;
70 int iResult;
71} g_aTestBlocks[] =
72{
73 /*
74 * Single object parsing.
75 * An object is represented by one or multiple key=value pairs which are
76 * separated by a single "\0". If this termination is missing it will be assumed
77 * that we need to collect more data to do a successful parsing.
78 */
79 /* Invalid stuff. */
80 { NULL, 0, 0, 0, 0, VERR_INVALID_POINTER },
81 { NULL, 512, 0, 0, 0, VERR_INVALID_POINTER },
82 { "", 0, 0, 0, 0, VERR_INVALID_PARAMETER },
83 { "", 0, 0, 0, 0, VERR_INVALID_PARAMETER },
84 { "foo=bar1", 0, 0, 0, 0, VERR_INVALID_PARAMETER },
85 { "foo=bar2", 0, 50, 50, 0, VERR_INVALID_PARAMETER },
86 /* Empty buffers. */
87 { "", 1, 0, 1, 0, VINF_SUCCESS },
88 { "\0", 1, 0, 1, 0, VINF_SUCCESS },
89 /* Unterminated values (missing "\0"). */
90 { STR_SIZE("test1"), 0, 0, 0, VERR_MORE_DATA },
91 { STR_SIZE("test2="), 0, 0, 0, VERR_MORE_DATA },
92 { STR_SIZE("test3=test3"), 0, 0, 0, VERR_MORE_DATA },
93 { STR_SIZE("test4=test4\0t41"), 0, sizeof("test4=test4\0") - 1, 1, VERR_MORE_DATA },
94 { STR_SIZE("test5=test5\0t51=t51"), 0, sizeof("test5=test5\0") - 1, 1, VERR_MORE_DATA },
95 /* Next block unterminated. */
96 { STR_SIZE("t51=t51\0t52=t52\0\0t53=t53"), 0, sizeof("t51=t51\0t52=t52\0") - 1, 2, VINF_SUCCESS },
97 { STR_SIZE("test6=test6\0\0t61=t61"), 0, sizeof("test6=test6\0") - 1, 1, VINF_SUCCESS },
98 /* Good stuff. */
99 { STR_SIZE("test61=\0test611=test611\0"), 0, sizeof("test61=\0test611=test611\0") - 1, 2, VINF_SUCCESS },
100 { STR_SIZE("test7=test7\0\0"), 0, sizeof("test7=test7\0") - 1, 1, VINF_SUCCESS },
101 { STR_SIZE("test8=test8\0t81=t81\0\0"), 0, sizeof("test8=test8\0t81=t81\0") - 1, 2, VINF_SUCCESS },
102 /* Good stuff, but with a second block -- should be *not* taken into account since
103 * we're only interested in parsing/handling the first object. */
104 { STR_SIZE("t9=t9\0t91=t91\0\0t92=t92\0\0"), 0, sizeof("t9=t9\0t91=t91\0") - 1, 2, VINF_SUCCESS },
105 /* Nasty stuff. */
106 /* iso 8859-1 encoding (?) of 'aou' all with diaeresis '=f' and 'ao' with diaeresis. */
107 { STR_SIZE("\xe4\xf6\xfc=\x66\xe4\xf6\0\0"), 0, sizeof("\xe4\xf6\xfc=\x66\xe4\xf6\0") - 1, 1, VINF_SUCCESS },
108 /* Like above, but after the first '\0' it adds 'ooo=aaa' all letters with diaeresis. */
109 { STR_SIZE("\xe4\xf6\xfc=\x66\xe4\xf6\0\xf6\xf6\xf6=\xe4\xe4\xe4"),
110 0, sizeof("\xe4\xf6\xfc=\x66\xe4\xf6\0") - 1, 1, VERR_MORE_DATA },
111 /* Some "real world" examples. */
112 { 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,
113 3, VINF_SUCCESS }
114};
115
116static struct
117{
118 const char *pbData;
119 size_t cbData;
120 /** Number of data blocks retrieved. These are separated by "\0\0". */
121 uint32_t cBlocks;
122 /** Overall result when done parsing. */
123 int iResult;
124} const g_aTestStream[] =
125{
126 /* No blocks. */
127 { "\0\0\0\0", sizeof("\0\0\0\0"), 0, VERR_NO_DATA },
128 /* Good stuff. */
129 { "\0b1=b1\0\0", sizeof("\0b1=b1\0\0"), 1, VERR_NO_DATA },
130 { "b1=b1\0\0", sizeof("b1=b1\0\0"), 1, VERR_NO_DATA },
131 { "b1=b1\0b2=b2\0\0", sizeof("b1=b1\0b2=b2\0\0"), 1, VERR_NO_DATA },
132 { "b1=b1\0b2=b2\0\0\0", sizeof("b1=b1\0b2=b2\0\0\0"), 1, VERR_NO_DATA }
133};
134
135int manualTest(void)
136{
137 int rc = VINF_SUCCESS;
138 static struct
139 {
140 const char *pbData;
141 size_t cbData;
142 uint32_t offStart;
143 uint32_t offAfter;
144 uint32_t cMapElements;
145 int iResult;
146 } const s_aTest[] =
147 {
148 { "test5=test5\0t51=t51", sizeof("test5=test5\0t51=t51"), 0, sizeof("test5=test5\0") - 1, 1, VERR_MORE_DATA },
149 { "\0\0test5=test5\0t51=t51", sizeof("\0\0test5=test5\0t51=t51"), 0, sizeof("\0\0test5=test5\0") - 1, 1, VERR_MORE_DATA },
150 };
151
152 for (unsigned iTest = 0; iTest < RT_ELEMENTS(s_aTest); iTest++)
153 {
154 RTTestIPrintf(RTTESTLVL_DEBUG, "Manual test #%d\n", iTest);
155
156 GuestProcessStream stream;
157 rc = stream.AddData((BYTE *)s_aTest[iTest].pbData, s_aTest[iTest].cbData);
158
159 for (;;)
160 {
161 GuestProcessStreamBlock block;
162 rc = stream.ParseBlock(block);
163 RTTestIPrintf(RTTESTLVL_DEBUG, "\tReturned with rc=%Rrc, numItems=%ld\n",
164 rc, block.GetCount());
165
166 if (block.GetCount())
167 break;
168 }
169 }
170
171 return rc;
172}
173
174int main()
175{
176 RTTEST hTest;
177 RTEXITCODE rcExit = RTTestInitAndCreate("tstParseBuffer", &hTest);
178 if (rcExit != RTEXITCODE_SUCCESS)
179 return rcExit;
180 RTTestBanner(hTest);
181
182 RTTestIPrintf(RTTESTLVL_DEBUG, "Initializing COM...\n");
183 HRESULT hrc = com::Initialize();
184 if (FAILED(hrc))
185 {
186 RTTestFailed(hTest, "Failed to initialize COM (%Rhrc)!\n", hrc);
187 return RTEXITCODE_FAILURE;
188 }
189
190#ifdef DEBUG_andy
191 int rc = manualTest();
192 if (RT_FAILURE(rc))
193 return RTEXITCODE_FAILURE;
194#endif
195
196 AssertCompile(sizeof("sizecheck") == 10);
197 AssertCompile(sizeof("off=rab") == 8);
198 AssertCompile(sizeof("off=rab\0\0") == 10);
199
200 RTTestSub(hTest, "Lines");
201 for (unsigned iTest = 0; iTest < RT_ELEMENTS(g_aTestBlocks); iTest++)
202 {
203 RTTestIPrintf(RTTESTLVL_DEBUG, "=> Test #%u\n", iTest);
204
205 GuestProcessStream stream;
206 if (RT_FAILURE(g_aTestBlocks[iTest].iResult))
207 RTTestDisableAssertions(hTest);
208 int iResult = stream.AddData((BYTE *)g_aTestBlocks[iTest].pbData, g_aTestBlocks[iTest].cbData);
209 if (RT_FAILURE(g_aTestBlocks[iTest].iResult))
210 RTTestRestoreAssertions(hTest);
211 if (RT_SUCCESS(iResult))
212 {
213 GuestProcessStreamBlock curBlock;
214 iResult = stream.ParseBlock(curBlock);
215 if (iResult != g_aTestBlocks[iTest].iResult)
216 RTTestFailed(hTest, "Block #%u: Returned %Rrc, expected %Rrc", iTest, iResult, g_aTestBlocks[iTest].iResult);
217 else if (stream.GetOffset() != g_aTestBlocks[iTest].offAfter)
218 RTTestFailed(hTest, "Block #%uOffset %zu wrong, expected %u\n",
219 iTest, stream.GetOffset(), g_aTestBlocks[iTest].offAfter);
220 else if (iResult == VERR_MORE_DATA)
221 RTTestIPrintf(RTTESTLVL_DEBUG, "\tMore data (Offset: %zu)\n", stream.GetOffset());
222
223 if (RT_SUCCESS(iResult) || iResult == VERR_MORE_DATA)
224 if (curBlock.GetCount() != g_aTestBlocks[iTest].cMapElements)
225 RTTestFailed(hTest, "Block #%u: Map has %u elements, expected %u\n",
226 iTest, curBlock.GetCount(), g_aTestBlocks[iTest].cMapElements);
227
228 /* There is remaining data left in the buffer (which needs to be merged
229 * with a following buffer) -- print it. */
230 size_t off = stream.GetOffset();
231 size_t cbToWrite = g_aTestBlocks[iTest].cbData - off;
232 if (cbToWrite)
233 {
234 RTTestIPrintf(RTTESTLVL_DEBUG, "\tRemaining (%u):\n", cbToWrite);
235
236 /* How to properly get the current RTTESTLVL (aka IPRT_TEST_MAX_LEVEL) here?
237 * Hack alert: Using RTEnvGet for now. */
238 if (!RTStrICmp(RTEnvGet("IPRT_TEST_MAX_LEVEL"), "debug"))
239 RTStrmWriteEx(g_pStdOut, &g_aTestBlocks[iTest].pbData[off], cbToWrite - 1, NULL);
240 }
241 }
242 }
243
244 RTTestSub(hTest, "Blocks");
245 for (unsigned iTest = 0; iTest < RT_ELEMENTS(g_aTestStream); iTest++)
246 {
247 RTTestIPrintf(RTTESTLVL_DEBUG, "=> Block test #%u\n", iTest);
248
249 GuestProcessStream stream;
250 int iResult = stream.AddData((BYTE*)g_aTestStream[iTest].pbData, g_aTestStream[iTest].cbData);
251 if (RT_SUCCESS(iResult))
252 {
253 uint32_t cBlocks = 0;
254 uint8_t uSafeCouunter = 0;
255 do
256 {
257 GuestProcessStreamBlock curBlock;
258 iResult = stream.ParseBlock(curBlock);
259 RTTestIPrintf(RTTESTLVL_DEBUG, "Block #%u: Returned with %Rrc", iTest, iResult);
260 if (RT_SUCCESS(iResult))
261 {
262 /* Only count block which have at least one pair. */
263 if (curBlock.GetCount())
264 cBlocks++;
265 }
266 if (uSafeCouunter++ > 32)
267 break;
268 } while (RT_SUCCESS(iResult));
269
270 if (iResult != g_aTestStream[iTest].iResult)
271 RTTestFailed(hTest, "Block #%uReturned %Rrc, expected %Rrc", iTest, iResult, g_aTestStream[iTest].iResult);
272 else if (cBlocks != g_aTestStream[iTest].cBlocks)
273 RTTestFailed(hTest, "Block #%uReturned %u blocks, expected %u", iTest, cBlocks, g_aTestStream[iTest].cBlocks);
274 }
275 else
276 RTTestFailed(hTest, "Block #%u: Adding data failed with %Rrc", iTest, iResult);
277 }
278
279 RTTestIPrintf(RTTESTLVL_DEBUG, "Shutting down COM...\n");
280 com::Shutdown();
281
282 /*
283 * Summary.
284 */
285 return RTTestSummaryAndDestroy(hTest);
286}
287
Note: See TracBrowser for help on using the repository browser.

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