blob: fea6e31bbdbae3d35967b8b1edaa3db649b571a7 [file] [log] [blame]
/*
* Copyright 2015 Facebook, Inc.
*
* 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.
*/
#include <gtest/gtest.h>
#include <folly/futures/Future.h>
#include <folly/futures/InlineExecutor.h>
#include <folly/futures/ManualExecutor.h>
#include <folly/futures/QueuedImmediateExecutor.h>
#include <folly/Baton.h>
using namespace folly;
TEST(ManualExecutor, runIsStable) {
ManualExecutor x;
size_t count = 0;
auto f1 = [&]() { count++; };
auto f2 = [&]() { x.add(f1); x.add(f1); };
x.add(f2);
x.run();
}
TEST(ManualExecutor, scheduleDur) {
ManualExecutor x;
size_t count = 0;
std::chrono::milliseconds dur {10};
x.schedule([&]{ count++; }, dur);
EXPECT_EQ(count, 0);
x.run();
EXPECT_EQ(count, 0);
x.advance(dur/2);
EXPECT_EQ(count, 0);
x.advance(dur/2);
EXPECT_EQ(count, 1);
}
TEST(ManualExecutor, clockStartsAt0) {
ManualExecutor x;
EXPECT_EQ(x.now(), x.now().min());
}
TEST(ManualExecutor, scheduleAbs) {
ManualExecutor x;
size_t count = 0;
x.scheduleAt([&]{ count++; }, x.now() + std::chrono::milliseconds(10));
EXPECT_EQ(count, 0);
x.advance(std::chrono::milliseconds(10));
EXPECT_EQ(count, 1);
}
TEST(ManualExecutor, advanceTo) {
ManualExecutor x;
size_t count = 0;
x.scheduleAt([&]{ count++; }, std::chrono::steady_clock::now());
EXPECT_EQ(count, 0);
x.advanceTo(std::chrono::steady_clock::now());
EXPECT_EQ(count, 1);
}
TEST(ManualExecutor, advanceBack) {
ManualExecutor x;
size_t count = 0;
x.advance(std::chrono::microseconds(5));
x.schedule([&]{ count++; }, std::chrono::microseconds(6));
EXPECT_EQ(count, 0);
x.advanceTo(x.now() - std::chrono::microseconds(1));
EXPECT_EQ(count, 0);
}
TEST(ManualExecutor, advanceNeg) {
ManualExecutor x;
size_t count = 0;
x.advance(std::chrono::microseconds(5));
x.schedule([&]{ count++; }, std::chrono::microseconds(6));
EXPECT_EQ(count, 0);
x.advance(std::chrono::microseconds(-1));
EXPECT_EQ(count, 0);
}
TEST(ManualExecutor, waitForDoesNotDeadlock) {
ManualExecutor east, west;
folly::Baton<> baton;
auto f = makeFuture()
.via(&east)
.then([](Try<Unit>){ return makeFuture(); })
.via(&west);
std::thread t([&]{
baton.post();
west.waitFor(f);
});
baton.wait();
east.run();
t.join();
}
TEST(Executor, InlineExecutor) {
InlineExecutor x;
size_t counter = 0;
x.add([&]{
x.add([&]{
EXPECT_EQ(counter, 0);
counter++;
});
EXPECT_EQ(counter, 1);
counter++;
});
EXPECT_EQ(counter, 2);
}
TEST(Executor, QueuedImmediateExecutor) {
QueuedImmediateExecutor x;
size_t counter = 0;
x.add([&]{
x.add([&]{
EXPECT_EQ(1, counter);
counter++;
});
EXPECT_EQ(0, counter);
counter++;
});
EXPECT_EQ(2, counter);
}
TEST(Executor, Runnable) {
InlineExecutor x;
size_t counter = 0;
struct Runnable {
std::function<void()> fn;
void operator()() { fn(); }
};
Runnable f;
f.fn = [&]{ counter++; };
x.add(f);
EXPECT_EQ(counter, 1);
}
TEST(Executor, RunnablePtr) {
InlineExecutor x;
struct Runnable {
std::function<void()> fn;
void operator()() { fn(); }
};
size_t counter = 0;
auto fnp = std::make_shared<Runnable>();
fnp->fn = [&]{ counter++; };
x.addPtr(fnp);
EXPECT_EQ(counter, 1);
}
TEST(Executor, ThrowableThen) {
InlineExecutor x;
auto f = Future<Unit>().via(&x).then([](){
throw std::runtime_error("Faildog");
});
EXPECT_THROW(f.value(), std::exception);
}
class CrappyExecutor : public Executor {
public:
void add(Func f) override {
throw std::runtime_error("bad");
}
};
TEST(Executor, CrappyExecutor) {
CrappyExecutor x;
bool flag = false;
auto f = folly::via(&x).onError([&](std::runtime_error& e) {
EXPECT_STREQ("bad", e.what());
flag = true;
});
EXPECT_TRUE(flag);
}
/* Exception should break out of Executor::run() even in Future destroyed */
TEST(Executor, exceptionsOnAbandonedFutures)
{
ManualExecutor x;
Promise<unsigned> p;
{
p.getFuture().via(&x)
.then([&] (Try<unsigned>&& i) {
i.value();
})
.onErrorThrowIn(&x);
}
p.setException(std::logic_error("fubar"));
EXPECT_THROW(while (x.run()) {}, std::logic_error);
}
/* Implementation of onErrorThrowIn() shall not interfere with onError()'s behavior */
TEST(Executor, execeptionsOnAbandondedFuturesOnError)
{
ManualExecutor x;
Promise<unsigned> p;
unsigned k = 0;
{
auto f = p.getFuture().via(&x)
.then([&] (unsigned i) { EXPECT_TRUE(false); })
.onError([&] (std::logic_error& e) { ++k; });
}
p.setException(std::logic_error("fubar"));
while (x.run()) {}
EXPECT_EQ(1, k);
}