1 /* 2 * Copyright (C) 2014 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 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 13 * distribution. 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. 17 * 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. 29 */ 30 31 #include "config.h" 32 #include "platform/PurgeableVector.h" 33 34 #include "platform/TestingPlatformSupport.h" 35 #include "public/platform/WebDiscardableMemory.h" 36 #include "wtf/Vector.h" 37 38 #include <algorithm> 39 #include <cstdlib> 40 41 #include <gtest/gtest.h> 42 43 using namespace WebCore; 44 45 namespace { 46 47 const size_t kTestSize = 32 * 1024; 48 49 enum DiscardableMemorySupport { 50 DontSupportDiscardableMemory, 51 SupportDiscardableMemory, 52 }; 53 54 class PurgeableVectorTestWithPlatformSupport : public testing::TestWithParam<DiscardableMemorySupport> { 55 public: 56 PurgeableVectorTestWithPlatformSupport() : m_testingPlatformSupport(makeTestingPlatformSupportConfig()) { } 57 58 protected: 59 bool isDiscardableMemorySupported() const { return GetParam() == SupportDiscardableMemory; } 60 61 TestingPlatformSupport::Config makeTestingPlatformSupportConfig() const 62 { 63 TestingPlatformSupport::Config config; 64 config.hasDiscardableMemorySupport = isDiscardableMemorySupported(); 65 return config; 66 } 67 68 PurgeableVector::PurgeableOption makePurgeableOption() const 69 { 70 return isDiscardableMemorySupported() ? PurgeableVector::Purgeable : PurgeableVector::NotPurgeable; 71 } 72 73 private: 74 TestingPlatformSupport m_testingPlatformSupport; 75 }; 76 77 TEST_P(PurgeableVectorTestWithPlatformSupport, grow) 78 { 79 PurgeableVector purgeableVector(makePurgeableOption()); 80 purgeableVector.grow(kTestSize); 81 ASSERT_EQ(kTestSize, purgeableVector.size()); 82 // Make sure the underlying buffer was actually (re)allocated. 83 memset(purgeableVector.data(), 0, purgeableVector.size()); 84 } 85 86 TEST_P(PurgeableVectorTestWithPlatformSupport, clear) 87 { 88 Vector<char> testData(kTestSize); 89 std::generate(testData.begin(), testData.end(), &std::rand); 90 91 PurgeableVector purgeableVector(makePurgeableOption()); 92 purgeableVector.append(testData.data(), testData.size()); 93 EXPECT_EQ(testData.size(), purgeableVector.size()); 94 95 purgeableVector.clear(); 96 EXPECT_EQ(0U, purgeableVector.size()); 97 EXPECT_EQ(0, purgeableVector.data()); 98 } 99 100 TEST_P(PurgeableVectorTestWithPlatformSupport, clearDoesNotResetLockCounter) 101 { 102 PurgeableVector purgeableVector(makePurgeableOption()); 103 purgeableVector.clear(); 104 EXPECT_TRUE(purgeableVector.isLocked()); 105 purgeableVector.unlock(); 106 EXPECT_FALSE(purgeableVector.isLocked()); 107 } 108 109 TEST_P(PurgeableVectorTestWithPlatformSupport, reserveCapacityDoesNotChangeSize) 110 { 111 PurgeableVector purgeableVector(makePurgeableOption()); 112 EXPECT_EQ(0U, purgeableVector.size()); 113 purgeableVector.reserveCapacity(kTestSize); 114 EXPECT_EQ(0U, purgeableVector.size()); 115 } 116 117 TEST_P(PurgeableVectorTestWithPlatformSupport, multipleAppends) 118 { 119 Vector<char> testData(kTestSize); 120 std::generate(testData.begin(), testData.end(), &std::rand); 121 122 PurgeableVector purgeableVector(makePurgeableOption()); 123 // Force an allocation. 124 const char kSmallString[] = "hello"; 125 purgeableVector.append(kSmallString, sizeof(kSmallString)); 126 const char* const data = purgeableVector.data(); 127 128 // Append all the testing data in 4 iterations. The |data| pointer should 129 // have been changed at the end of the unit test due to reallocations. 130 const size_t kIterationCount = 4; 131 ASSERT_EQ(0U, testData.size() % kIterationCount); 132 for (size_t i = 0; i < kIterationCount; ++i) { 133 const char* const testDataStart = testData.data() + i * (testData.size() / kIterationCount); 134 purgeableVector.append(testDataStart, testData.size() / kIterationCount); 135 ASSERT_EQ((i + 1) * testData.size() / kIterationCount, purgeableVector.size() - sizeof(kSmallString)); 136 } 137 138 ASSERT_EQ(sizeof(kSmallString) + testData.size(), purgeableVector.size()); 139 EXPECT_NE(data, purgeableVector.data()); 140 EXPECT_EQ(0, memcmp(purgeableVector.data() + sizeof(kSmallString), testData.data(), testData.size())); 141 } 142 143 TEST_P(PurgeableVectorTestWithPlatformSupport, multipleAppendsAfterReserveCapacity) 144 { 145 Vector<char> testData(kTestSize); 146 std::generate(testData.begin(), testData.end(), &std::rand); 147 148 PurgeableVector purgeableVector(makePurgeableOption()); 149 purgeableVector.reserveCapacity(testData.size()); 150 const char* const data = purgeableVector.data(); 151 152 // The |data| pointer should be unchanged at the end of the unit test 153 // meaning that there should not have been any reallocation. 154 const size_t kIterationCount = 4; 155 ASSERT_EQ(0U, testData.size() % kIterationCount); 156 for (size_t i = 0; i < kIterationCount; ++i) { 157 const char* const testDataStart = testData.data() + i * (testData.size() / kIterationCount); 158 purgeableVector.append(testDataStart, testData.size() / kIterationCount); 159 ASSERT_EQ((i + 1) * testData.size() / kIterationCount, purgeableVector.size()); 160 } 161 162 ASSERT_EQ(testData.size(), purgeableVector.size()); 163 EXPECT_EQ(data, purgeableVector.data()); 164 EXPECT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); 165 } 166 167 TEST_P(PurgeableVectorTestWithPlatformSupport, reserveCapacityUsesExactCapacityWhenVectorIsEmpty) 168 { 169 Vector<char> testData(kTestSize); 170 std::generate(testData.begin(), testData.end(), &std::rand); 171 172 PurgeableVector purgeableVector(makePurgeableOption()); 173 purgeableVector.reserveCapacity(kTestSize); 174 const char* const data = purgeableVector.data(); 175 176 purgeableVector.append(testData.data(), testData.size()); 177 EXPECT_EQ(data, purgeableVector.data()); 178 EXPECT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); 179 180 // This test is not reliable if the PurgeableVector uses a plain WTF::Vector 181 // for storage, as it does if discardable memory is not supported; the vectors 182 // capacity will always be expanded to fill the PartitionAlloc bucket. 183 if (isDiscardableMemorySupported()) { 184 // Appending one extra byte should cause a reallocation since the first 185 // allocation happened while the purgeable vector was empty. This behavior 186 // helps us guarantee that there is no memory waste on very small vectors 187 // (which SharedBuffer requires). 188 purgeableVector.append(testData.data(), 1); 189 EXPECT_NE(data, purgeableVector.data()); 190 } 191 } 192 193 TEST_P(PurgeableVectorTestWithPlatformSupport, appendReservesCapacityIfNeeded) 194 { 195 Vector<char> testData(kTestSize); 196 std::generate(testData.begin(), testData.end(), &std::rand); 197 198 PurgeableVector purgeableVector(makePurgeableOption()); 199 // No reserveCapacity(). 200 ASSERT_FALSE(purgeableVector.data()); 201 202 purgeableVector.append(testData.data(), testData.size()); 203 ASSERT_EQ(testData.size(), purgeableVector.size()); 204 ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); 205 } 206 207 TEST_P(PurgeableVectorTestWithPlatformSupport, adopt) 208 { 209 Vector<char> testData(kTestSize); 210 std::generate(testData.begin(), testData.end(), &std::rand); 211 const Vector<char> testDataCopy(testData); 212 const char* const testDataPtr = testData.data(); 213 214 PurgeableVector purgeableVector(makePurgeableOption()); 215 purgeableVector.adopt(testData); 216 EXPECT_TRUE(testData.isEmpty()); 217 EXPECT_EQ(kTestSize, purgeableVector.size()); 218 ASSERT_EQ(0, memcmp(purgeableVector.data(), testDataCopy.data(), testDataCopy.size())); 219 220 if (isDiscardableMemorySupported()) { 221 // An extra discardable memory allocation + memcpy() should have happened. 222 EXPECT_NE(testDataPtr, purgeableVector.data()); 223 } else { 224 // Vector::swap() should have been used. 225 EXPECT_EQ(testDataPtr, purgeableVector.data()); 226 } 227 } 228 229 TEST_P(PurgeableVectorTestWithPlatformSupport, adoptEmptyVector) 230 { 231 Vector<char> testData; 232 PurgeableVector purgeableVector(makePurgeableOption()); 233 purgeableVector.adopt(testData); 234 } 235 236 TEST(PurgeableVectorTestWithPlatformSupport, adoptDiscardsPreviousData) 237 { 238 Vector<char> testData; 239 std::generate(testData.begin(), testData.end(), &std::rand); 240 241 PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); 242 static const char smallString[] = "hello"; 243 purgeableVector.append(smallString, sizeof(smallString)); 244 ASSERT_EQ(0, memcmp(purgeableVector.data(), smallString, sizeof(smallString))); 245 246 purgeableVector.adopt(testData); 247 EXPECT_EQ(testData.size(), purgeableVector.size()); 248 ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); 249 } 250 251 TEST_P(PurgeableVectorTestWithPlatformSupport, unlockWithoutHintAtConstruction) 252 { 253 Vector<char> testData(30000); 254 std::generate(testData.begin(), testData.end(), &std::rand); 255 256 unsigned length = testData.size(); 257 PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); 258 purgeableVector.append(testData.data(), length); 259 ASSERT_EQ(length, purgeableVector.size()); 260 const char* data = purgeableVector.data(); 261 262 purgeableVector.unlock(); 263 264 // Note that the purgeable vector must be locked before calling data(). 265 const bool wasPurged = !purgeableVector.lock(); 266 if (isDiscardableMemorySupported()) { 267 // The implementation of purgeable memory used for testing always purges data upon unlock(). 268 EXPECT_TRUE(wasPurged); 269 } 270 271 if (isDiscardableMemorySupported()) { 272 // The data should have been moved from the heap-allocated vector to a purgeable buffer. 273 ASSERT_NE(data, purgeableVector.data()); 274 } else { 275 ASSERT_EQ(data, purgeableVector.data()); 276 } 277 278 if (!wasPurged) 279 ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), length)); 280 } 281 282 TEST(PurgeableVectorTest, unlockOnEmptyPurgeableVector) 283 { 284 PurgeableVector purgeableVector; 285 ASSERT_EQ(0U, purgeableVector.size()); 286 purgeableVector.unlock(); 287 ASSERT_FALSE(purgeableVector.isLocked()); 288 } 289 290 TEST_P(PurgeableVectorTestWithPlatformSupport, unlockOnPurgeableVectorWithPurgeableHint) 291 { 292 Vector<char> testData(kTestSize); 293 std::generate(testData.begin(), testData.end(), &std::rand); 294 295 PurgeableVector purgeableVector; 296 purgeableVector.append(testData.data(), kTestSize); 297 const char* const data = purgeableVector.data(); 298 299 // unlock() should happen in place, i.e. without causing any reallocation. 300 // Note that the instance must be locked when data() is called. 301 purgeableVector.unlock(); 302 EXPECT_FALSE(purgeableVector.isLocked()); 303 purgeableVector.lock(); 304 EXPECT_TRUE(purgeableVector.isLocked()); 305 EXPECT_EQ(data, purgeableVector.data()); 306 } 307 308 TEST_P(PurgeableVectorTestWithPlatformSupport, lockingUsesACounter) 309 { 310 Vector<char> testData(kTestSize); 311 std::generate(testData.begin(), testData.end(), &std::rand); 312 313 PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); 314 purgeableVector.append(testData.data(), testData.size()); 315 ASSERT_EQ(testData.size(), purgeableVector.size()); 316 317 ASSERT_TRUE(purgeableVector.isLocked()); // SharedBuffer is locked at creation. 318 ASSERT_TRUE(purgeableVector.lock()); // Add an extra lock. 319 ASSERT_TRUE(purgeableVector.isLocked()); 320 321 purgeableVector.unlock(); 322 ASSERT_TRUE(purgeableVector.isLocked()); 323 324 purgeableVector.unlock(); 325 ASSERT_FALSE(purgeableVector.isLocked()); 326 327 if (purgeableVector.lock()) 328 ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); 329 } 330 331 // Instantiates all the unit tests using the SharedBufferTestWithPlatformSupport fixture both with 332 // and without discardable memory support. 333 INSTANTIATE_TEST_CASE_P(testsWithPlatformSetUp, PurgeableVectorTestWithPlatformSupport, 334 ::testing::Values(DontSupportDiscardableMemory, SupportDiscardableMemory)); 335 336 } // namespace 337 338