mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
unittest: Allow mocking allocator calls
This commit is contained in:
parent
b4c0c61f5c
commit
8fd3d31329
@ -17,10 +17,31 @@
|
|||||||
// Force je_ prefix on jemalloc functions.
|
// Force je_ prefix on jemalloc functions.
|
||||||
# define JEMALLOC_NO_DEMANGLE
|
# define JEMALLOC_NO_DEMANGLE
|
||||||
# include <jemalloc/jemalloc.h>
|
# include <jemalloc/jemalloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef UNIT_TESTING
|
||||||
|
# define malloc(size) mem_malloc(size)
|
||||||
|
# define calloc(count, size) mem_calloc(count, size)
|
||||||
|
# define realloc(ptr, size) mem_realloc(ptr, size)
|
||||||
|
# define free(ptr) mem_free(ptr)
|
||||||
|
# ifdef HAVE_JEMALLOC
|
||||||
|
MemMalloc mem_malloc = &je_malloc;
|
||||||
|
MemFree mem_free = &je_free;
|
||||||
|
MemCalloc mem_calloc = &je_calloc;
|
||||||
|
MemRealloc mem_realloc = &je_realloc;
|
||||||
|
# else
|
||||||
|
MemMalloc mem_malloc = &malloc;
|
||||||
|
MemFree mem_free = &free;
|
||||||
|
MemCalloc mem_calloc = &calloc;
|
||||||
|
MemRealloc mem_realloc = &realloc;
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# ifdef HAVE_JEMALLOC
|
||||||
# define malloc(size) je_malloc(size)
|
# define malloc(size) je_malloc(size)
|
||||||
# define calloc(count, size) je_calloc(count, size)
|
# define calloc(count, size) je_calloc(count, size)
|
||||||
# define realloc(ptr, size) je_realloc(ptr, size)
|
# define realloc(ptr, size) je_realloc(ptr, size)
|
||||||
# define free(ptr) je_free(ptr)
|
# define free(ptr) je_free(ptr)
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
@ -371,6 +392,7 @@ size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size)
|
|||||||
/// @return pointer to a copy of the string
|
/// @return pointer to a copy of the string
|
||||||
char *xstrdup(const char *str)
|
char *xstrdup(const char *str)
|
||||||
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
|
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
return xmemdupz(str, strlen(str));
|
return xmemdupz(str, strlen(str));
|
||||||
}
|
}
|
||||||
@ -401,6 +423,7 @@ void *xmemrchr(const void *src, uint8_t c, size_t len)
|
|||||||
/// @return pointer to a copy of the string
|
/// @return pointer to a copy of the string
|
||||||
char *xstrndup(const char *str, size_t len)
|
char *xstrndup(const char *str, size_t len)
|
||||||
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
|
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
char *p = memchr(str, '\0', len);
|
char *p = memchr(str, '\0', len);
|
||||||
return xmemdupz(str, p ? (size_t)(p - str) : len);
|
return xmemdupz(str, p ? (size_t)(p - str) : len);
|
||||||
@ -488,13 +511,13 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
|
|||||||
void free_all_mem(void)
|
void free_all_mem(void)
|
||||||
{
|
{
|
||||||
buf_T *buf, *nextbuf;
|
buf_T *buf, *nextbuf;
|
||||||
|
static bool entered = false;
|
||||||
|
|
||||||
// When we cause a crash here it is caught and Vim tries to exit cleanly.
|
/* When we cause a crash here it is caught and Vim tries to exit cleanly.
|
||||||
// Don't try freeing everything again.
|
* Don't try freeing everything again. */
|
||||||
if (entered_free_all_mem) {
|
if (entered)
|
||||||
return;
|
return;
|
||||||
}
|
entered = true;
|
||||||
entered_free_all_mem = true;
|
|
||||||
|
|
||||||
// Don't want to trigger autocommands from here on.
|
// Don't want to trigger autocommands from here on.
|
||||||
block_autocmds();
|
block_autocmds();
|
||||||
|
@ -5,6 +5,16 @@
|
|||||||
#include <stddef.h> // for size_t
|
#include <stddef.h> // for size_t
|
||||||
#include <time.h> // for time_t
|
#include <time.h> // for time_t
|
||||||
|
|
||||||
|
typedef void *(*MemMalloc)(size_t);
|
||||||
|
typedef void (*MemFree)(void *);
|
||||||
|
typedef void *(*MemCalloc)(size_t, size_t);
|
||||||
|
typedef void *(*MemRealloc)(void *, size_t);
|
||||||
|
|
||||||
|
extern MemMalloc mem_malloc;
|
||||||
|
extern MemFree mem_free;
|
||||||
|
extern MemCalloc mem_calloc;
|
||||||
|
extern MemRealloc mem_realloc;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "memory.h.generated.h"
|
# include "memory.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -122,6 +122,32 @@ typvalt2lua = function(t, processed)
|
|||||||
end)(t, processed or {}))
|
end)(t, processed or {}))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function list_iter(l)
|
||||||
|
local init_s = {
|
||||||
|
idx=0,
|
||||||
|
li=l.lv_first,
|
||||||
|
}
|
||||||
|
local function f(s, _)
|
||||||
|
-- (listitem_T *) NULL is equal to nil, but yet it is not false.
|
||||||
|
if s.li == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local ret_li = s.li
|
||||||
|
s.li = s.li.li_next
|
||||||
|
s.idx = s.idx + 1
|
||||||
|
return s.idx, ret_li
|
||||||
|
end
|
||||||
|
return f, init_s, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function list_items(l)
|
||||||
|
local ret = {}
|
||||||
|
for i, li in list_iter(l) do
|
||||||
|
ret[i] = li
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
lst2tbl = function(l, processed)
|
lst2tbl = function(l, processed)
|
||||||
if l == nil then
|
if l == nil then
|
||||||
return null_list
|
return null_list
|
||||||
@ -133,11 +159,8 @@ lst2tbl = function(l, processed)
|
|||||||
end
|
end
|
||||||
local ret = {[type_key]=list_type}
|
local ret = {[type_key]=list_type}
|
||||||
processed[p_key] = ret
|
processed[p_key] = ret
|
||||||
local li = l.lv_first
|
for i, li in list_iter(l) do
|
||||||
-- (listitem_T *) NULL is equal to nil, but yet it is not false.
|
ret[i] = typvalt2lua(li.li_tv, processed)
|
||||||
while li ~= nil do
|
|
||||||
ret[#ret + 1] = typvalt2lua(li.li_tv, processed)
|
|
||||||
li = li.li_next
|
|
||||||
end
|
end
|
||||||
if ret[1] then
|
if ret[1] then
|
||||||
ret[type_key] = nil
|
ret[type_key] = nil
|
||||||
@ -324,6 +347,22 @@ lua2typvalt = function(l, processed)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function void(ptr)
|
||||||
|
return ffi.cast('void*', ptr)
|
||||||
|
end
|
||||||
|
|
||||||
|
local alloc_logging_helpers = {
|
||||||
|
list = function(l) return {func='calloc', args={1, ffi.sizeof('list_T')}, ret=void(l)} end,
|
||||||
|
li = function(li) return {func='malloc', args={ffi.sizeof('listitem_T')}, ret=void(li)} end,
|
||||||
|
dict = function(d) return {func='malloc', args={ffi.sizeof('dict_T')}, ret=void(d)} end,
|
||||||
|
di = function(di, size)
|
||||||
|
return {func='malloc', args={ffi.offsetof('dictitem_T', 'di_key') + size + 1}, ret=void(di)}
|
||||||
|
end,
|
||||||
|
str = function(s, size) return {func='malloc', args={size + 1}, ret=void(s)} end,
|
||||||
|
|
||||||
|
freed = function(p) return {func='free', args={p and void(p)}} end,
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
null_string=null_string,
|
null_string=null_string,
|
||||||
null_list=null_list,
|
null_list=null_list,
|
||||||
@ -350,5 +389,10 @@ return {
|
|||||||
li_alloc=li_alloc,
|
li_alloc=li_alloc,
|
||||||
|
|
||||||
dict_iter=dict_iter,
|
dict_iter=dict_iter,
|
||||||
|
list_iter=list_iter,
|
||||||
first_di=first_di,
|
first_di=first_di,
|
||||||
|
|
||||||
|
alloc_logging_helpers=alloc_logging_helpers,
|
||||||
|
|
||||||
|
list_items=list_items,
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,12 @@ local neq = global_helpers.neq
|
|||||||
local eq = global_helpers.eq
|
local eq = global_helpers.eq
|
||||||
local ok = global_helpers.ok
|
local ok = global_helpers.ok
|
||||||
|
|
||||||
|
-- C constants.
|
||||||
|
local NULL = ffi.cast('void*', 0)
|
||||||
|
|
||||||
|
local OK = 1
|
||||||
|
local FAIL = 0
|
||||||
|
|
||||||
-- add some standard header locations
|
-- add some standard header locations
|
||||||
for _, p in ipairs(Paths.include_paths) do
|
for _, p in ipairs(Paths.include_paths) do
|
||||||
Preprocess.add_to_include_path(p)
|
Preprocess.add_to_include_path(p)
|
||||||
@ -118,6 +124,67 @@ local function cppimport(path)
|
|||||||
return cimport(Paths.test_include_path .. '/' .. path)
|
return cimport(Paths.test_include_path .. '/' .. path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function alloc_log_new(eq)
|
||||||
|
local log = {
|
||||||
|
log={},
|
||||||
|
lib=cimport('./src/nvim/memory.h'),
|
||||||
|
original_functions={},
|
||||||
|
null={['\0:is_null']=true},
|
||||||
|
}
|
||||||
|
local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'}
|
||||||
|
function log:save_original_functions()
|
||||||
|
for _, funcname in ipairs(allocator_functions) do
|
||||||
|
self.original_functions[funcname] = self.lib['mem_' .. funcname]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function log:set_mocks()
|
||||||
|
for _, k in ipairs(allocator_functions) do
|
||||||
|
do
|
||||||
|
local kk = k
|
||||||
|
self.lib['mem_' .. k] = function(...)
|
||||||
|
local log_entry = {func=kk, args={...}}
|
||||||
|
self.log[#self.log + 1] = log_entry
|
||||||
|
if kk == 'free' then
|
||||||
|
self.original_functions[kk](...)
|
||||||
|
else
|
||||||
|
log_entry.ret = self.original_functions[kk](...)
|
||||||
|
end
|
||||||
|
for i, v in ipairs(log_entry.args) do
|
||||||
|
if v == nil then
|
||||||
|
-- XXX This thing thinks that {NULL} ~= {NULL}.
|
||||||
|
log_entry.args[i] = self.null
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.hook then self:hook(log_entry) end
|
||||||
|
if log_entry.ret then
|
||||||
|
return log_entry.ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function log:clear()
|
||||||
|
self.log = {}
|
||||||
|
end
|
||||||
|
function log:check(exp)
|
||||||
|
eq(exp, self.log)
|
||||||
|
self:clear()
|
||||||
|
end
|
||||||
|
function log:restore_original_functions()
|
||||||
|
for k, v in pairs(self.original_functions) do
|
||||||
|
self.lib['mem_' .. k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function log:before_each()
|
||||||
|
log:save_original_functions()
|
||||||
|
log:set_mocks()
|
||||||
|
end
|
||||||
|
function log:after_each()
|
||||||
|
log:restore_original_functions()
|
||||||
|
end
|
||||||
|
return log
|
||||||
|
end
|
||||||
|
|
||||||
cimport('./src/nvim/types.h')
|
cimport('./src/nvim/types.h')
|
||||||
|
|
||||||
-- take a pointer to a C-allocated string and return an interned
|
-- take a pointer to a C-allocated string and return an interned
|
||||||
@ -142,12 +209,6 @@ do
|
|||||||
main.event_init()
|
main.event_init()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- C constants.
|
|
||||||
local NULL = ffi.cast('void*', 0)
|
|
||||||
|
|
||||||
local OK = 1
|
|
||||||
local FAIL = 0
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cimport = cimport,
|
cimport = cimport,
|
||||||
cppimport = cppimport,
|
cppimport = cppimport,
|
||||||
@ -161,5 +222,6 @@ return {
|
|||||||
to_cstr = to_cstr,
|
to_cstr = to_cstr,
|
||||||
NULL = NULL,
|
NULL = NULL,
|
||||||
OK = OK,
|
OK = OK,
|
||||||
FAIL = FAIL
|
FAIL = FAIL,
|
||||||
|
alloc_log_new = alloc_log_new,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user