/* Copyright (C) 2018 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/confdb.c" static void check_db_content(conf_t *conf, knot_db_txn_t *txn, int count) { ok(db_check_version(conf, txn) == KNOT_EOK, "Version check"); if (count >= 0) { ok(conf->api->count(txn) == 1 + count, "Check DB entries count"); } } static void check_code( conf_t *conf, knot_db_txn_t *txn, uint8_t section_code, const yp_name_t *name, db_action_t action, int ret, uint8_t ref_code) { uint8_t code = 0; ok(db_code(conf, txn, section_code, name, action, &code) == ret, "Compare DB code return"); if (ret != KNOT_EOK) { return; } uint8_t k[64] = { section_code, 0 }; memcpy(k + 2, name + 1, name[0]); knot_db_val_t key = { .data = k, .len = 2 + name[0] }; knot_db_val_t val; ret = conf->api->find(txn, &key, &val, 0); switch (action) { case DB_GET: case DB_SET: ok(code == ref_code, "Compare DB code"); is_int(KNOT_EOK, ret, "Find DB code"); ok(val.len == 1, "Compare DB code length"); ok(((uint8_t *)val.data)[0] == code, "Compare DB code value"); break; case DB_DEL: is_int(KNOT_ENOENT, ret, "Find item code"); break; } } static void test_db_code(conf_t *conf, knot_db_txn_t *txn) { // Add codes. check_code(conf, txn, 0, C_SERVER, DB_SET, KNOT_EOK, KEY1_FIRST); check_db_content(conf, txn, 1); check_code(conf, txn, 0, C_LOG, DB_SET, KNOT_EOK, KEY1_FIRST + 1); check_db_content(conf, txn, 2); check_code(conf, txn, 2, C_IDENT, DB_SET, KNOT_EOK, KEY1_FIRST); check_db_content(conf, txn, 3); check_code(conf, txn, 0, C_ZONE, DB_SET, KNOT_EOK, KEY1_FIRST + 2); check_db_content(conf, txn, 4); check_code(conf, txn, 2, C_VERSION, DB_SET, KNOT_EOK, KEY1_FIRST + 1); check_db_content(conf, txn, 5); // Add existing code (no change). check_code(conf, txn, 0, C_SERVER, DB_SET, KNOT_EOK, KEY1_FIRST); check_db_content(conf, txn, 5); // Get codes. check_code(conf, txn, 0, C_SERVER, DB_GET, KNOT_EOK, KEY1_FIRST); check_db_content(conf, txn, 5); check_code(conf, txn, 0, C_RMT, DB_GET, KNOT_ENOENT, 0); check_db_content(conf, txn, 5); // Delete not existing code. check_code(conf, txn, 0, C_COMMENT, DB_DEL, KNOT_ENOENT, 0); check_db_content(conf, txn, 5); // Delete codes. check_code(conf, txn, 0, C_SERVER, DB_DEL, KNOT_EOK, 0); check_db_content(conf, txn, 4); check_code(conf, txn, 2, C_IDENT, DB_DEL, KNOT_EOK, 0); check_db_content(conf, txn, 3); // Reuse deleted codes. check_code(conf, txn, 0, C_ACL, DB_SET, KNOT_EOK, KEY1_FIRST); check_db_content(conf, txn, 4); check_code(conf, txn, 2, C_NSID, DB_SET, KNOT_EOK, KEY1_FIRST); check_db_content(conf, txn, 5); } static void check_set( conf_t *conf, knot_db_txn_t *txn, const yp_name_t *key0, const yp_name_t *key1, const uint8_t *id, size_t id_len, int ret, const uint8_t *data, size_t data_len, const uint8_t *exp_data, size_t exp_data_len) { ok(conf_db_set(conf, txn, key0, key1, id, id_len, data, data_len) == ret, "Check set return"); if (ret != KNOT_EOK || (key1 == NULL && id == NULL)) { return; } uint8_t section_code, item_code; section_code = 0, item_code = 0; // prevents Wuninitialized ok(db_code(conf, txn, KEY0_ROOT, key0, DB_GET, §ion_code) == KNOT_EOK, "Get DB section code"); if (key1 != NULL) { ok(db_code(conf, txn, section_code, key1, DB_GET, &item_code) == KNOT_EOK, "Get DB item code"); } else { item_code = KEY1_ID; } uint8_t k[64] = { section_code, item_code }; if (id != NULL) { memcpy(k + 2, id, id_len); } knot_db_val_t key = { .data = k, .len = 2 + id_len }; knot_db_val_t val; ok(conf->api->find(txn, &key, &val, 0) == KNOT_EOK, "Get inserted data"); ok(val.len == exp_data_len, "Compare data length"); ok(memcmp(val.data, exp_data, exp_data_len) == 0, "Compare data"); check_db_content(conf, txn, -1); } static void test_conf_db_set(conf_t *conf, knot_db_txn_t *txn) { // Set section without item - noop. check_set(conf, txn, C_INCL, NULL, NULL, 0, KNOT_EOK, NULL, 0, NULL, 0); // Set singlevalued item. check_set(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK, (uint8_t *)"\0", 1, (uint8_t *)"\0", 1); check_set(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK, (uint8_t *)"a b\0", 4, (uint8_t *)"a b\0", 4); // Set multivalued item. check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK, (uint8_t *)"\0", 1, (uint8_t *)"\x00\x01""\0", 3); check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK, (uint8_t *)"a\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0", 7); check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK, (uint8_t *)"b\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11); // Set group id. check_set(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK, NULL, 0, (uint8_t *)"", 0); // Set singlevalued item with id. check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK, (uint8_t *)"\0", 1, (uint8_t *)"\0", 1); check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK, (uint8_t *)"a b\0", 4, (uint8_t *)"a b\0", 4); // Set multivalued item with id. check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK, (uint8_t *)"\0", 1, (uint8_t *)"\x00\x01""\0", 3); check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK, (uint8_t *)"a\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0", 7); check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK, (uint8_t *)"b\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11); // ERR set invalid section. check_set(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM, NULL, 0, NULL, 0); // ERR set invalid item. check_set(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM, NULL, 0, NULL, 0); // ERR redefine section id. check_set(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_CONF_EREDEFINE, NULL, 0, NULL, 0); // ERR set singlevalued item with non-existing id. check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID, NULL, 0, NULL, 0); } static void check_get( conf_t *conf, knot_db_txn_t *txn, const yp_name_t *key0, const yp_name_t *key1, const uint8_t *id, size_t id_len, int ret, const uint8_t *exp_data, size_t exp_data_len) { conf_val_t val; ok(conf_db_get(conf, txn, key0, key1, id, id_len, &val) == ret, "Check get return"); if (ret != KNOT_EOK) { return; } ok(val.blob_len == exp_data_len, "Compare data length"); ok(val.blob != NULL && memcmp(val.blob, exp_data, exp_data_len) == 0, "Compare data"); check_db_content(conf, txn, -1); } static void test_conf_db_get(conf_t *conf, knot_db_txn_t *txn) { // Get singlevalued item. check_get(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK, (uint8_t *)"a b\0", 4); // Get multivalued item. check_get(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11); // Get group id. check_get(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK, (uint8_t *)"", 0); // Get singlevalued item with id. check_get(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK, (uint8_t *)"a b\0", 4); // Get multivalued item with id. check_get(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11); // ERR get section without item. check_get(conf, txn, C_INCL, NULL, NULL, 0, KNOT_EINVAL, NULL, 0); // ERR get invalid section. check_get(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM, NULL, 0); // ERR get invalid item. check_get(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM, NULL, 0); // ERR get singlevalued item with non-existing id. check_get(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID, NULL, 0); } static void check_unset( conf_t *conf, knot_db_txn_t *txn, const yp_name_t *key0, const yp_name_t *key1, const uint8_t *id, size_t id_len, int ret, const uint8_t *data, size_t data_len, const uint8_t *exp_data, size_t exp_data_len) { ok(conf_db_unset(conf, txn, key0, key1, id, id_len, data, data_len, false) == ret, "Check unset return"); if (ret != KNOT_EOK) { return; } uint8_t section_code, item_code; ok(db_code(conf, txn, KEY0_ROOT, key0, DB_GET, §ion_code) == KNOT_EOK, "Get DB section code"); if (key1 != NULL) { ok(db_code(conf, txn, section_code, key1, DB_GET, &item_code) == KNOT_EOK, "Get DB item code"); } else { item_code = KEY1_ID; } uint8_t k[64] = { section_code, item_code }; if (id != NULL) { memcpy(k + 2, id, id_len); } knot_db_val_t key = { .data = k, .len = 2 + id_len }; knot_db_val_t val; ret = conf->api->find(txn, &key, &val, 0); if (exp_data != NULL) { is_int(KNOT_EOK, ret, "Get deleted data"); ok(val.len == exp_data_len, "Compare data length"); ok(memcmp(val.data, exp_data, exp_data_len) == 0, "Compare data"); } else { is_int(KNOT_ENOENT, ret, "Get deleted data"); } check_db_content(conf, txn, -1); } static void check_unset_key( conf_t *conf, knot_db_txn_t *txn, const yp_name_t *key0, const yp_name_t *key1, const uint8_t *id, size_t id_len, int ret) { ok(conf_db_unset(conf, txn, key0, key1, id, id_len, NULL, 0, true) == ret, "Check unset return"); if (ret != KNOT_EOK) { return; } uint8_t section_code, item_code; ret = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, §ion_code); if (key1 == NULL && id_len == 0) { is_int(KNOT_ENOENT, ret, "Get DB section code"); } else { is_int(KNOT_EOK, ret, "Get DB section code"); ret = db_code(conf, txn, section_code, key1, DB_GET, &item_code); is_int(KNOT_ENOENT, ret, "Get DB item code"); } check_db_content(conf, txn, -1); } static void test_conf_db_unset(conf_t *conf, knot_db_txn_t *txn) { // ERR unset section without item. check_unset(conf, txn, C_INCL, NULL, NULL, 0, KNOT_ENOENT, NULL, 0, NULL, 0); // ERR unset invalid section. check_unset(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM, NULL, 0, NULL, 0); // ERR unset invalid item. check_unset(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM, NULL, 0, NULL, 0); // ERR unset singlevalued item with non-existing id. check_unset(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID, NULL, 0, NULL, 0); // ERR unset singlevalued item invalid value. check_unset(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_ENOENT, (uint8_t *)"x\0", 2, NULL, 0); // Unset singlevalued item data. check_unset(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK, (uint8_t *)"a b\0", 4, NULL, 0); // Unset item. check_unset_key(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK); // Unset multivalued item. check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK, (uint8_t *)"a", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""b\0", 7); check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK, (uint8_t *)"", 1, (uint8_t *)"\x00\x02""b\0", 4); check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK, (uint8_t *)"b", 2, NULL, 0); // Unset item. check_unset_key(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK); // Unset section. check_unset_key(conf, txn, C_SERVER, NULL, NULL, 0, KNOT_EOK); // Unset singlevalued item with id - all data at one step. check_unset(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK, NULL, 0, NULL, 0); // Unset multivalued item with id - all data at one step (non-null data!). check_unset(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK, NULL + 1, 0, NULL, 0); // Unset group id. check_unset(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK, NULL, 0, NULL, 0); } static void test_conf_db_iter(conf_t *conf, knot_db_txn_t *txn) { const size_t total = 4; char names[][10] = { "alfa", "beta", "delta", "epsilon" }; // Prepare identifiers to iterate through. for (size_t i = 0; i < total; i++) { check_set(conf, txn, C_RMT, NULL, (uint8_t *)names[i], strlen(names[i]), KNOT_EOK, NULL, 0, (uint8_t *)"", 0); } // Create section iterator. conf_iter_t iter; int ret = conf_db_iter_begin(conf, txn, C_RMT, &iter); is_int(KNOT_EOK, ret, "Create iterator"); // Iterate through the section. size_t count = 0; while (ret == KNOT_EOK) { const uint8_t *id; size_t id_len; id = NULL, id_len = 0; // prevents Wuninitialized ret = conf_db_iter_id(conf, &iter, &id, &id_len); is_int(KNOT_EOK, ret, "Get iteration id"); ok(id_len == strlen(names[count]), "Compare iteration id length"); ok(memcmp(id, names[count], id_len) == 0, "Compare iteration id"); ok(conf_db_iter_del(conf, &iter) == KNOT_EOK, "Delete iteration key"); count++; ret = conf_db_iter_next(conf, &iter); } is_int(KNOT_EOF, ret, "Finished iteration"); ok(count == total, "Check iteration count"); // Check empty section. ret = conf_db_iter_begin(conf, txn, C_RMT, &iter); is_int(KNOT_ENOENT, ret, "Create iterator"); // ERR non-iterable section. ok(conf_db_iter_begin(conf, txn, C_SERVER, &iter) == KNOT_ENOTSUP, "Create iterator"); // ERR empty section. ok(conf_db_iter_begin(conf, txn, C_ZONE, &iter) == KNOT_ENOENT, "Create iterator"); // ERR section with no code. ok(conf_db_iter_begin(conf, txn, C_LOG, &iter) == KNOT_ENOENT, "Create iterator"); } int main(int argc, char *argv[]) { plan_lazy(); ok(test_conf("", NULL) == KNOT_EOK, "Prepare configuration"); check_db_content(conf(), &conf()->read_txn, 0); knot_db_txn_t txn; ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction"); diag("db_code"); test_db_code(conf(), &txn); conf()->api->txn_abort(&txn); ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction"); diag("conf_db_set"); test_conf_db_set(conf(), &txn); diag("conf_db_get"); test_conf_db_get(conf(), &txn); diag("conf_db_unset"); test_conf_db_unset(conf(), &txn); conf()->api->txn_abort(&txn); ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction"); diag("conf_db_iter"); test_conf_db_iter(conf(), &txn); conf()->api->txn_abort(&txn); conf_free(conf()); return 0; }