Add TestDriver class

This commit is contained in:
2024-01-30 11:13:21 -08:00
parent 47faa51a35
commit 3735a43553
2 changed files with 102 additions and 92 deletions

View File

@@ -17,8 +17,6 @@
#include <arm_neon.h> #include <arm_neon.h>
#endif #endif
#define DEBUG_VERBOSE 0
// ==================== BEGIN IMPLEMENTATION ==================== // ==================== BEGIN IMPLEMENTATION ====================
struct Entry { struct Entry {
@@ -45,11 +43,6 @@ struct Node {
Type type = Type::Invalid; Type type = Type::Invalid;
}; };
Node *getChild(Node *self, uint8_t index);
int getChildGeq(Node *self, int child);
Node *&getOrCreateChild(Node *&self, uint8_t index);
Node *newNode();
void eraseChild(Node *self, uint8_t index);
struct Node4 : Node { struct Node4 : Node {
// Sorted // Sorted
@@ -1015,95 +1008,15 @@ int main(void) {
#ifdef ENABLE_FUZZ #ifdef ENABLE_FUZZ
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// TODO call setOldestVersion, and check range writes/reads TestDriver<ConflictSet::Impl> driver{data, size};
Arbitrary arbitrary{{data, size}};
int64_t writeVersion = 0; do {
ConflictSet::Impl cs{writeVersion}; bool success = checkCorrectness(driver.cs.root, driver.refImpl);
ReferenceImpl refImpl{writeVersion};
while (arbitrary.hasEntropy()) {
Arena arena;
{
int numWrites = arbitrary.bounded(10);
int64_t v = ++writeVersion;
auto *writes = new (arena) ConflictSet::WriteRange[numWrites];
auto keys = set<std::string_view>(arena);
while (int(keys.size()) < numWrites) {
if (!arbitrary.hasEntropy()) {
// Tell the fuzzer it's not interesting
return -1;
}
int keyLen = arbitrary.bounded(8);
auto *begin = new (arena) uint8_t[keyLen];
arbitrary.randomBytes(begin, keyLen);
keys.insert(std::string_view((const char *)begin, keyLen));
}
auto iter = keys.begin();
for (int i = 0; i < numWrites; ++i) {
writes[i].begin.p = (const uint8_t *)iter->data();
writes[i].begin.len = iter->size();
++iter;
writes[i].end.len = 0;
writes[i].writeVersion = v;
#if DEBUG_VERBOSE && !defined(NDEBUG)
printf("Write: {%s} -> %d\n", printable(writes[i].begin).c_str(),
int(writes[i].writeVersion));
#endif
}
assert(iter == keys.end());
cs.addWrites(writes, numWrites);
refImpl.addWrites(writes, numWrites);
}
bool success = checkCorrectness(cs.root, refImpl);
if (!success) { if (!success) {
abort(); abort();
} }
{ } while (!driver.next());
int numReads = arbitrary.bounded(10);
int64_t v = writeVersion - arbitrary.bounded(10);
auto *reads = new (arena) ConflictSet::ReadRange[numReads];
auto keys = set<std::string_view>(arena);
while (int(keys.size()) < numReads) {
if (!arbitrary.hasEntropy()) {
// Tell the fuzzer it's not interesting
return -1;
}
int keyLen = arbitrary.bounded(8);
auto *begin = new (arena) uint8_t[keyLen];
arbitrary.randomBytes(begin, keyLen);
keys.insert(std::string_view((const char *)begin, keyLen));
}
auto iter = keys.begin();
for (int i = 0; i < numReads; ++i) {
reads[i].begin.p = (const uint8_t *)iter->data();
reads[i].begin.len = iter->size();
++iter;
reads[i].end.len = 0;
reads[i].readVersion = v;
#if DEBUG_VERBOSE && !defined(NDEBUG)
printf("Read: {%s} at %d\n", printable(reads[i].begin).c_str(),
int(reads[i].readVersion));
#endif
}
assert(iter == keys.end());
auto *results1 = new (arena) ConflictSet::Result[numReads];
auto *results2 = new (arena) ConflictSet::Result[numReads];
cs.check(reads, results1, numReads);
refImpl.check(reads, results2, numReads);
for (int i = 0; i < numReads; ++i) {
if (results1[i] != results2[i]) {
fprintf(stderr, "Expected %d, got %d for read of %s at version %d\n",
results2[i], results1[i], printable(reads[i].begin).c_str(),
int(reads[i].readVersion));
std::string referenceLogicalMap;
refImpl.printLogical(referenceLogicalMap);
fprintf(stderr, "Logical map:\n\n%s\n", referenceLogicalMap.c_str());
abort();
}
}
}
}
return 0; return 0;
} }
#endif #endif

View File

@@ -2,6 +2,7 @@
#include "ConflictSet.h" #include "ConflictSet.h"
#include <cassert>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@@ -12,6 +13,11 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#define DEBUG_VERBOSE 0
// This header contains code that we want to reuse outside of ConflictSet.cpp or
// want to exclude from coverage since it's only testing related.
// GCOVR_EXCL_START // GCOVR_EXCL_START
__attribute__((always_inline)) inline void *safe_malloc(size_t s) { __attribute__((always_inline)) inline void *safe_malloc(size_t s) {
@@ -442,4 +448,95 @@ inline std::string printable(const Key &key) {
return printable(std::string_view((const char *)key.p, key.len)); return printable(std::string_view((const char *)key.p, key.len));
} }
template <class ConflictSetImpl> struct TestDriver {
// TODO call setOldestVersion, and check range writes/reads
Arbitrary arbitrary;
explicit TestDriver(const uint8_t *data, size_t size)
: arbitrary({data, size}) {}
int64_t writeVersion = 0;
ConflictSetImpl cs{writeVersion};
ReferenceImpl refImpl{writeVersion};
// Call until it returns true, for "done". Check internal invariants etc
// between calls to next.
bool next() {
if (!arbitrary.hasEntropy()) {
return true;
}
Arena arena;
{
int numWrites = arbitrary.bounded(10);
int64_t v = ++writeVersion;
auto *writes = new (arena) ConflictSet::WriteRange[numWrites];
auto keys = set<std::string_view>(arena);
while (int(keys.size()) < numWrites) {
if (!arbitrary.hasEntropy()) {
return true;
}
int keyLen = arbitrary.bounded(8);
auto *begin = new (arena) uint8_t[keyLen];
arbitrary.randomBytes(begin, keyLen);
keys.insert(std::string_view((const char *)begin, keyLen));
}
auto iter = keys.begin();
for (int i = 0; i < numWrites; ++i) {
writes[i].begin.p = (const uint8_t *)iter->data();
writes[i].begin.len = iter->size();
++iter;
writes[i].end.len = 0;
writes[i].writeVersion = v;
#if DEBUG_VERBOSE && !defined(NDEBUG)
printf("Write: {%s} -> %d\n", printable(writes[i].begin).c_str(),
int(writes[i].writeVersion));
#endif
}
assert(iter == keys.end());
cs.addWrites(writes, numWrites);
refImpl.addWrites(writes, numWrites);
}
{
int numReads = arbitrary.bounded(10);
int64_t v = writeVersion - arbitrary.bounded(10);
auto *reads = new (arena) ConflictSet::ReadRange[numReads];
auto keys = set<std::string_view>(arena);
while (int(keys.size()) < numReads) {
if (!arbitrary.hasEntropy()) {
return true;
}
int keyLen = arbitrary.bounded(8);
auto *begin = new (arena) uint8_t[keyLen];
arbitrary.randomBytes(begin, keyLen);
keys.insert(std::string_view((const char *)begin, keyLen));
}
auto iter = keys.begin();
for (int i = 0; i < numReads; ++i) {
reads[i].begin.p = (const uint8_t *)iter->data();
reads[i].begin.len = iter->size();
++iter;
reads[i].end.len = 0;
reads[i].readVersion = v;
#if DEBUG_VERBOSE && !defined(NDEBUG)
printf("Read: {%s} at %d\n", printable(reads[i].begin).c_str(),
int(reads[i].readVersion));
#endif
}
assert(iter == keys.end());
auto *results1 = new (arena) ConflictSet::Result[numReads];
auto *results2 = new (arena) ConflictSet::Result[numReads];
cs.check(reads, results1, numReads);
refImpl.check(reads, results2, numReads);
for (int i = 0; i < numReads; ++i) {
if (results1[i] != results2[i]) {
fprintf(stderr, "Expected %d, got %d for read of %s at version %d\n",
results2[i], results1[i], printable(reads[i].begin).c_str(),
int(reads[i].readVersion));
abort();
}
}
}
return false;
}
};
// GCOVR_EXCL_STOP // GCOVR_EXCL_STOP