2 * Copyright (C) 2014 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "platform/PurgeableVector.h"
34 #include "platform/TestingPlatformSupport.h"
35 #include "public/platform/WebDiscardableMemory.h"
36 #include "wtf/Vector.h"
39 #include <gtest/gtest.h>
43 const size_t kTestSize
= 32 * 1024;
45 enum DiscardableMemorySupport
{
46 DontSupportDiscardableMemory
,
47 SupportDiscardableMemory
,
50 class PurgeableVectorTestWithPlatformSupport
: public testing::TestWithParam
<DiscardableMemorySupport
> {
52 PurgeableVectorTestWithPlatformSupport() : m_testingPlatformSupport(makeTestingPlatformSupportConfig()) { }
55 bool isDiscardableMemorySupported() const { return GetParam() == SupportDiscardableMemory
; }
57 TestingPlatformSupport::Config
makeTestingPlatformSupportConfig() const
59 TestingPlatformSupport::Config config
;
60 config
.hasDiscardableMemorySupport
= isDiscardableMemorySupported();
64 PurgeableVector::PurgeableOption
makePurgeableOption() const
66 return isDiscardableMemorySupported() ? PurgeableVector::Purgeable
: PurgeableVector::NotPurgeable
;
70 TestingPlatformSupport m_testingPlatformSupport
;
73 TEST_P(PurgeableVectorTestWithPlatformSupport
, grow
)
75 PurgeableVector
purgeableVector(makePurgeableOption());
76 purgeableVector
.grow(kTestSize
);
77 ASSERT_EQ(kTestSize
, purgeableVector
.size());
78 // Make sure the underlying buffer was actually (re)allocated.
79 memset(purgeableVector
.data(), 0, purgeableVector
.size());
82 TEST_P(PurgeableVectorTestWithPlatformSupport
, clear
)
84 Vector
<char> testData(kTestSize
);
85 std::generate(testData
.begin(), testData
.end(), &std::rand
);
87 PurgeableVector
purgeableVector(makePurgeableOption());
88 purgeableVector
.append(testData
.data(), testData
.size());
89 EXPECT_EQ(testData
.size(), purgeableVector
.size());
91 purgeableVector
.clear();
92 EXPECT_EQ(0U, purgeableVector
.size());
93 EXPECT_EQ(0, purgeableVector
.data());
96 TEST_P(PurgeableVectorTestWithPlatformSupport
, clearDoesNotResetLockCounter
)
98 PurgeableVector
purgeableVector(makePurgeableOption());
99 purgeableVector
.clear();
100 EXPECT_TRUE(purgeableVector
.isLocked());
101 purgeableVector
.unlock();
102 EXPECT_FALSE(purgeableVector
.isLocked());
105 TEST_P(PurgeableVectorTestWithPlatformSupport
, reserveCapacityDoesNotChangeSize
)
107 PurgeableVector
purgeableVector(makePurgeableOption());
108 EXPECT_EQ(0U, purgeableVector
.size());
109 purgeableVector
.reserveCapacity(kTestSize
);
110 EXPECT_EQ(0U, purgeableVector
.size());
113 TEST_P(PurgeableVectorTestWithPlatformSupport
, multipleAppends
)
115 Vector
<char> testData(kTestSize
);
116 std::generate(testData
.begin(), testData
.end(), &std::rand
);
118 PurgeableVector
purgeableVector(makePurgeableOption());
119 // Force an allocation.
120 const char kSmallString
[] = "hello";
121 purgeableVector
.append(kSmallString
, sizeof(kSmallString
));
122 const char* const data
= purgeableVector
.data();
124 // Append all the testing data in 4 iterations. The |data| pointer should
125 // have been changed at the end of the unit test due to reallocations.
126 const size_t kIterationCount
= 4;
127 ASSERT_EQ(0U, testData
.size() % kIterationCount
);
128 for (size_t i
= 0; i
< kIterationCount
; ++i
) {
129 const char* const testDataStart
= testData
.data() + i
* (testData
.size() / kIterationCount
);
130 purgeableVector
.append(testDataStart
, testData
.size() / kIterationCount
);
131 ASSERT_EQ((i
+ 1) * testData
.size() / kIterationCount
, purgeableVector
.size() - sizeof(kSmallString
));
134 ASSERT_EQ(sizeof(kSmallString
) + testData
.size(), purgeableVector
.size());
135 EXPECT_NE(data
, purgeableVector
.data());
136 EXPECT_EQ(0, memcmp(purgeableVector
.data() + sizeof(kSmallString
), testData
.data(), testData
.size()));
139 TEST_P(PurgeableVectorTestWithPlatformSupport
, multipleAppendsAfterReserveCapacity
)
141 Vector
<char> testData(kTestSize
);
142 std::generate(testData
.begin(), testData
.end(), &std::rand
);
144 PurgeableVector
purgeableVector(makePurgeableOption());
145 purgeableVector
.reserveCapacity(testData
.size());
146 const char* const data
= purgeableVector
.data();
148 // The |data| pointer should be unchanged at the end of the unit test
149 // meaning that there should not have been any reallocation.
150 const size_t kIterationCount
= 4;
151 ASSERT_EQ(0U, testData
.size() % kIterationCount
);
152 for (size_t i
= 0; i
< kIterationCount
; ++i
) {
153 const char* const testDataStart
= testData
.data() + i
* (testData
.size() / kIterationCount
);
154 purgeableVector
.append(testDataStart
, testData
.size() / kIterationCount
);
155 ASSERT_EQ((i
+ 1) * testData
.size() / kIterationCount
, purgeableVector
.size());
158 ASSERT_EQ(testData
.size(), purgeableVector
.size());
159 EXPECT_EQ(data
, purgeableVector
.data());
160 EXPECT_EQ(0, memcmp(purgeableVector
.data(), testData
.data(), testData
.size()));
163 TEST_P(PurgeableVectorTestWithPlatformSupport
, reserveCapacityUsesExactCapacityWhenVectorIsEmpty
)
165 Vector
<char> testData(kTestSize
);
166 std::generate(testData
.begin(), testData
.end(), &std::rand
);
168 PurgeableVector
purgeableVector(makePurgeableOption());
169 purgeableVector
.reserveCapacity(kTestSize
);
170 const char* const data
= purgeableVector
.data();
172 purgeableVector
.append(testData
.data(), testData
.size());
173 EXPECT_EQ(data
, purgeableVector
.data());
174 EXPECT_EQ(0, memcmp(purgeableVector
.data(), testData
.data(), testData
.size()));
176 // This test is not reliable if the PurgeableVector uses a plain WTF::Vector
177 // for storage, as it does if discardable memory is not supported; the vectors
178 // capacity will always be expanded to fill the PartitionAlloc bucket.
179 if (isDiscardableMemorySupported()) {
180 // Appending one extra byte should cause a reallocation since the first
181 // allocation happened while the purgeable vector was empty. This behavior
182 // helps us guarantee that there is no memory waste on very small vectors
183 // (which SharedBuffer requires).
184 purgeableVector
.append(testData
.data(), 1);
185 EXPECT_NE(data
, purgeableVector
.data());
189 TEST_P(PurgeableVectorTestWithPlatformSupport
, appendReservesCapacityIfNeeded
)
191 Vector
<char> testData(kTestSize
);
192 std::generate(testData
.begin(), testData
.end(), &std::rand
);
194 PurgeableVector
purgeableVector(makePurgeableOption());
195 // No reserveCapacity().
196 ASSERT_FALSE(purgeableVector
.data());
198 purgeableVector
.append(testData
.data(), testData
.size());
199 ASSERT_EQ(testData
.size(), purgeableVector
.size());
200 ASSERT_EQ(0, memcmp(purgeableVector
.data(), testData
.data(), testData
.size()));
203 TEST_P(PurgeableVectorTestWithPlatformSupport
, adopt
)
205 Vector
<char> testData(kTestSize
);
206 std::generate(testData
.begin(), testData
.end(), &std::rand
);
207 const Vector
<char> testDataCopy(testData
);
208 const char* const testDataPtr
= testData
.data();
210 PurgeableVector
purgeableVector(makePurgeableOption());
211 purgeableVector
.adopt(testData
);
212 EXPECT_TRUE(testData
.isEmpty());
213 EXPECT_EQ(kTestSize
, purgeableVector
.size());
214 ASSERT_EQ(0, memcmp(purgeableVector
.data(), testDataCopy
.data(), testDataCopy
.size()));
216 if (isDiscardableMemorySupported()) {
217 // An extra discardable memory allocation + memcpy() should have happened.
218 EXPECT_NE(testDataPtr
, purgeableVector
.data());
220 // Vector::swap() should have been used.
221 EXPECT_EQ(testDataPtr
, purgeableVector
.data());
225 TEST_P(PurgeableVectorTestWithPlatformSupport
, adoptEmptyVector
)
227 Vector
<char> testData
;
228 PurgeableVector
purgeableVector(makePurgeableOption());
229 purgeableVector
.adopt(testData
);
232 TEST(PurgeableVectorTestWithPlatformSupport
, adoptDiscardsPreviousData
)
234 Vector
<char> testData
;
235 std::generate(testData
.begin(), testData
.end(), &std::rand
);
237 PurgeableVector
purgeableVector(PurgeableVector::NotPurgeable
);
238 static const char smallString
[] = "hello";
239 purgeableVector
.append(smallString
, sizeof(smallString
));
240 ASSERT_EQ(0, memcmp(purgeableVector
.data(), smallString
, sizeof(smallString
)));
242 purgeableVector
.adopt(testData
);
243 EXPECT_EQ(testData
.size(), purgeableVector
.size());
244 ASSERT_EQ(0, memcmp(purgeableVector
.data(), testData
.data(), testData
.size()));
247 TEST_P(PurgeableVectorTestWithPlatformSupport
, unlockWithoutHintAtConstruction
)
249 Vector
<char> testData(30000);
250 std::generate(testData
.begin(), testData
.end(), &std::rand
);
252 unsigned length
= testData
.size();
253 PurgeableVector
purgeableVector(PurgeableVector::NotPurgeable
);
254 purgeableVector
.append(testData
.data(), length
);
255 ASSERT_EQ(length
, purgeableVector
.size());
256 const char* data
= purgeableVector
.data();
258 purgeableVector
.unlock();
260 // Note that the purgeable vector must be locked before calling data().
261 const bool wasPurged
= !purgeableVector
.lock();
262 if (isDiscardableMemorySupported()) {
263 // The implementation of purgeable memory used for testing always purges data upon unlock().
264 EXPECT_TRUE(wasPurged
);
267 if (isDiscardableMemorySupported()) {
268 // The data should have been moved from the heap-allocated vector to a purgeable buffer.
269 ASSERT_NE(data
, purgeableVector
.data());
271 ASSERT_EQ(data
, purgeableVector
.data());
275 ASSERT_EQ(0, memcmp(purgeableVector
.data(), testData
.data(), length
));
278 TEST(PurgeableVectorTest
, unlockOnEmptyPurgeableVector
)
280 PurgeableVector purgeableVector
;
281 ASSERT_EQ(0U, purgeableVector
.size());
282 purgeableVector
.unlock();
283 ASSERT_FALSE(purgeableVector
.isLocked());
286 TEST_P(PurgeableVectorTestWithPlatformSupport
, unlockOnPurgeableVectorWithPurgeableHint
)
288 Vector
<char> testData(kTestSize
);
289 std::generate(testData
.begin(), testData
.end(), &std::rand
);
291 PurgeableVector purgeableVector
;
292 purgeableVector
.append(testData
.data(), kTestSize
);
293 const char* const data
= purgeableVector
.data();
295 // unlock() should happen in place, i.e. without causing any reallocation.
296 // Note that the instance must be locked when data() is called.
297 purgeableVector
.unlock();
298 EXPECT_FALSE(purgeableVector
.isLocked());
299 purgeableVector
.lock();
300 EXPECT_TRUE(purgeableVector
.isLocked());
301 EXPECT_EQ(data
, purgeableVector
.data());
304 TEST_P(PurgeableVectorTestWithPlatformSupport
, lockingUsesACounter
)
306 Vector
<char> testData(kTestSize
);
307 std::generate(testData
.begin(), testData
.end(), &std::rand
);
309 PurgeableVector
purgeableVector(PurgeableVector::NotPurgeable
);
310 purgeableVector
.append(testData
.data(), testData
.size());
311 ASSERT_EQ(testData
.size(), purgeableVector
.size());
313 ASSERT_TRUE(purgeableVector
.isLocked()); // SharedBuffer is locked at creation.
314 ASSERT_TRUE(purgeableVector
.lock()); // Add an extra lock.
315 ASSERT_TRUE(purgeableVector
.isLocked());
317 purgeableVector
.unlock();
318 ASSERT_TRUE(purgeableVector
.isLocked());
320 purgeableVector
.unlock();
321 ASSERT_FALSE(purgeableVector
.isLocked());
323 if (purgeableVector
.lock())
324 ASSERT_EQ(0, memcmp(purgeableVector
.data(), testData
.data(), testData
.size()));
327 // Instantiates all the unit tests using the SharedBufferTestWithPlatformSupport fixture both with
328 // and without discardable memory support.
329 INSTANTIATE_TEST_CASE_P(testsWithPlatformSetUp
, PurgeableVectorTestWithPlatformSupport
,
330 ::testing::Values(DontSupportDiscardableMemory
, SupportDiscardableMemory
));