390 lines
12 KiB
C++
390 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the <organization> nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ''AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "CppUTest/TestHarness.h"
|
|
#include "CppUTest/SimpleStringInternalCache.h"
|
|
#include "CppUTest/TestTestingFixture.h"
|
|
|
|
class TestFunctionWithCache : public ExecFunction
|
|
{
|
|
public:
|
|
void (*testFunction)(SimpleStringInternalCache*, size_t);
|
|
SimpleStringInternalCache* parameter;
|
|
size_t allocationSize;
|
|
|
|
void exec() CPPUTEST_OVERRIDE
|
|
{
|
|
testFunction(parameter, allocationSize);
|
|
}
|
|
};
|
|
|
|
TEST_GROUP(SimpleStringInternalCache)
|
|
{
|
|
SimpleStringInternalCache cache;
|
|
MemoryAccountant accountant;
|
|
MemoryLeakAllocator* defaultAllocator;
|
|
AccountingTestMemoryAllocator* allocator;
|
|
|
|
TestFunctionWithCache testFunction;
|
|
TestTestingFixture fixture;
|
|
|
|
void setup() CPPUTEST_OVERRIDE
|
|
{
|
|
fixture.setTestFunction(&testFunction);
|
|
testFunction.parameter = &cache;
|
|
|
|
defaultAllocator = new MemoryLeakAllocator(defaultMallocAllocator());
|
|
allocator = new AccountingTestMemoryAllocator(accountant, defaultAllocator);
|
|
cache.setAllocator(defaultAllocator);
|
|
}
|
|
|
|
void teardown() CPPUTEST_OVERRIDE
|
|
{
|
|
cache.clearAllIncludingCurrentlyUsedMemory();
|
|
accountant.clear();
|
|
delete allocator;
|
|
delete defaultAllocator;
|
|
}
|
|
|
|
void createCacheForSize(size_t size, size_t amount)
|
|
{
|
|
for (size_t i = 0; i < amount; i++) {
|
|
char* memory = cache.alloc(size);
|
|
cache.dealloc(memory, size);
|
|
}
|
|
}
|
|
};
|
|
|
|
TEST(SimpleStringInternalCache, cacheHitWithOneEntry)
|
|
{
|
|
createCacheForSize(10, 1);
|
|
cache.setAllocator(allocator);
|
|
|
|
char* mem = cache.alloc(10);
|
|
mem[0] = 'B';
|
|
mem[3] = 'A';
|
|
mem[9] = 'S';
|
|
|
|
cache.setAllocator(allocator->originalAllocator());
|
|
|
|
LONGS_EQUAL(0, accountant.totalAllocationsOfSize(10));
|
|
CHECK(!cache.hasFreeBlocksOfSize(10));
|
|
|
|
cache.setAllocator(allocator);
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, cacheHitWithTwoEntries)
|
|
{
|
|
createCacheForSize(10, 2);
|
|
cache.setAllocator(allocator);
|
|
|
|
cache.alloc(10);
|
|
cache.alloc(10);
|
|
|
|
cache.setAllocator(allocator->originalAllocator());
|
|
|
|
LONGS_EQUAL(0, accountant.totalAllocationsOfSize(10));
|
|
CHECK(!cache.hasFreeBlocksOfSize(10));
|
|
|
|
cache.setAllocator(allocator);
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, allocatingMoreThanCacheAvailable)
|
|
{
|
|
createCacheForSize(10, 1);
|
|
cache.setAllocator(allocator);
|
|
|
|
cache.alloc(10);
|
|
cache.alloc(10);
|
|
|
|
cache.setAllocator(allocator->originalAllocator());
|
|
|
|
LONGS_EQUAL(1, accountant.totalAllocationsOfSize(32));
|
|
CHECK(!cache.hasFreeBlocksOfSize(10));
|
|
|
|
cache.setAllocator(allocator);
|
|
}
|
|
|
|
|
|
TEST(SimpleStringInternalCache, allocationWillReuseTheAllocatedBlocks)
|
|
{
|
|
cache.setAllocator(allocator);
|
|
|
|
char* mem = cache.alloc(10);
|
|
cache.dealloc(mem, 10);
|
|
mem = cache.alloc(10);
|
|
cache.dealloc(mem, 10);
|
|
|
|
LONGS_EQUAL(1, accountant.totalAllocationsOfSize(32));
|
|
}
|
|
|
|
|
|
TEST(SimpleStringInternalCache, multipleDifferentSizeAllocationsAndDeallocations)
|
|
{
|
|
cache.setAllocator(allocator);
|
|
|
|
char* mem10 = cache.alloc(10);
|
|
char* mem11 = cache.alloc(11);
|
|
|
|
char* mem100 = cache.alloc(100);
|
|
cache.dealloc(mem100, 100);
|
|
|
|
char* mem101 = cache.alloc(101);
|
|
char* mem102 = cache.alloc(102);
|
|
char* mem103 = cache.alloc(103);
|
|
cache.dealloc(mem101, 102);
|
|
cache.dealloc(mem102, 103);
|
|
cache.dealloc(mem103, 104);
|
|
|
|
cache.alloc(105);
|
|
cache.alloc(106);
|
|
cache.alloc(107);
|
|
|
|
cache.dealloc(mem10, 10);
|
|
cache.dealloc(mem11, 11);
|
|
|
|
LONGS_EQUAL(2, accountant.totalAllocationsOfSize(32));
|
|
LONGS_EQUAL(3, accountant.totalAllocationsOfSize(128));
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, deallocOfCachedMemoryWillNotDealloc)
|
|
{
|
|
cache.setAllocator(allocator);
|
|
|
|
char* mem = cache.alloc(10);
|
|
cache.dealloc(mem, 10);
|
|
|
|
LONGS_EQUAL(0, accountant.totalDeallocationsOfSize(32));
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, clearCacheWillRemoveAllCachedMemoryButNotAllUsedMemory)
|
|
{
|
|
cache.setAllocator(allocator);
|
|
|
|
char* mem = cache.alloc(10);
|
|
cache.dealloc(mem, 10);
|
|
|
|
cache.alloc(60);
|
|
|
|
cache.clearCache();
|
|
|
|
LONGS_EQUAL(1, accountant.totalDeallocationsOfSize(32));
|
|
LONGS_EQUAL(0, accountant.totalDeallocationsOfSize(64));
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, clearAllIncludingCurrentlyUsedMemory)
|
|
{
|
|
cache.setAllocator(allocator);
|
|
|
|
cache.alloc(60);
|
|
|
|
cache.clearAllIncludingCurrentlyUsedMemory();
|
|
|
|
LONGS_EQUAL(1, accountant.totalDeallocationsOfSize(64));
|
|
}
|
|
|
|
|
|
|
|
TEST(SimpleStringInternalCache, allocatingLargerStringThanCached)
|
|
{
|
|
cache.setAllocator(allocator);
|
|
|
|
char* mem = cache.alloc(1234);
|
|
cache.dealloc(mem, 1234);
|
|
|
|
LONGS_EQUAL(1, accountant.totalAllocationsOfSize(1234));
|
|
LONGS_EQUAL(1, accountant.totalDeallocationsOfSize(1234));
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, allocatingMultipleLargerStringThanCached)
|
|
{
|
|
cache.setAllocator(allocator);
|
|
|
|
char* mem = cache.alloc(1234);
|
|
char* mem2 = cache.alloc(1234);
|
|
char* mem3 = cache.alloc(1234);
|
|
|
|
cache.dealloc(mem2, 1234);
|
|
cache.dealloc(mem, 1234);
|
|
cache.dealloc(mem3, 1234);
|
|
|
|
LONGS_EQUAL(3, accountant.totalAllocationsOfSize(1234));
|
|
LONGS_EQUAL(3, accountant.totalDeallocationsOfSize(1234));
|
|
}
|
|
|
|
|
|
TEST(SimpleStringInternalCache, clearAllIncludingCurrentlyUsedMemoryAlsoReleasesLargeNonCachesMemory)
|
|
{
|
|
cache.setAllocator(allocator);
|
|
|
|
cache.alloc(1234);
|
|
cache.alloc(1234);
|
|
cache.alloc(1234);
|
|
|
|
cache.clearAllIncludingCurrentlyUsedMemory();
|
|
|
|
LONGS_EQUAL(3, accountant.totalAllocationsOfSize(1234));
|
|
LONGS_EQUAL(3, accountant.totalDeallocationsOfSize(1234));
|
|
}
|
|
|
|
static void deallocatingStringMemoryThatWasntAllocatedWithCache_(SimpleStringInternalCache* cache, size_t allocationSize)
|
|
{
|
|
char* mem = defaultMallocAllocator()->alloc_memory(allocationSize, __FILE__, __LINE__);
|
|
mem[0] = 'B';
|
|
mem[1] = 'a';
|
|
mem[2] = 's';
|
|
mem[3] = '\0';
|
|
cache->dealloc(mem, allocationSize);
|
|
defaultMallocAllocator()->free_memory(mem, allocationSize, __FILE__, __LINE__);
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, deallocatingMemoryThatWasntAllocatedWhileCacheWasInPlaceProducesWarning)
|
|
{
|
|
testFunction.testFunction = deallocatingStringMemoryThatWasntAllocatedWithCache_;
|
|
testFunction.allocationSize = 123;
|
|
|
|
cache.setAllocator(allocator);
|
|
fixture.runAllTests();
|
|
|
|
fixture.assertPrintContains("\nWARNING: Attempting to deallocate a String buffer that was allocated while not caching. Ignoring it!\n"
|
|
"This is likely due statics and will cause problems.\n"
|
|
"Only warning once to avoid recursive warnings.\n"
|
|
"String we are deallocating: \"Bas\"\n");
|
|
|
|
}
|
|
|
|
static void deallocatingStringMemoryTwiceThatWasntAllocatedWithCache_(SimpleStringInternalCache* cache, size_t allocationSize)
|
|
{
|
|
char* mem = defaultMallocAllocator()->alloc_memory(allocationSize, __FILE__, __LINE__);
|
|
mem[0] = '\0';
|
|
cache->dealloc(mem, allocationSize);
|
|
cache->dealloc(mem, allocationSize);
|
|
defaultMallocAllocator()->free_memory(mem, allocationSize, __FILE__, __LINE__);
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, deallocatingMemoryThatWasntAllocatedWhileCacheWasInPlaceProducesWarningButOnlyOnce)
|
|
{
|
|
testFunction.testFunction = deallocatingStringMemoryTwiceThatWasntAllocatedWithCache_;
|
|
testFunction.allocationSize = 123;
|
|
|
|
cache.setAllocator(allocator);
|
|
fixture.runAllTests();
|
|
|
|
LONGS_EQUAL(1, fixture.getOutput().count("WARNING"));
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, deallocatingLargeMemoryThatWasntAllocatedWhileCacheWasInPlaceProducesWarning)
|
|
{
|
|
testFunction.testFunction = deallocatingStringMemoryThatWasntAllocatedWithCache_;
|
|
testFunction.allocationSize = 12345;
|
|
|
|
cache.setAllocator(allocator);
|
|
fixture.runAllTests();
|
|
|
|
fixture.assertPrintContains("\nWARNING: Attempting to deallocate a String buffer that was allocated while not caching. Ignoring it!\n"
|
|
"This is likely due statics and will cause problems.\n"
|
|
"Only warning once to avoid recursive warnings.\n"
|
|
"String we are deallocating: \"Bas\"\n");
|
|
|
|
}
|
|
|
|
TEST(SimpleStringInternalCache, deallocatingLargeMemoryThatWasntAllocatedWhileCacheWasInPlaceProducesWarningButOnlyOnce)
|
|
{
|
|
testFunction.testFunction = deallocatingStringMemoryTwiceThatWasntAllocatedWithCache_;
|
|
testFunction.allocationSize = 12345;
|
|
|
|
cache.setAllocator(allocator);
|
|
fixture.runAllTests();
|
|
|
|
LONGS_EQUAL(1, fixture.getOutput().count("WARNING"));
|
|
}
|
|
|
|
TEST_GROUP(SimpleStringCacheAllocator)
|
|
{
|
|
SimpleStringCacheAllocator* allocator;
|
|
SimpleStringInternalCache cache;
|
|
MemoryAccountant accountant;
|
|
AccountingTestMemoryAllocator* accountingAllocator;
|
|
|
|
void setup() CPPUTEST_OVERRIDE
|
|
{
|
|
accountingAllocator = new AccountingTestMemoryAllocator(accountant, defaultMallocAllocator());
|
|
allocator = new SimpleStringCacheAllocator(cache, accountingAllocator);
|
|
}
|
|
|
|
void teardown() CPPUTEST_OVERRIDE
|
|
{
|
|
cache.clearCache();
|
|
delete allocator;
|
|
delete accountingAllocator;
|
|
}
|
|
};
|
|
|
|
TEST(SimpleStringCacheAllocator, allocationIsCached)
|
|
{
|
|
char* mem = allocator->alloc_memory(10, __FILE__, __LINE__);
|
|
allocator->free_memory(mem, 10, __FILE__, __LINE__);
|
|
|
|
size_t totalAllocations = accountant.totalAllocations();
|
|
size_t totalDeallocations = accountant.totalDeallocations();
|
|
|
|
mem = allocator->alloc_memory(10, __FILE__, __LINE__);
|
|
allocator->free_memory(mem, 10, __FILE__, __LINE__);
|
|
|
|
LONGS_EQUAL(totalAllocations, accountant.totalAllocations());
|
|
LONGS_EQUAL(totalDeallocations, accountant.totalDeallocations());
|
|
}
|
|
|
|
TEST(SimpleStringCacheAllocator, originalAllocator)
|
|
{
|
|
POINTERS_EQUAL(defaultMallocAllocator(), allocator->actualAllocator());
|
|
STRCMP_EQUAL(defaultMallocAllocator()->alloc_name(), allocator->alloc_name());
|
|
STRCMP_EQUAL(defaultMallocAllocator()->free_name(), allocator->free_name());
|
|
}
|
|
|
|
TEST(SimpleStringCacheAllocator, name)
|
|
{
|
|
STRCMP_EQUAL("SimpleStringCacheAllocator", allocator->name());
|
|
}
|
|
|
|
|
|
|
|
TEST_GROUP(GlobalSimpleStringCache)
|
|
{
|
|
};
|
|
|
|
TEST(GlobalSimpleStringCache, installsAndRemovedCache)
|
|
{
|
|
TestMemoryAllocator* originalStringAllocator = SimpleString::getStringAllocator();
|
|
{
|
|
GlobalSimpleStringCache cache;
|
|
STRCMP_EQUAL("SimpleStringCacheAllocator", SimpleString::getStringAllocator()->name());
|
|
POINTERS_EQUAL(cache.getAllocator(), SimpleString::getStringAllocator());
|
|
}
|
|
POINTERS_EQUAL(originalStringAllocator, SimpleString::getStringAllocator());
|
|
}
|