sapling/eden/fs/testharness/StoredObject.h
Adam Simpkins 1a6ba19f67 implement a FakeBackingStore class
Summary:
Rename the existing TestBackingStore class to FakeBackingStore, and fill it out
with an implementation that allows test code to control the store.

The test code can populate the store with Trees and Blobs to return, and can
control when the Futures returned by the store are fulfilled.

Reviewed By: bolinfest

Differential Revision: D4338577

fbshipit-source-id: 79221b04d844bd6011078b799e55182de4ccdfdc
2016-12-20 16:24:17 -08:00

135 lines
3.3 KiB
C++

/*
* Copyright (c) 2016, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <folly/futures/Future.h>
#include <memory>
namespace facebook {
namespace eden {
class Blob;
class Hash;
class Tree;
template <typename T>
class StoredObject;
using StoredBlob = StoredObject<Blob>;
using StoredHash = StoredObject<Hash>;
using StoredTree = StoredObject<Tree>;
/**
* A helper class for TestBackingStore.
*
* This contains a Tree, Blob, or Hash, but allows tracking when it should
* actually be marked ready to return to callers. The getFuture() API can be
* used to get a folly::Future that will be fulfilled when the object is marked
* ready.
*
* This allows test code to test the code behavior when backing store objects
* are not immediately ready.
*/
template <typename T>
class StoredObject {
public:
explicit StoredObject(const T& t) : object_(t) {}
/**
* Get the underlying object.
*/
const T& get() const {
return object_;
}
/**
* Get a Future for this object.
*
* If the StoredObject is ready, the returned future will already have a
* value available. Otherwise the future will become ready when trigger() or
* setReady() is called on this StoredObject.
*/
folly::Future<std::unique_ptr<T>> getFuture() {
auto data = data_.wlock();
if (data->ready) {
return folly::makeFuture(std::make_unique<T>(object_));
}
data->promises.emplace_back();
return data->promises.back().getFuture();
}
/**
* Mark the object as ready.
*
* This will fulfill any pending Futures waiting on this object.
* New Futures returned by getFuture() after setReady() is called will be
* immediately ready.
*/
void setReady() {
std::vector<folly::Promise<std::unique_ptr<T>>> promises;
{
auto data = data_.wlock();
data->ready = true;
data->promises.swap(promises);
}
triggerImpl(promises);
}
/**
* Fulfill all pending Futures waiting on this object.
*
* This fulfills currently pending Futures, but subsequent calls to
* getFuture() will still return Futures that are not ready yet.
*/
void trigger() {
std::vector<folly::Promise<std::unique_ptr<T>>> promises;
{
auto data = data_.wlock();
data->promises.swap(promises);
}
triggerImpl(promises);
}
/**
* Fail all pending Futures waiting on this object.
*
* This fulfills currently pending Futures with the specified exception.
*/
template <class E>
void triggerError(const E& e) {
std::vector<folly::Promise<std::unique_ptr<T>>> promises;
{
auto data = data_.wlock();
data->promises.swap(promises);
}
for (auto& p : promises) {
p.setException(e);
}
}
private:
struct Data {
bool ready{false};
std::vector<folly::Promise<std::unique_ptr<T>>> promises;
};
void triggerImpl(std::vector<folly::Promise<std::unique_ptr<T>>>& promises) {
for (auto& p : promises) {
p.setValue(std::make_unique<T>(object_));
}
}
const T object_;
folly::Synchronized<Data> data_;
};
}
}