(wangle) cold via
[folly.git] / folly / wangle / test / LaterTest.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/ManualExecutor.h>
21 #include <folly/wangle/InlineExecutor.h>
22 #include <folly/wangle/Later.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 LaterFixture : public testing::Test {
38   LaterFixture() :
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   ~LaterFixture() {
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   Later<void> later;
64   std::shared_ptr<ManualExecutor> westExecutor;
65   std::shared_ptr<ManualExecutor> eastExecutor;
66   std::shared_ptr<ManualWaiter> waiter;
67   InlineExecutor inlineExecutor;
68   bool done;
69   std::thread t;
70 };
71
72 TEST(Later, construct_and_launch) {
73   bool fulfilled = false;
74   auto later = Later<void>().then([&](Try<void>&& t) {
75     fulfilled = true;
76     return makeFuture<int>(1);
77   });
78
79   // has not started yet.
80   EXPECT_FALSE(fulfilled);
81
82   EXPECT_EQ(later.launch().value(), 1);
83   EXPECT_TRUE(fulfilled);
84 }
85
86 TEST(Later, then_value) {
87   auto future = Later<int>(std::move(1))
88     .then([](Try<int>&& t) {
89       return t.value() == 1;
90     })
91     .launch();
92
93   EXPECT_TRUE(future.value());
94 }
95
96 TEST(Later, then_future) {
97   auto future = Later<int>(1)
98     .then([](Try<int>&& t) {
99       return makeFuture(t.value() == 1);
100     })
101     .launch();
102   EXPECT_TRUE(future.value());
103 }
104
105 TEST_F(LaterFixture, thread_hops) {
106   auto westThreadId = std::this_thread::get_id();
107   auto future = later.via(eastExecutor.get()).then([=](Try<void>&& t) {
108     EXPECT_NE(std::this_thread::get_id(), westThreadId);
109     return makeFuture<int>(1);
110   }).via(westExecutor.get()
111   ).then([=](Try<int>&& t) {
112     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
113     return t.value();
114   }).launch();
115   while (!future.isReady()) {
116     waiter->makeProgress();
117   }
118   EXPECT_EQ(future.value(), 1);
119 }
120
121 TEST_F(LaterFixture, wrapping_preexisting_async_modules) {
122   auto westThreadId = std::this_thread::get_id();
123   std::function<void(std::function<void(int&&)>&&)> wrapper =
124     [=](std::function<void(int&&)>&& fn) {
125       addAsync(2, 2, std::move(fn));
126     };
127   auto future = Later<int>(std::move(wrapper))
128   .via(westExecutor.get())
129   .then([=](Try<int>&& t) {
130     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
131     return t.value();
132   })
133   .launch();
134   while (!future.isReady()) {
135     waiter->makeProgress();
136   }
137   EXPECT_EQ(future.value(), 4);
138 }
139
140 TEST_F(LaterFixture, chain_laters) {
141   auto westThreadId = std::this_thread::get_id();
142   auto future = later.via(eastExecutor.get()).then([=](Try<void>&& t) {
143     EXPECT_NE(std::this_thread::get_id(), westThreadId);
144     return makeFuture<int>(1);
145   }).then([=](Try<int>&& t) {
146     int val = t.value();
147     return Later<int>(std::move(val)).via(westExecutor.get())
148       .then([=](Try<int>&& t) mutable {
149         EXPECT_EQ(std::this_thread::get_id(), westThreadId);
150         return t.value();
151       });
152   }).then([=](Try<int>&& t) {
153     EXPECT_EQ(std::this_thread::get_id(), westThreadId);
154     return t.value();
155   }).launch();
156
157   while (!future.isReady()) {
158     waiter->makeProgress();
159   }
160   EXPECT_EQ(future.value(), 1);
161 }