libsecret: Fix up the tests for recent regressions

This commit is contained in:
Stef Walter 2015-04-14 10:48:19 +02:00
parent 70d1bc3307
commit ad77f143e6
4 changed files with 112 additions and 95 deletions

View File

@ -23,86 +23,118 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
static GTestDBus *test_bus = NULL; #ifdef __linux
#include <sys/prctl.h>
#endif
static gchar *service_name = NULL;
static GPid pid = 0; static GPid pid = 0;
static gboolean static void
on_python_child_setup (gpointer user_data)
{
#ifdef __linux
prctl (PR_SET_PDEATHSIG, 15);
#endif
}
static gchar *
read_until_end (int fd,
GError **error)
{
GString *data;
gsize len;
gssize ret;
gchar *pos;
data = g_string_new ("");
for (;;) {
len = data->len;
g_string_set_size (data, len + 256);
ret = read (fd, data->str + len, 256);
if (ret < 0) {
if (errno != EAGAIN) {
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
"Couldn't read from mock service: %s", g_strerror (errno));
g_string_free (data, TRUE);
return NULL;
}
} else if (ret == 0) {
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Remote closed the output");
g_string_free (data, TRUE);
return NULL;
} else {
data->len = len + ret;
data->str[data->len] = '\0';
}
pos = strchr (data->str, '\n');
if (pos) {
g_string_set_size (data, pos - data->str);
break;
}
}
return g_string_free (data, FALSE);
}
static const gchar *
service_start (const gchar *mock_script, service_start (const gchar *mock_script,
GError **error) GError **error)
{ {
gchar ready[8] = { 0, };
GSpawnFlags flags; GSpawnFlags flags;
int wait_pipe[2];
GPollFD poll_fd;
gboolean ret; gboolean ret;
gint polled; gint output;
gchar *argv[] = { gchar *argv[] = {
"python", (gchar *)mock_script, "python", (gchar *)mock_script,
"--name", MOCK_SERVICE_NAME,
"--ready", ready,
NULL NULL
}; };
g_return_val_if_fail (mock_script != NULL, FALSE); g_return_val_if_fail (mock_script != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
test_bus = g_test_dbus_new (G_TEST_DBUS_NONE); g_free (service_name);
g_test_dbus_up (test_bus); service_name = NULL;
g_setenv ("SECRET_SERVICE_BUS_NAME", MOCK_SERVICE_NAME, TRUE); flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD;
ret = g_spawn_async_with_pipes (SRCDIR, argv, NULL, flags, on_python_child_setup, NULL, &pid,
if (pipe (wait_pipe) < 0) { NULL, &output, NULL, error);
g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
"Couldn't create pipe for mock service");
return FALSE;
}
snprintf (ready, sizeof (ready), "%d", wait_pipe[1]);
flags = G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
ret = g_spawn_async (SRCDIR, argv, NULL, flags, NULL, NULL, &pid, error);
close (wait_pipe[1]);
if (ret) { if (ret) {
poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; service_name = read_until_end (output, error);
poll_fd.fd = wait_pipe[0]; if (service_name) {
poll_fd.revents = 0; g_strstrip (service_name);
g_assert_cmpstr (service_name, !=, "");
polled = g_poll (&poll_fd, 1, 2000); g_setenv ("SECRET_SERVICE_BUS_NAME", service_name, TRUE);
if (polled < -1) }
g_warning ("couldn't poll file descirptor: %s", g_strerror (errno)); close (output);
if (polled != 1)
g_warning ("couldn't wait for mock service");
} }
close (wait_pipe[0]); return service_name;
return ret;
} }
gboolean const gchar *
mock_service_start (const gchar *mock_script, mock_service_start (const gchar *mock_script,
GError **error) GError **error)
{ {
gchar *path; gchar *path;
gboolean ret; const gchar *name;
path = g_build_filename (SRCDIR, "libsecret", mock_script, NULL); path = g_build_filename (SRCDIR, "libsecret", mock_script, NULL);
ret = service_start (path, error); name = service_start (path, error);
g_free (path); g_free (path);
return ret; return name;
} }
void void
mock_service_stop (void) mock_service_stop (void)
{ {
const gchar *prgname; while (g_main_context_iteration (NULL, FALSE));
if (!pid)
return;
if (pid) {
if (kill (pid, SIGTERM) < 0) { if (kill (pid, SIGTERM) < 0) {
if (errno != ESRCH) if (errno != ESRCH)
g_warning ("kill() failed: %s", g_strerror (errno)); g_warning ("kill() failed: %s", g_strerror (errno));
@ -110,21 +142,8 @@ mock_service_stop (void)
g_spawn_close_pid (pid); g_spawn_close_pid (pid);
pid = 0; pid = 0;
}
while (g_main_context_iteration (NULL, FALSE)); while (g_main_context_iteration (NULL, FALSE));
g_unsetenv ("SECRET_SERVICE_BUS_NAME");
/*
* HACK: Don't worry about leaking tests when running under gjs.
* Waiting for the connection to go away is hopeless due to
* the way gjs garbage collects.
*/
prgname = g_get_prgname ();
if (prgname && strstr (prgname, "gjs")) {
g_test_dbus_stop (test_bus);
} else {
g_test_dbus_down (test_bus);
g_object_unref (test_bus);
}
test_bus = NULL;
} }

View File

@ -17,9 +17,7 @@
#include <glib.h> #include <glib.h>
#define MOCK_SERVICE_NAME "org.mock.Service" const gchar * mock_service_start (const gchar *mock_script,
gboolean mock_service_start (const gchar *mock_script,
GError **error); GError **error);
void mock_service_stop (void); void mock_service_stop (void);

View File

@ -28,8 +28,6 @@ import gobject
COLLECTION_PREFIX = "/org/freedesktop/secrets/collection/" COLLECTION_PREFIX = "/org/freedesktop/secrets/collection/"
bus_name = 'org.freedesktop.Secret.MockService'
ready_pipe = -1
objects = { } objects = { }
class NotSupported(dbus.exceptions.DBusException): class NotSupported(dbus.exceptions.DBusException):
@ -132,7 +130,7 @@ class SecretPrompt(dbus.service.Object):
self.path = "/org/freedesktop/secrets/prompts/%s" % prompt_name self.path = "/org/freedesktop/secrets/prompts/%s" % prompt_name
else: else:
self.path = "/org/freedesktop/secrets/prompts/%s" % next_identifier('p') self.path = "/org/freedesktop/secrets/prompts/%s" % next_identifier('p')
dbus.service.Object.__init__(self, service.bus_name, self.path) dbus.service.Object.__init__(self, service.bus, self.path)
service.add_prompt(self) service.add_prompt(self)
assert self.path not in objects assert self.path not in objects
objects[self.path] = self objects[self.path] = self
@ -166,7 +164,7 @@ class SecretSession(dbus.service.Object):
self.algorithm = algorithm self.algorithm = algorithm
self.key = key self.key = key
self.path = "/org/freedesktop/secrets/sessions/%s" % next_identifier('s') self.path = "/org/freedesktop/secrets/sessions/%s" % next_identifier('s')
dbus.service.Object.__init__(self, service.bus_name, self.path) dbus.service.Object.__init__(self, service.bus, self.path)
service.add_session(self) service.add_session(self)
objects[self.path] = self objects[self.path] = self
@ -206,7 +204,7 @@ class SecretItem(dbus.service.Object):
self.path = "%s/%s" % (collection.path, identifier) self.path = "%s/%s" % (collection.path, identifier)
self.confirm = confirm self.confirm = confirm
self.created = self.modified = time.time() self.created = self.modified = time.time()
dbus.service.Object.__init__(self, collection.service.bus_name, self.path) dbus.service.Object.__init__(self, collection.service.bus, self.path)
self.collection.add_item(self) self.collection.add_item(self)
objects[self.path] = self objects[self.path] = self
@ -327,7 +325,7 @@ class SecretCollection(dbus.service.Object):
self.created = self.modified = time.time() self.created = self.modified = time.time()
self.aliased = set() self.aliased = set()
self.path = "%s%s" % (COLLECTION_PREFIX, identifier) self.path = "%s%s" % (COLLECTION_PREFIX, identifier)
dbus.service.Object.__init__(self, service.bus_name, self.path) dbus.service.Object.__init__(self, service.bus, self.path)
self.service.add_collection(self) self.service.add_collection(self)
objects[self.path] = self objects[self.path] = self
@ -476,12 +474,9 @@ class SecretService(dbus.service.Object):
"dh-ietf1024-sha256-aes128-cbc-pkcs7": AesAlgorithm(), "dh-ietf1024-sha256-aes128-cbc-pkcs7": AesAlgorithm(),
} }
def __init__(self, name=None): def __init__(self):
if name == None: self.bus = dbus.SessionBus()
name = bus_name dbus.service.Object.__init__(self, self.bus, '/org/freedesktop/secrets')
bus = dbus.SessionBus()
self.bus_name = dbus.service.BusName(name, allow_replacement=True, replace_existing=True)
dbus.service.Object.__init__(self, self.bus_name, '/org/freedesktop/secrets')
self.sessions = { } self.sessions = { }
self.prompts = { } self.prompts = { }
self.collections = { } self.collections = { }
@ -493,9 +488,7 @@ class SecretService(dbus.service.Object):
for session in list(self.sessions.get(old_owner, [])): for session in list(self.sessions.get(old_owner, [])):
session.Close() session.Close()
bus.add_signal_receiver(on_name_owner_changed, self.bus.add_signal_receiver(on_name_owner_changed, 'NameOwnerChanged', 'org.freedesktop.DBus')
'NameOwnerChanged',
'org.freedesktop.DBus')
def add_standard_objects(self): def add_standard_objects(self):
collection = SecretCollection(self, "english", label="Collection One", locked=False) collection = SecretCollection(self, "english", label="Collection One", locked=False)
@ -532,12 +525,12 @@ class SecretService(dbus.service.Object):
self.set_alias('session', collection) self.set_alias('session', collection)
def listen(self): def listen(self):
global ready_pipe
loop = gobject.MainLoop() loop = gobject.MainLoop()
if ready_pipe >= 0: name = self.bus.get_unique_name()
os.write(ready_pipe, "GO") if not name:
os.close(ready_pipe) raise NameError("No unique name available")
ready_pipe = -1 print name
sys.stdout.flush()
loop.run() loop.run()
def add_session(self, session): def add_session(self, session):
@ -696,18 +689,12 @@ class SecretService(dbus.service.Object):
def parse_options(args): def parse_options(args):
global bus_name, ready_pipe
try: try:
opts, args = getopt.getopt(args, "nr", ["name=", "ready="]) opts, args = getopt.getopt(args, "", [])
except getopt.GetoptError, err: except getopt.GetoptError, err:
print str(err) print str(err)
sys.exit(2) sys.exit(2)
for o, a in opts: for o, a in opts:
if o in ("-n", "--name"):
bus_name = a
elif o in ("-r", "--ready"):
ready_pipe = int(a)
else:
assert False, "unhandled option" assert False, "unhandled option"
return args return args

View File

@ -47,8 +47,17 @@ static void
teardown_mock (Test *test, teardown_mock (Test *test,
gconstpointer unused) gconstpointer unused)
{ {
/*
* Because the entire model of GDBus using another worker-thread
* is shit and racy as hell. If I had more time I would fix GDBus not to segfault
* during tests, but for now this is the best I can do.
*/
g_usleep (G_USEC_PER_SEC);
secret_service_disconnect (); secret_service_disconnect ();
mock_service_stop (); mock_service_stop ();
while (g_main_context_iteration (NULL, FALSE));
} }
static void static void
@ -98,6 +107,10 @@ test_get_sync (Test *test,
g_object_add_weak_pointer (G_OBJECT (service3), (gpointer *)&service3); g_object_add_weak_pointer (G_OBJECT (service3), (gpointer *)&service3);
g_object_unref (service3); g_object_unref (service3);
/* Because the entire model of GDBus using another worker-thread is shite */
g_usleep (G_USEC_PER_SEC);
secret_service_disconnect (); secret_service_disconnect ();
g_assert (service3 == NULL); g_assert (service3 == NULL);
} }