Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
bpo-40602: Write unit tests for _Py_hashtable_t
  • Loading branch information
vstinner committed May 14, 2020
commit 3bea283f241b60cbd88f02fceefac23c0fddbcbe
2 changes: 1 addition & 1 deletion Include/internal/pycore_hashtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ _Py_hashtable_get_entry(_Py_hashtable_t *ht, const void *key)

Use _Py_hashtable_get_entry() to distinguish entry value equal to NULL
and entry not found. */
extern void *_Py_hashtable_get(_Py_hashtable_t *ht, const void *key);
PyAPI_FUNC(void*) _Py_hashtable_get(_Py_hashtable_t *ht, const void *key);


// Remove a key and its associated value without calling key and value destroy
Expand Down
84 changes: 84 additions & 0 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "Python.h"
#include "pycore_byteswap.h" // _Py_bswap32()
#include "pycore_initconfig.h" // _Py_GetConfigsAsDict()
#include "pycore_hashtable.h" // _Py_hashtable_new()
#include "pycore_gc.h" // PyGC_Head


Expand Down Expand Up @@ -62,10 +63,93 @@ test_bswap(PyObject *self, PyObject *Py_UNUSED(args))
}


#define TO_PTR(ch) ((void*)(uintptr_t)ch)
#define FROM_PTR(ptr) ((uintptr_t)ptr)

static Py_uhash_t
hash_char(const void *key)
{
char ch = (char)FROM_PTR(key);
return ch;
}


static int
hashtable_cb(_Py_hashtable_t *table, const void *key, const void *value, void *user_data)
{
int *count = (int *)user_data;
*count += 1;
return 0;
}


static PyObject*
test_hashtable(PyObject *self, PyObject *Py_UNUSED(args))
{
_Py_hashtable_t *table = _Py_hashtable_new(hash_char,
_Py_hashtable_compare_direct);
if (table == NULL) {
return PyErr_NoMemory();
}

#define VALUE(key) (1 + ((int)(key) - 'a'))

// Test _Py_hashtable_set()
char key;
for (key='a'; key <= 'z'; key++) {
int value = VALUE(key);
if (_Py_hashtable_set(table, TO_PTR(key), TO_PTR(value)) < 0) {
_Py_hashtable_destroy(table);
return PyErr_NoMemory();
}
}
assert(table->entries == 26);
assert(table->num_buckets > table->entries);

// Test _Py_hashtable_get_entry()
for (key='a'; key <= 'z'; key++) {
_Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(table, TO_PTR(key));
assert(entry != NULL);
assert(entry->key = TO_PTR(key));
assert(entry->value = TO_PTR(VALUE(key)));
}

// Test _Py_hashtable_get()
for (key='a'; key <= 'z'; key++) {
void *value_ptr = _Py_hashtable_get(table, TO_PTR(key));
int value = (int)FROM_PTR(value_ptr);
assert(value == VALUE(key));
}

// Test _Py_hashtable_steal()
key = 'p';
void *value_ptr = _Py_hashtable_steal(table, TO_PTR(key));
int value = (int)FROM_PTR(value_ptr);
assert(value == VALUE(key));

assert(table->entries == 25);

// Test _Py_hashtable_foreach()
int count = 0;
int res = _Py_hashtable_foreach(table, hashtable_cb, &count);
assert(res == 0);
assert(count == 25);

// Test _Py_hashtable_clear()
_Py_hashtable_clear(table);
assert(table->entries == 0);
assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);

_Py_hashtable_destroy(table);
Py_RETURN_NONE;
}


static PyMethodDef TestMethods[] = {
{"get_configs", get_configs, METH_NOARGS},
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
{"test_bswap", test_bswap, METH_NOARGS},
{"test_hashtable", test_hashtable, METH_NOARGS},
{NULL, NULL} /* sentinel */
};

Expand Down