mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2025-12-20 08:00:11 +01:00
Similar to ebf487ef, but in a different situation.
Signed-off-by: Simon McVittie <smcv@collabora.com>
880 lines
22 KiB
C
880 lines
22 KiB
C
/*
|
|
* Copyright 2002-2009 Red Hat Inc.
|
|
* Copyright 2011-2017 Collabora Ltd.
|
|
* Copyright 2017 Endless Mobile, Inc.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction,
|
|
* including without limitation the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "test-utils.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#if HAVE_LOCALE_H
|
|
#include <locale.h>
|
|
#endif
|
|
|
|
#include <dbus/dbus-sysdeps.h>
|
|
|
|
#ifdef DBUS_UNIX
|
|
# include <sys/types.h>
|
|
# include <unistd.h>
|
|
|
|
# include <dbus/dbus-sysdeps-unix.h>
|
|
#else
|
|
# include <dbus/dbus-sysdeps-win.h>
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
/* Necessary for the Linux-specific fd leak checking code only */
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#include "dbus/dbus-message-internal.h"
|
|
|
|
/*
|
|
* Like strdup(), but crash on out-of-memory, and pass through NULL
|
|
* unchanged (the "0" in the name is meant to be a mnemonic for this,
|
|
* similar to g_strcmp0()).
|
|
*/
|
|
static char *
|
|
strdup0_or_die (const char *str)
|
|
{
|
|
char *ret;
|
|
|
|
if (str == NULL)
|
|
return NULL; /* not an error */
|
|
|
|
ret = strdup (str);
|
|
|
|
if (ret == NULL)
|
|
_dbus_test_fatal ("Out of memory");
|
|
|
|
return ret;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
DBusLoop *loop;
|
|
DBusConnection *connection;
|
|
|
|
} CData;
|
|
|
|
static dbus_bool_t
|
|
add_watch (DBusWatch *watch,
|
|
void *data)
|
|
{
|
|
CData *cd = data;
|
|
|
|
return _dbus_loop_add_watch (cd->loop, watch);
|
|
}
|
|
|
|
static void
|
|
remove_watch (DBusWatch *watch,
|
|
void *data)
|
|
{
|
|
CData *cd = data;
|
|
|
|
_dbus_loop_remove_watch (cd->loop, watch);
|
|
}
|
|
|
|
static void
|
|
toggle_watch (DBusWatch *watch,
|
|
void *data)
|
|
{
|
|
CData *cd = data;
|
|
|
|
_dbus_loop_toggle_watch (cd->loop, watch);
|
|
}
|
|
|
|
static dbus_bool_t
|
|
add_timeout (DBusTimeout *timeout,
|
|
void *data)
|
|
{
|
|
CData *cd = data;
|
|
|
|
return _dbus_loop_add_timeout (cd->loop, timeout);
|
|
}
|
|
|
|
static void
|
|
remove_timeout (DBusTimeout *timeout,
|
|
void *data)
|
|
{
|
|
CData *cd = data;
|
|
|
|
_dbus_loop_remove_timeout (cd->loop, timeout);
|
|
}
|
|
|
|
static void
|
|
dispatch_status_function (DBusConnection *connection,
|
|
DBusDispatchStatus new_status,
|
|
void *data)
|
|
{
|
|
DBusLoop *loop = data;
|
|
|
|
if (new_status != DBUS_DISPATCH_COMPLETE)
|
|
{
|
|
while (!_dbus_loop_queue_dispatch (loop, connection))
|
|
_dbus_wait_for_memory ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
cdata_free (void *data)
|
|
{
|
|
CData *cd = data;
|
|
|
|
dbus_connection_unref (cd->connection);
|
|
_dbus_loop_unref (cd->loop);
|
|
|
|
dbus_free (cd);
|
|
}
|
|
|
|
static CData*
|
|
cdata_new (DBusLoop *loop,
|
|
DBusConnection *connection)
|
|
{
|
|
CData *cd;
|
|
|
|
cd = dbus_new0 (CData, 1);
|
|
if (cd == NULL)
|
|
return NULL;
|
|
|
|
cd->loop = loop;
|
|
cd->connection = connection;
|
|
|
|
dbus_connection_ref (cd->connection);
|
|
_dbus_loop_ref (cd->loop);
|
|
|
|
return cd;
|
|
}
|
|
|
|
dbus_bool_t
|
|
test_connection_try_setup (TestMainContext *ctx,
|
|
DBusConnection *connection)
|
|
{
|
|
DBusLoop *loop = ctx;
|
|
CData *cd;
|
|
|
|
cd = NULL;
|
|
|
|
dbus_connection_set_dispatch_status_function (connection, dispatch_status_function,
|
|
loop, NULL);
|
|
/* ownership of cd taken */
|
|
|
|
cd = cdata_new (loop, connection);
|
|
if (cd == NULL)
|
|
goto nomem;
|
|
|
|
if (!dbus_connection_set_watch_functions (connection,
|
|
add_watch,
|
|
remove_watch,
|
|
toggle_watch,
|
|
cd, cdata_free))
|
|
goto nomem;
|
|
|
|
|
|
cd = cdata_new (loop, connection);
|
|
if (cd == NULL)
|
|
goto nomem;
|
|
|
|
if (!dbus_connection_set_timeout_functions (connection,
|
|
add_timeout,
|
|
remove_timeout,
|
|
NULL,
|
|
cd, cdata_free))
|
|
goto nomem;
|
|
|
|
/* ownership taken */
|
|
cd = NULL;
|
|
|
|
if (dbus_connection_get_dispatch_status (connection) != DBUS_DISPATCH_COMPLETE)
|
|
{
|
|
if (!_dbus_loop_queue_dispatch (loop, connection))
|
|
goto nomem;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
nomem:
|
|
if (cd)
|
|
cdata_free (cd);
|
|
|
|
dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL);
|
|
dbus_connection_set_watch_functions (connection, NULL, NULL, NULL, NULL, NULL);
|
|
dbus_connection_set_timeout_functions (connection, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void die (const char *message) _DBUS_GNUC_NORETURN;
|
|
|
|
static void
|
|
die (const char *message)
|
|
{
|
|
printf ("Bail out! %s\n", message);
|
|
fflush (stdout);
|
|
exit (1);
|
|
}
|
|
|
|
void
|
|
test_connection_setup (TestMainContext *ctx,
|
|
DBusConnection *connection)
|
|
{
|
|
if (!test_connection_try_setup (ctx, connection))
|
|
die ("Not enough memory to set up connection");
|
|
}
|
|
|
|
void
|
|
test_connection_shutdown (TestMainContext *ctx,
|
|
DBusConnection *connection)
|
|
{
|
|
if (!dbus_connection_set_watch_functions (connection,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, NULL))
|
|
die ("setting watch functions to NULL failed");
|
|
|
|
if (!dbus_connection_set_timeout_functions (connection,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, NULL))
|
|
die ("setting timeout functions to NULL failed");
|
|
|
|
dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
DBusLoop *loop;
|
|
DBusServer *server;
|
|
} ServerData;
|
|
|
|
static void
|
|
serverdata_free (void *data)
|
|
{
|
|
ServerData *sd = data;
|
|
|
|
dbus_server_unref (sd->server);
|
|
_dbus_loop_unref (sd->loop);
|
|
|
|
dbus_free (sd);
|
|
}
|
|
|
|
static ServerData*
|
|
serverdata_new (DBusLoop *loop,
|
|
DBusServer *server)
|
|
{
|
|
ServerData *sd;
|
|
|
|
sd = dbus_new0 (ServerData, 1);
|
|
if (sd == NULL)
|
|
return NULL;
|
|
|
|
sd->loop = loop;
|
|
sd->server = server;
|
|
|
|
dbus_server_ref (sd->server);
|
|
_dbus_loop_ref (sd->loop);
|
|
|
|
return sd;
|
|
}
|
|
|
|
static dbus_bool_t
|
|
add_server_watch (DBusWatch *watch,
|
|
void *data)
|
|
{
|
|
ServerData *context = data;
|
|
|
|
return _dbus_loop_add_watch (context->loop, watch);
|
|
}
|
|
|
|
static void
|
|
toggle_server_watch (DBusWatch *watch,
|
|
void *data)
|
|
{
|
|
ServerData *context = data;
|
|
|
|
_dbus_loop_toggle_watch (context->loop, watch);
|
|
}
|
|
|
|
static void
|
|
remove_server_watch (DBusWatch *watch,
|
|
void *data)
|
|
{
|
|
ServerData *context = data;
|
|
|
|
_dbus_loop_remove_watch (context->loop, watch);
|
|
}
|
|
|
|
static dbus_bool_t
|
|
add_server_timeout (DBusTimeout *timeout,
|
|
void *data)
|
|
{
|
|
ServerData *context = data;
|
|
|
|
return _dbus_loop_add_timeout (context->loop, timeout);
|
|
}
|
|
|
|
static void
|
|
remove_server_timeout (DBusTimeout *timeout,
|
|
void *data)
|
|
{
|
|
ServerData *context = data;
|
|
|
|
_dbus_loop_remove_timeout (context->loop, timeout);
|
|
}
|
|
|
|
dbus_bool_t
|
|
test_server_try_setup (TestMainContext *ctx,
|
|
DBusServer *server)
|
|
{
|
|
DBusLoop *loop = ctx;
|
|
ServerData *sd;
|
|
|
|
sd = serverdata_new (loop, server);
|
|
if (sd == NULL)
|
|
goto nomem;
|
|
|
|
if (!dbus_server_set_watch_functions (server,
|
|
add_server_watch,
|
|
remove_server_watch,
|
|
toggle_server_watch,
|
|
sd,
|
|
serverdata_free))
|
|
{
|
|
goto nomem;
|
|
}
|
|
|
|
sd = serverdata_new (loop, server);
|
|
if (sd == NULL)
|
|
goto nomem;
|
|
|
|
if (!dbus_server_set_timeout_functions (server,
|
|
add_server_timeout,
|
|
remove_server_timeout,
|
|
NULL,
|
|
sd, serverdata_free))
|
|
{
|
|
goto nomem;
|
|
}
|
|
return TRUE;
|
|
|
|
nomem:
|
|
if (sd)
|
|
serverdata_free (sd);
|
|
|
|
test_server_shutdown (loop, server);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
test_server_setup (TestMainContext *ctx,
|
|
DBusServer *server)
|
|
{
|
|
if (!test_server_try_setup (ctx, server))
|
|
die ("Not enough memory to set up server");
|
|
}
|
|
|
|
void
|
|
test_server_shutdown (TestMainContext *ctx,
|
|
DBusServer *server)
|
|
{
|
|
dbus_server_disconnect (server);
|
|
|
|
if (!dbus_server_set_watch_functions (server,
|
|
NULL, NULL, NULL,
|
|
NULL,
|
|
NULL))
|
|
die ("setting watch functions to NULL failed");
|
|
|
|
if (!dbus_server_set_timeout_functions (server,
|
|
NULL, NULL, NULL,
|
|
NULL,
|
|
NULL))
|
|
die ("setting timeout functions to NULL failed");
|
|
}
|
|
|
|
TestMainContext *
|
|
test_main_context_get (void)
|
|
{
|
|
TestMainContext *ret = _dbus_loop_new ();
|
|
|
|
if (ret == NULL)
|
|
die ("Out of memory");
|
|
|
|
return ret;
|
|
}
|
|
|
|
TestMainContext *
|
|
test_main_context_try_get (void)
|
|
{
|
|
return _dbus_loop_new ();
|
|
}
|
|
|
|
TestMainContext *
|
|
test_main_context_ref (TestMainContext *ctx)
|
|
{
|
|
return _dbus_loop_ref (ctx);
|
|
}
|
|
|
|
void test_main_context_unref (TestMainContext *ctx)
|
|
{
|
|
_dbus_loop_unref (ctx);
|
|
}
|
|
|
|
void test_main_context_iterate (TestMainContext *ctx,
|
|
dbus_bool_t may_block)
|
|
{
|
|
_dbus_loop_iterate (ctx, may_block);
|
|
}
|
|
|
|
void
|
|
test_pending_call_store_reply (DBusPendingCall *pc,
|
|
void *data)
|
|
{
|
|
DBusMessage **message_p = data;
|
|
|
|
*message_p = dbus_pending_call_steal_reply (pc);
|
|
_dbus_assert (*message_p != NULL);
|
|
}
|
|
|
|
#ifdef DBUS_UNIX
|
|
|
|
/*
|
|
* Set uid to a machine-readable authentication identity (numeric Unix
|
|
* uid or ConvertSidToStringSid-style Windows SID) that is likely to exist,
|
|
* and differs from the identity of the current process.
|
|
*
|
|
* @param uid Populated with a machine-readable authentication identity
|
|
* on success
|
|
* @returns #FALSE if no memory
|
|
*/
|
|
dbus_bool_t
|
|
_dbus_test_append_different_uid (DBusString *uid)
|
|
{
|
|
if (geteuid () == 0)
|
|
return _dbus_string_append (uid, "65534");
|
|
else
|
|
return _dbus_string_append (uid, "0");
|
|
}
|
|
|
|
/*
|
|
* Set uid to a human-readable authentication identity (login name)
|
|
* that is likely to exist, and differs from the identity of the current
|
|
* process. This function currently only exists on Unix platforms.
|
|
*
|
|
* @param uid Populated with a machine-readable authentication identity
|
|
* on success
|
|
* @returns #FALSE if no memory
|
|
*/
|
|
dbus_bool_t
|
|
_dbus_test_append_different_username (DBusString *username)
|
|
{
|
|
if (geteuid () == 0)
|
|
return _dbus_string_append (username, "nobody");
|
|
else
|
|
return _dbus_string_append (username, "root");
|
|
}
|
|
|
|
#else /* !defined(DBUS_UNIX) */
|
|
|
|
#define ANONYMOUS_SID "S-1-5-7"
|
|
#define LOCAL_SYSTEM_SID "S-1-5-18"
|
|
|
|
dbus_bool_t
|
|
_dbus_test_append_different_uid (DBusString *uid)
|
|
{
|
|
char *sid = NULL;
|
|
dbus_bool_t ret;
|
|
|
|
if (!_dbus_getsid (&sid, _dbus_getpid ()))
|
|
return FALSE;
|
|
|
|
if (strcmp (sid, ANONYMOUS_SID) == 0)
|
|
ret = _dbus_string_append (uid, LOCAL_SYSTEM_SID);
|
|
else
|
|
ret = _dbus_string_append (uid, ANONYMOUS_SID);
|
|
|
|
LocalFree (sid);
|
|
return ret;
|
|
}
|
|
|
|
#endif /* !defined(DBUS_UNIX) */
|
|
|
|
#ifdef __linux__
|
|
struct DBusInitialFDs {
|
|
fd_set set;
|
|
};
|
|
#endif
|
|
|
|
DBusInitialFDs *
|
|
_dbus_check_fdleaks_enter (void)
|
|
{
|
|
#ifdef __linux__
|
|
DIR *d;
|
|
DBusInitialFDs *fds;
|
|
|
|
/* this is plain malloc so it won't interfere with leak checking */
|
|
fds = malloc (sizeof (DBusInitialFDs));
|
|
_dbus_assert (fds != NULL);
|
|
|
|
/* This works on Linux only */
|
|
|
|
if ((d = opendir ("/proc/self/fd")))
|
|
{
|
|
struct dirent *de;
|
|
|
|
while ((de = readdir(d)))
|
|
{
|
|
long l;
|
|
char *e = NULL;
|
|
int fd;
|
|
|
|
if (de->d_name[0] == '.')
|
|
continue;
|
|
|
|
errno = 0;
|
|
l = strtol (de->d_name, &e, 10);
|
|
_dbus_assert (errno == 0 && e && !*e);
|
|
|
|
fd = (int) l;
|
|
|
|
if (fd < 3)
|
|
continue;
|
|
|
|
if (fd == dirfd (d))
|
|
continue;
|
|
|
|
if (fd >= FD_SETSIZE)
|
|
{
|
|
_dbus_verbose ("FD %d unexpectedly large; cannot track whether "
|
|
"it is leaked\n", fd);
|
|
continue;
|
|
}
|
|
|
|
FD_SET (fd, &fds->set);
|
|
}
|
|
|
|
closedir (d);
|
|
}
|
|
|
|
return fds;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
_dbus_check_fdleaks_leave (DBusInitialFDs *fds,
|
|
const char *context)
|
|
{
|
|
#ifdef __linux__
|
|
DIR *d;
|
|
|
|
/* This works on Linux only */
|
|
|
|
if ((d = opendir ("/proc/self/fd")))
|
|
{
|
|
struct dirent *de;
|
|
|
|
while ((de = readdir(d)))
|
|
{
|
|
char description[256] = { 0 };
|
|
char proc_self_fd[256] = { 0 };
|
|
size_t description_length = sizeof (description) - 1;
|
|
long l;
|
|
char *e = NULL;
|
|
int fd;
|
|
|
|
if (de->d_name[0] == '.')
|
|
continue;
|
|
|
|
errno = 0;
|
|
l = strtol (de->d_name, &e, 10);
|
|
_dbus_assert (errno == 0 && e && !*e);
|
|
|
|
fd = (int) l;
|
|
|
|
if (fd < 3)
|
|
continue;
|
|
|
|
if (fd == dirfd (d))
|
|
continue;
|
|
|
|
if (fd >= FD_SETSIZE)
|
|
{
|
|
_dbus_verbose ("FD %d unexpectedly large; cannot track whether "
|
|
"it is leaked\n", fd);
|
|
continue;
|
|
}
|
|
|
|
if (FD_ISSET (fd, &fds->set))
|
|
continue;
|
|
|
|
snprintf (proc_self_fd, sizeof (proc_self_fd) - 1,
|
|
"/proc/self/fd/%ld", l);
|
|
proc_self_fd[sizeof (proc_self_fd) - 1] = '\0';
|
|
|
|
if (readlink (proc_self_fd, description, description_length) <= 0)
|
|
snprintf (description, sizeof (description) - 1, "(unknown)");
|
|
|
|
description[sizeof (description) - 1] = '\0';
|
|
|
|
_dbus_test_fatal ("file descriptor %i \"%s\" leaked in %s.",
|
|
fd, description, context);
|
|
}
|
|
|
|
closedir (d);
|
|
}
|
|
|
|
free (fds);
|
|
#else
|
|
_dbus_assert (fds == NULL);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
_dbus_test_help_page (const char *appname)
|
|
{
|
|
fprintf(stdout, "%s [<options>] [<test-data-dir>] [<specific-test>]\n", appname);
|
|
fprintf(stdout, "Options:\n");
|
|
fprintf(stdout, " --help this page\n");
|
|
fprintf(stdout, " --list-tests show available tests\n");
|
|
fprintf(stdout, " --tap expect test data dir to be set by environment variable DBUS_TEST_DATA\n");
|
|
fprintf(stdout, "Environment variables:\n");
|
|
fprintf(stdout, " DBUS_TEST_ONLY=<specific-test> set specific test to run\n");
|
|
fprintf(stdout, " DBUS_TEST_DATA=<test-data-dir> set test data dir (required when using --tap)\n");
|
|
}
|
|
|
|
static void
|
|
_dbus_test_show_available_tests (size_t n_tests,
|
|
const DBusTestCase *tests)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < n_tests; i++)
|
|
{
|
|
if (tests[i].name == NULL)
|
|
break;
|
|
|
|
fprintf(stdout, "%s\n", tests[i].name);
|
|
}
|
|
}
|
|
|
|
static const DBusTestCase *
|
|
_dbus_test_find_test (size_t n_tests,
|
|
const DBusTestCase *tests,
|
|
const char *specific_test)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < n_tests; i++)
|
|
{
|
|
if (tests[i].name == NULL)
|
|
break;
|
|
|
|
if (strcmp (specific_test, tests[i].name) == 0)
|
|
return &tests[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* _dbus_test_main:
|
|
* @argc: number of command-line arguments
|
|
* @argv: array of @argc arguments
|
|
* @n_tests: length of @tests
|
|
* @tests: array of @n_tests tests
|
|
* @flags: flags affecting all tests
|
|
* @test_pre_hook: if not %NULL, called before each test
|
|
* @test_post_hook: if not %NULL, called after each test
|
|
*
|
|
* Wrapper for dbus tests that do not use GLib. Processing of @tests
|
|
* can be terminated early by an entry with @name = NULL, which is a
|
|
* convenient way to put a trailing comma on every "real" test entry
|
|
* without breaking compilation on pedantic C compilers.
|
|
*/
|
|
int
|
|
_dbus_test_main (int argc,
|
|
char **argv,
|
|
size_t n_tests,
|
|
const DBusTestCase *tests,
|
|
DBusTestFlags flags,
|
|
void (*test_pre_hook) (void),
|
|
void (*test_post_hook) (void))
|
|
{
|
|
char *test_data_dir;
|
|
char *specific_test;
|
|
size_t i;
|
|
|
|
#ifdef DBUS_UNIX
|
|
/* close any inherited fds so dbus-spawn's check for close-on-exec works */
|
|
_dbus_close_all ();
|
|
#endif
|
|
|
|
#if HAVE_SETLOCALE
|
|
setlocale(LC_ALL, "");
|
|
#endif
|
|
|
|
if (argc > 1 && strcmp (argv[1], "--help") == 0)
|
|
{
|
|
_dbus_test_help_page (argv[0]);
|
|
exit(0);
|
|
}
|
|
|
|
else if (argc > 1 && strcmp (argv[1], "--list-tests") == 0)
|
|
{
|
|
_dbus_test_show_available_tests (n_tests, tests);
|
|
exit (0);
|
|
}
|
|
|
|
/* We can't assume that strings from _dbus_getenv() will remain valid
|
|
* forever, because some tests call setenv(), which is allowed to
|
|
* reallocate the entire environment block, and in Wine it seems that it
|
|
* genuinely does; so we copy them.
|
|
*
|
|
* We can't use _dbus_strdup() here because the test might be checking
|
|
* for memory leaks, so we don't want any libdbus allocations still
|
|
* alive at the end; so we use strdup(), which is not in Standard C but
|
|
* is available in both POSIX and Windows. */
|
|
if (argc > 1 && strcmp (argv[1], "--tap") != 0)
|
|
test_data_dir = strdup0_or_die (argv[1]);
|
|
else
|
|
test_data_dir = strdup0_or_die (_dbus_getenv ("DBUS_TEST_DATA"));
|
|
|
|
if (test_data_dir != NULL)
|
|
_dbus_test_diag ("Test data in %s", test_data_dir);
|
|
else if (flags & DBUS_TEST_FLAGS_REQUIRE_DATA)
|
|
{
|
|
_dbus_test_help_page (argv[0]);
|
|
_dbus_test_fatal ("Must specify test data directory as argv[1] or "
|
|
"in DBUS_TEST_DATA environment variable");
|
|
}
|
|
else
|
|
_dbus_test_diag ("No test data!");
|
|
|
|
if (argc > 2)
|
|
specific_test = strdup0_or_die (argv[2]);
|
|
else
|
|
specific_test = strdup0_or_die (_dbus_getenv ("DBUS_TEST_ONLY"));
|
|
|
|
/* check that test is present */
|
|
if (specific_test)
|
|
{
|
|
if (_dbus_test_find_test (n_tests, tests, specific_test) == NULL)
|
|
{
|
|
_dbus_test_fatal ("Invalid test name '%s' specified", specific_test);
|
|
}
|
|
}
|
|
|
|
/* Some NSS modules like those for sssd and LDAP might allocate fds
|
|
* on a one-per-process basis. Make sure those have already been
|
|
* allocated before we enter the code under test, so that they don't
|
|
* show up as having been "leaked" by the first module of code under
|
|
* test that happens to do a NSS lookup. */
|
|
{
|
|
DBusString lookup;
|
|
dbus_uid_t ignored_uid = DBUS_UID_UNSET;
|
|
dbus_gid_t ignored_gid = DBUS_GID_UNSET;
|
|
|
|
_dbus_string_init_const (&lookup, "dbus-no-user-with-this-name");
|
|
/* We use a username that almost certainly doesn't exist, because
|
|
* if we used something like root it might get handled early in the
|
|
* NSS search order, before we get as far as asking sssd or LDAP. */
|
|
_dbus_parse_unix_user_from_config (&lookup, &ignored_uid);
|
|
|
|
/* Same for groups */
|
|
_dbus_string_init_const (&lookup, "dbus-no-group-with-this-name");
|
|
_dbus_parse_unix_group_from_config (&lookup, &ignored_gid);
|
|
|
|
_dbus_test_check_memleaks ("initial nss query");
|
|
}
|
|
|
|
for (i = 0; i < n_tests; i++)
|
|
{
|
|
dbus_int64_t before, after;
|
|
DBusInitialFDs *initial_fds = NULL;
|
|
|
|
if (tests[i].name == NULL)
|
|
break;
|
|
|
|
if (n_tests > 1 &&
|
|
specific_test != NULL &&
|
|
strcmp (specific_test, tests[i].name) != 0)
|
|
{
|
|
_dbus_test_skip ("%s - Only intending to run %s",
|
|
tests[i].name, specific_test);
|
|
continue;
|
|
}
|
|
|
|
_dbus_test_diag ("Running test: %s", tests[i].name);
|
|
_dbus_get_monotonic_time (&before, NULL);
|
|
|
|
if (test_pre_hook)
|
|
test_pre_hook ();
|
|
|
|
if (flags & DBUS_TEST_FLAGS_CHECK_FD_LEAKS)
|
|
initial_fds = _dbus_check_fdleaks_enter ();
|
|
|
|
if (tests[i].func (test_data_dir))
|
|
_dbus_test_ok ("%s", tests[i].name);
|
|
else
|
|
_dbus_test_not_ok ("%s", tests[i].name);
|
|
|
|
_dbus_get_monotonic_time (&after, NULL);
|
|
|
|
_dbus_test_diag ("%s test took %" DBUS_INT64_MODIFIER "d seconds",
|
|
tests[i].name, after - before);
|
|
|
|
if (test_post_hook)
|
|
test_post_hook ();
|
|
|
|
if (flags & DBUS_TEST_FLAGS_CHECK_MEMORY_LEAKS)
|
|
_dbus_test_check_memleaks (tests[i].name);
|
|
|
|
if (flags & DBUS_TEST_FLAGS_CHECK_FD_LEAKS)
|
|
_dbus_check_fdleaks_leave (initial_fds, tests[i].name);
|
|
}
|
|
|
|
free (test_data_dir);
|
|
free (specific_test);
|
|
|
|
return _dbus_test_done_testing ();
|
|
}
|
|
|
|
/* If embedded tests are enabled, the TAP helpers have to be in the
|
|
* shared library because some of the embedded tests call them. If not,
|
|
* implement them here. We #include the file here instead of adding it
|
|
* to SOURCES because Automake versions older than 1.16 can't cope with
|
|
* expanding directory variables in SOURCES when using subdir-objects. */
|
|
#ifndef DBUS_ENABLE_EMBEDDED_TESTS
|
|
#include "dbus/dbus-test-tap.c"
|
|
#endif
|