folly/Bits.h (BitIterator): avoid -Wsign-compare error
[folly.git] / folly / wangle / 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/wangle/futures/Future.h>
21 #include <folly/wangle/futures/InlineExecutor.h>
22 #include <folly/wangle/futures/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     westExecutor(new ManualExecutor),
40     eastExecutor(new ManualExecutor),
41     waiter(new ManualWaiter(westExecutor)),
42     done(false)
43   {
44     t = std::thread([=] {
45       ManualWaiter eastWaiter(eastExecutor);
46       while (!done)
47         eastWaiter.makeProgress();
48       });
49   }
50
51   ~ViaFixture() {
52     done = true;
53     eastExecutor->add([=]() { });
54     t.join();
55   }
56
57   void addAsync(int a, int b, std::function<void(int&&)>&& cob) {
58     eastExecutor->add([=]() {
59       cob(a + b);
60     });
61   }
62
63   std::shared_ptr<ManualExecutor> westExecutor;
64   std::shared_ptr<ManualExecutor> eastExecutor;
65   std::shared_ptr<ManualWaiter> waiter;
66   InlineExecutor inlineExecutor;
67   bool done;
68   std::thread t;
69 };
70
71 TEST(Via, exception_on_launch) {
72   auto future = makeFuture<int>(std::runtime_error("E"));
73   EXPECT_THROW(future.value(), std::runtime_error);
74 }
75
76 TEST(Via, then_value) {
77   auto future = makeFuture(std::move(1))
78     .then([](Try<int>&& t) {
79       return t.value() == 1;
80     })
81     ;
82
83   EXPECT_TRUE(future.value());
84 }
85
86 TEST(Via, then_future) {
87   auto future = makeFuture(1)
88     .then([](Try<int>&& t) {
89       return makeFuture(t.value() == 1);
90     })
91     ;
92   EXPECT_TRUE(future.value());
93 }
94
95 static Future<std::string> doWorkStatic(Try<std::string>&& t) {
96   return makeFuture(t.value() + ";static");
97 }
98
99 TEST(Via, then_function) {
100   struct Worker {
101     Future<std::string> doWork(Try<std::string>&& t) {
102       return makeFuture(t.value() + ";class");
103     }
104     static Future<std::string> doWorkStatic(Try<std::string>&& t) {
105       return makeFuture(t.value() + ";class-static");
106     }
107   } w;
108
109   auto f = makeFuture(std::string("start"))
110     .then(doWorkStatic)
111     .then(Worker::doWorkStatic)
112     .then(&w, &Worker::doWork)
113     ;
114
115   EXPECT_EQ(f.value(), "start;static;class-static;class");
116 }
117
118 TEST_F(ViaFixture, deactivateChain) {
119   bool flag = false;
120   auto f = makeFuture().deactivate();
121   EXPECT_FALSE(f.isActive());
122   auto f2 = f.then([&](Try<void>){ flag = true; });
123   EXPECT_FALSE(flag);
124 }
125
126 TEST_F(ViaFixture, deactivateActivateChain) {
127   bool flag = false;
128   // you can do this all day long with temporaries.
129   auto f1 = makeFuture().deactivate().activate().deactivate();
130   // Chaining on activate/deactivate requires an rvalue, so you have to move
131   // one of these two ways (if you're not using a temporary).
132   auto f2 = std::move(f1).activate();
133   f2.deactivate();
134   auto f3 = std::move(f2.activate());
135   f3.then([&](Try<void>){ flag = true; });
136   EXPECT_TRUE(flag);
137 }
138
139 TEST_F(ViaFixture, thread_hops) {
140   auto westThreadId = std::this_thread::get_id();
141   auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
142     EXPECT_NE(std::this_thread::get_id(), westThreadId);
143     return makeFuture<int>(1);
144   }).via(westExecutor.get()
145   ).then([=](Try<int>&& t) {
146     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
147     return t.value();
148   });
149   while (!f.isReady()) {
150     waiter->makeProgress();
151   }
152   EXPECT_EQ(f.value(), 1);
153 }
154
155 TEST_F(ViaFixture, chain_vias) {
156   auto westThreadId = std::this_thread::get_id();
157   auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
158     EXPECT_NE(std::this_thread::get_id(), westThreadId);
159     return makeFuture<int>(1);
160   }).then([=](Try<int>&& t) {
161     int val = t.value();
162     return makeFuture(std::move(val)).via(westExecutor.get())
163       .then([=](Try<int>&& t) mutable {
164         EXPECT_EQ(std::this_thread::get_id(), westThreadId);
165         return t.value();
166       });
167   }).then([=](Try<int>&& t) {
168     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
169     return t.value();
170   });
171
172   while (!f.isReady()) {
173     waiter->makeProgress();
174   }
175   EXPECT_EQ(f.value(), 1);
176 }
177
178 TEST_F(ViaFixture, bareViaAssignment) {
179   auto f = via(eastExecutor.get());
180 }
181 TEST_F(ViaFixture, viaAssignment) {
182   // via()&&
183   auto f = makeFuture().via(eastExecutor.get());
184   // via()&
185   auto f2 = f.via(eastExecutor.get());
186 }