Files

174 lines
7.6 KiB
C++

// Executes the real CFT with the existing stdio-admission model.
#include "FakeFilesystem.h"
#define FILESYSTEM FakeFilesystem
#include "transaction_under_test.h"
#include <cstdio>
using Writer = mesh::ContactFileTransaction;
using Progress = Writer::CommitProgress;
using Bytes = std::vector<uint8_t>;
Bytes image(size_t bytes) {
Bytes result(bytes);
for (size_t i = 0; i < bytes; ++i) result[i] = uint8_t(i * 23 + i / 7);
return result;
}
bool presence(FakeFilesystem* fs, const char* path, bool& found) {
++fs->metadata_probes;
if (fs->metadata_error) return false;
found = fs->exists(path);
return true;
}
void begin(Writer& writer, bool background) {
for (unsigned i = 0; !writer && i < 8; ++i)
assert(writer.serviceBegin(background) != Writer::BeginProgress::Failed);
assert(writer);
}
void records(Writer& writer, const Bytes& bytes, size_t cap) {
assert(bytes.size() % 152 == 0);
for (size_t offset = 0; offset < bytes.size(); offset += 152) {
const unsigned before = SPIFFS.backend_writes;
assert(writer.write(bytes.data() + offset, 152) == 152);
assert(SPIFFS.backend_writes - before <= 1);
assert(SPIFFS.largest_backend_write <= cap);
}
}
Progress finish(Writer& writer, unsigned budget = 8) {
Progress progress = Progress::Pending;
for (unsigned i = 0; progress == Progress::Pending && i < 2000; ++i)
progress = writer.serviceCommit(true, budget);
assert(progress != Progress::Pending);
return progress;
}
void measure(unsigned count, bool background) {
SPIFFS = FakeFilesystem(); SPIFFS.emulate_stdio = true;
const Bytes bytes = image(count * 152);
Writer writer(&SPIFFS, "/contacts3", presence, true);
begin(writer, background);
const unsigned configurations = SPIFFS.buffer_config_calls;
assert(writer.serviceBegin(!background) == Writer::BeginProgress::Ready);
assert(SPIFFS.buffer_config_calls == configurations); // selection latched
const size_t cap = background ? 502 : 251;
records(writer, bytes, cap);
assert(SPIFFS.writes == count);
assert(SPIFFS.backend_writes == bytes.size() / cap);
assert(finish(writer) == Progress::Succeeded);
assert(SPIFFS.files.at("/contacts3") == bytes);
assert(SPIFFS.backend_writes == (bytes.size() + cap - 1) / cap);
assert(SPIFFS.largest_backend_write <= cap);
assert(SPIFFS.missing_remove_logs == 0);
printf("{\"records\":%u,\"background\":%s,\"bytes\":%zu,\"logical_writes\":%u,"
"\"backend_writes\":%u,\"largest_backend_write\":%zu}\n",
count, background ? "true" : "false", bytes.size(), SPIFFS.writes,
SPIFFS.backend_writes, SPIFFS.largest_backend_write);
}
void synchronousDefault() {
SPIFFS = FakeFilesystem(); SPIFFS.emulate_stdio = true;
const Bytes bytes = image(347 * 152);
Writer writer(&SPIFFS, "/contacts3", presence);
assert(writer); records(writer, bytes, 251);
assert(writer.commit());
assert(SPIFFS.backend_writes == 211 && SPIFFS.largest_backend_write == 251);
assert(SPIFFS.files.at("/contacts3") == bytes);
}
void configureTime(bool earlier, bool configure) {
SPIFFS = FakeFilesystem(); SPIFFS.emulate_stdio = true;
Writer writer(&SPIFFS, "/contacts3", presence, true);
// Reach the open stream without configuring it, regardless of whether
// background preparation also retires an earlier backup.
unsigned passes = 0;
while (SPIFFS.opens == 0) {
assert(++passes < 16);
assert(writer.serviceBegin(earlier) == Writer::BeginProgress::Pending);
}
assert(SPIFFS.buffer_config_calls == 0 && SPIFFS.writes == 0);
assert(writer.serviceBegin(configure) == Writer::BeginProgress::Ready);
const unsigned configurations = SPIFFS.buffer_config_calls;
assert(writer.serviceBegin(!configure) == Writer::BeginProgress::Ready);
assert(SPIFFS.buffer_config_calls == configurations);
records(writer, image(4 * 152), configure ? 502 : 251);
assert(SPIFFS.largest_backend_write == (configure ? 502 : 251));
}
void cancellation() {
// gcd(152,502)=2:251 records are exactly76 full502 buffers.
for (bool background : {false,true}) for (unsigned n : {1U,2U,3U,4U,250U,251U,252U,347U}) {
SPIFFS = FakeFilesystem(); SPIFFS.emulate_stdio = true;
const Bytes old = image(152), next = image(n * 152);
SPIFFS.files["/contacts3"] = old;
const size_t cap = background ? 502 : 251;
unsigned before_close = 0;
{
Writer writer(&SPIFFS, "/contacts3", presence, true); begin(writer,background);
records(writer,next,cap);
assert(SPIFFS.backend_writes == next.size() / cap);
before_close = SPIFFS.backend_writes;
assert(SPIFFS.files.at("/contacts3") == old);
}
assert(SPIFFS.backend_writes-before_close == unsigned(next.size()%cap != 0));
assert(SPIFFS.backend_writes == (next.size()+cap-1)/cap);
assert(SPIFFS.largest_backend_write <= cap);
assert(SPIFFS.files.at("/contacts3") == old && !SPIFFS.exists("/contacts3.tmp"));
}
}
void setupFailures() {
for (bool background : {false,true}) for (unsigned fault : {1U,2U,3U}) {
SPIFFS=FakeFilesystem(); SPIFFS.emulate_stdio=true;
const Bytes old=image(152); SPIFFS.files["/contacts3"]=old;
if (fault==1) SPIFFS.fail_buffer_config=1;
if (fault==2) SPIFFS.metadata_error=true;
if (fault==3) SPIFFS.fail_create=SPIFFS.partial_create_failure=true;
{
Writer writer(&SPIFFS,"/contacts3",presence,true);
auto result=Writer::BeginProgress::Pending;
for (unsigned i=0;result==Writer::BeginProgress::Pending && i<8;++i)
result=writer.serviceBegin(background);
assert(result==Writer::BeginProgress::Failed && !writer);
assert(writer.write(old.data(),old.size())==0);
assert(writer.serviceCommit()==Progress::Failed);
assert(SPIFFS.writes==0 && SPIFFS.backend_writes==0 && SPIFFS.reads==0);
}
assert(SPIFFS.files.at("/contacts3")==old);
if(fault!=2) assert(!SPIFFS.exists("/contacts3.tmp"));
}
}
void faultsAndCrash() {
for (bool background : {false,true}) for(unsigned fault : {0U,1U,2U,3U,4U,5U}) {
SPIFFS=FakeFilesystem(); SPIFFS.emulate_stdio=true;
const Bytes old=image(152),next=image(4*152);SPIFFS.files["/contacts3"]=old;
{
Writer writer(&SPIFFS,"/contacts3",presence,true);begin(writer,background);
if(fault==1) {
SPIFFS.max_write=151;
assert(writer.write(next.data(),152)==151);
} else records(writer,next,background?502:251);
if(fault==2) SPIFFS.fail_read="/contacts3.tmp";
if(fault==3) SPIFFS.fail_rename=1;
if(fault==4) SPIFFS.fail_rename=2;
if(fault==5) {
assert(writer.serviceCommit()==Progress::Pending); // flush before corrupting temp
SPIFFS.files.at("/contacts3.tmp")[30]^=1;
}
const auto result=finish(writer);
assert(result==(fault==0?Progress::Succeeded:Progress::Failed));
}
assert(SPIFFS.files.at("/contacts3")== (fault==0?next:old));
assert(!SPIFFS.exists("/contacts3.tmp"));
const auto snapshots=SPIFFS.rename_snapshots;
for (const auto& snapshot:snapshots) {
SPIFFS=FakeFilesystem();SPIFFS.files=snapshot;
assert(Writer::recover(&SPIFFS,"/contacts3",presence));
const auto& recovered=SPIFFS.files.at("/contacts3");
assert(recovered==old || recovered==next);
}
}
}
int main(int argc,char** argv) {
const std::string mode=argc>1?argv[1]:"all";
if(mode=="measure") {
for(unsigned n : {347U,349U,350U}) for(bool background : {false,true}) measure(n,background);
} else if(mode=="default") synchronousDefault();
else if(mode=="configure") {configureTime(true,false);configureTime(false,true);}
else if(mode=="cancel") cancellation();
else if(mode=="faults") {setupFailures();faultsAndCrash();}
else assert(false && "unknown fixture mode");
}