blob: 4319e5a4b21f94d50677ece0ce34d0f23d701c0e [file] [log] [blame]
/*
*
* Connection Manager
*
* Copyright (C) 2011 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <gdbus.h>
#include "session-test.h"
#define POLICYDIR STORAGEDIR "/session_policy_local"
enum test_session_state {
TEST_SESSION_STATE_0 = 0,
TEST_SESSION_STATE_1 = 1,
TEST_SESSION_STATE_2 = 2,
TEST_SESSION_STATE_3 = 3,
};
static enum test_session_state get_session_state(struct test_session *session)
{
return GPOINTER_TO_UINT(session->fix->user_data);
}
static void set_session_state(struct test_session *session,
enum test_session_state state)
{
session->fix->user_data = GUINT_TO_POINTER(state);
}
static struct test_session *get_session(struct test_session *session,
unsigned int index)
{
return &session->fix->session[index];
}
static void test_session_create_no_notify(struct test_fix *fix)
{
DBusMessage *msg;
util_session_create(fix, 1);
msg = manager_create_session(fix->session->connection,
fix->session->info, "/foo");
g_assert(msg);
g_assert(dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR);
dbus_message_unref(msg);
util_idle_call(fix, util_quit_loop, util_session_destroy);
}
static void test_session_destroy_no_notify(struct test_fix *fix)
{
DBusMessage *msg;
util_session_create(fix, 1);
msg = manager_destroy_session(fix->session->connection, "/foo");
g_assert(!msg);
util_idle_call(fix, util_quit_loop, util_session_destroy);
}
static void test_session_create_notify(struct test_session *session)
{
LOG("session %p", session);
util_idle_call(session->fix, util_quit_loop, util_session_destroy);
}
static void test_session_create(struct test_fix *fix)
{
struct test_session *session;
DBusMessage *msg;
int err;
util_session_create(fix, 1);
session = fix->session;
session->notify_path = "/foo";
session->notify = test_session_create_notify;
err = session_notify_register(session, session->notify_path);
g_assert(err == 0);
msg = manager_create_session(session->connection,
session->info,
session->notify_path);
g_assert(msg);
g_assert(dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR);
dbus_message_unref(msg);
}
static void test_session_create_destroy(struct test_fix *fix)
{
struct test_session *session;
util_session_create(fix, 1);
session = fix->session;
session->notify_path = g_strdup("/foo");
util_session_init(fix->session);
util_session_cleanup(fix->session);
util_idle_call(fix, util_quit_loop, util_session_destroy);
}
static void test_session_create_dup_notification(struct test_fix *fix)
{
struct test_session *session0, *session1;
DBusMessage *msg;
util_session_create(fix, 2);
session0 = &fix->session[0];
session1 = &fix->session[1];
session0->notify_path = g_strdup("/foo");
session1->notify_path = session0->notify_path;
util_session_init(session0);
msg = manager_create_session(session1->connection,
session1->info,
session1->notify_path);
g_assert(msg);
util_session_cleanup(session0);
util_idle_call(fix, util_quit_loop, util_session_destroy);
}
static void test_session_create_many_notify(struct test_session *session)
{
unsigned int nr;
LOG("session %p", session);
nr = GPOINTER_TO_UINT(session->fix->user_data);
nr--;
session->fix->user_data = GUINT_TO_POINTER(nr);
if (nr > 0)
return;
util_idle_call(session->fix, util_quit_loop, util_session_destroy);
}
static void test_session_create_many(struct test_fix *fix)
{
struct test_session *session;
unsigned int i, max;
max = 100;
fix->user_data = GUINT_TO_POINTER(max);
util_session_create(fix, max);
for (i = 0; i < max; i++) {
session = &fix->session[i];
session->notify_path = g_strdup_printf("/foo/%d", i);
session->notify = test_session_create_many_notify;
util_session_init(session);
}
}
static void set_session_mode(struct test_fix *fix,
bool enable)
{
DBusMessage *msg;
msg = manager_set_session_mode(fix->main_connection, enable);
g_assert(msg);
g_assert(dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR);
dbus_message_unref(msg);
}
static void test_session_connect_notify(struct test_session *session)
{
LOG("session %p state %d", session, session->info->state);
if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
return;
util_session_cleanup(session);
util_idle_call(session->fix, util_quit_loop, util_session_destroy);
}
static void test_session_connect(struct test_fix *fix)
{
struct test_session *session;
DBusMessage *msg;
util_session_create(fix, 1);
session = fix->session;
session->notify_path = g_strdup("/foo");
session->notify = test_session_connect_notify;
util_session_init(session);
msg = session_connect(session->connection, session);
g_assert(msg);
g_assert(dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR);
dbus_message_unref(msg);
}
static void test_session_disconnect_notify(struct test_session *session)
{
LOG("session %p state %d", session, session->info->state);
if (session->info->state >= CONNMAN_SESSION_STATE_CONNECTED)
return;
util_session_cleanup(session);
util_idle_call(session->fix, util_quit_loop, util_session_destroy);
}
static void test_session_disconnect(struct test_fix *fix)
{
struct test_session *session;
DBusMessage *msg;
util_session_create(fix, 1);
session = fix->session;
session->notify_path = g_strdup("/foo");
session->notify = test_session_disconnect_notify;
util_session_init(session);
msg = session_disconnect(session->connection, session);
g_assert(msg);
dbus_message_unref(msg);
}
static void test_session_connect_disconnect_notify(struct test_session *session)
{
enum test_session_state state = get_session_state(session);
enum test_session_state next_state = state;
DBusMessage *msg;
LOG("state %d session %p %s state %d", state, session,
session->notify_path, session->info->state);
switch (state) {
case TEST_SESSION_STATE_0:
if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
next_state = TEST_SESSION_STATE_1;
if (session->info->state == CONNMAN_SESSION_STATE_CONNECTED) {
LOG("state was already connected, continuing");
next_state = TEST_SESSION_STATE_2;
}
break;
case TEST_SESSION_STATE_1:
if (session->info->state >= CONNMAN_SESSION_STATE_CONNECTED)
next_state = TEST_SESSION_STATE_2;
break;
case TEST_SESSION_STATE_2:
if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
next_state = TEST_SESSION_STATE_3;
default:
break;
}
if (state == next_state)
return;
set_session_state(session, next_state);
LOG("next_state %d", next_state);
switch (next_state) {
case TEST_SESSION_STATE_1:
msg = session_connect(session->connection, session);
g_assert(msg);
dbus_message_unref(msg);
return;
case TEST_SESSION_STATE_2:
msg = session_disconnect(session->connection, session);
g_assert(msg);
dbus_message_unref(msg);
return;
case TEST_SESSION_STATE_3:
util_session_cleanup(session);
util_idle_call(session->fix, util_quit_loop,
util_session_destroy);
return;
default:
return;
}
}
static void test_session_connect_disconnect(struct test_fix *fix)
{
struct test_session *session;
/*
* +-------------------+
* | START |
* +-------------------+
* |
* | connect foo
* v
* +-------------------+
* | FOO-CONNECTED |
* +-------------------+
* |
* | disconnect foo
* v
* +-------------------+
* | END |
* +-------------------+
*/
util_session_create(fix, 1);
session = fix->session;
session->notify_path = g_strdup("/foo");
session->notify = test_session_connect_disconnect_notify;
util_session_init(session);
set_session_state(session, TEST_SESSION_STATE_0);
}
static void test_session_connect_free_ride_notify(struct test_session *session)
{
struct test_session *session0 = get_session(session, 0);
struct test_session *session1 = get_session(session, 1);
enum test_session_state state = get_session_state(session);
enum test_session_state next_state = state;
DBusMessage *msg;
LOG("state %d session %p %s state %d", state, session,
session->notify_path, session->info->state);
switch (state) {
case TEST_SESSION_STATE_0:
if (session0->info->state == CONNMAN_SESSION_STATE_DISCONNECTED
&& session1->info->state ==
CONNMAN_SESSION_STATE_DISCONNECTED) {
next_state = TEST_SESSION_STATE_1;
}
if (session0->info->state == CONNMAN_SESSION_STATE_CONNECTED &&
session1->info->state ==
CONNMAN_SESSION_STATE_CONNECTED) {
LOG("state was already connected, continuing");
next_state = TEST_SESSION_STATE_2;
}
break;
case TEST_SESSION_STATE_1:
if (session0->info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
session1->info->state >=
CONNMAN_SESSION_STATE_CONNECTED) {
next_state = TEST_SESSION_STATE_2;
}
break;
case TEST_SESSION_STATE_2:
if (session0->info->state == CONNMAN_SESSION_STATE_DISCONNECTED
&& (session1->info->state ==
CONNMAN_SESSION_STATE_DISCONNECTED ||
session1->info->state ==
CONNMAN_SESSION_STATE_CONNECTED) ) {
LOG("session0 /foo is disconnected, session1 /bar "
"can be either connected or disconnected");
next_state = TEST_SESSION_STATE_3;
}
break;
case TEST_SESSION_STATE_3:
return;
}
if (state == next_state)
return;
set_session_state(session, next_state);
LOG("next_state %d", next_state);
switch (next_state) {
case TEST_SESSION_STATE_0:
return;
case TEST_SESSION_STATE_1:
msg = session_connect(session0->connection, session0);
g_assert(msg);
dbus_message_unref(msg);
return;
case TEST_SESSION_STATE_2:
msg = session_disconnect(session0->connection, session0);
g_assert(msg);
dbus_message_unref(msg);
return;
case TEST_SESSION_STATE_3:
util_session_cleanup(session0);
util_session_cleanup(session1);
util_idle_call(session0->fix, util_quit_loop,
util_session_destroy);
return;
}
}
static void test_session_connect_free_ride(struct test_fix *fix)
{
struct test_session *session0, *session1;
/*
* +-------------------+
* | START |
* +-------------------+
* |
* | connect foo
* v
* +-------------------+
* | FOO-CONNECTED |
* +-------------------+
* |
* | free-ride bar
* v
* +-------------------+
* | FOO-BAR-CONNECTED |
* +-------------------+
* |
* | disconnect foo
* v
* +-------------------+
* | END |
* +-------------------+
*/
util_session_create(fix, 2);
session0 = &fix->session[0];
session1 = &fix->session[1];
session0->notify_path = g_strdup("/foo");
session1->notify_path = g_strdup("/bar");
session0->notify = test_session_connect_free_ride_notify;
session1->notify = test_session_connect_free_ride_notify;
util_session_init(session0);
util_session_init(session1);
set_session_state(session0, TEST_SESSION_STATE_0);
}
static void policy_save(GKeyFile *keyfile, char *pathname)
{
gchar *data = NULL;
gsize length = 0;
GError *error = NULL;
data = g_key_file_to_data(keyfile, &length, NULL);
if (!g_file_set_contents(pathname, data, length, &error)) {
DBG("Failed to store information: %s", error->message);
g_error_free(error);
g_assert(0);
}
g_free(data);
}
static void policy_allowed_bearers(const char *allowed_bearers)
{
struct passwd *pwd;
uid_t uid;
char *pathname;
GKeyFile *keyfile;
LOG("update to '%s'", allowed_bearers);
uid = getuid();
pwd = getpwuid(uid);
g_assert(pwd);
keyfile = g_key_file_new();
g_key_file_set_string(keyfile, "policy_foo", "uid", pwd->pw_name);
g_key_file_set_string(keyfile, "policy_foo", "AllowedBearers",
allowed_bearers);
pathname = g_strdup_printf("%s/foo.policy", POLICYDIR);
policy_save(keyfile, pathname);
g_free(pathname);
g_key_file_free(keyfile);
}
static void policy_remove_file(void)
{
char *pathname;
pathname = g_strdup_printf("%s/foo.policy", POLICYDIR);
unlink(pathname);
g_free(pathname);
}
static void test_session_policy_notify(struct test_session *session)
{
enum test_session_state state = get_session_state(session);
enum test_session_state next_state = state;
DBusMessage *msg;
LOG("state %d session %p %s state %d", state, session,
session->notify_path, session->info->state);
switch (state) {
case TEST_SESSION_STATE_0:
if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
next_state = TEST_SESSION_STATE_1;
break;
case TEST_SESSION_STATE_1:
if (session->info->state >= CONNMAN_SESSION_STATE_CONNECTED)
next_state = TEST_SESSION_STATE_2;
break;
case TEST_SESSION_STATE_2:
if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
next_state = TEST_SESSION_STATE_3;
default:
break;
}
if (state == next_state)
return;
set_session_state(session, next_state);
LOG("next_state %d", next_state);
switch (next_state) {
case TEST_SESSION_STATE_1:
policy_allowed_bearers("ethernet");
msg = session_connect(session->connection, session);
g_assert(msg);
dbus_message_unref(msg);
return;
case TEST_SESSION_STATE_2:
policy_allowed_bearers("");
return;
case TEST_SESSION_STATE_3:
policy_remove_file();
util_session_cleanup(session);
util_idle_call(session->fix, util_quit_loop,
util_session_destroy);
return;
default:
return;
}
}
static void test_session_policy(struct test_fix *fix)
{
struct test_session *session;
/*
* +-------------------+
* | START |
* +-------------------+
* |
* | write policy AllowedBearers = ethernet
* v
* +-------------------+
* | FOO-CONNECTED |
* +-------------------+
* |
* | write policy AllowedBearers =
* v
* +-------------------+
* | END |
* +-------------------+
*/
policy_remove_file();
util_session_create(fix, 1);
session = fix->session;
session->notify_path = g_strdup("/foo");
session->notify = test_session_policy_notify;
util_session_init(session);
set_session_state(session, TEST_SESSION_STATE_0);
}
static bool is_online(struct test_fix *fix)
{
if (g_strcmp0(fix->manager.state, "online") == 0)
return true;
return false;
}
static void enable_session_mode(struct test_fix *fix)
{
set_session_mode(fix, true);
if (!is_online(fix))
util_idle_call(fix, util_quit_loop, NULL);
}
static void manager_state_changed(struct test_fix *fix)
{
if (!is_online(fix)) {
fix->manager_changed = NULL;
util_idle_call(fix, util_quit_loop, NULL);
}
}
static void disable_session_mode(struct test_fix *fix)
{
set_session_mode(fix, false);
}
static void setup_cb(struct test_fix *fix)
{
fix->manager_changed = manager_state_changed;
util_call(fix, enable_session_mode, NULL);
}
static void teardown_cb(struct test_fix *fix)
{
util_call(fix, disable_session_mode, NULL);
util_idle_call(fix, util_quit_loop, NULL);
}
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
util_test_add("/manager/session create no notify",
test_session_create_no_notify, setup_cb, teardown_cb);
util_test_add("/manager/session destroy no notify",
test_session_destroy_no_notify, setup_cb, teardown_cb);
util_test_add("/manager/session create",
test_session_create, setup_cb, teardown_cb);
util_test_add("/manager/session create destroy",
test_session_create_destroy, setup_cb, teardown_cb);
util_test_add("/manager/session create duplicate notification",
test_session_create_dup_notification, setup_cb, teardown_cb);
util_test_add("/manager/session create many",
test_session_create_many, setup_cb, teardown_cb);
util_test_add("/session/connect",
test_session_connect, setup_cb, teardown_cb);
util_test_add("/session/disconnect",
test_session_disconnect, setup_cb, teardown_cb);
util_test_add("/session/connect disconnect",
test_session_connect_disconnect, setup_cb, teardown_cb);
util_test_add("/session/connect free-ride",
test_session_connect_free_ride, setup_cb, teardown_cb);
util_test_add("/session/policy",
test_session_policy, setup_cb, teardown_cb);
return g_test_run();
}