2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <folly/experimental/observer/SimpleObservable.h>
20 #include <folly/portability/GTest.h>
21 #include <folly/synchronization/Baton.h>
23 using namespace folly::observer;
25 TEST(Observer, Observable) {
26 SimpleObservable<int> observable(42);
27 auto observer = observable.getObserver();
29 EXPECT_EQ(42, **observer);
32 auto waitingObserver = makeObserver([observer, &baton]() {
39 observable.setValue(24);
41 EXPECT_TRUE(baton.try_wait_for(std::chrono::seconds{1}));
43 EXPECT_EQ(24, **observer);
46 TEST(Observer, MakeObserver) {
47 SimpleObservable<int> observable(42);
49 auto observer = makeObserver([child = observable.getObserver()]() {
53 EXPECT_EQ(43, **observer);
56 auto waitingObserver = makeObserver([observer, &baton]() {
63 observable.setValue(24);
65 EXPECT_TRUE(baton.try_wait_for(std::chrono::seconds{1}));
67 EXPECT_EQ(25, **observer);
70 TEST(Observer, MakeObserverDiamond) {
71 SimpleObservable<int> observable(42);
73 auto observer1 = makeObserver([child = observable.getObserver()]() {
77 auto observer2 = makeObserver([child = observable.getObserver()]() {
78 return std::make_shared<int>(**child + 2);
81 auto observer = makeObserver(
82 [observer1, observer2]() { return (**observer1) * (**observer2); });
84 EXPECT_EQ(43 * 44, *observer.getSnapshot());
87 auto waitingObserver = makeObserver([observer, &baton]() {
94 observable.setValue(24);
96 EXPECT_TRUE(baton.try_wait_for(std::chrono::seconds{1}));
98 EXPECT_EQ(25 * 26, **observer);
101 TEST(Observer, CreateException) {
102 struct ExpectedException {};
104 auto observer = makeObserver(
105 []() -> std::shared_ptr<int> { throw ExpectedException(); }),
110 makeObserver([]() -> std::shared_ptr<int> { return nullptr; }),
114 TEST(Observer, NullValue) {
115 SimpleObservable<int> observable(41);
116 auto oddObserver = makeObserver([innerObserver = observable.getObserver()]() {
117 auto value = **innerObserver;
119 if (value % 2 != 0) {
123 throw std::logic_error("I prefer odd numbers");
126 folly::Baton<> baton;
127 auto waitingObserver = makeObserver([oddObserver, &baton]() {
130 return folly::Unit();
134 EXPECT_EQ(82, **oddObserver);
136 observable.setValue(2);
138 // Waiting observer shouldn't be updated
139 EXPECT_FALSE(baton.try_wait_for(std::chrono::seconds{1}));
142 EXPECT_EQ(82, **oddObserver);
144 observable.setValue(23);
146 EXPECT_TRUE(baton.try_wait_for(std::chrono::seconds{1}));
148 EXPECT_EQ(46, **oddObserver);
151 TEST(Observer, Cycle) {
152 SimpleObservable<int> observable(0);
153 auto observer = observable.getObserver();
154 folly::Optional<Observer<int>> observerB;
156 auto observerA = makeObserver([observer, &observerB]() {
157 auto value = **observer;
164 observerB = makeObserver([observerA]() { return **observerA; });
166 auto collectObserver = makeObserver([observer, observerA, &observerB]() {
167 auto value = **observer;
168 auto valueA = **observerA;
169 auto valueB = ***observerB;
173 EXPECT_EQ(0, valueB);
175 EXPECT_EQ(1, valueA);
176 EXPECT_EQ(0, valueB);
178 } else if (value == 2) {
179 EXPECT_EQ(value, valueA);
180 EXPECT_TRUE(valueB == 0 || valueB == 2);
182 EXPECT_EQ(value, valueA);
183 EXPECT_EQ(value, valueB);
189 folly::Baton<> baton;
190 auto waitingObserver = makeObserver([collectObserver, &baton]() {
193 return folly::Unit();
197 EXPECT_EQ(0, **collectObserver);
199 for (size_t i = 1; i <= 3; ++i) {
200 observable.setValue(i);
202 EXPECT_TRUE(baton.try_wait_for(std::chrono::seconds{1}));
205 EXPECT_EQ(i, **collectObserver);
209 TEST(Observer, Stress) {
210 SimpleObservable<int> observable(0);
212 auto values = std::make_shared<folly::Synchronized<std::vector<int>>>();
214 auto observer = makeObserver([ child = observable.getObserver(), values ]() {
215 auto value = **child * 10;
217 [&](std::vector<int>& values) { values.push_back(value); });
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());
227 constexpr size_t numIters = 10000;
229 for (size_t i = 1; i <= numIters; ++i) {
230 observable.setValue(i);
233 while (**observer != numIters * 10) {
234 std::this_thread::yield();
237 values->withRLock([numIters = numIters](const std::vector<int>& values) {
238 EXPECT_EQ(numIters * 10, values.back());
239 EXPECT_LT(values.size(), numIters / 2);
241 EXPECT_EQ(0, values[0]);
242 EXPECT_EQ(numIters * 10, values.back());
244 for (auto value : values) {
245 EXPECT_EQ(0, value % 10);
248 for (size_t i = 0; i < values.size() - 1; ++i) {
249 EXPECT_LE(values[i], values[i + 1]);
254 TEST(Observer, TLObserver) {
255 auto createTLObserver = [](int value) {
256 return folly::observer::makeTLObserver([=] { return value; });
260 std::make_unique<folly::observer::TLObserver<int>>(createTLObserver(42));
262 k = std::make_unique<folly::observer::TLObserver<int>>(createTLObserver(41));
266 TEST(Observer, SubscribeCallback) {
267 static auto mainThreadId = std::this_thread::get_id();
268 static std::function<void()> updatesCob;
269 static bool slowGet = false;
270 static std::atomic<size_t> getCallsStart{0};
271 static std::atomic<size_t> getCallsFinish{0};
275 EXPECT_EQ(mainThreadId, std::this_thread::get_id());
279 using element_type = int;
280 static std::shared_ptr<const int> get(Observable&) {
283 /* sleep override */ std::this_thread::sleep_for(
284 std::chrono::seconds{2});
287 return std::make_shared<const int>(42);
290 static void subscribe(Observable&, std::function<void()> cob) {
291 updatesCob = std::move(cob);
294 static void unsubscribe(Observable&) {}
297 std::thread cobThread;
300 folly::observer::ObserverCreator<Observable, Traits>().getObserver();
302 EXPECT_TRUE(updatesCob);
303 EXPECT_EQ(2, getCallsStart);
304 EXPECT_EQ(2, getCallsFinish);
307 EXPECT_EQ(3, getCallsStart);
308 EXPECT_EQ(3, getCallsFinish);
311 cobThread = std::thread([] { updatesCob(); });
312 /* sleep override */ std::this_thread::sleep_for(std::chrono::seconds{1});
313 EXPECT_EQ(4, getCallsStart);
314 EXPECT_EQ(3, getCallsFinish);
316 // Observer is destroyed here
319 // Make sure that destroying the observer actually joined the updates callback
320 EXPECT_EQ(4, getCallsStart);
321 EXPECT_EQ(4, getCallsFinish);