|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/threading/sequence_local_storage_slot.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/threading/sequence_local_storage_map.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class SequenceLocalStorageSlotTest : public testing::Test { | 
|  | protected: | 
|  | SequenceLocalStorageSlotTest() | 
|  | : scoped_sequence_local_storage_(&sequence_local_storage_) {} | 
|  |  | 
|  | internal::SequenceLocalStorageMap sequence_local_storage_; | 
|  | internal::ScopedSetSequenceLocalStorageMapForCurrentThread | 
|  | scoped_sequence_local_storage_; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(SequenceLocalStorageSlotTest); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Verify that a value stored with Set() can be retrieved with Get(). | 
|  | TEST_F(SequenceLocalStorageSlotTest, GetSet) { | 
|  | SequenceLocalStorageSlot<int> slot; | 
|  | slot.Set(5); | 
|  | EXPECT_EQ(slot.Get(), 5); | 
|  | } | 
|  |  | 
|  | // Verify that setting an object in a SequenceLocalStorageSlot creates a copy | 
|  | // of that object independent of the original one. | 
|  | TEST_F(SequenceLocalStorageSlotTest, SetObjectIsIndependent) { | 
|  | bool should_be_false = false; | 
|  |  | 
|  | SequenceLocalStorageSlot<bool> slot; | 
|  |  | 
|  | slot.Set(should_be_false); | 
|  |  | 
|  | EXPECT_FALSE(slot.Get()); | 
|  | slot.Get() = true; | 
|  | EXPECT_TRUE(slot.Get()); | 
|  |  | 
|  | EXPECT_NE(should_be_false, slot.Get()); | 
|  | } | 
|  |  | 
|  | // Verify that multiple slots work and that calling Get after overwriting | 
|  | // a value in a slot yields the new value. | 
|  | TEST_F(SequenceLocalStorageSlotTest, GetSetMultipleSlots) { | 
|  | SequenceLocalStorageSlot<int> slot1; | 
|  | SequenceLocalStorageSlot<int> slot2; | 
|  | SequenceLocalStorageSlot<int> slot3; | 
|  |  | 
|  | slot1.Set(1); | 
|  | slot2.Set(2); | 
|  | slot3.Set(3); | 
|  |  | 
|  | EXPECT_EQ(slot1.Get(), 1); | 
|  | EXPECT_EQ(slot2.Get(), 2); | 
|  | EXPECT_EQ(slot3.Get(), 3); | 
|  |  | 
|  | slot3.Set(4); | 
|  | slot2.Set(5); | 
|  | slot1.Set(6); | 
|  |  | 
|  | EXPECT_EQ(slot3.Get(), 4); | 
|  | EXPECT_EQ(slot2.Get(), 5); | 
|  | EXPECT_EQ(slot1.Get(), 6); | 
|  | } | 
|  |  | 
|  | // Verify that changing the the value returned by Get() changes the value | 
|  | // in sequence local storage. | 
|  | TEST_F(SequenceLocalStorageSlotTest, GetReferenceModifiable) { | 
|  | SequenceLocalStorageSlot<bool> slot; | 
|  | slot.Set(false); | 
|  | slot.Get() = true; | 
|  | EXPECT_TRUE(slot.Get()); | 
|  | } | 
|  |  | 
|  | // Verify that a move-only type can be stored in sequence local storage. | 
|  | TEST_F(SequenceLocalStorageSlotTest, SetGetWithMoveOnlyType) { | 
|  | std::unique_ptr<int> int_unique_ptr = std::make_unique<int>(5); | 
|  |  | 
|  | SequenceLocalStorageSlot<std::unique_ptr<int>> slot; | 
|  | slot.Set(std::move(int_unique_ptr)); | 
|  |  | 
|  | EXPECT_EQ(*slot.Get(), 5); | 
|  | } | 
|  |  | 
|  | // Verify that a Get() without a previous Set() on a slot returns a | 
|  | // default-constructed value. | 
|  | TEST_F(SequenceLocalStorageSlotTest, GetWithoutSetDefaultConstructs) { | 
|  | struct DefaultConstructable { | 
|  | int x = 0x12345678; | 
|  | }; | 
|  |  | 
|  | SequenceLocalStorageSlot<DefaultConstructable> slot; | 
|  |  | 
|  | EXPECT_EQ(slot.Get().x, 0x12345678); | 
|  | } | 
|  |  | 
|  | // Verify that a Get() without a previous Set() on a slot with a POD-type | 
|  | // returns a default-constructed value. | 
|  | // Note: this test could be flaky and give a false pass. If it's flaky, the test | 
|  | // might've "passed" because the memory for the slot happened to be zeroed. | 
|  | TEST_F(SequenceLocalStorageSlotTest, GetWithoutSetDefaultConstructsPOD) { | 
|  | SequenceLocalStorageSlot<void*> slot; | 
|  |  | 
|  | EXPECT_EQ(slot.Get(), nullptr); | 
|  | } | 
|  |  | 
|  | // Verify that the value of a slot is specific to a SequenceLocalStorageMap | 
|  | TEST(SequenceLocalStorageSlotMultipleMapTest, SetGetMultipleMapsOneSlot) { | 
|  | SequenceLocalStorageSlot<unsigned int> slot; | 
|  | internal::SequenceLocalStorageMap sequence_local_storage_maps[5]; | 
|  |  | 
|  | // Set the value of the slot to be the index of the current | 
|  | // SequenceLocalStorageMaps in the vector | 
|  | for (unsigned int i = 0; i < arraysize(sequence_local_storage_maps); ++i) { | 
|  | internal::ScopedSetSequenceLocalStorageMapForCurrentThread | 
|  | scoped_sequence_local_storage(&sequence_local_storage_maps[i]); | 
|  |  | 
|  | slot.Set(i); | 
|  | } | 
|  |  | 
|  | for (unsigned int i = 0; i < arraysize(sequence_local_storage_maps); ++i) { | 
|  | internal::ScopedSetSequenceLocalStorageMapForCurrentThread | 
|  | scoped_sequence_local_storage(&sequence_local_storage_maps[i]); | 
|  |  | 
|  | EXPECT_EQ(slot.Get(), i); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace base |