150 lines
4.0 KiB
C++
150 lines
4.0 KiB
C++
#include <iostream>
|
|
#include <cstdlib>
|
|
|
|
#include <sstream>
|
|
#include <memory>
|
|
#include <set>
|
|
|
|
#include <hoytech/error.h>
|
|
#include <hoytech/hex.h>
|
|
|
|
#include "negentropy.h"
|
|
#include "negentropy/storage/BTreeLMDB.h"
|
|
#include "negentropy/storage/BTreeMem.h"
|
|
#include "negentropy/storage/btree/debug.h"
|
|
|
|
|
|
|
|
|
|
struct Verifier {
|
|
bool isLMDB;
|
|
|
|
std::set<uint64_t> addedTimestamps;
|
|
|
|
Verifier(bool isLMDB) : isLMDB(isLMDB) {}
|
|
|
|
void insert(negentropy::storage::btree::BTreeCore &btree, uint64_t timestamp){
|
|
negentropy::Item item(timestamp, std::string(32, (unsigned char)(timestamp % 256)));
|
|
btree.insertItem(item);
|
|
addedTimestamps.insert(timestamp);
|
|
doVerify(btree);
|
|
}
|
|
|
|
void erase(negentropy::storage::btree::BTreeCore &btree, uint64_t timestamp){
|
|
negentropy::Item item(timestamp, std::string(32, (unsigned char)(timestamp % 256)));
|
|
btree.eraseItem(item);
|
|
addedTimestamps.erase(timestamp);
|
|
doVerify(btree);
|
|
}
|
|
|
|
void doVerify(negentropy::storage::btree::BTreeCore &btree) {
|
|
try {
|
|
negentropy::storage::btree::verify(btree, isLMDB);
|
|
} catch (...) {
|
|
std::cout << "TREE FAILED INVARIANTS:" << std::endl;
|
|
negentropy::storage::btree::dump(btree);
|
|
throw;
|
|
}
|
|
|
|
if (btree.size() != addedTimestamps.size()) throw negentropy::err("verify size mismatch");
|
|
auto iter = addedTimestamps.begin();
|
|
|
|
btree.iterate(0, btree.size(), [&](const auto &item, size_t i) {
|
|
if (item.timestamp != *iter) throw negentropy::err("verify element mismatch");
|
|
iter = std::next(iter);
|
|
return true;
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void doFuzz(negentropy::storage::btree::BTreeCore &btree, Verifier &v) {
|
|
if (btree.size() != 0) throw negentropy::err("expected empty tree");
|
|
|
|
|
|
// Verify return values
|
|
|
|
if (!btree.insert(100, std::string(32, '\x01'))) throw negentropy::err("didn't insert element?");
|
|
if (btree.insert(100, std::string(32, '\x01'))) throw negentropy::err("double inserted element?");
|
|
if (!btree.erase(100, std::string(32, '\x01'))) throw negentropy::err("didn't erase element?");
|
|
if (btree.erase(100, std::string(32, '\x01'))) throw negentropy::err("erased non-existing element?");
|
|
|
|
|
|
// Fuzz test: Insertion phase
|
|
|
|
while (btree.size() < 5000) {
|
|
if (rand() % 3 <= 1) {
|
|
int timestamp;
|
|
|
|
do {
|
|
timestamp = rand();
|
|
} while (v.addedTimestamps.contains(timestamp));
|
|
|
|
std::cout << "INSERT " << timestamp << " size = " << btree.size() << std::endl;
|
|
v.insert(btree, timestamp);
|
|
} else if (v.addedTimestamps.size()) {
|
|
auto it = v.addedTimestamps.begin();
|
|
std::advance(it, rand() % v.addedTimestamps.size());
|
|
|
|
std::cout << "DEL " << (*it) << std::endl;
|
|
v.erase(btree, *it);
|
|
}
|
|
}
|
|
|
|
// Fuzz test: Removal phase
|
|
|
|
std::cout << "REMOVING ALL" << std::endl;
|
|
|
|
while (btree.size()) {
|
|
auto it = v.addedTimestamps.begin();
|
|
std::advance(it, rand() % v.addedTimestamps.size());
|
|
auto timestamp = *it;
|
|
|
|
std::cout << "DEL " << timestamp << " size = " << btree.size() << std::endl;
|
|
v.erase(btree, *it);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
std::cout << "SIZEOF NODE: " << sizeof(negentropy::storage::Node) << std::endl;
|
|
|
|
|
|
srand(0);
|
|
|
|
|
|
if (::getenv("NE_FUZZ_LMDB")) {
|
|
system("mkdir -p testdb/");
|
|
system("rm -f testdb/*");
|
|
|
|
auto env = lmdb::env::create();
|
|
env.set_max_dbs(64);
|
|
env.set_mapsize(1'000'000'000ULL);
|
|
env.open("testdb/", 0);
|
|
|
|
auto txn = lmdb::txn::begin(env);
|
|
auto btreeDbi = negentropy::storage::BTreeLMDB::setupDB(txn, "test-data");
|
|
|
|
negentropy::storage::BTreeLMDB btree(txn, btreeDbi, 0);
|
|
|
|
Verifier v(true);
|
|
doFuzz(btree, v);
|
|
|
|
btree.flush();
|
|
txn.commit();
|
|
} else {
|
|
Verifier v(false);
|
|
negentropy::storage::BTreeMem btree;
|
|
doFuzz(btree, v);
|
|
}
|
|
|
|
|
|
std::cout << "OK" << std::endl;
|
|
|
|
return 0;
|
|
}
|