Add unit tests for platform-specific mutex implementation.

The tests are enabled with the embedded tests; the required
low-level functions from the dbus library are decorated with
DBUS_EMBEDDED_TESTS_EXPORT to indicate the appropriate usage.

On Windows, all tests are run; on unix-like operating systems,
individual tests are disabled:
- the tests on #NULL pointers of type DBus[C|R]Mutex, since they
  point to a data structure and would cause a segment violation
  when accessed.
- the multiple lock test for type DBusCMutex, since it would block
  the current thread.

Since the whole point of "rmutex" is to be able to lock multiple
times, the "rmutex double lock" test is enabled on unix-like
operating systems too.

Signed-off-by: Ralf Habacker <ralf.habacker@freenet.de>
This commit is contained in:
Ralf Habacker 2022-03-02 13:17:10 +01:00
parent 72a7758e38
commit 5f1bc83d36
6 changed files with 310 additions and 0 deletions

View file

@ -195,6 +195,13 @@ static dbus_bool_t warn_initted = FALSE;
static dbus_bool_t fatal_warnings = FALSE;
static dbus_bool_t fatal_warnings_on_check_failed = TRUE;
static int check_failed_count = 0;
int _dbus_get_check_failed_count (void)
{
return check_failed_count;
}
static void
init_warnings(void)
{
@ -221,6 +228,8 @@ init_warnings(void)
}
}
check_failed_count = 0;
warn_initted = TRUE;
}
}
@ -288,6 +297,8 @@ _dbus_warn_check_failed(const char *format,
fflush (stderr);
_dbus_abort ();
}
else
check_failed_count++;
}
#ifdef DBUS_ENABLE_VERBOSE_MODE

View file

@ -49,6 +49,9 @@ void _dbus_warn_return_if_fail (const char *function,
const char *file,
int line);
DBUS_EMBEDDED_TESTS_EXPORT
int _dbus_get_check_failed_count (void);
#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
#define _DBUS_FUNCTION_NAME __func__
#elif defined(__GNUC__) || defined(_MSC_VER)

View file

@ -81,9 +81,13 @@ void _dbus_condvar_free_at_location (DBusCondVar **location_p);
*
* @return mutex instance or #NULL on OOM
*/
DBUS_EMBEDDED_TESTS_EXPORT
DBusRMutex *_dbus_platform_rmutex_new (void);
DBUS_EMBEDDED_TESTS_EXPORT
void _dbus_platform_rmutex_free (DBusRMutex *mutex);
DBUS_EMBEDDED_TESTS_EXPORT
void _dbus_platform_rmutex_lock (DBusRMutex *mutex);
DBUS_EMBEDDED_TESTS_EXPORT
void _dbus_platform_rmutex_unlock (DBusRMutex *mutex);
/**
@ -91,9 +95,13 @@ void _dbus_platform_rmutex_unlock (DBusRMutex *mutex);
*
* @return mutex instance or #NULL on OOM
*/
DBUS_EMBEDDED_TESTS_EXPORT
DBusCMutex *_dbus_platform_cmutex_new (void);
DBUS_EMBEDDED_TESTS_EXPORT
void _dbus_platform_cmutex_free (DBusCMutex *mutex);
DBUS_EMBEDDED_TESTS_EXPORT
void _dbus_platform_cmutex_lock (DBusCMutex *mutex);
DBUS_EMBEDDED_TESTS_EXPORT
void _dbus_platform_cmutex_unlock (DBusCMutex *mutex);
DBusCondVar* _dbus_platform_condvar_new (void);

View file

@ -144,6 +144,8 @@ if(DBUS_ENABLE_EMBEDDED_TESTS)
add_test_executable(test-misc-internals "${SOURCES}" dbus-testutils)
set_target_properties(test-misc-internals PROPERTIES COMPILE_FLAGS ${DBUS_INTERNAL_CLIENT_DEFINITIONS})
add_test_executable(test-platform-mutex test-platform-mutex.c ${DBUS_INTERNAL_LIBRARIES} dbus-testutils)
set(SOURCES bus/main.c)
add_test_executable(test-bus "${SOURCES}" dbus-daemon-internal dbus-testutils ${EXPAT_LIBRARIES})
set_target_properties(test-bus PROPERTIES COMPILE_FLAGS ${DBUS_INTERNAL_CLIENT_DEFINITIONS})

View file

@ -85,6 +85,7 @@ TEST_BINARIES += test-spawn
endif
uninstallable_test_programs += \
test-platform-mutex \
test-bus \
test-bus-dispatch \
test-bus-dispatch-sha1 \
@ -256,6 +257,12 @@ test_misc_internals_SOURCES = \
$(NULL)
test_misc_internals_LDADD = libdbus-testutils.la
test_platform_mutex_SOURCES = test-platform-mutex.c
test_platform_mutex_LDADD = \
$(top_builddir)/dbus/libdbus-internal.la \
libdbus-testutils.la \
$(NULL)
EXTRA_DIST += dbus-test-runner
testexecdir = $(libexecdir)/installed-tests/dbus

279
test/test-platform-mutex.c Normal file
View file

@ -0,0 +1,279 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* test-platform-mutex.c
*
* Copyright © 2022 Ralf Habacker <ralf.habacker@freenet.de>
* SPDX-License-Identifier: AFL-2.1 or GPL-2.0-or-later
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <config.h>
#include "dbus/dbus-internals.h"
#include "dbus/dbus-spawn.h"
#include "dbus/dbus-sysdeps.h"
#include "dbus/dbus-test.h"
#include "test/test-utils.h"
#define fail_if_mutex_is_null(a) if (a == NULL) _dbus_test_fatal ("Could not create cmutex")
static dbus_bool_t
_dbus_check_cmutex_new (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusCMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
mutex = _dbus_platform_cmutex_new ();
fail_if_mutex_is_null (mutex);
_dbus_platform_cmutex_free (mutex);
return _dbus_get_check_failed_count () == count;
}
static dbus_bool_t
_dbus_check_cmutex_lock (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusCMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
mutex = _dbus_platform_cmutex_new ();
fail_if_mutex_is_null (mutex);
_dbus_platform_cmutex_lock (mutex);
_dbus_platform_cmutex_unlock (mutex);
_dbus_platform_cmutex_free (mutex);
return _dbus_get_check_failed_count () == count;
}
#ifdef DBUS_WIN
static dbus_bool_t
_dbus_check_cmutex_lock_null_pointer (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusCMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
_dbus_platform_cmutex_lock (mutex);
return _dbus_get_check_failed_count () - count == 1;
}
static dbus_bool_t
_dbus_check_cmutex_double_lock (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusCMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
mutex = _dbus_platform_cmutex_new ();
fail_if_mutex_is_null (mutex);
_dbus_platform_cmutex_lock (mutex);
_dbus_platform_cmutex_lock (mutex);
_dbus_platform_cmutex_unlock (mutex);
_dbus_platform_cmutex_unlock (mutex);
_dbus_platform_cmutex_free (mutex);
return _dbus_get_check_failed_count () == count;
}
static dbus_bool_t
_dbus_check_cmutex_unlock_null_pointer (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusCMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
_dbus_platform_cmutex_unlock (mutex);
return _dbus_get_check_failed_count () - count == 1;
}
static dbus_bool_t
_dbus_check_cmutex_null_free (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusCMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
_dbus_platform_cmutex_free (mutex); /* programming error (NULL isn't a mutex) */
return _dbus_get_check_failed_count () - count == 1;
}
static dbus_bool_t
_dbus_check_cmutex_double_free (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusCMutex *mutex = NULL;
int count;
mutex = _dbus_platform_cmutex_new ();
fail_if_mutex_is_null (mutex);
count = _dbus_get_check_failed_count ();
_dbus_platform_cmutex_free (mutex);
if (_dbus_get_check_failed_count () - count > 0)
{
_dbus_test_not_ok ("free'ing mutex failed");
return FALSE;
}
_dbus_platform_cmutex_free (mutex);
return _dbus_get_check_failed_count () - count == 1;
}
#else
/*
* #NULL pointers of type DBusCMutex cannot be tested on unix-like
* operating systems, because they are pointing to a data structure
* and would cause a segment violation when accessed.
*/
#endif
static dbus_bool_t
_dbus_check_rmutex_new (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusRMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
mutex = _dbus_platform_rmutex_new ();
fail_if_mutex_is_null (mutex);
_dbus_platform_rmutex_free (mutex);
return _dbus_get_check_failed_count () == count;
}
static dbus_bool_t
_dbus_check_rmutex_lock (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusRMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
mutex = _dbus_platform_rmutex_new ();
fail_if_mutex_is_null (mutex);
_dbus_platform_rmutex_lock (mutex);
_dbus_platform_rmutex_unlock (mutex);
_dbus_platform_rmutex_free (mutex);
return _dbus_get_check_failed_count () == count;
}
#ifdef DBUS_WIN
static dbus_bool_t
_dbus_check_rmutex_lock_null_pointer (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusRMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
_dbus_platform_rmutex_lock (mutex);
return _dbus_get_check_failed_count () - count == 1;
}
#endif
static dbus_bool_t
_dbus_check_rmutex_double_lock (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusRMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
mutex = _dbus_platform_rmutex_new ();
fail_if_mutex_is_null (mutex);
_dbus_platform_rmutex_lock (mutex);
_dbus_platform_rmutex_lock (mutex);
_dbus_platform_rmutex_unlock (mutex);
_dbus_platform_rmutex_unlock (mutex);
_dbus_platform_rmutex_free (mutex);
return _dbus_get_check_failed_count () == count;
}
#ifdef DBUS_WIN
static dbus_bool_t
_dbus_check_rmutex_unlock_null_pointer (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusRMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
_dbus_platform_rmutex_unlock (mutex);
return _dbus_get_check_failed_count () - count == 1;
}
static dbus_bool_t
_dbus_check_rmutex_null_free (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusRMutex *mutex = NULL;
int count;
count = _dbus_get_check_failed_count ();
_dbus_platform_rmutex_free (mutex); /* programming error (NULL isn't a mutex) */
return _dbus_get_check_failed_count () - count == 1;
}
static dbus_bool_t
_dbus_check_rmutex_double_free (const char *test_data_dir _DBUS_GNUC_UNUSED)
{
DBusRMutex *mutex = NULL;
int count;
mutex = _dbus_platform_rmutex_new ();
fail_if_mutex_is_null (mutex);
count = _dbus_get_check_failed_count ();
_dbus_platform_rmutex_free (mutex);
if (_dbus_get_check_failed_count () - count > 0)
{
_dbus_test_not_ok ("free'ing mutex failed");
return FALSE;
}
_dbus_platform_rmutex_free (mutex);
return _dbus_get_check_failed_count () - count == 1;
}
#else
/*
* #NULL pointers of type DBusRMutex cannot be tested on unix-like
* operating systems, because they are pointing to a data structure
* and would cause a segment violation when accessed.
*/
#endif
static DBusTestCase tests[] =
{
{ "cmutex_new", _dbus_check_cmutex_new},
{ "cmutex_lock", _dbus_check_cmutex_lock},
#ifdef DBUS_WIN
{ "cmutex_lock_null_pointer", _dbus_check_cmutex_lock_null_pointer},
{ "cmutex_double_lock", _dbus_check_cmutex_double_lock},
{ "cmutex_unlock_null_pointer", _dbus_check_cmutex_unlock_null_pointer},
{ "cmutex_null_free", _dbus_check_cmutex_null_free},
{ "cmutex_double_free", _dbus_check_cmutex_double_free},
#endif
{ "rmutex_new", _dbus_check_rmutex_new},
{ "rmutex_lock", _dbus_check_rmutex_lock},
#ifdef DBUS_WIN
{ "rmutex_lock_null_pointer", _dbus_check_rmutex_lock_null_pointer},
#endif
{ "rmutex_double_lock", _dbus_check_rmutex_double_lock},
#ifdef DBUS_WIN
{ "rmutex_unlock_null_pointer", _dbus_check_rmutex_unlock_null_pointer},
{ "rmutex_null_free", _dbus_check_rmutex_null_free},
{ "rmutex_double_free", _dbus_check_rmutex_double_free},
#endif
};
int
main (int argc,
char **argv)
{
dbus_setenv ("DBUS_FATAL_WARNINGS", "0");
return _dbus_test_main (argc, argv, _DBUS_N_ELEMENTS (tests), tests,
DBUS_TEST_FLAGS_CHECK_MEMORY_LEAKS,
NULL, NULL);
}