minor Timekeeper bug
[folly.git] / folly / futures / test / TimekeeperTest.cpp
1 /*
2  * Copyright 2015 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
19 #include <folly/futures/Timekeeper.h>
20
21 #include <unistd.h>
22
23 using namespace folly;
24 using std::chrono::milliseconds;
25
26 std::chrono::milliseconds const zero_ms(0);
27 std::chrono::milliseconds const one_ms(1);
28 std::chrono::milliseconds const awhile(10);
29 std::chrono::seconds const too_long(10);
30
31 std::chrono::steady_clock::time_point now() {
32   return std::chrono::steady_clock::now();
33 }
34
35 struct TimekeeperFixture : public testing::Test {
36   TimekeeperFixture() :
37     timeLord_(folly::detail::getTimekeeperSingleton())
38   {}
39
40   Timekeeper* timeLord_;
41 };
42
43 TEST_F(TimekeeperFixture, after) {
44   auto t1 = now();
45   auto f = timeLord_->after(awhile);
46   EXPECT_FALSE(f.isReady());
47   f.get();
48   auto t2 = now();
49
50   EXPECT_GE(t2 - t1, awhile);
51 }
52
53 TEST(Timekeeper, futureGet) {
54   Promise<int> p;
55   auto t = std::thread([&]{ p.setValue(42); });
56   EXPECT_EQ(42, p.getFuture().get());
57   t.join();
58 }
59
60 TEST(Timekeeper, futureGetBeforeTimeout) {
61   Promise<int> p;
62   auto t = std::thread([&]{ p.setValue(42); });
63   // Technically this is a race and if the test server is REALLY overloaded
64   // and it takes more than a second to do that thread it could be flaky. But
65   // I want a low timeout (in human terms) so if this regresses and someone
66   // runs it by hand they're not sitting there forever wondering why it's
67   // blocked, and get a useful error message instead. If it does get flaky,
68   // empirically increase the timeout to the point where it's very improbable.
69   EXPECT_EQ(42, p.getFuture().get(std::chrono::seconds(2)));
70   t.join();
71 }
72
73 TEST(Timekeeper, futureGetTimeout) {
74   Promise<int> p;
75   EXPECT_THROW(p.getFuture().get(one_ms), folly::TimedOut);
76 }
77
78 TEST(Timekeeper, futureSleep) {
79   auto t1 = now();
80   futures::sleep(one_ms).get();
81   EXPECT_GE(now() - t1, one_ms);
82 }
83
84 TEST(Timekeeper, futureDelayed) {
85   auto t1 = now();
86   auto dur = makeFuture()
87     .delayed(one_ms)
88     .then([=]{ return now() - t1; })
89     .get();
90
91   EXPECT_GE(dur, one_ms);
92 }
93
94 TEST(Timekeeper, futureWithinThrows) {
95   Promise<int> p;
96   auto f = p.getFuture()
97     .within(one_ms)
98     .onError([](TimedOut&) { return -1; });
99
100   EXPECT_EQ(-1, f.get());
101 }
102
103 TEST(Timekeeper, futureWithinAlreadyComplete) {
104   auto f = makeFuture(42)
105     .within(one_ms)
106     .onError([&](TimedOut&){ return -1; });
107
108   EXPECT_EQ(42, f.get());
109 }
110
111 TEST(Timekeeper, futureWithinFinishesInTime) {
112   Promise<int> p;
113   auto f = p.getFuture()
114     .within(std::chrono::minutes(1))
115     .onError([&](TimedOut&){ return -1; });
116   p.setValue(42);
117
118   EXPECT_EQ(42, f.get());
119 }
120
121 TEST(Timekeeper, futureWithinVoidSpecialization) {
122   makeFuture().within(one_ms);
123 }
124
125 TEST(Timekeeper, futureWithinException) {
126   Promise<Unit> p;
127   auto f = p.getFuture().within(awhile, std::runtime_error("expected"));
128   EXPECT_THROW(f.get(), std::runtime_error);
129 }
130
131 TEST(Timekeeper, onTimeout) {
132   bool flag = false;
133   makeFuture(42).delayed(one_ms)
134     .onTimeout(zero_ms, [&]{ flag = true; return -1; })
135     .get();
136   EXPECT_TRUE(flag);
137 }
138
139 TEST(Timekeeper, onTimeoutReturnsFuture) {
140   bool flag = false;
141   makeFuture(42).delayed(one_ms)
142     .onTimeout(zero_ms, [&]{ flag = true; return makeFuture(-1); })
143     .get();
144   EXPECT_TRUE(flag);
145 }
146
147 TEST(Timekeeper, onTimeoutVoid) {
148   makeFuture().delayed(one_ms)
149     .onTimeout(zero_ms, [&]{
150      });
151   makeFuture().delayed(one_ms)
152     .onTimeout(zero_ms, [&]{
153        return makeFuture<Unit>(std::runtime_error("expected"));
154      });
155   // just testing compilation here
156 }
157
158 TEST(Timekeeper, interruptDoesntCrash) {
159   auto f = futures::sleep(too_long);
160   f.cancel();
161 }
162
163 TEST(Timekeeper, chainedInterruptTest) {
164   bool test = false;
165   auto f = futures::sleep(milliseconds(100)).then([&](){
166     test = true;
167   });
168   f.cancel();
169   f.wait();
170   EXPECT_FALSE(test);
171 }
172
173 TEST(Timekeeper, executor) {
174   class ExecutorTester : public Executor {
175    public:
176     void add(Func f) override {
177       count++;
178       f();
179     }
180     std::atomic<int> count{0};
181   };
182
183   auto f = makeFuture();
184   ExecutorTester tester;
185   f.via(&tester).within(one_ms).then([&](){}).wait();
186   EXPECT_EQ(2, tester.count);
187 }
188
189 // TODO(5921764)
190 /*
191 TEST(Timekeeper, onTimeoutPropagates) {
192   bool flag = false;
193   EXPECT_THROW(
194     makeFuture(42).delayed(one_ms)
195       .onTimeout(zero_ms, [&]{ flag = true; })
196       .get(),
197     TimedOut);
198   EXPECT_TRUE(flag);
199 }
200 */
201
202 TEST_F(TimekeeperFixture, atBeforeNow) {
203   auto f = timeLord_->at(now() - too_long);
204   EXPECT_TRUE(f.isReady());
205   EXPECT_FALSE(f.hasException());
206 }
207
208 TEST_F(TimekeeperFixture, howToCastDuration) {
209   // I'm not sure whether this rounds up or down but it's irrelevant for the
210   // purpose of this example.
211   auto f = timeLord_->after(std::chrono::duration_cast<Duration>(
212       std::chrono::nanoseconds(1)));
213 }