// 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_map.h"
#include <memory>
#include <utility>
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace internal {
namespace {
constexpr int kSlotId = 1;
class SetOnDestroy {
public:
SetOnDestroy(bool* was_destroyed_ptr)
: was_destroyed_ptr_(was_destroyed_ptr) {
DCHECK(was_destroyed_ptr_);
DCHECK(!(*was_destroyed_ptr_));
}
~SetOnDestroy() {
DCHECK(!(*was_destroyed_ptr_));
*was_destroyed_ptr_ = true;
}
private:
bool* const was_destroyed_ptr_;
DISALLOW_COPY_AND_ASSIGN(SetOnDestroy);
};
template <typename T, typename... Args>
SequenceLocalStorageMap::ValueDestructorPair CreateValueDestructorPair(
Args... args) {
T* value = new T(args...);
SequenceLocalStorageMap::ValueDestructorPair::DestructorFunc* destructor =
[](void* ptr) { std::default_delete<T>()(static_cast<T*>(ptr)); };
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{
value, destructor};
return value_destructor_pair;
}
} // namespace
// Verify that setting a value in the SequenceLocalStorageMap, then getting
// it will yield the same value.
TEST(SequenceLocalStorageMapTest, SetGet) {
SequenceLocalStorageMap sequence_local_storage_map;
ScopedSetSequenceLocalStorageMapForCurrentThread
scoped_sequence_local_storage_map(&sequence_local_storage_map);
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
CreateValueDestructorPair<int>(5);
sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
EXPECT_EQ(*static_cast<int*>(sequence_local_storage_map.Get(kSlotId)), 5);
}
// Verify that the destructor is called on a value stored in the
// SequenceLocalStorageMap when SequenceLocalStorageMap is destroyed.
TEST(SequenceLocalStorageMapTest, Destructor) {
bool set_on_destruction = false;
{
SequenceLocalStorageMap sequence_local_storage_map;
ScopedSetSequenceLocalStorageMapForCurrentThread
scoped_sequence_local_storage_map(&sequence_local_storage_map);
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction);
sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
}
EXPECT_TRUE(set_on_destruction);
}
// Verify that overwriting a value already in the SequenceLocalStorageMap
// calls value's destructor.
TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwrite) {
bool set_on_destruction = false;
bool set_on_destruction2 = false;
{
SequenceLocalStorageMap sequence_local_storage_map;
ScopedSetSequenceLocalStorageMapForCurrentThread
scoped_sequence_local_storage_map(&sequence_local_storage_map);
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction);
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 =
CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction2);
sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
ASSERT_FALSE(set_on_destruction);
// Overwrites the old value in the slot.
sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair2));
// Destructor should've been called for the old value in the slot, and not
// yet called for the new value.
EXPECT_TRUE(set_on_destruction);
EXPECT_FALSE(set_on_destruction2);
}
EXPECT_TRUE(set_on_destruction2);
}
} // namespace internal
} // namespace base