9a3758c28c9e9875b0c1e06c323769088ae6cee8
[folly.git] / folly / wangle / 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/wangle/Future.h>
21 #include <folly/wangle/InlineExecutor.h>
22 #include <folly/wangle/ManualExecutor.h>
23
24 using namespace folly::wangle;
25
26 struct ManualWaiter {
27   explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex) : ex(ex) {}
28
29   void makeProgress() {
30     ex->wait();
31     ex->run();
32   }
33
34   std::shared_ptr<ManualExecutor> ex;
35 };
36
37 struct ViaFixture : public testing::Test {
38   ViaFixture() :
39     future_(makeFuture().deactivate()),
40     westExecutor(new ManualExecutor),
41     eastExecutor(new ManualExecutor),
42     waiter(new ManualWaiter(westExecutor)),
43     done(false)
44   {
45     t = std::thread([=] {
46       ManualWaiter eastWaiter(eastExecutor);
47       while (!done)
48         eastWaiter.makeProgress();
49       });
50   }
51
52   ~ViaFixture() {
53     done = true;
54     eastExecutor->add([=]() { });
55     t.join();
56   }
57
58   void addAsync(int a, int b, std::function<void(int&&)>&& cob) {
59     eastExecutor->add([=]() {
60       cob(a + b);
61     });
62   }
63
64   Future<void> future_;
65   std::shared_ptr<ManualExecutor> westExecutor;
66   std::shared_ptr<ManualExecutor> eastExecutor;
67   std::shared_ptr<ManualWaiter> waiter;
68   InlineExecutor inlineExecutor;
69   bool done;
70   std::thread t;
71 };
72
73 TEST(Via, exception_on_launch) {
74   auto future = makeFuture<int>(std::runtime_error("E"));
75   EXPECT_THROW(future.value(), std::runtime_error);
76 }
77
78 TEST(Via, then_value) {
79   auto future = makeFuture(std::move(1))
80     .then([](Try<int>&& t) {
81       return t.value() == 1;
82     })
83     ;
84
85   EXPECT_TRUE(future.value());
86 }
87
88 TEST(Via, then_future) {
89   auto future = makeFuture(1)
90     .then([](Try<int>&& t) {
91       return makeFuture(t.value() == 1);
92     })
93     ;
94   EXPECT_TRUE(future.value());
95 }
96
97 static Future<std::string> doWorkStatic(Try<std::string>&& t) {
98   return makeFuture(t.value() + ";static");
99 }
100
101 TEST(Via, then_function) {
102   struct Worker {
103     Future<std::string> doWork(Try<std::string>&& t) {
104       return makeFuture(t.value() + ";class");
105     }
106     static Future<std::string> doWorkStatic(Try<std::string>&& t) {
107       return makeFuture(t.value() + ";class-static");
108     }
109   } w;
110
111   auto f = makeFuture(std::string("start"))
112     .then(doWorkStatic)
113     .then(Worker::doWorkStatic)
114     .then(&w, &Worker::doWork)
115     ;
116
117   EXPECT_EQ(f.value(), "start;static;class-static;class");
118 }
119
120 TEST_F(ViaFixture, thread_hops) {
121   auto westThreadId = std::this_thread::get_id();
122   auto f = future_.via(eastExecutor.get()).then([=](Try<void>&& t) {
123     EXPECT_NE(std::this_thread::get_id(), westThreadId);
124     return makeFuture<int>(1);
125   }).via(westExecutor.get()
126   ).then([=](Try<int>&& t) {
127     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
128     return t.value();
129   });
130   while (!f.isReady()) {
131     waiter->makeProgress();
132   }
133   EXPECT_EQ(f.value(), 1);
134 }
135
136 TEST_F(ViaFixture, chain_vias) {
137   auto westThreadId = std::this_thread::get_id();
138   auto f = future_.via(eastExecutor.get()).then([=](Try<void>&& t) {
139     EXPECT_NE(std::this_thread::get_id(), westThreadId);
140     return makeFuture<int>(1);
141   }).then([=](Try<int>&& t) {
142     int val = t.value();
143     return makeFuture(std::move(val)).via(westExecutor.get())
144       .then([=](Try<int>&& t) mutable {
145         EXPECT_EQ(std::this_thread::get_id(), westThreadId);
146         return t.value();
147       });
148   }).then([=](Try<int>&& t) {
149     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
150     return t.value();
151   });
152
153   while (!f.isReady()) {
154     waiter->makeProgress();
155   }
156   EXPECT_EQ(f.value(), 1);
157 }
158