You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2026-06-20 01:17:49 +02:00
Refactor SFTP unit tests.
Replace the verbose, hand-built HrnLibSsh2 script entries in the SFTP unit tests with a set of per-function HRN_LIBSSH2_* response macros (one per libssh2 shim function). Each macro names its function, bakes in the fixed parameters the production code always passes, and defaults the common result, so a test supplies only the values that vary as trailing designated initializers. The harness now defaults omitted values rather than requiring every field to be spelled out: libssh2_session_hostkey() defaults length/type/value; libssh2_sftp_stat_ex() defaults an omitted .attr to a regular file with mode 0640, defaults .flags to the standard attribute set (adding a size for regular files), and defaults .uid/.gid to the test user/group. Add HRN_LIBSSH2_ATTR_EXISTENCE (path exists but reports no attributes) and HRN_LIBSSH2_OWNER_ROOT (report ownership by root) sentinels, plus HRN_LIBSSH2_DIR/FILE/LINK/FIFO() helpers that OR a file type with an octal mode. Replace the NULL-terminated script array and hrnLibSsh2ScriptSet(array) with HRN_LIBSSH2_SCRIPT_SET(...), which computes the script length so no terminator entry is needed; hrnLibSsh2ScriptSet() now takes an explicit size. Rebuild HRNLIBSSH2_MACRO_STARTUP/SHUTDOWN() and HOSTKEY_HASH_ENTRY() on top of the new macros, and stat_ex now verifies the requested stat_type (via .follow) instead of scripting it as a parameter. Reorganize the tests themselves: split bundled comment groups into TEST_TITLE sections, split scripts per section, and drop redundant connect/disconnect setup. Remove tests that duplicate Posix tests for common code. Remove duplicative SFTP tests. Rename HrnLibSsh2 fields for clarity (attrPerms -> attr/mode, symlinkExTarget -> target). These changes cut sftpTest.c roughly in half.
This commit is contained in:
+1
-1
@@ -632,7 +632,7 @@ unit:
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: sftp
|
||||
total: 20
|
||||
total: 14
|
||||
harness:
|
||||
name: libSsh2
|
||||
integration: false
|
||||
|
||||
@@ -11,6 +11,7 @@ libssh2 Test Harness
|
||||
#include "common/type/json.h"
|
||||
#include "common/type/string.h"
|
||||
#include "common/type/variantList.h"
|
||||
#include "common/user.h"
|
||||
|
||||
#include "common/harnessLibSsh2.h"
|
||||
#include "common/harnessTest.h"
|
||||
@@ -20,12 +21,19 @@ libssh2 shim error prefix
|
||||
***********************************************************************************************************************************/
|
||||
#define LIBSSH2_ERROR_PREFIX "LIBSSH2 SHIM ERROR"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Standard stat attribute flags reported when a scripted response omits .flags
|
||||
***********************************************************************************************************************************/
|
||||
#define HRN_LIBSSH2_ATTR_DEFAULT \
|
||||
(LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME | LIBSSH2_SFTP_ATTR_UIDGID)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Script that defines how shim functions operate
|
||||
***********************************************************************************************************************************/
|
||||
static HrnLibSsh2 hrnLibSsh2Script[1024];
|
||||
static bool hrnLibSsh2ScriptDone = true;
|
||||
static unsigned int hrnLibSsh2ScriptIdx;
|
||||
static unsigned int hrnLibSsh2ScriptSize;
|
||||
|
||||
// If there is a script failure change the behavior of cleanup functions to return immediately so the real error will be reported
|
||||
// rather than a bogus scripting error during cleanup
|
||||
@@ -36,24 +44,19 @@ static char hrnLibSsh2ScriptError[4096];
|
||||
Set libssh2 script
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
hrnLibSsh2ScriptSet(HrnLibSsh2 *hrnLibSsh2ScriptParam)
|
||||
hrnLibSsh2ScriptSet(const HrnLibSsh2 *const script, const unsigned int scriptSize)
|
||||
{
|
||||
if (!hrnLibSsh2ScriptDone)
|
||||
THROW(AssertError, "previous libssh2 script has not yet completed");
|
||||
|
||||
if (hrnLibSsh2ScriptParam[0].function == NULL)
|
||||
if (scriptSize == 0)
|
||||
THROW(AssertError, "libssh2 script must have entries");
|
||||
|
||||
// Copy records into local storage
|
||||
unsigned int copyIdx = 0;
|
||||
for (unsigned int copyIdx = 0; copyIdx < scriptSize; copyIdx++)
|
||||
hrnLibSsh2Script[copyIdx] = script[copyIdx];
|
||||
|
||||
while (hrnLibSsh2ScriptParam[copyIdx].function != NULL)
|
||||
{
|
||||
hrnLibSsh2Script[copyIdx] = hrnLibSsh2ScriptParam[copyIdx];
|
||||
copyIdx++;
|
||||
}
|
||||
|
||||
hrnLibSsh2Script[copyIdx].function = NULL;
|
||||
hrnLibSsh2ScriptSize = scriptSize;
|
||||
hrnLibSsh2ScriptDone = false;
|
||||
hrnLibSsh2ScriptIdx = 0;
|
||||
}
|
||||
@@ -146,7 +149,7 @@ hrnLibSsh2ScriptRun(const char *const function, const VariantList *const param,
|
||||
|
||||
hrnLibSsh2ScriptIdx++;
|
||||
|
||||
if (hrnLibSsh2Script[hrnLibSsh2ScriptIdx].function == NULL)
|
||||
if (hrnLibSsh2ScriptIdx >= hrnLibSsh2ScriptSize)
|
||||
hrnLibSsh2ScriptDone = true;
|
||||
|
||||
strFree(paramStr);
|
||||
@@ -333,10 +336,11 @@ libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len, int *type)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_SESSION_HOSTKEY, NULL, (HrnLibSsh2 *)session);
|
||||
|
||||
*len = (size_t)hrnLibSsh2->len;
|
||||
*type = (int)hrnLibSsh2->type;
|
||||
// Default the host key length (20), type (RSA), and value (HOSTKEY) when omitted
|
||||
*len = hrnLibSsh2->len == 0 ? 20 : (size_t)hrnLibSsh2->len;
|
||||
*type = hrnLibSsh2->type == 0 ? LIBSSH2_HOSTKEY_TYPE_RSA : (int)hrnLibSsh2->type;
|
||||
|
||||
return hrnLibSsh2->resultNull ? NULL : (const char *)hrnLibSsh2->resultZ;
|
||||
return hrnLibSsh2->resultNull ? NULL : (const char *)(hrnLibSsh2->resultZ != NULL ? hrnLibSsh2->resultZ : HOSTKEY);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -557,27 +561,45 @@ libssh2_sftp_stat_ex(
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_STAT_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(path)),
|
||||
varNewInt(stat_type)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
HRNLIBSSH2_SFTP_STAT_EX, varLstAdd(varLstNew(), varNewStrZ(path)), (HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
// When the scripted response follows symlinks, verify the production code requested LIBSSH2_SFTP_STAT (otherwise stat_type is
|
||||
// not checked, since it only affects the result for symlinks)
|
||||
if (hrnLibSsh2->follow && stat_type != LIBSSH2_SFTP_STAT)
|
||||
THROW_FMT(AssertError, "libssh2_sftp_stat_ex expected stat_type LIBSSH2_SFTP_STAT but got %d", stat_type);
|
||||
|
||||
if (attrs == NULL)
|
||||
THROW(AssertError, "attrs is NULL");
|
||||
|
||||
attrs->flags = 0;
|
||||
attrs->flags |= (unsigned long)hrnLibSsh2->flags;
|
||||
// An omitted (0) attr defaults to a regular file with mode 0640
|
||||
attrs->permissions =
|
||||
hrnLibSsh2->attr == 0 ? (unsigned long)(LIBSSH2_SFTP_S_IFREG | 0640) : (unsigned long)hrnLibSsh2->attr;
|
||||
|
||||
attrs->permissions = 0;
|
||||
attrs->permissions |= (unsigned long)hrnLibSsh2->attrPerms;
|
||||
// Default the response flags: omitted (0) reports the standard attribute set, plus LIBSSH2_SFTP_ATTR_SIZE for a regular file
|
||||
// (which reports a size); HRN_LIBSSH2_ATTR_EXISTENCE reports none (the path exists but provides no attributes)
|
||||
uint64_t flags = hrnLibSsh2->flags;
|
||||
|
||||
if (flags == 0)
|
||||
{
|
||||
flags = HRN_LIBSSH2_ATTR_DEFAULT;
|
||||
|
||||
if (LIBSSH2_SFTP_S_ISREG(attrs->permissions))
|
||||
flags |= LIBSSH2_SFTP_ATTR_SIZE;
|
||||
}
|
||||
else if (flags == HRN_LIBSSH2_ATTR_EXISTENCE)
|
||||
flags = 0;
|
||||
|
||||
attrs->flags = (unsigned long)flags;
|
||||
|
||||
attrs->mtime = (unsigned long)hrnLibSsh2->mtime;
|
||||
attrs->uid = (unsigned long)hrnLibSsh2->uid;
|
||||
attrs->gid = (unsigned long)hrnLibSsh2->gid;
|
||||
|
||||
// An omitted (0) uid/gid defaults to the test user/group; HRN_LIBSSH2_OWNER_ROOT selects root (0)
|
||||
attrs->uid =
|
||||
hrnLibSsh2->uid == HRN_LIBSSH2_OWNER_ROOT ? 0 : hrnLibSsh2->uid == 0 ? (unsigned long)userId() : (unsigned long)hrnLibSsh2->uid;
|
||||
attrs->gid =
|
||||
hrnLibSsh2->gid == HRN_LIBSSH2_OWNER_ROOT ? 0 : hrnLibSsh2->gid == 0 ? (unsigned long)groupId() : (unsigned long)hrnLibSsh2->gid;
|
||||
attrs->filesize = hrnLibSsh2->filesize;
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
@@ -626,15 +648,15 @@ libssh2_sftp_symlink_ex(
|
||||
{
|
||||
case LIBSSH2_SFTP_READLINK:
|
||||
case LIBSSH2_SFTP_REALPATH:
|
||||
if (hrnLibSsh2->symlinkExTarget != NULL)
|
||||
if (hrnLibSsh2->target != NULL)
|
||||
{
|
||||
if (strSize(hrnLibSsh2->symlinkExTarget) < PATH_MAX)
|
||||
strncpy(target, strZ(hrnLibSsh2->symlinkExTarget), strSize(hrnLibSsh2->symlinkExTarget));
|
||||
if (strSize(hrnLibSsh2->target) < PATH_MAX)
|
||||
strncpy(target, strZ(hrnLibSsh2->target), strSize(hrnLibSsh2->target));
|
||||
else
|
||||
THROW_FMT(AssertError, "symlinkExTarget too large for target buffer");
|
||||
THROW_FMT(AssertError, "link target too large for buffer");
|
||||
}
|
||||
|
||||
rc = hrnLibSsh2->resultInt != 0 ? hrnLibSsh2->resultInt : (int)strSize(hrnLibSsh2->symlinkExTarget);
|
||||
rc = hrnLibSsh2->resultInt != 0 ? hrnLibSsh2->resultInt : (int)strSize(hrnLibSsh2->target);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -665,15 +687,21 @@ libssh2_sftp_open_ex(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(filename)),
|
||||
varNewUInt64(flags)),
|
||||
varNewInt64(mode)),
|
||||
varLstNew(), varNewStrZ(filename)),
|
||||
varNewUInt64(flags)),
|
||||
varNewInt(open_type)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
// Verify the open mode. It is not part of the scripted param so it can be defaulted: a write open with an omitted (0) mode
|
||||
// expects 0640, while other opens expect mode 0.
|
||||
long modeExpected =
|
||||
hrnLibSsh2->mode != 0 ? (long)hrnLibSsh2->mode : ((flags & LIBSSH2_FXF_WRITE) != 0 ? 0640 : 0);
|
||||
|
||||
if (mode != modeExpected)
|
||||
THROW_FMT(AssertError, "libssh2_sftp_open_ex expected mode %ld but got %ld", modeExpected, mode);
|
||||
|
||||
return hrnLibSsh2->resultNull ? NULL : (LIBSSH2_SFTP_HANDLE *)hrnLibSsh2;
|
||||
}
|
||||
|
||||
@@ -697,9 +725,7 @@ libssh2_sftp_readdir_ex(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(buffer)),
|
||||
varNewUInt64(buffer_maxlen)),
|
||||
varLstNew(), varNewUInt64(buffer_maxlen)),
|
||||
varNewStrZ(longentry)),
|
||||
varNewUInt64(longentry_maxlen)),
|
||||
(HrnLibSsh2 *)handle);
|
||||
@@ -709,7 +735,10 @@ libssh2_sftp_readdir_ex(
|
||||
if (hrnLibSsh2->fileName != NULL)
|
||||
strncpy(buffer, strZ(hrnLibSsh2->fileName), buffer_maxlen);
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
// libssh2_sftp_readdir_ex() returns the number of bytes in the entry name (0 at end of directory). Derive it from the scripted
|
||||
// file name so tests need not specify it; a negative resultInt (e.g. EAGAIN) is returned as-is.
|
||||
return hrnLibSsh2->resultInt < 0 ?
|
||||
hrnLibSsh2->resultInt : hrnLibSsh2->fileName != NULL ? (int)strSize(hrnLibSsh2->fileName) : 0;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -745,15 +774,16 @@ libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_le
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_MKDIR_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(path)),
|
||||
varNewInt64(mode)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
HRNLIBSSH2_SFTP_MKDIR_EX, varLstAdd(varLstNew(), varNewStrZ(path)), (HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
// Verify the mode. It is not part of the scripted param so it can be defaulted: an omitted (0) mode expects 0750.
|
||||
long modeExpected = hrnLibSsh2->mode != 0 ? (long)hrnLibSsh2->mode : 0750;
|
||||
|
||||
if (mode != modeExpected)
|
||||
THROW_FMT(AssertError, "libssh2_sftp_mkdir_ex expected mode %ld but got %ld", modeExpected, mode);
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
@@ -781,8 +811,9 @@ libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxle
|
||||
if (hrnLibSsh2->readBuffer != NULL)
|
||||
strncpy(buffer, strZ(hrnLibSsh2->readBuffer), strSize(hrnLibSsh2->readBuffer));
|
||||
|
||||
// number of bytes populated
|
||||
return hrnLibSsh2->resultInt;
|
||||
// Return the number of bytes read, defaulting an omitted (0) result to the read buffer size (0 when there is no read buffer)
|
||||
return hrnLibSsh2->resultInt != 0 ?
|
||||
hrnLibSsh2->resultInt : (hrnLibSsh2->readBuffer != NULL ? (ssize_t)strSize(hrnLibSsh2->readBuffer) : 0);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -908,8 +939,8 @@ libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
// Return number of bytes written
|
||||
return hrnLibSsh2->resultInt;
|
||||
// Return the number of bytes written, defaulting an omitted (0) result to the full count
|
||||
return hrnLibSsh2->resultInt != 0 ? hrnLibSsh2->resultInt : (ssize_t)count;
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
||||
|
||||
@@ -12,7 +12,9 @@ Scripted testing for libssh2 so exact results can be returned for unit testing.
|
||||
|
||||
#include <libssh2.h>
|
||||
#include <libssh2_sftp.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/macro.h"
|
||||
#include "common/time.h"
|
||||
@@ -31,6 +33,10 @@ libssh2 authorization constants
|
||||
#define ETC_KNOWNHOSTS2_FILE_CSTR "/etc/ssh/ssh_known_hosts2"
|
||||
#define HOSTKEY "12345678901234567890"
|
||||
|
||||
// Sentinel .uid/.gid for scripted stat responses selecting root (0). An omitted (0) .uid/.gid instead defaults to the test
|
||||
// user/group, so use HRN_LIBSSH2_OWNER_ROOT when a response must report ownership by root.
|
||||
#define HRN_LIBSSH2_OWNER_ROOT UINT_MAX
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Function constants
|
||||
***********************************************************************************************************************************/
|
||||
@@ -68,40 +74,6 @@ Function constants
|
||||
#define HRNLIBSSH2_SFTP_WRITE "libssh2_sftp_write"
|
||||
#define HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX "libssh2_userauth_publickey_fromfile_ex"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for defining groups of functions that implement commands
|
||||
***********************************************************************************************************************************/
|
||||
// Set of functions mimicking libssh2 initialization and authorization
|
||||
#define HRNLIBSSH2_MACRO_STARTUP() \
|
||||
{.function = HRNLIBSSH2_INIT, .param = "[0]", .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SESSION_INIT_EX, .param = "[null,null,null,null]"}, \
|
||||
{.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_INIT}, \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_READFILE, .param = "[\"" KNOWNHOSTS_FILE_CSTR "\",1]", .resultInt = 5}, \
|
||||
{.function = HRNLIBSSH2_SESSION_HOSTKEY, .len = 20, .type = LIBSSH2_HOSTKEY_TYPE_RSA, .resultZ = HOSTKEY}, \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_CHECKP, .param = "[\"localhost\",22,\"" HOSTKEY "\",20,65537]", \
|
||||
.resultInt = LIBSSH2_KNOWNHOST_CHECK_MATCH}, \
|
||||
{.function = HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX, \
|
||||
.param = "[\"" TEST_USER "\"," TEST_USER_LEN ",\"" KEYPUB_CSTR "\",\"" KEYPRIV_CSTR "\",null]", \
|
||||
.resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SFTP_INIT}
|
||||
|
||||
// Set of functions mimicking libssh2 shutdown and disconnect
|
||||
#define HRNLIBSSH2_MACRO_SHUTDOWN() \
|
||||
{.function = HRNLIBSSH2_SFTP_SHUTDOWN, .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SESSION_DISCONNECT_EX, .param ="[11,\"pgBackRest instance shutdown\",\"\"]", .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SESSION_FREE, .resultInt = 0}, \
|
||||
{.function = NULL} \
|
||||
|
||||
// Older systems do not support LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
#define HOSTKEY_HASH_ENTRY() \
|
||||
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[3]", .resultZ = "12345678910123456789"}
|
||||
#else
|
||||
#define HOSTKEY_HASH_ENTRY() \
|
||||
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[2]", .resultZ = "12345678910123456789"}
|
||||
#endif
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Structure for scripting libssh2 responses
|
||||
***********************************************************************************************************************************/
|
||||
@@ -115,12 +87,14 @@ typedef struct HrnLibSsh2
|
||||
const char *resultZ; // Zero-terminated result value
|
||||
bool resultNull; // Return null from function that normally returns a struct ptr
|
||||
uint64_t flags; // libssh2 flags
|
||||
uint64_t attrPerms; // libssh2 attr perms
|
||||
uint64_t attr; // libssh2 attributes (file type and mode bits)
|
||||
uint64_t mode; // libssh2_sftp_open_ex file mode (write open defaults to 0640)
|
||||
bool follow; // stat_ex follows symlinks (LIBSSH2_SFTP_STAT vs LSTAT)
|
||||
uint64_t atime, mtime; // libssh2 timestamps
|
||||
uint64_t uid, gid; // libssh2 uid/gid
|
||||
uint64_t filesize; // libssh2 filesize
|
||||
uint64_t offset; // libssh2 seek offset
|
||||
const String *symlinkExTarget; // libssh2_sftp_symlink_ex target
|
||||
const String *target; // libssh2_sftp_symlink_ex target
|
||||
const String *fileName; // libssh2_readdir* libssh2_stat* filename
|
||||
const String *readBuffer; // what to copy into read buffer
|
||||
TimeMSec sleep; // Sleep specified milliseconds before returning from function
|
||||
@@ -129,10 +103,221 @@ typedef struct HrnLibSsh2
|
||||
const char *errMsg; // libssh2_session_last_error error msg
|
||||
} HrnLibSsh2;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Older systems do not support LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
***********************************************************************************************************************************/
|
||||
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
#define HOSTKEY_HASH_ENTRY() HRN_LIBSSH2_HOSTKEY_HASH(3, .resultZ = "12345678910123456789")
|
||||
#else
|
||||
#define HOSTKEY_HASH_ENTRY() HRN_LIBSSH2_HOSTKEY_HASH(2, .resultZ = "12345678910123456789")
|
||||
#endif
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Scripted response macros, one per libssh2 shim function
|
||||
|
||||
Each expands to an HrnLibSsh2 script entry that names the function, bakes its fixed parameters, and defaults the common result, so a
|
||||
test supplies only the values that vary as trailing designated initializers (e.g. .resultInt, .resultNull). The associated helper
|
||||
macros build the values those entries take (.attr, .flags, ...).
|
||||
***********************************************************************************************************************************/
|
||||
// Sentinel .flags reporting a path that exists but carries no attributes (the harness maps it to libssh2 flags 0). Uses a value
|
||||
// that cannot collide with a real LIBSSH2_SFTP_ATTR_* combination.
|
||||
#define HRN_LIBSSH2_ATTR_EXISTENCE UINT64_MAX
|
||||
|
||||
// .attr shorthand: file type OR'd with an octal mode (the LIBSSH2_SFTP_S_I* permission bits are the octal mode bits)
|
||||
#define HRN_LIBSSH2_DIR(mode) (LIBSSH2_SFTP_S_IFDIR | (mode))
|
||||
#define HRN_LIBSSH2_FILE(mode) (LIBSSH2_SFTP_S_IFREG | (mode))
|
||||
#define HRN_LIBSSH2_LINK(mode) (LIBSSH2_SFTP_S_IFLNK | (mode))
|
||||
#define HRN_LIBSSH2_FIFO(mode) (LIBSSH2_SFTP_S_IFIFO | (mode))
|
||||
|
||||
// libssh2_sftp_readdir_ex() response returning a directory entry (an empty name signals end of directory)
|
||||
#define HRN_LIBSSH2_READDIR(name, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_READDIR_EX, .param = "[4095,null,0]", .fileName = STRDEF(name), __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_stat_ex() response. The path is the full server path (e.g. TEST_PATH "/sub"). Attributes are trailing arguments
|
||||
// (.attr, .flags, .mtime, .uid, .gid, .filesize, ...); the harness defaults omitted ones: .attr (0) to a regular file (set it with
|
||||
// the HRN_LIBSSH2_DIR/FILE/LINK/FIFO() helpers), .uid/.gid (0) to the test user/group (HRN_LIBSSH2_OWNER_ROOT for root), and
|
||||
// .flags (0) to the standard attribute set (permissions, times, owner) plus a size for a regular file. Reports success
|
||||
// (LIBSSH2_ERROR_NONE) by default; set .resultInt for a failure. Set .follow = true to require LIBSSH2_SFTP_STAT.
|
||||
#define HRN_LIBSSH2_STAT(path, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_STAT_EX, .param = "[\"" path "\"]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_close_handle() response. Reports success (LIBSSH2_ERROR_NONE) by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_CLOSE(...) {.function = HRNLIBSSH2_SFTP_CLOSE_HANDLE, __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_fsync() response. Reports success (LIBSSH2_ERROR_NONE) by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_FSYNC(...) {.function = HRNLIBSSH2_SFTP_FSYNC, __VA_ARGS__}
|
||||
|
||||
// libssh2_session_block_directions() response. Reports no blocked direction (SSH2_NO_BLOCK_READING_WRITING) by default; set
|
||||
// .resultInt to an SSH2_BLOCK_* direction to make the production code wait.
|
||||
#define HRN_LIBSSH2_BLOCK(...) {.function = HRNLIBSSH2_SESSION_BLOCK_DIRECTIONS, __VA_ARGS__}
|
||||
|
||||
// libssh2_session_last_errno() response returning the given libssh2 error
|
||||
#define HRN_LIBSSH2_ERRNO(error, ...) \
|
||||
{.function = HRNLIBSSH2_SESSION_LAST_ERRNO, .resultInt = (error), __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_last_error() response returning the given SFTP status (LIBSSH2_FX_*)
|
||||
#define HRN_LIBSSH2_SFTP_ERROR(code, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_LAST_ERROR, .resultUInt = (code), __VA_ARGS__}
|
||||
|
||||
// libssh2_session_last_error() response returning the given libssh2 error and, via .errMsg, the message string
|
||||
#define HRN_LIBSSH2_SESSION_ERROR(error, ...) \
|
||||
{.function = HRNLIBSSH2_SESSION_LAST_ERROR, .resultInt = (error), __VA_ARGS__}
|
||||
|
||||
// libssh2_init() response (always called with flags 0). Reports success (LIBSSH2_ERROR_NONE) by default; set .resultInt for a
|
||||
// failure.
|
||||
#define HRN_LIBSSH2_INIT(...) \
|
||||
{.function = HRNLIBSSH2_INIT, .param = "[0]", __VA_ARGS__}
|
||||
|
||||
// libssh2_session_init_ex() response (the production code always passes NULL allocators). Returns a session by default; set
|
||||
// .resultNull = true for a failure.
|
||||
#define HRN_LIBSSH2_SESSION_INIT(...) \
|
||||
{.function = HRNLIBSSH2_SESSION_INIT_EX, .param = "[null,null,null,null]", __VA_ARGS__}
|
||||
|
||||
// libssh2_session_handshake() response. Reports success (LIBSSH2_ERROR_NONE) by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_HANDSHAKE(...) \
|
||||
{.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, __VA_ARGS__}
|
||||
|
||||
// libssh2_session_hostkey() response. The harness defaults the host key length (20), type (LIBSSH2_HOSTKEY_TYPE_RSA), and
|
||||
// value (HOSTKEY); set .type for another key type or .resultNull = true for a failure.
|
||||
#define HRN_LIBSSH2_HOSTKEY(...) \
|
||||
{.function = HRNLIBSSH2_SESSION_HOSTKEY, __VA_ARGS__}
|
||||
|
||||
// libssh2_hostkey_hash() response for the given hash type. Set .resultZ to the hash, or .resultNull = true for a failure.
|
||||
#define HRN_LIBSSH2_HOSTKEY_HASH(hashType, ...) \
|
||||
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[" #hashType "]", __VA_ARGS__}
|
||||
|
||||
// libssh2_knownhost_init() response. Returns a known-hosts collection by default; set .resultNull = true for a failure.
|
||||
#define HRN_LIBSSH2_KNOWNHOST_INIT(...) \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_INIT, __VA_ARGS__}
|
||||
|
||||
// libssh2_knownhost_readfile() response for the given file (LIBSSH2_KNOWNHOST_FILE_OPENSSH). Reports 0 hosts read by default; set
|
||||
// .resultInt to the host count or a failure.
|
||||
#define HRN_LIBSSH2_KNOWNHOST_READFILE(file, ...) \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_READFILE, .param = "[\"" file "\",1]", __VA_ARGS__}
|
||||
|
||||
// libssh2_knownhost_writefile() response for the given file (LIBSSH2_KNOWNHOST_FILE_OPENSSH). Reports success (LIBSSH2_ERROR_NONE)
|
||||
// by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_KNOWNHOST_WRITEFILE(file, ...) \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_WRITEFILE, .param = "[\"" file "\",1]", __VA_ARGS__}
|
||||
|
||||
// libssh2_knownhost_checkp() response for localhost returning the given LIBSSH2_KNOWNHOST_CHECK_* result
|
||||
#define HRN_LIBSSH2_KNOWNHOST_CHECK(result) \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_CHECKP, .param = "[\"localhost\",22,\"" HOSTKEY "\",20,65537]", .resultInt = (result)}
|
||||
|
||||
// libssh2_knownhost_addc() response adding localhost with the given type mask. Reports success by default; set .resultInt for a
|
||||
// failure.
|
||||
#define HRN_LIBSSH2_KNOWNHOST_ADD(typeMask, ...) \
|
||||
{.function = HRNLIBSSH2_KNOWNHOST_ADDC, \
|
||||
.param = "[\"localhost\",null,\"" HOSTKEY "\",20,\"Generated from pgBackRest\",25," #typeMask "]", __VA_ARGS__}
|
||||
|
||||
// libssh2_userauth_publickey_fromfile_ex() response for the standard public-key login (TEST_USER with KEYPUB_CSTR / KEYPRIV_CSTR
|
||||
// and no passphrase). Reports success (LIBSSH2_ERROR_NONE) by default; set .resultInt for a failure. A login that omits the public
|
||||
// key or supplies a passphrase is scripted longhand.
|
||||
#define HRN_LIBSSH2_USERAUTH(...) \
|
||||
{.function = HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX, \
|
||||
.param = "[\"" TEST_USER "\"," TEST_USER_LEN ",\"" KEYPUB_CSTR "\",\"" KEYPRIV_CSTR "\",null]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_init() response. Returns an SFTP session by default; set .resultNull = true for a failure.
|
||||
#define HRN_LIBSSH2_SFTP_INIT(...) \
|
||||
{.function = HRNLIBSSH2_SFTP_INIT, __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_shutdown() response. Reports success (LIBSSH2_ERROR_NONE) by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_SHUTDOWN(...) {.function = HRNLIBSSH2_SFTP_SHUTDOWN, __VA_ARGS__}
|
||||
|
||||
// libssh2_session_disconnect_ex() response (the production code always passes the standard shutdown reason). Reports success
|
||||
// (LIBSSH2_ERROR_NONE) by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_DISCONNECT(...) \
|
||||
{.function = HRNLIBSSH2_SESSION_DISCONNECT_EX, .param = "[11,\"pgBackRest instance shutdown\",\"\"]", __VA_ARGS__}
|
||||
|
||||
// libssh2_session_free() response. Reports success (LIBSSH2_ERROR_NONE) by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_SESSION_FREE(...) {.function = HRNLIBSSH2_SESSION_FREE, __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_seek64() expectation verifying the production code seeks to the given byte offset (the function returns void)
|
||||
#define HRN_LIBSSH2_SEEK(offset, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_SEEK64, .param = "[" #offset "]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_read() response (size is the production buffer_maxlen). Set .readBuffer to the bytes returned; the result
|
||||
// (bytes read) defaults to the read buffer size, so set .resultInt only for a short read or a failure.
|
||||
#define HRN_LIBSSH2_READ(size, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_READ, .param = "[" #size "]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_write() response (size is the production byte size). The result (bytes written) defaults to the full size, so set
|
||||
// .resultInt only for a short write or a failure.
|
||||
#define HRN_LIBSSH2_WRITE(size, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_WRITE, .param = "[" #size "]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_open_ex() responses, one per production open path (param is ["path",flags,open_type]): a directory (flags 0,
|
||||
// LIBSSH2_SFTP_OPENDIR), a file for reading (LIBSSH2_FXF_READ, LIBSSH2_SFTP_OPENFILE), or a file for writing
|
||||
// (LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC = 26, LIBSSH2_SFTP_OPENFILE). The path is the full server path (e.g.
|
||||
// TEST_PATH "/sub"). The harness verifies the open mode separately; a write open defaults to 0640, so set .mode only when it
|
||||
// differs. Pass .resultNull = true (optionally with .resultInt) for a failed open.
|
||||
#define HRN_LIBSSH2_OPENDIR(path, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" path "\",0,1]", __VA_ARGS__}
|
||||
#define HRN_LIBSSH2_OPEN_READ(path, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" path "\",1,0]", __VA_ARGS__}
|
||||
#define HRN_LIBSSH2_OPEN_WRITE(path, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_OPEN_EX, .param = "[\"" path "\",26,0]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_symlink_ex() response reading a link destination (param is ["path","",LIBSSH2_SFTP_READLINK]; the empty target is
|
||||
// the production output buffer). The path is the full server path (e.g. TEST_PATH "/sub"). Reports success by default; set .target
|
||||
// to the destination, or .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_READLINK(path, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_SYMLINK_EX, .param = "[\"" path "\",\"\",1]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_unlink_ex() response. The path is the full server path (e.g. TEST_PATH "/sub"). Reports success (LIBSSH2_ERROR_NONE)
|
||||
// by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_UNLINK(path, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_UNLINK_EX, .param = "[\"" path "\"]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_mkdir_ex() response. The path is the full server path (e.g. TEST_PATH "/sub"). The harness verifies the mode
|
||||
// separately and defaults an omitted .mode to 0750. Reports success by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_MKDIR(path, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_MKDIR_EX, .param = "[\"" path "\"]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_rmdir_ex() response. The path is the full server path (e.g. TEST_PATH "/sub"). Reports success by default; set
|
||||
// .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_RMDIR(path, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_RMDIR_EX, .param = "[\"" path "\"]", __VA_ARGS__}
|
||||
|
||||
// libssh2_sftp_rename_ex() response (flags 7 = LIBSSH2_SFTP_RENAME_OVERWRITE | _ATOMIC | _NATIVE, as the production code always
|
||||
// passes). Both paths are full server paths (e.g. TEST_PATH "/sub"). Reports success by default; set .resultInt for a failure.
|
||||
#define HRN_LIBSSH2_RENAME(source, dest, ...) \
|
||||
{.function = HRNLIBSSH2_SFTP_RENAME_EX, .param = "[\"" source "\",\"" dest "\",7]", __VA_ARGS__}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for defining groups of functions that implement commands
|
||||
***********************************************************************************************************************************/
|
||||
// Set of functions mimicking libssh2 initialization and authorization
|
||||
#define HRNLIBSSH2_MACRO_STARTUP() \
|
||||
HRN_LIBSSH2_INIT(), \
|
||||
HRN_LIBSSH2_SESSION_INIT(), \
|
||||
HRN_LIBSSH2_HANDSHAKE(), \
|
||||
HRN_LIBSSH2_KNOWNHOST_INIT(), \
|
||||
HRN_LIBSSH2_KNOWNHOST_READFILE(KNOWNHOSTS_FILE_CSTR, .resultInt = 5), \
|
||||
HRN_LIBSSH2_HOSTKEY(), \
|
||||
HRN_LIBSSH2_KNOWNHOST_CHECK(LIBSSH2_KNOWNHOST_CHECK_MATCH), \
|
||||
HRN_LIBSSH2_USERAUTH(), \
|
||||
HRN_LIBSSH2_SFTP_INIT()
|
||||
|
||||
// Set of functions mimicking libssh2 shutdown and disconnect
|
||||
#define HRNLIBSSH2_MACRO_SHUTDOWN() \
|
||||
HRN_LIBSSH2_SHUTDOWN(), \
|
||||
HRN_LIBSSH2_DISCONNECT(), \
|
||||
HRN_LIBSSH2_SESSION_FREE()
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void hrnLibSsh2ScriptSet(HrnLibSsh2 *hrnLibSsh2ScriptParam);
|
||||
// Set a libssh2 script. The prior script must have completed. The script length is computed, so no terminator entry is needed.
|
||||
#define HRN_LIBSSH2_SCRIPT_SET(...) \
|
||||
do \
|
||||
{ \
|
||||
const HrnLibSsh2 script[] = {__VA_ARGS__}; \
|
||||
hrnLibSsh2ScriptSet(script, LENGTH_OF(script)); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
void hrnLibSsh2ScriptSet(const HrnLibSsh2 *script, unsigned int scriptSize);
|
||||
|
||||
#endif // HARNESS_LIBSSH2_REAL
|
||||
|
||||
|
||||
+2343
-6138
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user