/* $Id: tstDBGCParser.cpp 31926 2010-08-24 13:50:41Z vboxsync $ */ /** @file * DBGC Testcase - Command Parser. */ /* * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include "../DBGCInternal.h" #include #include /******************************************************************************* * Internal Functions * *******************************************************************************/ static DECLCALLBACK(bool) tstDBGCBackInput(PDBGCBACK pBack, uint32_t cMillies); static DECLCALLBACK(int) tstDBGCBackRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead); static DECLCALLBACK(int) tstDBGCBackWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten); static DECLCALLBACK(void) tstDBGCBackSetReady(PDBGCBACK pBack, bool fReady); /******************************************************************************* * Global Variables * *******************************************************************************/ /** The test handle. */ static RTTEST g_hTest = NIL_RTTEST; /** The DBGC backend structure for use in this testcase. */ static DBGCBACK g_tstBack = { tstDBGCBackInput, tstDBGCBackRead, tstDBGCBackWrite, tstDBGCBackSetReady }; /** For keeping track of output prefixing. */ static bool g_fPendingPrefix = true; /** Pointer to the current input position. */ const char *g_pszInput = NULL; /** The output of the last command. */ static char g_szOutput[1024]; /** The current offset into g_szOutput. */ static size_t g_offOutput = 0; /** * Checks if there is input. * * @returns true if there is input ready. * @returns false if there not input ready. * @param pBack Pointer to the backend structure supplied by * the backend. The backend can use this to find * it's instance data. * @param cMillies Number of milliseconds to wait on input data. */ static DECLCALLBACK(bool) tstDBGCBackInput(PDBGCBACK pBack, uint32_t cMillies) { return g_pszInput != NULL && *g_pszInput != '\0'; } /** * Read input. * * @returns VBox status code. * @param pBack Pointer to the backend structure supplied by * the backend. The backend can use this to find * it's instance data. * @param pvBuf Where to put the bytes we read. * @param cbBuf Maximum nymber of bytes to read. * @param pcbRead Where to store the number of bytes actually read. * If NULL the entire buffer must be filled for a * successful return. */ static DECLCALLBACK(int) tstDBGCBackRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead) { if (g_pszInput && *g_pszInput) { size_t cb = strlen(g_pszInput); if (cb > cbBuf) cb = cbBuf; *pcbRead = cb; memcpy(pvBuf, g_pszInput, cb); g_pszInput += cb; } else *pcbRead = 0; return VINF_SUCCESS; } /** * Write (output). * * @returns VBox status code. * @param pBack Pointer to the backend structure supplied by * the backend. The backend can use this to find * it's instance data. * @param pvBuf What to write. * @param cbBuf Number of bytes to write. * @param pcbWritten Where to store the number of bytes actually written. * If NULL the entire buffer must be successfully written. */ static DECLCALLBACK(int) tstDBGCBackWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten) { const char *pch = (const char *)pvBuf; if (pcbWritten) *pcbWritten = cbBuf; while (cbBuf-- > 0) { /* screen/log output */ if (g_fPendingPrefix) { RTTestPrintfNl(g_hTest, RTTESTLVL_ALWAYS, "OUTPUT: "); g_fPendingPrefix = false; } if (*pch == '\n') g_fPendingPrefix = true; RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "%c", *pch); /* buffer output */ if (g_offOutput < sizeof(g_szOutput) - 1) { g_szOutput[g_offOutput++] = *pch; g_szOutput[g_offOutput] = '\0'; } /* advance */ pch++; } return VINF_SUCCESS; } /** * Ready / busy notification. * * @param pBack Pointer to the backend structure supplied by * the backend. The backend can use this to find * it's instance data. * @param fReady Whether it's ready (true) or busy (false). */ static DECLCALLBACK(void) tstDBGCBackSetReady(PDBGCBACK pBack, bool fReady) { } /** * Completes the output, making sure that we're in * the 1 position of a new line. */ static void tstCompleteOutput(void) { if (!g_fPendingPrefix) RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "\n"); g_fPendingPrefix = true; } /** * Tries one command string. * @param pDbgc Pointer to the debugger instance. * @param pszCmds The command to test. * @param rcCmd The expected result. * @param fNoExecute When set, the command is not executed. * @param pszExpected Expected output. This does not need to include all * of the output, just the start of it. Thus the * prompt can be omitted. */ static void tstTryEx(PDBGC pDbgc, const char *pszCmds, int rcCmd, bool fNoExecute, const char *pszExpected) { RT_ZERO(g_szOutput); g_offOutput = 0; g_pszInput = pszCmds; if (strchr(pszCmds, '\0')[-1] == '\n') RTTestPrintfNl(g_hTest, RTTESTLVL_ALWAYS, "RUNNING: %s", pszCmds); else RTTestPrintfNl(g_hTest, RTTESTLVL_ALWAYS, "RUNNING: %s\n", pszCmds); pDbgc->rcCmd = VERR_INTERNAL_ERROR; dbgcProcessInput(pDbgc, fNoExecute); tstCompleteOutput(); if (pDbgc->rcCmd != rcCmd) RTTestFailed(g_hTest, "rcCmd=%Rrc expected =%Rrc\n", pDbgc->rcCmd, rcCmd); else if ( !fNoExecute && pszExpected && strncmp(pszExpected, g_szOutput, strlen(pszExpected))) RTTestFailed(g_hTest, "Wrong output - expected \"%s\"", pszExpected); } /** * Tries one command string without executing it. * * @param pDbgc Pointer to the debugger instance. * @param pszCmds The command to test. * @param rcCmd The expected result. */ static void tstTry(PDBGC pDbgc, const char *pszCmds, int rcCmd) { return tstTryEx(pDbgc, pszCmds, rcCmd, true /*fNoExecute*/, NULL); } /** * Tries to execute one command string. * @param pDbgc Pointer to the debugger instance. * @param pszCmds The command to test. * @param rcCmd The expected result. * @param pszExpected Expected output. This does not need to include all * of the output, just the start of it. Thus the * prompt can be omitted. */ static void tstTryExec(PDBGC pDbgc, const char *pszCmds, int rcCmd, const char *pszExpected) { return tstTryEx(pDbgc, pszCmds, rcCmd, false /*fNoExecute*/, pszExpected); } /** * Test an operator on an expression resulting a plain number. * * @param pDbgc Pointer to the debugger instance. * @param pszExpr The express to test. * @param u64Expect The expected result. */ static void tstNumOp(PDBGC pDbgc, const char *pszExpr, uint64_t u64Expect) { char szCmd[80]; RTStrPrintf(szCmd, sizeof(szCmd), "format %s\n", pszExpr); char szExpected[80]; RTStrPrintf(szExpected, sizeof(szExpected), "Number: hex %llx dec 0i%lld oct 0t%llo", u64Expect, u64Expect, u64Expect); return tstTryEx(pDbgc, szCmd, VINF_SUCCESS, false /*fNoExecute*/, szExpected); } int main() { /* * Init. */ int rc = RTTestInitAndCreate("tstDBGCParser", &g_hTest); if (rc) return rc; RTTestBanner(g_hTest); /* * Create a DBGC instance. */ PDBGC pDbgc; rc = dbgcCreate(&pDbgc, &g_tstBack, 0); if (RT_SUCCESS(rc)) { rc = dbgcProcessInput(pDbgc, true /* fNoExecute */); tstCompleteOutput(); if (RT_SUCCESS(rc)) { tstTry(pDbgc, "stop\n", VINF_SUCCESS); tstTry(pDbgc, "format 1\n", VINF_SUCCESS); tstTry(pDbgc, "format \n", VERR_PARSE_TOO_FEW_ARGUMENTS); tstTry(pDbgc, "format 0 1 23 4\n", VERR_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "sa 3 23 4 'q' \"21123123\" 'b' \n", VINF_SUCCESS); tstNumOp(pDbgc, "1", 1); tstNumOp(pDbgc, "1", 1); tstNumOp(pDbgc, "1", 1); tstNumOp(pDbgc, "+1", 1); tstNumOp(pDbgc, "++++++1", 1); tstNumOp(pDbgc, "-1", UINT64_MAX); tstNumOp(pDbgc, "--1", 1); tstNumOp(pDbgc, "---1", UINT64_MAX); tstNumOp(pDbgc, "----1", 1); tstNumOp(pDbgc, "~0", UINT64_MAX); tstNumOp(pDbgc, "~1", UINT64_MAX-1); tstNumOp(pDbgc, "~~0", 0); tstNumOp(pDbgc, "~~1", 1); tstNumOp(pDbgc, "!1", 0); tstNumOp(pDbgc, "!0", 1); tstNumOp(pDbgc, "!42", 0); tstNumOp(pDbgc, "!!42", 1); tstNumOp(pDbgc, "!!!42", 0); tstNumOp(pDbgc, "!!!!42", 1); tstNumOp(pDbgc, "1 +1", 2); tstNumOp(pDbgc, "1 + 1", 2); tstNumOp(pDbgc, "1+1", 2); tstNumOp(pDbgc, "1+ 1", 2); tstNumOp(pDbgc, "1 - 1", 0); tstNumOp(pDbgc, "99 - 90", 9); tstNumOp(pDbgc, "2 * 2", 4); tstNumOp(pDbgc, "2 / 2", 1); tstNumOp(pDbgc, "2 / 0", UINT64_MAX); tstNumOp(pDbgc, "0i1024 / 0i4", 256); tstNumOp(pDbgc, "1<<1", 2); tstNumOp(pDbgc, "1<<0i32", 0x0000000100000000); tstNumOp(pDbgc, "1<<0i48", 0x0001000000000000); tstNumOp(pDbgc, "1<<0i63", 0x8000000000000000); tstNumOp(pDbgc, "fedcba0987654321>>0i04", 0x0fedcba098765432); tstNumOp(pDbgc, "fedcba0987654321>>0i32", 0xfedcba09); tstNumOp(pDbgc, "fedcba0987654321>>0i48", 0x0000fedc); tstNumOp(pDbgc, "0ef & 4", 4); tstNumOp(pDbgc, "01234567891 & fff", 0x00000000891); tstNumOp(pDbgc, "01234567891 & ~fff", 0x01234567000); tstNumOp(pDbgc, "1 | 1", 1); tstNumOp(pDbgc, "0 | 4", 4); tstNumOp(pDbgc, "4 | 0", 4); tstNumOp(pDbgc, "4 | 4", 4); tstNumOp(pDbgc, "1 | 4 | 2", 7); tstNumOp(pDbgc, "1 ^ 1", 0); tstNumOp(pDbgc, "1 ^ 0", 1); tstNumOp(pDbgc, "0 ^ 1", 1); tstNumOp(pDbgc, "3 ^ 1", 2); tstNumOp(pDbgc, "7 ^ 3", 4); tstNumOp(pDbgc, "7 || 3", 1); tstNumOp(pDbgc, "1 || 0", 1); tstNumOp(pDbgc, "0 || 1", 1); tstNumOp(pDbgc, "0 || 0", 0); tstNumOp(pDbgc, "0 && 0", 0); tstNumOp(pDbgc, "1 && 0", 0); tstNumOp(pDbgc, "0 && 1", 0); tstNumOp(pDbgc, "1 && 1", 1); tstNumOp(pDbgc, "4 && 1", 1); } dbgcDestroy(pDbgc); } /* * Summary */ return RTTestSummaryAndDestroy(g_hTest); }