Home | History | Annotate | Download | only in platform
      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