| /* | 
 |  *  Copyright 2018 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "rtc_base/unique_id_generator.h" | 
 |  | 
 | #include <cstddef> | 
 | #include <cstdint> | 
 | #include <set> | 
 | #include <vector> | 
 |  | 
 | #include "absl/algorithm/container.h" | 
 | #include "absl/functional/any_invocable.h" | 
 | #include "api/location.h" | 
 | #include "api/task_queue/task_queue_base.h" | 
 | #include "api/units/time_delta.h" | 
 | #include "rtc_base/checks.h" | 
 | #include "test/gmock.h" | 
 | #include "test/gtest.h" | 
 |  | 
 | using ::testing::IsEmpty; | 
 | using ::testing::Test; | 
 |  | 
 | namespace webrtc { | 
 | namespace { | 
 | // Utility class that registers itself as the currently active task queue. | 
 | class FakeTaskQueue : public TaskQueueBase { | 
 |  public: | 
 |   FakeTaskQueue() : task_queue_setter_(this) {} | 
 |  | 
 |   void Delete() override {} | 
 |   void PostTaskImpl(absl::AnyInvocable<void() &&> task, | 
 |                     const PostTaskTraits& traits, | 
 |                     const Location& location) override {} | 
 |   void PostDelayedTaskImpl(absl::AnyInvocable<void() &&> task, | 
 |                            TimeDelta delay, | 
 |                            const PostDelayedTaskTraits& traits, | 
 |                            const Location& location) override {} | 
 |  | 
 |  private: | 
 |   CurrentTaskQueueSetter task_queue_setter_; | 
 | }; | 
 | }  // namespace | 
 |  | 
 | template <typename Generator> | 
 | class UniqueIdGeneratorTest : public Test {}; | 
 |  | 
 | using test_types = ::testing::Types<UniqueNumberGenerator<uint8_t>, | 
 |                                     UniqueNumberGenerator<uint16_t>, | 
 |                                     UniqueNumberGenerator<uint32_t>, | 
 |                                     UniqueNumberGenerator<int>, | 
 |                                     UniqueRandomIdGenerator, | 
 |                                     UniqueStringGenerator>; | 
 |  | 
 | TYPED_TEST_SUITE(UniqueIdGeneratorTest, test_types); | 
 |  | 
 | TYPED_TEST(UniqueIdGeneratorTest, ElementsDoNotRepeat) { | 
 |   typedef TypeParam Generator; | 
 |   const size_t num_elements = 255; | 
 |   Generator generator; | 
 |   std::vector<typename Generator::value_type> values; | 
 |   for (size_t i = 0; i < num_elements; i++) { | 
 |     values.push_back(generator.Generate()); | 
 |   } | 
 |  | 
 |   EXPECT_EQ(num_elements, values.size()); | 
 |   // Use a set to check uniqueness. | 
 |   std::set<typename Generator::value_type> set(values.begin(), values.end()); | 
 |   EXPECT_EQ(values.size(), set.size()) << "Returned values were not unique."; | 
 | } | 
 |  | 
 | TYPED_TEST(UniqueIdGeneratorTest, KnownElementsAreNotGenerated) { | 
 |   typedef TypeParam Generator; | 
 |   const size_t num_elements = 100; | 
 |   Generator generator1; | 
 |   std::vector<typename Generator::value_type> known_values; | 
 |   for (size_t i = 0; i < num_elements; i++) { | 
 |     known_values.push_back(generator1.Generate()); | 
 |   } | 
 |   EXPECT_EQ(num_elements, known_values.size()); | 
 |   Generator generator2(known_values); | 
 |  | 
 |   std::vector<typename Generator::value_type> values; | 
 |   for (size_t i = 0; i < num_elements; i++) { | 
 |     values.push_back(generator2.Generate()); | 
 |   } | 
 |   EXPECT_THAT(values, ::testing::SizeIs(num_elements)); | 
 |   absl::c_sort(values); | 
 |   absl::c_sort(known_values); | 
 |   std::vector<typename Generator::value_type> intersection; | 
 |   absl::c_set_intersection(values, known_values, | 
 |                            std::back_inserter(intersection)); | 
 |   EXPECT_THAT(intersection, IsEmpty()); | 
 | } | 
 |  | 
 | TYPED_TEST(UniqueIdGeneratorTest, AddedElementsAreNotGenerated) { | 
 |   typedef TypeParam Generator; | 
 |   const size_t num_elements = 100; | 
 |   Generator generator1; | 
 |   std::vector<typename Generator::value_type> known_values; | 
 |   for (size_t i = 0; i < num_elements; i++) { | 
 |     known_values.push_back(generator1.Generate()); | 
 |   } | 
 |   EXPECT_EQ(num_elements, known_values.size()); | 
 |  | 
 |   Generator generator2; | 
 |  | 
 |   for (const typename Generator::value_type& value : known_values) { | 
 |     generator2.AddKnownId(value); | 
 |   } | 
 |  | 
 |   std::vector<typename Generator::value_type> values; | 
 |   for (size_t i = 0; i < num_elements; i++) { | 
 |     values.push_back(generator2.Generate()); | 
 |   } | 
 |   EXPECT_THAT(values, ::testing::SizeIs(num_elements)); | 
 |   absl::c_sort(values); | 
 |   absl::c_sort(known_values); | 
 |   std::vector<typename Generator::value_type> intersection; | 
 |   absl::c_set_intersection(values, known_values, | 
 |                            std::back_inserter(intersection)); | 
 |   EXPECT_THAT(intersection, IsEmpty()); | 
 | } | 
 |  | 
 | TYPED_TEST(UniqueIdGeneratorTest, AddKnownIdOnNewIdReturnsTrue) { | 
 |   typedef TypeParam Generator; | 
 |  | 
 |   Generator generator1; | 
 |   const typename Generator::value_type id = generator1.Generate(); | 
 |  | 
 |   Generator generator2; | 
 |   EXPECT_TRUE(generator2.AddKnownId(id)); | 
 | } | 
 |  | 
 | TYPED_TEST(UniqueIdGeneratorTest, AddKnownIdCalledAgainForSameIdReturnsFalse) { | 
 |   typedef TypeParam Generator; | 
 |  | 
 |   Generator generator1; | 
 |   const typename Generator::value_type id = generator1.Generate(); | 
 |  | 
 |   Generator generator2; | 
 |   ASSERT_TRUE(generator2.AddKnownId(id)); | 
 |   EXPECT_FALSE(generator2.AddKnownId(id)); | 
 | } | 
 |  | 
 | TYPED_TEST(UniqueIdGeneratorTest, | 
 |            AddKnownIdOnIdProvidedAsKnownToCtorReturnsFalse) { | 
 |   typedef TypeParam Generator; | 
 |  | 
 |   Generator generator1; | 
 |   const typename Generator::value_type id = generator1.Generate(); | 
 |   std::vector<typename Generator::value_type> known_values = {id}; | 
 |  | 
 |   Generator generator2(known_values); | 
 |   EXPECT_FALSE(generator2.AddKnownId(id)); | 
 | } | 
 |  | 
 | // Tests that it's OK to construct the generator in one execution environment | 
 | // (thread/task queue) but use it in another. | 
 | TEST(UniqueNumberGenerator, UsedOnSecondaryThread) { | 
 |   const auto* current_tq = TaskQueueBase::Current(); | 
 |   // Construct the generator before `fake_task_queue` to ensure that it is | 
 |   // constructed in a different execution environment than what | 
 |   // `fake_task_queue` will represent. | 
 |   UniqueNumberGenerator<uint32_t> generator; | 
 |  | 
 |   FakeTaskQueue fake_task_queue; | 
 |   // Sanity check to make sure we're in a different runtime environment. | 
 |   ASSERT_NE(current_tq, TaskQueueBase::Current()); | 
 |  | 
 |   // Generating an id should be fine in this context. | 
 |   generator.Generate(); | 
 | } | 
 |  | 
 | #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) | 
 | TEST(UniqueNumberGeneratorDeathTest, FailsWhenUsedInWrongContext) { | 
 |   // Instantiate the generator before the `loop`. This ensures that | 
 |   // thread/sequence checkers will pick up a different thread environment than | 
 |   // `fake_task_queue` will represent. | 
 |   UniqueNumberGenerator<uint32_t> generator; | 
 |  | 
 |   // Instantiate a fake task queue that will register itself as the current tq. | 
 |   FakeTaskQueue initial_fake_task_queue; | 
 |   // Generate an ID on the current thread. This causes the generator to attach | 
 |   // to the current thread context. | 
 |   generator.Generate(); | 
 |  | 
 |   // Instantiate a fake task queue that will register itself as the current tq. | 
 |   FakeTaskQueue fake_task_queue; | 
 |  | 
 |   // Attempting to generate an id should now trigger a dcheck. | 
 |   EXPECT_DEATH(generator.Generate(), ""); | 
 | } | 
 | #endif | 
 |  | 
 | }  // namespace webrtc |