/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2017 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* This test allocate and free some resources in order to be able to detect
* leaks using a leak detector.
*
* To use with GCC/Clang address sanitizer you can set or add these options:
* CFLAGS="-fsanitize=address -fno-omit-frame-pointer"
* LDFLAGS="-fsanitize=address -lasan"
* Note that early GCC address sanitizer don't have a leak detector.
*
* To use Valgrind you can run the test with:
* valgrind --tool=memcheck --leak-check=full ./test-leaks
* For cleaner output you should suppress GLib checks with glib.supp file.
*/
#include <config.h>
#include <unistd.h>
#include <spice.h>
#include "test-glib-compat.h"
#include "basic-event-loop.h"
#include "test-display-base.h"
#include "sys-socket.h"
#define PKI_DIR SPICE_TOP_SRCDIR "/server/tests/pki/"
static void server_leaks(void)
{
int result;
SpiceCoreInterface *core;
SpiceServer *server = spice_server_new();
int sv[2];
g_assert_nonnull(server);
core = basic_event_loop_init();
g_assert_nonnull(core);
result = spice_server_set_tls(server, 5922,
PKI_DIR "ca-cert.pem",
PKI_DIR "server-cert.pem",
PKI_DIR "server-key.pem",
NULL, NULL, NULL);
g_assert_cmpint(result, ==, 0);
g_assert_cmpint(spice_server_init(server, core), ==, 0);
/* cause the allocation of spice name */
spice_server_set_name(server, "Test Spice Name");
/* cause the allocation of security options */
result = spice_server_set_channel_security(server, "main", SPICE_CHANNEL_SECURITY_SSL);
g_assert_cmpint(result, ==, 0);
/* spice_server_add_ssl_client should not leak when it's given a disconnected socket */
g_test_expect_message(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
"*SSL_accept failed*");
g_assert_cmpint(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv), ==, 0);
socket_close(sv[1]);
result = spice_server_add_ssl_client(server, sv[0], 1);
g_assert_cmpint(result, ==, -1);
/* if the function fails, it should not close the socket */
g_assert_cmpint(socket_close(sv[0]), ==, 0);
spice_server_destroy(server);
basic_event_loop_destroy();
}
static int vmc_write(SPICE_GNUC_UNUSED SpiceCharDeviceInstance *sin,
SPICE_GNUC_UNUSED const uint8_t *buf,
int len)
{
return len;
}
static int vmc_read(SPICE_GNUC_UNUSED SpiceCharDeviceInstance *sin,
SPICE_GNUC_UNUSED uint8_t *buf,
SPICE_GNUC_UNUSED int len)
{
return 0;
}
static void vmc_state(SPICE_GNUC_UNUSED SpiceCharDeviceInstance *sin,
SPICE_GNUC_UNUSED int connected)
{
}
static SpiceCharDeviceInterface vmc_interface = {
.base = {
.type = SPICE_INTERFACE_CHAR_DEVICE,
.description = "test spice virtual channel char device",
.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
},
.state = vmc_state,
.write = vmc_write,
.read = vmc_read,
};
static SpiceCharDeviceInstance vmc_instance;
static void vmc_leaks(void)
{
SpiceCoreInterface *core = basic_event_loop_init();
Test *test = test_new(core);
int status;
vmc_instance.subtype = "usbredir";
vmc_instance.base.sif = &vmc_interface.base;
spice_server_add_interface(test->server, &vmc_instance.base);
status = spice_server_remove_interface(&vmc_instance.base);
g_assert_cmpint(status, ==, 0);
vmc_instance.subtype = "port";
vmc_instance.portname = "org.spice-space.webdav.0";
vmc_instance.base.sif = &vmc_interface.base;
spice_server_add_interface(test->server, &vmc_instance.base);
status = spice_server_remove_interface(&vmc_instance.base);
g_assert_cmpint(status, ==, 0);
vmc_instance.subtype = "port";
vmc_instance.portname = "default_port";
vmc_instance.base.sif = &vmc_interface.base;
spice_server_add_interface(test->server, &vmc_instance.base);
status = spice_server_remove_interface(&vmc_instance.base);
g_assert_cmpint(status, ==, 0);
test_destroy(test);
basic_event_loop_destroy();
}
static void migrate_cb(SpiceMigrateInstance *sin)
{
}
static const SpiceMigrateInterface migrate_interface = {
.base = {
.type = SPICE_INTERFACE_MIGRATION,
.description = "migration",
.major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
.minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
},
.migrate_connect_complete = migrate_cb,
.migrate_end_complete = migrate_cb,
};
static void migration_leaks(void)
{
SpiceCoreInterface *core;
SpiceServer *server = spice_server_new();
SpiceMigrateInstance migrate;
g_assert_nonnull(server);
core = basic_event_loop_init();
g_assert_nonnull(core);
g_assert_cmpint(spice_server_init(server, core), ==, 0);
migrate.base.sif = &migrate_interface.base;
spice_server_add_interface(server, &migrate.base);
spice_server_destroy(server);
basic_event_loop_destroy();
}
static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height)
{
}
static void tablet_position(SpiceTabletInstance* sin, int x, int y,
uint32_t buttons_state)
{
}
static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
uint32_t buttons_state)
{
}
static void tablet_buttons(SpiceTabletInstance *sin,
uint32_t buttons_state)
{
}
static const SpiceTabletInterface tablet_interface = {
.base = {
.type = SPICE_INTERFACE_TABLET,
.description = "tablet",
.major_version = SPICE_INTERFACE_TABLET_MAJOR,
.minor_version = SPICE_INTERFACE_TABLET_MINOR,
},
.set_logical_size = tablet_set_logical_size,
.position = tablet_position,
.wheel = tablet_wheel,
.buttons = tablet_buttons,
};
static void tablet_leaks(void)
{
SpiceCoreInterface *core;
SpiceServer *server;
SpiceTabletInstance tablet;
core = basic_event_loop_init();
g_assert_nonnull(core);
// test if leaks without spice_server_remove_interface
server = spice_server_new();
g_assert_nonnull(server);
g_assert_cmpint(spice_server_init(server, core), ==, 0);
tablet.base.sif = &tablet_interface.base;
spice_server_add_interface(server, &tablet.base);
spice_server_destroy(server);
// test if leaks with spice_server_remove_interface
server = spice_server_new();
g_assert_nonnull(server);
g_assert_cmpint(spice_server_init(server, core), ==, 0);
tablet.base.sif = &tablet_interface.base;
spice_server_add_interface(server, &tablet.base);
spice_server_remove_interface(&tablet.base);
spice_server_destroy(server);
basic_event_loop_destroy();
}
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/server/server leaks", server_leaks);
g_test_add_func("/server/vmc leaks", vmc_leaks);
g_test_add_func("/server/migration leaks", migration_leaks);
g_test_add_func("/server/tablet leaks", tablet_leaks);
return g_test_run();
}