1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2026-05-22 10:15:16 +02:00

Add execOne() to simplify exec for build, documentation, and testing.

The core Exec object is efficient but geared toward the specific needs of core and not ease-of-use as required for build, documentation, and testing.

execOne() works similarly to system() except that it automatically redirects stderr to stdout and captures the output.
This commit is contained in:
David Steele
2024-02-24 11:22:48 +13:00
parent 6356a2b76c
commit 6c45b57fa8
10 changed files with 213 additions and 82 deletions
+4 -4
View File
@@ -406,10 +406,10 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: exec
total: 1
total: 2
coverage:
- common/exec
- build/common/exec
# ----------------------------------------------------------------------------------------------------------------------------
- name: ini
@@ -483,7 +483,7 @@ unit:
- protocol/server
include:
- common/exec
- build/common/exec
# ********************************************************************************************************************************
- name: config
@@ -700,7 +700,7 @@ unit:
test:
# ----------------------------------------------------------------------------------------------------------------------------
- name: test
total: 3
total: 2
coverage:
- test/command/test/build
+10 -41
View File
@@ -5,43 +5,13 @@ Test Command
#include <stdlib.h>
#include "build/common/exec.h"
#include "command/test/build.h"
#include "common/debug.h"
#include "common/log.h"
#include "config/config.h"
#include "storage/posix/storage.h"
/**********************************************************************************************************************************/
static const String *cmdTestExecLog;
static void
cmdTestExec(const String *const command)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, command);
FUNCTION_LOG_END();
ASSERT(cmdTestExecLog != NULL);
ASSERT(command != NULL);
MEM_CONTEXT_TEMP_BEGIN()
{
LOG_DETAIL_FMT("exec: %s", strZ(command));
if (system(zNewFmt("%s > %s 2>&1", strZ(command), strZ(cmdTestExecLog))) != 0)
{
const Buffer *const buffer = storageGetP(storageNewReadP(storagePosixNewP(FSLASH_STR), cmdTestExecLog));
THROW_FMT(
ExecuteError, "unable to execute: %s > %s 2>&1:\n%s", strZ(command), strZ(cmdTestExecLog),
zNewFmt("\n%s", strZ(strTrim(strNewBuf(buffer)))));
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
static void
cmdTestPathCreate(const Storage *const storage, const String *const path)
@@ -55,17 +25,19 @@ cmdTestPathCreate(const Storage *const storage, const String *const path)
ASSERT(storage != NULL);
const String *const rmCommand = strNewFmt("rm -rf '%s'/*", strZ(storagePathP(storage, path)));
TRY_BEGIN()
{
storagePathRemoveP(storage, path, .recurse = true);
execOneP(rmCommand);
}
CATCH_ANY()
{
// Reset permissions
cmdTestExec(strNewFmt("chmod -R 777 %s", strZ(storagePathP(storage, path))));
execOneP(strNewFmt("chmod -R 777 '%s'", strZ(storagePathP(storage, path))));
// Try to remove again
storagePathRemoveP(storage, path, .recurse = true);
execOneP(rmCommand);
}
TRY_END();
@@ -100,9 +72,6 @@ cmdTest(
MEM_CONTEXT_TEMP_BEGIN()
{
// Log file name
cmdTestExecLog = strNewFmt("%s/exec-%u.log", strZ(pathTest), vmId);
// Find test
ASSERT(moduleName != NULL);
@@ -144,9 +113,9 @@ cmdTest(
{
LOG_DETAIL("meson setup");
cmdTestExec(
execOneP(
strNewFmt(
"meson setup -Dwerror=true -Dfatal-errors=true %s %s %s", strZ(mesonSetup), strZ(pathUnitBuild),
"meson setup -Dwerror=true -Dfatal-errors=true %s '%s' '%s'", strZ(mesonSetup), strZ(pathUnitBuild),
strZ(pathUnit)));
}
// Else reconfigure
@@ -154,7 +123,7 @@ cmdTest(
{
LOG_DETAIL("meson configure");
cmdTestExec(strNewFmt("meson configure %s %s", strZ(mesonSetup), strZ(pathUnitBuild)));
execOneP(strNewFmt("meson configure %s '%s'", strZ(mesonSetup), strZ(pathUnitBuild)));
}
// Remove old coverage data. Note that coverage can be in different paths depending on the meson version.
@@ -175,7 +144,7 @@ cmdTest(
storageRemoveP(storageUnitBuild, STRDEF("gmon.out"));
// Ninja build
cmdTestExec(strNewFmt("ninja -C %s", strZ(pathUnitBuild)));
execOneP(strNewFmt("ninja -C '%s'", strZ(pathUnitBuild)));
buildRetry = false;
}
CATCH_ANY()
+2
View File
@@ -26,6 +26,7 @@ subdir('command/help')
# test target
####################################################################################################################################
src_test = [
'../../src/build/common/exec.c',
'../../src/build/common/render.c',
'../../src/build/common/string.c',
'../../src/build/common/yaml.c',
@@ -34,6 +35,7 @@ src_test = [
'../../src/command/help/help.c',
'../../src/common/compress/bz2/common.c',
'../../src/common/compress/bz2/decompress.c',
'../../src/common/fork.c',
'../../src/common/ini.c',
'../../src/common/io/fd.c',
'../../src/common/io/fdRead.c',
+38
View File
@@ -108,5 +108,43 @@ testRun(void)
TEST_RESULT_VOID(execFree(exec), "sleep exited as expected");
}
// *****************************************************************************************************************************
if (testBegin("execOne()"))
{
Exec *exec = NULL;
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("exec without output");
TEST_RESULT_STR_Z(execOneP(STRDEF("ls " TEST_PATH)), "", "exec ls");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("touch file");
TEST_RESULT_STR_Z(execOneP(STRDEF("touch " TEST_PATH "/file")), "", "exec touch");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("exec with custom shell and output");
TEST_RESULT_STR_Z(execOneP(STRDEF("ls " TEST_PATH), .shell = STRDEF("sh -c")), "file\n", "exec ls");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("exec exits from signal");
TEST_ASSIGN(exec, execNew(STRDEF("cat"), NULL, STRDEF("cat"), 1000), "new cat exec");
TEST_RESULT_VOID(execOpen(exec), "open cat exec");
kill(exec->processId, SIGKILL);
TEST_ERROR(execProcess(exec), ExecuteError, "cat terminated unexpectedly on signal 9");
TEST_RESULT_VOID(execFree(exec), "free exec");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("exec exits with error");
TEST_ERROR(
execOneP(STRDEF("cat missing.txt")), UnknownError,
"cat missing.txt terminated unexpectedly [1]: cat: missing.txt: No such file or directory");
}
FUNCTION_HARNESS_RETURN_VOID();
}
+1 -1
View File
@@ -299,7 +299,7 @@ testRun(void)
OBJ_NEW_BASE_BEGIN(Exec, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{
protocolHelperClient.exec = OBJ_NEW_ALLOC();
*protocolHelperClient.exec = (Exec){.name = strNewZ("test"), .command = strNewZ("test"), .processId = INT_MAX};
*protocolHelperClient.exec = (Exec){.pub = {.command = strNewZ("test")}, .name = strNewZ("test"), .processId = INT_MAX};
memContextCallbackSet(memContextCurrent(), execFreeResource, protocolHelperClient.exec);
}
OBJ_NEW_END();
-22
View File
@@ -66,28 +66,6 @@ testRun(void)
TEST_RESULT_STR_Z(cmdBldPathRelative(STRDEF("/tmp"), STRDEF("/tmp/sub")), "sub", "base is sub of compare");
}
// *****************************************************************************************************************************
if (testBegin("cmdTestExec()"))
{
cmdTestExecLog = STRDEF(TEST_PATH "/error.log");
bool error = false;
// The error will vary by OS so just make sure an error was thrown
TRY_BEGIN()
{
cmdTestExec(STRDEF("/bogus/bogus"));
}
CATCH(ExecuteError)
{
error = true;
}
TRY_END();
TEST_RESULT_BOOL(error, true, "an error should be thrown");
cmdTestExecLog = NULL;
}
// *****************************************************************************************************************************
if (testBegin("TestDef and TestBuild"))
{