5cb66827879122038f8b007a75c5eb35de37fe2d
[folly.git] / folly / futures / test / ViaTest.cpp
1 /*
2  * Copyright 2014 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 <gtest/gtest.h>
18 #include <thread>
19
20 #include <folly/futures/Future.h>
21 #include <folly/futures/InlineExecutor.h>
22 #include <folly/futures/ManualExecutor.h>
23 #include <folly/futures/DrivableExecutor.h>
24
25 using namespace folly;
26
27 struct ManualWaiter : public DrivableExecutor {
28   explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex) : ex(ex) {}
29
30   void add(Func f) {
31     ex->add(f);
32   }
33
34   void drive() override {
35     ex->wait();
36     ex->run();
37   }
38
39   std::shared_ptr<ManualExecutor> ex;
40 };
41
42 struct ViaFixture : public testing::Test {
43   ViaFixture() :
44     westExecutor(new ManualExecutor),
45     eastExecutor(new ManualExecutor),
46     waiter(new ManualWaiter(westExecutor)),
47     done(false)
48   {
49     t = std::thread([=] {
50       ManualWaiter eastWaiter(eastExecutor);
51       while (!done)
52         eastWaiter.drive();
53       });
54   }
55
56   ~ViaFixture() {
57     done = true;
58     eastExecutor->add([=]() { });
59     t.join();
60   }
61
62   void addAsync(int a, int b, std::function<void(int&&)>&& cob) {
63     eastExecutor->add([=]() {
64       cob(a + b);
65     });
66   }
67
68   std::shared_ptr<ManualExecutor> westExecutor;
69   std::shared_ptr<ManualExecutor> eastExecutor;
70   std::shared_ptr<ManualWaiter> waiter;
71   InlineExecutor inlineExecutor;
72   bool done;
73   std::thread t;
74 };
75
76 TEST(Via, exception_on_launch) {
77   auto future = makeFuture<int>(std::runtime_error("E"));
78   EXPECT_THROW(future.value(), std::runtime_error);
79 }
80
81 TEST(Via, then_value) {
82   auto future = makeFuture(std::move(1))
83     .then([](Try<int>&& t) {
84       return t.value() == 1;
85     })
86     ;
87
88   EXPECT_TRUE(future.value());
89 }
90
91 TEST(Via, then_future) {
92   auto future = makeFuture(1)
93     .then([](Try<int>&& t) {
94       return makeFuture(t.value() == 1);
95     })
96     ;
97   EXPECT_TRUE(future.value());
98 }
99
100 static Future<std::string> doWorkStatic(Try<std::string>&& t) {
101   return makeFuture(t.value() + ";static");
102 }
103
104 TEST(Via, then_function) {
105   struct Worker {
106     Future<std::string> doWork(Try<std::string>&& t) {
107       return makeFuture(t.value() + ";class");
108     }
109     static Future<std::string> doWorkStatic(Try<std::string>&& t) {
110       return makeFuture(t.value() + ";class-static");
111     }
112   } w;
113
114   auto f = makeFuture(std::string("start"))
115     .then(doWorkStatic)
116     .then(Worker::doWorkStatic)
117     .then(&w, &Worker::doWork)
118     ;
119
120   EXPECT_EQ(f.value(), "start;static;class-static;class");
121 }
122
123 TEST_F(ViaFixture, deactivateChain) {
124   bool flag = false;
125   auto f = makeFuture().deactivate();
126   EXPECT_FALSE(f.isActive());
127   auto f2 = f.then([&](Try<void>){ flag = true; });
128   EXPECT_FALSE(flag);
129 }
130
131 TEST_F(ViaFixture, deactivateActivateChain) {
132   bool flag = false;
133   // you can do this all day long with temporaries.
134   auto f1 = makeFuture().deactivate().activate().deactivate();
135   // Chaining on activate/deactivate requires an rvalue, so you have to move
136   // one of these two ways (if you're not using a temporary).
137   auto f2 = std::move(f1).activate();
138   f2.deactivate();
139   auto f3 = std::move(f2.activate());
140   f3.then([&](Try<void>){ flag = true; });
141   EXPECT_TRUE(flag);
142 }
143
144 TEST_F(ViaFixture, thread_hops) {
145   auto westThreadId = std::this_thread::get_id();
146   auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
147     EXPECT_NE(std::this_thread::get_id(), westThreadId);
148     return makeFuture<int>(1);
149   }).via(westExecutor.get()
150   ).then([=](Try<int>&& t) {
151     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
152     return t.value();
153   });
154   EXPECT_EQ(f.getVia(waiter.get()), 1);
155 }
156
157 TEST_F(ViaFixture, chain_vias) {
158   auto westThreadId = std::this_thread::get_id();
159   auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
160     EXPECT_NE(std::this_thread::get_id(), westThreadId);
161     return makeFuture<int>(1);
162   }).then([=](Try<int>&& t) {
163     int val = t.value();
164     return makeFuture(std::move(val)).via(westExecutor.get())
165       .then([=](Try<int>&& t) mutable {
166         EXPECT_EQ(std::this_thread::get_id(), westThreadId);
167         return t.value();
168       });
169   }).then([=](Try<int>&& t) {
170     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
171     return t.value();
172   });
173
174   EXPECT_EQ(f.getVia(waiter.get()), 1);
175 }
176
177 TEST_F(ViaFixture, bareViaAssignment) {
178   auto f = via(eastExecutor.get());
179 }
180 TEST_F(ViaFixture, viaAssignment) {
181   // via()&&
182   auto f = makeFuture().via(eastExecutor.get());
183   // via()&
184   auto f2 = f.via(eastExecutor.get());
185 }
186
187 TEST(Via, chain1) {
188   EXPECT_EQ(42,
189             makeFuture()
190             .then(futures::chain<void, int>([] { return 42; }))
191             .get());
192 }
193
194 TEST(Via, chain3) {
195   int count = 0;
196   auto f = makeFuture().then(futures::chain<void, int>(
197       [&]{ count++; return 3.14159; },
198       [&](double) { count++; return std::string("hello"); },
199       [&]{ count++; return makeFuture(42); }));
200   EXPECT_EQ(42, f.get());
201   EXPECT_EQ(3, count);
202 }