Bitcoin Core 32.99.0
P2P Digital Currency
util_not_null_tests.cpp
Go to the documentation of this file.
1// Copyright (c) The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://opensource.org/license/mit/.
4
5#include <sync.h>
6#include <test/util/common.h>
7#include <util/not_null.h>
8
9#include <boost/test/unit_test.hpp>
10
11#include <memory>
12#include <set>
13#include <type_traits>
14#include <unordered_set>
15
16BOOST_AUTO_TEST_SUITE(util_not_null_tests)
17
18static_assert(std::is_same_v<util::NotNullUniquePtr<int>, util::NotNull<std::unique_ptr<int>>>);
19static_assert(std::is_same_v<util::NotNullSharedPtr<int>, util::NotNull<std::shared_ptr<int>>>);
20
21template <class P>
22concept NotNullConstructible = requires { typename std::bool_constant<(util::NotNull{P{}}, true)>; };
23
25{
26 struct SomePtr {
27 constexpr bool operator==(std::nullptr_t) const { return false; }
28 };
29 struct NullPtr {
30 constexpr bool operator==(std::nullptr_t) const { return true; }
31 };
32
33 static_assert(NotNullConstructible<SomePtr>);
34 static_assert(!NotNullConstructible<NullPtr>);
36 static_assert(!NotNullConstructible<int*>);
37}
38
39template <class T>
40concept NotNullType = requires { typename util::NotNull<T>; };
41
42BOOST_AUTO_TEST_CASE(check_ctor_ref)
43{
44 static_assert(!NotNullType<std::shared_ptr<int>&>);
45 static_assert(!NotNullType<int*&>);
46}
47
48BOOST_AUTO_TEST_CASE(check_ctor_conv)
49{
50 using Ptr = std::shared_ptr<int>;
51 using NNPtr = util::NotNullSharedPtr<int>;
52
53 // For context: std::shared_ptr's raw pointer constructor is explicit
54 static_assert(std::constructible_from<Ptr, int*>);
55 static_assert(!std::convertible_to<int*, Ptr>);
56
57 // util::NotNull's constructor is explicit
58 static_assert(std::constructible_from<NNPtr, Ptr>);
59 static_assert(!std::convertible_to<Ptr, NNPtr>);
60
61 // For context: std::reference_wrapper's constructor is implicit
62 using Ref = std::reference_wrapper<int>;
63 static_assert(std::constructible_from<Ref, int&>);
64 static_assert(std::convertible_to<int&, Ref>);
65
66 using UniqPtr = std::unique_ptr<int>;
67 using NNUniqPtr = util::NotNullUniquePtr<int>;
68
69 // NNPtr can be copied, but NNUniqPtr can not:
70 static_assert(std::is_nothrow_constructible_v<NNPtr, NNPtr&>);
71 static_assert(std::is_nothrow_constructible_v<NNPtr, const NNPtr&>);
72 static_assert(std::is_nothrow_constructible_v<NNUniqPtr, NNUniqPtr&&>);
73 static_assert(!std::is_constructible_v<NNUniqPtr, NNUniqPtr&>);
74 static_assert(!std::is_constructible_v<NNUniqPtr, const NNUniqPtr&>);
75 static_assert(!std::is_constructible_v<NNPtr, const NNUniqPtr&>);
76
77 // NNPtr from NNUniqPtr may throw:
78 static_assert(!std::is_nothrow_constructible_v<NNPtr, NNUniqPtr&&>);
79 static_assert(std::is_constructible_v<NNPtr, NNUniqPtr&&>);
80
81 // Extraction is implicit:
82 static_assert(std::convertible_to<NNUniqPtr&&, UniqPtr>);
83 static_assert(std::convertible_to<const NNPtr&, Ptr>);
84 static_assert(std::convertible_to<NNPtr&&, Ptr>);
85}
86
87template <class Target, class Source>
88concept NotNullCopyConvertible = requires {
89 typename std::bool_constant<([] {
90 util::NotNull<Source> copy_source{Source{}};
91 util::NotNull<Target>{copy_source};
92 return true;
93 }())>;
94};
95
96template <class Target, class Source>
97concept NotNullMoveConvertible = requires { typename std::bool_constant<(util::NotNull<Target>{util::NotNull<Source>{Source{}}}, true)>; };
98
99BOOST_AUTO_TEST_CASE(check_cross_type_conversion)
100{
101 struct SomePtr;
102 struct NullPtr {
103 constexpr NullPtr(const SomePtr&) {}
104 constexpr bool operator==(std::nullptr_t) const { return true; }
105 };
106 struct SomePtr {
107 constexpr explicit operator bool() const { return true; }
108 constexpr bool operator==(std::nullptr_t) const { return false; }
109 };
110
111 // Assume checks failed conversions during constant evaluation.
112#if !defined(_MSC_VER)
113 // MSVC does not reject this failed constant evaluation in a requires-expression.
114 // See https://github.com/bitcoin/bitcoin/issues/36153.
117#endif
118}
119
120BOOST_AUTO_TEST_CASE(check_null_smart_pointer)
121{
122 // This conversion can throw, allowing the test hook to observe Assert.
123 static_assert(!std::is_nothrow_constructible_v<util::NotNullSharedPtr<int>, std::unique_ptr<int>>);
126 HasReason{"Internal bug detected: m_ptr != nullptr"});
127}
128
129template <class P>
130concept NullptrComparable = requires(P val) { val == nullptr; };
131template <class P>
132concept HasOperatorBool = requires(P val) { bool{val}; };
133
134BOOST_AUTO_TEST_CASE(check_nullptr_compare)
135{
136 // The deleted ctor for nullptr also disallows nullptr compare:
139
142}
143
144BOOST_AUTO_TEST_CASE(check_nocopy_ptr)
145{
146 using NoCopyPtr = std::unique_ptr<int>;
147 {
148 NoCopyPtr no_copy_ptr{std::make_unique<int>()};
149 util::NotNull no_copy{std::move(no_copy_ptr)};
150 NoCopyPtr extract{std::move(no_copy)};
151 (void)extract;
152 }
153 {
154 util::NotNull no_copy{NoCopyPtr{std::make_unique<int>()}};
155 NoCopyPtr extract{std::move(no_copy).get()};
156 (void)extract;
157 }
158}
159
161{
162 struct MyBase {
163 virtual ~MyBase() = default;
164 };
165 struct MyDerived : public MyBase {
166 };
167
168 util::NotNull p{std::make_shared<MyDerived>()};
169 util::NotNull q{std::make_shared<MyBase>()};
170 q = p;
171 const bool same_ptr{q == p};
172 BOOST_CHECK(same_ptr);
173
174 util::NotNull nn_derived{std::make_unique<MyDerived>()};
175 util::NotNull nn_base{std::make_unique<MyBase>()};
176 nn_base = std::move(nn_derived);
177 std::unique_ptr<MyBase> base_inner{std::move(nn_base)};
178}
179
181{
182 auto a{std::make_unique<int>()};
183 util::NotNull box{std::move(a)};
184 auto new_box{std::move(box)};
185
186 auto b{std::make_shared<int>()};
187 util::NotNull arc{b};
188 auto new_arc{std::move(arc)};
189}
190
192{
193 util::NotNull box_a{std::make_unique<int>(1)};
194 util::NotNull box_b{std::make_unique<int>(2)};
195 static_assert(std::is_nothrow_swappable_v<decltype(box_a)>);
196 BOOST_CHECK_EQUAL(*box_a - *box_b, -1);
197 swap(box_a, box_b);
198 BOOST_CHECK_EQUAL(*box_a - *box_b, 1);
199}
200
202{
203 util::NotNull p{std::make_unique<int>(2)};
204 *p = 3;
205 BOOST_CHECK_EQUAL(*p, 3);
206 *p.get() = 4;
207 BOOST_CHECK_EQUAL(*p, 4);
208}
209
210BOOST_AUTO_TEST_CASE(check_compare_set)
211{
212 auto a{std::make_shared<int>(1)};
213 auto b{std::make_shared<int>(2)};
214 std::set<util::NotNullSharedPtr<int>> uniq{};
215 uniq.emplace(a);
216 uniq.emplace(a);
217 uniq.emplace(b);
218 BOOST_CHECK_EQUAL(uniq.size(), 2);
219}
220
221BOOST_AUTO_TEST_CASE(check_hash_set)
222{
223 auto a{std::make_shared<int>(1)};
224 auto b{std::make_shared<int>(2)};
225 std::unordered_set<util::NotNullSharedPtr<int>> uniq{};
226 uniq.emplace(a);
227 uniq.emplace(a);
228 uniq.emplace(b);
229 BOOST_CHECK_EQUAL(uniq.size(), 2);
230}
231
233{
234 struct Thing {
235 Mutex mutex{};
236 int a GUARDED_BY(mutex){2};
237 };
238 const util::NotNull p{std::make_shared<Thing>()};
239 // Clang thread safety annotation (TSA) may not be able to derive that
240 // operator-> and operator* refer to the same object. So use a named
241 // reference before taking the lock.
242 Thing& ref{*p};
243 LOCK(ref.mutex);
244 BOOST_CHECK_EQUAL(ref.a, 2);
245}
246
BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
Definition: common.h:19
NotNull is intended to be used to denote that a smart pointer type can not hold a nullptr value.
Definition: not_null.h:45
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EQUAL(headers.FindFirst("key"), "value")
BOOST_CHECK_EXCEPTION(HTTPHeaders{}.Read(reader), std::runtime_error, HasReason{"Empty HTTP header name"})
bool operator==(const CNetAddr &a, const CNetAddr &b)
Definition: netaddress.cpp:609
#define BOOST_CHECK(expr)
Definition: object.cpp:16
#define extract(n)
Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits.
#define LOCK(cs)
Definition: sync.h:268
#define GUARDED_BY(x)
Definition: threadsafety.h:37
BOOST_AUTO_TEST_CASE(check_ctor)