VirtualBox

Changeset 70632 in vbox


Ignore:
Timestamp:
Jan 18, 2018 3:20:49 PM (7 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
120356
Message:

ValidationKit/utils/SerialTest: Implement simple read/write test

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ValidationKit/utils/serial/SerialTest.cpp

    r69985 r70632  
    55
    66/*
    7  * Copyright (C) 2017 Oracle Corporation
     7 * Copyright (C) 2017-2018 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3131#include <iprt/err.h>
    3232#include <iprt/getopt.h>
     33#include <iprt/mem.h>
    3334#include <iprt/path.h>
    3435#include <iprt/param.h>
     
    5051*********************************************************************************************************************************/
    5152
    52 
     53/** Pointer to the serial test data instance. */
     54typedef struct SERIALTEST *PSERIALTEST;
     55
     56/**
     57 * Test callback function.
     58 *
     59 * @returns IPRT status code.
     60 * @param   pSerialTest         The serial test instance data.
     61 */
     62typedef DECLCALLBACK(int) FNSERIALTESTRUN(PSERIALTEST pSerialTest);
     63/** Pointer to the serial test callback. */
     64typedef FNSERIALTESTRUN *PFNSERIALTESTRUN;
     65
     66
     67/**
     68 * The serial test instance data.
     69 */
     70typedef struct SERIALTEST
     71{
     72    /** The assigned test handle. */
     73    RTTEST                      hTest;
     74    /** The assigned serial port. */
     75    RTSERIALPORT                hSerialPort;
     76    /** The currently active config. */
     77    PCRTSERIALPORTCFG           pSerialCfg;
     78} SERIALTEST;
     79
     80
     81/**
     82 * Test descriptor.
     83 */
     84typedef struct SERIALTESTDESC
     85{
     86    /** Test ID. */
     87    const char                  *pszId;
     88    /** Test description. */
     89    const char                  *pszDesc;
     90    /** Test run callback. */
     91    PFNSERIALTESTRUN            pfnRun;
     92} SERIALTESTDESC;
     93/** Pointer to a test descriptor. */
     94typedef SERIALTESTDESC *PSERIALTESTDESC;
     95/** Pointer to a constant test descriptor. */
     96typedef const SERIALTESTDESC *PCSERIALTESTDESC;
     97
     98
     99/**
     100 * TX/RX buffer containing a simple counter.
     101 */
     102typedef struct SERIALTESTTXRXBUFCNT
     103{
     104    /** The current counter value. */
     105    uint32_t                    iCnt;
     106    /** Number of bytes left to receive/transmit. */
     107    size_t                      cbTxRxLeft;
     108    /** The offset into the buffer to receive to/send from. */
     109    uint32_t                    offBuf;
     110    /** Maximum size to send/receive before processing is needed again. */
     111    size_t                      cbTxRxMax;
     112    /** The data buffer. */
     113    uint8_t                     abBuf[_1K];
     114} SERIALTESTTXRXBUFCNT;
     115/** Pointer to a TX/RX buffer. */
     116typedef SERIALTESTTXRXBUFCNT *PSERIALTESTTXRXBUFCNT;
    53117
    54118/*********************************************************************************************************************************
     
    66130    {"--stopbits",         's', RTGETOPT_REQ_STRING },
    67131    {"--loopbackdevice",   'l', RTGETOPT_REQ_STRING },
     132    {"--tests",            't', RTGETOPT_REQ_STRING },
     133    {"--txbytes",          'x', RTGETOPT_REQ_UINT32 },
    68134    {"--help",             'h', RTGETOPT_REQ_NOTHING}
    69135};
    70136
     137
     138static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest);
     139
     140/** Implemented tests. */
     141static const SERIALTESTDESC g_aSerialTests[] =
     142{
     143    {"readwrite", "Simple Read/Write test", serialTestRunReadWrite }
     144};
     145
    71146/** The test handle. */
    72 static RTTEST          g_hTest;
     147static RTTEST          g_hTest               = NIL_RTTEST;
    73148/** The serial port handle. */
    74 static RTSERIALPORT    g_hSerialPort = NIL_RTSERIALPORT;
     149static RTSERIALPORT    g_hSerialPort         = NIL_RTSERIALPORT;
    75150/** The loopback serial port handle if configured. */
    76151static RTSERIALPORT    g_hSerialPortLoopback = NIL_RTSERIALPORT;
     152/** Number of bytes to transmit for read/write tests. */
     153static size_t          g_cbTx                = _1M;
    77154/** The config used. */
    78155static RTSERIALPORTCFG g_SerialPortCfg =
     
    89166
    90167
    91 
    92 /**
    93  * Runs the selected serial tests with the given configuration.
     168/**
     169 * Initializes a TX buffer.
     170 *
     171 * @returns nothing.
     172 * @param   pSerBuf             The serial buffer to initialize.
     173 * @param   cbTx                Maximum number of bytes to transmit.
     174 */
     175static void serialTestTxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbTx)
     176{
     177    pSerBuf->iCnt      = 0;
     178    pSerBuf->offBuf    = 0;
     179    pSerBuf->cbTxRxMax = 0;
     180    pSerBuf->cbTxRxLeft = cbTx;
     181    RT_ZERO(pSerBuf->abBuf);
     182}
     183
     184
     185/**
     186 * Initializes a RX buffer.
     187 *
     188 * @returns nothing.
     189 * @param   pSerBuf             The serial buffer to initialize.
     190 * @param   cbRx                Maximum number of bytes to receive.
     191 */
     192static void serialTestRxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbRx)
     193{
     194    pSerBuf->iCnt      = 0;
     195    pSerBuf->offBuf    = 0;
     196    pSerBuf->cbTxRxMax = sizeof(pSerBuf->abBuf);
     197    pSerBuf->cbTxRxLeft = cbRx;
     198    RT_ZERO(pSerBuf->abBuf);
     199}
     200
     201
     202/**
     203 * Prepares the given TX buffer with data for sending it out.
     204 *
     205 * @returns nothing.
     206 * @param   pSerBuf             The TX buffer pointer.
     207 */
     208static void serialTestTxBufPrepare(PSERIALTESTTXRXBUFCNT pSerBuf)
     209{
     210    /* Move the data to the front to make room at the end to fill. */
     211    if (pSerBuf->offBuf)
     212    {
     213        memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[pSerBuf->offBuf], sizeof(pSerBuf->abBuf) - pSerBuf->offBuf);
     214        pSerBuf->offBuf = 0;
     215    }
     216
     217    /* Fill up with data. */
     218    uint32_t offData = 0;
     219    while (pSerBuf->cbTxRxMax + sizeof(uint32_t) <= sizeof(pSerBuf->abBuf))
     220    {
     221        pSerBuf->iCnt++;
     222        *(uint32_t *)&pSerBuf->abBuf[pSerBuf->offBuf + offData] = pSerBuf->iCnt;
     223        pSerBuf->cbTxRxMax += sizeof(uint32_t);
     224        offData            += sizeof(uint32_t);
     225    }
     226}
     227
     228
     229/**
     230 * Sends a new batch of data from the TX buffer preapring new data if required.
    94231 *
    95232 * @returns IPRT status code.
    96  */
    97 static int serialTestRun(void)
    98 {
    99     return VERR_NOT_IMPLEMENTED;
     233 * @param   hSerialPort         The serial port handle to send the data to.
     234 * @param   pSerBuf             The TX buffer pointer.
     235 */
     236static int serialTestTxBufSend(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf)
     237{
     238    int rc = VINF_SUCCESS;
     239
     240    if (pSerBuf->cbTxRxLeft)
     241    {
     242        if (!pSerBuf->cbTxRxMax)
     243            serialTestTxBufPrepare(pSerBuf);
     244
     245        size_t cbToWrite = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft);
     246        size_t cbWritten = 0;
     247        rc = RTSerialPortWriteNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToWrite, &cbWritten);
     248        if (RT_SUCCESS(rc))
     249        {
     250            pSerBuf->cbTxRxMax  -= cbWritten;
     251            pSerBuf->offBuf     += cbWritten;
     252            pSerBuf->cbTxRxLeft -= cbWritten;
     253        }
     254    }
     255
     256    return rc;
     257}
     258
     259
     260/**
     261 * Receives dat from the given serial port into the supplied RX buffer and does some validity checking.
     262 *
     263 * @returns IPRT status code.
     264 * @param   hSerialPort         The serial port handle to receive data from.
     265 * @param   pSerBuf             The RX buffer pointer.
     266 */
     267static int serialTestRxBufRecv(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf)
     268{
     269    int rc = VINF_SUCCESS;
     270
     271    if (pSerBuf->cbTxRxLeft)
     272    {
     273        size_t cbToRead = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft);
     274        size_t cbRead = 0;
     275        rc = RTSerialPortReadNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToRead, &cbRead);
     276        if (RT_SUCCESS(rc))
     277        {
     278            pSerBuf->offBuf     += cbRead;
     279            pSerBuf->cbTxRxMax  -= cbRead;
     280            pSerBuf->cbTxRxLeft -= cbRead;
     281        }
     282    }
     283
     284    return rc;
     285}
     286
     287
     288/**
     289 * Verifies the data in the given RX buffer for correct transmission.
     290 *
     291 * @returns nothing.
     292 * @param   hTest               The test handle to report errors to.
     293 * @param   pSerBuf             The RX buffer pointer.
     294 * @param   iCntTx              The current TX counter value the RX buffer should never get ahead of.
     295 */
     296static void serialTestRxBufVerify(RTTEST hTest, PSERIALTESTTXRXBUFCNT pSerBuf, uint32_t iCntTx)
     297{
     298    uint32_t offRx = 0;
     299
     300    while (offRx + sizeof(uint32_t) < pSerBuf->offBuf)
     301    {
     302        uint32_t u32Val = *(uint32_t *)&pSerBuf->abBuf[offRx];
     303        offRx += sizeof(uint32_t);
     304
     305        if (RT_UNLIKELY(u32Val != ++pSerBuf->iCnt))
     306            RTTestFailed(hTest, "Data corruption/loss detected, expected counter value %u got %u\n",
     307                         pSerBuf->iCnt, u32Val);
     308    }
     309
     310    if (RT_UNLIKELY(pSerBuf->iCnt > iCntTx))
     311        RTTestFailed(hTest, "Overtook the send buffer, expected maximum counter value %u got %u\n",
     312                     iCntTx, pSerBuf->iCnt);
     313
     314    /* Remove processed data from the buffer and move the rest to the front. */
     315    if (offRx)
     316    {
     317        memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[offRx], sizeof(pSerBuf->abBuf) - offRx);
     318        pSerBuf->offBuf    -= offRx;
     319        pSerBuf->cbTxRxMax += offRx;
     320    }
     321}
     322
     323
     324/**
     325 * Runs a simple read/write test.
     326 *
     327 * @returns IPRT status code.
     328 * @param   pSerialTest         The serial test configuration.
     329 */
     330static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest)
     331{
     332    uint64_t tsStart = RTTimeMilliTS();
     333    SERIALTESTTXRXBUFCNT SerBufTx;
     334    SERIALTESTTXRXBUFCNT SerBufRx;
     335
     336    serialTestTxBufInit(&SerBufTx, g_cbTx);
     337    serialTestRxBufInit(&SerBufRx, g_cbTx);
     338
     339    int rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
     340    while (   RT_SUCCESS(rc)
     341           && (   SerBufTx.cbTxRxLeft
     342               || SerBufRx.cbTxRxLeft))
     343    {
     344        uint32_t fEvts = 0;
     345        uint32_t fEvtsQuery = 0;
     346        if (SerBufTx.cbTxRxLeft)
     347            fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_TX;
     348        if (SerBufRx.cbTxRxLeft)
     349            fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_RX;
     350
     351        rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
     352        if (RT_FAILURE(rc))
     353            break;
     354
     355        if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
     356        {
     357            rc = serialTestRxBufRecv(pSerialTest->hSerialPort, &SerBufRx);
     358            if (RT_FAILURE(rc))
     359                break;
     360
     361            serialTestRxBufVerify(pSerialTest->hTest, &SerBufRx, SerBufTx.iCnt);
     362        }
     363        if (   RT_SUCCESS(rc)
     364            && (fEvts & RTSERIALPORT_EVT_F_DATA_TX))
     365            rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
     366    }
     367
     368    uint64_t tsRuntime = RTTimeMilliTS() - tsStart;
     369    tsRuntime /= 1000; /* Seconds */
     370    RTTestValue(pSerialTest->hTest, "Throughput", g_cbTx / tsRuntime, RTTESTUNIT_BYTES_PER_SEC);
     371
     372    return rc;
     373}
     374
     375
     376/**
     377 * Returns an array of test descriptors get from the given string.
     378 *
     379 * @returns Pointer to the array of test descriptors.
     380 * @param   pszTests            The string containing the tests separated with ':'.
     381 */
     382static PSERIALTESTDESC serialTestSelectFromCmdLine(const char *pszTests)
     383{
     384    size_t cTests = 1;
     385
     386    const char *pszNext = strchr(pszTests, ':');
     387    while (pszNext)
     388    {
     389        pszNext++;
     390        cTests++;
     391        pszNext = strchr(pszNext, ':');
     392    }
     393
     394    PSERIALTESTDESC paTests = (PSERIALTESTDESC)RTMemAllocZ((cTests + 1) * sizeof(SERIALTESTDESC));
     395    if (RT_LIKELY(paTests))
     396    {
     397        uint32_t iTest = 0;
     398
     399        pszNext = strchr(pszTests, ':');
     400        while (pszNext)
     401        {
     402            bool fFound = false;
     403
     404            pszNext++; /* Skip : character. */
     405
     406            for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++)
     407            {
     408                if (!RTStrNICmp(pszTests, g_aSerialTests[i].pszId, pszNext - pszTests - 1))
     409                {
     410                    memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC));
     411                    fFound = true;
     412                    break;
     413                }
     414            }
     415
     416            if (RT_UNLIKELY(!fFound))
     417            {
     418                RTPrintf("Testcase \"%.*s\" not known\n", pszNext - pszTests - 1, pszTests);
     419                RTMemFree(paTests);
     420                return NULL;
     421            }
     422
     423            pszTests = pszNext;
     424            pszNext = strchr(pszTests, ':');
     425        }
     426
     427        /* Fill last descriptor. */
     428        bool fFound = false;
     429        for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++)
     430        {
     431            if (!RTStrICmp(pszTests, g_aSerialTests[i].pszId))
     432            {
     433                memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC));
     434                fFound = true;
     435                break;
     436            }
     437        }
     438
     439        if (RT_UNLIKELY(!fFound))
     440        {
     441            RTPrintf("Testcase \"%s\" not known\n", pszTests);
     442            RTMemFree(paTests);
     443            paTests = NULL;
     444        }
     445    }
     446    else
     447        RTPrintf("Failed to allocate test descriptors for %u selected tests\n", cTests);
     448
     449    return paTests;
    100450}
    101451
     
    138488            case 'l':
    139489                pszHelp = "Use the given serial port device as the loopback device";
     490                break;
     491            case 't':
     492                pszHelp = "The tests to run separated by ':'";
     493                break;
     494            case 'x':
     495                pszHelp = "Number of bytes to transmit during read/write tests";
    140496                break;
    141497            default:
     
    164520    const char *pszDevice = NULL;
    165521    const char *pszDeviceLoopback = NULL;
     522    PSERIALTESTDESC paTests = NULL;
    166523
    167524    RTGETOPTUNION ValueUnion;
     
    229586                }
    230587                break;
     588            case 't':
     589                paTests = serialTestSelectFromCmdLine(ValueUnion.psz);
     590                if (!paTests)
     591                    return RTEXITCODE_FAILURE;
     592                break;
     593            case 'x':
     594                g_cbTx = ValueUnion.u32;
     595                break;
    231596            default:
    232597                return RTGetOptPrintError(rc, &ValueUnion);
    233598        }
     599    }
     600
     601    if (!paTests)
     602    {
     603        /* Select all. */
     604        paTests = (PSERIALTESTDESC)RTMemAllocZ((RT_ELEMENTS(g_aSerialTests) + 1) * sizeof(SERIALTESTDESC));
     605        if (RT_UNLIKELY(!paTests))
     606        {
     607            RTPrintf("Failed to allocate memory for test descriptors\n");
     608            return RTEXITCODE_FAILURE;
     609        }
     610        memcpy(paTests, &g_aSerialTests[0], RT_ELEMENTS(g_aSerialTests) * sizeof(SERIALTESTDESC));
    234611    }
    235612
     
    273650
    274651                    if (RT_SUCCESS(rc))
    275                         rc = serialTestRun();
     652                    {
     653                        SERIALTEST Test;
     654                        PSERIALTESTDESC pTest = &paTests[0];
     655
     656                        Test.hTest       = g_hTest;
     657                        Test.hSerialPort = g_hSerialPort;
     658                        Test.pSerialCfg  = &g_SerialPortCfg;
     659
     660                        while (pTest->pszId)
     661                        {
     662                            RTTestSub(g_hTest, pTest->pszDesc);
     663                            rc = pTest->pfnRun(&Test);
     664                            if (RT_FAILURE(rc))
     665                                RTTestFailed(g_hTest, "Running test \"%s\" failed with %Rrc\n", pTest->pszId, rc);
     666
     667                            RTTestSubDone(g_hTest);
     668                            pTest++;
     669                        }
     670                    }
    276671                }
    277672                else
     
    287682        RTTestFailed(g_hTest, "No device given on command line\n");
    288683
     684    RTMemFree(paTests);
    289685    RTEXITCODE rcExit = RTTestSummaryAndDestroy(g_hTest);
    290686    return rcExit;
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