/*
* Copyright 2016-2018, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* rpmemd_db_test.c -- unit test for pool set database
*
* usage: rpmemd_db <log-file> <root_dir> <pool_desc_1> <pool_desc_2>
*/
#include "file.h"
#include "unittest.h"
#include "librpmem.h"
#include "rpmemd_db.h"
#include "rpmemd_log.h"
#include "util_pmem.h"
#include "set.h"
#include "out.h"
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#define POOL_MODE 0644
#define FAILED_FUNC(func_name) \
UT_ERR("!%s(): %s() failed", __func__, func_name);
#define FAILED_FUNC_PARAM(func_name, param) \
UT_ERR("!%s(): %s(%s) failed", __func__, func_name, param);
#define NPOOLS_DUAL 2
#define POOL_ATTR_CREATE 0
#define POOL_ATTR_OPEN 1
#define POOL_ATTR_SET_ATTR 2
#define POOL_STATE_INITIAL 0
#define POOL_STATE_CREATED 1
#define POOL_STATE_OPENED 2
#define POOL_STATE_CLOSED POOL_STATE_CREATED
#define POOL_STATE_REMOVED POOL_STATE_INITIAL
/*
* fill_rand -- fill a buffer with random values
*/
static void
fill_rand(void *addr, size_t len)
{
unsigned char *buff = addr;
srand(time(NULL));
for (unsigned i = 0; i < len; i++)
buff[i] = (rand() % ('z' - 'a')) + 'a';
}
/*
* test_init -- test rpmemd_db_init() and rpmemd_db_fini()
*/
static int
test_init(const char *root_dir)
{
struct rpmemd_db *db;
db = rpmemd_db_init(root_dir, POOL_MODE);
if (db == NULL) {
FAILED_FUNC("rpmemd_db_init");
return -1;
}
rpmemd_db_fini(db);
return 0;
}
/*
* test_check_dir -- test rpmemd_db_check_dir()
*/
static int
test_check_dir(const char *root_dir)
{
struct rpmemd_db *db;
int ret;
db = rpmemd_db_init(root_dir, POOL_MODE);
if (db == NULL) {
FAILED_FUNC("rpmemd_db_init");
return -1;
}
ret = rpmemd_db_check_dir(db);
if (ret) {
FAILED_FUNC("rpmemd_db_check_dir");
}
rpmemd_db_fini(db);
return ret;
}
/*
* test_create -- test rpmemd_db_pool_create()
*/
static int
test_create(const char *root_dir, const char *pool_desc)
{
struct rpmem_pool_attr attr;
memset(&attr, 0, sizeof(attr));
attr.incompat_features = 2;
struct rpmemd_db_pool *prp;
struct rpmemd_db *db;
int ret = -1;
db = rpmemd_db_init(root_dir, POOL_MODE);
if (db == NULL) {
FAILED_FUNC("rpmemd_db_init");
return -1;
}
prp = rpmemd_db_pool_create(db, pool_desc, 0, &attr);
if (prp == NULL) {
FAILED_FUNC("rpmemd_db_pool_create");
goto fini;
}
rpmemd_db_pool_close(db, prp);
ret = rpmemd_db_pool_remove(db, pool_desc, 0, 0);
if (ret) {
FAILED_FUNC("rpmemd_db_pool_remove");
}
fini:
rpmemd_db_fini(db);
return ret;
}
/*
* test_create_dual -- dual test for rpmemd_db_pool_create()
*/
static int
test_create_dual(const char *root_dir, const char *pool_desc_1,
const char *pool_desc_2)
{
struct rpmem_pool_attr attr1;
memset(&attr1, 0, sizeof(attr1));
attr1.incompat_features = 2;
struct rpmemd_db_pool *prp1, *prp2;
struct rpmemd_db *db;
int ret = -1;
db = rpmemd_db_init(root_dir, POOL_MODE);
if (db == NULL) {
FAILED_FUNC("rpmemd_db_init");
return -1;
}
/* test dual create */
prp1 = rpmemd_db_pool_create(db, pool_desc_1, 0, &attr1);
if (prp1 == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_create", pool_desc_1);
goto err_create_1;
}
prp2 = rpmemd_db_pool_create(db, pool_desc_2, 0, &attr1);
if (prp2 == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_create", pool_desc_2);
goto err_create_2;
}
rpmemd_db_pool_close(db, prp2);
rpmemd_db_pool_close(db, prp1);
ret = rpmemd_db_pool_remove(db, pool_desc_2, 0, 0);
if (ret) {
FAILED_FUNC_PARAM("rpmemd_db_pool_remove", pool_desc_2);
goto err_remove_2;
}
ret = rpmemd_db_pool_remove(db, pool_desc_1, 0, 0);
if (ret) {
FAILED_FUNC_PARAM("rpmemd_db_pool_remove", pool_desc_1);
}
goto fini;
err_create_2:
rpmemd_db_pool_close(db, prp1);
err_remove_2:
rpmemd_db_pool_remove(db, pool_desc_1, 0, 0);
err_create_1:
fini:
rpmemd_db_fini(db);
return ret;
}
/*
* compare_attr -- compare pool's attributes
*/
static void
compare_attr(struct rpmem_pool_attr *a1, struct rpmem_pool_attr *a2)
{
char *msg;
if (a1->major != a2->major) {
msg = "major";
goto err_mismatch;
}
if (a1->compat_features != a2->compat_features) {
msg = "compat_features";
goto err_mismatch;
}
if (a1->incompat_features != a2->incompat_features) {
msg = "incompat_features";
goto err_mismatch;
}
if (a1->ro_compat_features != a2->ro_compat_features) {
msg = "ro_compat_features";
goto err_mismatch;
}
if (memcmp(a1->signature, a2->signature, RPMEM_POOL_HDR_SIG_LEN)) {
msg = "signature";
goto err_mismatch;
}
if (memcmp(a1->poolset_uuid, a2->poolset_uuid,
RPMEM_POOL_HDR_UUID_LEN)) {
msg = "poolset_uuid";
goto err_mismatch;
}
if (memcmp(a1->uuid, a2->uuid, RPMEM_POOL_HDR_UUID_LEN)) {
msg = "uuid";
goto err_mismatch;
}
if (memcmp(a1->next_uuid, a2->next_uuid, RPMEM_POOL_HDR_UUID_LEN)) {
msg = "next_uuid";
goto err_mismatch;
}
if (memcmp(a1->prev_uuid, a2->prev_uuid, RPMEM_POOL_HDR_UUID_LEN)) {
msg = "prev_uuid";
goto err_mismatch;
}
return;
err_mismatch:
errno = EINVAL;
UT_FATAL("%s(): pool attributes mismatch (%s)", __func__, msg);
}
/*
* test_open -- test rpmemd_db_pool_open()
*/
static int
test_open(const char *root_dir, const char *pool_desc)
{
struct rpmem_pool_attr attr1, attr2;
struct rpmemd_db_pool *prp;
struct rpmemd_db *db;
int ret = -1;
fill_rand(&attr1, sizeof(attr1));
attr1.major = 1;
attr1.incompat_features = 2;
attr1.compat_features = 0;
db = rpmemd_db_init(root_dir, POOL_MODE);
if (db == NULL) {
FAILED_FUNC("rpmemd_db_init");
return -1;
}
prp = rpmemd_db_pool_create(db, pool_desc, 0, &attr1);
if (prp == NULL) {
FAILED_FUNC("rpmemd_db_pool_create");
goto fini;
}
rpmemd_db_pool_close(db, prp);
prp = rpmemd_db_pool_open(db, pool_desc, 0, &attr2);
if (prp == NULL) {
FAILED_FUNC("rpmemd_db_pool_open");
goto fini;
}
rpmemd_db_pool_close(db, prp);
compare_attr(&attr1, &attr2);
ret = rpmemd_db_pool_remove(db, pool_desc, 0, 0);
if (ret) {
FAILED_FUNC("rpmemd_db_pool_remove");
}
fini:
rpmemd_db_fini(db);
return ret;
}
/*
* test_open_dual -- dual test for rpmemd_db_pool_open()
*/
static int
test_open_dual(const char *root_dir, const char *pool_desc_1,
const char *pool_desc_2)
{
struct rpmem_pool_attr attr1a, attr2a, attr1b, attr2b;
struct rpmemd_db_pool *prp1, *prp2;
struct rpmemd_db *db;
int ret = -1;
fill_rand(&attr1a, sizeof(attr1a));
fill_rand(&attr1b, sizeof(attr1b));
attr1a.major = 1;
attr1a.incompat_features = 2;
attr1a.compat_features = 0;
attr1b.major = 1;
attr1b.incompat_features = 2;
attr1b.compat_features = 0;
db = rpmemd_db_init(root_dir, POOL_MODE);
if (db == NULL) {
FAILED_FUNC("rpmemd_db_init");
return -1;
}
prp1 = rpmemd_db_pool_create(db, pool_desc_1, 0, &attr1a);
if (prp1 == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_create", pool_desc_1);
goto err_create_1;
}
rpmemd_db_pool_close(db, prp1);
prp2 = rpmemd_db_pool_create(db, pool_desc_2, 0, &attr1b);
if (prp2 == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_create", pool_desc_2);
goto err_create_2;
}
rpmemd_db_pool_close(db, prp2);
/* test dual open */
prp1 = rpmemd_db_pool_open(db, pool_desc_1, 0, &attr2a);
if (prp1 == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_open", pool_desc_1);
goto err_open_1;
}
prp2 = rpmemd_db_pool_open(db, pool_desc_2, 0, &attr2b);
if (prp2 == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_open", pool_desc_2);
goto err_open_2;
}
rpmemd_db_pool_close(db, prp1);
rpmemd_db_pool_close(db, prp2);
compare_attr(&attr1a, &attr2a);
compare_attr(&attr1b, &attr2b);
ret = rpmemd_db_pool_remove(db, pool_desc_2, 0, 0);
if (ret) {
FAILED_FUNC_PARAM("rpmemd_db_pool_remove", pool_desc_2);
goto err_remove_2;
}
ret = rpmemd_db_pool_remove(db, pool_desc_1, 0, 0);
if (ret) {
FAILED_FUNC_PARAM("rpmemd_db_pool_remove", pool_desc_1);
}
goto fini;
err_open_2:
rpmemd_db_pool_close(db, prp1);
err_open_1:
rpmemd_db_pool_remove(db, pool_desc_2, 0, 0);
err_create_2:
err_remove_2:
rpmemd_db_pool_remove(db, pool_desc_1, 0, 0);
err_create_1:
fini:
rpmemd_db_fini(db);
return ret;
}
/*
* test_set_attr -- test rpmemd_db_pool_set_attr()
*/
static int
test_set_attr(const char *root_dir, const char *pool_desc)
{
struct rpmem_pool_attr attr[3];
struct rpmemd_db_pool *prp;
struct rpmemd_db *db;
int ret = -1;
fill_rand(&attr[POOL_ATTR_CREATE], sizeof(attr[POOL_ATTR_CREATE]));
fill_rand(&attr[POOL_ATTR_SET_ATTR], sizeof(attr[POOL_ATTR_SET_ATTR]));
attr[POOL_ATTR_CREATE].major = 1;
attr[POOL_ATTR_CREATE].incompat_features = 2;
attr[POOL_ATTR_CREATE].compat_features = 0;
attr[POOL_ATTR_SET_ATTR].major = 1;
attr[POOL_ATTR_SET_ATTR].incompat_features = 2;
attr[POOL_ATTR_SET_ATTR].compat_features = 0;
db = rpmemd_db_init(root_dir, POOL_MODE);
if (db == NULL) {
FAILED_FUNC("rpmemd_db_init");
return -1;
}
prp = rpmemd_db_pool_create(db, pool_desc, 0, &attr[POOL_ATTR_CREATE]);
if (prp == NULL) {
FAILED_FUNC("rpmemd_db_pool_create");
goto err_create;
}
rpmemd_db_pool_close(db, prp);
prp = rpmemd_db_pool_open(db, pool_desc, 0, &attr[POOL_ATTR_OPEN]);
if (prp == NULL) {
FAILED_FUNC("rpmemd_db_pool_open");
goto err_open;
}
compare_attr(&attr[POOL_ATTR_CREATE], &attr[POOL_ATTR_OPEN]);
ret = rpmemd_db_pool_set_attr(prp, &attr[POOL_ATTR_SET_ATTR]);
if (ret) {
FAILED_FUNC("rpmemd_db_pool_set_attr");
goto err_set_attr;
}
rpmemd_db_pool_close(db, prp);
prp = rpmemd_db_pool_open(db, pool_desc, 0, &attr[POOL_ATTR_OPEN]);
if (prp == NULL) {
FAILED_FUNC("rpmemd_db_pool_open");
goto err_open;
}
compare_attr(&attr[POOL_ATTR_SET_ATTR], &attr[POOL_ATTR_OPEN]);
rpmemd_db_pool_close(db, prp);
ret = rpmemd_db_pool_remove(db, pool_desc, 0, 0);
if (ret) {
FAILED_FUNC("rpmemd_db_pool_remove");
}
goto fini;
err_set_attr:
rpmemd_db_pool_close(db, prp);
err_open:
rpmemd_db_pool_remove(db, pool_desc, 0, 0);
err_create:
fini:
rpmemd_db_fini(db);
return ret;
}
/*
* test_set_attr_dual -- dual test for rpmemd_db_pool_set_attr()
*/
static int
test_set_attr_dual(const char *root_dir, const char *pool_desc_1,
const char *pool_desc_2)
{
struct rpmem_pool_attr attr[NPOOLS_DUAL][3];
struct rpmemd_db_pool *prp[NPOOLS_DUAL];
const char *pool_desc[NPOOLS_DUAL] = {pool_desc_1, pool_desc_2};
unsigned pool_state[NPOOLS_DUAL] = {POOL_STATE_INITIAL};
struct rpmemd_db *db;
int ret = -1;
/* initialize rpmem database */
db = rpmemd_db_init(root_dir, POOL_MODE);
if (db == NULL) {
FAILED_FUNC("rpmemd_db_init");
return -1;
}
for (unsigned p = 0; p < NPOOLS_DUAL; ++p) {
/*
* generate random pool attributes for create and set
* attributes operations
*/
fill_rand(&attr[p][POOL_ATTR_CREATE],
sizeof(attr[p][POOL_ATTR_CREATE]));
fill_rand(&attr[p][POOL_ATTR_SET_ATTR],
sizeof(attr[p][POOL_ATTR_SET_ATTR]));
attr[p][POOL_ATTR_CREATE].major = 1;
attr[p][POOL_ATTR_CREATE].incompat_features = 2;
attr[p][POOL_ATTR_CREATE].compat_features = 0;
attr[p][POOL_ATTR_SET_ATTR].major = 1;
attr[p][POOL_ATTR_SET_ATTR].incompat_features = 2;
attr[p][POOL_ATTR_SET_ATTR].compat_features = 0;
/* create pool */
prp[p] = rpmemd_db_pool_create(db, pool_desc[p], 0,
&attr[p][POOL_ATTR_CREATE]);
if (prp[p] == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_create",
pool_desc[p]);
goto err;
}
rpmemd_db_pool_close(db, prp[p]);
pool_state[p] = POOL_STATE_CREATED;
}
/* open pools and check pool attributes */
for (unsigned p = 0; p < NPOOLS_DUAL; ++p) {
prp[p] = rpmemd_db_pool_open(db, pool_desc[p], 0,
&attr[p][POOL_ATTR_OPEN]);
if (prp[p] == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_open", pool_desc[p]);
goto err;
}
pool_state[p] = POOL_STATE_OPENED;
compare_attr(&attr[p][POOL_ATTR_CREATE],
&attr[p][POOL_ATTR_OPEN]);
}
/* set attributes and close pools */
for (unsigned p = 0; p < NPOOLS_DUAL; ++p) {
ret = rpmemd_db_pool_set_attr(prp[p],
&attr[p][POOL_ATTR_SET_ATTR]);
if (ret) {
FAILED_FUNC_PARAM("rpmemd_db_pool_set_attr",
pool_desc[p]);
goto err;
}
rpmemd_db_pool_close(db, prp[p]);
pool_state[p] = POOL_STATE_CLOSED;
}
/* open pools and check attributes */
for (unsigned p = 0; p < NPOOLS_DUAL; ++p) {
prp[p] = rpmemd_db_pool_open(db, pool_desc[p], 0,
&attr[p][POOL_ATTR_OPEN]);
if (prp[p] == NULL) {
FAILED_FUNC_PARAM("rpmemd_db_pool_open", pool_desc[p]);
goto err;
}
pool_state[p] = POOL_STATE_OPENED;
compare_attr(&attr[p][POOL_ATTR_SET_ATTR],
&attr[p][POOL_ATTR_OPEN]);
}
err:
for (unsigned p = 0; p < NPOOLS_DUAL; ++p) {
if (pool_state[p] == POOL_STATE_OPENED) {
rpmemd_db_pool_close(db, prp[p]);
pool_state[p] = POOL_STATE_CLOSED;
}
if (pool_state[p] == POOL_STATE_CREATED) {
ret = rpmemd_db_pool_remove(db, pool_desc[p], 0, 0);
if (ret) {
FAILED_FUNC_PARAM("rpmemd_db_pool_remove",
pool_desc[p]);
}
pool_state[p] = POOL_STATE_REMOVED;
}
}
rpmemd_db_fini(db);
return ret;
}
static int
exists_cb(struct part_file *pf, void *arg)
{
return util_file_exists(pf->part->path);
}
static int
noexists_cb(struct part_file *pf, void *arg)
{
int exists = util_file_exists(pf->part->path);
if (exists < 0)
return -1;
else
return !exists;
}
/*
* test_remove -- test for rpmemd_db_pool_remove()
*/
static void
test_remove(const char *root_dir, const char *pool_desc)
{
struct rpmem_pool_attr attr;
struct rpmemd_db_pool *prp;
struct rpmemd_db *db;
int ret;
char path[PATH_MAX];
snprintf(path, PATH_MAX, "%s/%s", root_dir, pool_desc);
fill_rand(&attr, sizeof(attr));
strncpy((char *)attr.poolset_uuid, "TEST", sizeof(attr.poolset_uuid));
attr.incompat_features = 2;
attr.compat_features = 0;
db = rpmemd_db_init(root_dir, POOL_MODE);
UT_ASSERTne(db, NULL);
prp = rpmemd_db_pool_create(db, pool_desc, 0, &attr);
UT_ASSERTne(prp, NULL);
rpmemd_db_pool_close(db, prp);
ret = util_poolset_foreach_part(path, exists_cb, NULL);
UT_ASSERTeq(ret, 1);
ret = rpmemd_db_pool_remove(db, pool_desc, 0, 0);
UT_ASSERTeq(ret, 0);
ret = util_poolset_foreach_part(path, noexists_cb, NULL);
UT_ASSERTeq(ret, 1);
prp = rpmemd_db_pool_create(db, pool_desc, 0, &attr);
UT_ASSERTne(prp, NULL);
rpmemd_db_pool_close(db, prp);
ret = rpmemd_db_pool_remove(db, pool_desc, 0, 1);
UT_ASSERTeq(ret, 0);
ret = util_file_exists(path);
UT_ASSERTne(ret, 1);
rpmemd_db_fini(db);
}
int
main(int argc, char *argv[])
{
char *pool_desc[2], *log_file;
char root_dir[PATH_MAX];
START(argc, argv, "rpmemd_db");
util_init();
out_init("rpmemd_db", "RPMEM_LOG_LEVEL", "RPMEM_LOG_FILE", 0, 0);
if (argc != 5)
UT_FATAL("usage: %s <log-file> <root_dir> <pool_desc_1>"
" <pool_desc_2>", argv[0]);
log_file = argv[1];
if (realpath(argv[2], root_dir) == NULL)
UT_FATAL("!realpath(%s)", argv[1]);
pool_desc[0] = argv[3];
pool_desc[1] = argv[4];
if (rpmemd_log_init("rpmemd error: ", log_file, 0))
FAILED_FUNC("rpmemd_log_init");
test_init(root_dir);
test_check_dir(root_dir);
test_create(root_dir, pool_desc[0]);
test_create_dual(root_dir, pool_desc[0], pool_desc[1]);
test_open(root_dir, pool_desc[0]);
test_open_dual(root_dir, pool_desc[0], pool_desc[1]);
test_set_attr(root_dir, pool_desc[0]);
test_set_attr_dual(root_dir, pool_desc[0], pool_desc[1]);
test_remove(root_dir, pool_desc[0]);
rpmemd_log_close();
out_fini();
DONE(NULL);
}