- A checkpoint larger than one record (16 MiB) is written as CHKPT_BEGIN [size u64, CRC-32], CHKPT_DATA chunks and CHKPT_END. Replay assembles it and applies it only when complete with the size and CRC matching; an unfinished one is a torn tail (valid_bytes stops before it). Buffers over 16 MiB could not be checkpointed before: a lost record left their journal behind for good, and they were never compacted. Records are written as segments straight from the snapshot. Header version 2; version 1 journals are still read. - Checkpoint thresholds scale with the buffer size (edits of half its size; the timer only after a sixteenth; compaction at twice its size; gap retries spaced by size), and a checkpoint that would compact is written straight into the compacted journal instead of being appended and then copied. - The recovery prompt keeps the content it replayed for its preview and installs it on 'y', instead of replaying the journal a second time. - A base file over 64 MiB gets its CRC from the writer thread before it creates the journal (it had none, so touching such a file made its journal unusable for recovery). - kte_tests takes optional name substrings to run a subset of tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
52 lines
1.5 KiB
C++
52 lines
1.5 KiB
C++
#include "Test.h"
|
|
#include <iostream>
|
|
#include <chrono>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
// Usage: kte_tests [substring...] runs the tests whose names contain any of
|
|
// the substrings (all tests when none are given).
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
using namespace std::chrono;
|
|
auto ® = ktet::registry();
|
|
std::vector<std::string> filters(argv + 1, argv + argc);
|
|
auto selected = [&filters](const std::string &name) {
|
|
if (filters.empty())
|
|
return true;
|
|
for (const auto &f: filters)
|
|
if (name.find(f) != std::string::npos)
|
|
return true;
|
|
return false;
|
|
};
|
|
std::cout << "kte unit tests: " << reg.size() << " test(s)\n";
|
|
int failed = 0;
|
|
auto t0 = steady_clock::now();
|
|
for (const auto &tc: reg) {
|
|
if (!selected(tc.name))
|
|
continue;
|
|
auto ts = steady_clock::now();
|
|
try {
|
|
tc.fn();
|
|
auto te = steady_clock::now();
|
|
auto ms = duration_cast<milliseconds>(te - ts).count();
|
|
std::cout << "[ OK ] " << tc.name << " (" << ms << " ms)\n";
|
|
} catch (const ktet::AssertionFailure &e) {
|
|
++failed;
|
|
std::cerr << "[FAIL] " << tc.name << " -> " << e.msg << "\n";
|
|
} catch (const std::exception &e) {
|
|
++failed;
|
|
std::cerr << "[EXCP] " << tc.name << " -> " << e.what() << "\n";
|
|
} catch (...) {
|
|
++failed;
|
|
std::cerr << "[EXCP] " << tc.name << " -> unknown exception\n";
|
|
}
|
|
}
|
|
auto t1 = steady_clock::now();
|
|
auto total_ms = duration_cast<milliseconds>(t1 - t0).count();
|
|
std::cout << "Done in " << total_ms << " ms. Failures: " << failed << "\n";
|
|
return failed == 0 ? 0 : 1;
|
|
}
|