5580d8bd88af1d04f020372cda821d814e88d8b6
[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) override {
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   EXPECT_TRUE(future.value());
97 }
98
99 static Future<std::string> doWorkStatic(Try<std::string>&& t) {
100   return makeFuture(t.value() + ";static");
101 }
102
103 TEST(Via, then_function) {
104   struct Worker {
105     Future<std::string> doWork(Try<std::string>&& t) {
106       return makeFuture(t.value() + ";class");
107     }
108     static Future<std::string> doWorkStatic(Try<std::string>&& t) {
109       return makeFuture(t.value() + ";class-static");
110     }
111   } w;
112
113   auto f = makeFuture(std::string("start"))
114     .then(doWorkStatic)
115     .then(Worker::doWorkStatic)
116     .then(&Worker::doWork, &w)
117     ;
118
119   EXPECT_EQ(f.value(), "start;static;class-static;class");
120 }
121
122 TEST_F(ViaFixture, thread_hops) {
123   auto westThreadId = std::this_thread::get_id();
124   auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
125     EXPECT_NE(std::this_thread::get_id(), westThreadId);
126     return makeFuture<int>(1);
127   }).via(westExecutor.get()
128   ).then([=](Try<int>&& t) {
129     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
130     return t.value();
131   });
132   EXPECT_EQ(f.getVia(waiter.get()), 1);
133 }
134
135 TEST_F(ViaFixture, chain_vias) {
136   auto westThreadId = std::this_thread::get_id();
137   auto f = via(eastExecutor.get()).then([=]() {
138     EXPECT_NE(std::this_thread::get_id(), westThreadId);
139     return 1;
140   }).then([=](int val) {
141     return makeFuture(val).via(westExecutor.get())
142       .then([=](int val) mutable {
143         EXPECT_EQ(std::this_thread::get_id(), westThreadId);
144         return val + 1;
145       });
146   }).then([=](int val) {
147     // even though ultimately the future that triggers this one executed in
148     // the west thread, this then() inherited the executor from its
149     // predecessor, ie the eastExecutor.
150     EXPECT_NE(std::this_thread::get_id(), westThreadId);
151     return val + 1;
152   }).via(westExecutor.get()).then([=](int val) {
153     // go back to west, so we can wait on it
154     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
155     return val + 1;
156   });
157
158   EXPECT_EQ(f.getVia(waiter.get()), 4);
159 }
160
161 TEST_F(ViaFixture, bareViaAssignment) {
162   auto f = via(eastExecutor.get());
163 }
164 TEST_F(ViaFixture, viaAssignment) {
165   // via()&&
166   auto f = makeFuture().via(eastExecutor.get());
167   // via()&
168   auto f2 = f.via(eastExecutor.get());
169 }
170
171 TEST(Via, chain1) {
172   EXPECT_EQ(42,
173             makeFuture()
174             .then(futures::chain<void, int>([] { return 42; }))
175             .get());
176 }
177
178 TEST(Via, chain3) {
179   int count = 0;
180   auto f = makeFuture().then(futures::chain<void, int>(
181       [&]{ count++; return 3.14159; },
182       [&](double) { count++; return std::string("hello"); },
183       [&]{ count++; return makeFuture(42); }));
184   EXPECT_EQ(42, f.get());
185   EXPECT_EQ(3, count);
186 }