Move utils for file system tests into common directory to allow other tests to use them. Signed-off-by: Franciszek Zdobylak <fzdobylak@antmicro.com>
468 lines
8.4 KiB
C
468 lines
8.4 KiB
C
/*
|
|
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
#include <zephyr/ztest.h>
|
|
#include "test_fs_util.h"
|
|
|
|
static const char *path_vextend(struct testfs_path *pp,
|
|
va_list ap)
|
|
{
|
|
const char *ep = va_arg(ap, const char *);
|
|
const char *endp = pp->path + sizeof(pp->path);
|
|
|
|
while (ep) {
|
|
size_t len = strlen(ep);
|
|
char *eos = pp->eos;
|
|
size_t rem = (endp - eos);
|
|
|
|
if (strcmp(ep, "..") == 0) {
|
|
char *sp = strrchr(pp->path, '/');
|
|
|
|
if (sp == pp->path) {
|
|
eos = sp + 1;
|
|
} else {
|
|
eos = sp;
|
|
}
|
|
} else if ((1 + len + 1) < rem) { /* /, ep, EOS */
|
|
*eos = '/';
|
|
++eos;
|
|
memcpy(eos, ep, len);
|
|
eos += len;
|
|
} else {
|
|
break;
|
|
}
|
|
*eos = 0;
|
|
pp->eos = eos;
|
|
|
|
ep = va_arg(ap, const char *);
|
|
}
|
|
return pp->path;
|
|
}
|
|
|
|
const char *testfs_path_init(struct testfs_path *pp,
|
|
const struct fs_mount_t *mp,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (mp == NULL) {
|
|
pp->path[0] = '/';
|
|
pp->eos = pp->path + 1;
|
|
} else {
|
|
size_t len = strlen(mp->mnt_point);
|
|
|
|
__ASSERT('/' == mp->mnt_point[0], "relative mount point");
|
|
if ((len + 1) >= sizeof(pp->path)) {
|
|
len = sizeof(pp->path) - 1;
|
|
}
|
|
strncpy(pp->path, mp->mnt_point, len);
|
|
pp->eos = pp->path + len;
|
|
}
|
|
*pp->eos = '\0';
|
|
|
|
va_start(ap, mp);
|
|
path_vextend(pp, ap);
|
|
va_end(ap);
|
|
return pp->path;
|
|
}
|
|
|
|
const char *testfs_path_extend(struct testfs_path *pp,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, pp);
|
|
path_vextend(pp, ap);
|
|
va_end(ap);
|
|
|
|
return pp->path;
|
|
}
|
|
|
|
int testfs_write_constant(struct fs_file_t *fp,
|
|
uint8_t value,
|
|
unsigned int len)
|
|
{
|
|
uint8_t buffer[TESTFS_BUFFER_SIZE];
|
|
unsigned int rem = len;
|
|
|
|
memset(buffer, value, sizeof(buffer));
|
|
|
|
while (rem > 0) {
|
|
unsigned int count = sizeof(buffer);
|
|
|
|
if (count > rem) {
|
|
count = rem;
|
|
}
|
|
|
|
ssize_t rc = fs_write(fp, buffer, count);
|
|
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
|
|
rem -= count;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
int testfs_verify_constant(struct fs_file_t *fp,
|
|
uint8_t value,
|
|
unsigned int len)
|
|
{
|
|
uint8_t buffer[TESTFS_BUFFER_SIZE];
|
|
unsigned int match = 0;
|
|
|
|
while (len > 0) {
|
|
unsigned int count = sizeof(buffer);
|
|
|
|
if (count > len) {
|
|
count = len;
|
|
}
|
|
|
|
int rc = fs_read(fp, buffer, count);
|
|
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
|
|
if (rc > count) {
|
|
return -EIO;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < rc; ++i) {
|
|
if (value != buffer[i]) {
|
|
break;
|
|
}
|
|
++match;
|
|
}
|
|
|
|
if (rc < count) {
|
|
break;
|
|
}
|
|
|
|
len -= count;
|
|
}
|
|
return match;
|
|
}
|
|
|
|
int testfs_write_incrementing(struct fs_file_t *fp,
|
|
uint8_t value,
|
|
unsigned int len)
|
|
{
|
|
uint8_t buffer[TESTFS_BUFFER_SIZE];
|
|
unsigned int rem = len;
|
|
|
|
while (rem > 0) {
|
|
unsigned int count = sizeof(buffer);
|
|
|
|
if (count > rem) {
|
|
count = rem;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < count; ++i) {
|
|
buffer[i] = value++;
|
|
}
|
|
|
|
ssize_t rc = fs_write(fp, buffer, count);
|
|
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
|
|
rem -= count;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
int testfs_verify_incrementing(struct fs_file_t *fp,
|
|
uint8_t value,
|
|
unsigned int len)
|
|
{
|
|
uint8_t buffer[TESTFS_BUFFER_SIZE];
|
|
unsigned int match = 0;
|
|
|
|
while (len > 0) {
|
|
unsigned int count = sizeof(buffer);
|
|
|
|
if (count > len) {
|
|
count = len;
|
|
}
|
|
|
|
int rc = fs_read(fp, buffer, count);
|
|
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
|
|
if (rc > count) {
|
|
return -EIO;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < rc; ++i) {
|
|
if (value++ != buffer[i]) {
|
|
break;
|
|
}
|
|
++match;
|
|
}
|
|
|
|
if (rc < count) {
|
|
break;
|
|
}
|
|
|
|
len -= count;
|
|
}
|
|
return match;
|
|
}
|
|
|
|
int testfs_build(struct testfs_path *root,
|
|
const struct testfs_bcmd *cp)
|
|
{
|
|
int rc;
|
|
|
|
while (!TESTFS_BCMD_IS_END(cp)) {
|
|
|
|
if (TESTFS_BCMD_IS_FILE(cp)) {
|
|
struct fs_file_t file;
|
|
|
|
fs_file_t_init(&file);
|
|
rc = fs_open(&file,
|
|
testfs_path_extend(root,
|
|
cp->name,
|
|
TESTFS_PATH_END),
|
|
FS_O_CREATE | FS_O_RDWR);
|
|
TC_PRINT("create at %s with %u from 0x%02x: %d\n",
|
|
root->path, cp->size, cp->value, rc);
|
|
if (rc == 0) {
|
|
rc = testfs_write_incrementing(&file, cp->value, cp->size);
|
|
(void)fs_close(&file);
|
|
}
|
|
testfs_path_extend(root, "..", TESTFS_PATH_END);
|
|
|
|
if (rc < 0) {
|
|
TC_PRINT("FAILED create/write %s: %d\n",
|
|
root->path, rc);
|
|
return rc;
|
|
}
|
|
|
|
} else if (TESTFS_BCMD_IS_ENTER_DIR(cp)) {
|
|
testfs_path_extend(root,
|
|
cp->name,
|
|
TESTFS_PATH_END);
|
|
rc = fs_mkdir(root->path);
|
|
TC_PRINT("mkdir %s: %d\n", root->path, rc);
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
} else if (TESTFS_BCMD_IS_EXIT_DIR(cp)) {
|
|
TC_PRINT("exit directory %s\n", root->path);
|
|
testfs_path_extend(root, "..", TESTFS_PATH_END);
|
|
} else {
|
|
TC_PRINT("ERROR: unexpected build command");
|
|
return -EINVAL;
|
|
}
|
|
++cp;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Check the found entry against a probably match. Recurse into
|
|
* matched directories.
|
|
*
|
|
* Sets the matched field of *cp if all tests pass.
|
|
*
|
|
* Return a negative error, or a nonnegative count of foreign
|
|
* files.
|
|
*/
|
|
static int check_layout_entry(struct testfs_path *pp,
|
|
const struct fs_dirent *statp,
|
|
struct testfs_bcmd *cp,
|
|
struct testfs_bcmd *ecp)
|
|
{
|
|
int rc = 0;
|
|
unsigned int foreign = 0;
|
|
|
|
/* Create the full path */
|
|
testfs_path_extend(pp, statp->name,
|
|
TESTFS_PATH_END);
|
|
|
|
/* Also check file content */
|
|
if (statp->type == FS_DIR_ENTRY_FILE) {
|
|
struct fs_file_t file;
|
|
|
|
fs_file_t_init(&file);
|
|
rc = fs_open(&file, pp->path, FS_O_CREATE | FS_O_RDWR);
|
|
if (rc < 0) {
|
|
TC_PRINT("%s: content check open failed: %d\n",
|
|
pp->path, rc);
|
|
rc = -ENOENT;
|
|
goto out;
|
|
}
|
|
rc = testfs_verify_incrementing(&file, cp->value, cp->size);
|
|
if (rc != cp->size) {
|
|
TC_PRINT("%s: content check failed: %d\n",
|
|
pp->path, rc);
|
|
if (rc >= 0) {
|
|
rc = -EIO;
|
|
}
|
|
goto out;
|
|
}
|
|
rc = fs_close(&file);
|
|
if (rc != 0) {
|
|
TC_PRINT("%s: content check close failed: %d\n",
|
|
pp->path, rc);
|
|
}
|
|
} else if (statp->type == FS_DIR_ENTRY_DIR) {
|
|
rc = testfs_bcmd_verify_layout(pp,
|
|
cp + 1,
|
|
testfs_bcmd_exitdir(cp, ecp));
|
|
if (rc >= 0) {
|
|
foreign = rc;
|
|
}
|
|
}
|
|
|
|
out:
|
|
testfs_path_extend(pp, "..",
|
|
TESTFS_PATH_END);
|
|
|
|
if (rc >= 0) {
|
|
cp->matched = true;
|
|
rc = foreign;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int testfs_bcmd_verify_layout(struct testfs_path *pp,
|
|
struct testfs_bcmd *scp,
|
|
struct testfs_bcmd *ecp)
|
|
{
|
|
struct fs_dir_t dir;
|
|
unsigned int count = 0;
|
|
unsigned int foreign = 0;
|
|
struct testfs_bcmd *cp = scp;
|
|
|
|
while (cp < ecp) {
|
|
cp->matched = false;
|
|
++cp;
|
|
}
|
|
|
|
fs_dir_t_init(&dir);
|
|
|
|
int rc = fs_opendir(&dir, pp->path);
|
|
|
|
if (rc != 0) {
|
|
TC_PRINT("%s: opendir failed: %d\n", pp->path, rc);
|
|
if (rc > 0) {
|
|
rc = -EIO;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
TC_PRINT("check %s for %zu entries\n", pp->path, ecp - scp);
|
|
while (rc >= 0) {
|
|
struct fs_dirent stat;
|
|
|
|
rc = fs_readdir(&dir, &stat);
|
|
if (rc != 0) {
|
|
TC_PRINT("readdir failed: %d", rc);
|
|
rc = -EIO;
|
|
break;
|
|
}
|
|
|
|
if (stat.name[0] == '\0') {
|
|
break;
|
|
}
|
|
|
|
++count;
|
|
|
|
cp = testfs_bcmd_find(&stat, scp, ecp);
|
|
|
|
bool dotdir = ((stat.type == FS_DIR_ENTRY_DIR)
|
|
&& ((strcmp(stat.name, ".") == 0)
|
|
|| (strcmp(stat.name, "..") == 0)));
|
|
|
|
TC_PRINT("%s %s%s%s %zu\n", pp->path,
|
|
stat.name,
|
|
(stat.type == FS_DIR_ENTRY_FILE) ? "" : "/",
|
|
dotdir ? " SYNTHESIZED"
|
|
: (cp == NULL) ? " FOREIGN"
|
|
: "",
|
|
stat.size);
|
|
|
|
if (dotdir) {
|
|
zassert_true(false,
|
|
"special directories observed");
|
|
} else if (cp != NULL) {
|
|
rc = check_layout_entry(pp, &stat, cp, ecp);
|
|
if (rc > 0) {
|
|
foreign += rc;
|
|
}
|
|
} else {
|
|
foreign += 1;
|
|
}
|
|
}
|
|
TC_PRINT("%s found %u entries, %u foreign\n", pp->path, count, foreign);
|
|
|
|
int rc2 = fs_closedir(&dir);
|
|
|
|
if (rc2 != 0) {
|
|
TC_PRINT("%s: closedir failed: %d\n",
|
|
pp->path, rc2);
|
|
if (rc >= 0) {
|
|
rc = (rc2 >= 0) ? -EIO : rc2;
|
|
}
|
|
}
|
|
|
|
if (rc >= 0) {
|
|
rc = foreign;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
struct testfs_bcmd *testfs_bcmd_exitdir(struct testfs_bcmd *cp,
|
|
struct testfs_bcmd *ecp)
|
|
{
|
|
unsigned int level = 1;
|
|
|
|
/* Skip to the paired EXIT_DIR */
|
|
while ((level > 0) && (++cp < ecp)) {
|
|
if (TESTFS_BCMD_IS_ENTER_DIR(cp)) {
|
|
++level;
|
|
} else if (TESTFS_BCMD_IS_EXIT_DIR(cp)) {
|
|
--level;
|
|
}
|
|
}
|
|
return cp;
|
|
}
|
|
|
|
struct testfs_bcmd *testfs_bcmd_find(struct fs_dirent *statp,
|
|
struct testfs_bcmd *scp,
|
|
struct testfs_bcmd *ecp)
|
|
{
|
|
struct testfs_bcmd *cp = scp;
|
|
|
|
while (cp < ecp) {
|
|
if ((cp->type == statp->type)
|
|
&& (cp->name != NULL)
|
|
&& (strcmp(cp->name, statp->name) == 0)
|
|
&& (cp->size == statp->size)) {
|
|
return cp;
|
|
}
|
|
|
|
if (TESTFS_BCMD_IS_ENTER_DIR(cp)) {
|
|
cp = testfs_bcmd_exitdir(cp, ecp);
|
|
}
|
|
++cp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|