diff options
Diffstat (limited to 'Firestore/core/test/firebase/firestore/util/executor_std_test.cc')
-rw-r--r-- | Firestore/core/test/firebase/firestore/util/executor_std_test.cc | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/Firestore/core/test/firebase/firestore/util/executor_std_test.cc b/Firestore/core/test/firebase/firestore/util/executor_std_test.cc new file mode 100644 index 0000000..43cad60 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/executor_std_test.cc @@ -0,0 +1,240 @@ +/* + * Copyright 2018 Google + * + * 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 "Firestore/core/test/firebase/firestore/util/executor_test.h" + +#include <chrono> // NOLINT(build/c++11) +#include <cstdlib> +#include <future> // NOLINT(build/c++11) +#include <string> +#include <thread> // NOLINT(build/c++11) + +#include "Firestore/core/src/firebase/firestore/util/executor_std.h" +#include "Firestore/core/test/firebase/firestore/util/async_tests_util.h" +#include "absl/memory/memory.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace chr = std::chrono; +using async::Schedule; + +class ScheduleTest : public ::testing::Test { + public: + ScheduleTest() : start_time{now()} { + } + + using ScheduleT = Schedule<int>; + + ScheduleT schedule; + ScheduleT::TimePoint start_time; +}; + +// Schedule tests + +TEST_F(ScheduleTest, PopIfDue_Immediate) { + EXPECT_FALSE(schedule.PopIfDue().has_value()); + + // Push values in a deliberately non-sorted order. + schedule.Push(3, start_time); + schedule.Push(1, start_time); + schedule.Push(2, start_time); + EXPECT_FALSE(schedule.empty()); + EXPECT_EQ(schedule.size(), 3u); + + EXPECT_EQ(schedule.PopIfDue().value(), 3); + EXPECT_EQ(schedule.PopIfDue().value(), 1); + EXPECT_EQ(schedule.PopIfDue().value(), 2); + EXPECT_FALSE(schedule.PopIfDue().has_value()); + EXPECT_TRUE(schedule.empty()); + EXPECT_EQ(schedule.size(), 0u); +} + +TEST_F(ScheduleTest, PopIfDue_Delayed) { + schedule.Push(1, start_time + chr::milliseconds(5)); + schedule.Push(2, start_time + chr::milliseconds(3)); + schedule.Push(3, start_time + chr::milliseconds(1)); + + EXPECT_FALSE(schedule.PopIfDue().has_value()); + std::this_thread::sleep_for(chr::milliseconds(5)); + + EXPECT_EQ(schedule.PopIfDue().value(), 3); + EXPECT_EQ(schedule.PopIfDue().value(), 2); + EXPECT_EQ(schedule.PopIfDue().value(), 1); + EXPECT_TRUE(schedule.empty()); +} + +TEST_F(ScheduleTest, PopBlocking) { + schedule.Push(1, start_time + chr::milliseconds(3)); + EXPECT_FALSE(schedule.PopIfDue().has_value()); + + EXPECT_EQ(schedule.PopBlocking(), 1); + EXPECT_GE(now(), start_time + chr::milliseconds(3)); + EXPECT_TRUE(schedule.empty()); +} + +TEST_F(ScheduleTest, RemoveIf) { + schedule.Push(1, start_time); + schedule.Push(2, now() + chr::minutes(1)); + + auto maybe_removed = schedule.RemoveIf([](const int v) { return v == 1; }); + EXPECT_TRUE(maybe_removed.has_value()); + EXPECT_EQ(maybe_removed.value(), 1); + + // Non-existent value. + maybe_removed = schedule.RemoveIf([](const int v) { return v == 1; }); + EXPECT_FALSE(maybe_removed.has_value()); + + maybe_removed = schedule.RemoveIf([](const int v) { return v == 2; }); + EXPECT_TRUE(maybe_removed.has_value()); + EXPECT_EQ(maybe_removed.value(), 2); + EXPECT_TRUE(schedule.empty()); +} + +TEST_F(ScheduleTest, Ordering) { + schedule.Push(11, start_time + chr::milliseconds(5)); + schedule.Push(1, start_time); + schedule.Push(2, start_time); + schedule.Push(9, start_time + chr::milliseconds(2)); + schedule.Push(3, start_time); + schedule.Push(10, start_time + chr::milliseconds(3)); + schedule.Push(12, start_time + chr::milliseconds(5)); + schedule.Push(4, start_time); + schedule.Push(5, start_time); + schedule.Push(6, start_time); + schedule.Push(8, start_time + chr::milliseconds(1)); + schedule.Push(7, start_time); + + std::vector<int> values; + while (!schedule.empty()) { + values.push_back(schedule.PopBlocking()); + } + const std::vector<int> expected = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + EXPECT_EQ(values, expected); +} + +TEST_F(ScheduleTest, AddingEntryUnblocksEmptyQueue) { + const auto future = std::async(std::launch::async, [&] { + ASSERT_FALSE(schedule.PopIfDue().has_value()); + EXPECT_EQ(schedule.PopBlocking(), 1); + }); + + std::this_thread::sleep_for(chr::milliseconds(5)); + schedule.Push(1, start_time); + ABORT_ON_TIMEOUT(future); +} + +TEST_F(ScheduleTest, PopBlockingUnblocksOnNewPastDueEntries) { + const auto far_away = start_time + chr::seconds(10); + schedule.Push(5, far_away); + + const auto future = std::async(std::launch::async, [&] { + ASSERT_FALSE(schedule.PopIfDue().has_value()); + EXPECT_EQ(schedule.PopBlocking(), 3); + }); + + std::this_thread::sleep_for(chr::milliseconds(5)); + schedule.Push(3, start_time); + ABORT_ON_TIMEOUT(future); +} + +TEST_F(ScheduleTest, PopBlockingAdjustsWaitTimeOnNewSoonerEntries) { + const auto far_away = start_time + chr::seconds(10); + schedule.Push(5, far_away); + + const auto future = std::async(std::launch::async, [&] { + ASSERT_FALSE(schedule.PopIfDue().has_value()); + EXPECT_EQ(schedule.PopBlocking(), 3); + // Make sure schedule hasn't been waiting longer than necessary. + EXPECT_LT(now(), far_away); + }); + + std::this_thread::sleep_for(chr::milliseconds(5)); + schedule.Push(3, start_time + chr::milliseconds(100)); + ABORT_ON_TIMEOUT(future); +} + +TEST_F(ScheduleTest, PopBlockingCanReadjustTimeIfSeveralElementsAreAdded) { + const auto far_away = start_time + chr::seconds(5); + const auto very_far_away = start_time + chr::seconds(10); + schedule.Push(3, very_far_away); + + const auto future = std::async(std::launch::async, [&] { + ASSERT_FALSE(schedule.PopIfDue().has_value()); + EXPECT_EQ(schedule.PopBlocking(), 1); + EXPECT_LT(now(), far_away); + }); + + std::this_thread::sleep_for(chr::milliseconds(5)); + schedule.Push(2, far_away); + std::this_thread::sleep_for(chr::milliseconds(1)); + schedule.Push(1, start_time + chr::milliseconds(100)); + ABORT_ON_TIMEOUT(future); +} + +TEST_F(ScheduleTest, PopBlockingNoticesRemovals) { + const auto future = std::async(std::launch::async, [&] { + schedule.Push(1, start_time + chr::milliseconds(50)); + schedule.Push(2, start_time + chr::milliseconds(100)); + ASSERT_FALSE(schedule.PopIfDue().has_value()); + EXPECT_EQ(schedule.PopBlocking(), 2); + }); + + while (schedule.empty()) { + std::this_thread::sleep_for(chr::milliseconds(1)); + } + const auto maybe_removed = + schedule.RemoveIf([](const int v) { return v == 1; }); + EXPECT_EQ(maybe_removed.value(), 1); + ABORT_ON_TIMEOUT(future); +} + +TEST_F(ScheduleTest, PopBlockingIsNotAffectedByIrrelevantRemovals) { + const auto future = std::async(std::launch::async, [&] { + schedule.Push(1, start_time + chr::milliseconds(50)); + schedule.Push(2, start_time + chr::seconds(10)); + ASSERT_FALSE(schedule.PopIfDue().has_value()); + EXPECT_EQ(schedule.PopBlocking(), 1); + }); + + while (schedule.empty()) { + std::this_thread::sleep_for(chr::milliseconds(1)); + } + const auto maybe_removed = + schedule.RemoveIf([](const int v) { return v == 2; }); + EXPECT_EQ(maybe_removed.value(), 2); + ABORT_ON_TIMEOUT(future); +} + +// ExecutorStd tests + +namespace { + +inline std::unique_ptr<internal::Executor> ExecutorFactory() { + return absl::make_unique<internal::ExecutorStd>(); +} + +} // namespace + +INSTANTIATE_TEST_CASE_P(ExecutorTestStd, + ExecutorTest, + ::testing::Values(ExecutorFactory)); + +} // namespace util +} // namespace firestore +} // namespace firebase |