444 lines
14 KiB
C++
444 lines
14 KiB
C++
#include "CppUTest/TestHarness.h"
|
|
#include "CppUTest/TestMemoryAllocator.h"
|
|
#include "CppUTest/MemoryLeakDetector.h"
|
|
#include "CppUTest/TestOutput.h"
|
|
#include "CppUTest/TestRegistry.h"
|
|
#include "CppUTest/PlatformSpecificFunctions.h"
|
|
#include "CppUTest/TestTestingFixture.h"
|
|
#include "AllocationInCppFile.h"
|
|
|
|
#include "CppUTest/TestHarness_c.h"
|
|
#include "AllocationInCFile.h"
|
|
|
|
|
|
TEST_GROUP(BasicBehavior)
|
|
{
|
|
};
|
|
|
|
TEST(BasicBehavior, CanDeleteNullPointers)
|
|
{
|
|
delete (char*) NULLPTR;
|
|
delete [] (char*) NULLPTR;
|
|
}
|
|
|
|
#if CPPUTEST_USE_MEM_LEAK_DETECTION
|
|
|
|
#if __cplusplus >= 201402L
|
|
TEST(BasicBehavior, DeleteWithSizeParameterWorks)
|
|
{
|
|
char* charMemory = new char;
|
|
char* charArrayMemory = new char[10];
|
|
::operator delete(charMemory, sizeof(char));
|
|
::operator delete[](charArrayMemory, sizeof(char)* 10);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef CPPUTEST_USE_MALLOC_MACROS
|
|
|
|
/* This include is added because *sometimes* the cstdlib does an #undef. This should have been prevented */
|
|
#if CPPUTEST_USE_STD_CPP_LIB
|
|
#include <cstdlib>
|
|
#endif
|
|
|
|
TEST(BasicBehavior, bothMallocAndFreeAreOverloaded)
|
|
{
|
|
void* memory = cpputest_malloc_location(sizeof(char), "file", 10);
|
|
free(memory);
|
|
|
|
memory = malloc(sizeof(unsigned char));
|
|
cpputest_free_location(memory, "file", 10);
|
|
}
|
|
|
|
#endif
|
|
|
|
TEST_GROUP(MemoryLeakOverridesToBeUsedInProductionCode)
|
|
{
|
|
MemoryLeakDetector* memLeakDetector;
|
|
void setup() CPPUTEST_OVERRIDE
|
|
{
|
|
memLeakDetector = MemoryLeakWarningPlugin::getGlobalDetector();
|
|
}
|
|
|
|
};
|
|
|
|
#if CPPUTEST_USE_MEM_LEAK_DETECTION
|
|
|
|
#ifdef CPPUTEST_USE_NEW_MACROS
|
|
#undef new
|
|
#endif
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, newDeleteOverloadsWithIntLineWorks)
|
|
{
|
|
const int line = 42;
|
|
char* leak = new("TestFile.cpp", line) char;
|
|
|
|
size_t memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
|
|
STRCMP_NOCASE_CONTAINS("Allocated at: TestFile.cpp and line: 42.", memLeakDetector->report(mem_leak_period_checking));
|
|
|
|
::operator delete (leak, "TestFile.cpp", line);
|
|
|
|
LONGS_EQUAL(memLeaks-1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, newDeleteOverloadsWithSizeTLineWorks)
|
|
{
|
|
const size_t line = 42;
|
|
char* leak = new("TestFile.cpp", line) char;
|
|
|
|
size_t memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
|
|
STRCMP_NOCASE_CONTAINS("Allocated at: TestFile.cpp and line: 42.", memLeakDetector->report(mem_leak_period_checking));
|
|
|
|
::operator delete (leak, "TestFile.cpp", line);
|
|
|
|
LONGS_EQUAL(memLeaks-1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, newDeleteArrayOverloadsWithIntLineWorks)
|
|
{
|
|
const int line = 42;
|
|
char* leak = new("TestFile.cpp", line) char[10];
|
|
|
|
size_t memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
|
|
STRCMP_NOCASE_CONTAINS("Allocated at: TestFile.cpp and line: 42.", memLeakDetector->report(mem_leak_period_checking));
|
|
|
|
::operator delete [] (leak, "TestFile.cpp", line);
|
|
|
|
LONGS_EQUAL(memLeaks-1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, newDeleteArrayOverloadsWithSizeTLineWorks)
|
|
{
|
|
const size_t line = 42;
|
|
char* leak = new("TestFile.cpp", line) char[10];
|
|
|
|
size_t memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
|
|
STRCMP_NOCASE_CONTAINS("Allocated at: TestFile.cpp and line: 42.", memLeakDetector->report(mem_leak_period_checking));
|
|
|
|
::operator delete [] (leak, "TestFile.cpp", line);
|
|
|
|
LONGS_EQUAL(memLeaks-1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
}
|
|
#ifdef CPPUTEST_USE_NEW_MACROS
|
|
#include "CppUTest/MemoryLeakDetectorNewMacros.h" // redefine the 'new' macro
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CPPUTEST_USE_MALLOC_MACROS
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocOverrideIsUsed)
|
|
{
|
|
size_t memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
|
|
void* memory = malloc(10);
|
|
LONGS_EQUAL(memLeaks+1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
free (memory);
|
|
}
|
|
|
|
#ifdef CPPUTEST_USE_STRDUP_MACROS
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, StrdupOverrideIsUsed)
|
|
{
|
|
size_t memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
|
|
char* memory = strdup("0123456789");
|
|
LONGS_EQUAL(memLeaks+1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
free (memory);
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, StrndupOverrideIsUsed)
|
|
{
|
|
size_t memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
|
|
char* memory = strndup("0123456789", 10);
|
|
LONGS_EQUAL(memLeaks+1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
free (memory);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, UseNativeMallocByTemporarlySwitchingOffMalloc)
|
|
{
|
|
size_t memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
|
|
#ifdef CPPUTEST_USE_MALLOC_MACROS
|
|
#undef malloc
|
|
#undef free
|
|
#endif
|
|
|
|
#if CPPUTEST_USE_STD_C_LIB
|
|
void* memory = malloc(10);
|
|
LONGS_EQUAL(memLeaks, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
free (memory);
|
|
#else
|
|
void* memory = PlatformSpecificMalloc(10);
|
|
LONGS_EQUAL(memLeaks, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
PlatformSpecificFree (memory);
|
|
#endif
|
|
|
|
#ifdef CPPUTEST_USE_MALLOC_MACROS
|
|
#include "CppUTest/MemoryLeakDetectorMallocMacros.h"
|
|
#endif
|
|
}
|
|
|
|
/* TEST... allowing for a new overload in a class */
|
|
class NewDummyClass
|
|
{
|
|
public:
|
|
static bool overloaded_new_called;
|
|
|
|
#ifdef CPPUTEST_USE_NEW_MACROS
|
|
#undef new
|
|
#endif
|
|
void* operator new (size_t size)
|
|
#ifdef CPPUTEST_USE_NEW_MACROS
|
|
#include "CppUTest/MemoryLeakDetectorNewMacros.h"
|
|
#endif
|
|
{
|
|
overloaded_new_called = true;
|
|
return malloc(size);
|
|
}
|
|
void dummyFunction()
|
|
{
|
|
char* memory = new char;
|
|
delete memory;
|
|
}
|
|
};
|
|
bool NewDummyClass::overloaded_new_called = false;
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, NoSideEffectsFromTurningOffNewMacros)
|
|
{
|
|
/*
|
|
* Interesting effect of wrapping the operator new around the macro is
|
|
* that the actual new that is called is a different one than expected.
|
|
*
|
|
* The overloaded operator new doesn't actually ever get called.
|
|
*
|
|
* This might come as a surprise, so it is important to realize!
|
|
*/
|
|
NewDummyClass dummy;
|
|
dummy.dummyFunction();
|
|
// CHECK(dummy.overloaded_new_called);
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, UseNativeNewByTemporarlySwitchingOffNew)
|
|
{
|
|
#ifdef CPPUTEST_USE_NEW_MACROS
|
|
#undef new
|
|
#undef delete
|
|
#endif
|
|
char* memory = new char[10];
|
|
delete [] memory;
|
|
#ifdef CPPUTEST_USE_NEW_MACROS
|
|
#include "CppUTest/MemoryLeakDetectorNewMacros.h"
|
|
#endif
|
|
}
|
|
|
|
|
|
#if CPPUTEST_USE_MEM_LEAK_DETECTION
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewMacroOverloadViaIncludeFileWorks)
|
|
{
|
|
char* leak = newAllocation();
|
|
STRCMP_NOCASE_CONTAINS("AllocationInCppFile.cpp", memLeakDetector->report(mem_leak_period_checking));
|
|
delete leak;
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewArrayMacroOverloadViaIncludeFileWorks)
|
|
{
|
|
char* leak = newArrayAllocation();
|
|
STRCMP_NOCASE_CONTAINS("AllocationInCppFile.cpp", memLeakDetector->report(mem_leak_period_checking));
|
|
delete[] leak;
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocOverrideWorks)
|
|
{
|
|
char* leak = mallocAllocation();
|
|
STRCMP_NOCASE_CONTAINS("AllocationInCFile.c", memLeakDetector->report(mem_leak_period_checking));
|
|
freeAllocation(leak);
|
|
}
|
|
|
|
#ifdef CPPUTEST_USE_STRDUP_MACROS
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, StrdupOverrideWorks)
|
|
{
|
|
char* leak = strdupAllocation();
|
|
STRCMP_NOCASE_CONTAINS("AllocationInCFile.c", memLeakDetector->report(mem_leak_period_checking));
|
|
freeAllocation(leak);
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, StrndupOverrideWorks)
|
|
{
|
|
char* leak = strndupAllocation();
|
|
STRCMP_NOCASE_CONTAINS("AllocationInCFile.c", memLeakDetector->report(mem_leak_period_checking));
|
|
freeAllocation(leak);
|
|
}
|
|
#endif
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocWithButFreeWithoutLeakDetectionDoesntCrash)
|
|
{
|
|
char* leak = mallocAllocation();
|
|
freeAllocationWithoutMacro(leak);
|
|
LONGS_EQUAL(2, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
|
|
memLeakDetector->removeMemoryLeakInformationWithoutCheckingOrDeallocatingTheMemoryButDeallocatingTheAccountInformation(getCurrentMallocAllocator(), leak, true);
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewOverloadingWithoutMacroWorks)
|
|
{
|
|
char* leak = newAllocationWithoutMacro();
|
|
STRCMP_CONTAINS("unknown", memLeakDetector->report(mem_leak_period_checking));
|
|
delete leak;
|
|
}
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewArrayOverloadingWithoutMacroWorks)
|
|
{
|
|
char* leak = newArrayAllocationWithoutMacro();
|
|
STRCMP_CONTAINS("unknown", memLeakDetector->report(mem_leak_period_checking));
|
|
delete[] leak;
|
|
}
|
|
|
|
#else
|
|
|
|
TEST(MemoryLeakOverridesToBeUsedInProductionCode, MemoryOverridesAreDisabled)
|
|
{
|
|
char* leak = newAllocation();
|
|
STRCMP_EQUAL("No memory leaks were detected.", memLeakDetector->report(mem_leak_period_checking));
|
|
delete leak;
|
|
}
|
|
|
|
#endif
|
|
|
|
TEST_GROUP(OutOfMemoryTestsForOperatorNew)
|
|
{
|
|
TestMemoryAllocator* no_memory_allocator;
|
|
GlobalMemoryAllocatorStash memoryAllocatorStash;
|
|
void setup() CPPUTEST_OVERRIDE
|
|
{
|
|
memoryAllocatorStash.save();
|
|
no_memory_allocator = new NullUnknownAllocator;
|
|
setCurrentNewAllocator(no_memory_allocator);
|
|
setCurrentNewArrayAllocator(no_memory_allocator);
|
|
}
|
|
|
|
void teardown() CPPUTEST_OVERRIDE
|
|
{
|
|
memoryAllocatorStash.restore();
|
|
delete no_memory_allocator;
|
|
}
|
|
};
|
|
|
|
#if CPPUTEST_USE_MEM_LEAK_DETECTION
|
|
|
|
#if CPPUTEST_HAVE_EXCEPTIONS
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorThrowsAnExceptionWhenUsingStdCppNew)
|
|
{
|
|
CHECK_THROWS(CPPUTEST_BAD_ALLOC, new char);
|
|
}
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorThrowsAnExceptionWhenUsingStdCppNew)
|
|
{
|
|
CHECK_THROWS(CPPUTEST_BAD_ALLOC, new char[10]);
|
|
}
|
|
|
|
TEST_GROUP(TestForExceptionsInConstructor)
|
|
{
|
|
};
|
|
|
|
TEST(TestForExceptionsInConstructor,ConstructorThrowsAnException)
|
|
{
|
|
CHECK_THROWS(int, new ClassThatThrowsAnExceptionInTheConstructor);
|
|
}
|
|
|
|
TEST(TestForExceptionsInConstructor,ConstructorThrowsAnExceptionAllocatedAsArray)
|
|
{
|
|
CHECK_THROWS(int, new ClassThatThrowsAnExceptionInTheConstructor[10]);
|
|
}
|
|
|
|
#else
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNull)
|
|
{
|
|
POINTERS_EQUAL(NULLPTR, new char);
|
|
}
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNull)
|
|
{
|
|
POINTERS_EQUAL(NULLPTR, new char[10]);
|
|
}
|
|
|
|
#endif
|
|
|
|
#undef new
|
|
|
|
#if CPPUTEST_HAVE_EXCEPTIONS
|
|
|
|
|
|
/*
|
|
* CLang 4.2 and memory allocation.
|
|
*
|
|
* Clang 4.2 has done some optimizations to their memory management that actually causes slightly different behavior than what the C++ Standard defines.
|
|
* Usually this is not a problem... but in this case, it is a problem.
|
|
*
|
|
* More information about the optimization can be found at: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3433.html
|
|
* We've done a bug-report to clang to fix some of this non-standard behavior, which is open at: http://llvm.org/bugs/show_bug.cgi?id=15541
|
|
*
|
|
* I very much hope that nobody would actually ever hit this bug/optimization as it is hard to figure out what is going on.
|
|
*
|
|
* The original test simply did "new char". Because the memory wasn't assigned to anything and is local in context, the optimization *doesn't* call
|
|
* the operator new overload. Because it doesn't call the operator new (optimizing away a call to operator new), therefore the method wouldn't throw an exception
|
|
* and therefore this test failed.
|
|
*
|
|
* The first attempt to fix this is to create a local variable and assigned the memory to that. Also this doesn't work as it still detects the allocation is
|
|
* local and optimizes away the memory call.
|
|
*
|
|
* Now, we assign the memory on some static global which fools the optimizer to believe that it isn't local and it stops optimizing the operator new call.
|
|
*
|
|
* We (Bas Vodde and Terry Yin) suspect that in a real product, you wouldn't be able to detect the optimization and it's breaking of Standard C++. Therefore,
|
|
* for now, we keep this hack in the test to fool the optimizer and hope nobody will ever notice this 'optimizer behavior' in a real product.
|
|
*
|
|
* Update 2020: The gcc compiler implemented the same optimization, but it seems to be slightly smarter and discovered that we assign to a static variable.
|
|
* Thus it still optimized away the call to operator new. Did another bug report, but it is unlikely to get fixed. You can find it at:
|
|
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94671
|
|
*
|
|
* Changed the variable to be external so it would definitively be a mistake to optimize the call.
|
|
*
|
|
*/
|
|
|
|
extern char* some_memory;
|
|
char* some_memory;
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorThrowsAnExceptionWhenUsingStdCppNewWithoutOverride)
|
|
{
|
|
CHECK_THROWS(CPPUTEST_BAD_ALLOC, some_memory = new char);
|
|
}
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorThrowsAnExceptionWhenUsingStdCppNewWithoutOverride)
|
|
{
|
|
CHECK_THROWS(CPPUTEST_BAD_ALLOC, some_memory = new char[10]);
|
|
}
|
|
|
|
#if CPPUTEST_USE_STD_CPP_LIB
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNullWithoutOverride)
|
|
{
|
|
POINTERS_EQUAL(NULLPTR, new (std::nothrow) char);
|
|
}
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNullWithoutOverride)
|
|
{
|
|
POINTERS_EQUAL(NULLPTR, new (std::nothrow) char[10]);
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNullWithoutOverride)
|
|
{
|
|
POINTERS_EQUAL(NULLPTR, new char);
|
|
}
|
|
|
|
TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNullWithoutOverride)
|
|
{
|
|
POINTERS_EQUAL(NULLPTR, new char[10]);
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|