1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/containers/vector_buffer.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/memory/raw_ptr.h"
9 #include "base/test/bind.h"
10 #include "base/test/copy_only_int.h"
11 #include "base/test/move_only_int.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace base::internal {
16
17 namespace {
18
19 class TRIVIAL_ABI TrivialAbiWithCountingOperations {
20 public:
TrivialAbiWithCountingOperations(int * destruction_counter,int * move_counter)21 TrivialAbiWithCountingOperations(int* destruction_counter, int* move_counter)
22 : destruction_counter_(destruction_counter),
23 move_counter_(move_counter) {}
24
~TrivialAbiWithCountingOperations()25 ~TrivialAbiWithCountingOperations() { ++*destruction_counter_; }
26
27 // Copy construction and assignment should not be used.
28 TrivialAbiWithCountingOperations(const TrivialAbiWithCountingOperations&) =
29 delete;
30 TrivialAbiWithCountingOperations& operator=(
31 const TrivialAbiWithCountingOperations&) = delete;
32
33 // Count how many times the move constructor is used.
TrivialAbiWithCountingOperations(TrivialAbiWithCountingOperations && rhs)34 TrivialAbiWithCountingOperations(TrivialAbiWithCountingOperations&& rhs)
35 : destruction_counter_(rhs.destruction_counter_),
36 move_counter_(rhs.move_counter_) {
37 ++*move_counter_;
38 }
39
40 // Move assignment should not be used.
41 TrivialAbiWithCountingOperations& operator=(
42 TrivialAbiWithCountingOperations&&) = delete;
43
44 private:
45 raw_ptr<int> destruction_counter_;
46 raw_ptr<int> move_counter_;
47 };
48
49 } // namespace
50
TEST(VectorBuffer,DeletePOD)51 TEST(VectorBuffer, DeletePOD) {
52 constexpr int size = 10;
53 VectorBuffer<int> buffer(size);
54 for (int i = 0; i < size; i++)
55 buffer[i] = i + 1;
56
57 VectorBuffer<int>::DestructRange(buffer.as_span());
58
59 // Delete should do nothing.
60 for (int i = 0; i < size; i++)
61 EXPECT_EQ(i + 1, buffer[i]);
62 }
63
TEST(VectorBuffer,DeleteMoveOnly)64 TEST(VectorBuffer, DeleteMoveOnly) {
65 constexpr int size = 10;
66 VectorBuffer<MoveOnlyInt> buffer(size);
67 for (int i = 0; i < size; i++) {
68 // SAFETY: `i < size`, and `size` is the buffer's allocation size, so
69 // `begin() + i` is inside the buffer.
70 new (UNSAFE_BUFFERS(buffer.begin() + i)) MoveOnlyInt(i + 1);
71 }
72
73 std::vector<int> destroyed_instances;
74 auto scoped_callback_cleanup =
75 MoveOnlyInt::SetScopedDestructionCallback(BindLambdaForTesting(
76 [&](int value) { destroyed_instances.push_back(value); }));
77 VectorBuffer<MoveOnlyInt>::DestructRange(buffer.as_span());
78
79 EXPECT_THAT(destroyed_instances,
80 ::testing::ElementsAre(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
81 }
82
TEST(VectorBuffer,PODMove)83 TEST(VectorBuffer, PODMove) {
84 constexpr int size = 10;
85 VectorBuffer<int> dest(size);
86
87 VectorBuffer<int> original(size);
88 for (int i = 0; i < size; i++)
89 original[i] = i + 1;
90
91 VectorBuffer<int>::MoveConstructRange(original.as_span(), dest.as_span());
92 for (int i = 0; i < size; i++)
93 EXPECT_EQ(i + 1, dest[i]);
94 }
95
TEST(VectorBuffer,MovableMove)96 TEST(VectorBuffer, MovableMove) {
97 constexpr int size = 10;
98 VectorBuffer<MoveOnlyInt> dest(size);
99
100 VectorBuffer<MoveOnlyInt> original(size);
101 for (int i = 0; i < size; i++) {
102 // SAFETY: `i < size`, and `size` is the buffer's allocation size, so
103 // `begin() + i` is inside the buffer.
104 new (UNSAFE_BUFFERS(original.begin() + i)) MoveOnlyInt(i + 1);
105 }
106
107 std::vector<int> destroyed_instances;
108 auto scoped_callback_cleanup =
109 MoveOnlyInt::SetScopedDestructionCallback(BindLambdaForTesting(
110 [&](int value) { destroyed_instances.push_back(value); }));
111 VectorBuffer<MoveOnlyInt>::MoveConstructRange(original.as_span(),
112 dest.as_span());
113
114 for (int i = 0; i < size; i++) {
115 EXPECT_EQ(i + 1, dest[i].data());
116 }
117 // The original values were consumed, so when the original elements are
118 // destroyed, the destruction callback should report 0.
119 EXPECT_THAT(destroyed_instances,
120 ::testing::ElementsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
121 }
122
TEST(VectorBuffer,CopyToMove)123 TEST(VectorBuffer, CopyToMove) {
124 constexpr int size = 10;
125 VectorBuffer<CopyOnlyInt> dest(size);
126
127 VectorBuffer<CopyOnlyInt> original(size);
128 for (int i = 0; i < size; i++) {
129 // SAFETY: `i < size`, and `size` is the buffer's allocation size, so
130 // `begin() + i` is inside the buffer.
131 new (UNSAFE_BUFFERS(original.begin() + i)) CopyOnlyInt(i + 1);
132 }
133
134 std::vector<int> destroyed_instances;
135 auto scoped_callback_cleanup =
136 CopyOnlyInt::SetScopedDestructionCallback(BindLambdaForTesting(
137 [&](int value) { destroyed_instances.push_back(value); }));
138 VectorBuffer<CopyOnlyInt>::MoveConstructRange(original.as_span(),
139 dest.as_span());
140
141 for (int i = 0; i < size; i++) {
142 EXPECT_EQ(i + 1, dest[i].data());
143 }
144
145 EXPECT_THAT(destroyed_instances,
146 ::testing::ElementsAre(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
147 }
148
TEST(VectorBuffer,TrivialAbiMove)149 TEST(VectorBuffer, TrivialAbiMove) {
150 // Currently trivial relocation doesn't work on Windows for some reason, so
151 // the test needs to handle both cases.
152 constexpr bool kHaveTrivialRelocation =
153 IS_TRIVIALLY_RELOCATABLE(TrivialAbiWithCountingOperations);
154 constexpr int size = 10;
155 VectorBuffer<TrivialAbiWithCountingOperations> dest(size);
156
157 int destruction_count = 0;
158 int move_count = 0;
159 VectorBuffer<TrivialAbiWithCountingOperations> original(size);
160 for (int i = 0; i < size; i++) {
161 // SAFETY: `i < size`, and `size` is the buffer's allocation size, so
162 // `begin() + i` is inside the buffer.
163 new (UNSAFE_BUFFERS(original.begin() + i))
164 TrivialAbiWithCountingOperations(&destruction_count, &move_count);
165 }
166
167 VectorBuffer<TrivialAbiWithCountingOperations>::MoveConstructRange(
168 original.as_span(), dest.as_span());
169
170 // We expect the move to have been performed via memcpy, without calling move
171 // constructors or destructors.
172 EXPECT_EQ(destruction_count, kHaveTrivialRelocation ? 0 : size);
173 EXPECT_EQ(move_count, kHaveTrivialRelocation ? 0 : size);
174
175 dest.DestructRange(dest.as_span());
176 EXPECT_EQ(destruction_count, kHaveTrivialRelocation ? size : size * 2);
177 EXPECT_EQ(move_count, kHaveTrivialRelocation ? 0 : size);
178 }
179
180 } // namespace base::internal
181