Arene Base
Fundamental Utilities For Safety Critical C++
Loading...
Searching...
No Matches
external_vector.hpp
Go to the documentation of this file.
1// Copyright 2026, Toyota Motor Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#ifndef INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_INLINE_CONTAINER_TESTING_EXTERNAL_VECTOR_HPP_
6#define INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_INLINE_CONTAINER_TESTING_EXTERNAL_VECTOR_HPP_
7
8#include <algorithm>
9#include <cstddef>
10#include <initializer_list>
11#include <iterator>
12#include <limits>
13#include <memory>
14#include <stdexcept>
15#include <tuple>
16#include <type_traits>
17#include <utility>
18#include <vector>
19
20#include <gmock/gmock.h>
21#include <gtest/gtest.h>
22
23#include "arene/base/array.hpp"
24#include "arene/base/byte.hpp"
25#include "arene/base/compiler_support/diagnostics.hpp"
26#include "arene/base/compiler_support/platform_queries.hpp"
27#include "arene/base/compiler_support/preprocessor.hpp"
28#include "arene/base/inline_container/external_vector.hpp"
29#include "arene/base/inline_container/testing/customization.hpp"
30#include "arene/base/iterator.hpp"
31#include "arene/base/optional.hpp"
32#include "arene/base/span.hpp"
33#include "arene/base/testing/gtest.hpp"
34#include "arene/base/utility/swap.hpp"
35
36namespace arene {
37namespace base {
38namespace testing {
39
40//////////////////////////////////////////////////////////////////////
41// Customization points for generating values of user-defined types //
42//////////////////////////////////////////////////////////////////////
43
44/// @brief Create a test external vector containing a range of test values
45/// @tparam T The type to contain in the test vector
46/// @param storage The storage space to use for the values stored in the vector
47/// @param begin The first index of test value to put in the vector
48/// @param end The after-end index of test value to put in the vector
49/// @return An external_vector<T> containing [test_value(begin), test_value(end))
50template <typename T>
51constexpr auto test_external_vector(
57 for (std::size_t ii = begin; ii < end; ++ii) {
59 }
60 return vec;
61}
62
63/////////////////////////
64// Parameterized tests //
65/////////////////////////
66
68ARENE_IGNORE_ALL("-Wfloat-equal", "These tests don't perform arithmetic, so equality is OK even for floating point");
69
70/// @brief Test fixture for all type-parameterized @c external_vector tests
71/// @tparam T The type parameter currently being used for tests; filled in by Google Test as @c TypeParam
72template <typename T>
73class ExternalVectorTest : public ::testing::Test {
74 protected:
75 /// @brief The base capacity to use for most @c external_vector instantiations in the parameterized tests
76 static constexpr std::size_t capacity = 25; // TODO Allow users to specify (max?) capacity
77
78 /// @brief The type of the storage array for this type with the specified capacity
79 /// @tparam Capacity The capacity of the expected container
80 template <std::size_t Capacity>
81 using storage_type = arene::base::array<arene::base::byte, Capacity * sizeof(T)>;
82
83 /// @brief The type of the storage array for this type with the default capacity
85
86 /// @brief An instance of the default storage type to use as backing for a default capacity vector
87 // NOLINTNEXTLINE(readability-identifier-naming)
89
90 /// @brief A standard vector with the correct type, used in most test cases
92
93 /// @brief A helper template to get the type resulting from a @c try_construct call
94 /// @tparam Vector An @c external_vector type which we will try to construct
95 /// @tparam Args The arguments with which we will try to construct @c Vector
96 template <typename Vector, typename... Args>
98
99 /// @brief A type with a @c noexcept(false) conversion operator to @c T (doesn't actually throw, just declares it)
101 /// @brief Implicit conversion operator to @c T which declares that it might throw, though it doesn't actually
102 /// @return The 0th test value of @c T
103 // NOLINTNEXTLINE(hicpp-explicit-conversions)
104 operator T() const noexcept(false) { return ::arene::base::testing::test_value<T>(0); }
105 };
106
107 /// @brief A bidirectional iterator of @c T which declares that it could throw, used in some @c noexcept tests
109 /// @brief The category of this iterator
111 /// @brief The type used for differences in this iterator
113 /// @brief The type returned by dereferencing this iterator
114 using reference = T&;
115 /// @brief The value type pointed to by this iterator
116 using value_type = T;
117 /// @brief This iterator as a pointer (disabled)
118 using pointer = void;
119
120 /// @brief Dereference operator; never defined
121 auto operator*() -> reference;
122
123 /// @brief Pre-increment operator; never defined
125 /// @brief Post-increment operator; never defined
127 /// @brief Pre-decrement operator; never defined
129 /// @brief Post-decrement operator; never defined
131 /// @brief Equality operator; never defined
133 /// @brief Inequality operator; never defined
135 };
136
137 /// @brief An input iterator pointing to any type whose operators have adjustable @c noexcept specifications
138 /// @tparam U The value type of the iterator
139 /// @tparam OperatorsNoexcept The @c noexcept specifications of all operators are equal to this
140 template <typename U, bool OperatorsNoexcept>
142 /// @brief The category of this iterator
144 /// @brief The type used for differences in this iterator
146 /// @brief The type returned by dereferencing this iterator
147 using reference = U&;
148 /// @brief The value type pointed to by this iterator
149 using value_type = U;
150 /// @brief This iterator as a pointer (disabled)
151 using pointer = void;
152
153 /// @brief Dereference operator; never defined
154 auto operator*() noexcept(OperatorsNoexcept) -> reference;
155
156 /// @brief Pre-increment operator; never defined
158 /// @brief Post-increment operator; never defined
160 /// @brief Equality operator; never defined
162 /// @brief Inequality operator; never defined
164 };
165
166 /// @brief An input iterator pointing to any type which declares that it could throw, used in some @c noexcept tests
167 /// @tparam U The value type of the iterator
168 template <typename U>
169 using throwing_input_iterator = input_iterator_with_configurable_noexcept<U, false>;
170 /// @brief An input iterator pointing to any type which declares that it can not throw, used in some @c noexcept tests
171 /// @tparam U The value type of the iterator
172 template <typename U>
173 using non_throwing_input_iterator = input_iterator_with_configurable_noexcept<U, true>;
174
175 /// @brief Construct a @c vector from the passed parameters; allows calling as this->construct, which can be shorter
176 /// @tparam U The types of the arguments to the constructor
177 /// @param params The parameters to the constructor
178 template <typename... U>
179 static constexpr auto construct(U&&... params) noexcept(noexcept(vector(std::forward<U>(params)...))) -> vector {
180 return vector(std::forward<U>(params)...);
181 }
182
183 /// @brief Return the @c idx 'th test value of the current @c T , parameterized by test suite users
184 /// @param idx The index of the test value to get
185 /// @return The @c idx 'th test value of the current @c T
186 static constexpr auto test_value(std::size_t idx) noexcept(std::is_nothrow_move_constructible<T>::value) -> T {
187 return ::arene::base::testing::test_value<T>(idx);
188 }
189
190 /// @brief Return a @c vector containing the test values @c [begin,end) of @c T , parameterized by test suite users
191 /// @param storage The storage space to use for the values stored in the vector
192 /// @param begin The index of the first test value to put into the test vector
193 /// @param end The index after that of the last test value to put into the test vector
194 /// @return A test vector holding the test values determined by the indices
195 static constexpr auto test_external_vector(
196 arene::base::span<arene::base::byte> storage,
197 std::size_t begin,
198 std::size_t end
200 return ::arene::base::testing::test_external_vector<T>(storage, begin, end);
201 }
202
203 /// @brief Return a @c vector containing the test values @c [0,size) of @c T , parameterized by test suite users
204 /// @param storage The storage space to use for the values stored in the vector
205 /// @param size The number of test values to put into the test vector
206 /// @return A test vector holding the test values determined by the size
207 static constexpr auto test_external_vector(arene::base::span<arene::base::byte> storage, std::size_t size) noexcept(
209 ) -> vector {
210 return test_external_vector(storage, 0, size);
211 }
212
213 /// @brief Return a @c vector containing the test values @c [0,capacity) of @c T , parameterized by test suite users
214 /// @param storage The storage space to use for the values stored in the vector
215 /// @return A test vector holding the test values determined by the capacity
216 static constexpr auto test_external_vector(arene::base::span<arene::base::byte> storage
218 return test_external_vector(storage, capacity);
219 }
220};
221
222// An empty definition of this static variable is required until C++17.
223template <typename T>
225
226/// @brief The Death Tests use the same fixture as the "normal" tests. Inherits so that it will be a distinct type.
227/// @tparam T The type parameter of a given test instantiation
228template <typename T>
230
231// Declare the three test suites for Google Test; test cases will all be added to one of these.
234
235/// @test A default-constructed `external_vector` is empty
236TYPED_TEST_P(ExternalVectorTest, CanConstructAnEmptyVector) { ASSERT_TRUE(TestFixture::construct().empty()); }
237
238/// @test The capacity of a default-constructed `external_vector` is equal to the supplied template parameter
240 EXPECT_EQ(TestFixture::construct(TestFixture::standard_storage).capacity(), TestFixture::capacity);
241
242 constexpr std::size_t capacity2 = 19;
243 alignas(TypeParam) typename TestFixture::template storage_type<capacity2> storage2{};
244 ASSERT_TRUE(external_vector<TypeParam>{storage2}.capacity() == capacity2);
245}
246
247/// @test The `capacity` member function of `external_vector` is declared `noexcept`
248TYPED_TEST_P(ExternalVectorTest, CapacityIsNoexcept) {
249 STATIC_ASSERT_TRUE(noexcept(TestFixture::construct().capacity()));
250 STATIC_ASSERT_TRUE(noexcept(TestFixture::construct(TestFixture::standard_storage).capacity()));
251}
252
253/// @test The size of a default-constructed `external_vector` is zero
254TYPED_TEST_P(ExternalVectorTest, InitialSizeIsZero) {
255 ASSERT_EQ(TestFixture::construct().size(), 0);
256 ASSERT_EQ(TestFixture::construct(TestFixture::standard_storage).size(), 0);
257}
258
259/// @test With no storage, @c data returns a null pointer
260TYPED_TEST_P(ExternalVectorTest, NoStorageDataIsNull) { ASSERT_EQ(TestFixture::construct().data(), nullptr); }
261
262/// @test The size of an `external_vector` initialized from a list of values is correct
263TYPED_TEST_P(ExternalVectorTest, SizeIsCorrect) {
264 ASSERT_EQ(
265 (typename TestFixture::vector{
266 TestFixture::standard_storage,
267 {TestFixture::test_value(0), TestFixture::test_value(1)}
268 }.size()),
269 2
270 );
271}
272
273/// @test The `size` member function of `external_vector` is declared `noexcept`
274TYPED_TEST_P(ExternalVectorTest, SizeIsNoexcept) { STATIC_ASSERT_TRUE(noexcept(TestFixture::construct().size())); }
275
276/// @test Using `push_back` to add an item to a default-constructed `external_vector` increases the size
277TYPED_TEST_P(ExternalVectorTest, CanPushBackAValue) {
278 typename TestFixture::vector vec{TestFixture::standard_storage};
279 vec.push_back(TestFixture::test_value(0));
280 EXPECT_EQ(vec.size(), 1);
281}
282
283/// @test After using `push_back` to add an item to a default-constructed `external_vector`, that item can be retrieved
284/// via the `back` member function
286 typename TestFixture::vector vec{TestFixture::standard_storage};
287 TypeParam const value = TestFixture::test_value(0);
288 vec.push_back(value);
289 EXPECT_EQ(vec.back(), value);
290}
291
292/// @test After using `push_back` to add an item to a default-constructed `external_vector`, the vector is no longer
293/// empty
295 typename TestFixture::vector vec{TestFixture::standard_storage};
296 TypeParam const value = TestFixture::test_value(0);
297 vec.push_back(value);
298 ASSERT_FALSE(vec.empty());
299}
300
301/// @test After using `push_back` twice to add two items to a default-constructed `external_vector`, the size is
302/// incremented to 2, and the second item can be retrieved via the `back` member function
304 typename TestFixture::vector vec{TestFixture::standard_storage};
305 TypeParam const value1 = TestFixture::test_value(0);
306 vec.push_back(value1);
307 TypeParam const value2 = TestFixture::test_value(1);
308 vec.push_back(value2);
309 EXPECT_EQ(vec.size(), 2);
310 EXPECT_EQ(vec.back(), value2);
311}
312
313/// @test `push_back` is `noexcept` if and only if the value type can be constructed without throwing
315 constexpr bool copy_noexcept = std::is_nothrow_copy_constructible<TypeParam>{};
316 constexpr bool move_noexcept = std::is_nothrow_move_constructible<TypeParam>{};
317 STATIC_ASSERT_EQ(
318 noexcept(TestFixture::construct(std::declval<arene::base::span<arene::base::byte>>())
319 .push_back(std::declval<TypeParam const&>())),
320 copy_noexcept
321 );
322 STATIC_ASSERT_EQ(
323 noexcept(TestFixture::construct(std::declval<arene::base::span<arene::base::byte>>())
324 .push_back(std::declval<TypeParam&&>())),
325 move_noexcept
326 );
327}
328
329/// @test A default-constructed `external_vector` is empty
330TYPED_TEST_P(ExternalVectorTest, EmptybyDefault) { ASSERT_TRUE(TestFixture::construct().empty()); }
331
332/// @test A `external_vector` constructed with just storage is empty
333TYPED_TEST_P(ExternalVectorTest, EmptyIfJustStorage) {
334 ASSERT_TRUE(TestFixture::construct(TestFixture::standard_storage).empty());
335}
336
337/// @test The `empty` member function of `external_vector` is `noexcept`
338TYPED_TEST_P(ExternalVectorTest, EmptyIsNoexcept) { STATIC_ASSERT_TRUE(noexcept(TestFixture::construct().empty())); }
339
340/// @test The `at` member function throws a `std::out_of_range` exception when invoked with an index of 0 on
341/// a default-constructed `external_vector`
343#if ARENE_IS_ON(ARENE_EXCEPTIONS_ENABLED) // conditional removal of at() tests based on exception state.
344 ASSERT_THROW(TestFixture::construct().at(0), std::out_of_range);
345#else
346 GTEST_SKIP() << "`at` tests are skipped when exceptions are disabled.";
347#endif
348}
349
350/// @test The `at` member function returns the value stored when invoked with an index of 0 on a
351/// `external_vector` into which one element has been stored via `push_back`
353#if ARENE_IS_ON(ARENE_EXCEPTIONS_ENABLED) // conditional removal of at() tests based on exception state.
354 auto vec = TestFixture::construct(TestFixture::standard_storage);
355 TypeParam const value = TestFixture::test_value(0);
356 vec.push_back(value);
357 EXPECT_EQ(vec.at(0), value);
358#else
359 GTEST_SKIP() << "`at` tests are skipped when exceptions are disabled.";
360#endif
361}
362
363/// @test The return type of the `at` member function on a non-`const` `external_vector` is a non-`const` reference to
364/// `value_type`
365TYPED_TEST_P(ExternalVectorTest, AtReturnsReference) {
366#if ARENE_IS_ON(ARENE_EXCEPTIONS_ENABLED) // conditional removal of at() tests based on exception state.
367 ::testing::StaticAssertTypeEq<decltype(std::declval<typename TestFixture::vector&>().at(0)), TypeParam&>();
368#else
369 GTEST_SKIP() << "`at` tests are skipped when exceptions are disabled.";
370#endif
371}
372
373/// @test The return type of the `at` member function on a `const` `external_vector` is a `const` reference to
374/// `value_type`
376#if ARENE_IS_ON(ARENE_EXCEPTIONS_ENABLED) // conditional removal of at() tests based on exception state.
377 ::testing::StaticAssertTypeEq<decltype(std::declval<typename TestFixture::vector const&>().at(0)), TypeParam const&>(
378 );
379#else
380 GTEST_SKIP() << "`at` tests are skipped when exceptions are disabled.";
381#endif
382}
383
384/// @test The `at` member function returns the corresponding value stored when invoked with an index of 0 or
385/// 1 on an `external_vector` into which two elements have been stored via `push_back`
387#if ARENE_IS_ON(ARENE_EXCEPTIONS_ENABLED) // conditional removal of at() tests based on exception state.
388 auto vec = TestFixture::construct(TestFixture::standard_storage);
389 TypeParam const value1 = TestFixture::test_value(0);
390 vec.push_back(value1);
391 TypeParam const value2 = TestFixture::test_value(1);
392 vec.push_back(value2);
393
394 EXPECT_EQ(vec.at(0), value1);
395 EXPECT_EQ(vec.at(1), value2);
396#else
397 GTEST_SKIP() << "`at` tests are skipped when exceptions are disabled.";
398#endif
399}
400
401/// @test The `at` member function throws when invoked with an index that is equal to or larger than the size of the
402/// vector on an `external_vector` into which two elements have been stored via `push_back`
404#if ARENE_IS_ON(ARENE_EXCEPTIONS_ENABLED) // conditional removal of at() tests based on exception state.
405 auto vec = TestFixture::construct(TestFixture::standard_storage);
406 TypeParam const value1 = TestFixture::test_value(0);
407 vec.push_back(value1);
408 TypeParam const value2 = TestFixture::test_value(1);
409 vec.push_back(value2);
410
411 ASSERT_THROW(vec.at(vec.size()), std::out_of_range);
412 ASSERT_THROW(vec.at(vec.size() * 2), std::out_of_range);
413 ASSERT_THROW(vec.at(std::numeric_limits<std::size_t>::max()), std::out_of_range);
414#else
415 GTEST_SKIP() << "`at` tests are skipped when exceptions are disabled.";
416#endif
417}
418
419/// @test The `at` member function returns the corresponding value stored when invoked via a `const` reference to a
420/// vector with an index of 0 or 1 on an `external_vector` into which two elements have been stored via `push_back`.
422#if ARENE_IS_ON(ARENE_EXCEPTIONS_ENABLED) // conditional removal of at() tests based on exception state.
423 auto vec = TestFixture::construct(TestFixture::standard_storage);
424 TypeParam const value1 = TestFixture::test_value(0);
425 vec.push_back(value1);
426 TypeParam const value2 = TestFixture::test_value(1);
427 vec.push_back(value2);
428
429 auto const& const_vec = vec;
430 EXPECT_EQ(const_vec.at(0), value1);
431 EXPECT_EQ(const_vec.at(1), value2);
432#else
433 GTEST_SKIP() << "`at` tests are skipped when exceptions are disabled.";
434#endif
435}
436
437/// @test The `at` member function throws when invoked via a `const` reference to a vector with an index that is equal
438/// to or larger than the size of an `external_vector` into which two elements have been stored via `push_back`
440#if ARENE_IS_ON(ARENE_EXCEPTIONS_ENABLED) // conditional removal of at() tests based on exception state.
441 auto vec = TestFixture::construct(TestFixture::standard_storage);
442 TypeParam const value1 = TestFixture::test_value(0);
443 vec.push_back(value1);
444 TypeParam const value2 = TestFixture::test_value(1);
445 vec.push_back(value2);
446
447 auto const& const_vec = vec;
448
449 ASSERT_THROW(const_vec.at(vec.size()), std::out_of_range);
450 ASSERT_THROW(const_vec.at(vec.size() * 2), std::out_of_range);
451 ASSERT_THROW(const_vec.at(std::numeric_limits<std::size_t>::max()), std::out_of_range);
452#else
453 GTEST_SKIP() << "`at` tests are skipped when exceptions are disabled.";
454#endif
455}
456
457/// @test The `max_size` member function of `external_vector` returns the capacity of the storage
459 ASSERT_EQ(TestFixture::construct(TestFixture::standard_storage).max_size(), TestFixture::capacity);
460 ASSERT_EQ(TestFixture::construct().max_size(), 0);
461 ASSERT_TRUE(noexcept(TestFixture::construct().max_size()));
462
463 constexpr std::size_t capacity2 = 19;
464 alignas(TypeParam) typename TestFixture::template storage_type<capacity2> storage2{};
465 ASSERT_EQ(typename TestFixture::vector{storage2}.max_size(), capacity2);
466}
467
468/// @test Invoking `push_back` on a vector that is at capacity terminates the program with a precondition violation.
470 auto vec = TestFixture::construct(TestFixture::standard_storage);
471 for (std::size_t i = 0; i < vec.max_size(); ++i) {
472 vec.push_back(TestFixture::test_value(0));
473 }
474 ASSERT_DEATH(vec.push_back(TestFixture::test_value(0)), "Precondition violation");
475}
476
477/// @test The size of a default-constructed vector with a capacity of zero is zero, when obtained via the
478/// `size` member function, and via comparing the iterators returned from `begin` and `end`
480 external_vector<TypeParam> const zvec{};
481 external_vector<TypeParam const> const czvec{};
482 ASSERT_EQ(zvec.begin(), zvec.end());
483 ASSERT_EQ(czvec.begin(), czvec.end());
484 ASSERT_EQ(zvec.size(), 0);
485 ASSERT_EQ(czvec.size(), 0);
486}
487
488/// @test Invoking `push_back` on a zero-capacity vector terminates the program with a precondition violation
490 external_vector<TypeParam> vec{};
491 ASSERT_DEATH(vec.push_back(TestFixture::test_value(0)), "Precondition violation");
492}
493
494/// @test Given a default-constructed `external_vector`, after one element has been added via `push_back`, `back`
495/// returns a reference to that element. After a second element has been added via `push_back`, `back` now returns a
496/// reference to the second element.
497TYPED_TEST_P(ExternalVectorTest, BackReturnsReference) {
498 auto vec = TestFixture::construct(TestFixture::standard_storage);
499
500 TypeParam const value1 = TestFixture::test_value(0);
501 vec.push_back(value1);
502 TypeParam& val1 = vec.back();
503
504 TypeParam const value2 = TestFixture::test_value(1);
505 vec.push_back(value2);
506 TypeParam& val2 = vec.back();
507
508 EXPECT_EQ(std::addressof(val1), std::addressof(vec[0]));
509 EXPECT_EQ(std::addressof(val2), std::addressof(vec[1]));
510}
511
512/// @test Given a default-constructed `external_vector`, after one element has been added via `push_back`, `back` called
513/// via a `const` reference to the vector returns a `const` reference to that element. After a second element has been
514/// added via `push_back`, `back` called via a `const` reference to the vector now returns a `const` reference to the
515/// second element.
517 auto vec = TestFixture::construct(TestFixture::standard_storage);
518 auto const& const_vec = vec;
519
520 TypeParam const value1 = TestFixture::test_value(0);
521 vec.push_back(value1);
522 auto& val1 = const_vec.back();
523 ::testing::StaticAssertTypeEq<decltype(val1), TypeParam const&>();
524
525 TypeParam const value2 = TestFixture::test_value(1);
526 vec.push_back(value2);
527 auto& val2 = const_vec.back();
528 ::testing::StaticAssertTypeEq<decltype(val2), TypeParam const&>();
529
530 EXPECT_EQ(std::addressof(val1), &const_vec[0]);
531 EXPECT_EQ(std::addressof(val2), &const_vec[1]);
532}
533
534/// @test Invoking `back` cannot throw
535TYPED_TEST_P(ExternalVectorTest, BackIsNoexcept) {
536 STATIC_ASSERT_TRUE(noexcept(std::declval<typename TestFixture::vector&>().back()));
537 STATIC_ASSERT_TRUE(noexcept(std::declval<typename TestFixture::vector const&>().back()));
538}
539
540/// @test An `external_vector` can be constructed from a `std::initializer_list` without throwing exceptions, and the
541/// size and values stored in the vector are the same as the size and values of the initializer list.
543 std::initializer_list<TypeParam> const values{
544 TestFixture::test_value(0),
545 TestFixture::test_value(1),
546 TestFixture::test_value(2),
547 TestFixture::test_value(3)
548 };
549
550 typename TestFixture::vector const vec{TestFixture::standard_storage, values};
551 STATIC_ASSERT_EQ(
552 noexcept(typename TestFixture::vector{TestFixture::standard_storage, values}),
553 std::is_nothrow_copy_constructible<TypeParam>{}
554 );
555
556 EXPECT_THAT(vec, ::testing::ElementsAreArray(values));
557}
558
559/// @test Given an `external_vector` initialized from an initializer list, the `front` member function can be used to
560/// retrieve the first value, which must be equal to the first value of the vector as retrieved via `at(0)`
561TYPED_TEST_P(ExternalVectorTest, CanGetFront) {
562 auto vec = TestFixture::test_external_vector(TestFixture::standard_storage, 4);
563 EXPECT_EQ(std::addressof(vec.front()), std::addressof(vec[0]));
564 ::testing::StaticAssertTypeEq<decltype(vec.front()), TypeParam&>();
565 STATIC_ASSERT_TRUE(noexcept(vec.front()));
566 auto const& const_vec = vec;
567 EXPECT_EQ(&const_vec.front(), &const_vec[0]);
568 ::testing::StaticAssertTypeEq<decltype(const_vec.front()), TypeParam const&>();
569 STATIC_ASSERT_TRUE(noexcept(const_vec.front()));
570}
571
572/// @test The `begin` and `end` member functions of an `external_vector` must return an `iterator`, and they must be
573/// equal for a default-constructed vector
574TYPED_TEST_P(ExternalVectorTest, CanGetIterator) {
575 auto vec = TestFixture::construct(TestFixture::standard_storage);
576 ::testing::StaticAssertTypeEq<decltype(vec.begin()), typename TestFixture::vector::iterator>();
577 ::testing::StaticAssertTypeEq<decltype(vec.end()), typename TestFixture::vector::iterator>();
578 STATIC_ASSERT_TRUE(noexcept(vec.begin()));
579 STATIC_ASSERT_TRUE(noexcept(vec.end()));
580 EXPECT_EQ(vec.begin(), vec.end());
581}
582
583/// @test Given an `external_vector` constructed from an initializer list, the iterators returned from `begin` and `end`
584/// can be passed as an iterator pair to `std::mismatch` to compare the values in the vector to those in the initializer
585/// list, and the return value of the call to `std::mismatch` must be the end iterators of the vector and initializer
586/// list, to indicate that the values are the same.
587TYPED_TEST_P(ExternalVectorTest, CanIterateOverValues) {
588 std::initializer_list<TypeParam> const values{
589 TestFixture::test_value(0),
590 TestFixture::test_value(1),
591 TestFixture::test_value(2),
592 TestFixture::test_value(3)
593 };
594 typename TestFixture::vector vec{
595 TestFixture::standard_storage,
596 values
597 }; // NOLINT(misc-const-correctness) Explicitly testing lvalues
598 auto const result = std::mismatch(values.begin(), values.end(), vec.begin(), vec.end());
599 EXPECT_EQ(result.first, values.end());
600 EXPECT_EQ(result.second, vec.end());
601}
602
603/// @test Given a `const` `external_vector` constructed from an initializer list, the iterators returned from `begin`
604/// and `end` can be passed as an iterator pair to `std::mismatch` to compare the values in the vector to those in the
605/// initializer list, and the return value of the call to `std::mismatch` must be the end iterators of the vector and
606/// initializer list, to indicate that the values are the same.
608 std::initializer_list<TypeParam> const values{
609 TestFixture::test_value(0),
610 TestFixture::test_value(1),
611 TestFixture::test_value(2),
612 TestFixture::test_value(3)
613 };
614 typename TestFixture::vector const vec{TestFixture::standard_storage, values};
615 auto const result = std::mismatch(values.begin(), values.end(), vec.begin(), vec.end());
616 EXPECT_EQ(result.first, values.end());
617 EXPECT_EQ(result.second, vec.end());
618}
619
620/// @test The `iterator` type of an `external_vector` must have the required type aliases for a random-access iterator.
621TYPED_TEST_P(ExternalVectorTest, IteratorTypedefs) {
622 using vec = typename TestFixture::vector;
623 ::testing::StaticAssertTypeEq<typename std::iterator_traits<typename vec::iterator>::value_type, TypeParam>();
624 ::testing::StaticAssertTypeEq<typename std::iterator_traits<typename vec::iterator>::reference, TypeParam&>();
625 ::testing::StaticAssertTypeEq<typename std::iterator_traits<typename vec::iterator>::pointer, TypeParam*>();
626 ::testing::StaticAssertTypeEq<typename std::iterator_traits<typename vec::iterator>::difference_type, std::ptrdiff_t>(
627 );
628 ::testing::StaticAssertTypeEq<
629 typename std::iterator_traits<typename vec::iterator>::iterator_category,
630 std::random_access_iterator_tag>();
631
632 ::testing::StaticAssertTypeEq<typename std::iterator_traits<typename vec::const_iterator>::value_type, TypeParam>();
633 ::testing::
634 StaticAssertTypeEq<typename std::iterator_traits<typename vec::const_iterator>::reference, TypeParam const&>();
635 ::testing::StaticAssertTypeEq<typename std::iterator_traits<typename vec::const_iterator>::pointer, TypeParam const*>(
636 );
637 ::testing::
638 StaticAssertTypeEq<typename std::iterator_traits<typename vec::const_iterator>::difference_type, std::ptrdiff_t>(
639 );
640 ::testing::StaticAssertTypeEq<
641 typename std::iterator_traits<typename vec::const_iterator>::iterator_category,
642 std::random_access_iterator_tag>();
643}
644
645/// @test `external_vector` must have the required type aliases for a sequence container
646TYPED_TEST_P(ExternalVectorTest, Typedefs) {
647 using vec = typename TestFixture::vector;
648 ::testing::StaticAssertTypeEq<typename vec::value_type, TypeParam>();
649 ::testing::StaticAssertTypeEq<typename vec::pointer, TypeParam*>();
650 ::testing::StaticAssertTypeEq<typename vec::const_pointer, TypeParam const*>();
651 ::testing::StaticAssertTypeEq<typename vec::reference, TypeParam&>();
652 ::testing::StaticAssertTypeEq<typename vec::const_reference, TypeParam const&>();
653 ::testing::StaticAssertTypeEq<typename vec::size_type, std::size_t>();
654 ::testing::StaticAssertTypeEq<typename vec::difference_type, std::ptrdiff_t>();
655 ::testing::
656 StaticAssertTypeEq<typename vec::reverse_iterator, ::arene::base::reverse_iterator<typename vec::iterator>>();
657 ::testing::StaticAssertTypeEq<
658 typename vec::const_reverse_iterator,
659 ::arene::base::reverse_iterator<typename vec::const_iterator>>();
660}
661
662template <typename Vec, typename... Args>
663constexpr auto constexpr_iterate(Args&&... args) -> bool {
664 std::initializer_list<typename Vec::value_type> const values{std::forward<Args>(args)...};
665 Vec vec{std::forward<Args>(args)...};
666
667 // This test is specifically testing a range-based for loop, so we do that even though the iteration gets weird
668 auto val_it = values.begin();
669 for (auto const& vec_val : vec) {
670 if (val_it == values.end()) {
671 return false; // The ranges are unequal if this happens while there are still values in `vec`
672 }
673 if (vec_val != *val_it) {
674 return false;
675 }
676 ++val_it;
677 }
678
679 return val_it == values.end(); // If this reached the end and all elements were equal, the ranges are equal
680}
681
682/// @test Given a non-empty `external_vector`, invoking `erase` with the iterator returned from `begin` removes the
683/// first element, moving the remaining elements to one lower index and decreasing the size by one, and returns an
684/// iterator to the new first element.
685TYPED_TEST_P(ExternalVectorTest, CanEraseAtBeginning) {
686 TypeParam const value1 = TestFixture::test_value(0);
687 TypeParam const value2 = TestFixture::test_value(1);
688 TypeParam const value3 = TestFixture::test_value(2);
689 TypeParam const value4 = TestFixture::test_value(3);
690
691 typename TestFixture::vector vec{TestFixture::standard_storage, {value1, value2, value3, value4}};
692
693 STATIC_ASSERT_TRUE(std::is_same<
694 decltype(vec.erase(std::declval<typename TestFixture::vector::const_iterator>())),
695 typename TestFixture::vector::iterator>::value);
696 auto const pos = vec.erase(vec.begin());
697 EXPECT_EQ(pos, vec.begin());
698 EXPECT_EQ(vec.size(), 3);
699 EXPECT_EQ(vec[0], value2);
700 EXPECT_EQ(vec[1], value3);
701 EXPECT_EQ(vec[2], value4);
702}
703
704/// @test Given a non-empty `external_vector`, invoking `erase` with an iterator referring to an element that is neither
705/// the first or last element removes that element, moves the following elements to one lower index, reduces the size by
706/// one, and returns an iterator to the element that replaced the erased element.
707TYPED_TEST_P(ExternalVectorTest, CanEraseInMiddle) {
708 TypeParam const value1 = TestFixture::test_value(0);
709 TypeParam const value2 = TestFixture::test_value(1);
710 TypeParam const value3 = TestFixture::test_value(2);
711 TypeParam const value4 = TestFixture::test_value(3);
712
713 typename TestFixture::vector vec{TestFixture::standard_storage, {value1, value2, value3, value4}};
714
715 STATIC_ASSERT_TRUE(std::is_same<
716 decltype(vec.erase(std::declval<typename TestFixture::vector::const_iterator>())),
717 typename TestFixture::vector::iterator>::value);
718 auto pos = vec.erase(vec.begin() + 1);
719 EXPECT_EQ(pos, vec.begin() + 1);
720 EXPECT_EQ(vec.size(), 3);
721 EXPECT_EQ(vec[0], value1);
722 EXPECT_EQ(vec[1], value3);
723 EXPECT_EQ(vec[2], value4);
724}
725
726/// @test Invoking `erase` with an iterator range referring to elements in the same `external_vector`, removes those
727/// elements, moves the following elements to take their place, and returns an iterator with the same offset from the
728/// beginning of the vector as the first erased element
729TYPED_TEST_P(ExternalVectorTest, CanEraseRange) {
730 auto vec = TestFixture::construct(TestFixture::standard_storage);
731 constexpr std::size_t count = 10;
732 constexpr std::size_t erase_start = 2;
733 constexpr std::size_t erase_end = 5;
734 std::vector<TypeParam> expected;
735 for (std::size_t idx = 0; idx < count; ++idx) {
736 vec.push_back(TestFixture::test_value(idx));
737 if (idx < erase_start || idx >= erase_end) {
738 expected.push_back(TestFixture::test_value(idx));
739 }
740 }
741
742 ::testing::StaticAssertTypeEq<
743 decltype(vec.erase(
744 std::declval<typename TestFixture::vector::const_iterator>(),
745 std::declval<typename TestFixture::vector::const_iterator>()
746 )),
747 typename TestFixture::vector::iterator>();
748 auto const pos = vec.erase(vec.begin() + erase_start, vec.begin() + erase_end);
749 EXPECT_EQ(pos, vec.begin() + erase_start);
750 EXPECT_THAT(vec, ::testing::ElementsAreArray(expected));
751}
752
753/// @test Constructing with just a size creates a vector with that many default-constructed elements
755 constexpr std::size_t count = 3;
756 auto value_vec = TestFixture::construct(TestFixture::standard_storage, count);
757
758 STATIC_ASSERT_EQ(
759 noexcept(typename TestFixture::vector(TestFixture::standard_storage, count)),
760 std::is_nothrow_default_constructible<TypeParam>{}
761 );
762 EXPECT_EQ(value_vec.size(), count);
763 EXPECT_THAT(value_vec, ::testing::Each(::testing::Eq(TypeParam{})));
764}
765
766/// @test Constructing an `external_vector` with a count and a value creates the specified number of elements
767/// copy-constructed from the supplied value, and sets the `size` of the vector to that count.
769 constexpr std::size_t count = 14;
770 TypeParam const value = TestFixture::test_value(0);
771 auto const value_vec = TestFixture::construct(TestFixture::standard_storage, count, value);
772
773 STATIC_ASSERT_EQ(
774 noexcept(typename TestFixture::vector(TestFixture::standard_storage, count, value)),
775 std::is_nothrow_copy_constructible<TypeParam>{}
776 );
777 EXPECT_EQ(value_vec.size(), count);
778 EXPECT_THAT(value_vec, ::testing::Each(::testing::Eq(value)));
779}
780
781/// @test The `external_vector` copy constructor copies the stored elements and size from the original vector.
783 auto const vec = TestFixture::test_external_vector(TestFixture::standard_storage, 6);
784 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
785 // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
786 typename TestFixture::vector const copy(storage2, vec);
787
788 EXPECT_THAT(copy, ::testing::ElementsAreArray(vec));
789}
790
791/// @test The `external_vector` move constructor moves the stored elements from the original vector
793 std::initializer_list<TypeParam> const values = {
794 TestFixture::test_value(0),
795 TestFixture::test_value(1),
796 TestFixture::test_value(2),
797 TestFixture::test_value(3),
798 TestFixture::test_value(4)
799 };
800 typename TestFixture::vector vec(TestFixture::standard_storage, values);
801 // NOLINTNEXTLINE(hicpp-move-const-arg)
802 typename TestFixture::vector const moved(std::move(vec));
803
804 EXPECT_THAT(moved, ::testing::ElementsAreArray(values));
805}
806
807/// @test The `external_vector` move constructor with new storage moves the stored elements from the original vector
809 std::initializer_list<TypeParam> const values = {
810 TestFixture::test_value(0),
811 TestFixture::test_value(1),
812 TestFixture::test_value(2),
813 TestFixture::test_value(3),
814 TestFixture::test_value(4)
815 };
816 typename TestFixture::vector vec(TestFixture::standard_storage, values);
817
818 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
819 // NOLINTNEXTLINE(hicpp-move-const-arg)
820 typename TestFixture::vector const moved(storage2, std::move(vec));
821
822 EXPECT_THAT(moved, ::testing::ElementsAreArray(values));
823}
824
825/// @test The `external_vector` copy-assignment operator uses copy assignment for elements from the source where there
826/// is already a corresponding element in the destination, and copy construction for elements that do not have a
827/// corresponding element in the destination
829 constexpr std::size_t count = 14;
830 constexpr std::size_t smaller_count = 10;
831
832 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
833 auto vec = TestFixture::test_external_vector(TestFixture::standard_storage, count);
834 auto copy = TestFixture::test_external_vector(storage2, smaller_count);
835 copy = vec;
836
837 ASSERT_EQ(vec.size(), count);
838 ASSERT_EQ(copy.size(), count);
839 for (std::size_t i = 0; i < count; ++i) {
840 EXPECT_EQ(vec[i], copy[i]);
841 EXPECT_EQ(TestFixture::test_value(i), copy[i]);
842 }
843}
844
845/// @test The `external_vector` copy-assignment operator destroys the excess elements in the destination when the source
846/// vector has fewer elements than the destination vector
848 constexpr std::size_t count = 14;
849 constexpr std::size_t larger_count = 18;
850
851 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
852 auto vec = TestFixture::test_external_vector(TestFixture::standard_storage, count);
853 auto copy = TestFixture::test_external_vector(storage2, larger_count);
854 copy = vec;
855
856 ASSERT_EQ(vec.size(), count);
857 ASSERT_EQ(copy.size(), count);
858 for (std::size_t i = 0; i < count; ++i) {
859 EXPECT_EQ(vec[i], copy[i]);
860 EXPECT_EQ(TestFixture::test_value(i), copy[i]);
861 }
862}
863
864/// @test Copy-assignment for `external_vector` is `noexcept` if and only if the stored element type is both
865/// nothrow-copy-constructible and nothrow-copy-assignable
867 using vec = ::arene::base::external_vector<TypeParam>;
868
869 constexpr bool should_be_copyable = std::is_copy_constructible<TypeParam>{} && std::is_copy_assignable<TypeParam>{};
870 STATIC_ASSERT_FALSE(std::is_copy_constructible<vec>{});
871 STATIC_ASSERT_EQ(std::is_copy_assignable<vec>{}, should_be_copyable);
872
873 constexpr bool should_be_noexcept =
874 std::is_nothrow_copy_constructible<TypeParam>{} && std::is_nothrow_copy_assignable<TypeParam>{};
875 STATIC_ASSERT_FALSE(std::is_nothrow_copy_constructible<vec>{});
876 STATIC_ASSERT_EQ(std::is_nothrow_copy_assignable<vec>{}, should_be_noexcept);
877}
878
879/// @test The `external_vector` move-assignment operator uses move assignment for elements from the source where there
880/// is already a corresponding element in the destination, and move construction for elements that do not have a
881/// corresponding element in the destination
883 constexpr std::size_t count = 14;
884 constexpr std::size_t smaller_count = 10;
885
886 auto first = TestFixture::construct(TestFixture::standard_storage);
887 std::vector<TypeParam> expected;
888 for (std::size_t idx = 0; idx < count; ++idx) {
889 first.push_back(TestFixture::test_value(idx));
890 expected.push_back(TestFixture::test_value(idx));
891 }
892
893 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
894 auto second = TestFixture::test_external_vector(storage2, smaller_count);
895 // NOLINTNEXTLINE(hicpp-move-const-arg)
896 second = std::move(first);
897
898 EXPECT_EQ(second.size(), count);
899 EXPECT_THAT(second, ::testing::ElementsAreArray(expected));
900}
901
902/// @test The `external_vector` move-assignment operator destroys the excess elements in the destination when the source
903/// vector has fewer elements than the destination vector
905 constexpr std::size_t count = 14;
906 constexpr std::size_t larger_count = 18;
907
908 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
909 auto first = TestFixture::test_external_vector(TestFixture::standard_storage, count);
910 auto second = TestFixture::test_external_vector(storage2, larger_count);
911 second = std::move(first);
912
913 ASSERT_EQ(second.size(), count);
914 for (std::size_t i = 0; i < count; ++i) {
915 EXPECT_EQ(TestFixture::test_value(i), second[i]);
916 }
917}
918
919/// @test Move-assignment for `external_vector` is `noexcept` if and only if the value type is
920/// nothrow-move-constructible and nothrow-move-assignable
922 using vec = ::arene::base::external_vector<TypeParam>;
923
924 constexpr bool should_be_movable = std::is_move_assignable<TypeParam>{} && std::is_move_constructible<TypeParam>{};
925 STATIC_ASSERT_TRUE(std::is_move_constructible<vec>{});
926 STATIC_ASSERT_EQ(std::is_move_assignable<vec>{}, should_be_movable);
927
928 constexpr bool should_be_noexcept =
929 std::is_nothrow_move_assignable<TypeParam>{} && std::is_nothrow_move_constructible<TypeParam>{};
930 STATIC_ASSERT_TRUE(std::is_nothrow_move_constructible<vec>{});
931 STATIC_ASSERT_EQ(std::is_nothrow_move_assignable<vec>{}, should_be_noexcept);
932}
933
934// A Matcher that checks if the elements in an argument (by index) match the result of calling a function with the index
935// NOLINTNEXTLINE(modernize-use-trailing-return-type) The declaration happens in a macro and we can't control it
937 for (std::size_t ii = 0; ii < arg.size(); ++ii) {
938 if (arg[ii] != function(ii)) {
939 return false;
940 }
941 }
942
943 return true;
944}
945
946/// @test Invoking `pop_back` on a non-empty `external_vector` destroys the last element and reduces the size by one.
947TYPED_TEST_P(ExternalVectorTest, CanPopBack) {
948 auto vec = TestFixture::test_external_vector(TestFixture::standard_storage, 5);
949
950 vec.push_back(TestFixture::test_value(5));
951 vec.pop_back();
952
953 EXPECT_THAT(vec, elements_match_function(TestFixture::test_value));
954}
955
956/// @test `pop_back` is declared `noexcept`
957TYPED_TEST_P(ExternalVectorTest, PopBackIsNoexcept) {
958 STATIC_ASSERT_TRUE(noexcept(std::declval<typename TestFixture::vector>().pop_back()));
959}
960
961/// @test `erase` is `noexcept` if and only if the element type has `noexcept` move operations
963 // NOLINTBEGIN(misc-const-correctness) can't see through static_assert usage
964 auto vec = TestFixture::construct(TestFixture::standard_storage);
965 // NOLINTEND(misc-const-correctness)
966
967 STATIC_ASSERT_TRUE(noexcept(vec.erase(vec.begin(), vec.end())));
968 STATIC_ASSERT_TRUE(noexcept(vec.erase(vec.begin())));
969}
970
971/// @test Given an empty `external_vector`, and the iterator returned from `begin`, `insert` can add an element to the
972/// vector, setting the size to one.
974 TypeParam const value = TestFixture::test_value(0);
975 auto vec1 = TestFixture::construct(TestFixture::standard_storage);
976 auto const& const_vec1 = vec1;
977 auto const itr = vec1.insert(const_vec1.begin(), value);
978 EXPECT_EQ(itr, vec1.begin());
979 EXPECT_EQ(vec1.size(), 1);
980 EXPECT_EQ(vec1[0], value);
981}
982
983/// @test Given an `external_vector` with existing elements, and the iterator returned from `end`, `insert` can add an
984/// element to the end of the vector, increasing the size by one
986 TypeParam const value0 = TestFixture::test_value(0);
987 TypeParam const value1 = TestFixture::test_value(1);
988 typename TestFixture::vector vec1{TestFixture::standard_storage, {value0}};
989 auto const& const_vec1 = vec1;
990 auto const pos = vec1.insert(const_vec1.end(), value1);
991 EXPECT_EQ(pos, vec1.begin() + 1);
992 EXPECT_EQ(vec1.size(), 2);
993 EXPECT_EQ(vec1[0], value0);
994 EXPECT_EQ(vec1[1], value1);
995}
996
997/// @test Given an `external_vector` with existing elements, and an iterator to an existing element in the vector,
998/// `insert` can add an element at the iterator position, increasing the vector size by one. The existing element at the
999/// location referenced by the iterator, and the subsequent elements, are moved to make room for the new element.
1001 TypeParam const value0 = TestFixture::test_value(0);
1002 TypeParam const value1 = TestFixture::test_value(1);
1003 TypeParam const value2 = TestFixture::test_value(2);
1004 TypeParam const value3 = TestFixture::test_value(3);
1005 TypeParam const value4 = TestFixture::test_value(4);
1006 typename TestFixture::vector vec1{TestFixture::standard_storage, {value0, value1, value2, value4}};
1007 auto const& const_vec1 = vec1;
1008 auto const pos = vec1.insert(const_vec1.begin() + 1, value3);
1009 EXPECT_EQ(pos, vec1.begin() + 1);
1010 EXPECT_EQ(vec1.size(), 5);
1011 EXPECT_EQ(vec1[0], value0);
1012 EXPECT_EQ(vec1[1], value3);
1013 EXPECT_EQ(vec1[2], value1);
1014 EXPECT_EQ(vec1[3], value2);
1015 EXPECT_EQ(vec1[4], value4);
1016}
1017
1018/// @test Invoking `insert` on an `external_vector` with `size` equal to the capacity terminates the program with a
1019/// precondition violation
1021 TypeParam const value1 = TestFixture::test_value(0);
1022 TypeParam const value2 = TestFixture::test_value(1);
1023 auto vec1 = TestFixture::construct(TestFixture::standard_storage, TestFixture::capacity, value1);
1024 EXPECT_EQ(vec1.size(), TestFixture::capacity);
1025 ASSERT_DEATH(vec1.insert(vec1.end(), value2), "Precondition violation");
1026 ASSERT_DEATH(vec1.insert(vec1.begin(), value2), "Precondition violation");
1027 EXPECT_EQ(vec1.size(), TestFixture::capacity);
1028 for (auto& value : vec1) {
1029 EXPECT_EQ(value, value1);
1030 }
1031}
1032
1033/// @test Invoking the `external_vector` constructor with a count larger than the capacity terminates the program with a
1034/// precondition violation
1036 ExternalVectorDeathTest,
1039) {
1040 ASSERT_DEATH(
1041 TestFixture::construct(TestFixture::standard_storage, TestFixture::capacity + 1),
1042 "Precondition violation"
1043 );
1044}
1045
1046/// @test Invoking the `external_vector` constructor with a count and value where the count is larger than the capacity
1047/// terminates the program with a precondition violation
1049 TypeParam const source = TestFixture::test_value(0);
1050 ASSERT_DEATH(
1051 TestFixture::construct(TestFixture::standard_storage, TestFixture::capacity + 1, source),
1052 "Precondition violation"
1053 );
1054}
1055
1056/// @test Invoking the `external_vector` constructor with an initializer list with more elements than the capacity
1057/// terminates the program with a precondition violation
1059 constexpr std::size_t small_capacity = 4;
1060 using vec = external_vector<TypeParam>;
1061 TypeParam const value = TestFixture::test_value(0);
1062
1063 alignas(TypeParam) typename TestFixture::template storage_type<small_capacity> storage{};
1064 ASSERT_DEATH((vec{storage, {value, value, value, value, value}}), "Precondition violation");
1065}
1066
1067template <typename Vec>
1068constexpr auto constexpr_resize(Vec vec, std::size_t size) -> Vec {
1069 vec.resize(size);
1070 return vec;
1071}
1072
1073/// @test `resize` is `noexcept` if and only if the default constructor of the element type is `noexcept`
1075 ExternalVectorTest,
1078) {
1079 STATIC_ASSERT_EQ(noexcept(TestFixture::construct().resize(0)), std::is_nothrow_default_constructible<TypeParam>{});
1080}
1081
1082/// @test `resize` with a value to copy is `noexcept` if and only if the copy constructor of the element type is
1083/// `noexcept`
1085 STATIC_ASSERT_EQ(
1086 noexcept(std::declval<typename TestFixture::vector&>().resize(0, TestFixture::test_value(0))),
1087 std::is_nothrow_copy_constructible<TypeParam>{}
1088 );
1089}
1090
1091/// @test Invoking the `resize` member function with a size and a value on a vector with more elements than the
1092/// specified size sets the size of the vector to the specified count and does not construct new elements
1094 constexpr std::size_t initial_count = 5;
1095 TypeParam const initial_value = TestFixture::test_value(0);
1096 typename TestFixture::vector vec(TestFixture::standard_storage, initial_count, initial_value);
1097 constexpr std::size_t count = 3;
1098 TypeParam const value = TestFixture::test_value(1);
1099 vec.resize(count, value);
1100 ASSERT_EQ(vec.size(), count);
1101
1102 for (std::size_t idx = 0; idx < count; ++idx) {
1103 EXPECT_EQ(vec[idx], initial_value);
1104 }
1105}
1106
1107/// @test Invoking the `resize` member function with a size and a value on a vector with fewer elements than the
1108/// specified size sets the size of the vector to the specified count, and constructs a number of new elements
1109/// equal to the difference between the specified size and the previous size by copy-construction from the specified
1110/// value
1112 constexpr std::size_t initial_count = 5;
1113 TypeParam const initial_value = TestFixture::test_value(0);
1114 typename TestFixture::vector vec(TestFixture::standard_storage, initial_count, initial_value);
1115 constexpr std::size_t count = 17;
1116 TypeParam const value = TestFixture::test_value(1);
1117 vec.resize(count, value);
1118 ASSERT_EQ(vec.size(), count);
1119
1120 for (std::size_t idx = 0; idx < initial_count; ++idx) {
1121 EXPECT_EQ(vec[idx], initial_value);
1122 }
1123 for (std::size_t idx = initial_count; idx < count; ++idx) {
1124 EXPECT_EQ(vec[idx], value);
1125 }
1126}
1127
1128/// @test An `external_vector` can be constructed from an iterator range, creating a vector holding copies of the
1129/// elements from the source range, with a size equal to the number of elements in the source range
1131 auto const source = TestFixture::test_external_vector(TestFixture::standard_storage, 4);
1132
1133 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
1134 typename TestFixture::vector vec(storage2, source.begin(), source.end());
1135
1136 EXPECT_EQ(vec.size(), source.size());
1137 for (std::size_t i = 0; i < vec.size(); ++i) {
1138 EXPECT_EQ(vec[i], source[i]);
1139 }
1140}
1141
1142/// @test Construction of an `external_vector` from an iterator range is `noexcept` if operations on the iterator are
1143/// `noexcept` and the element type of the vector is nothrow-constructible from the reference type of the iterator
1145 using source_it = typename std::vector<TypeParam>::const_iterator;
1146 using source_val = typename source_it::reference;
1147
1148 constexpr bool it_operations_noexcept = noexcept(++std::declval<source_it>())&& noexcept(*std::declval<source_it>());
1149 constexpr bool element_construction_noexcept =
1150 std::is_nothrow_constructible<typename TestFixture::vector::value_type, source_val>{};
1151
1152 STATIC_ASSERT_TRUE((
1153 std::is_constructible<typename TestFixture::vector, arene::base::span<arene::base::byte>, source_it, source_it>{}
1154 ));
1155 STATIC_ASSERT_EQ(
1156 (std::is_nothrow_constructible<
1157 typename TestFixture::vector,
1158 arene::base::span<arene::base::byte>,
1159 source_it,
1160 source_it>{}),
1161 it_operations_noexcept && element_construction_noexcept
1162 );
1163
1164 using throws_when_converted = typename TestFixture::throws_when_converted_to_t;
1165 using convertible_array = ::arene::base::array<throws_when_converted, TestFixture::capacity>;
1166 using throwing_bidirectional_iterator = typename TestFixture::throwing_bidirectional_iterator_of_t;
1167
1168 STATIC_ASSERT_TRUE(std::is_constructible<
1169 typename TestFixture::vector,
1170 arene::base::span<arene::base::byte>,
1171 typename convertible_array::const_iterator,
1172 typename convertible_array::const_iterator>::value);
1173 STATIC_ASSERT_FALSE(std::is_nothrow_constructible<
1174 typename TestFixture::vector,
1175 arene::base::span<arene::base::byte>,
1176 typename convertible_array::const_iterator,
1177 typename convertible_array::const_iterator>::value);
1178 STATIC_ASSERT_TRUE(std::is_constructible<
1179 typename TestFixture::vector,
1180 arene::base::span<arene::base::byte>,
1181 throwing_bidirectional_iterator,
1182 throwing_bidirectional_iterator>::value);
1183 STATIC_ASSERT_FALSE(std::is_nothrow_constructible<
1184 typename TestFixture::vector,
1185 arene::base::span<arene::base::byte>,
1186 throwing_bidirectional_iterator,
1187 throwing_bidirectional_iterator>::value);
1188}
1189
1190/// @test Construction of an `external_vector` from an input iterator range is `noexcept` if operations on the iterator
1191/// are `noexcept` and the element type of the vector is nothrow-constructible from the reference type of the iterator
1193 using stream_it = std::istream_iterator<TypeParam>;
1194 using throws_when_converted = typename TestFixture::throws_when_converted_to_t;
1195 using throwing_it_throwing_type = typename TestFixture::template throwing_input_iterator<throws_when_converted>;
1196 using throwing_it_raw_type = typename TestFixture::template throwing_input_iterator<TypeParam>;
1197 using nothrow_it_throwing_type = typename TestFixture::template non_throwing_input_iterator<throws_when_converted>;
1198 using nothrow_it_raw_type = typename TestFixture::template non_throwing_input_iterator<TypeParam>;
1199
1200 constexpr bool it_operations_noexcept = noexcept(++std::declval<stream_it>())&& noexcept(*std::declval<stream_it>());
1201 constexpr bool element_construction_noexcept =
1202 std::is_nothrow_constructible<typename TestFixture::vector::value_type, typename stream_it::reference>{};
1203
1204 STATIC_ASSERT_TRUE((
1205 std::is_constructible<typename TestFixture::vector, arene::base::span<arene::base::byte>, stream_it, stream_it>{}
1206 ));
1207 STATIC_ASSERT_EQ(
1208 (std::is_nothrow_constructible<
1209 typename TestFixture::vector,
1210 arene::base::span<arene::base::byte>,
1211 stream_it,
1212 stream_it>{}),
1213 it_operations_noexcept && element_construction_noexcept
1214 );
1215
1216 STATIC_ASSERT_TRUE(std::is_constructible<
1217 typename TestFixture::vector,
1218 arene::base::span<arene::base::byte>,
1219 throwing_it_throwing_type,
1220 throwing_it_throwing_type>::value);
1221 STATIC_ASSERT_FALSE(std::is_nothrow_constructible<
1222 typename TestFixture::vector,
1223 arene::base::span<arene::base::byte>,
1224 throwing_it_throwing_type,
1225 throwing_it_throwing_type>::value);
1226 STATIC_ASSERT_TRUE(std::is_constructible<
1227 typename TestFixture::vector,
1228 arene::base::span<arene::base::byte>,
1229 throwing_it_raw_type,
1230 throwing_it_raw_type>::value);
1231 STATIC_ASSERT_FALSE(std::is_nothrow_constructible<
1232 typename TestFixture::vector,
1233 arene::base::span<arene::base::byte>,
1234 throwing_it_raw_type,
1235 throwing_it_raw_type>::value);
1236 STATIC_ASSERT_TRUE(std::is_constructible<
1237 typename TestFixture::vector,
1238 arene::base::span<arene::base::byte>,
1239 nothrow_it_raw_type,
1240 nothrow_it_raw_type>::value);
1241 STATIC_ASSERT_EQ(
1242 (std::is_nothrow_constructible<
1243 typename TestFixture::vector,
1244 arene::base::span<arene::base::byte>,
1245 nothrow_it_raw_type,
1246 nothrow_it_raw_type>::value),
1247 std::is_nothrow_copy_constructible<TypeParam>::value
1248 );
1249 STATIC_ASSERT_TRUE(std::is_constructible<
1250 typename TestFixture::vector,
1251 arene::base::span<arene::base::byte>,
1252 nothrow_it_throwing_type,
1253 nothrow_it_throwing_type>::value);
1254 STATIC_ASSERT_FALSE(std::is_nothrow_constructible<
1255 typename TestFixture::vector,
1256 arene::base::span<arene::base::byte>,
1257 nothrow_it_throwing_type,
1258 nothrow_it_throwing_type>::value);
1259}
1260
1261/// @test Given an `external_vector` holding existing elements, assigning to that vector with an initializer list
1262/// holding more elements copy assigns over the existing elements, and then copy-constructs new elements to increase the
1263/// size of the vector to that of the initializer list
1265 constexpr std::size_t smaller_count = 10;
1266
1267 std::initializer_list<TypeParam> const value{
1268 TestFixture::test_value(0),
1269 TestFixture::test_value(1),
1270 TestFixture::test_value(2),
1271 TestFixture::test_value(3),
1272 TestFixture::test_value(4),
1273 TestFixture::test_value(5),
1274 TestFixture::test_value(6),
1275 TestFixture::test_value(7),
1276 TestFixture::test_value(8),
1277 TestFixture::test_value(9),
1278 TestFixture::test_value(10),
1279 TestFixture::test_value(11),
1280 TestFixture::test_value(12),
1281 TestFixture::test_value(13)
1282 };
1283
1284 auto copy = TestFixture::test_external_vector(TestFixture::standard_storage, smaller_count);
1285 copy = value;
1286
1287 EXPECT_THAT(copy, ::testing::ElementsAreArray(value));
1288}
1289
1290/// @test Given an `external_vector` holding existing elements, assigning to that vector with an initializer list
1291/// holding fewer elements copy assigns over the existing elements up to the size of the initializer list, and then
1292/// destroys a number of elements equal to the difference between the old size of the vector and the size of the
1293/// initializer list
1295 constexpr std::size_t larger_count = 18;
1296
1297 std::initializer_list<TypeParam> const value{
1298 TestFixture::test_value(0),
1299 TestFixture::test_value(1),
1300 TestFixture::test_value(2),
1301 TestFixture::test_value(3),
1302 TestFixture::test_value(4),
1303 TestFixture::test_value(5),
1304 TestFixture::test_value(6),
1305 TestFixture::test_value(7),
1306 TestFixture::test_value(8),
1307 TestFixture::test_value(9),
1308 TestFixture::test_value(10),
1309 TestFixture::test_value(11),
1310 TestFixture::test_value(12),
1311 TestFixture::test_value(13)
1312 };
1313
1314 alignas(TypeParam) typename TestFixture::template storage_type<larger_count> storage{};
1315 auto copy = TestFixture::test_external_vector(storage, larger_count);
1316 copy = value;
1317
1318 EXPECT_THAT(copy, ::testing::ElementsAreArray(value));
1319}
1320
1321/// @test Invoking the `assign` member function with an iterator range sets the vector to have a size equal to the
1322/// number of elements in the range, and each element to be a copy of the corresponding element in the range
1324 std::vector<TypeParam> const source{
1325 TestFixture::test_value(3),
1326 TestFixture::test_value(4),
1327 TestFixture::test_value(5),
1328 TestFixture::test_value(6)
1329 };
1330
1331 auto vec = TestFixture::test_external_vector(TestFixture::standard_storage, 3);
1332 vec.assign(source.begin(), source.end());
1333
1334 EXPECT_THAT(vec, ::testing::ElementsAreArray(source));
1335}
1336
1337/// @test Assignment of an `external_vector` from an iterator range is `noexcept` if operations on the iterator are
1338/// `noexcept` and the element type of the vector is nothrow-constructible from the reference type of the iterator
1340 using array_it = typename array<TypeParam, TestFixture::capacity>::const_iterator;
1341 using throws_when_converted = typename TestFixture::throws_when_converted_to_t;
1342 using convertible_array_it = typename array<throws_when_converted, TestFixture::capacity>::const_iterator;
1343 using throwing_bidirectional_iterator = typename TestFixture::throwing_bidirectional_iterator_of_t;
1344
1345 constexpr bool it_operations_noexcept = noexcept(++std::declval<array_it>())&& noexcept(*std::declval<array_it>());
1346 constexpr bool element_construction_noexcept =
1347 std::is_nothrow_constructible<typename TestFixture::vector::value_type, typename array_it::reference>{} &&
1348 std::is_nothrow_assignable<typename TestFixture::vector::value_type&, typename array_it::reference>{};
1349
1350 STATIC_ASSERT_EQ(
1351 noexcept(std::declval<typename TestFixture::vector&>().assign(std::declval<array_it>(), std::declval<array_it>())
1352 ),
1353 it_operations_noexcept && element_construction_noexcept
1354 );
1355 STATIC_ASSERT_FALSE(noexcept(std::declval<typename TestFixture::vector&>().assign(
1356 std::declval<convertible_array_it>(),
1357 std::declval<convertible_array_it>()
1358 )));
1359 STATIC_ASSERT_FALSE(noexcept(std::declval<typename TestFixture::vector&>().assign(
1360 std::declval<throwing_bidirectional_iterator>(),
1361 std::declval<throwing_bidirectional_iterator>()
1362 )));
1363}
1364
1365/// @test Invoking the `assign` member function with an initializer list sets the vector to have a size equal to the
1366/// number of elements in the initializer list, and each element to be a copy of the corresponding element in the
1367/// initializer list
1369 constexpr std::size_t smaller_count = 10;
1370
1371 std::initializer_list<TypeParam> const value{
1372 TestFixture::test_value(0),
1373 TestFixture::test_value(1),
1374 TestFixture::test_value(2),
1375 TestFixture::test_value(3),
1376 TestFixture::test_value(4),
1377 TestFixture::test_value(5),
1378 TestFixture::test_value(6),
1379 TestFixture::test_value(7),
1380 TestFixture::test_value(8),
1381 TestFixture::test_value(9),
1382 TestFixture::test_value(10),
1383 TestFixture::test_value(11),
1384 TestFixture::test_value(12),
1385 TestFixture::test_value(13)
1386 };
1387
1388 auto copy = TestFixture::test_external_vector(TestFixture::standard_storage, smaller_count);
1389 copy.assign(value);
1390
1391 EXPECT_THAT(copy, ::testing::ElementsAreArray(value));
1392}
1393
1394/// @test Assignment of an `external_vector` from an initializer list is `noexcept` if the element type of the vector is
1395/// nothrow-copy-constructible
1397 constexpr bool is_nothrow_copyable =
1398 std::is_nothrow_copy_constructible<TypeParam>{} && std::is_nothrow_copy_assignable<TypeParam>{};
1399
1400 std::initializer_list<TypeParam> const values{
1401 TestFixture::test_value(0),
1402 TestFixture::test_value(1),
1403 TestFixture::test_value(2),
1404 TestFixture::test_value(3),
1405 TestFixture::test_value(4),
1406 TestFixture::test_value(5),
1407 TestFixture::test_value(6),
1408 TestFixture::test_value(7),
1409 TestFixture::test_value(8),
1410 TestFixture::test_value(9),
1411 TestFixture::test_value(10),
1412 TestFixture::test_value(11),
1413 TestFixture::test_value(12),
1414 TestFixture::test_value(13)
1415 };
1416 STATIC_ASSERT_EQ(noexcept(typename TestFixture::vector{}.assign(values)), is_nothrow_copyable);
1417}
1418
1419/// @test Invoking the `assign` member function with an iterator range specified using input iterators sets the vector
1420/// to have a size equal to the number of elements in the range, and each element to be a copy of the corresponding
1421/// element in the range
1423 std::vector<TypeParam> const source{
1424 TestFixture::test_value(3),
1425 TestFixture::test_value(4),
1426 TestFixture::test_value(5),
1427 TestFixture::test_value(6)
1428 };
1429
1430 auto vec = TestFixture::test_external_vector(TestFixture::standard_storage, 3);
1431 vec.assign(source.begin(), source.end());
1432
1433 EXPECT_THAT(vec, ::testing::ElementsAreArray(source));
1434}
1435
1436/// @test Assignment of an `external_vector` from an input iterator range is `noexcept` if operations on the iterator
1437/// are `noexcept` and the element type of the vector is nothrow-constructible from the value type of the iterator
1439 using throws_when_converted = typename TestFixture::throws_when_converted_to_t;
1440 using throwing_it_throwing_type = typename TestFixture::template throwing_input_iterator<throws_when_converted>;
1441 using throwing_it_raw_type = typename TestFixture::template throwing_input_iterator<TypeParam>;
1442 using nothrow_it_throwing_type = typename TestFixture::template non_throwing_input_iterator<throws_when_converted>;
1443 using nothrow_it_raw_type = typename TestFixture::template non_throwing_input_iterator<TypeParam>;
1444
1445 STATIC_ASSERT_FALSE(noexcept(typename TestFixture::vector{}.assign(
1446 std::declval<std::istream_iterator<TypeParam>>(),
1447 std::declval<std::istream_iterator<TypeParam>>()
1448 )));
1449 STATIC_ASSERT_FALSE(noexcept(typename TestFixture::vector{}.assign(
1450 std::declval<throwing_it_throwing_type>(),
1451 std::declval<throwing_it_throwing_type>()
1452 )));
1453 STATIC_ASSERT_FALSE(noexcept(
1454 typename TestFixture::vector{}.assign(std::declval<throwing_it_raw_type>(), std::declval<throwing_it_raw_type>())
1455 ));
1456 STATIC_ASSERT_EQ(
1457 noexcept(typename TestFixture::vector{}
1458 .assign(std::declval<nothrow_it_raw_type>(), std::declval<nothrow_it_raw_type>())),
1459 std::is_nothrow_copy_assignable<TypeParam>::value
1460 );
1461 STATIC_ASSERT_FALSE(noexcept(typename TestFixture::vector{}.assign(
1462 std::declval<nothrow_it_throwing_type>(),
1463 std::declval<nothrow_it_throwing_type>()
1464 )));
1465}
1466
1467/// @test Invoking the `assign` member function with a count and a value sets the size of the vector to the specified
1468/// count, where each element is a copy of the supplied value
1470 constexpr std::size_t count = 14;
1471 constexpr std::size_t smaller_count = 10;
1472
1473 auto copy = TestFixture::test_external_vector(TestFixture::standard_storage, smaller_count);
1474 TypeParam const value = TestFixture::test_value(0);
1475 copy.assign(count, value);
1476
1477 EXPECT_EQ(copy.size(), count);
1478 for (std::size_t i = 0; i < count; ++i) {
1479 EXPECT_EQ(copy[i], value);
1480 }
1481}
1482
1483/// @test The `assign` member function taking a size and a value is not `noexcept` if the value type is
1484/// not nothrow-copy-constructible
1486 constexpr std::size_t count = 14;
1487 TypeParam const value = TestFixture::test_value(0);
1488
1489 constexpr bool is_nothrow_copyable =
1490 std::is_nothrow_copy_constructible<TypeParam>{} && std::is_nothrow_copy_assignable<TypeParam>{};
1491 STATIC_ASSERT_EQ(noexcept(typename TestFixture::vector{}.assign(count, value)), is_nothrow_copyable);
1492}
1493
1494/// @test Invoking the `assign` member function with a count and a value where the count is larger than the capacity of
1495/// the vector terminates the program with a precondition violation
1497 constexpr std::size_t smaller_count = 10;
1498
1499 TypeParam const initial_value = TestFixture::test_value(0);
1500 typename TestFixture::vector copy(TestFixture::standard_storage, smaller_count, initial_value);
1501 TypeParam const value = TestFixture::test_value(1);
1502 ASSERT_DEATH(copy.assign(TestFixture::capacity + 1, value), "Precondition violation");
1503
1504 EXPECT_EQ(copy.size(), smaller_count);
1505 for (std::size_t i = 0; i < smaller_count; ++i) {
1506 EXPECT_EQ(copy[i], initial_value);
1507 }
1508}
1509
1510/// @test The index operator returns a reference to the element type of the vector
1512 ::testing::StaticAssertTypeEq<decltype(std::declval<typename TestFixture::vector&>()[0]), TypeParam&>();
1513}
1514
1515/// @test The index operator for a `const` vector returns a `const` reference to the element type of the vector
1517 ::testing::StaticAssertTypeEq<decltype(std::declval<typename TestFixture::vector const&>()[0]), TypeParam const&>();
1518}
1519
1520/// @test Given an `external_vector` with multiple elements, the index operator can be used with appropriate index
1521/// values to access each element, and the returned reference is the same as that returned by `at` given the same index
1523 auto vec = TestFixture::construct(TestFixture::standard_storage);
1524 auto const& const_vec = vec;
1525 TypeParam const value1 = TestFixture::test_value(0);
1526 vec.push_back(value1);
1527 TypeParam const value2 = TestFixture::test_value(1);
1528 vec.push_back(value2);
1529
1530 EXPECT_EQ(vec[0], value1);
1531 EXPECT_EQ(vec[1], value2);
1532 // NOLINTNEXTLINE(readability-container-data-pointer)
1533 EXPECT_EQ(&const_vec[0], std::addressof(vec[0]));
1534 EXPECT_EQ(&const_vec[1], std::addressof(vec[1]));
1535}
1536
1537/// @test The index operator is `noexcept`
1539 STATIC_ASSERT_TRUE(noexcept(std::declval<typename TestFixture::vector&>()[0]));
1540}
1541
1542/// @test Passing an index that is equal to or larger than the size of the vector terminates the program with a
1543/// precondition violation
1544TYPED_TEST_P(ExternalVectorDeathTest, IndexOperatorOutOfRange) {
1545 ASSERT_DEATH(TestFixture::construct()[0], "index < this->size()");
1546}
1547
1548/// @test The index operator is `noexcept` for a `const` vector
1550 STATIC_ASSERT_TRUE(noexcept(std::declval<typename TestFixture::vector const&>()[0]));
1551}
1552
1553/// @test The `data` member function is noexcept and returns a pointer to the first element of a non-empty vector
1554TYPED_TEST_P(ExternalVectorTest, Data) {
1555 typename TestFixture::vector vec{
1556 TestFixture::standard_storage,
1557 {TestFixture::test_value(3), TestFixture::test_value(4)}
1558 };
1559 auto const& const_vec = vec;
1560
1561 // NOLINTNEXTLINE(readability-container-data-pointer)
1562 EXPECT_EQ(vec.data(), std::addressof(vec[0]));
1563 // NOLINTNEXTLINE(readability-container-data-pointer)
1564 EXPECT_EQ(const_vec.data(), std::addressof(vec[0]));
1565
1566 STATIC_ASSERT_TRUE(noexcept(vec.data()));
1567 STATIC_ASSERT_TRUE(noexcept(const_vec.data()));
1568
1569 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
1570 auto const value2 = TestFixture::test_external_vector(storage2, 3);
1571 // This first assertion is not static because std::addressof is not constexpr until C++17.
1572 // NOLINTNEXTLINE(readability-container-data-pointer)
1573 ASSERT_EQ(value2.data(), std::addressof(value2[0]));
1574 alignas(TypeParam) typename TestFixture::standard_storage_type storage3{};
1575 ASSERT_NE(TestFixture::test_external_vector(storage3, 3).data(), nullptr);
1576}
1577
1578/// @test Given an `external_vector` which is not empty, invoking the `clear` method makes the vector empty
1580 auto vec = TestFixture::test_external_vector(TestFixture::standard_storage, 2);
1581
1582 EXPECT_EQ(vec.size(), 2);
1583 EXPECT_FALSE(vec.empty());
1584 vec.clear();
1585 EXPECT_EQ(vec.size(), 0);
1586 EXPECT_TRUE(vec.empty());
1587
1588 STATIC_ASSERT_TRUE(noexcept(vec.clear()));
1589}
1590
1591/// @brief Checks whether or not the given container has any duplicates, which we need to assert for test logic
1592/// @tparam Container The type of the container to check
1593/// @param container The container to check
1594/// @return @c true if @c container has any duplicate entries, @c false if every entry is unique
1595template <typename Container>
1596constexpr auto contains_duplicates(Container const& container) noexcept -> bool {
1597 // This runs at compile time and we can't assume that the type is hashable so we do the slow O(N^2) implementation
1598 for (std::size_t ii = 0UL; ii + 1UL < container.size(); ++ii) {
1599 for (std::size_t jj = ii + 1UL; jj < container.size(); ++jj) {
1600 if (container[ii] == container[jj]) {
1601 return true;
1602 }
1603 }
1604 }
1605
1606 return false;
1607}
1608
1609/// @brief Inefficiently bubble sorts the argument because @c std::sort is not constexpr in C++14
1610/// @tparam Container The type of a container
1611/// @param container A container to be sorted
1612/// @return An instance of @c Container with copies of the elements of @c container , but sorted
1613template <typename Container>
1614constexpr auto bubble_sort_helper(Container container) noexcept -> Container {
1615 bool swapped{true};
1616 while (swapped) {
1617 swapped = false;
1618 for (std::size_t ii = 0UL; ii + 1UL < container.size(); ++ii) {
1619 if (container[ii + 1] < container[ii]) {
1620 // std::swap is not constexpr until C++20
1621 typename Container::value_type const temp{container[ii]};
1622 container[ii] = container[ii + 1];
1623 container[ii + 1] = temp;
1624 swapped = true;
1625 }
1626 }
1627 }
1628 return container;
1629}
1630
1631/// A type trait to tell if the given type parameter is fully comparable or not for the purposes of these tests
1632/// @tparam T The type to check
1633template <typename T>
1636
1637/// @test Two instances of `external_vector` can be compared for ordering using `operator<` and `operator>`. One vector
1638/// compares less than another if the first has a smaller size than the second, and all elements are equal to the
1639/// corresponding element in the second vector with the same index or they have the same size, and there is an element
1640/// such that all elements with a lower index compare equal to the corresponding element in the second vector, and that
1641/// element compares less than the corresponding element of the second vector.
1643 // ASSERT_FALSE(contains_duplicates(TestFixture::test_external_vector(TestFixture::standard_storage, 4)));
1644
1645 // auto sorted_test_values = bubble_sort_helper(TestFixture::test_external_vector(TestFixture::standard_storage, 4));
1646 // typename TestFixture::vector low_value(sorted_test_values.begin(), sorted_test_values.begin() + 2UL);
1647 // typename TestFixture::vector high_value(sorted_test_values.begin() + 2UL, sorted_test_values.begin() + 4UL);
1648 // typename TestFixture::vector long_value(sorted_test_values.begin(), sorted_test_values.begin() + 3UL);
1649 // typename TestFixture::vector low_value_copy{low_value};
1650
1651 // ASSERT_TRUE(low_value < high_value);
1652 // ASSERT_TRUE(low_value < long_value);
1653 // ASSERT_FALSE(low_value < low_value_copy);
1654 // ASSERT_FALSE(high_value < low_value);
1655 // ASSERT_TRUE(high_value > low_value);
1656 // ASSERT_TRUE(long_value > low_value);
1657 // ASSERT_FALSE(low_value_copy > low_value);
1658 // ASSERT_FALSE(low_value > high_value);
1659
1660 // ASSERT_TRUE(low_value <= high_value);
1661 // ASSERT_TRUE(low_value <= long_value);
1662 // ASSERT_TRUE(low_value <= low_value_copy);
1663 // ASSERT_FALSE(high_value <= low_value);
1664 // ASSERT_TRUE(high_value >= low_value);
1665 // ASSERT_TRUE(long_value >= low_value);
1666 // ASSERT_TRUE(low_value_copy >= low_value);
1667 // ASSERT_FALSE(low_value >= high_value);
1668}
1669
1670/// @test The `emplace_back` member function can be used to add an element to an `external_vector`. The new element is
1671/// constructed with the arguments supplied to `emplace_back`
1672TYPED_TEST_P(ExternalVectorTest, CanEmplaceBack) {
1673 struct with_constructor_args {
1674 TypeParam value;
1675 // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
1676 TypeParam& reference;
1677 std::unique_ptr<TypeParam> pointer;
1678
1679 with_constructor_args(TypeParam val, TypeParam& ref, std::unique_ptr<TypeParam> ptr)
1680 : value(val),
1681 reference(ref),
1682 pointer(std::move(ptr)) {}
1683 };
1684
1685 alignas(with_constructor_args)
1686 arene::base::array<arene::base::byte, sizeof(with_constructor_args) * TestFixture::capacity>
1687 storage{};
1688 external_vector<with_constructor_args> vec(storage);
1689
1690 TypeParam const value1 = TestFixture::test_value(0);
1691 TypeParam value2 = TestFixture::test_value(1);
1692 TypeParam const value3 = TestFixture::test_value(2);
1693 auto& result = vec.emplace_back(value1, value2, std::make_unique<TypeParam>(value3));
1694
1695 ::testing::StaticAssertTypeEq<decltype(result), with_constructor_args&>();
1696 EXPECT_EQ(vec.size(), 1);
1697 ASSERT_FALSE(vec.empty());
1698 EXPECT_EQ(std::addressof(result), std::addressof(vec[0]));
1699
1700 EXPECT_EQ(result.value, value1);
1701 EXPECT_EQ(std::addressof(result.reference), std::addressof(value2));
1702 ASSERT_NE(result.pointer, nullptr);
1703 EXPECT_EQ(*result.pointer, value3);
1704}
1705
1706template <typename T>
1711
1712/// @test `emplace_back` is `noexcept` if and only if the element type of the vector is nothrow-constructible from the
1713/// supplied arguments
1715 STATIC_ASSERT_EQ(
1716 noexcept(TestFixture::construct().emplace_back(std::declval<TypeParam&&>())),
1717 std::is_nothrow_move_constructible<TypeParam>{}
1718 );
1719 STATIC_ASSERT_EQ(
1720 noexcept(TestFixture::construct().emplace_back(std::declval<TypeParam const&>())),
1721 std::is_nothrow_copy_constructible<TypeParam>{}
1722 );
1723
1724 STATIC_ASSERT_FALSE(
1725 noexcept(external_vector<throws_without_nullptr<TypeParam>>{}.emplace_back(std::declval<TypeParam>()))
1726 );
1727 STATIC_ASSERT_TRUE(
1728 noexcept(external_vector<throws_without_nullptr<TypeParam>>{}.emplace_back(std::declval<TypeParam>(), nullptr))
1729 );
1730}
1731
1732/// @test `emplace` with a position is `noexcept` if and only if the element type of the vector is
1733/// nothrow-move-constructible, nothrow-move-assignable, and nothrow-constructible from the supplied arguments
1735 using const_it = typename TestFixture::vector::const_iterator;
1736
1737 constexpr bool is_nothrow_movable =
1738 std::is_nothrow_move_constructible<TypeParam>{} && std::is_nothrow_move_assignable<TypeParam>{};
1739
1740 STATIC_ASSERT_EQ(
1741 noexcept(TestFixture::construct().emplace(std::declval<const_it>(), std::declval<TypeParam&&>())),
1742 is_nothrow_movable && std::is_nothrow_move_constructible<TypeParam>{}
1743 );
1744 STATIC_ASSERT_EQ(
1745 noexcept(TestFixture::construct().emplace(std::declval<const_it>(), std::declval<TypeParam const&>())),
1746 is_nothrow_movable && std::is_nothrow_copy_constructible<TypeParam>{}
1747 );
1748
1749 using overload_test_external_vector = external_vector<throws_without_nullptr<TypeParam>>;
1750 using overload_it = typename overload_test_external_vector::const_iterator;
1751 STATIC_ASSERT_FALSE(
1752 noexcept(overload_test_external_vector{}.emplace(std::declval<overload_it>(), std::declval<TypeParam>()))
1753 );
1754 STATIC_ASSERT_TRUE(
1755 noexcept(overload_test_external_vector{}.emplace(std::declval<overload_it>(), std::declval<TypeParam>(), nullptr))
1756 );
1757}
1758
1759/// @test Emplacing using the default constructor has the right exception specification. This test is separate from the
1760/// ones above due to a bug in GCC8 which tries to instantiate the `emplace_back` and `emplace` functions before
1761/// checking their exception specifications, which fails for types that are not default-constructible.
1763 ExternalVectorTest,
1766) {
1767 using const_it = typename TestFixture::vector::const_iterator;
1768
1769 constexpr bool is_nothrow_movable =
1770 std::is_nothrow_move_constructible<TypeParam>{} && std::is_nothrow_move_assignable<TypeParam>{};
1771
1772 STATIC_ASSERT_EQ(
1773 noexcept(TestFixture::construct().emplace_back()),
1774 std::is_nothrow_default_constructible<TypeParam>{}
1775 );
1776 STATIC_ASSERT_EQ(
1777 noexcept(TestFixture::construct().emplace(std::declval<const_it>())),
1778 is_nothrow_movable && std::is_nothrow_default_constructible<TypeParam>{}
1779 );
1780}
1781
1782/// @test Invoking `emplace_back` on an `external_vector<T>` which has a size equal to its capacity terminates the
1783/// program with a precondition violation
1785 auto vec = TestFixture::test_external_vector(TestFixture::standard_storage, TestFixture::capacity);
1786
1787 TypeParam const value1 = TestFixture::test_value(0);
1788 ASSERT_DEATH(vec.emplace_back(value1), "Precondition violation");
1789}
1790
1791/// @test Invoking `swap` without a namespace qualification on two `external_vector` instances exchanges the contents,
1792/// so the elements from one have been moved to the other, and vice-versa.
1794 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
1795 auto const source1 = TestFixture::test_external_vector(TestFixture::standard_storage, 12);
1796 auto const source2 = TestFixture::test_external_vector(storage2, 12, 16);
1797 alignas(TypeParam) typename TestFixture::standard_storage_type storage3{};
1798 alignas(TypeParam) typename TestFixture::standard_storage_type storage4{};
1799 typename TestFixture::vector value1(storage3, source1);
1800 typename TestFixture::vector value2(storage4, source2);
1801
1802 swap(value1, value2);
1803
1804 EXPECT_EQ(value1, source2);
1805 EXPECT_EQ(value2, source1);
1806}
1807
1808/// @test Swapping two `external_vector`s is `noexcept` if and only if the element type is nothrow swappable
1810 using vec = typename TestFixture::vector;
1811 constexpr bool element_nothrow_swappable = ::arene::base::is_nothrow_swappable_v<TypeParam>;
1812
1813 STATIC_ASSERT_EQ(::arene::base::is_nothrow_swappable_v<vec>, element_nothrow_swappable);
1814 STATIC_ASSERT_EQ(noexcept(std::declval<vec>().swap(std::declval<vec&>())), element_nothrow_swappable);
1815}
1816
1817/// @test `insert` is `noexcept` if the element type of the vector is nothrow-constructible from the supplied value, and
1818/// is nothrow-move-constructible and nothrow-move-assignable
1820 using vec = typename TestFixture::vector;
1821 using iterator = typename vec::const_iterator;
1822
1823 constexpr bool is_nothrow_movable =
1824 std::is_nothrow_move_constructible<TypeParam>{} && std::is_nothrow_move_assignable<TypeParam>{};
1825
1826 STATIC_ASSERT_EQ(noexcept(vec{}.insert(std::declval<iterator>(), std::declval<TypeParam&&>())), is_nothrow_movable);
1827 STATIC_ASSERT_EQ(
1828 noexcept(vec{}.insert(std::declval<iterator>(), std::declval<TypeParam const&>())),
1829 is_nothrow_movable && std::is_nothrow_copy_constructible<TypeParam>{}
1830 );
1831}
1832
1833/// @test `insert` is `noexcept` if the element type of the vector is nothrow-copy-constructible, and is
1834/// nothrow-move-constructible and nothrow-move-assignable
1836 using vec = typename TestFixture::vector;
1837 using iterator = typename vec::const_iterator;
1838
1839 constexpr bool is_nothrow_movable =
1840 std::is_nothrow_move_constructible<TypeParam>{} && std::is_nothrow_move_assignable<TypeParam>{};
1841
1842 STATIC_ASSERT_EQ(
1843 noexcept(vec{}.insert(std::declval<iterator>(), 0U, std::declval<TypeParam&&>())),
1844 is_nothrow_movable && std::is_nothrow_copy_constructible<TypeParam>{}
1845 );
1846 STATIC_ASSERT_EQ(
1847 noexcept(vec{}.insert(std::declval<iterator>(), 0U, std::declval<TypeParam const&>())),
1848 is_nothrow_movable && std::is_nothrow_copy_constructible<TypeParam>{}
1849 );
1850}
1851
1852/// @test Invoking `emplace` inserts a new value at the position indicated, constructing the new value with the supplied
1853/// arguments, and returning an iterator referencing the new value
1854TYPED_TEST_P(ExternalVectorTest, EmplaceInsert) {
1855 struct with_constructor_args {
1856 TypeParam value;
1857 TypeParam* raw_pointer;
1858 std::unique_ptr<TypeParam> unique_pointer;
1859
1860 with_constructor_args(TypeParam val, TypeParam* rptr, std::unique_ptr<TypeParam> uptr)
1861 : value(val),
1862 raw_pointer(rptr),
1863 unique_pointer(std::move(uptr)) {}
1864 };
1865
1866 alignas(with_constructor_args)
1867 arene::base::array<arene::base::byte, sizeof(with_constructor_args) * TestFixture::capacity>
1868 storage{};
1869 external_vector<with_constructor_args> vec(storage);
1870 auto const& const_vec1 = vec;
1871 TypeParam const value1 = TestFixture::test_value(0);
1872 TypeParam value2 = TestFixture::test_value(1);
1873 TypeParam const value3 = TestFixture::test_value(2);
1874 auto pos = vec.emplace(const_vec1.begin(), value1, std::addressof(value2), std::make_unique<TypeParam>(value3));
1875 EXPECT_EQ(pos, vec.begin());
1876 EXPECT_EQ(vec.size(), 1);
1877 EXPECT_EQ(&*pos, std::addressof(vec[0]));
1878
1879 EXPECT_EQ(pos->value, value1);
1880 ASSERT_NE(pos->raw_pointer, nullptr);
1881 EXPECT_EQ(pos->raw_pointer, std::addressof(value2));
1882 ASSERT_NE(pos->unique_pointer, nullptr);
1883 EXPECT_EQ(*pos->unique_pointer, value3);
1884}
1885
1886/// @test Invoking `insert` with a count and a value to insert more elements than there are remaining spaces in the
1887/// vector before it exceeds capacity terminates the program with a precondition violation.
1889 auto value = TestFixture::test_external_vector(TestFixture::standard_storage, 4);
1890 ASSERT_DEATH(
1891 value.insert(value.begin(), (TestFixture::capacity + 1) - value.size(), TestFixture::test_value(5)),
1892 "Precondition violation"
1893 );
1894 EXPECT_EQ(value.size(), 4);
1895}
1896
1897/// @test Invoking `insert` with the end iterator of the `external_vector` and a count and value appends the specified
1898/// number of copies of the supplied value to the end of the vector.
1900 TypeParam const value1 = TestFixture::test_value(0);
1901 TypeParam const value2 = TestFixture::test_value(1);
1902 TypeParam const value3 = TestFixture::test_value(2);
1903 typename TestFixture::vector vec{TestFixture::standard_storage, {value1, value2}};
1904 constexpr std::size_t count = 5;
1905
1906 vec.insert(vec.end(), count, value3);
1907 EXPECT_EQ(vec.size(), count + 2);
1908 EXPECT_EQ(vec[0], value1);
1909 EXPECT_EQ(vec[1], value2);
1910 for (std::size_t i = 0; i < count; ++i) {
1911 EXPECT_EQ(vec[i + 2], value3);
1912 }
1913}
1914
1915/// @test Invoking `insert` with an iterator referring to an existing element of an `external_vector` and a count and
1916/// value moves the referred to element and subsequent elements to make room, and inserts the specified number of copies
1917/// of the supplied value prior to the existing element.
1919 TypeParam const value1 = TestFixture::test_value(0);
1920 TypeParam const value2 = TestFixture::test_value(1);
1921 TypeParam const value3 = TestFixture::test_value(2);
1922 TypeParam const value4 = TestFixture::test_value(3);
1923 TypeParam const value_inserted = TestFixture::test_value(4);
1924 typename TestFixture::vector vec{TestFixture::standard_storage, {value1, value2, value3, value4}};
1925 constexpr std::size_t count = 5;
1926
1927 vec.insert(vec.begin() + 1, count, value_inserted);
1928 EXPECT_EQ(vec.size(), count + 4);
1929 EXPECT_EQ(vec[0], value1);
1930 for (std::size_t i = 0; i < count; ++i) {
1931 EXPECT_EQ(vec[i + 1], value_inserted);
1932 }
1933 EXPECT_EQ(vec[count + 1], value2);
1934 EXPECT_EQ(vec[count + 2], value3);
1935 EXPECT_EQ(vec[count + 3], value4);
1936}
1937
1938/// @test Invoking `insert` with an iterator referring to an existing element of an `external_vector` and a source
1939/// iterator range denoted by a pair of iterators moves the referred to element and subsequent elements to make room,
1940/// and inserts the elements from the source iterator range prior to the existing element.
1942 TypeParam const value1 = TestFixture::test_value(0);
1943 TypeParam const value2 = TestFixture::test_value(1);
1944 TypeParam const value3 = TestFixture::test_value(2);
1945 TypeParam const value4 = TestFixture::test_value(3);
1946 auto vec1 = TestFixture::test_external_vector(TestFixture::standard_storage, 4);
1947 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
1948 auto const vec2 = TestFixture::test_external_vector(storage2, 4, 11);
1949
1950 vec1.insert(vec1.begin() + 1, vec2.begin(), vec2.end());
1951 EXPECT_EQ(vec1.size(), vec2.size() + 4);
1952 EXPECT_EQ(vec1[0], value1);
1953 for (std::size_t idx = 0; idx < vec2.size(); ++idx) {
1954 EXPECT_EQ(vec1[idx + 1], vec2[idx]);
1955 }
1956 EXPECT_EQ(vec1[vec2.size() + 1], value2);
1957 EXPECT_EQ(vec1[vec2.size() + 2], value3);
1958 EXPECT_EQ(vec1[vec2.size() + 3], value4);
1959}
1960
1961/// @test `insert` is `noexcept` if the element type of the vector is nothrow-constructible from the supplied iterator's
1962/// reference type, and is nothrow-move-constructible and nothrow-move-assignable
1964 using ivec_iter = typename TestFixture::vector::const_iterator;
1965 using source_array = ::arene::base::array<TypeParam, TestFixture::capacity>;
1966 using throws_when_converted = typename TestFixture::throws_when_converted_to_t;
1967 using throw_conv_array = ::arene::base::array<throws_when_converted, TestFixture::capacity>;
1968 using throwing_it_throwing_type = typename TestFixture::template throwing_input_iterator<throws_when_converted>;
1969 using throwing_it_raw_type = typename TestFixture::template throwing_input_iterator<TypeParam>;
1970 using nothrow_it_throwing_type = typename TestFixture::template non_throwing_input_iterator<throws_when_converted>;
1971 using nothrow_it_raw_type = typename TestFixture::template non_throwing_input_iterator<TypeParam>;
1972
1973 constexpr bool is_nothrow_movable =
1974 std::is_nothrow_move_constructible<TypeParam>{} && std::is_nothrow_move_assignable<TypeParam>{};
1975
1976 STATIC_ASSERT_EQ(
1977 noexcept(this->construct().insert(
1978 std::declval<ivec_iter>(),
1979 std::declval<typename source_array::const_iterator>(),
1980 std::declval<typename source_array::const_iterator>()
1981 )),
1982 is_nothrow_movable && std::is_nothrow_copy_constructible<TypeParam>{}
1983 );
1984
1985 STATIC_ASSERT_FALSE(noexcept(this->construct().insert(
1986 std::declval<ivec_iter>(),
1987 std::declval<typename throw_conv_array::const_iterator>(),
1988 std::declval<typename throw_conv_array::const_iterator>()
1989 )));
1990
1991 STATIC_ASSERT_EQ(
1992 noexcept(this->construct().insert(
1993 std::declval<ivec_iter>(),
1994 std::declval<nothrow_it_raw_type>(),
1995 std::declval<nothrow_it_raw_type>()
1996 )),
1997 is_nothrow_movable && std::is_nothrow_copy_constructible<TypeParam>{}
1998 );
1999 STATIC_ASSERT_FALSE(noexcept(this->construct().insert(
2000 std::declval<ivec_iter>(),
2001 std::declval<std::istream_iterator<TypeParam>>(),
2002 std::declval<std::istream_iterator<TypeParam>>()
2003 )));
2004 STATIC_ASSERT_FALSE(noexcept(this->construct().insert(
2005 std::declval<ivec_iter>(),
2006 std::declval<throwing_it_throwing_type>(),
2007 std::declval<throwing_it_throwing_type>()
2008 )));
2009 STATIC_ASSERT_FALSE(noexcept(this->construct().insert(
2010 std::declval<ivec_iter>(),
2011 std::declval<throwing_it_raw_type>(),
2012 std::declval<throwing_it_raw_type>()
2013 )));
2014 STATIC_ASSERT_FALSE(noexcept(this->construct().insert(
2015 std::declval<ivec_iter>(),
2016 std::declval<nothrow_it_throwing_type>(),
2017 std::declval<nothrow_it_throwing_type>()
2018 )));
2019}
2020
2021/// @test `cbegin` and `cend` return `const_iterator`s to the corresponding locations of the `external_vector`.
2022TYPED_TEST_P(ExternalVectorTest, CanGetCBeginAndEnd) {
2023 auto const value1 = TestFixture::test_external_vector(TestFixture::standard_storage, 4);
2024
2025 ASSERT_EQ(value1.cbegin(), value1.begin());
2026 ASSERT_EQ(value1.cend(), value1.end());
2027
2028 ::testing::StaticAssertTypeEq<decltype(value1.cbegin()), typename TestFixture::vector::const_iterator>();
2029 STATIC_ASSERT_TRUE(noexcept(value1.cbegin()));
2030 STATIC_ASSERT_TRUE(noexcept(value1.cend()));
2031
2032 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2033 auto value2 = TestFixture::test_external_vector(storage2, 4);
2034 ::testing::StaticAssertTypeEq<decltype(value2.cbegin()), typename TestFixture::vector::const_iterator>();
2035 ::testing::StaticAssertTypeEq<decltype(value2.cend()), typename TestFixture::vector::const_iterator>();
2036
2037 EXPECT_EQ(value2.begin(), value2.cbegin());
2038 EXPECT_EQ(value2.end(), value2.cend());
2039}
2040
2041/// @test `rbegin`, `rend`, `crbegin` and `crend` return reverse iterators to the appropriate locations in the
2042/// `external_vector`, such that iteration _forwards_ from `rbegin` or `crbegin` iterates through the elements of the
2043/// `external_vector` in reverse order.
2044TYPED_TEST_P(ExternalVectorTest, ReverseIteration) {
2045 auto const value1 = TestFixture::test_external_vector(TestFixture::standard_storage, 4);
2046
2047 auto itr = value1.rbegin();
2048 ASSERT_NE(itr, value1.rend());
2049 EXPECT_EQ(*itr, value1[3]);
2050 ++itr;
2051 ASSERT_NE(itr, value1.rend());
2052 EXPECT_EQ(*itr, value1[2]);
2053 ++itr;
2054 ASSERT_NE(itr, value1.rend());
2055 EXPECT_EQ(*itr, value1[1]);
2056 ++itr;
2057 ASSERT_NE(itr, value1.rend());
2058 EXPECT_EQ(*itr, value1[0]);
2059 ++itr;
2060 ASSERT_EQ(itr, value1.rend());
2061
2062 auto citr = value1.crbegin();
2063 ASSERT_NE(citr, value1.crend());
2064 EXPECT_EQ(*citr, value1[3]);
2065 ++citr;
2066 ASSERT_NE(citr, value1.crend());
2067 EXPECT_EQ(*citr, value1[2]);
2068 ++citr;
2069 ASSERT_NE(citr, value1.crend());
2070 EXPECT_EQ(*citr, value1[1]);
2071 ++citr;
2072 ASSERT_NE(citr, value1.crend());
2073 EXPECT_EQ(*citr, value1[0]);
2074 ++citr;
2075 ASSERT_EQ(citr, value1.crend());
2076}
2077
2078/// @test An `external_vector` can be assigned from an `external_vector` with a larger capacity, provided the number of
2079/// elements in the source vector is less than the capacity of the target vector
2081 constexpr std::size_t larger_capacity = TestFixture::capacity + 2UL;
2082 alignas(TypeParam) typename TestFixture::template storage_type<larger_capacity> storage{};
2083 external_vector<TypeParam> const large_vector{
2084 storage,
2085 {TestFixture::test_value(0),
2086 TestFixture::test_value(1),
2087 TestFixture::test_value(2),
2088 TestFixture::test_value(3),
2089 TestFixture::test_value(4)}
2090 };
2091
2092 alignas(TypeParam) typename TestFixture::template storage_type<TestFixture::capacity> storage2{};
2093 typename TestFixture::vector small_vector(storage2, large_vector);
2094 small_vector = large_vector;
2095
2096 EXPECT_EQ(small_vector.size(), large_vector.size());
2097 for (std::size_t i = 0; i < small_vector.size(); ++i) {
2098 EXPECT_EQ(small_vector[i], large_vector[i]);
2099 }
2100}
2101
2102/// @test `try_construct` can be used to construct an empty `external_vector`.
2104 using vec = typename TestFixture::vector;
2105 STATIC_ASSERT_TRUE(std::is_same<decltype(vec::try_construct()), ::arene::base::optional<vec>>::value);
2106 STATIC_ASSERT_TRUE(noexcept(vec::try_construct()));
2107
2108 ASSERT_TRUE(vec::try_construct().has_value());
2109 ASSERT_TRUE(vec::try_construct()->empty());
2110}
2111
2112/// @test `try_construct` can be used to construct an `external_vector` with a specified number of elements
2114 ExternalVectorTest,
2117) {
2118 using vec = typename TestFixture::vector;
2119 constexpr std::size_t size1 = 10;
2120 STATIC_ASSERT_TRUE(
2121 std::is_same<decltype(vec::try_construct(TestFixture::standard_storage, size1)), ::arene::base::optional<vec>>::
2122 value
2123 );
2124 STATIC_ASSERT_TRUE(noexcept(vec::try_construct(TestFixture::standard_storage, size1)));
2125
2126 ASSERT_TRUE(vec::try_construct(TestFixture::standard_storage, size1).has_value());
2127 ASSERT_FALSE(vec::try_construct(TestFixture::standard_storage, size1)->empty());
2128 ASSERT_EQ(vec::try_construct(TestFixture::standard_storage, size1)->size(), size1);
2129}
2130
2131/// @test `try_construct` can be used to attempt to construct an `external_vector` with a specified number of elements
2132/// that exceeds the capacity of the vector, in which case the result is an empty `optional`.
2134 ExternalVectorTest,
2137) {
2138 using vec = typename TestFixture::vector;
2139 constexpr std::size_t size1 = TestFixture::capacity + 1;
2140 STATIC_ASSERT_TRUE(
2141 std::is_same<decltype(vec::try_construct(TestFixture::standard_storage, size1)), ::arene::base::optional<vec>>::
2142 value
2143 );
2144 STATIC_ASSERT_TRUE(noexcept(vec::try_construct(TestFixture::standard_storage, size1)));
2145
2146 ASSERT_FALSE(vec::try_construct(TestFixture::standard_storage, size1).has_value());
2147}
2148
2149/// @test `try_construct` with a number of elements is not considered for overload resolution if the element type of the
2150/// `external_vector<T>` is not default-constructible.
2152 constexpr bool can_construct_with_size_only = ::arene::base::substitution_succeeds<
2153 TestFixture::template try_construct_result,
2154 typename TestFixture::vector,
2155 arene::base::span<arene::base::byte>,
2156 std::size_t>;
2157 STATIC_ASSERT_EQ(can_construct_with_size_only, std::is_default_constructible<TypeParam>{});
2158}
2159
2160/// @test The `external_vector` constructor with a number of elements is not considered for overload resolution if the
2161/// element type of the `external_vector<T>` is not default-constructible.
2163 STATIC_ASSERT_EQ(
2164 (std::is_constructible<typename TestFixture::vector, arene::base::span<arene::base::byte>, std::size_t>::value),
2165 std::is_default_constructible<TypeParam>{}
2166 );
2167}
2168
2169/// @test The default constructor and corresponding `try_construct` function of `external_vector<T>` is always
2170/// `noexcept`, even if the stored element type's default constructor can throw
2172 using ivec = typename TestFixture::vector;
2173 STATIC_ASSERT_TRUE(noexcept(ivec::try_construct()));
2174 STATIC_ASSERT_TRUE(noexcept(ivec{}));
2175
2176 ASSERT_TRUE(ivec::try_construct().has_value());
2177 ASSERT_TRUE(ivec::try_construct()->empty());
2178 ASSERT_TRUE(ivec().empty());
2179}
2180
2181/// @test The constructor with just storage and corresponding `try_construct` function of `external_vector<T>` is always
2182/// `noexcept`, even if the stored element type's default constructor can throw
2184 using ivec = typename TestFixture::vector;
2185 STATIC_ASSERT_TRUE(noexcept(ivec::try_construct(TestFixture::standard_storage)));
2186 STATIC_ASSERT_TRUE(noexcept(ivec{TestFixture::standard_storage}));
2187
2188 ASSERT_TRUE(ivec::try_construct(TestFixture::standard_storage).has_value());
2189 ASSERT_TRUE(ivec::try_construct(TestFixture::standard_storage)->empty());
2190 ASSERT_TRUE(ivec(TestFixture::standard_storage).empty());
2191}
2192
2193/// @test The `try_construct` function of `external_vector` that takes a number of elements is not `noexcept` if the
2194/// default constructor of the stored element type is not `noexcept`
2196 ExternalVectorTest,
2199) {
2200 using ivec = typename TestFixture::vector;
2201 constexpr std::size_t small_size = 10;
2202 STATIC_ASSERT_TRUE(std::is_same<
2203 decltype(ivec::try_construct(TestFixture::standard_storage, small_size)),
2204 ::arene::base::optional<ivec>>::value);
2205 STATIC_ASSERT_EQ(
2206 noexcept(ivec::try_construct(TestFixture::standard_storage, small_size)),
2207 std::is_default_constructible<TypeParam>{}
2208 );
2209}
2210
2211/// @test The constructor of `external_vector` that takes a number of elements is not `noexcept` if the default
2212/// constructor of the stored element type is not `noexcept`
2214 ExternalVectorTest,
2217) {
2218 using ivec = typename TestFixture::vector;
2219 constexpr std::size_t size1 = TestFixture::capacity - 1;
2220 STATIC_ASSERT_EQ(
2221 noexcept(ivec(TestFixture::standard_storage, size1)),
2222 std::is_nothrow_default_constructible<TypeParam>{}
2223 );
2224}
2225
2226/// @test Specifying a number of elements larger than the capacity of an `external_vector` when invoking the
2227/// constructor terminates the program with a precondition violation
2229 ExternalVectorDeathTest,
2232) {
2233 constexpr std::size_t size1 = TestFixture::capacity + 1;
2234 ASSERT_DEATH(
2235 this->construct(TestFixture::standard_storage, size1),
2236 "Precondition violation"
2237 ); // dies regardless of exception behavior
2238}
2239
2240/// @test The overload of `try_construct` that takes a size and source value is not considered for overload resolution
2241/// if the element type of the `external_vector<T>` is not copy-constructible.
2243 using ivec = typename TestFixture::vector;
2244 constexpr bool copy_ok = std::is_copy_constructible<TypeParam>::value;
2245 STATIC_ASSERT_EQ(
2246 (std::is_constructible<ivec, arene::base::span<arene::base::byte>, std::size_t, TypeParam>::value),
2247 copy_ok
2248 );
2249 STATIC_ASSERT_EQ(
2250 (::arene::base::substitution_succeeds<
2251 TestFixture::template try_construct_result,
2252 ivec,
2253 arene::base::span<arene::base::byte>,
2254 std::size_t,
2255 TypeParam>),
2256 copy_ok
2257 );
2258}
2259
2260/// @test Invoking `try_construct` with an initializer list constructs an `external_vector` holding copies of the
2261/// elements from the initializer list
2263 static std::initializer_list<TypeParam> const values{
2264 TestFixture::test_value(0),
2265 TestFixture::test_value(1),
2266 TestFixture::test_value(2),
2267 TestFixture::test_value(3)
2268 };
2269
2270 STATIC_ASSERT_EQ(
2271 noexcept(TestFixture::vector::try_construct(TestFixture::standard_storage, values)),
2272 std::is_nothrow_copy_constructible<TypeParam>{}
2273 );
2274 ASSERT_TRUE(TestFixture::vector::try_construct(TestFixture::standard_storage, values).has_value());
2275 ASSERT_EQ(TestFixture::vector::try_construct(TestFixture::standard_storage, values)->size(), values.size());
2276
2277 auto res = TestFixture::vector::try_construct(TestFixture::standard_storage, values);
2278 EXPECT_THAT(*res, ::testing::ElementsAreArray(values));
2279}
2280
2281/// @test Invoking `try_construct` with an initializer list holding more elements than the capacity of the
2282/// `external_vector` returns an empty `optional`
2284 static std::initializer_list<TypeParam> const values{
2285 TestFixture::test_value(0), TestFixture::test_value(1), TestFixture::test_value(2),
2286 TestFixture::test_value(3), TestFixture::test_value(4), TestFixture::test_value(5),
2287 TestFixture::test_value(6), TestFixture::test_value(7), TestFixture::test_value(8),
2288 TestFixture::test_value(9), TestFixture::test_value(10), TestFixture::test_value(11),
2289 TestFixture::test_value(12), TestFixture::test_value(13), TestFixture::test_value(14),
2290 TestFixture::test_value(15), TestFixture::test_value(16), TestFixture::test_value(17),
2291 TestFixture::test_value(18), TestFixture::test_value(19), TestFixture::test_value(20),
2292 TestFixture::test_value(21), TestFixture::test_value(22), TestFixture::test_value(23),
2293 TestFixture::test_value(24), TestFixture::test_value(25)
2294 };
2295
2296 ASSERT_GT(values.size(), TestFixture::capacity);
2297 ASSERT_FALSE(TestFixture::vector::try_construct(TestFixture::standard_storage, values).has_value());
2298}
2299
2300/// @test Invoking `try_construct` with an existing `external_vector` returns a copy of that source vector
2302 using ivec = typename TestFixture::vector;
2303 auto const values = TestFixture::test_external_vector(TestFixture::standard_storage);
2304
2305 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2306 STATIC_ASSERT_TRUE(::arene::base::substitution_succeeds<
2307 TestFixture::template try_construct_result,
2308 ivec,
2309 arene::base::span<arene::base::byte>,
2310 ivec const&>);
2311 STATIC_ASSERT_EQ(
2312 noexcept(ivec::try_construct(storage2, values)),
2313 std::is_nothrow_copy_constructible<TypeParam>::value
2314 );
2315 auto res = ivec::try_construct(storage2, values);
2316 ASSERT_TRUE(res.has_value());
2317 EXPECT_THAT(*res, ::testing::ElementsAreArray(values));
2318}
2319
2320/// @test Constructing an `external_vector` with an existing `external_vector` copies the elements from the source
2321/// vector
2322TYPED_TEST_P(ExternalVectorTest, CanCopyViaConstruct) {
2323 auto const values = TestFixture::test_external_vector(TestFixture::standard_storage);
2324
2325 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2326 STATIC_ASSERT_EQ(
2327 noexcept(typename TestFixture::vector(storage2, values)),
2328 std::is_nothrow_copy_constructible<TypeParam>{}
2329 );
2330 auto res = this->construct(storage2, values);
2331 EXPECT_THAT(res, ::testing::ElementsAreArray(values));
2332}
2333
2334/// @test Invoking `try_construct` with an existing `external_vector` with a smaller capacity returns an
2335/// `external_vector` with the same elements as the source vector
2337 using ivec = typename TestFixture::vector;
2338 constexpr std::size_t smaller_capacity = TestFixture::capacity - 1;
2339 alignas(TypeParam) alignas(arene::base::byte)
2340 arene::base::array<arene::base::byte, smaller_capacity * sizeof(TypeParam)>
2341 storage{};
2342
2343 external_vector<TypeParam> const values{TestFixture::test_external_vector(storage, smaller_capacity)};
2344
2345 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2346 STATIC_ASSERT_TRUE(::arene::base::substitution_succeeds<
2347 TestFixture::template try_construct_result,
2348 ivec,
2349 arene::base::span<arene::base::byte>,
2350 ivec const&>);
2351 STATIC_ASSERT_EQ(
2352 noexcept(ivec::try_construct(storage2, values)),
2353 std::is_nothrow_copy_constructible<TypeParam>::value
2354 );
2355 auto res = ivec::try_construct(storage2, values);
2356 ASSERT_TRUE(res.has_value());
2357 EXPECT_THAT(*res, ::testing::ElementsAreArray(values));
2358}
2359
2360/// @test Constructing an `external_vector` from an existing `external_vector` with a smaller capacity returns an
2361/// `external_vector` with the same elements as the source vector
2363 constexpr std::size_t smaller_capacity = TestFixture::capacity - 1;
2364 alignas(TypeParam) typename TestFixture::template storage_type<smaller_capacity> storage{};
2365 static external_vector<TypeParam> const values{TestFixture::test_external_vector(storage, smaller_capacity)};
2366
2367 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2368 STATIC_ASSERT_EQ(
2369 noexcept(typename TestFixture::vector(storage2, values)),
2370 std::is_nothrow_copy_constructible<TypeParam>{}
2371 );
2372 auto res = this->construct(storage2, values);
2373 EXPECT_THAT(res, ::testing::ElementsAreArray(values));
2374}
2375
2376/// @test Invoking `try_construct` with an existing `external_vector` with a larger capacity but a size that is less
2377/// than or equal to the capacity of the destination type returns an `external_vector` with the same elements as the
2378/// source vector
2380 using ivec = typename TestFixture::vector;
2381 constexpr std::size_t larger_capacity = TestFixture::capacity + 1;
2382 alignas(TypeParam) alignas(arene::base::byte)
2383 arene::base::array<arene::base::byte, larger_capacity * sizeof(TypeParam)>
2384 storage{};
2385 auto const values = TestFixture::test_external_vector(storage, 0, TestFixture::capacity);
2386
2387 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2388 STATIC_ASSERT_TRUE(::arene::base::substitution_succeeds<
2389 TestFixture::template try_construct_result,
2390 ivec,
2391 arene::base::span<arene::base::byte>,
2392 ivec const&>);
2393 STATIC_ASSERT_EQ(
2394 noexcept(ivec::try_construct(storage2, values)),
2395 std::is_nothrow_copy_constructible<TypeParam>::value
2396 );
2397 auto res = ivec::try_construct(storage2, values);
2398 ASSERT_TRUE(res.has_value());
2399 EXPECT_THAT(*res, ::testing::ElementsAreArray(values));
2400}
2401
2402/// @test Invoking `try_construct` with an existing `external_vector` with a larger capacity and a size that is larger
2403/// than the capacity of the destination type returns an empty `optional`
2405 constexpr std::size_t larger_capacity = TestFixture::capacity + 1;
2406 alignas(TypeParam) alignas(arene::base::byte)
2407 arene::base::array<arene::base::byte, larger_capacity * sizeof(TypeParam)>
2408 storage{};
2409 auto const values = TestFixture::test_external_vector(storage, 0, larger_capacity);
2410
2411 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2412 EXPECT_GT(values.size(), TestFixture::capacity);
2413 EXPECT_FALSE(TestFixture::vector::try_construct(storage2, values).has_value());
2414}
2415
2416/// @test Constructing an `external_vector` from an existing `external_vector` with a larger capacity but a size that is
2417/// less than or equal to the capacity of the destination type copies elements from the source vector
2419 constexpr std::size_t larger_capacity = TestFixture::capacity + 1;
2420 alignas(TypeParam) typename TestFixture::template storage_type<larger_capacity> storage{};
2421 auto const values = TestFixture::test_external_vector(storage, 0, TestFixture::capacity);
2422 constexpr bool copy_is_noexcept = std::is_nothrow_copy_constructible<TypeParam>::value;
2423
2424 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2425 STATIC_ASSERT_EQ(noexcept(typename TestFixture::vector(storage2, values)), copy_is_noexcept);
2426 auto res = this->construct(storage2, values);
2427 EXPECT_THAT(res, ::testing::ElementsAreArray(values));
2428}
2429
2430/// @test `external_vector` is not copy-constructible
2432 using ivec = typename TestFixture::vector;
2433
2434 STATIC_ASSERT_FALSE((std::is_constructible<ivec, ivec const&>::value));
2435 STATIC_ASSERT_FALSE(
2436 (::arene::base::substitution_succeeds<TestFixture::template try_construct_result, ivec, ivec const&>)
2437 );
2438}
2439
2440/// @test `external_vector` is constructible with storage and an old vector if and only if the data type is
2441/// copy-constructible
2443 using ivec = typename TestFixture::vector;
2444 constexpr bool copy_ok = std::is_copy_constructible<TypeParam>::value;
2445
2446 STATIC_ASSERT_EQ((std::is_constructible<ivec, span<byte>, ivec const&>::value), copy_ok);
2447 STATIC_ASSERT_EQ(
2448 (::arene::base::substitution_succeeds<TestFixture::template try_construct_result, ivec, span<byte>, ivec const&>),
2449 copy_ok
2450 );
2451}
2452
2453/// @test Invoking `try_construct` with an rvalue `external_vector` constructs a new `external_vector`, and
2454/// move-constructs the elements from the source to the new vector.
2456 auto const values = TestFixture::test_external_vector(TestFixture::standard_storage);
2457 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2458 typename TestFixture::vector values_copy(storage2, values);
2459
2460 STATIC_ASSERT_TRUE(::arene::base::substitution_succeeds<
2461 TestFixture::template try_construct_result,
2462 typename TestFixture::vector,
2463 typename TestFixture::vector&&>);
2464 STATIC_ASSERT_EQ(
2465 noexcept(TestFixture::vector::try_construct(std::move(values_copy))),
2466 std::is_nothrow_move_constructible<TypeParam>::value
2467 );
2468 auto res = TestFixture::vector::try_construct(std::move(values_copy));
2469 ASSERT_TRUE(res.has_value());
2470 EXPECT_THAT(*res, ::testing::ElementsAreArray(values));
2471}
2472
2473/// @test Constructing an `external_vector` from an rvalue `external_vector` move-constructs the elements from the
2474/// source to the new vector.
2475TYPED_TEST_P(ExternalVectorTest, CanMoveViaConstruct) {
2476 auto const values = TestFixture::test_external_vector(TestFixture::standard_storage);
2477
2478 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2479 typename TestFixture::vector source(storage2, values);
2480 // NOLINTNEXTLINE(hicpp-move-const-arg)
2481 STATIC_ASSERT_TRUE(noexcept(typename TestFixture::vector(std::move(source))));
2482 // NOLINTNEXTLINE(hicpp-move-const-arg)
2483 auto res = this->construct(std::move(source));
2484 EXPECT_THAT(res, ::testing::ElementsAreArray(values));
2485}
2486
2487/// @test Invoking `try_construct` with an rvalue `external_vector` with a larger capacity and a size that is larger
2488/// than the capacity of the destination storage returns an empty `optional`
2490 constexpr std::size_t larger_capacity = TestFixture::capacity + 1;
2491 alignas(TypeParam) alignas(arene::base::byte)
2492 arene::base::array<arene::base::byte, larger_capacity * sizeof(TypeParam)>
2493 storage{};
2494 auto values = TestFixture::test_external_vector(storage, TestFixture::capacity + 1);
2495
2496 ASSERT_GT(values.size(), TestFixture::capacity);
2497
2498 ASSERT_FALSE(
2499 // NOLINTNEXTLINE(hicpp-move-const-arg)
2500 TestFixture::vector::try_construct(TestFixture::standard_storage, external_vector<TypeParam>{std::move(values)})
2501 .has_value()
2502 );
2503}
2504
2505/// @test `external_vector` is not move constructible if the element type is not move constructible
2507 using ivec = external_vector<TypeParam>;
2508 constexpr bool move_ok = std::is_move_constructible<TypeParam>::value;
2509
2510 STATIC_ASSERT_EQ((std::is_constructible<ivec, ivec&&>::value), move_ok);
2511 STATIC_ASSERT_EQ(
2512 (::arene::base::substitution_succeeds<TestFixture::template try_construct_result, ivec, ivec&&>),
2513 move_ok
2514 );
2515}
2516
2517/// @test Invoking `try_construct` with an iterator range returns an `external_vector` with the same elements as the
2518/// source range
2520 auto const source = TestFixture::test_external_vector(TestFixture::standard_storage, 5);
2521
2522 alignas(TypeParam) typename TestFixture::standard_storage_type storage2{};
2523 auto maybe_vec = TestFixture::vector::try_construct(storage2, source.begin(), source.end());
2524 ASSERT_TRUE(maybe_vec.has_value());
2525
2526 ASSERT_EQ(maybe_vec->size(), source.size());
2527 for (std::size_t i = 0; i < maybe_vec->size(); ++i) {
2528 EXPECT_EQ((*maybe_vec)[i], source[i]);
2529 }
2530}
2531
2532/// @test `try_construct` from an iterator range is `noexcept` if the element type of the vector is
2533/// nothrow-constructible from the iterator's reference type
2535 using array = ::arene::base::array<TypeParam, TestFixture::capacity>;
2536 using throws_when_converted = typename TestFixture::throws_when_converted_to_t;
2537 using convertible_array = ::arene::base::array<throws_when_converted, TestFixture::capacity>;
2538 using throwing_bidirectional_iterator = typename TestFixture::throwing_bidirectional_iterator_of_t;
2539
2540 STATIC_ASSERT_EQ(
2541 noexcept(TestFixture::vector::try_construct(
2542 std::declval<arene::base::span<arene::base::byte>>(),
2543 std::declval<typename array::const_iterator>(),
2544 std::declval<typename array::const_iterator>()
2545 )),
2546 std::is_nothrow_copy_constructible<TypeParam>::value
2547 );
2548 STATIC_ASSERT_FALSE(noexcept(TestFixture::vector::try_construct(
2549 std::declval<arene::base::span<arene::base::byte>>(),
2550 std::declval<typename convertible_array::const_iterator>(),
2551 std::declval<typename convertible_array::const_iterator>()
2552 )));
2553 STATIC_ASSERT_FALSE(noexcept(TestFixture::vector::try_construct(
2554 std::declval<arene::base::span<arene::base::byte>>(),
2555 std::declval<throwing_bidirectional_iterator>(),
2556 std::declval<throwing_bidirectional_iterator>()
2557 )));
2558}
2559
2560/// @test `try_construct` from an input iterator range is `noexcept` if the element type of the vector is
2561/// nothrow-constructible from the iterator's reference type
2563 using throws_when_converted = typename TestFixture::throws_when_converted_to_t;
2564 using throwing_it_throwing_type = typename TestFixture::template throwing_input_iterator<throws_when_converted>;
2565 using throwing_it_raw_type = typename TestFixture::template throwing_input_iterator<TypeParam>;
2566 using nothrow_it_throwing_type = typename TestFixture::template non_throwing_input_iterator<throws_when_converted>;
2567 using nothrow_it_raw_type = typename TestFixture::template non_throwing_input_iterator<TypeParam>;
2568
2569 STATIC_ASSERT_FALSE(noexcept(TestFixture::vector::try_construct(
2570 std::declval<arene::base::span<arene::base::byte>>(),
2571 std::declval<std::istream_iterator<TypeParam>>(),
2572 std::declval<std::istream_iterator<TypeParam>>()
2573 )));
2574 STATIC_ASSERT_FALSE(noexcept(TestFixture::vector::try_construct(
2575 std::declval<arene::base::span<arene::base::byte>>(),
2576 std::declval<throwing_it_throwing_type>(),
2577 std::declval<throwing_it_throwing_type>()
2578 )));
2579 STATIC_ASSERT_FALSE(noexcept(TestFixture::vector::try_construct(
2580 std::declval<arene::base::span<arene::base::byte>>(),
2581 std::declval<throwing_it_raw_type>(),
2582 std::declval<throwing_it_raw_type>()
2583 )));
2584 STATIC_ASSERT_EQ(
2585 noexcept(TestFixture::vector::try_construct(
2586 std::declval<arene::base::span<arene::base::byte>>(),
2587 std::declval<nothrow_it_raw_type>(),
2588 std::declval<nothrow_it_raw_type>()
2589 )),
2590 std::is_nothrow_copy_constructible<TypeParam>::value
2591 );
2592 STATIC_ASSERT_FALSE(noexcept(TestFixture::vector::try_construct(
2593 std::declval<arene::base::span<arene::base::byte>>(),
2594 std::declval<nothrow_it_throwing_type>(),
2595 std::declval<nothrow_it_throwing_type>()
2596 )));
2597}
2598
2599/// @test Dereferencing a default-constructed `external_vector` iterator terminates the program with a precondition
2600/// violation
2602 ASSERT_DEATH(std::ignore = *typename TestFixture::vector::iterator{}, "Precondition violation");
2603 ASSERT_DEATH(std::ignore = *typename TestFixture::vector::const_iterator{}, "Precondition violation");
2604}
2605
2606/// @test Iterator arithmetic on a default-constructed `external_vector` iterator terminates the program with a
2607/// precondition violation
2609 ASSERT_DEATH(std::ignore = typename TestFixture::vector::iterator{} + 1, "Precondition violation");
2610 ASSERT_DEATH(std::ignore = typename TestFixture::vector::const_iterator{} + 1, "Precondition violation");
2611}
2612
2614
2616 ExternalVectorTest,
2665 CanPopBack,
2683 Data,
2739 Typedefs,
2741);
2742
2744 ExternalVectorDeathTest,
2758);
2759
2760} // namespace testing
2761} // namespace base
2762} // namespace arene
2763
2764#endif // INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_INLINE_CONTAINER_TESTING_EXTERNAL_VECTOR_HPP_
The Death Tests use the same fixture as the "normal" tests. Inherits so that it will be a distinct ty...
Definition external_vector.hpp:229
Test fixture for all type-parameterized external_vector tests.
Definition external_vector.hpp:73
standard_storage_type standard_storage
An instance of the default storage type to use as backing for a default capacity vector.
Definition external_vector.hpp:88
static constexpr auto test_external_vector(arene::base::span< arene::base::byte > storage, std::size_t begin, std::size_t end) noexcept(std::is_nothrow_move_constructible< T >::value) -> vector
Return a vector containing the test values [begin,end) of T , parameterized by test suite users.
Definition external_vector.hpp:195
static constexpr auto test_external_vector(arene::base::span< arene::base::byte > storage, std::size_t size) noexcept(std::is_nothrow_move_constructible< T >::value) -> vector
Return a vector containing the test values [0,size) of T , parameterized by test suite users.
Definition external_vector.hpp:207
static constexpr auto test_value(std::size_t idx) noexcept(std::is_nothrow_move_constructible< T >::value) -> T
Return the idx 'th test value of the current T , parameterized by test suite users.
Definition external_vector.hpp:186
static constexpr auto test_external_vector(arene::base::span< arene::base::byte > storage) noexcept(std::is_nothrow_move_constructible< T >::value) -> vector
Return a vector containing the test values [0,capacity) of T , parameterized by test suite users.
Definition external_vector.hpp:216
static constexpr auto construct(U &&... params) noexcept(noexcept(vector(std::forward< U >(params)...))) -> vector
Construct a vector from the passed parameters; allows calling as this->construct, which can be shorte...
Definition external_vector.hpp:179
static constexpr std::size_t capacity
The base capacity to use for most external_vector instantiations in the parameterized tests.
Definition external_vector.hpp:76
Definition customization.hpp:36
constexpr auto constexpr_resize(Vec vec, std::size_t size) -> Vec
Definition external_vector.hpp:1068
constexpr auto constexpr_iterate(Args &&... args) -> bool
Definition external_vector.hpp:663
constexpr auto bubble_sort_helper(Container container) noexcept -> Container
Inefficiently bubble sorts the argument because std::sort is not constexpr in C++14.
Definition external_vector.hpp:1614
constexpr auto test_external_vector(arene::base::span< arene::base::byte > storage, std::size_t begin, std::size_t end) noexcept(std::is_nothrow_copy_constructible< T >::value) -> ::arene::base::external_vector< T >
Create a test external vector containing a range of test values.
Definition external_vector.hpp:51
TYPED_TEST_SUITE_P(ExternalVectorDeathTest)
constexpr auto contains_duplicates(Container const &container) noexcept -> bool
Checks whether or not the given container has any duplicates, which we need to assert for test logic.
Definition external_vector.hpp:1596
TYPED_TEST_SUITE_P(ExternalVectorTest)
REGISTER_TYPED_TEST_SUITE_P(ExternalVectorTest, ADLSwapSwapsContentsForBasicTypes, AfterClearVectorIsEmpty, AfterPushBackCanGetLastElement, AfterPushBackVectorIsNotEmpty, AssignFromInitListUsingAssignFunctionIsConditionallyNoexcept, AssignFromSizeAndValueIsConditionallyNoexcept, AtIndexSizeOrLargerThrows, AtIndexSizeOrLargerThrowsWithConstVec, AtOnConstVectorReturnsConstReference, AtReturnsReference, BackIsNoexcept, BackReturnsReference, CanAccessMultipleValuesWithAt, CanAccessMultipleValuesWithAtWithConstVec, CanAccessMultipleValuesWithIndexOperator, CanAssignFromBiggerCapacityVector, CanAssignFromInitListUsingAssignFunction, CanAssignFromInputIteratorRange, CanAssignFromIteratorRange, CanAssignFromSizeAndValue, CanCompareForOrdering, CanConstructAnEmptyVector, CanConstructFromInitializerList, CanConstructFromIteratorRange, CanConstructWithSize, CanConstructWithSizeAndValue, CanConstructWithSizeViaTryConstruct, CanCopyViaConstruct, CanCopyViaConstructFromLargerWithFewerElements, CanCopyViaConstructFromSmaller, CanCopyViaTryConstruct, CanCopyViaTryConstructFromLargerWithFewerElements, CanCopyViaTryConstructFromSmaller, CanDefaultConstructViaTryConstruct, CanEmplaceBack, CanEraseAtBeginning, CanEraseInMiddle, CanEraseRange, CanGetCBeginAndEnd, CanGetFront, CanGetIterator, CanInsertAtEndOfExistingVector, CanInsertInEmptyVector, CanInsertInMiddleOfExistingVector, CanIterateOverConstValues, CanIterateOverValues, CanMoveViaConstruct, CanMoveViaTryConstruct, CanPopBack, CanPushBackAValue, CanTryConstructFromInitializerList, CanTryConstructFromIteratorRange, CannotConstructWithSizeForANonDefaultConstructibleType, CannotTryConstructWithSizeForANonDefaultConstructibleType, CapacityIsAsSpecified, CapacityIsNoexcept, ConstBackReturnsConstReference, ConstructWithExcessiveSizeViaTryConstructFails, ConstructWithSizeViaNormalConstructWithDefaultConstructorNotNoexceptIsNotNoexcept, ConstructWithSizeViaTryConstructNotNoexceptIfDefaultConstructorNotNoexcept, CopyAssignOverLargerVectorDestroysExcess, CopyAssignOverLargerVectorFromInitListDestroysExcess, CopyAssignOverSmallerVectorCopiesElements, CopyAssignOverSmallerVectorFromInitListCopiesElements, CopyAssignmentIsConditionallyNoexcept, CopyingVectorCopiesElements, Data, DefaultEmplaceIsConditionallyNoexcept, DefaultTryConstructNoexceptEvenIfDefaultConstructorNotNoexcept, EmplaceAtPositionIsConditionallyNoexcept, EmplaceBackIsConditionallyNoexcept, EmplaceInsert, EmptyIfJustStorage, EmptyIsNoexcept, EmptybyDefault, EraseIsNoexceptIfTypeHasNoexceptMove, FailToCopyViaTryConstructFromLargerWithMoreElements, FailToMoveViaTryConstructFromLargerWithMoreElements, IndexOperatorIsNoexcept, IndexOperatorOnConstVectorIsNoexcept, IndexOperatorOnConstVectorReturnsConstReference, IndexOperatorReturnsReference, InitialSizeIsZero, InputIteratorRangeAssignmentIsConditionallyNoexcept, InputIteratorRangeConstructionIsConditionallyNoexcept, InputIteratorRangeTryConstructionIsConditionallyNoexcept, InsertFromIteratorRangeInMiddle, InsertFromIteratorRangeIsConditionallyNoexcept, InsertIsConditionallyNoexcept, InsertNAtEndInsertsElements, InsertNInMiddleInsertsElements, InsertWithCountIsConditionallyNoexcept, IteratorRangeAssignmentIsConditionallyNoexcept, IteratorRangeConstructionIsConditionallyNoexcept, IteratorRangeTryConstructionIsConditionallyNoexcept, IteratorTypedefs, MaxSizeEqualsCapacity, MoveAssignOverLargerVectorDestroysExcess, MoveAssignOverSmallerVectorMovesElements, MoveAssignmentIsConditionallyNoexcept, MovingVectorMovesElements, MovingVectorWithNewStorageMovesElements, NoStorageDataIsNull, NotConstructibleWithSizeAndSourceIfDataNotCopyable, NotCopyConstructible, NotCopyConstructibleIfDataTypeNotCopyConstructible, NotMoveConstructibleIfDataTypeNotMoveConstructible, PopBackIsNoexcept, PushBackIsConditionallyNoexcept, ResizeIsConditionallyNoexcept, ResizeWithValueIsConditionallyNoexcept, ResizingFromLargeToSmallLeavesElements, ResizingFromSmallToLargeConstructsElementsAsCopiesOfSpecified, RetrievingAValueWithAtReturnsValueAfterPushBack, RetrievingAValueWithAtThrowsOnEmpty, ReverseIteration, SecondPushBackChangesLastValue, SizeIsCorrect, SizeIsNoexcept, StorageOnlyTryConstructNoexceptEvenIfDefaultConstructorNotNoexcept, SwapNoexceptMatchesElementType, TryConstructFromTooLargeInitializerListReturnsEmpty, Typedefs, ZeroSizeVectorIsEmpty)
ARENE_IGNORE_ALL("-Wfloat-equal", "These tests don't perform arithmetic, so equality is OK even for floating point")
MATCHER_P(elements_match_function, function, "")
Definition external_vector.hpp:936
constexpr bool is_fully_comparable_v
Definition external_vector.hpp:1634
REGISTER_TYPED_TEST_SUITE_P(ExternalVectorDeathTest, AddingToInvalidIteratorIsPreconditionViolation, AssignIsPreconditionViolationIfSizeOverCapacity, ConstructWithExcessiveSizeViaNormalConstructIsAlwaysPreconditionViolation, ConstructingWithOutOfRangeSizeAndSourceIsPreconditionViolation, ConstructingWithOutOfRangeSizeIsPreconditionViolation, ConstructingWithTooManyInitializersIsPreconditionViolation, DereferencingInvalidIteratorIsPreconditionViolation, EmplaceBackIsPreconditionViolationIfFull, IndexOperatorOutOfRange, InsertNIsPreconditionViolationIfTooManyItems, InsertWhenAtCapacityIsPreconditionViolation, PushBackBeyondMaxSizeIsPreconditionViolation, PushBackOnZeroSizeVectorIsPreconditionViolation)
Definition array_exceptions_disabled.cpp:11
CONDITIONAL_TYPED_TEST_P(InlineDequeTest, PushFrontOneElement, is_double_ended< TypeParam >)
Definition deque.hpp:218
TYPED_TEST_P(InlineDequeTest, CanConstruct)
Definition deque.hpp:196
CONDITIONAL_TYPED_TEST_P(InlineDequeTest, BasicFunctionFromBack, TypeParam::capacity >=2)
Definition deque.hpp:546
Copyright 2026, Toyota Motor Corporation.
Definition array_exceptions_disabled.cpp:10
An input iterator pointing to any type whose operators have adjustable noexcept specifications.
Definition external_vector.hpp:141
auto operator*() noexcept(OperatorsNoexcept) -> reference
Dereference operator; never defined.
auto operator++() noexcept(OperatorsNoexcept) -> input_iterator_with_configurable_noexcept &
Pre-increment operator; never defined.
auto operator==(input_iterator_with_configurable_noexcept const &) noexcept(OperatorsNoexcept) -> bool
Equality operator; never defined.
auto operator!=(input_iterator_with_configurable_noexcept const &) noexcept(OperatorsNoexcept) -> bool
Inequality operator; never defined.
auto operator++(int) noexcept(OperatorsNoexcept) -> input_iterator_with_configurable_noexcept
Post-increment operator; never defined.
A bidirectional iterator of T which declares that it could throw, used in some noexcept tests.
Definition external_vector.hpp:108
auto operator!=(throwing_bidirectional_iterator_of_t const &) -> bool
Inequality operator; never defined.
auto operator--() -> throwing_bidirectional_iterator_of_t &
Pre-decrement operator; never defined.
auto operator++(int) -> throwing_bidirectional_iterator_of_t
Post-increment operator; never defined.
auto operator--(int) -> throwing_bidirectional_iterator_of_t
Post-decrement operator; never defined.
auto operator==(throwing_bidirectional_iterator_of_t const &) -> bool
Equality operator; never defined.
auto operator*() -> reference
Dereference operator; never defined.
auto operator++() -> throwing_bidirectional_iterator_of_t &
Pre-increment operator; never defined.
A type with a noexcept(false) conversion operator to T (doesn't actually throw, just declares it)
Definition external_vector.hpp:100
operator T() const noexcept(false)
Implicit conversion operator to T which declares that it might throw, though it doesn't actually.
Definition external_vector.hpp:104
Definition external_vector.hpp:1707
throws_without_nullptr(T)
Definition external_vector.hpp:1708
throws_without_nullptr(T, std::nullptr_t) noexcept
Definition external_vector.hpp:1709