foundationdb/bindings/flow/tester/Tester.actor.h

243 lines
6.6 KiB
C++

/*
* Tester.actor.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// When actually compiled (NO_INTELLISENSE), include the generated version of this file. In intellisense use the source version.
#if defined(NO_INTELLISENSE) && !defined(FDB_FLOW_TESTER_TESTER_ACTOR_G_H)
#define FDB_FLOW_TESTER_TESTER_ACTOR_G_H
#include "Tester.actor.g.h"
#elif !defined(FDB_FLOW_TESTER_TESTER_ACTOR_H)
#define FDB_FLOW_TESTER_TESTER_ACTOR_H
#pragma once
#include "flow/IDispatched.h"
#include "bindings/flow/fdb_flow.h"
#include "bindings/flow/IDirectory.h"
#include "bindings/flow/Subspace.h"
#include "bindings/flow/DirectoryLayer.h"
#define LOG_ALL 0
#define LOG_INSTRUCTIONS LOG_ALL || 0
#define LOG_OPS LOG_ALL || 0
#define LOG_DIRS LOG_ALL || 0
#define LOG_ERRORS LOG_ALL || 0
struct FlowTesterData;
struct StackItem {
StackItem() : index(-1) {}
StackItem(uint32_t i, Future<Standalone<StringRef>> v) : index(i), value(v) {}
StackItem(uint32_t i, Standalone<StringRef> v) : index(i), value(v) {}
uint32_t index;
Future<Standalone<StringRef>> value;
};
struct FlowTesterStack {
uint32_t index;
std::vector<StackItem> data;
void push(Future<Standalone<StringRef>> value) {
data.push_back(StackItem(index, value));
}
void push(Standalone<StringRef> value) {
push(Future<Standalone<StringRef>>(value));
}
void push(const StackItem& item) {
data.push_back(item);
}
void pushTuple(StringRef value, bool utf8=false) {
FDB::Tuple t;
t.append(value, utf8);
data.push_back(StackItem(index, t.pack()));
}
void pushError(int errorCode) {
FDB::Tuple t;
t.append(LiteralStringRef("ERROR"));
t.append(format("%d", errorCode));
// pack above as error string into another tuple
pushTuple(t.pack().toString());
}
std::vector<StackItem> pop(uint32_t count = 1) {
std::vector<StackItem> items;
while (!data.empty() && count > 0) {
items.push_back(data.back());
data.pop_back();
count--;
}
return items;
}
Future<std::vector<FDB::Tuple>> waitAndPop(int count);
Future<FDB::Tuple> waitAndPop();
void dup() {
if (data.empty())
return;
data.push_back(data.back());
}
void clear() {
data.clear();
}
};
struct InstructionData : public ReferenceCounted<InstructionData> {
bool isDatabase;
bool isSnapshot;
StringRef instruction;
Reference<FDB::Transaction> tr;
InstructionData(bool _isDatabase, bool _isSnapshot, StringRef _instruction, Reference<FDB::Transaction> _tr)
: isDatabase(_isDatabase)
, isSnapshot(_isSnapshot)
, instruction(_instruction)
, tr(_tr) {}
};
struct FlowTesterData;
struct InstructionFunc : IDispatched<InstructionFunc, std::string, std::function<Future<Void>(Reference<FlowTesterData> data, Reference<InstructionData> instruction)>> {
static Future<Void> call(std::string op, Reference<FlowTesterData> data, Reference<InstructionData> instruction) {
ASSERT(data);
ASSERT(instruction);
auto it = dispatches().find(op);
if(it == dispatches().end()) {
fprintf(stderr, "Unrecognized instruction: %s\n", op.c_str());
ASSERT(false);
}
return dispatch(op)(data, instruction);
}
};
#define REGISTER_INSTRUCTION_FUNC(Op) REGISTER_COMMAND(InstructionFunc, Op, name, call)
struct DirectoryOrSubspace {
Optional<Reference<FDB::IDirectory>> directory;
Optional<FDB::Subspace*> subspace;
DirectoryOrSubspace() {}
DirectoryOrSubspace(Reference<FDB::IDirectory> directory) : directory(directory) {}
DirectoryOrSubspace(FDB::Subspace *subspace) : subspace(subspace) {}
DirectoryOrSubspace(Reference<FDB::DirectorySubspace> dirSubspace) : directory(dirSubspace), subspace(dirSubspace.getPtr()) {}
bool valid() {
return directory.present() || subspace.present();
}
std::string typeString() {
if(directory.present() && subspace.present()) {
return "DirectorySubspace";
}
else if(directory.present()) {
return "IDirectory";
}
else if(subspace.present()) {
return "Subspace";
}
else {
return "InvalidDirectory";
}
}
};
struct DirectoryTesterData {
std::vector<DirectoryOrSubspace> directoryList;
int directoryListIndex;
int directoryErrorIndex;
Reference<FDB::IDirectory> directory() {
ASSERT(directoryListIndex < directoryList.size());
ASSERT(directoryList[directoryListIndex].directory.present());
return directoryList[directoryListIndex].directory.get();
}
FDB::Subspace* subspace() {
ASSERT(directoryListIndex < directoryList.size());
ASSERT(directoryList[directoryListIndex].subspace.present());
return directoryList[directoryListIndex].subspace.get();
}
DirectoryTesterData() : directoryListIndex(0), directoryErrorIndex(0) {
directoryList.push_back(Reference<FDB::IDirectory>(new FDB::DirectoryLayer()));
}
template <class T>
void push(T item) {
directoryList.push_back(DirectoryOrSubspace(item));
if(LOG_DIRS) {
printf("Pushed %s at %lu\n", directoryList.back().typeString().c_str(), directoryList.size()-1);
fflush(stdout);
}
}
void push() { push(DirectoryOrSubspace()); }
};
struct FlowTesterData : public ReferenceCounted<FlowTesterData> {
FDB::API *api;
Reference<FDB::DatabaseContext> db;
Standalone<FDB::RangeResultRef> instructions;
Standalone<StringRef> trName;
FlowTesterStack stack;
FDB::Version lastVersion;
DirectoryTesterData directoryData;
std::vector<Future<Void>> subThreads;
Future<Void> processInstruction(Reference<InstructionData> instruction) {
return InstructionFunc::call(instruction->instruction.toString(), Reference<FlowTesterData>::addRef(this), instruction);
}
FlowTesterData(FDB::API *api) {
this->api = api;
}
};
std::string tupleToString(FDB::Tuple const& tuple);
ACTOR template <class F>
Future<decltype(fake<F>()().getValue())> executeMutation(Reference<InstructionData> instruction, F func) {
loop {
try {
state decltype(fake<F>()().getValue()) result = wait(func());
if(instruction->isDatabase) {
Void _ = wait(instruction->tr->commit());
}
return result;
}
catch(Error &e) {
if(instruction->isDatabase) {
Void _ = wait(instruction->tr->onError(e));
}
else {
throw;
}
}
}
}
#endif