Files
arcs/arcs-sdk/modules/cpputest/tests/CppUTest/MemoryLeakWarningTest.cpp
2026-08-13 16:50:52 +08:00

521 lines
17 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/TestRegistry.h"
#include "CppUTest/TestOutput.h"
#include "CppUTest/MemoryLeakWarningPlugin.h"
#include "CppUTest/MemoryLeakDetector.h"
#include "CppUTest/TestMemoryAllocator.h"
#include "CppUTest/TestTestingFixture.h"
#include "CppUTest/TestHarness_c.h"
#include "CppUTest/SimpleMutex.h"
#include "DummyMemoryLeakDetector.h"
TEST_GROUP(MemoryLeakWarningLocalDetectorTest)
{
DummyMemoryLeakFailure dummy;
};
TEST(MemoryLeakWarningLocalDetectorTest, localDetectorReturnsNewGlobalWhenNoneWasSet)
{
MemoryLeakWarningPlugin memoryLeakWarningPlugin("TestMemoryLeakWarningPlugin", NULLPTR);
CHECK(NULLPTR != memoryLeakWarningPlugin.getMemoryLeakDetector());
}
TEST(MemoryLeakWarningLocalDetectorTest, localDetectorIsTheOneSpecifiedInConstructor)
{
MemoryLeakDetector localDetector(&dummy);
MemoryLeakWarningPlugin memoryLeakWarningPlugin("TestMemoryLeakWarningPlugin", &localDetector);
POINTERS_EQUAL(&localDetector, memoryLeakWarningPlugin.getMemoryLeakDetector());
}
TEST(MemoryLeakWarningLocalDetectorTest, localDetectorIsGlobalDetector)
{
MemoryLeakDetector* globalDetector = MemoryLeakWarningPlugin::getGlobalDetector();
MemoryLeakWarningPlugin memoryLeakWarningPlugin("TestMemoryLeakWarningPlugin", NULLPTR);
MemoryLeakDetector* localDetector = memoryLeakWarningPlugin.getMemoryLeakDetector();
POINTERS_EQUAL(globalDetector, localDetector);
}
static char* leak1;
static long* leak2;
static MemoryLeakDetector* detector;
static MemoryLeakWarningPlugin* memPlugin;
static TestMemoryAllocator* allocator;
TEST_GROUP(MemoryLeakWarningTest)
{
DummyMemoryLeakFailure dummy;
TestTestingFixture* fixture;
void setup() CPPUTEST_OVERRIDE
{
fixture = new TestTestingFixture();
detector = new MemoryLeakDetector(&dummy);
allocator = new TestMemoryAllocator;
memPlugin = new MemoryLeakWarningPlugin("TestMemoryLeakWarningPlugin", detector);
fixture->installPlugin(memPlugin);
memPlugin->enable();
leak1 = NULLPTR;
leak2 = NULLPTR;
}
void teardown() CPPUTEST_OVERRIDE
{
detector->deallocMemory(allocator, leak1);
detector->deallocMemory(allocator, leak2);
delete fixture;
delete memPlugin;
delete detector;
delete allocator;
}
};
static void testTwoLeaks_()
{
leak1 = detector->allocMemory(allocator, 10);
leak2 = (long*) (void*) detector->allocMemory(allocator, 4);
}
#if CPPUTEST_USE_MEM_LEAK_DETECTION
TEST(MemoryLeakWarningTest, TwoLeaks)
{
fixture->setTestFunction(testTwoLeaks_);
fixture->runAllTests();
LONGS_EQUAL(1, fixture->getFailureCount());
}
#else
TEST(MemoryLeakWarningTest, TwoLeaks)
{
fixture->setTestFunction(testTwoLeaks_);
fixture->runAllTests();
LONGS_EQUAL(0, fixture->getFailureCount());
}
#endif
static void testLeakWarningWithPluginDisabled_()
{
memPlugin->expectLeaksInTest(1);
leak1 = (char*) cpputest_malloc_location_with_leak_detection(10, __FILE__, __LINE__);
}
TEST(MemoryLeakWarningTest, LeakWarningWithPluginDisabled)
{
fixture->setTestFunction(testLeakWarningWithPluginDisabled_);
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
fixture->runAllTests();
LONGS_EQUAL(0, fixture->getFailureCount());
fixture->assertPrintContains("Warning: Expected 1 leak(s), but leak detection was disabled");
cpputest_free_location_with_leak_detection(leak1, __FILE__, __LINE__);
leak1 = NULLPTR;
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
}
static void testIgnore2_()
{
memPlugin->expectLeaksInTest(2);
leak1 = detector->allocMemory(allocator, 10);
leak2 = (long*) (void*) detector->allocMemory(allocator, 4);
}
TEST(MemoryLeakWarningTest, Ignore2)
{
fixture->setTestFunction(testIgnore2_);
fixture->runAllTests();
LONGS_EQUAL(0, fixture->getFailureCount());
}
static void failAndLeakMemory_()
{
leak1 = detector->allocMemory(allocator, 10);
FAIL("");
}
TEST(MemoryLeakWarningTest, FailingTestDoesNotReportMemoryLeaks)
{
fixture->setTestFunction(failAndLeakMemory_);
fixture->runAllTests();
LONGS_EQUAL(1, fixture->getFailureCount());
}
static bool cpputestHasCrashed;
TEST_GROUP(MemoryLeakWarningGlobalDetectorTest)
{
MemoryLeakDetector* detector;
MemoryLeakFailure* failureReporter;
DummyMemoryLeakDetector * dummyDetector;
MemoryLeakFailure* dummyReporter;
GlobalMemoryAllocatorStash memoryAllocatorStash;
static void crashMethod()
{
cpputestHasCrashed = true;
}
void setup() CPPUTEST_OVERRIDE
{
memoryAllocatorStash.save();
detector = MemoryLeakWarningPlugin::getGlobalDetector();
failureReporter = MemoryLeakWarningPlugin::getGlobalFailureReporter();
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
dummyReporter = new DummyMemoryLeakFailure;
dummyDetector = new DummyMemoryLeakDetector(dummyReporter);
UtestShell::setCrashMethod(crashMethod);
cpputestHasCrashed = false;
}
void teardown() CPPUTEST_OVERRIDE
{
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
if (!DummyMemoryLeakDetector::wasDeleted()) delete dummyDetector;
if (!DummyMemoryLeakFailure::wasDeleted()) delete dummyReporter;
MemoryLeakWarningPlugin::setGlobalDetector(detector, failureReporter);
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
UtestShell::resetCrashMethod();
memoryAllocatorStash.restore();
}
};
TEST(MemoryLeakWarningGlobalDetectorTest, turnOffNewOverloadsCausesNoAdditionalLeaks)
{
size_t storedAmountOfLeaks = detector->totalMemoryLeaks(mem_leak_period_all);
char* arrayMemory = new char[100];
char* nonArrayMemory = new char;
char* mallocMemory = (char*) cpputest_malloc_location_with_leak_detection(10, "file", 10);
char* reallocMemory = (char*) cpputest_realloc_location_with_leak_detection(NULLPTR, 10, "file", 10);
LONGS_EQUAL(storedAmountOfLeaks, detector->totalMemoryLeaks(mem_leak_period_all));
cpputest_free_location_with_leak_detection(mallocMemory, "file", 10);
cpputest_free_location_with_leak_detection(reallocMemory, "file", 10);
delete [] arrayMemory;
delete nonArrayMemory;
}
TEST(MemoryLeakWarningGlobalDetectorTest, destroyGlobalDetector)
{
MemoryLeakWarningPlugin::setGlobalDetector(dummyDetector, dummyReporter);
MemoryLeakWarningPlugin::destroyGlobalDetector();
CHECK(DummyMemoryLeakDetector::wasDeleted());
CHECK(DummyMemoryLeakFailure::wasDeleted());
}
TEST(MemoryLeakWarningGlobalDetectorTest, MemoryWarningPluginCanBeSetToDestroyTheGlobalDetector)
{
MemoryLeakWarningPlugin* plugin = new MemoryLeakWarningPlugin("dummy");
plugin->destroyGlobalDetectorAndTurnOffMemoryLeakDetectionInDestructor(true);
MemoryLeakWarningPlugin::setGlobalDetector(dummyDetector, dummyReporter);
delete plugin;
CHECK(DummyMemoryLeakDetector::wasDeleted());
}
#if CPPUTEST_USE_MEM_LEAK_DETECTION
TEST(MemoryLeakWarningGlobalDetectorTest, crashOnLeakWithOperatorNew)
{
MemoryLeakWarningPlugin::setGlobalDetector(dummyDetector, dummyReporter);
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
crash_on_allocation_number(1);
char* memory = new char[100];
CHECK(cpputestHasCrashed);
delete [] memory;
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
}
TEST(MemoryLeakWarningGlobalDetectorTest, crashOnLeakWithOperatorNewArray)
{
MemoryLeakWarningPlugin::setGlobalDetector(dummyDetector, dummyReporter);
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
crash_on_allocation_number(1);
char* memory = new char;
CHECK(cpputestHasCrashed);
delete memory;
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
}
TEST(MemoryLeakWarningGlobalDetectorTest, crashOnLeakWithOperatorMalloc)
{
MemoryLeakWarningPlugin::setGlobalDetector(dummyDetector, dummyReporter);
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
crash_on_allocation_number(1);
char* memory = (char*) cpputest_malloc(10);
CHECK(cpputestHasCrashed);
cpputest_free(memory);
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
}
TEST(MemoryLeakWarningGlobalDetectorTest, gettingTheGlobalDetectorDoesNotRestoreTheMemoryLeakOverloadsWhenTheyWereAlreadyOff)
{
MemoryLeakWarningPlugin::setGlobalDetector(NULLPTR, NULLPTR);
MemoryLeakDetector* temporaryDetector = MemoryLeakWarningPlugin::getGlobalDetector();
MemoryLeakFailure* temporaryReporter = MemoryLeakWarningPlugin::getGlobalFailureReporter();
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
bool areNewDeleteOverloaded = MemoryLeakWarningPlugin::areNewDeleteOverloaded();
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
CHECK(!areNewDeleteOverloaded);
delete temporaryReporter;
delete temporaryDetector;
MemoryLeakWarningPlugin::setGlobalDetector(NULLPTR, NULLPTR);
}
TEST(MemoryLeakWarningGlobalDetectorTest, checkIfTheMemoryLeakOverloadsAreOn)
{
MemoryLeakWarningPlugin::turnOnDefaultNotThreadSafeNewDeleteOverloads();
CHECK(MemoryLeakWarningPlugin::areNewDeleteOverloaded());
MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
}
TEST(MemoryLeakWarningGlobalDetectorTest, checkIfTheMemoryLeakOverloadsAreOff)
{
MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
bool areNewDeleteOverloaded = MemoryLeakWarningPlugin::areNewDeleteOverloaded();
MemoryLeakWarningPlugin::turnOnDefaultNotThreadSafeNewDeleteOverloads();
CHECK(!areNewDeleteOverloaded);
}
TEST(MemoryLeakWarningGlobalDetectorTest, checkIfTheMemoryLeakOverloadsAreOnWithRestore)
{
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
CHECK(MemoryLeakWarningPlugin::areNewDeleteOverloaded());
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
}
TEST(MemoryLeakWarningGlobalDetectorTest, checkIfTheMemoryLeakOverloadsAreOffWithSaveDisable)
{
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
CHECK(!MemoryLeakWarningPlugin::areNewDeleteOverloaded());
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
}
TEST(MemoryLeakWarningGlobalDetectorTest, threadSafeMemoryLeakDetectorOverloadsAreAlsoOverloaded)
{
MemoryLeakWarningPlugin::restoreNewDeleteOverloads();
MemoryLeakWarningPlugin::turnOnThreadSafeNewDeleteOverloads();
CHECK(MemoryLeakWarningPlugin::areNewDeleteOverloaded());
MemoryLeakWarningPlugin::turnOnDefaultNotThreadSafeNewDeleteOverloads();
MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads();
}
#endif
#if CPPUTEST_USE_STD_CPP_LIB
TEST(MemoryLeakWarningGlobalDetectorTest, turnOffNewOverloadsNoThrowCausesNoAdditionalLeaks)
{
#undef new
size_t storedAmountOfLeaks = detector->totalMemoryLeaks(mem_leak_period_all);
char* nonMemoryNoThrow = new (std::nothrow) char;
char* nonArrayMemoryNoThrow = new (std::nothrow) char[10];
char* nonArrayMemoryThrow = new char[10];
LONGS_EQUAL(storedAmountOfLeaks, detector->totalMemoryLeaks(mem_leak_period_all));
::operator delete(nonMemoryNoThrow, std::nothrow);
::operator delete[](nonArrayMemoryNoThrow, std::nothrow);
::operator delete[](nonArrayMemoryThrow, std::nothrow);
#ifdef CPPUTEST_USE_NEW_MACROS
#include "CppUTest/MemoryLeakDetectorNewMacros.h"
#endif
}
#if CPPUTEST_USE_MEM_LEAK_DETECTION
static int mutexLockCount = 0;
static int mutexUnlockCount = 0;
static void StubMutexLock(PlatformSpecificMutex)
{
mutexLockCount++;
}
static void StubMutexUnlock(PlatformSpecificMutex)
{
mutexUnlockCount++;
}
TEST_GROUP(MemoryLeakWarningThreadSafe)
{
void setup() CPPUTEST_OVERRIDE
{
UT_PTR_SET(PlatformSpecificMutexLock, StubMutexLock);
UT_PTR_SET(PlatformSpecificMutexUnlock, StubMutexUnlock);
mutexLockCount = 0;
mutexUnlockCount = 0;
}
void teardown() CPPUTEST_OVERRIDE
{
}
};
TEST(MemoryLeakWarningThreadSafe, turnOnThreadSafeMallocFreeReallocOverloadsDebug)
{
size_t storedAmountOfLeaks = MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all);
MemoryLeakWarningPlugin::turnOnThreadSafeNewDeleteOverloads();
int *n = (int*) cpputest_malloc(sizeof(int));
LONGS_EQUAL(storedAmountOfLeaks + 1, MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all));
CHECK_EQUAL(1, mutexLockCount);
CHECK_EQUAL(1, mutexUnlockCount);
n = (int*) cpputest_realloc(n, sizeof(int)*3);
LONGS_EQUAL(storedAmountOfLeaks + 1, MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all));
CHECK_EQUAL(2, mutexLockCount);
CHECK_EQUAL(2, mutexUnlockCount);
cpputest_free(n);
LONGS_EQUAL(storedAmountOfLeaks, MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all));
CHECK_EQUAL(3, mutexLockCount);
CHECK_EQUAL(3, mutexUnlockCount);
MemoryLeakWarningPlugin::turnOnDefaultNotThreadSafeNewDeleteOverloads();
}
TEST(MemoryLeakWarningThreadSafe, turnOnThreadSafeNewDeleteOverloadsDebug)
{
size_t storedAmountOfLeaks = MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all);
MemoryLeakWarningPlugin::turnOnThreadSafeNewDeleteOverloads();
int *n = new int;
char *str = new char[20];
LONGS_EQUAL(storedAmountOfLeaks + 2, MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all));
CHECK_EQUAL(2, mutexLockCount);
CHECK_EQUAL(2, mutexUnlockCount);
delete [] str;
delete n;
LONGS_EQUAL(storedAmountOfLeaks, MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all));
CHECK_EQUAL(4, mutexLockCount);
CHECK_EQUAL(4, mutexUnlockCount);
MemoryLeakWarningPlugin::turnOnDefaultNotThreadSafeNewDeleteOverloads();
}
#ifdef __clang__
IGNORE_TEST(MemoryLeakWarningThreadSafe, turnOnThreadSafeNewDeleteOverloads)
{
/* Clang misbehaves with -O2 - it will not overload operator new or
* operator new[] no matter what. Therefore, this test is must be ignored.
*/
}
#else
TEST(MemoryLeakWarningThreadSafe, turnOnThreadSafeNewDeleteOverloads)
{
#undef new
size_t storedAmountOfLeaks = MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all);
MemoryLeakWarningPlugin::turnOnThreadSafeNewDeleteOverloads();
int *n = new int;
int *n_nothrow = new (std::nothrow) int;
char *str = new char[20];
char *str_nothrow = new (std::nothrow) char[20];
LONGS_EQUAL(storedAmountOfLeaks + 4, MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all));
CHECK_EQUAL(4, mutexLockCount);
CHECK_EQUAL(4, mutexUnlockCount);
delete [] str_nothrow;
delete [] str;
delete n;
delete n_nothrow;
LONGS_EQUAL(storedAmountOfLeaks, MemoryLeakWarningPlugin::getGlobalDetector()->totalMemoryLeaks(mem_leak_period_all));
CHECK_EQUAL(8, mutexLockCount);
CHECK_EQUAL(8, mutexUnlockCount);
MemoryLeakWarningPlugin::turnOnDefaultNotThreadSafeNewDeleteOverloads();
#ifdef CPPUTEST_USE_NEW_MACROS
#include "CppUTest/MemoryLeakDetectorNewMacros.h"
#endif
}
#endif
#endif
#endif