This was acomplished by making errSymbolAdd add the error symbol directly into the global hash table and removing errnumlist which is not needed anymore. Unit tests were added for checking the following cases: - Adding a valid symbol and checking that it exists (fixed by this change) - Getting an existing error symbol - Getting a non existing error symbol - Adding an invalid error symbol (fixed by this change) - Adding an error symbol with a code that already exists (fixed by this change) Therefore, issue #268 was fixed error: statically allocate error symbol hash table This will allow calling errSymbolAdd before errSymBld, therefore, a function adding error symbols can now be run before iocInit or errlogInit error: add a constant for the minimum module number Make adding an identical error symbol not fail A test case was also added which test that adding an error symbol with same error code and message as one added before will not fail Add locking to error symbol table This protects the cases of: - simultaneously adding and requesting of an error symbol - simultaneously adding many error symbols Update release notes regarding adding error symbols
611 lines
17 KiB
C
611 lines
17 KiB
C
/*************************************************************************\
|
|
* Copyright (c) 2010 Brookhaven Science Associates, as Operator of
|
|
* Brookhaven National Laboratory.
|
|
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
|
* National Laboratory.
|
|
* SPDX-License-Identifier: EPICS
|
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
/*
|
|
* Author: Michael Davidsaver
|
|
* Date: 2010-09-24
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "epicsAssert.h"
|
|
#include "epicsThread.h"
|
|
#include "epicsEvent.h"
|
|
#include "dbDefs.h"
|
|
#include "errlog.h"
|
|
#include "epicsUnitTest.h"
|
|
#include "testMain.h"
|
|
#include "iocLog.h"
|
|
#include "logClient.h"
|
|
#include "envDefs.h"
|
|
#include "osiSock.h"
|
|
#include "fdmgr.h"
|
|
#include "epicsString.h"
|
|
#include "errSymTbl.h"
|
|
|
|
/* private between errlog.c and this test */
|
|
LIBCOM_API
|
|
void errlogStripANSI(char *msg);
|
|
|
|
#define LOGBUFSIZE 2048
|
|
|
|
static
|
|
const char longmsg[]="A0123456789abcdef"
|
|
"B0123456789abcdef"
|
|
"C0123456789abcdef"
|
|
"D0123456789abcdef"
|
|
"E0123456789abcdef"
|
|
"F0123456789abcdef"
|
|
"G0123456789abcdef"
|
|
"H0123456789abcdef"
|
|
"I0123456789abcdef"
|
|
"J0123456789abcdef"
|
|
"K0123456789abcdef"
|
|
"L0123456789abcdef"
|
|
"M0123456789abcdef"
|
|
"N0123456789abcdef"
|
|
"O0123456789abcdef"
|
|
"P0123456789abcdef"
|
|
"Q0123456789abcdef"
|
|
"R0123456789abcdef"
|
|
"S0123456789abcdef"
|
|
;
|
|
STATIC_ASSERT(NELEMENTS(longmsg)==324);
|
|
|
|
static
|
|
const char truncmsg[]="A0123456789abcdef"
|
|
"B0123456789abcdef"
|
|
"C0123456789abcdef"
|
|
"D0123456789abcdef"
|
|
"E0123456789abcdef"
|
|
"F0123456789abcdef"
|
|
"G0123456789abcdef"
|
|
"H0123456789abcdef"
|
|
"I0123456789abcdef"
|
|
"J0123456789abcdef"
|
|
"K0123456789abcdef"
|
|
"L0123456789abcdef"
|
|
"M0123456789abcdef"
|
|
"N0123456789abcdef"
|
|
"O01<<TRUNCATED>>\n"
|
|
;
|
|
STATIC_ASSERT(NELEMENTS(truncmsg)==256);
|
|
|
|
typedef struct {
|
|
unsigned int count;
|
|
const char* expect;
|
|
size_t checkLen;
|
|
epicsEventId jammer;
|
|
int jam;
|
|
epicsEventId done;
|
|
} clientPvt;
|
|
|
|
static void testLogPrefix(void);
|
|
static void acceptNewClient( void *pParam );
|
|
static void readFromClient( void *pParam );
|
|
static void testPrefixLogandCompare( const char* logmessage);
|
|
|
|
static void *pfdctx;
|
|
static SOCKET sock;
|
|
static SOCKET insock;
|
|
|
|
static const char* prefixactualmsg[]= {
|
|
"A message without prefix",
|
|
"A message with prefix",
|
|
"DONE"
|
|
};
|
|
static const char *prefixstring = "fac=LI21 ";
|
|
static const char prefixexpectedmsg[] = "A message without prefix"
|
|
"fac=LI21 A message with prefix"
|
|
"fac=LI21 DONE"
|
|
;
|
|
static char prefixmsgbuffer[1024];
|
|
|
|
|
|
static void
|
|
testEqInt_(int line, int lhs, int rhs, const char *LHS, const char *RHS)
|
|
{
|
|
testOk(lhs==rhs, "%d: %s (%d) == %s (%d)", line, LHS, lhs, RHS, rhs);
|
|
}
|
|
#define testEqInt(L, R) testEqInt_(__LINE__, L, R, #L, #R);
|
|
|
|
static
|
|
void logClient(void* raw, const char* msg)
|
|
{
|
|
clientPvt *pvt = raw;
|
|
size_t len;
|
|
char show[46];
|
|
|
|
/* Simulate thread priority on non-realtime
|
|
* OSs like Linux. This will cause the logging
|
|
* thread to sleep with the buffer lock held.
|
|
*/
|
|
if (pvt->jam > 0) {
|
|
pvt->jam = 0;
|
|
epicsEventMustWait(pvt->jammer);
|
|
} else if (pvt->jam < 0) {
|
|
pvt->jam++;
|
|
if (pvt->jam == 0)
|
|
epicsEventMustWait(pvt->jammer);
|
|
}
|
|
|
|
len = strlen(msg);
|
|
if (len > 45) {
|
|
/* Only show start and end of long messages */
|
|
strncpy(show, msg, 20);
|
|
show[20] = 0;
|
|
strcat(show + 20, " ... ");
|
|
strcat(show + 25, msg + len - 20);
|
|
}
|
|
else {
|
|
strcpy(show, msg);
|
|
}
|
|
|
|
if (pvt->checkLen)
|
|
if (!testOk(pvt->checkLen == len, "Received %d chars", (int) len)) {
|
|
testDiag("Expected %d", (int) pvt->checkLen);
|
|
if (!pvt->expect)
|
|
testDiag("Message is \"%s\"", show);
|
|
}
|
|
|
|
if (pvt->expect) {
|
|
if (!testOk(strcmp(pvt->expect, msg) == 0, "Message is \"%s\"", show)) {
|
|
len = strlen(pvt->expect);
|
|
if (len > 45) {
|
|
testDiag("Expected \"%.20s ... %s\"",
|
|
pvt->expect, pvt->expect + len - 20);
|
|
}
|
|
else {
|
|
testDiag("Expected \"%s\"", pvt->expect);
|
|
}
|
|
}
|
|
}
|
|
|
|
pvt->count++;
|
|
epicsEventSignal(pvt->done);
|
|
}
|
|
|
|
static
|
|
void testANSIStrip(void)
|
|
{
|
|
char scratch[64];
|
|
char actual[128];
|
|
char input[128];
|
|
#define testEscape(INP, EXPECT) \
|
|
strcpy(scratch, INP); \
|
|
epicsStrnEscapedFromRaw(input, sizeof(input), INP, sizeof(INP)); \
|
|
errlogStripANSI(scratch); \
|
|
epicsStrnEscapedFromRaw(actual, sizeof(actual), scratch, epicsStrnLen(scratch, sizeof(scratch))); \
|
|
testOk(strcmp(scratch, EXPECT)==0, "input \"%s\" expect \"%s\" actual \"%s\"", input, EXPECT, actual)
|
|
|
|
testEscape("", "");
|
|
testEscape("hello", "hello");
|
|
testEscape("he\033[31;1mllo", "hello");
|
|
testEscape("\033[31;1mhello", "hello");
|
|
testEscape("hello\033[31;1m", "hello");
|
|
testEscape("hello\033[31;1", "hello");
|
|
testEscape("hello\033[", "hello");
|
|
testEscape("hello\033", "hello");
|
|
|
|
#undef testEscape
|
|
}
|
|
|
|
static void testErrorMessageMatches(long status, const char *expected)
|
|
{
|
|
const char *msg = errSymMsg(status);
|
|
testOk(strcmp(msg, expected) == 0,
|
|
"Error code %ld returns \"%s\", expected message \"%s\"", status,
|
|
msg, expected
|
|
);
|
|
}
|
|
|
|
static void testGettingExistingErrorSymbol()
|
|
{
|
|
testErrorMessageMatches(S_err_invCode, "Invalid error symbol code");
|
|
}
|
|
|
|
static void testGettingNonExistingErrorSymbol()
|
|
{
|
|
long invented_code = (0x7999 << 16) | 0x9998;
|
|
testErrorMessageMatches(invented_code, "<Unknown code>");
|
|
}
|
|
|
|
static void testAddingErrorSymbol()
|
|
{
|
|
long invented_code = (0x7999 << 16) | 0x9999;
|
|
errSymbolAdd(invented_code, "Invented Error Message");
|
|
testErrorMessageMatches(invented_code, "Invented Error Message");
|
|
}
|
|
|
|
static void testAddingInvalidErrorSymbol()
|
|
{
|
|
long invented_code = (500 << 16) | 0x1;
|
|
testOk(errSymbolAdd(invented_code, "No matter"),
|
|
"Adding error symbol with module code < 501 should fail");
|
|
}
|
|
|
|
static void testAddingExistingErrorSymbol()
|
|
{
|
|
testOk(errSymbolAdd(S_err_invCode, "Duplicate"),
|
|
"Adding an error symbol with an existing error code should fail");
|
|
}
|
|
|
|
static void testAddingExistingErrorSymbolWithSameMessage()
|
|
{
|
|
long invented_code = (0x7999 << 16) | 0x9997;
|
|
errSymbolAdd(invented_code, "Invented Error Message");
|
|
testOk(!errSymbolAdd(invented_code, "Invented Error Message"),
|
|
"Adding identical error symbol shouldn't fail");
|
|
}
|
|
|
|
MAIN(epicsErrlogTest)
|
|
{
|
|
size_t mlen, i, N;
|
|
char msg[256];
|
|
clientPvt pvt, pvt2;
|
|
|
|
testPlan(54);
|
|
|
|
testANSIStrip();
|
|
|
|
strcpy(msg, truncmsg);
|
|
|
|
errlogInit2(LOGBUFSIZE, 256);
|
|
|
|
pvt.count = 0;
|
|
pvt2.count = 0;
|
|
|
|
pvt.expect = NULL;
|
|
pvt2.expect = NULL;
|
|
|
|
pvt.checkLen = 0;
|
|
pvt2.checkLen = 0;
|
|
|
|
pvt.jam = 0;
|
|
pvt2.jam = 0;
|
|
|
|
pvt.jammer = epicsEventMustCreate(epicsEventEmpty);
|
|
pvt.done = epicsEventMustCreate(epicsEventEmpty);
|
|
pvt2.jammer = epicsEventMustCreate(epicsEventEmpty);
|
|
pvt2.done = epicsEventMustCreate(epicsEventEmpty);
|
|
|
|
testDiag("Check listener registration");
|
|
|
|
errlogAddListener(&logClient, &pvt);
|
|
|
|
pvt.expect = "Testing";
|
|
pvt.checkLen = strlen(pvt.expect);
|
|
|
|
errlogPrintfNoConsole("%s", pvt.expect);
|
|
errlogFlush();
|
|
|
|
epicsEventMustWait(pvt.done);
|
|
testEqInt(pvt.count, 1);
|
|
|
|
errlogAddListener(&logClient, &pvt2);
|
|
|
|
/* logClient will not see ANSI escape sequences */
|
|
pvt2.expect = pvt.expect = "Testing2";
|
|
pvt2.checkLen = pvt.checkLen = strlen(pvt.expect);
|
|
|
|
errlogPrintfNoConsole("%s", ANSI_RED("Testing2"));
|
|
errlogFlush();
|
|
|
|
epicsEventMustWait(pvt.done);
|
|
testEqInt(pvt.count, 2);
|
|
|
|
epicsEventMustWait(pvt2.done);
|
|
testEqInt(pvt2.count, 1);
|
|
|
|
/* Removes the first listener */
|
|
testOk(1 == errlogRemoveListeners(&logClient, &pvt),
|
|
"Removed 1 listener");
|
|
|
|
pvt2.expect = "Testing3";
|
|
pvt2.checkLen = strlen(pvt2.expect);
|
|
|
|
errlogPrintfNoConsole("%s", pvt2.expect);
|
|
errlogFlush();
|
|
|
|
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
|
"%d: Listener 1 didn't run", __LINE__);
|
|
testOk(epicsEventTryWait(pvt2.done) == epicsEventOK,
|
|
"%d: Listener 2 ran", __LINE__);
|
|
testEqInt(pvt.count, 2);
|
|
testEqInt(pvt2.count, 2);
|
|
|
|
/* Add the second listener again, then remove both instances */
|
|
errlogAddListener(&logClient, &pvt2);
|
|
testOk(2 == errlogRemoveListeners(&logClient, &pvt2),
|
|
"Removed 2 listeners");
|
|
|
|
errlogPrintfNoConsole("Something different");
|
|
errlogFlush();
|
|
|
|
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
|
"%d: Listener 1 didn't run", __LINE__);
|
|
testOk(epicsEventTryWait(pvt2.done) == epicsEventWaitTimeout,
|
|
"%d: Listener 2 didn't run", __LINE__);
|
|
testEqInt(pvt.count, 2);
|
|
testEqInt(pvt2.count, 2);
|
|
|
|
/* Re-add one listener */
|
|
errlogAddListener(&logClient, &pvt);
|
|
|
|
testDiag("Check truncation");
|
|
|
|
pvt.expect = truncmsg;
|
|
pvt.checkLen = 255;
|
|
|
|
errlogPrintfNoConsole("%s", longmsg);
|
|
errlogFlush();
|
|
|
|
epicsEventMustWait(pvt.done);
|
|
testEqInt(pvt.count, 3);
|
|
|
|
pvt.expect = NULL;
|
|
|
|
testDiag("Check priority");
|
|
/* For the following tests it is important that
|
|
* the buffer should not flush until we request it
|
|
*/
|
|
pvt.jam = 1;
|
|
|
|
errlogPrintfNoConsole("%s", longmsg);
|
|
|
|
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
|
"%d: Listener 1 didn't run", __LINE__);
|
|
testEqInt(pvt.count, 3);
|
|
|
|
epicsEventSignal(pvt.jammer);
|
|
errlogFlush();
|
|
|
|
epicsEventMustWait(pvt.done);
|
|
testEqInt(pvt.count, 4);
|
|
|
|
testDiag("Find buffer capacity (%u theoretical)",LOGBUFSIZE);
|
|
|
|
pvt.checkLen = 0;
|
|
|
|
for (mlen = 8; mlen <= 255; mlen *= 2) {
|
|
double eff;
|
|
char save = msg[mlen - 1];
|
|
|
|
N = LOGBUFSIZE / mlen; /* # of of messages to send */
|
|
msg[mlen - 1] = '\0';
|
|
pvt.count = 0;
|
|
/* pvt.checkLen = mlen - 1; */
|
|
|
|
pvt.jam = 1;
|
|
|
|
for (i = 0; i < N; i++) {
|
|
errlogPrintfNoConsole("%s", msg);
|
|
}
|
|
|
|
epicsEventSignal(pvt.jammer);
|
|
errlogFlush();
|
|
|
|
eff = (double) (pvt.count * mlen) / LOGBUFSIZE * 100.0;
|
|
testDiag(" For %d messages of length %d got %u (%.1f%% efficient)",
|
|
(int) N, (int) mlen, pvt.count, eff);
|
|
|
|
msg[mlen - 1] = save;
|
|
N = pvt.count; /* Save final count for the test below */
|
|
|
|
/* Clear "errlog: <n> messages were discarded" status */
|
|
pvt.checkLen = 0;
|
|
errlogPrintfNoConsole(".");
|
|
errlogFlush();
|
|
}
|
|
|
|
testOk(epicsEventTryWait(pvt.done) == epicsEventOK,
|
|
"%d: Listener 1 ran", __LINE__);
|
|
|
|
testDiag("Checking buffer use after partial flush");
|
|
|
|
/* Use the numbers from the largest block size above */
|
|
mlen /= 2;
|
|
msg[mlen - 1] = '\0';
|
|
|
|
pvt.jam = 1;
|
|
pvt.count = 0;
|
|
|
|
testDiag("Filling with %d messages of size %d", (int) N, (int) mlen);
|
|
|
|
for (i = 0; i < N; i++) {
|
|
errlogPrintfNoConsole("%s", msg);
|
|
}
|
|
|
|
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
|
"%d: Listener 1 didn't run", __LINE__);
|
|
testEqInt(pvt.count, 0);
|
|
|
|
/* Extract the first 2 messages, 2*(sizeof(msgNode) + 128) bytes */
|
|
pvt.jam = -2;
|
|
epicsEventSignal(pvt.jammer);
|
|
epicsThreadSleep(0.5);
|
|
|
|
testDiag("Drained %u messages", pvt.count);
|
|
epicsEventMustWait(pvt.done);
|
|
testEqInt(pvt.count, 2);
|
|
|
|
/* The buffer has space for 1 more message: sizeof(msgNode) + 256 bytes */
|
|
errlogPrintfNoConsole("%s", msg); /* Use up that space */
|
|
|
|
testDiag("Overflow the buffer");
|
|
errlogPrintfNoConsole("%s", msg);
|
|
|
|
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
|
"%d: Listener 1 didn't run", __LINE__);
|
|
testEqInt(pvt.count, 2);
|
|
|
|
epicsEventSignal(pvt.jammer); /* Empty */
|
|
errlogFlush();
|
|
|
|
testDiag("Logged %u messages", pvt.count);
|
|
epicsEventMustWait(pvt.done);
|
|
/* Expect N+1 messages +- 1 depending on impl */
|
|
testOk(pvt.count >= N && pvt.count<=N+2, "Logged %u messages, expected %zu", pvt.count, N+1);
|
|
|
|
/* Clean up */
|
|
testOk(1 == errlogRemoveListeners(&logClient, &pvt),
|
|
"Removed 1 listener");
|
|
|
|
osiSockAttach();
|
|
testLogPrefix();
|
|
osiSockRelease();
|
|
|
|
testGettingExistingErrorSymbol();
|
|
testGettingNonExistingErrorSymbol();
|
|
testAddingErrorSymbol();
|
|
testAddingInvalidErrorSymbol();
|
|
testAddingExistingErrorSymbol();
|
|
testAddingExistingErrorSymbolWithSameMessage();
|
|
|
|
return testDone();
|
|
}
|
|
/*
|
|
* Tests the log prefix code
|
|
* The prefix is only applied to log messages as they go out to the socket,
|
|
* so we need to create a server listening on a port, accept connections etc.
|
|
* This code is a reduced version of the code in iocLogServer.
|
|
*/
|
|
static void testLogPrefix(void) {
|
|
struct sockaddr_in serverAddr;
|
|
int status;
|
|
struct timeval timeout;
|
|
struct sockaddr_in actualServerAddr;
|
|
osiSocklen_t actualServerAddrSize;
|
|
char portstring[16];
|
|
|
|
|
|
testDiag("Testing iocLogPrefix");
|
|
|
|
timeout.tv_sec = 5; /* in seconds */
|
|
timeout.tv_usec = 0;
|
|
|
|
memset((void*)prefixmsgbuffer, 0, sizeof prefixmsgbuffer);
|
|
|
|
/* Clear "errlog: <n> messages were discarded" status */
|
|
errlogPrintfNoConsole(".");
|
|
errlogFlush();
|
|
|
|
sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
|
|
if (sock == INVALID_SOCKET) {
|
|
testAbort("epicsSocketCreate failed.");
|
|
}
|
|
|
|
/* We listen on a an available port. */
|
|
memset((void *)&serverAddr, 0, sizeof serverAddr);
|
|
serverAddr.sin_family = AF_INET;
|
|
serverAddr.sin_port = htons(0);
|
|
|
|
status = bind (sock, (struct sockaddr *)&serverAddr,
|
|
sizeof (serverAddr) );
|
|
if (status < 0) {
|
|
testAbort("bind failed; all ports in use?");
|
|
}
|
|
|
|
status = listen(sock, 10);
|
|
if (status < 0) {
|
|
testAbort("listen failed!");
|
|
}
|
|
|
|
/* Determine the port that the OS chose */
|
|
actualServerAddrSize = sizeof actualServerAddr;
|
|
memset((void *)&actualServerAddr, 0, sizeof serverAddr);
|
|
status = getsockname(sock, (struct sockaddr *) &actualServerAddr,
|
|
&actualServerAddrSize);
|
|
if (status < 0) {
|
|
testAbort("Can't find port number!");
|
|
}
|
|
|
|
sprintf(portstring, "%d", ntohs(actualServerAddr.sin_port));
|
|
testDiag("Listening on port %s", portstring);
|
|
|
|
/* Set the EPICS environment variables for logging. */
|
|
epicsEnvSet ( "EPICS_IOC_LOG_INET", "localhost" );
|
|
epicsEnvSet ( "EPICS_IOC_LOG_PORT", portstring );
|
|
|
|
pfdctx = (void *) fdmgr_init();
|
|
if (status < 0) {
|
|
testAbort("fdmgr_init failed!");
|
|
}
|
|
|
|
status = fdmgr_add_callback(pfdctx, sock, fdi_read,
|
|
acceptNewClient, &serverAddr);
|
|
|
|
if (status < 0) {
|
|
testAbort("fdmgr_add_callback failed!");
|
|
}
|
|
|
|
testOk1(iocLogInit() == 0);
|
|
fdmgr_pend_event(pfdctx, &timeout);
|
|
|
|
testPrefixLogandCompare(prefixactualmsg[0]);
|
|
|
|
iocLogPrefix(prefixstring);
|
|
testPrefixLogandCompare(prefixactualmsg[1]);
|
|
testPrefixLogandCompare(prefixactualmsg[2]);
|
|
epicsSocketDestroy(sock);
|
|
}
|
|
|
|
static void testPrefixLogandCompare( const char* logmessage ) {
|
|
struct timeval timeout;
|
|
timeout.tv_sec = 5; /* in seconds */
|
|
timeout.tv_usec = 0;
|
|
|
|
errlogPrintfNoConsole("%s", logmessage);
|
|
errlogFlush();
|
|
iocLogFlush();
|
|
fdmgr_pend_event(pfdctx, &timeout);
|
|
}
|
|
|
|
static void acceptNewClient ( void *pParam )
|
|
{
|
|
osiSocklen_t addrSize;
|
|
struct sockaddr_in addr;
|
|
int status;
|
|
|
|
addrSize = sizeof ( addr );
|
|
insock = epicsSocketAccept ( sock, (struct sockaddr *)&addr, &addrSize );
|
|
testOk(insock != INVALID_SOCKET && addrSize >= sizeof (addr),
|
|
"Accepted new client");
|
|
|
|
status = fdmgr_add_callback(pfdctx, insock, fdi_read,
|
|
readFromClient, NULL);
|
|
|
|
testOk(status >= 0, "Client read configured");
|
|
}
|
|
|
|
static void readFromClient(void *pParam)
|
|
{
|
|
char recvbuf[1024];
|
|
int recvLength;
|
|
|
|
memset(recvbuf, 0, 1024);
|
|
recvLength = recv(insock, recvbuf, 1024, 0);
|
|
if (recvLength > 0) {
|
|
strcat(prefixmsgbuffer, recvbuf);
|
|
|
|
/* If we have received all of the messages. */
|
|
if (strstr(prefixmsgbuffer, "DONE") != NULL) {
|
|
size_t msglen = strlen(prefixexpectedmsg);
|
|
int prefixcmp = strncmp(prefixexpectedmsg, prefixmsgbuffer, msglen);
|
|
|
|
if (!testOk(prefixcmp == 0, "prefix matches")) {
|
|
testDiag("Expected '%s'\n", prefixexpectedmsg);
|
|
testDiag("Obtained '%s'\n", prefixmsgbuffer);
|
|
}
|
|
}
|
|
}
|
|
}
|