| /* |
| * Copyright 2015 Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <algorithm> |
| #include <atomic> |
| #include <exception> |
| #include <vector> |
| #include <folly/futures/Future.h> |
| |
| #include <gtest/gtest.h> |
| |
| using namespace std; |
| using namespace std::chrono; |
| using namespace folly; |
| |
| // Runs func num_times in parallel, expects that all of them will take |
| // at least min_duration and at least 1 execution will take less than |
| // max_duration. |
| template <typename D, typename F> |
| void multiAttemptExpectDurationWithin(size_t num_tries, |
| D min_duration, |
| D max_duration, |
| const F& func) { |
| vector<thread> threads(num_tries); |
| vector<D> durations(num_tries, D::min()); |
| for (size_t i = 0; i < num_tries; ++i) { |
| threads[i] = thread([&,i]{ |
| auto start = steady_clock::now(); |
| func(); |
| durations[i] = duration_cast<D>(steady_clock::now() - start); |
| }); |
| } |
| for (auto& t : threads) { |
| t.join(); |
| } |
| sort(durations.begin(), durations.end()); |
| for (auto d : durations) { |
| EXPECT_GE(d, min_duration); |
| } |
| EXPECT_LE(durations[0], max_duration); |
| } |
| |
| TEST(RetryingTest, has_op_call) { |
| using ew = exception_wrapper; |
| auto policy_raw = [](size_t n, const ew&) { return n < 3; }; |
| auto policy_fut = [](size_t n, const ew&) { return makeFuture(n < 3); }; |
| using namespace futures::detail; |
| EXPECT_TRUE(retrying_policy_traits<decltype(policy_raw)>::is_raw::value); |
| EXPECT_TRUE(retrying_policy_traits<decltype(policy_fut)>::is_fut::value); |
| } |
| |
| TEST(RetryingTest, basic) { |
| auto r = futures::retrying( |
| [](size_t n, const exception_wrapper&) { return n < 3; }, |
| [](size_t n) { |
| return n < 2 |
| ? makeFuture<size_t>(runtime_error("ha")) |
| : makeFuture(n); |
| } |
| ).wait(); |
| EXPECT_EQ(2, r.value()); |
| } |
| |
| TEST(RetryingTest, policy_future) { |
| atomic<size_t> sleeps {0}; |
| auto r = futures::retrying( |
| [&](size_t n, const exception_wrapper&) { |
| return n < 3 |
| ? makeFuture(++sleeps).then([] { return true; }) |
| : makeFuture(false); |
| }, |
| [](size_t n) { |
| return n < 2 |
| ? makeFuture<size_t>(runtime_error("ha")) |
| : makeFuture(n); |
| } |
| ).wait(); |
| EXPECT_EQ(2, r.value()); |
| EXPECT_EQ(2, sleeps); |
| } |
| |
| TEST(RetryingTest, policy_basic) { |
| auto r = futures::retrying( |
| futures::retryingPolicyBasic(3), |
| [](size_t n) { |
| return n < 2 |
| ? makeFuture<size_t>(runtime_error("ha")) |
| : makeFuture(n); |
| } |
| ).wait(); |
| EXPECT_EQ(2, r.value()); |
| } |
| |
| TEST(RetryingTest, policy_capped_jittered_exponential_backoff) { |
| multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{ |
| using ms = milliseconds; |
| auto r = futures::retrying( |
| futures::retryingPolicyCappedJitteredExponentialBackoff( |
| 3, ms(100), ms(1000), 0.1, mt19937_64(0), |
| [](size_t, const exception_wrapper&) { return true; }), |
| [](size_t n) { |
| return n < 2 |
| ? makeFuture<size_t>(runtime_error("ha")) |
| : makeFuture(n); |
| } |
| ).wait(); |
| EXPECT_EQ(2, r.value()); |
| }); |
| } |
| |
| TEST(RetryingTest, policy_sleep_defaults) { |
| multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{ |
| // To ensure that this compiles with default params. |
| using ms = milliseconds; |
| auto r = futures::retrying( |
| futures::retryingPolicyCappedJitteredExponentialBackoff( |
| 3, ms(100), ms(1000), 0.1), |
| [](size_t n) { |
| return n < 2 |
| ? makeFuture<size_t>(runtime_error("ha")) |
| : makeFuture(n); |
| } |
| ).wait(); |
| EXPECT_EQ(2, r.value()); |
| }); |
| } |
| |
| /* |
| TEST(RetryingTest, policy_sleep_cancel) { |
| multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{ |
| mt19937_64 rng(0); |
| using ms = milliseconds; |
| auto r = futures::retrying( |
| futures::retryingPolicyCappedJitteredExponentialBackoff( |
| 5, ms(100), ms(1000), 0.1, rng, |
| [](size_t n, const exception_wrapper&) { return true; }), |
| [](size_t n) { |
| return n < 4 |
| ? makeFuture<size_t>(runtime_error("ha")) |
| : makeFuture(n); |
| } |
| ); |
| r.cancel(); |
| r.wait(); |
| EXPECT_EQ(2, r.value()); |
| }); |
| } |
| */ |