• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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