1/*
2 * Copyright 2015-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <memory>
18#include <mutex>
19#include <queue>
20
21#include <folly/futures/Future.h>
22#include <folly/futures/Promise.h>
23#include <folly/portability/GTest.h>
24
25using namespace folly;
26
27inline void popAndFulfillPromise(
28 std::queue<std::shared_ptr<Promise<Unit>>>& ps,
29 std::mutex& ps_mutex) {
30 ps_mutex.lock();
31 auto p = ps.front();
32 ps.pop();
33 ps_mutex.unlock();
34 p->setValue();
35}
36
37inline std::function<Future<Unit>(void)> makeThunk(
38 std::queue<std::shared_ptr<Promise<Unit>>>& ps,
39 int& interrupt,
40 std::mutex& ps_mutex) {
41 return [&]() mutable {
42 auto p = std::make_shared<Promise<Unit>>();
43 p->setInterruptHandler(
44 [&](exception_wrapper const& /* e */) { ++interrupt; });
45 ps_mutex.lock();
46 ps.push(p);
47 ps_mutex.unlock();
48
49 return p->getFuture();
50 };
51}
52
53inline std::function<bool(void)> makePred(int& i) {
54 return [&]() {
55 bool res = i < 3;
56 ++i;
57 return res;
58 };
59}
60
61TEST(WhileDo, success) {
62 std::queue<std::shared_ptr<Promise<Unit>>> ps;
63 std::mutex ps_mutex;
64 int i = 0;
65 int interrupt = 0;
66 bool complete = false;
67 bool failure = false;
68
69 auto pred = makePred(i);
70 auto thunk = makeThunk(ps, interrupt, ps_mutex);
71 auto f = folly::whileDo(pred, thunk)
72 .thenValue([&](auto&&) mutable { complete = true; })
73 .onError([&](FutureException& /* e */) { failure = true; });
74
75 popAndFulfillPromise(ps, ps_mutex);
76 EXPECT_FALSE(complete);
77 EXPECT_FALSE(failure);
78
79 popAndFulfillPromise(ps, ps_mutex);
80 EXPECT_FALSE(complete);
81 EXPECT_FALSE(failure);
82
83 popAndFulfillPromise(ps, ps_mutex);
84 EXPECT_TRUE(f.isReady());
85 EXPECT_TRUE(complete);
86 EXPECT_FALSE(failure);
87}
88
89TEST(WhileDo, failure) {
90 std::queue<std::shared_ptr<Promise<Unit>>> ps;
91 std::mutex ps_mutex;
92 int i = 0;
93 int interrupt = 0;
94 bool complete = false;
95 bool failure = false;
96
97 auto pred = makePred(i);
98 auto thunk = makeThunk(ps, interrupt, ps_mutex);
99 auto f = folly::whileDo(pred, thunk)
100 .thenValue([&](auto&&) mutable { complete = true; })
101 .onError([&](FutureException& /* e */) { failure = true; });
102
103 popAndFulfillPromise(ps, ps_mutex);
104 EXPECT_FALSE(complete);
105 EXPECT_FALSE(failure);
106
107 ps_mutex.lock();
108 auto p2 = ps.front();
109 ps.pop();
110 ps_mutex.unlock();
111 FutureException eggs("eggs");
112 p2->setException(eggs);
113
114 EXPECT_TRUE(f.isReady());
115 EXPECT_FALSE(complete);
116 EXPECT_TRUE(failure);
117}
118
119TEST(WhileDo, interrupt) {
120 std::queue<std::shared_ptr<Promise<Unit>>> ps;
121 std::mutex ps_mutex;
122 int interrupt = 0;
123 bool complete = false;
124 bool failure = false;
125
126 int i = 0;
127 auto pred = makePred(i);
128 auto thunk = makeThunk(ps, interrupt, ps_mutex);
129 auto f = folly::whileDo(pred, thunk)
130 .thenValue([&](auto&&) mutable { complete = true; })
131 .onError([&](FutureException& /* e */) { failure = true; });
132
133 EXPECT_EQ(0, interrupt);
134
135 FutureException eggs("eggs");
136 f.raise(eggs);
137
138 for (int j = 1; j <= 3; ++j) {
139 EXPECT_EQ(1, interrupt);
140 popAndFulfillPromise(ps, ps_mutex);
141 }
142}
143