1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-06-18 23:57:33 +02:00

Improve fork harness to allow multiple children and setup pipes automatically.

There was a lot of extra boilerplate involved in setting up pipes so that is now automated.

In some cases testing with multiple children is useful so allow that as well.
This commit is contained in:
David Steele
2019-02-27 18:07:16 +02:00
parent 18b62a4220
commit 4be271ea2a
12 changed files with 231 additions and 171 deletions

View File

@ -115,6 +115,10 @@
<p>Create test matrix for <id>mock/archive</id> and <id>mock/stanza</id> to increase coverage and reduce tests.</p> <p>Create test matrix for <id>mock/archive</id> and <id>mock/stanza</id> to increase coverage and reduce tests.</p>
</release-item> </release-item>
<release-item>
<p>Improve fork harness to allow multiple children and setup pipes automatically.</p>
</release-item>
<release-item> <release-item>
<p>Reduce expect log level in <id>mock/archive</id> and <id>mock/stanza</id> tests.</p> <p>Reduce expect log level in <id>mock/archive</id> and <id>mock/stanza</id> tests.</p>
</release-item> </release-item>

View File

@ -8,96 +8,197 @@ The general form of the fork harness is:
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
// This block is required // This block is required and can be repeated up to HARNESS_FORK_CHILD_MAX times.
HARNESS_FORK_CHILD() //
// The first parameter is the expected exit code. If the child block does not have an explicit exit then it will automatically
// exit on 0.
//
// The second paramater specifies whether pipes should be setup between the parent and child processes. These can be accessed
// with the HARNESS_FORK_*() macros;
HARNESS_FORK_CHILD_BEGIN(0, true)
{ {
// Child test code goes here // Child test code goes here
} }
HARNESS_FORK_CHILD_END();
// This block is optional // This block is optional but generally useful
HARNESS_FORK_PARENT() HARNESS_FORK_PARENT_BEGIN()
{ {
// Parent test code goes here // Parent test code goes here
} }
HARNESS_FORK_PARENT_END();
// If the exit result from the child process is expected to be non-zero then that must be set with this optional statement
HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS_SET(<non-zero exit status>);
} }
HARNESS_FORK_END() HARNESS_FORK_END()
If the child process does not explicitly exit in HARNESS_FORK_CHILD() then it will exit with 0 at HARNESS_FORK_END(). This harness If the child process does not explicitly exit in HARNESS_FORK_CHILD_BEGIN/END() then it will exit with 0 at HARNESS_FORK_END().
is not intended for long-lived child processes. This harness is not intended for long-lived child processes.
There should not be any code outside the HARNESS_FORK_CHILD() and HARNESS_FORK_PARENT() blocks except the There should not be any code outside the HARNESS_FORK_CHILD_BEGIN/END() and HARNESS_FORK_PARENT_BEGIN/END() blocks.
HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS_SET() macro unless the code is intended to run in both the child and parent process which is
rare.
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
/*********************************************************************************************************************************** /***********************************************************************************************************************************
HARNESS_FORK_PROCESS_ID() Define the max number of child processes allowed
Return the id of the child process, 0 if in the child process.
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#define HARNESS_FORK_PROCESS_ID() \ #define HARNESS_FORK_CHILD_MAX 4
HARNESS_FORK_processId
/*********************************************************************************************************************************** /***********************************************************************************************************************************
HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS() Total number of child processes forked
***********************************************************************************************************************************/
#define HARNESS_FORK_PROCESS_TOTAL() \
HARNESS_FORK_processTotal
/***********************************************************************************************************************************
Return the process index of the child (i.e. the index in the total)
***********************************************************************************************************************************/
#define HARNESS_FORK_PROCESS_IDX() \
HARNESS_FORK_processIdx
/***********************************************************************************************************************************
Return the id of the child process, 0 if in the child process
***********************************************************************************************************************************/
#define HARNESS_FORK_PROCESS_ID(processId) \
HARNESS_FORK_processIdList[processId]
/***********************************************************************************************************************************
Return the pipe for the child process
***********************************************************************************************************************************/
#define HARNESS_FORK_PIPE(processId) \
HARNESS_FORK_pipe[processId]
/***********************************************************************************************************************************
Is the pipe required for this child process?
***********************************************************************************************************************************/
#define HARNESS_FORK_PIPE_REQUIRED(processId) \
HARNESS_FORK_pipeRequired[processId]
/***********************************************************************************************************************************
Get read/write pipe handles
***********************************************************************************************************************************/
#define HARNESS_FORK_CHILD_READ_PROCESS(processId) \
HARNESS_FORK_PIPE(processId)[1][0]
#define HARNESS_FORK_CHILD_READ() \
HARNESS_FORK_CHILD_READ_PROCESS(HARNESS_FORK_PROCESS_IDX())
#define HARNESS_FORK_CHILD_WRITE_PROCESS(processId) \
HARNESS_FORK_PIPE(processId)[0][1]
#define HARNESS_FORK_CHILD_WRITE() \
HARNESS_FORK_CHILD_WRITE_PROCESS(HARNESS_FORK_PROCESS_IDX())
#define HARNESS_FORK_PARENT_READ_PROCESS(processId) \
HARNESS_FORK_PIPE(processId)[0][0]
#define HARNESS_FORK_PARENT_WRITE_PROCESS(processId) \
HARNESS_FORK_PIPE(processId)[1][1]
/***********************************************************************************************************************************
At the end of the HARNESS_FORK block the parent will wait for the child to exit. By default an exit code of 0 is expected but that At the end of the HARNESS_FORK block the parent will wait for the child to exit. By default an exit code of 0 is expected but that
can be modified with the _SET macro can be modified when the child begins.
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#define HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS() \ #define HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS(processId) \
HARNESS_FORK_expectedExitCode HARNESS_FORK_expectedExitStatus[processId]
#define HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS_SET(expectedExitStatus) \
HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS() = expectedExitStatus;
/*********************************************************************************************************************************** /***********************************************************************************************************************************
HARNESS_FORK_BEGIN() Begin the fork block
Performs the fork and stores the process id.
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#define HARNESS_FORK_BEGIN() \ #define HARNESS_FORK_BEGIN() \
{ \ do \
pid_t HARNESS_FORK_PROCESS_ID() = fork(); \ { \
int HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS() = 0; unsigned int HARNESS_FORK_PROCESS_TOTAL() = 0; \
pid_t HARNESS_FORK_PROCESS_ID(HARNESS_FORK_CHILD_MAX) = {0}; \
int HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS(HARNESS_FORK_CHILD_MAX) = {0}; \
bool HARNESS_FORK_PIPE_REQUIRED(HARNESS_FORK_CHILD_MAX) = {0}; \
int HARNESS_FORK_PIPE(HARNESS_FORK_CHILD_MAX)[2][2] = {{{0}}};
/*********************************************************************************************************************************** /***********************************************************************************************************************************
HARNESS_FORK_CHILD() Create a child process
Is this the child process?
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#define HARNESS_FORK_CHILD() \ #define HARNESS_FORK_CHILD_BEGIN(expectedExitStatus, pipeRequired) \
if (HARNESS_FORK_PROCESS_ID() == 0) do \
{ \
HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS(HARNESS_FORK_PROCESS_TOTAL()) = expectedExitStatus; \
\
if (pipeRequired) \
{ \
HARNESS_FORK_PIPE_REQUIRED(HARNESS_FORK_PROCESS_TOTAL()) = true; \
\
THROW_ON_SYS_ERROR_FMT( \
pipe(HARNESS_FORK_PIPE(HARNESS_FORK_PROCESS_TOTAL())[0]) == -1, KernelError, \
"unable to create read pipe for child process %u", HARNESS_FORK_PROCESS_TOTAL()); \
THROW_ON_SYS_ERROR_FMT( \
pipe(HARNESS_FORK_PIPE(HARNESS_FORK_PROCESS_TOTAL())[1]) == -1, KernelError, \
"unable to create write pipe for child process %u", HARNESS_FORK_PROCESS_TOTAL()); \
} \
\
HARNESS_FORK_PROCESS_ID(HARNESS_FORK_PROCESS_TOTAL()) = fork(); \
\
if (HARNESS_FORK_PROCESS_ID(HARNESS_FORK_PROCESS_TOTAL()) == 0) \
{ \
unsigned int HARNESS_FORK_PROCESS_IDX() = HARNESS_FORK_PROCESS_TOTAL(); \
\
if (pipeRequired) \
{ \
close(HARNESS_FORK_PARENT_READ_PROCESS(HARNESS_FORK_PROCESS_IDX())); \
close(HARNESS_FORK_PARENT_WRITE_PROCESS(HARNESS_FORK_PROCESS_IDX())); \
}
#define HARNESS_FORK_CHILD_END() \
if (HARNESS_FORK_PIPE_REQUIRED(HARNESS_FORK_PROCESS_IDX())) \
{ \
close(HARNESS_FORK_CHILD_READ()); \
close(HARNESS_FORK_CHILD_WRITE()); \
} \
\
exit(0); \
} \
\
HARNESS_FORK_PROCESS_TOTAL()++; \
} \
while (0)
/*********************************************************************************************************************************** /***********************************************************************************************************************************
HARNESS_FORK_PARENT() Process in the parent
Is this the parent process?
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#define HARNESS_FORK_PARENT() \ #define HARNESS_FORK_PARENT_BEGIN() \
if (HARNESS_FORK_PROCESS_ID() != 0) do \
{ \
for (unsigned int processIdx = 0; processIdx < HARNESS_FORK_PROCESS_TOTAL(); processIdx++) \
{ \
if (HARNESS_FORK_PIPE_REQUIRED(processIdx)) \
{ \
close(HARNESS_FORK_CHILD_READ_PROCESS(processIdx)); \
close(HARNESS_FORK_CHILD_WRITE_PROCESS(processIdx)); \
} \
}
#define HARNESS_FORK_PARENT_END() \
for (unsigned int processIdx = 0; processIdx < HARNESS_FORK_PROCESS_TOTAL(); processIdx++) \
{ \
if (HARNESS_FORK_PIPE_REQUIRED(processIdx)) \
{ \
close(HARNESS_FORK_PARENT_READ_PROCESS(processIdx)); \
close(HARNESS_FORK_PARENT_WRITE_PROCESS(processIdx)); \
} \
} \
} \
while (0)
/*********************************************************************************************************************************** /***********************************************************************************************************************************
HARNESS_FORK_END() End the fork block and check exit status for all child processes
Finish the fork block by waiting for the child to exit.
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#define HARNESS_FORK_END() \ #define HARNESS_FORK_END() \
HARNESS_FORK_CHILD() \ for (unsigned int processIdx = 0; processIdx < HARNESS_FORK_PROCESS_TOTAL(); processIdx++) \
exit(0); \ { \
int processStatus; \
\ \
HARNESS_FORK_PARENT() \ if (waitpid(HARNESS_FORK_PROCESS_ID(processIdx), &processStatus, 0) != HARNESS_FORK_PROCESS_ID(processIdx)) \
{ \ THROW_SYS_ERROR_FMT(AssertError, "unable to find child process %u", processIdx); \
int processStatus; \
\ \
if (waitpid(HARNESS_FORK_PROCESS_ID(), &processStatus, 0) != HARNESS_FORK_PROCESS_ID()) \ if (WEXITSTATUS(processStatus) != HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS(processIdx)) \
THROW_SYS_ERROR(AssertError, "unable to find child process"); \ THROW_FMT(AssertError, "child %u exited with error %d", processIdx, WEXITSTATUS(processStatus)); \
\ } \
if (WEXITSTATUS(processStatus) != HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS()) \
THROW_FMT(AssertError, "child exited with error %d", WEXITSTATUS(processStatus)); \
} \ } \
} while (0)

View File

@ -305,10 +305,11 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_ERROR(cmdArchiveGet(), ParamRequiredError, "WAL segment to get required"); TEST_ERROR(cmdArchiveGet(), ParamRequiredError, "WAL segment to get required");
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
@ -320,10 +321,11 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_ERROR(cmdArchiveGet(), ParamRequiredError, "path to copy WAL segment required"); TEST_ERROR(cmdArchiveGet(), ParamRequiredError, "path to copy WAL segment required");
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
@ -342,7 +344,7 @@ testRun(void)
// Test this in a fork so we can use different Perl options in later tests // Test this in a fork so we can use different Perl options in later tests
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_ERROR_FMT( TEST_ERROR_FMT(
cmdArchiveGet(), FileMissingError, cmdArchiveGet(), FileMissingError,
@ -358,6 +360,7 @@ testRun(void)
strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath)),
strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath))); strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath)));
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
@ -372,7 +375,7 @@ testRun(void)
// Test this in a fork so we can use different Perl options in later tests // Test this in a fork so we can use different Perl options in later tests
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_ERROR_FMT( TEST_ERROR_FMT(
cmdArchiveGet(), FileMissingError, cmdArchiveGet(), FileMissingError,
@ -388,6 +391,7 @@ testRun(void)
strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath)),
strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath))); strPtr(cfgOptionStr(cfgOptRepoPath)), strPtr(cfgOptionStr(cfgOptRepoPath)));
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
@ -402,10 +406,11 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_RESULT_INT(cmdArchiveGet(), 1, "timeout getting WAL segment"); TEST_RESULT_INT(cmdArchiveGet(), 1, "timeout getting WAL segment");
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
@ -418,10 +423,11 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_RESULT_INT(cmdArchiveGet(), 1, "successful get of missing WAL"); TEST_RESULT_INT(cmdArchiveGet(), 1, "successful get of missing WAL");
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
@ -439,10 +445,11 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_RESULT_INT(cmdArchiveGet(), 0, "successful get"); TEST_RESULT_INT(cmdArchiveGet(), 0, "successful get");
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
@ -470,10 +477,11 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_RESULT_INT(cmdArchiveGet(), 0, "successful get"); TEST_RESULT_INT(cmdArchiveGet(), 0, "successful get");
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
@ -489,10 +497,11 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_RESULT_INT(cmdArchiveGet(), 1, "timeout waiting for lock"); TEST_RESULT_INT(cmdArchiveGet(), 1, "timeout waiting for lock");
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();

View File

@ -28,11 +28,8 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, true)
{ {
close(pipeRead[0]);
close(pipeWrite[1]);
StringList *argList = strLstNew(); StringList *argList = strLstNew();
strLstAddZ(argList, "pgbackrest"); strLstAddZ(argList, "pgbackrest");
strLstAddZ(argList, "--stanza=test1"); strLstAddZ(argList, "--stanza=test1");
@ -42,29 +39,22 @@ testRun(void)
strLstAddZ(argList, "remote"); strLstAddZ(argList, "remote");
harnessCfgLoad(strLstSize(argList), strLstPtr(argList)); harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
cmdRemote(pipeWrite[0], pipeRead[1]); cmdRemote(HARNESS_FORK_CHILD_READ(), HARNESS_FORK_CHILD_WRITE());
close(pipeRead[1]);
close(pipeWrite[0]);
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_PARENT() HARNESS_FORK_PARENT_BEGIN()
{ {
close(pipeRead[1]); IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000));
close(pipeWrite[0]);
IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("server read"), pipeRead[0], 2000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("server write"), pipeWrite[1])); IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)));
ioWriteOpen(write); ioWriteOpen(write);
ProtocolClient *client = protocolClientNew(strNew("test"), PROTOCOL_SERVICE_REMOTE_STR, read, write); ProtocolClient *client = protocolClientNew(strNew("test"), PROTOCOL_SERVICE_REMOTE_STR, read, write);
protocolClientNoOp(client); protocolClientNoOp(client);
protocolClientFree(client); protocolClientFree(client);
close(pipeRead[0]);
close(pipeWrite[1]);
} }
HARNESS_FORK_PARENT_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
} }

View File

@ -290,12 +290,11 @@ testRun(void)
// Test in a fork so the process does not actually exit // Test in a fork so the process does not actually exit
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(UnhandledError.code, false)
{ {
THROW(TestChildError, "does not get caught!"); THROW(TestChildError, "does not get caught!");
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS_SET(UnhandledError.code);
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
} }

View File

@ -29,13 +29,12 @@ testRun(void)
{ {
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(errorTypeCode(&TermError), false)
{ {
exitInit(); exitInit();
raise(SIGTERM); raise(SIGTERM);
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_CHILD_EXPECTED_EXIT_STATUS_SET(errorTypeCode(&TermError));
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
} }

View File

@ -18,7 +18,7 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
char buffer[1024]; char buffer[1024];
@ -30,6 +30,7 @@ testRun(void)
TEST_RESULT_INT(write(STDOUT_FILENO, buffer, strlen(buffer)), -1, "write to stdout fails"); TEST_RESULT_INT(write(STDOUT_FILENO, buffer, strlen(buffer)), -1, "write to stdout fails");
TEST_RESULT_INT(write(STDERR_FILENO, buffer, strlen(buffer)), -1, "write to stderr fails"); TEST_RESULT_INT(write(STDERR_FILENO, buffer, strlen(buffer)), -1, "write to stderr fails");
} }
HARNESS_FORK_CHILD_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
} }

View File

@ -464,22 +464,18 @@ testRun(void)
{ {
ioBufferSizeSet(16); ioBufferSizeSet(16);
// Create pipe for testing
int pipeTest[2];
THROW_ON_SYS_ERROR(pipe(pipeTest) == -1, KernelError, "unable to create test pipe");
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, true)
{ {
close(pipeTest[0]);
IoHandleWrite *write = NULL; IoHandleWrite *write = NULL;
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
TEST_RESULT_VOID(ioHandleWriteMove(NULL, MEM_CONTEXT_OLD()), "move null write"); TEST_RESULT_VOID(ioHandleWriteMove(NULL, MEM_CONTEXT_OLD()), "move null write");
TEST_ASSIGN( TEST_ASSIGN(
write, ioHandleWriteMove(ioHandleWriteNew(strNew("write test"), pipeTest[1]), MEM_CONTEXT_OLD()), write,
ioHandleWriteMove(ioHandleWriteNew(strNew("write test"), HARNESS_FORK_CHILD_WRITE()), MEM_CONTEXT_OLD()),
"move write"); "move write");
} }
MEM_CONTEXT_TEMP_END(); MEM_CONTEXT_TEMP_END();
@ -504,20 +500,20 @@ testRun(void)
// Free object // Free object
TEST_RESULT_VOID(ioHandleWriteFree(NULL), "free null write"); TEST_RESULT_VOID(ioHandleWriteFree(NULL), "free null write");
TEST_RESULT_VOID(ioHandleWriteFree(write), "free write"); TEST_RESULT_VOID(ioHandleWriteFree(write), "free write");
close(pipeTest[1]);
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_PARENT() HARNESS_FORK_PARENT_BEGIN()
{ {
close(pipeTest[1]);
IoHandleRead *read = NULL; IoHandleRead *read = NULL;
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
TEST_RESULT_VOID(ioHandleReadMove(NULL, MEM_CONTEXT_OLD()), "move null read"); TEST_RESULT_VOID(ioHandleReadMove(NULL, MEM_CONTEXT_OLD()), "move null read");
TEST_ASSIGN( TEST_ASSIGN(
read, ioHandleReadMove(ioHandleReadNew(strNew("read test"), pipeTest[0], 1000), MEM_CONTEXT_OLD()), read,
ioHandleReadMove(
ioHandleReadNew(strNew("read test"), HARNESS_FORK_PARENT_READ_PROCESS(0), 1000), MEM_CONTEXT_OLD()),
"move read"); "move read");
} }
MEM_CONTEXT_TEMP_END(); MEM_CONTEXT_TEMP_END();
@ -548,9 +544,8 @@ testRun(void)
// Free object // Free object
TEST_RESULT_VOID(ioHandleReadFree(NULL), "free null read"); TEST_RESULT_VOID(ioHandleReadFree(NULL), "free null read");
TEST_RESULT_VOID(ioHandleReadFree(read), "free read"); TEST_RESULT_VOID(ioHandleReadFree(read), "free read");
close(pipeTest[0]);
} }
HARNESS_FORK_PARENT_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();

View File

@ -82,13 +82,14 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
TEST_RESULT_BOOL(lockAcquireFile(backupLock, 0, true), true, "lock on fork"); TEST_RESULT_BOOL(lockAcquireFile(backupLock, 0, true), true, "lock on fork");
sleepMSec(500); sleepMSec(500);
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_PARENT() HARNESS_FORK_PARENT_BEGIN()
{ {
sleepMSec(250); sleepMSec(250);
TEST_ERROR( TEST_ERROR(
@ -99,6 +100,7 @@ testRun(void)
"unable to acquire lock on file '%s': Resource temporarily unavailable\n" "unable to acquire lock on file '%s': Resource temporarily unavailable\n"
"HINT: is another pgBackRest process running?", strPtr(backupLock)))); "HINT: is another pgBackRest process running?", strPtr(backupLock))));
} }
HARNESS_FORK_PARENT_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
} }

View File

@ -20,22 +20,13 @@ testRun(void)
// ***************************************************************************************************************************** // *****************************************************************************************************************************
if (testBegin("configProtocol() and configProtocolOption()")) if (testBegin("configProtocol() and configProtocolOption()"))
{ {
// Create pipes for testing. Read/write is from the perspective of the client.
int pipeRead[2];
int pipeWrite[2];
THROW_ON_SYS_ERROR(pipe(pipeRead) == -1, KernelError, "unable to read test pipe");
THROW_ON_SYS_ERROR(pipe(pipeWrite) == -1, KernelError, "unable to write test pipe");
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, true)
{ {
close(pipeRead[0]); IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000));
close(pipeWrite[1]);
IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("client read"), pipeWrite[0], 2000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("client write"), pipeRead[1])); IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE()));
ioWriteOpen(write); ioWriteOpen(write);
StringList *argList = strLstNew(); StringList *argList = strLstNew();
@ -49,19 +40,14 @@ testRun(void)
ProtocolServer *server = protocolServerNew(strNew("test"), strNew("config"), read, write); ProtocolServer *server = protocolServerNew(strNew("test"), strNew("config"), read, write);
protocolServerHandlerAdd(server, configProtocol); protocolServerHandlerAdd(server, configProtocol);
protocolServerProcess(server); protocolServerProcess(server);
close(pipeRead[1]);
close(pipeWrite[0]);
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_PARENT() HARNESS_FORK_PARENT_BEGIN()
{ {
close(pipeRead[1]); IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000));
close(pipeWrite[0]);
IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("server read"), pipeRead[0], 2000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("server write"), pipeWrite[1])); IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)));
ioWriteOpen(write); ioWriteOpen(write);
ProtocolClient *client = protocolClientNew(strNew("test"), strNew("config"), read, write); ProtocolClient *client = protocolClientNew(strNew("test"), strNew("config"), read, write);
@ -75,10 +61,8 @@ testRun(void)
"get options"); "get options");
protocolClientFree(client); protocolClientFree(client);
close(pipeRead[0]);
close(pipeWrite[1]);
} }
HARNESS_FORK_PARENT_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
} }

View File

@ -137,14 +137,11 @@ testRun(void)
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, true)
{ {
close(pipeRead[0]); IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("server read"), HARNESS_FORK_CHILD_READ(), 2000));
close(pipeWrite[1]);
IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("server read"), pipeWrite[0], 2000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("server write"), pipeRead[1])); IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("server write"), HARNESS_FORK_CHILD_WRITE()));
ioWriteOpen(write); ioWriteOpen(write);
// Various bogus greetings // Various bogus greetings
@ -190,19 +187,14 @@ testRun(void)
// Wait for exit // Wait for exit
TEST_RESULT_STR(strPtr(ioReadLine(read)), "{\"cmd\":\"exit\"}", "exit command"); TEST_RESULT_STR(strPtr(ioReadLine(read)), "{\"cmd\":\"exit\"}", "exit command");
close(pipeRead[1]);
close(pipeWrite[0]);
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_PARENT() HARNESS_FORK_PARENT_BEGIN()
{ {
close(pipeRead[1]); IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("client read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000));
close(pipeWrite[0]);
IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("client read"), pipeRead[0], 2000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("client write"), pipeWrite[1])); IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("client write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)));
ioWriteOpen(write); ioWriteOpen(write);
// Various bogus greetings // Various bogus greetings
@ -259,10 +251,8 @@ testRun(void)
// Free client // Free client
TEST_RESULT_VOID(protocolClientFree(client), "free client"); TEST_RESULT_VOID(protocolClientFree(client), "free client");
TEST_RESULT_VOID(protocolClientFree(NULL), "free null client"); TEST_RESULT_VOID(protocolClientFree(NULL), "free null client");
close(pipeRead[0]);
close(pipeWrite[1]);
} }
HARNESS_FORK_PARENT_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
} }
@ -270,22 +260,13 @@ testRun(void)
// ***************************************************************************************************************************** // *****************************************************************************************************************************
if (testBegin("ProtocolServer")) if (testBegin("ProtocolServer"))
{ {
// Create pipes for testing. Read/write is from the perspective of the client.
int pipeRead[2];
int pipeWrite[2];
THROW_ON_SYS_ERROR(pipe(pipeRead) == -1, KernelError, "unable to read test pipe");
THROW_ON_SYS_ERROR(pipe(pipeWrite) == -1, KernelError, "unable to write test pipe");
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, true)
{ {
close(pipeRead[0]); IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("client read"), HARNESS_FORK_CHILD_READ(), 2000));
close(pipeWrite[1]);
IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("client read"), pipeWrite[0], 2000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("client write"), pipeRead[1])); IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("client write"), HARNESS_FORK_CHILD_WRITE()));
ioWriteOpen(write); ioWriteOpen(write);
// Check greeting // Check greeting
@ -321,19 +302,14 @@ testRun(void)
// Exit // Exit
TEST_RESULT_VOID(ioWriteLine(write, strNew("{\"cmd\":\"exit\"}")), "write exit"); TEST_RESULT_VOID(ioWriteLine(write, strNew("{\"cmd\":\"exit\"}")), "write exit");
TEST_RESULT_VOID(ioWriteFlush(write), "flush exit"); TEST_RESULT_VOID(ioWriteFlush(write), "flush exit");
close(pipeRead[1]);
close(pipeWrite[0]);
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_PARENT() HARNESS_FORK_PARENT_BEGIN()
{ {
close(pipeRead[1]); IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("server read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000));
close(pipeWrite[0]);
IoRead *read = ioHandleReadIo(ioHandleReadNew(strNew("server read"), pipeRead[0], 2000));
ioReadOpen(read); ioReadOpen(read);
IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("server write"), pipeWrite[1])); IoWrite *write = ioHandleWriteIo(ioHandleWriteNew(strNew("server write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0)));
ioWriteOpen(write); ioWriteOpen(write);
// Send greeting // Send greeting
@ -360,10 +336,8 @@ testRun(void)
TEST_RESULT_VOID(protocolServerFree(server), "free server"); TEST_RESULT_VOID(protocolServerFree(server), "free server");
TEST_RESULT_VOID(protocolServerFree(NULL), "free null server"); TEST_RESULT_VOID(protocolServerFree(NULL), "free null server");
close(pipeRead[0]);
close(pipeWrite[1]);
} }
HARNESS_FORK_PARENT_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();
} }

View File

@ -156,16 +156,18 @@ testRun(void)
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
HARNESS_FORK_BEGIN() HARNESS_FORK_BEGIN()
{ {
HARNESS_FORK_CHILD() HARNESS_FORK_CHILD_BEGIN(0, false)
{ {
sleepMSec(250); sleepMSec(250);
TEST_RESULT_INT(system(strPtr(strNewFmt("touch %s", strPtr(fileExists)))), 0, "create exists file"); TEST_RESULT_INT(system(strPtr(strNewFmt("touch %s", strPtr(fileExists)))), 0, "create exists file");
} }
HARNESS_FORK_CHILD_END();
HARNESS_FORK_PARENT() HARNESS_FORK_PARENT_BEGIN()
{ {
TEST_RESULT_BOOL(storageExistsP(storageTest, fileExists, .timeout = 1000), true, "file exists after wait"); TEST_RESULT_BOOL(storageExistsP(storageTest, fileExists, .timeout = 1000), true, "file exists after wait");
} }
HARNESS_FORK_PARENT_END();
} }
HARNESS_FORK_END(); HARNESS_FORK_END();