xref: /aosp_15_r20/external/abseil-cpp/absl/base/no_destructor_test.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
1 // Copyright 2023 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/base/no_destructor.h"
16 
17 #include <array>
18 #include <initializer_list>
19 #include <string>
20 #include <type_traits>
21 #include <vector>
22 
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "absl/base/config.h"
26 #include "absl/base/internal/raw_logging.h"
27 
28 namespace {
29 
30 struct Blob {
Blob__anon891016cf0111::Blob31   Blob() : val(42) {}
Blob__anon891016cf0111::Blob32   Blob(int x, int y) : val(x + y) {}
Blob__anon891016cf0111::Blob33   Blob(std::initializer_list<int> xs) {
34     val = 0;
35     for (auto& x : xs) val += x;
36   }
37 
38   Blob(const Blob& /*b*/) = delete;
Blob__anon891016cf0111::Blob39   Blob(Blob&& b) noexcept : val(b.val) {
40     b.moved_out = true;
41   }  // moving is fine
42 
43   // no crash: NoDestructor indeed does not destruct (the moved-out Blob
44   // temporaries do get destroyed though)
~Blob__anon891016cf0111::Blob45   ~Blob() { ABSL_INTERNAL_CHECK(moved_out, "~Blob"); }
46 
47   int val;
48   bool moved_out = false;
49 };
50 
51 struct TypeWithDeletedDestructor {
52   ~TypeWithDeletedDestructor() = delete;
53 };
54 
TEST(NoDestructorTest,DestructorNeverCalled)55 TEST(NoDestructorTest, DestructorNeverCalled) {
56   absl::NoDestructor<TypeWithDeletedDestructor> a;
57   (void)a;
58 }
59 
TEST(NoDestructorTest,Noncopyable)60 TEST(NoDestructorTest, Noncopyable) {
61   using T = absl::NoDestructor<int>;
62 
63   EXPECT_FALSE((std::is_constructible<T, T>::value));
64   EXPECT_FALSE((std::is_constructible<T, const T>::value));
65   EXPECT_FALSE((std::is_constructible<T, T&>::value));
66   EXPECT_FALSE((std::is_constructible<T, const T&>::value));
67 
68   EXPECT_FALSE((std::is_assignable<T&, T>::value));
69   EXPECT_FALSE((std::is_assignable<T&, const T>::value));
70   EXPECT_FALSE((std::is_assignable<T&, T&>::value));
71   EXPECT_FALSE((std::is_assignable<T&, const T&>::value));
72 }
73 
TEST(NoDestructorTest,Interface)74 TEST(NoDestructorTest, Interface) {
75   EXPECT_TRUE(std::is_trivially_destructible<absl::NoDestructor<Blob>>::value);
76   EXPECT_TRUE(
77       std::is_trivially_destructible<absl::NoDestructor<const Blob>>::value);
78   {
79     absl::NoDestructor<Blob> b;  // default c-tor
80     // access: *, ->, get()
81     EXPECT_EQ(42, (*b).val);
82     (*b).val = 55;
83     EXPECT_EQ(55, b->val);
84     b->val = 66;
85     EXPECT_EQ(66, b.get()->val);
86     b.get()->val = 42;  // NOLINT
87     EXPECT_EQ(42, (*b).val);
88   }
89   {
90     absl::NoDestructor<const Blob> b(70, 7);  // regular c-tor, const
91     EXPECT_EQ(77, (*b).val);
92     EXPECT_EQ(77, b->val);
93     EXPECT_EQ(77, b.get()->val);
94   }
95   {
96     const absl::NoDestructor<Blob> b{
97         {20, 28, 40}};  // init-list c-tor, deep const
98     // This only works in clang, not in gcc:
99     // const absl::NoDestructor<Blob> b({20, 28, 40});
100     EXPECT_EQ(88, (*b).val);
101     EXPECT_EQ(88, b->val);
102     EXPECT_EQ(88, b.get()->val);
103   }
104 }
105 
TEST(NoDestructorTest,SfinaeRegressionAbstractArg)106 TEST(NoDestructorTest, SfinaeRegressionAbstractArg) {
107   struct Abstract {
108     virtual ~Abstract() = default;
109     virtual int foo() const = 0;
110   };
111 
112   struct Concrete : Abstract {
113     int foo() const override { return 17; }
114   };
115 
116   struct UsesAbstractInConstructor {
117     explicit UsesAbstractInConstructor(const Abstract& abstract)
118         : i(abstract.foo()) {}
119     int i;
120   };
121 
122   Concrete input;
123   absl::NoDestructor<UsesAbstractInConstructor> foo1(input);
124   EXPECT_EQ(foo1->i, 17);
125   absl::NoDestructor<UsesAbstractInConstructor> foo2(
126       static_cast<const Abstract&>(input));
127   EXPECT_EQ(foo2->i, 17);
128 }
129 
130 // ========================================================================= //
131 
Str0()132 std::string* Str0() {
133   static absl::NoDestructor<std::string> x;
134   return x.get();
135 }
136 
137 extern const std::string& Str2();
138 
Str1()139 const char* Str1() {
140   static absl::NoDestructor<std::string> x(Str2() + "_Str1");
141   return x->c_str();
142 }
143 
Str2()144 const std::string& Str2() {
145   static absl::NoDestructor<std::string> x("Str2");
146   return *x;
147 }
148 
Str2Copy()149 const std::string& Str2Copy() {
150   // Exercise copy construction
151   static absl::NoDestructor<std::string> x(Str2());
152   return *x;
153 }
154 
155 typedef std::array<std::string, 3> MyArray;
Array()156 const MyArray& Array() {
157   static absl::NoDestructor<MyArray> x{{{"foo", "bar", "baz"}}};
158   // This only works in clang, not in gcc:
159   // static absl::NoDestructor<MyArray> x({{"foo", "bar", "baz"}});
160   return *x;
161 }
162 
163 typedef std::vector<int> MyVector;
Vector()164 const MyVector& Vector() {
165   static absl::NoDestructor<MyVector> x{{1, 2, 3}};
166   return *x;
167 }
168 
Int()169 const int& Int() {
170   static absl::NoDestructor<int> x;
171   return *x;
172 }
173 
TEST(NoDestructorTest,StaticPattern)174 TEST(NoDestructorTest, StaticPattern) {
175   EXPECT_TRUE(
176       std::is_trivially_destructible<absl::NoDestructor<std::string>>::value);
177   EXPECT_TRUE(
178       std::is_trivially_destructible<absl::NoDestructor<MyArray>>::value);
179   EXPECT_TRUE(
180       std::is_trivially_destructible<absl::NoDestructor<MyVector>>::value);
181   EXPECT_TRUE(std::is_trivially_destructible<absl::NoDestructor<int>>::value);
182 
183   EXPECT_EQ(*Str0(), "");
184   Str0()->append("foo");
185   EXPECT_EQ(*Str0(), "foo");
186 
187   EXPECT_EQ(std::string(Str1()), "Str2_Str1");
188 
189   EXPECT_EQ(Str2(), "Str2");
190   EXPECT_EQ(Str2Copy(), "Str2");
191 
192   EXPECT_THAT(Array(), testing::ElementsAre("foo", "bar", "baz"));
193 
194   EXPECT_THAT(Vector(), testing::ElementsAre(1, 2, 3));
195 
196   EXPECT_EQ(0, Int());  // should get zero-initialized
197 }
198 
199 #ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
200 // This would fail to compile if Class Template Argument Deduction was not
201 // provided for absl::NoDestructor.
TEST(NoDestructorTest,ClassTemplateArgumentDeduction)202 TEST(NoDestructorTest, ClassTemplateArgumentDeduction) {
203   absl::NoDestructor i(1);
204   static_assert(std::is_same<decltype(i), absl::NoDestructor<int>>::value,
205                 "Expected deduced type to be int.");
206 }
207 #endif
208 
209 }  // namespace
210