Make Observer stress test pass a stress run
[folly.git] / folly / experimental / observer / test / ObserverTest.cpp
1 /*
2  * Copyright 2016 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 <thread>
18
19 #include <folly/Baton.h>
20 #include <folly/experimental/observer/SimpleObservable.h>
21 #include <folly/portability/GTest.h>
22
23 using namespace folly::observer;
24
25 TEST(Observer, Observable) {
26   SimpleObservable<int> observable(42);
27   auto observer = observable.getObserver();
28
29   EXPECT_EQ(42, **observer);
30
31   folly::Baton<> baton;
32   auto waitingObserver = makeObserver([observer, &baton]() {
33     *observer;
34     baton.post();
35     return folly::Unit();
36   });
37   baton.reset();
38
39   observable.setValue(24);
40
41   EXPECT_TRUE(baton.timed_wait(std::chrono::seconds{1}));
42
43   EXPECT_EQ(24, **observer);
44 }
45
46 TEST(Observer, MakeObserver) {
47   SimpleObservable<int> observable(42);
48
49   auto observer = makeObserver([child = observable.getObserver()]() {
50     return **child + 1;
51   });
52
53   EXPECT_EQ(43, **observer);
54
55   folly::Baton<> baton;
56   auto waitingObserver = makeObserver([observer, &baton]() {
57     *observer;
58     baton.post();
59     return folly::Unit();
60   });
61   baton.reset();
62
63   observable.setValue(24);
64
65   EXPECT_TRUE(baton.timed_wait(std::chrono::seconds{1}));
66
67   EXPECT_EQ(25, **observer);
68 }
69
70 TEST(Observer, MakeObserverDiamond) {
71   SimpleObservable<int> observable(42);
72
73   auto observer1 = makeObserver([child = observable.getObserver()]() {
74     return **child + 1;
75   });
76
77   auto observer2 = makeObserver([child = observable.getObserver()]() {
78     return std::make_shared<int>(**child + 2);
79   });
80
81   auto observer = makeObserver(
82       [observer1, observer2]() { return (**observer1) * (**observer2); });
83
84   EXPECT_EQ(43 * 44, *observer.getSnapshot());
85
86   folly::Baton<> baton;
87   auto waitingObserver = makeObserver([observer, &baton]() {
88     *observer;
89     baton.post();
90     return folly::Unit();
91   });
92   baton.reset();
93
94   observable.setValue(24);
95
96   EXPECT_TRUE(baton.timed_wait(std::chrono::seconds{1}));
97
98   EXPECT_EQ(25 * 26, **observer);
99 }
100
101 TEST(Observer, CreateException) {
102   struct ExpectedException {};
103   EXPECT_THROW(
104       auto observer = makeObserver(
105           []() -> std::shared_ptr<int> { throw ExpectedException(); }),
106       ExpectedException);
107
108   EXPECT_THROW(
109       auto observer =
110           makeObserver([]() -> std::shared_ptr<int> { return nullptr; }),
111       std::logic_error);
112 }
113
114 TEST(Observer, NullValue) {
115   SimpleObservable<int> observable(41);
116   auto oddObserver = makeObserver([innerObserver = observable.getObserver()]() {
117     auto value = **innerObserver;
118
119     if (value % 2 != 0) {
120       return value * 2;
121     }
122
123     throw std::logic_error("I prefer odd numbers");
124   });
125
126   folly::Baton<> baton;
127   auto waitingObserver = makeObserver([oddObserver, &baton]() {
128     *oddObserver;
129     baton.post();
130     return folly::Unit();
131   });
132
133   baton.reset();
134   EXPECT_EQ(82, **oddObserver);
135
136   observable.setValue(2);
137
138   // Waiting observer shouldn't be updated
139   EXPECT_FALSE(baton.timed_wait(std::chrono::seconds{1}));
140   baton.reset();
141
142   EXPECT_EQ(82, **oddObserver);
143
144   observable.setValue(23);
145
146   EXPECT_TRUE(baton.timed_wait(std::chrono::seconds{1}));
147
148   EXPECT_EQ(46, **oddObserver);
149 }
150
151 TEST(Observer, Cycle) {
152   SimpleObservable<int> observable(0);
153   auto observer = observable.getObserver();
154   folly::Optional<Observer<int>> observerB;
155
156   auto observerA = makeObserver([observer, &observerB]() {
157     auto value = **observer;
158     if (value == 1) {
159       **observerB;
160     }
161     return value;
162   });
163
164   observerB = makeObserver([observerA]() { return **observerA; });
165
166   auto collectObserver = makeObserver([observer, observerA, &observerB]() {
167     auto value = **observer;
168     auto valueA = **observerA;
169     auto valueB = ***observerB;
170
171     if (value == 1) {
172       if (valueA == 0) {
173         EXPECT_EQ(0, valueB);
174       } else {
175         EXPECT_EQ(1, valueA);
176         EXPECT_EQ(0, valueB);
177       }
178     } else if (value == 2) {
179       EXPECT_EQ(value, valueA);
180       EXPECT_TRUE(valueB == 0 || valueB == 2);
181     } else {
182       EXPECT_EQ(value, valueA);
183       EXPECT_EQ(value, valueB);
184     }
185
186     return value;
187   });
188
189   folly::Baton<> baton;
190   auto waitingObserver = makeObserver([collectObserver, &baton]() {
191     *collectObserver;
192     baton.post();
193     return folly::Unit();
194   });
195
196   baton.reset();
197   EXPECT_EQ(0, **collectObserver);
198
199   for (size_t i = 1; i <= 3; ++i) {
200     observable.setValue(i);
201
202     EXPECT_TRUE(baton.timed_wait(std::chrono::seconds{1}));
203     baton.reset();
204
205     EXPECT_EQ(i, **collectObserver);
206   }
207 }
208
209 TEST(Observer, Stress) {
210   SimpleObservable<int> observable(0);
211
212   auto values = std::make_shared<folly::Synchronized<std::vector<int>>>();
213
214   auto observer = makeObserver([ child = observable.getObserver(), values ]() {
215     auto value = **child * 10;
216     values->withWLock(
217         [&](std::vector<int>& values) { values.push_back(value); });
218     return value;
219   });
220
221   EXPECT_EQ(0, **observer);
222   values->withRLock([](const std::vector<int>& values) {
223     EXPECT_EQ(1, values.size());
224     EXPECT_EQ(0, values.back());
225   });
226
227   constexpr size_t numIters = 10000;
228
229   for (size_t i = 1; i <= numIters; ++i) {
230     observable.setValue(i);
231   }
232
233   while (**observer != numIters * 10) {
234     std::this_thread::yield();
235   }
236
237   values->withRLock([numIters = numIters](const std::vector<int>& values) {
238     EXPECT_EQ(numIters * 10, values.back());
239     EXPECT_LT(values.size(), numIters / 2);
240
241     EXPECT_EQ(0, values[0]);
242     EXPECT_EQ(numIters * 10, values.back());
243
244     for (auto value : values) {
245       EXPECT_EQ(0, value % 10);
246     }
247
248     for (size_t i = 0; i < values.size() - 1; ++i) {
249       EXPECT_LE(values[i], values[i + 1]);
250     }
251   });
252 }
253
254 TEST(Observer, TLObserver) {
255   auto createTLObserver = [](int value) {
256     return folly::observer::makeTLObserver([=] { return value; });
257   };
258
259   auto k =
260       std::make_unique<folly::observer::TLObserver<int>>(createTLObserver(42));
261   EXPECT_EQ(42, ***k);
262   k = std::make_unique<folly::observer::TLObserver<int>>(createTLObserver(41));
263   EXPECT_EQ(41, ***k);
264 }