Test to catch Issue #17

This commit is contained in:
Rhodri James 2017-04-26 00:01:56 +02:00 committed by Sebastian Pipping
parent d307e44fbf
commit ec322eb428
7 changed files with 250 additions and 8 deletions

View file

@ -142,12 +142,12 @@ endif(BUILD_examples)
if(BUILD_tests)
## these are unittests that can be run on any platform
add_executable(runtests tests/runtests.c tests/chardata.c tests/minicheck.c)
add_executable(runtests tests/runtests.c tests/chardata.c tests/minicheck.c tests/memcheck.c)
set_property(TARGET runtests PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
target_link_libraries(runtests expat)
add_test(runtests tests/runtests)
add_executable(runtestspp tests/runtestspp.cpp tests/chardata.c tests/minicheck.c)
add_executable(runtestspp tests/runtestspp.cpp tests/chardata.c tests/minicheck.c tests/memcheck.c)
set_property(TARGET runtestspp PROPERTY RUNTIME_OUTPUT_DIRECTORY tests)
target_link_libraries(runtestspp expat)
add_test(runtestspp tests/runtestspp)

View file

@ -165,12 +165,13 @@ examples/outline: examples/outline.@OBJEXT@ $(LIBRARY)
tests/chardata.@OBJEXT@: tests/chardata.c tests/chardata.h
tests/minicheck.@OBJEXT@: tests/minicheck.c tests/minicheck.h
tests/runtests.@OBJEXT@: tests/runtests.c tests/chardata.h
tests/runtests: tests/runtests.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ $(LIBRARY)
$(LINK_EXE) tests/runtests.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ $(LIBRARY)
tests/runtestspp.@OBJEXT@: tests/runtestspp.cpp tests/runtests.c tests/chardata.h
tests/runtestspp: tests/runtestspp.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ $(LIBRARY)
$(LINK_CXX_EXE) tests/runtestspp.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ $(LIBRARY)
tests/memcheck.@OBJEXT@: tests/memcheck.c tests/memcheck.h
tests/runtests.@OBJEXT@: tests/runtests.c tests/chardata.h tests/memcheck.h
tests/runtests: tests/runtests.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ tests/memcheck.@OBJEXT@ $(LIBRARY)
$(LINK_EXE) tests/runtests.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ tests/memcheck.@OBJEXT@ $(LIBRARY)
tests/runtestspp.@OBJEXT@: tests/runtestspp.cpp tests/runtests.c tests/chardata.h tests/memcheck.h
tests/runtestspp: tests/runtestspp.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ tests/memcheck.@OBJEXT@ $(LIBRARY)
$(LINK_CXX_EXE) tests/runtestspp.@OBJEXT@ tests/chardata.@OBJEXT@ tests/minicheck.@OBJEXT@ tests/memcheck.@OBJEXT@ $(LIBRARY)
tests/benchmark/benchmark.@OBJEXT@: tests/benchmark/benchmark.c
tests/benchmark/benchmark: tests/benchmark/benchmark.@OBJEXT@ $(LIBRARY)

173
expat/tests/memcheck.c Normal file
View file

@ -0,0 +1,173 @@
/* Copyright (c) 2017 The Expat Maintainers
* Copying is permitted under the MIT license. See the file COPYING
* for details.
*
* memcheck.c : debug allocators for the Expat test suite
*/
#include <stdio.h>
#include <stdlib.h>
#include "memcheck.h"
/* Structures to keep track of what has been allocated. Speed isn't a
* big issue for the tests this is required for, so we will use a
* doubly-linked list to make deletion easier.
*/
typedef struct allocation_entry {
struct allocation_entry * next;
struct allocation_entry * prev;
void * allocation;
size_t num_bytes;
} AllocationEntry;
static AllocationEntry *alloc_head = NULL;
static AllocationEntry *alloc_tail = NULL;
static AllocationEntry *find_allocation(void *ptr);
/* Allocate some memory and keep track of it. */
void *
tracking_malloc(size_t size)
{
AllocationEntry *entry = malloc(sizeof(AllocationEntry));
if (entry == NULL) {
printf("Allocator failure\n");
return NULL;
}
entry->num_bytes = size;
entry->allocation = malloc(size);
if (entry->allocation == NULL) {
free(entry);
return NULL;
}
entry->next = NULL;
/* Add to the list of allocations */
if (alloc_head == NULL) {
entry->prev = NULL;
alloc_head = alloc_tail = entry;
} else {
entry->prev = alloc_tail;
alloc_tail->next = entry;
alloc_tail = entry;
}
return entry->allocation;
}
static AllocationEntry *
find_allocation(void *ptr)
{
AllocationEntry *entry;
for (entry = alloc_head; entry != NULL; entry = entry->next) {
if (entry->allocation == ptr) {
return entry;
}
}
return NULL;
}
/* Free some memory and remove the tracking for it */
void
tracking_free(void *ptr)
{
AllocationEntry *entry;
if (ptr == NULL) {
/* There won't be an entry for this */
return;
}
entry = find_allocation(ptr);
if (entry != NULL) {
/* This is the relevant allocation. Unlink it */
if (entry->prev != NULL)
entry->prev->next = entry->next;
else
alloc_head = entry->next;
if (entry->next != NULL)
entry->next->prev = entry->prev;
else
alloc_tail = entry->next;
free(entry);
} else {
printf("Attempting to free unallocated memory at %p\n", ptr);
}
free(ptr);
}
/* Reallocate some memory and keep track of it */
void *
tracking_realloc(void *ptr, size_t size)
{
AllocationEntry *entry;
if (ptr == NULL) {
/* By definition, this is equivalent to malloc(size) */
return tracking_malloc(size);
}
if (size == 0) {
/* By definition, this is equivalent to free(ptr) */
tracking_free(ptr);
return NULL;
}
/* Find the allocation entry for this memory */
entry = find_allocation(ptr);
if (entry == NULL) {
printf("Attempting to realloc unallocated memory at %p\n", ptr);
entry = malloc(sizeof(AllocationEntry));
if (entry == NULL) {
printf("Reallocator failure\n");
return NULL;
}
entry->allocation = realloc(ptr, size);
if (entry->allocation == NULL) {
free(entry);
return NULL;
}
/* Add to the list of allocations */
entry->next = NULL;
if (alloc_head == NULL) {
entry->prev = NULL;
alloc_head = alloc_tail = entry;
} else {
entry->prev = alloc_tail;
alloc_tail->next = entry;
alloc_tail = entry;
}
} else {
entry->allocation = realloc(ptr, size);
if (entry->allocation == NULL) {
/* Realloc semantics say the original is still allocated */
entry->allocation = ptr;
return NULL;
}
}
entry->num_bytes = size;
return entry->allocation;
}
int
tracking_report(void)
{
AllocationEntry *entry;
if (alloc_head == NULL)
return 1;
/* Otherwise we have allocations that haven't been freed */
for (entry = alloc_head; entry != NULL; entry = entry->next)
{
printf("Allocated %lu bytes at %p\n",
entry->num_bytes, entry->allocation);
}
return 0;
}

34
expat/tests/memcheck.h Normal file
View file

@ -0,0 +1,34 @@
/* Copyright (c) 2017 The Expat Maintainers
* Copying is permitted under the MIT license. See the file COPYING
* for details.
*
* memcheck.h
*
* Interface to allocation functions that will track what has or has
* not been freed.
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef XML_MEMCHECK_H
#define XML_MEMCHECK_H 1
/* Allocation declarations */
void *tracking_malloc(size_t size);
void tracking_free(void *ptr);
void *tracking_realloc(void *ptr, size_t size);
/* End-of-test check to see if unfreed allocations remain. Returns
* TRUE (1) if there is nothing, otherwise prints a report of the
* remaining allocations and returns FALSE (0).
*/
int tracking_report(void);
#endif /* XML_MEMCHECK_H */
#ifdef __cplusplus
}
#endif

View file

@ -24,6 +24,7 @@
#include "chardata.h"
#include "internal.h" /* for UNUSED_P only */
#include "minicheck.h"
#include "memcheck.h"
#if defined(__amigaos__) && defined(__USE_INLINE__)
#include <proto/expat.h>
@ -2892,6 +2893,30 @@ START_TEST(test_misc_version)
}
END_TEST
/* Regression test for GitHub Issue #17: memory leak parsing attribute
* values with mixed bound and unbound namespaces.
*/
START_TEST(test_misc_attribute_leak)
{
const char *text = "<D xmlns:L=\"D\" l:a='' L:a=''/>";
XML_Memory_Handling_Suite memsuite = {
tracking_malloc,
tracking_realloc,
tracking_free
};
parser = XML_ParserCreate_MM("UTF-8", &memsuite, "\n");
expect_failure(text, XML_ERROR_UNBOUND_PREFIX,
"Unbound prefixes not found");
XML_ParserFree(parser);
/* Prevent the teardown trying to double free */
parser = NULL;
if (!tracking_report())
fail("Memory leak found");
}
END_TEST
static void
alloc_setup(void)
@ -3431,6 +3456,7 @@ make_suite(void)
tcase_add_test(tc_misc, test_misc_alloc_ns_parse_buffer);
tcase_add_test(tc_misc, test_misc_error_string);
tcase_add_test(tc_misc, test_misc_version);
tcase_add_test(tc_misc, test_misc_attribute_leak);
suite_add_tcase(s, tc_alloc);
tcase_add_checked_fixture(tc_alloc, alloc_setup, alloc_teardown);

View file

@ -126,11 +126,13 @@
<ItemGroup>
<ClCompile Include="chardata.c" />
<ClCompile Include="minicheck.c" />
<ClCompile Include="memcheck.h" />
<ClCompile Include="runtests.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="chardata.h" />
<ClInclude Include="minicheck.h" />
<ClInclude Include="memcheck.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View file

@ -21,6 +21,9 @@
<ClCompile Include="minicheck.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="memcheck.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="runtests.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -32,5 +35,8 @@
<ClInclude Include="minicheck.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="memcheck.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>