From f449f278dd3c70e479a035f50a9bb817a9b433ba Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:24:08 +0200 Subject: Adding upstream version 3.2.6. Signed-off-by: Daniel Baumann --- tests/knot/test_confio.c | 1100 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1100 insertions(+) create mode 100644 tests/knot/test_confio.c (limited to 'tests/knot/test_confio.c') diff --git a/tests/knot/test_confio.c b/tests/knot/test_confio.c new file mode 100644 index 0000000..f5d3c94 --- /dev/null +++ b/tests/knot/test_confio.c @@ -0,0 +1,1100 @@ +/* Copyright (C) 2022 CZ.NIC, z.s.p.o. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#include "test_conf.h" +#include "knot/conf/confio.h" +#include "knot/conf/tools.h" +#include "libknot/yparser/yptrafo.h" +#include "contrib/string.h" +#include "contrib/openbsd/strlcat.h" + +#define SKIP_OPENBSD skip("Nested transactions are not supported on OpenBSD"); +#define OUT_LEN 1024 +#define ZONE1 "zone1" +#define ZONE2 "zone2" +#define ZONE3 "zone3" + +char *format_key(conf_io_t *io) +{ + knot_dname_txt_storage_t id; + size_t id_len = sizeof(id); + + // Get the textual item id. + if (io->id_len > 0 && !io->id_as_data) { + if (yp_item_to_txt(io->key0->var.g.id, io->id, io->id_len, id, + &id_len, YP_SNOQUOTE) != KNOT_EOK) { + return NULL; + } + } + + // Get the item prefix. + const char *prefix = ""; + switch (io->type) { + case NEW: prefix = "+"; break; + case OLD: prefix = "-"; break; + default: break; + } + + // Format the item key. + return sprintf_alloc( + "%s%.*s%s%.*s%s%s%.*s", + prefix, (int)io->key0->name[0], io->key0->name + 1, + (io->id_len > 0 && !io->id_as_data ? "[" : ""), + (io->id_len > 0 && !io->id_as_data ? (int)id_len : 0), id, + (io->id_len > 0 && !io->id_as_data ? "]" : ""), + (io->key1 != NULL ? "." : ""), + (io->key1 != NULL ? (int)io->key1->name[0] : 0), + (io->key1 != NULL ? io->key1->name + 1 : "")); +} + +static int append_data(const yp_item_t *item, const uint8_t *bin, size_t bin_len, + char *out, size_t out_len) +{ + char buf[YP_MAX_TXT_DATA_LEN + 1] = "\0"; + size_t buf_len = sizeof(buf); + + int ret = yp_item_to_txt(item, bin, bin_len, buf, &buf_len, YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + + if (strlcat(out, buf, out_len) >= out_len) { + return KNOT_ESPACE; + } + + return KNOT_EOK; +} + +char *format_data(conf_io_t *io) +{ + char out[YP_MAX_TXT_DATA_LEN + 1] = "\0"; + + // Return the item identifier as the item data. + if (io->id_as_data) { + if (append_data(io->key0->var.g.id, io->id, io->id_len, out, + sizeof(out)) != KNOT_EOK) { + return NULL; + } + + return strdup(out); + } + + // Check for no data. + if (io->data.val == NULL && io->data.bin == NULL) { + return NULL; + } + + const yp_item_t *item = (io->key1 != NULL) ? io->key1 : io->key0; + + // Format explicit binary data value. + if (io->data.bin != NULL) { + if (append_data(item, io->data.bin, io->data.bin_len, out, + sizeof(out)) != KNOT_EOK) { + return NULL; + } + // Format multivalued item data. + } else if (item->flags & YP_FMULTI) { + size_t values = conf_val_count(io->data.val); + for (size_t i = 0; i < values; i++) { + // Skip other values if known index (counted from 1). + if (io->data.index > 0 && + io->data.index != i + 1) { + conf_val_next(io->data.val); + continue; + } + + if (i > 0) { + if (strlcat(out, " ", sizeof(out)) >= sizeof(out)) { + return NULL; + } + } + + conf_val(io->data.val); + if (append_data(item, io->data.val->data, io->data.val->len, + out, sizeof(out)) != KNOT_EOK) { + return NULL; + } + + conf_val_next(io->data.val); + } + // Format singlevalued item data. + } else { + conf_val(io->data.val); + if (append_data(item, io->data.val->data, io->data.val->len, out, + sizeof(out)) != KNOT_EOK) { + return NULL; + } + } + + return strdup(out); +} + +static int format_item(conf_io_t *io) +{ + char *out = (char *)io->misc; + + // Get the item key and data strings. + char *key = format_key(io); + char *data = format_data(io); + + // Format the item. + char *item = sprintf_alloc( + "%s%s%s%s", + (*out != '\0' ? "\n" : ""), + (key != NULL ? key : ""), + (data != NULL ? " = " : ""), + (data != NULL ? data : "")); + free(key); + free(data); + if (item == NULL) { + return KNOT_ENOMEM; + } + + // Append the item. + if (strlcat(out, item, OUT_LEN) >= OUT_LEN) { + free(item); + return KNOT_ESPACE; + } + + free(item); + + return KNOT_EOK; +} + +static void test_conf_io_begin(void) +{ + ok(conf_io_begin(true) == KNOT_TXN_ENOTEXISTS, "begin child txn with no parent"); + ok(conf()->io.txn == NULL, "check txn depth"); + +#if defined(__OpenBSD__) + SKIP_OPENBSD +#else + ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn"); + ok(conf()->io.txn == &(conf()->io.txn_stack[0]), "check txn depth"); + + ok(conf_io_begin(false) == KNOT_TXN_EEXISTS, "begin another parent txn"); + ok(conf()->io.txn == &(conf()->io.txn_stack[0]), "check txn depth"); + + for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) { + ok(conf_io_begin(true) == KNOT_EOK, "begin child txn"); + ok(conf()->io.txn == &(conf()->io.txn_stack[i]), "check txn depth"); + } + ok(conf_io_begin(true) == KNOT_TXN_EEXISTS, "begin another child txn"); + ok(conf()->io.txn == &(conf()->io.txn_stack[CONF_MAX_TXN_DEPTH - 1]), + "check txn depth"); + + conf_io_abort(false); + ok(conf()->io.txn == NULL, "check txn depth"); +#endif +} + +static void test_conf_io_abort(void) +{ +#if defined(__OpenBSD__) + SKIP_OPENBSD +#else + // Test child persistence after subchild abort. + { + char idx[2] = { '0' }; + ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn"); + ok(conf_io_set("server", "version", NULL, idx) == + KNOT_EOK, "set single value '%s'", idx); + } + + for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) { + char idx[2] = { '0' + i }; + ok(conf_io_begin(true) == KNOT_EOK, "begin child txn %s", idx); + ok(conf_io_set("server", "version", NULL, idx) == + KNOT_EOK, "set single value '%s'", idx); + } + + for (int i = CONF_MAX_TXN_DEPTH - 1; i > 0; i--) { + char idx[2] = { '0' + i }; + conf_io_abort(true); + conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_EOK, "check entry"); + const char *data = conf_str(&val); + ok(*data == (idx[0] - 1), "compare txn data '%s'", data); + } + + conf_io_abort(false); + ok(conf()->io.txn == NULL, "check txn depth"); + + // Test child abort with committed subchild. + ok(conf_io_begin(false) == KNOT_EOK, "begin new parent txn"); + ok(conf_io_begin(true) == KNOT_EOK, "begin child txn"); + ok(conf_io_begin(true) == KNOT_EOK, "begin subchild txn"); + ok(conf_io_set("server", "version", NULL, "text") == + KNOT_EOK, "set single value"); + ok(conf_io_commit(true) == KNOT_EOK, "commit subchild txn"); + conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_EOK, "check entry"); + const char *data = conf_str(&val); + ok(strcmp(data, "text") == 0, "compare subchild txn data '%s'", data); + conf_io_abort(true); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_ENOENT, "check entry"); + conf_io_abort(false); + + // Test unchanged read_txn. + val = conf_get_txn(conf(), &conf()->read_txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_ENOENT, "check entry"); +#endif +} + +static void test_conf_io_commit(void) +{ + ok(conf_io_commit(false) == KNOT_TXN_ENOTEXISTS, "commit no txt txn"); + ok(conf_io_commit(true) == KNOT_TXN_ENOTEXISTS, "commit no txt txn"); + +#if defined(__OpenBSD__) + SKIP_OPENBSD +#else + // Test subchild persistence after commit. + { + char idx[2] = { '0' }; + ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn"); + ok(conf_io_set("server", "version", NULL, idx) == + KNOT_EOK, "set single value '%s'", idx); + } + + for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) { + char idx[2] = { '0' + i }; + ok(conf_io_begin(true) == KNOT_EOK, "begin child txn %s", idx); + ok(conf_io_set("server", "version", NULL, idx) == + KNOT_EOK, "set single value '%s'", idx); + } + + for (int i = CONF_MAX_TXN_DEPTH - 1; i > 0; i--) { + char idx[2] = { '0' + i }; + ok(conf_io_commit(true) == KNOT_EOK, "commit child txn %s", idx); + conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_EOK, "check entry"); + const char *data = conf_str(&val); + ok(*data == ('0' + CONF_MAX_TXN_DEPTH - 1), "compare txn data '%s'", data); + } + + ok(conf_io_commit(false) == KNOT_EOK, "commit parent txn"); + ok(conf()->io.txn == NULL, "check txn depth"); + + // Test child persistence after parent commit. + ok(conf_io_begin(false) == KNOT_EOK, "begin new parent txn"); + conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_EOK, "check entry"); + char idx[2] = { '0' + CONF_MAX_TXN_DEPTH - 1 }; + const char *data = conf_str(&val); + ok(strcmp(data, idx) == 0, "compare final data '%s'", data); + conf_io_abort(false); + + // Test unchanged read_txn. + val = conf_get_txn(conf(), &conf()->read_txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_ENOENT, "check entry"); +#endif +} + +static void test_conf_io_check(void) +{ + conf_io_t io = { NULL }; + + // ERR no txn. + ok(conf_io_check(&io) == + KNOT_TXN_ENOTEXISTS, "check without active txn"); + + ok(conf_io_begin(false) == KNOT_EOK, "begin txn"); + + // Section check. + ok(conf_io_set("remote", "id", NULL, "remote1") == + KNOT_EOK, "set remote id"); + ok(conf_io_check(&io) == + KNOT_EINVAL, "check missing remote address"); + ok(io.error.code == KNOT_EINVAL, "compare error code"); + + ok(conf_io_set("remote", "address", "remote1", "1.1.1.1") == + KNOT_EOK, "set remote address"); + ok(conf_io_check(&io) == + KNOT_EOK, "check remote address"); + ok(io.error.code == KNOT_EOK, "compare error code"); + + // Item check. + ok(conf_io_set("zone", "domain", NULL, ZONE1) == + KNOT_EOK, "set zone domain "ZONE1); + ok(conf_io_set("zone", "master", ZONE1, "remote1") == + KNOT_EOK, "set zone master"); + + ok(conf_io_check(&io) == + KNOT_EOK, "check all"); + + ok(conf_io_unset("remote", NULL, NULL, NULL) == + KNOT_EOK, "unset remotes"); + + ok(conf_io_check(&io) == + KNOT_ENOENT, "check missing master remote"); + ok(io.error.code == KNOT_ENOENT, "compare error code"); + + conf_io_abort(false); +} + +static void test_conf_io_set(void) +{ + // ERR no txn. + ok(conf_io_set("server", "version", NULL, "text") == + KNOT_TXN_ENOTEXISTS, "set without active txn"); + + ok(conf_io_begin(false) == KNOT_EOK, "begin txn"); + + // ERR. + ok(conf_io_set(NULL, NULL, NULL, NULL) == + KNOT_EINVAL, "set NULL key0"); + ok(conf_io_set("", NULL, NULL, NULL) == + KNOT_YP_EINVAL_ITEM, "set empty key0"); + ok(conf_io_set("unknown", NULL, NULL, NULL) == + KNOT_YP_EINVAL_ITEM, "set unknown key0"); + ok(conf_io_set("server", "unknown", NULL, NULL) == + KNOT_YP_EINVAL_ITEM, "set unknown key1"); + ok(conf_io_set("include", NULL, NULL, NULL) == + KNOT_YP_ENODATA, "set non-group without data"); + ok(conf_io_set("server", "background-workers", NULL, "x") == + KNOT_EINVAL, "set invalid data"); + + // ERR callback + ok(conf_io_set("include", NULL, NULL, "invalid") == + KNOT_EFILE, "set invalid callback value"); + + // Single group, no item, no value. + ok(conf_io_set("server", NULL, NULL, NULL) == + KNOT_ENOTSUP, "set group no value"); + // Single group, no item, value. + ok(conf_io_set("server", NULL, NULL, "text") == + KNOT_YP_ENOTSUP_DATA, "set group value"); + // Single group, item, no value. + ok(conf_io_set("server", "version", NULL, NULL) == + KNOT_YP_ENODATA, "set group item no value"); + + // Single group, single value. + ok(conf_io_set("server", "version", NULL, "text") == + KNOT_EOK, "set single value"); + conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_EOK, "check entry"); + ok(strcmp(conf_str(&val), "text") == 0, "check entry value"); + + // Single group, multi value. + ok(conf_io_set("server", "listen", NULL, "1.1.1.1") == + KNOT_EOK, "set multivalue 1"); + ok(conf_io_set("server", "listen", NULL, "1.1.1.2") == + KNOT_EOK, "set multivalue 2"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN); + ok(val.code == KNOT_EOK, "check entry"); + ok(conf_val_count(&val) == 2, "check entry value count"); + + // Prepare dnames. + knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1); + ok(zone1 != NULL, "create dname "ZONE1); + knot_dname_t *zone2 = knot_dname_from_str_alloc(ZONE2); + ok(zone2 != NULL, "create dname "ZONE2); + knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3); + ok(zone3 != NULL, "create dname "ZONE3); + + // Multi group no id. + ok(conf_io_set("zone", "domain", NULL, NULL) == + KNOT_YP_ENOID, "set zone empty domain"); + + // Multi group ids. + ok(conf_io_set("zone", "domain", NULL, ZONE1) == + KNOT_EOK, "set zone domain "ZONE1); + ok(conf_io_set("zone", NULL, ZONE2, NULL) == + KNOT_EOK, "set zone domain "ZONE2); + + // Multi group, single value. + ok(conf_io_set("zone", "file", ZONE1, "name") == + KNOT_EOK, "set zone file"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1); + ok(val.code == KNOT_EOK, "check entry"); + ok(strcmp(conf_str(&val), "name") == 0, "check entry value"); + + // Multi group, single value, bad id. + ok(conf_io_set("zone", "file", ZONE3, "name") == + KNOT_YP_EINVAL_ID, "set zone file"); + + // Multi group, single value, all ids. + ok(conf_io_set("zone", "comment", NULL, "abc") == + KNOT_EOK, "set zones comment"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1); + ok(val.code == KNOT_EOK, "check entry"); + ok(strcmp(conf_str(&val), "abc") == 0, "check entry value"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2); + ok(val.code == KNOT_EOK, "check entry"); + ok(strcmp(conf_str(&val), "abc") == 0, "check entry value"); + + // Prepare different comment. + ok(conf_io_set("zone", "domain", NULL, ZONE3) == + KNOT_EOK, "set zone domain "ZONE3); + ok(conf_io_set("zone", "comment", ZONE3, "xyz") == + KNOT_EOK, "set zone comment"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3); + ok(val.code == KNOT_EOK, "check entry"); + ok(strcmp(conf_str(&val), "xyz") == 0, "check entry value"); + + knot_dname_free(zone1, NULL); + knot_dname_free(zone2, NULL); + knot_dname_free(zone3, NULL); + + ok(conf_io_commit(false) == KNOT_EOK, "commit txn"); + + // Update read-only transaction. + ok(conf_refresh_txn(conf()) == KNOT_EOK, "update read-only txn"); +} + +static void test_conf_io_unset(void) +{ + // ERR no txn. + ok(conf_io_unset("server", "version", NULL, "text") == + KNOT_TXN_ENOTEXISTS, "unset without active txn"); + + ok(conf_io_begin(false) == KNOT_EOK, "begin txn"); + + // ERR. + ok(conf_io_unset("", NULL, NULL, NULL) == + KNOT_YP_EINVAL_ITEM, "unset unknown key0"); + ok(conf_io_unset("unknown", NULL, NULL, NULL) == + KNOT_YP_EINVAL_ITEM, "unset unknown key0"); + ok(conf_io_unset("server", "unknown", NULL, NULL) == + KNOT_YP_EINVAL_ITEM, "unset unknown key1"); + ok(conf_io_unset("include", NULL, NULL, "file") == + KNOT_ENOTSUP, "unset non-group item"); + ok(conf_io_unset("server", "background-workers", NULL, "x") == + KNOT_EINVAL, "unset invalid data"); + + // Single group, single value. + ok(conf_io_unset("server", "version", NULL, "") == + KNOT_ENOENT, "unset zero length text value"); + conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_EOK, "check entry"); + + ok(conf_io_unset("server", "version", NULL, "bad text") == + KNOT_ENOENT, "unset bad value"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_EOK, "check entry"); + + ok(conf_io_unset("server", "version", NULL, "text") == + KNOT_EOK, "unset explicit value"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_ENOENT, "check entry"); + + // Restart transaction. + conf_io_abort(false); + ok(conf_io_begin(false) == KNOT_EOK, "restart txn"); + + ok(conf_io_unset("server", "version", NULL, NULL) == + KNOT_EOK, "unset value"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_ENOENT, "check entry"); + + // Single group, multi value. + ok(conf_io_unset("server", "listen", NULL, "9.9.9.9") == + KNOT_ENOENT, "unset bad value"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN); + ok(val.code == KNOT_EOK, "check entry"); + + ok(conf_io_unset("server", "listen", NULL, "1.1.1.1") == + KNOT_EOK, "unset explicit value"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN); + ok(val.code == KNOT_EOK, "check entry"); + ok(conf_val_count(&val) == 1, "check entry value count"); + + ok(conf_io_unset("server", "listen", NULL, NULL) == + KNOT_EOK, "unset value"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN); + ok(val.code == KNOT_ENOENT, "check entry"); + + // Restart transaction. + conf_io_abort(false); + ok(conf_io_begin(false) == KNOT_EOK, "restart txn"); + + // Whole section items. + ok(conf_io_unset("server", NULL, NULL, NULL) == + KNOT_EOK, "unset section"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_ENOENT, "check entry"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN); + ok(val.code == KNOT_ENOENT, "check entry"); + + // Restart transaction. + conf_io_abort(false); + ok(conf_io_begin(false) == KNOT_EOK, "restart txn"); + + // Prepare dnames. + knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1); + ok(zone1 != NULL, "create dname "ZONE1); + knot_dname_t *zone2 = knot_dname_from_str_alloc(ZONE2); + ok(zone2 != NULL, "create dname "ZONE2); + knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3); + ok(zone3 != NULL, "create dname "ZONE3); + + // Multi group, single value. + ok(conf_io_unset("zone", "file", ZONE1, "name") == + KNOT_EOK, "unset zone file"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + + // Multi group, single bad value, all ids. + ok(conf_io_unset("zone", "comment", NULL, "other") == + KNOT_EOK, "unset zones comment"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1); + ok(val.code == KNOT_EOK, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2); + ok(val.code == KNOT_EOK, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3); + ok(val.code == KNOT_EOK, "check entry"); + + // Restart transaction. + conf_io_abort(false); + ok(conf_io_begin(false) == KNOT_EOK, "restart txn"); + + // Multi group, single value (not all match), all ids. + ok(conf_io_unset("zone", "comment", NULL, "abc") == + KNOT_EOK, "unset some zones comment"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3); + ok(val.code == KNOT_EOK, "check entry"); + + // Restart transaction. + conf_io_abort(false); + ok(conf_io_begin(false) == KNOT_EOK, "restart txn"); + + // Multi group, single value (all match), all ids. + ok(conf_io_unset("zone", "comment", NULL, NULL) == + KNOT_EOK, "unset all zones comment"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + + // Restart transaction. + conf_io_abort(false); + ok(conf_io_begin(false) == KNOT_EOK, "restart txn"); + + // Multi group, all items, specific id. + ok(conf_io_unset("zone", NULL, ZONE1, NULL) == + KNOT_EOK, "unset zone items"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2); + ok(val.code == KNOT_EOK, "check entry"); + + // Restart transaction. + conf_io_abort(false); + ok(conf_io_begin(false) == KNOT_EOK, "restart txn"); + + // Multi group, all items, all ids. + ok(conf_io_unset("zone", NULL, NULL, NULL) == + KNOT_EOK, "unset zone items"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + + // Restart transaction. + conf_io_abort(false); + ok(conf_io_begin(false) == KNOT_EOK, "restart txn"); + + // All groups. + ok(conf_io_unset(NULL, NULL, NULL, NULL) == + KNOT_EOK, "unset all"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION); + ok(val.code == KNOT_ENOENT, "check entry"); + val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN); + ok(val.code == KNOT_ENOENT, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2); + ok(val.code == KNOT_YP_EINVAL_ID, "check entry"); + + knot_dname_free(zone1, NULL); + knot_dname_free(zone2, NULL); + knot_dname_free(zone3, NULL); + + conf_io_abort(false); +} + +static void test_conf_io_get(void) +{ + const char *ref; + char out[OUT_LEN]; + + conf_io_t io = { + .fcn = format_item, + .misc = out + }; + + // ERR no txn. + ok(conf_io_get("server", "version", NULL, false, &io) == + KNOT_TXN_ENOTEXISTS, "get without active txn"); + + // Get current, no active txn. + *out = '\0'; + ok(conf_io_get("server", "version", NULL, true, &io) == + KNOT_EOK, "get current without active txn"); + ref = "server.version = \"text\""; + ok(strcmp(ref, out) == 0, "compare result"); + + ok(conf_io_begin(false) == KNOT_EOK, "begin txn"); + + // ERR. + ok(conf_io_get("", NULL, NULL, true, &io) == + KNOT_YP_EINVAL_ITEM, "get empty key0"); + ok(conf_io_get("unknown", NULL, NULL, true, &io) == + KNOT_YP_EINVAL_ITEM, "get unknown key0"); + ok(conf_io_get("server", "unknown", NULL, true, &io) == + KNOT_YP_EINVAL_ITEM, "get unknown key1"); + ok(conf_io_get("include", NULL, NULL, true, &io) == + KNOT_ENOTSUP, "get non-group item"); + + // Update item in the active txn. + ok(conf_io_set("server", "version", NULL, "new text") == + KNOT_EOK, "set single value"); + + // Get new, active txn. + *out = '\0'; + ok(conf_io_get("server", "version", NULL, false, &io) == + KNOT_EOK, "get with active txn"); + ref = "server.version = \"new text\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Get current, active txn. + *out = '\0'; + ok(conf_io_get("server", "version", NULL, true, &io) == + KNOT_EOK, "get with active txn"); + ref = "server.version = \"text\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Multi value. + *out = '\0'; + ok(conf_io_get("server", "listen", NULL, true, &io) == + KNOT_EOK, "get with active txn"); + ref = "server.listen = \"1.1.1.1\" \"1.1.1.2\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Single group. + *out = '\0'; + ok(conf_io_get("server", NULL, NULL, true, &io) == + KNOT_EOK, "get with active txn"); + ref = "server.version = \"text\"\n" + "server.listen = \"1.1.1.1\" \"1.1.1.2\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Prepare dnames. + knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1); + ok(zone1 != NULL, "create dname "ZONE1); + + // Multi group, all values, all ids. + *out = '\0'; + ok(conf_io_get("zone", NULL, NULL, true, &io) == + KNOT_EOK, "get with active txn"); + ref = "zone.domain = \"zone1.\"\n" + "zone[zone1.].file = \"name\"\n" + "zone[zone1.].comment = \"abc\"\n" + "zone.domain = \"zone2.\"\n" + "zone[zone2.].comment = \"abc\"\n" + "zone.domain = \"zone3.\"\n" + "zone[zone3.].comment = \"xyz\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Multi group ids. + *out = '\0'; + ok(conf_io_get("zone", "domain", NULL, true, &io) == + KNOT_EOK, "get with active txn"); + ref = "zone.domain = \"zone1.\"\n" + "zone.domain = \"zone2.\"\n" + "zone.domain = \"zone3.\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Multi group, all values, single id. + *out = '\0'; + ok(conf_io_get("zone", NULL, ZONE1, true, &io) == + KNOT_EOK, "get with active txn"); + ref = "zone.domain = \"zone1.\"\n" + "zone[zone1.].file = \"name\"\n" + "zone[zone1.].comment = \"abc\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Multi group, single value, single id. + *out = '\0'; + ok(conf_io_get("zone", "file", ZONE1, true, &io) == + KNOT_EOK, "get with active txn"); + ref = "zone[zone1.].file = \"name\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // All groups. + *out = '\0'; + ok(conf_io_get(NULL, NULL, NULL, true, &io) == + KNOT_EOK, "get with active txn"); + ref = "server.version = \"text\"\n" + "server.listen = \"1.1.1.1\" \"1.1.1.2\"\n" + "zone.domain = \"zone1.\"\n" + "zone[zone1.].file = \"name\"\n" + "zone[zone1.].comment = \"abc\"\n" + "zone.domain = \"zone2.\"\n" + "zone[zone2.].comment = \"abc\"\n" + "zone.domain = \"zone3.\"\n" + "zone[zone3.].comment = \"xyz\""; + ok(strcmp(ref, out) == 0, "compare result"); + + knot_dname_free(zone1, NULL); + + conf_io_abort(false); +} + +static void test_conf_io_diff(void) +{ + const char *ref; + char out[OUT_LEN]; + + conf_io_t io = { + .fcn = format_item, + .misc = out + }; + + // ERR no txn. + ok(conf_io_diff("server", "version", NULL, &io) == + KNOT_TXN_ENOTEXISTS, "diff without active txn"); + + ok(conf_io_begin(false) == KNOT_EOK, "begin txn"); + + // ERR. + ok(conf_io_diff("", NULL, NULL, &io) == + KNOT_YP_EINVAL_ITEM, "diff empty key0"); + ok(conf_io_diff("unknown", NULL, NULL, &io) == + KNOT_YP_EINVAL_ITEM, "diff unknown key0"); + ok(conf_io_diff("server", "unknown", NULL, &io) == + KNOT_YP_EINVAL_ITEM, "diff unknown key1"); + ok(conf_io_diff("include", NULL, NULL, &io) == + KNOT_ENOTSUP, "diff non-group item"); + + *out = '\0'; + ok(conf_io_diff(NULL, NULL, NULL, &io) == KNOT_EOK, "diff no change"); + ref = ""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Update singlevalued item. + ok(conf_io_set("server", "version", NULL, "new text") == + KNOT_EOK, "set single value"); + + *out = '\0'; + ok(conf_io_diff("server", "version", NULL, &io) == KNOT_EOK, "diff single item"); + ref = "-server.version = \"text\"\n" + "+server.version = \"new text\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Update multivalued item. + ok(conf_io_unset("server", "listen", NULL, "1.1.1.1") == + KNOT_EOK, "unset multivalue"); + ok(conf_io_set("server", "listen", NULL, "1.1.1.3") == + KNOT_EOK, "set multivalue"); + + *out = '\0'; + ok(conf_io_diff("server", "listen", NULL, &io) == KNOT_EOK, "diff multi item"); + ref = "-server.listen = \"1.1.1.1\" \"1.1.1.2\"\n" + "+server.listen = \"1.1.1.2\" \"1.1.1.3\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Unset single item. + ok(conf_io_unset("zone", "comment", ZONE3, NULL) == + KNOT_EOK, "unset multivalue"); + + *out = '\0'; + ok(conf_io_diff("zone", NULL, ZONE3, &io) == KNOT_EOK, "diff section"); + ref = "-zone[zone3.].comment = \"xyz\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // Unset id. + ok(conf_io_unset("zone", NULL, ZONE1, NULL) == + KNOT_EOK, "unset id"); + ok(conf_io_unset("zone", NULL, ZONE2, NULL) == + KNOT_EOK, "unset id"); + + *out = '\0'; + ok(conf_io_diff("zone", NULL, ZONE2, &io) == KNOT_EOK, "diff id section"); + ref = "-zone.domain = \"zone2.\"\n" + "-zone[zone2.].comment = \"abc\""; + ok(strcmp(ref, out) == 0, "compare result"); + + *out = '\0'; + ok(conf_io_diff("zone", "domain", NULL, &io) == KNOT_EOK, "diff id"); + ref = "-zone.domain = \"zone1.\"\n" + "-zone.domain = \"zone2.\""; + ok(strcmp(ref, out) == 0, "compare result"); + + *out = '\0'; + ok(conf_io_diff(NULL, NULL, NULL, &io) == KNOT_EOK, "diff whole change"); + ref = "-server.version = \"text\"\n" + "+server.version = \"new text\"\n" + "-server.listen = \"1.1.1.1\" \"1.1.1.2\"\n" + "+server.listen = \"1.1.1.2\" \"1.1.1.3\"\n" + "-zone.domain = \"zone1.\"\n" + "-zone[zone1.].file = \"name\"\n" + "-zone[zone1.].comment = \"abc\"\n" + "-zone.domain = \"zone2.\"\n" + "-zone[zone2.].comment = \"abc\"\n" + "-zone[zone3.].comment = \"xyz\""; + ok(strcmp(ref, out) == 0, "compare result"); + + conf_io_abort(false); +} + +static void test_conf_io_list(void) +{ + const char *ref; + char out[OUT_LEN]; + + conf_io_t io = { + .fcn = format_item, + .misc = out + }; + + // ERR. + ok(conf_io_list("", NULL, NULL, false, true, &io) == + KNOT_YP_EINVAL_ITEM, "list empty key0"); + ok(conf_io_list("unknown", NULL, NULL, false, true, &io) == + KNOT_YP_EINVAL_ITEM, "list unknown key0"); + ok(conf_io_list("include", NULL, NULL, false, true, &io) == + KNOT_EOK, "list non-group item"); + ok(conf_io_list("template", NULL, NULL, false, false, &io) == + KNOT_TXN_ENOTEXISTS, "no active txn"); + + // Desc schema. + *out = '\0'; + ok(conf_io_list(NULL, NULL, NULL, true, true, &io) == + KNOT_EOK, "list schema"); + ref = "server\n" + "xdp\n" + "control\n" + "remote\n" + "template\n" + "zone\n" + "include"; + ok(strcmp(ref, out) == 0, "compare result"); + + // Desc group. + *out = '\0'; + ok(conf_io_list("server", NULL, NULL, true, true, &io) == + KNOT_EOK, "list group"); + ref = "server.version\n" + "server.listen\n" + "server.tcp-idle-timeout\n" + "server.tcp-io-timeout\n" + "server.tcp-remote-io-timeout\n" + "server.tcp-max-clients\n" + "server.tcp-reuseport\n" + "server.tcp-fastopen\n" + "server.quic-max-clients\n" + "server.quic-idle-close-timeout\n" + "server.quic-outbuf-max-size\n" + "server.socket-affinity\n" + "server.udp-workers\n" + "server.tcp-workers\n" + "server.background-workers\n" + "server.udp-max-payload\n" + "server.udp-max-payload-ipv4\n" + "server.udp-max-payload-ipv6\n" + "server.edns-client-subnet\n" + "server.answer-rotation\n" + "server.automatic-acl\n" + "server.dbus-event"; + ok(strcmp(ref, out) == 0, "compare result"); + + // List item options. + *out = '\0'; + ok(conf_io_list("zone", "zonefile-load", NULL, true, true, &io) == + KNOT_EOK, "list item options"); + ref = "zone.zonefile-load = \"opt1\"\n" + "zone.zonefile-load = \"opt2\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // List zone identifiers 1. + *out = '\0'; + ok(conf_io_list("zone", NULL, NULL, false, true, &io) == + KNOT_EOK, "list zone identifiers 1"); + ref = "zone[zone1.]\n" + "zone[zone2.]\n" + "zone[zone3.]"; + ok(strcmp(ref, out) == 0, "compare result"); + + // List zone identifiers 2. + *out = '\0'; + ok(conf_io_list("zone", "domain", NULL, false, true, &io) == + KNOT_EOK, "list zone identifiers 2"); + ref = "zone = \"zone1.\"\n" + "zone = \"zone2.\"\n" + "zone = \"zone3.\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // List item values. + *out = '\0'; + ok(conf_io_list("server", "listen", NULL, false, true, &io) == + KNOT_EOK, "list item values"); + ref = "server.listen = \"1.1.1.1\" \"1.1.1.2\""; + ok(strcmp(ref, out) == 0, "compare result"); + + // List multi-group item values. + *out = '\0'; + ok(conf_io_list("zone", "file", "zone1", false, true, &io) == + KNOT_EOK, "list multi-group item values"); + ref = "zone[zone1.].file = \"name\""; + ok(strcmp(ref, out) == 0, "compare result"); +} + +static const yp_item_t desc_server[] = { + { C_VERSION, YP_TSTR, YP_VNONE }, + { C_LISTEN, YP_TADDR, YP_VNONE, YP_FMULTI }, + // Required config cache items - assert fix. + { C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VNONE }, + { C_TCP_IO_TIMEOUT, YP_TINT, YP_VNONE }, + { C_TCP_RMT_IO_TIMEOUT, YP_TINT, YP_VNONE }, + { C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE }, + { C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE }, + { C_TCP_FASTOPEN, YP_TBOOL, YP_VNONE }, + { C_QUIC_MAX_CLIENTS, YP_TINT, YP_VNONE }, + { C_QUIC_IDLE_CLOSE, YP_TINT, YP_VNONE }, + { C_QUIC_OUTBUF_MAX_SIZE, YP_TINT, YP_VNONE }, + { C_SOCKET_AFFINITY, YP_TBOOL, YP_VNONE }, + { C_UDP_WORKERS, YP_TINT, YP_VNONE }, + { C_TCP_WORKERS, YP_TINT, YP_VNONE }, + { C_BG_WORKERS, YP_TINT, YP_VNONE }, + { C_UDP_MAX_PAYLOAD, YP_TINT, YP_VNONE }, + { C_UDP_MAX_PAYLOAD_IPV4, YP_TINT, YP_VNONE }, + { C_UDP_MAX_PAYLOAD_IPV6, YP_TINT, YP_VNONE }, + { C_ECS, YP_TBOOL, YP_VNONE }, + { C_ANS_ROTATION, YP_TBOOL, YP_VNONE }, + { C_AUTO_ACL, YP_TBOOL, YP_VNONE }, + { C_DBUS_EVENT, YP_TOPT, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_xdp[] = { + { C_UDP, YP_TBOOL, YP_VNONE }, + { C_TCP, YP_TBOOL, YP_VNONE }, + { C_QUIC, YP_TBOOL, YP_VNONE }, + { C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE }, + { C_TCP_INBUF_MAX_SIZE, YP_TINT, YP_VNONE }, + { C_TCP_OUTBUF_MAX_SIZE,YP_TINT, YP_VNONE }, + { C_TCP_IDLE_CLOSE, YP_TINT, YP_VNONE }, + { C_TCP_IDLE_RESET, YP_TINT, YP_VNONE }, + { C_TCP_RESEND, YP_TINT, YP_VNONE }, + { C_ROUTE_CHECK, YP_TBOOL, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_control[] = { + { C_TIMEOUT, YP_TINT, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_remote[] = { + { C_ID, YP_TSTR, YP_VNONE }, + { C_ADDR, YP_TADDR, YP_VNONE, YP_FMULTI }, + { NULL } +}; + +static const knot_lookup_t opts[] = { + { 1, "opt1" }, + { 2, "opt2" }, + { 0, NULL } +}; + +#define ZONE_ITEMS \ + { C_FILE, YP_TSTR, YP_VNONE }, \ + { C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \ + { C_ZONEFILE_LOAD, YP_TOPT, YP_VOPT = { opts, 0 } }, \ + { C_JOURNAL_CONTENT, YP_TOPT, YP_VOPT = { opts, 0 } }, \ + { C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE }, \ + { C_DNSSEC_VALIDATION, YP_TBOOL, YP_VNONE }, \ + { C_CATALOG_ROLE, YP_TOPT, YP_VOPT = { opts, 0 } }, \ + { C_CATALOG_TPL, YP_TREF, YP_VREF = { C_RMT } }, \ + { C_COMMENT, YP_TSTR, YP_VNONE }, + +static const yp_item_t desc_template[] = { + { C_ID, YP_TSTR, YP_VNONE }, + ZONE_ITEMS + { NULL } +}; + +static const yp_item_t desc_zone[] = { + { C_DOMAIN, YP_TDNAME, YP_VNONE }, + ZONE_ITEMS + { NULL } +}; + +const yp_item_t test_schema[] = { + { C_SRV, YP_TGRP, YP_VGRP = { desc_server } }, + { C_XDP, YP_TGRP, YP_VGRP = { desc_xdp } }, + { C_CTL, YP_TGRP, YP_VGRP = { desc_control } }, + { C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } }, + { C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } }, + { C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI, { check_zone } }, + { C_INCL, YP_TSTR, YP_VNONE, YP_FNONE, { include_file } }, + { NULL } +}; + +int main(int argc, char *argv[]) +{ + plan_lazy(); + + ok(test_conf("", test_schema) == KNOT_EOK, "Prepare configuration"); + + diag("conf_io_begin"); + test_conf_io_begin(); + + diag("conf_io_abort"); + test_conf_io_abort(); + + diag("conf_io_commit"); + test_conf_io_commit(); + + diag("conf_io_check"); + test_conf_io_check(); + + diag("conf_io_set"); + test_conf_io_set(); + + diag("conf_io_unset"); + test_conf_io_unset(); + + diag("conf_io_get"); + test_conf_io_get(); + + diag("conf_io_diff"); + test_conf_io_diff(); + + diag("conf_io_list"); + test_conf_io_list(); + + conf_free(conf()); + + return 0; +} -- cgit v1.2.3