Import Upstream version 2.72.4

This commit is contained in:
evinadmin 2023-07-04 11:23:22 +02:00
commit 4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions

7
tests/assert-msg-test.c Normal file
View file

@ -0,0 +1,7 @@
#include <glib.h>
int main(int argc, char **argv)
{
g_assert(42 < 0);
return 0;
}

View file

@ -0,0 +1,5 @@
run
set print elements 0
# Work around https://sourceware.org/bugzilla/show_bug.cgi?id=22501
print *((char**) &__glib_assert_msg)
quit

View file

@ -0,0 +1,9 @@
223
bar
baz
c
eer34
er1
foo
GTK+
z

View file

@ -0,0 +1,9 @@
z
c
eer34
223
er1
foo
bar
baz
GTK+

View file

@ -0,0 +1,9 @@
223
bar
baz
c
eer34
er1
foo
GTK+
z

View file

@ -0,0 +1,13 @@
bla001
bla02
bla03
bla4
bla10
bla100
event.c
event.h
eventgenerator.c
file.c
file.txt
file2.bla
file3.xx

View file

@ -0,0 +1,13 @@
file.txt
file2.bla
file.c
file3.xx
bla001
bla02
bla03
bla4
bla10
bla100
event.c
eventgenerator.c
event.h

View file

@ -0,0 +1,13 @@
bla001
bla02
bla03
bla10
bla100
bla4
event.c
eventgenerator.c
event.h
file2.bla
file3.xx
file.c
file.txt

421
tests/gio-test.c Normal file
View file

@ -0,0 +1,421 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 2000 Tor Lillqvist
*
* 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/>.
*/
/* A test program for the main loop and IO channel code.
* Just run it. Optional parameter is number of sub-processes.
*/
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include "config.h"
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#ifdef G_OS_WIN32
#include <io.h>
#include <fcntl.h>
#include <process.h>
#define STRICT
#include <windows.h>
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
#endif
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
static int nrunning;
static GMainLoop *main_loop;
#define BUFSIZE 5000 /* Larger than the circular buffer in
* giowin32.c on purpose.
*/
static int nkiddies;
static struct {
int fd;
int seq;
} *seqtab;
static GIOError
read_all (int fd,
GIOChannel *channel,
char *buffer,
guint nbytes,
guint *bytes_read)
{
guint left = nbytes;
gsize nb;
GIOError error = G_IO_ERROR_NONE;
char *bufp = buffer;
/* g_io_channel_read() doesn't necessarily return all the
* data we want at once.
*/
*bytes_read = 0;
while (left)
{
error = g_io_channel_read (channel, bufp, left, &nb);
if (error != G_IO_ERROR_NONE)
{
g_print ("gio-test: ...from %d: %d\n", fd, error);
if (error == G_IO_ERROR_AGAIN)
continue;
break;
}
if (nb == 0)
return error;
left -= nb;
bufp += nb;
*bytes_read += nb;
}
return error;
}
static void
shutdown_source (gpointer data)
{
if (g_source_remove (*(guint *) data))
{
nrunning--;
if (nrunning == 0)
g_main_loop_quit (main_loop);
}
}
static gboolean
recv_message (GIOChannel *channel,
GIOCondition cond,
gpointer data)
{
gint fd = g_io_channel_unix_get_fd (channel);
gboolean retval = TRUE;
g_debug ("gio-test: ...from %d:%s%s%s%s", fd,
(cond & G_IO_ERR) ? " ERR" : "",
(cond & G_IO_HUP) ? " HUP" : "",
(cond & G_IO_IN) ? " IN" : "",
(cond & G_IO_PRI) ? " PRI" : "");
if (cond & (G_IO_ERR | G_IO_HUP))
{
shutdown_source (data);
retval = FALSE;
}
if (cond & G_IO_IN)
{
char buf[BUFSIZE];
guint nbytes = 0;
guint nb;
guint j;
int i, seq;
GIOError error;
error = read_all (fd, channel, (gchar *) &seq, sizeof (seq), &nb);
if (error == G_IO_ERROR_NONE)
{
if (nb == 0)
{
g_debug ("gio-test: ...from %d: EOF", fd);
shutdown_source (data);
return FALSE;
}
g_assert (nb == sizeof (nbytes));
for (i = 0; i < nkiddies; i++)
if (seqtab[i].fd == fd)
{
g_assert_cmpint (seq, ==, seqtab[i].seq);
seqtab[i].seq++;
break;
}
error = read_all (fd, channel, (gchar *) &nbytes, sizeof (nbytes), &nb);
}
if (error != G_IO_ERROR_NONE)
return FALSE;
if (nb == 0)
{
g_debug ("gio-test: ...from %d: EOF", fd);
shutdown_source (data);
return FALSE;
}
g_assert (nb == sizeof (nbytes));
g_assert_cmpint (nbytes, <, BUFSIZE);
g_assert (nbytes < BUFSIZE);
g_debug ("gio-test: ...from %d: %d bytes", fd, nbytes);
if (nbytes > 0)
{
error = read_all (fd, channel, buf, nbytes, &nb);
if (error != G_IO_ERROR_NONE)
return FALSE;
if (nb == 0)
{
g_debug ("gio-test: ...from %d: EOF", fd);
shutdown_source (data);
return FALSE;
}
for (j = 0; j < nbytes; j++)
g_assert (buf[j] == ' ' + (char) ((nbytes + j) % 95));
g_debug ("gio-test: ...from %d: OK", fd);
}
}
return retval;
}
#ifdef G_OS_WIN32
static gboolean
recv_windows_message (GIOChannel *channel,
GIOCondition cond,
gpointer data)
{
GIOError error;
MSG msg;
gsize nb;
while (1)
{
error = g_io_channel_read (channel, (gchar *) &msg, sizeof (MSG), &nb);
if (error != G_IO_ERROR_NONE)
{
g_print ("gio-test: ...reading Windows message: G_IO_ERROR_%s\n",
(error == G_IO_ERROR_AGAIN ? "AGAIN" :
(error == G_IO_ERROR_INVAL ? "INVAL" :
(error == G_IO_ERROR_UNKNOWN ? "UNKNOWN" : "???"))));
if (error == G_IO_ERROR_AGAIN)
continue;
}
break;
}
g_print ("gio-test: ...Windows message for 0x%p: %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT "\n",
msg.hwnd, msg.message, msg.wParam, (gintptr)msg.lParam);
return TRUE;
}
LRESULT CALLBACK window_procedure (HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam);
LRESULT CALLBACK
window_procedure (HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam)
{
g_print ("gio-test: window_procedure for 0x%p: %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT "\n",
hwnd, message, wparam, (gintptr)lparam);
return DefWindowProc (hwnd, message, wparam, lparam);
}
#endif
int
main (int argc,
char **argv)
{
if (argc < 3)
{
/* Parent */
GIOChannel *my_read_channel;
gchar *cmdline;
int i;
#ifdef G_OS_WIN32
GTimeVal start, end;
GPollFD pollfd;
int pollresult;
ATOM klass;
static WNDCLASS wcl;
HWND hwnd;
GIOChannel *windows_messages_channel;
#endif
nkiddies = (argc == 1 ? 1 : atoi(argv[1]));
seqtab = g_malloc (nkiddies * 2 * sizeof (int));
#ifdef G_OS_WIN32
wcl.style = 0;
wcl.lpfnWndProc = window_procedure;
wcl.cbClsExtra = 0;
wcl.cbWndExtra = 0;
wcl.hInstance = GetModuleHandle (NULL);
wcl.hIcon = NULL;
wcl.hCursor = NULL;
wcl.hbrBackground = NULL;
wcl.lpszMenuName = NULL;
wcl.lpszClassName = "gio-test";
klass = RegisterClass (&wcl);
if (!klass)
{
g_print ("gio-test: RegisterClass failed\n");
exit (1);
}
hwnd = CreateWindow (MAKEINTATOM(klass), "gio-test", 0, 0, 0, 10, 10,
NULL, NULL, wcl.hInstance, NULL);
if (!hwnd)
{
g_print ("gio-test: CreateWindow failed\n");
exit (1);
}
windows_messages_channel = g_io_channel_win32_new_messages ((guint) (guintptr) hwnd);
g_io_add_watch (windows_messages_channel, G_IO_IN, recv_windows_message, 0);
#endif
for (i = 0; i < nkiddies; i++)
{
int pipe_to_sub[2], pipe_from_sub[2];
guint *id;
if (pipe (pipe_to_sub) == -1 ||
pipe (pipe_from_sub) == -1)
perror ("pipe"), exit (1);
seqtab[i].fd = pipe_from_sub[0];
seqtab[i].seq = 0;
my_read_channel = g_io_channel_unix_new (pipe_from_sub[0]);
id = g_new (guint, 1);
*id =
g_io_add_watch_full (my_read_channel,
G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
recv_message,
id, g_free);
nrunning++;
#ifdef G_OS_WIN32
cmdline = g_strdup_printf ("%d:%d:0x%p",
pipe_to_sub[0],
pipe_from_sub[1],
hwnd);
_spawnl (_P_NOWAIT, argv[0], argv[0], "--child", cmdline, NULL);
#else
cmdline = g_strdup_printf ("%s --child %d:%d &", argv[0],
pipe_to_sub[0], pipe_from_sub[1]);
system (cmdline);
g_free (cmdline);
#endif
close (pipe_to_sub[0]);
close (pipe_from_sub [1]);
#ifdef G_OS_WIN32
g_get_current_time (&start);
g_io_channel_win32_make_pollfd (my_read_channel, G_IO_IN, &pollfd);
pollresult = g_io_channel_win32_poll (&pollfd, 1, 100);
g_get_current_time (&end);
if (end.tv_usec < start.tv_usec)
end.tv_sec--, end.tv_usec += 1000000;
g_print ("gio-test: had to wait %ld.%03ld s, result:%d\n",
end.tv_sec - start.tv_sec,
(end.tv_usec - start.tv_usec) / 1000,
pollresult);
#endif
g_io_channel_unref (my_read_channel);
}
main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (main_loop);
g_main_loop_unref (main_loop);
g_free (seqtab);
}
else if (argc == 3)
{
/* Child */
int readfd, writefd;
#ifdef G_OS_WIN32
HWND hwnd;
#endif
int i, j;
char buf[BUFSIZE];
int buflen;
GTimeVal tv;
int n;
g_get_current_time (&tv);
sscanf (argv[2], "%d:%d%n", &readfd, &writefd, &n);
#ifdef G_OS_WIN32
sscanf (argv[2] + n, ":0x%p", &hwnd);
#endif
srand (tv.tv_sec ^ (tv.tv_usec / 1000) ^ readfd ^ (writefd << 4));
for (i = 0; i < 20 + rand() % 20; i++)
{
g_usleep (100 + (rand() % 10) * 5000);
buflen = rand() % BUFSIZE;
for (j = 0; j < buflen; j++)
buf[j] = ' ' + ((buflen + j) % 95);
g_debug ("gio-test: child writing %d+%d bytes to %d",
(int)(sizeof(i) + sizeof(buflen)), buflen, writefd);
write (writefd, &i, sizeof (i));
write (writefd, &buflen, sizeof (buflen));
write (writefd, buf, buflen);
#ifdef G_OS_WIN32
if (rand() % 100 < 5)
{
int msg = WM_USER + (rand() % 100);
WPARAM wparam = rand ();
LPARAM lparam = rand ();
g_print ("gio-test: child posting message %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT " to 0x%p\n",
msg, wparam, (gintptr)lparam, hwnd);
PostMessage (hwnd, msg, wparam, lparam);
}
#endif
}
g_debug ("gio-test: child exiting, closing %d", writefd);
close (writefd);
}
else
g_print ("Huh?\n");
return 0;
}

15
tests/gobject/.gitignore vendored Normal file
View file

@ -0,0 +1,15 @@
accumulator
defaultiface
dynamictype
gvalue-test
ifacecheck
ifaceinherit
ifaceinit
ifaceproperties
override
paramspec-test
performance
performance-threaded
references
signals
singleton

307
tests/gobject/accumulator.c Normal file
View file

@ -0,0 +1,307 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001, 2003 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/>.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "TestAccumulator"
#undef G_DISABLE_ASSERT
#undef G_DISABLE_CHECKS
#undef G_DISABLE_CAST_CHECKS
#include <string.h>
#include <glib-object.h>
#include "testmarshal.h"
#include "testcommon.h"
/* What this test tests is the behavior of signal accumulators
* Two accumulators are tested:
*
* 1: A custom accumulator that appends the returned strings
* 2: The standard g_signal_accumulator_true_handled that stops
* emission on TRUE returns.
*/
/*
* TestObject, a parent class for TestObject
*/
#define TEST_TYPE_OBJECT (test_object_get_type ())
typedef struct _TestObject TestObject;
typedef struct _TestObjectClass TestObjectClass;
struct _TestObject
{
GObject parent_instance;
};
struct _TestObjectClass
{
GObjectClass parent_class;
gchar* (*test_signal1) (TestObject *tobject,
gint param);
gboolean (*test_signal2) (TestObject *tobject,
gint param);
GVariant* (*test_signal3) (TestObject *tobject,
gboolean *weak_ptr);
};
static GType test_object_get_type (void);
static gboolean
test_signal1_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data)
{
const gchar *accu_string = g_value_get_string (return_accu);
const gchar *new_string = g_value_get_string (handler_return);
gchar *result_string;
if (accu_string)
result_string = g_strconcat (accu_string, new_string, NULL);
else if (new_string)
result_string = g_strdup (new_string);
else
result_string = NULL;
g_value_set_string_take_ownership (return_accu, result_string);
return TRUE;
}
static gchar *
test_object_signal1_callback_before (TestObject *tobject,
gint param,
gpointer data)
{
return g_strdup ("<before>");
}
static gchar *
test_object_real_signal1 (TestObject *tobject,
gint param)
{
return g_strdup ("<default>");
}
static gchar *
test_object_signal1_callback_after (TestObject *tobject,
gint param,
gpointer data)
{
return g_strdup ("<after>");
}
static gboolean
test_object_signal2_callback_before (TestObject *tobject,
gint param)
{
switch (param)
{
case 1: return TRUE;
case 2: return FALSE;
case 3: return FALSE;
case 4: return FALSE;
}
g_assert_not_reached ();
return FALSE;
}
static gboolean
test_object_real_signal2 (TestObject *tobject,
gint param)
{
switch (param)
{
case 1: g_assert_not_reached (); return FALSE;
case 2: return TRUE;
case 3: return FALSE;
case 4: return FALSE;
}
g_assert_not_reached ();
return FALSE;
}
static gboolean
test_object_signal2_callback_after (TestObject *tobject,
gint param)
{
switch (param)
{
case 1: g_assert_not_reached (); return FALSE;
case 2: g_assert_not_reached (); return FALSE;
case 3: return TRUE;
case 4: return FALSE;
}
g_assert_not_reached ();
return FALSE;
}
static gboolean
test_signal3_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data)
{
GVariant *variant;
variant = g_value_get_variant (handler_return);
g_assert (!g_variant_is_floating (variant));
g_value_set_variant (return_accu, variant);
return variant == NULL;
}
/* To be notified when the variant is finalised, we construct
* it from data with a custom GDestroyNotify.
*/
typedef struct {
char *mem;
gsize n;
gboolean *weak_ptr;
} VariantData;
static void
free_data (VariantData *data)
{
*(data->weak_ptr) = TRUE;
g_free (data->mem);
g_slice_free (VariantData, data);
}
static GVariant *
test_object_real_signal3 (TestObject *tobject,
gboolean *weak_ptr)
{
GVariant *variant;
VariantData *data;
variant = g_variant_ref_sink (g_variant_new_uint32 (42));
data = g_slice_new (VariantData);
data->weak_ptr = weak_ptr;
data->n = g_variant_get_size (variant);
data->mem = g_malloc (data->n);
g_variant_store (variant, data->mem);
g_variant_unref (variant);
variant = g_variant_new_from_data (G_VARIANT_TYPE ("u"),
data->mem,
data->n,
TRUE,
(GDestroyNotify) free_data,
data);
return g_variant_ref_sink (variant);
}
static void
test_object_class_init (TestObjectClass *class)
{
class->test_signal1 = test_object_real_signal1;
class->test_signal2 = test_object_real_signal2;
class->test_signal3 = test_object_real_signal3;
g_signal_new ("test-signal1",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (TestObjectClass, test_signal1),
test_signal1_accumulator, NULL,
test_marshal_STRING__INT,
G_TYPE_STRING, 1, G_TYPE_INT);
g_signal_new ("test-signal2",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (TestObjectClass, test_signal2),
g_signal_accumulator_true_handled, NULL,
test_marshal_BOOLEAN__INT,
G_TYPE_BOOLEAN, 1, G_TYPE_INT);
g_signal_new ("test-signal3",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (TestObjectClass, test_signal3),
test_signal3_accumulator, NULL,
test_marshal_VARIANT__POINTER,
G_TYPE_VARIANT, 1, G_TYPE_POINTER);
}
static DEFINE_TYPE(TestObject, test_object,
test_object_class_init, NULL, NULL,
G_TYPE_OBJECT)
int
main (int argc,
char *argv[])
{
TestObject *object;
gchar *string_result;
gboolean bool_result;
gboolean variant_finalised;
GVariant *variant_result;
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
G_LOG_LEVEL_WARNING |
G_LOG_LEVEL_CRITICAL);
object = g_object_new (TEST_TYPE_OBJECT, NULL);
g_signal_connect (object, "test-signal1",
G_CALLBACK (test_object_signal1_callback_before), NULL);
g_signal_connect_after (object, "test-signal1",
G_CALLBACK (test_object_signal1_callback_after), NULL);
g_signal_emit_by_name (object, "test-signal1", 0, &string_result);
g_assert (strcmp (string_result, "<before><default><after>") == 0);
g_free (string_result);
g_signal_connect (object, "test-signal2",
G_CALLBACK (test_object_signal2_callback_before), NULL);
g_signal_connect_after (object, "test-signal2",
G_CALLBACK (test_object_signal2_callback_after), NULL);
bool_result = FALSE;
g_signal_emit_by_name (object, "test-signal2", 1, &bool_result);
g_assert (bool_result == TRUE);
bool_result = FALSE;
g_signal_emit_by_name (object, "test-signal2", 2, &bool_result);
g_assert (bool_result == TRUE);
bool_result = FALSE;
g_signal_emit_by_name (object, "test-signal2", 3, &bool_result);
g_assert (bool_result == TRUE);
bool_result = TRUE;
g_signal_emit_by_name (object, "test-signal2", 4, &bool_result);
g_assert (bool_result == FALSE);
variant_finalised = FALSE;
variant_result = NULL;
g_signal_emit_by_name (object, "test-signal3", &variant_finalised, &variant_result);
g_assert (variant_result != NULL);
g_assert (!g_variant_is_floating (variant_result));
/* Test that variant_result had refcount 1 */
g_assert (!variant_finalised);
g_variant_unref (variant_result);
g_assert (variant_finalised);
g_object_unref (object);
return 0;
}

View file

@ -0,0 +1,199 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001, 2003 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/>.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "TestDefaultIface"
#undef G_DISABLE_ASSERT
#undef G_DISABLE_CHECKS
#undef G_DISABLE_CAST_CHECKS
#include <glib-object.h>
#include "testcommon.h"
#include "testmodule.h"
/* This test tests getting the default vtable for an interface
* and the initialization and finalization of such default
* interfaces.
*
* We test this both for static and for dynamic interfaces.
*/
/**********************************************************************
* Static interface tests
**********************************************************************/
typedef struct _TestStaticIfaceClass TestStaticIfaceClass;
struct _TestStaticIfaceClass
{
GTypeInterface base_iface;
guint val;
};
GType test_static_iface_get_type (void);
#define TEST_TYPE_STATIC_IFACE (test_static_iface_get_type ())
static void
test_static_iface_default_init (TestStaticIfaceClass *iface)
{
iface->val = 42;
}
DEFINE_IFACE (TestStaticIface, test_static_iface,
NULL, test_static_iface_default_init)
static void
test_static_iface (void)
{
TestStaticIfaceClass *static_iface;
/* Not loaded until we call ref for the first time */
static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
g_assert (static_iface == NULL);
/* Ref loads */
static_iface = g_type_default_interface_ref (TEST_TYPE_STATIC_IFACE);
g_assert (static_iface && static_iface->val == 42);
/* Peek then works */
static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
g_assert (static_iface && static_iface->val == 42);
/* Unref does nothing */
g_type_default_interface_unref (static_iface);
/* And peek still works */
static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
g_assert (static_iface && static_iface->val == 42);
}
/**********************************************************************
* Dynamic interface tests
**********************************************************************/
typedef struct _TestDynamicIfaceClass TestDynamicIfaceClass;
struct _TestDynamicIfaceClass
{
GTypeInterface base_iface;
guint val;
};
static GType test_dynamic_iface_type;
static gboolean dynamic_iface_init = FALSE;
#define TEST_TYPE_DYNAMIC_IFACE (test_dynamic_iface_type)
static void
test_dynamic_iface_default_init (TestStaticIfaceClass *iface)
{
dynamic_iface_init = TRUE;
iface->val = 42;
}
static void
test_dynamic_iface_default_finalize (TestStaticIfaceClass *iface)
{
dynamic_iface_init = FALSE;
}
static void
test_dynamic_iface_register (GTypeModule *module)
{
const GTypeInfo iface_info =
{
sizeof (TestDynamicIfaceClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) test_dynamic_iface_default_init,
(GClassFinalizeFunc) test_dynamic_iface_default_finalize,
NULL,
0,
0,
NULL,
NULL
};
test_dynamic_iface_type = g_type_module_register_type (module, G_TYPE_INTERFACE,
"TestDynamicIface", &iface_info, 0);
}
static void
module_register (GTypeModule *module)
{
test_dynamic_iface_register (module);
}
static void
test_dynamic_iface (void)
{
TestDynamicIfaceClass *dynamic_iface;
test_module_new (module_register);
/* Not loaded until we call ref for the first time */
dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
g_assert (dynamic_iface == NULL);
/* Ref loads */
dynamic_iface = g_type_default_interface_ref (TEST_TYPE_DYNAMIC_IFACE);
g_assert (dynamic_iface_init);
g_assert (dynamic_iface && dynamic_iface->val == 42);
/* Peek then works */
dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
g_assert (dynamic_iface && dynamic_iface->val == 42);
/* Unref causes finalize */
g_type_default_interface_unref (dynamic_iface);
#if 0
g_assert (!dynamic_iface_init);
#endif
/* Peek returns NULL */
dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
#if 0
g_assert (dynamic_iface == NULL);
#endif
/* Ref reloads */
dynamic_iface = g_type_default_interface_ref (TEST_TYPE_DYNAMIC_IFACE);
g_assert (dynamic_iface_init);
g_assert (dynamic_iface && dynamic_iface->val == 42);
/* And Unref causes finalize once more*/
g_type_default_interface_unref (dynamic_iface);
#if 0
g_assert (!dynamic_iface_init);
#endif
}
int
main (int argc,
char *argv[])
{
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
G_LOG_LEVEL_WARNING |
G_LOG_LEVEL_CRITICAL);
test_static_iface ();
test_dynamic_iface ();
return 0;
}

59
tests/gobject/deftype.c Normal file
View file

@ -0,0 +1,59 @@
/* deftype.c
* Copyright (C) 2006 Behdad Esfahbod
*
* 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/>.
*/
#include <glib-object.h>
/* see http://bugzilla.gnome.org/show_bug.cgi?id=337128 for the purpose of this test */
#define MY_G_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init) { \
const GInterfaceInfo g_implement_interface_info = { \
(GInterfaceInitFunc) iface_init, \
NULL, \
NULL \
}; \
g_type_add_interface_static (g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
}
#define MY_DEFINE_TYPE(TN, t_n, T_P) \
G_DEFINE_TYPE_WITH_CODE (TN, t_n, T_P, \
MY_G_IMPLEMENT_INTERFACE (G_TYPE_INTERFACE, NULL))
typedef struct _TypeName {
GObject parent_instance;
const char *name;
} TypeName;
typedef struct _TypeNameClass {
GObjectClass parent_parent;
} TypeNameClass;
GType type_name_get_type (void);
MY_DEFINE_TYPE (TypeName, type_name, G_TYPE_OBJECT)
static void type_name_init (TypeName *self)
{
}
static void type_name_class_init (TypeNameClass *klass)
{
}
int
main (void)
{
return 0;
}

175
tests/gobject/dynamictype.c Normal file
View file

@ -0,0 +1,175 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001, 2003 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/>.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "TestDynamicType"
#undef G_DISABLE_ASSERT
#undef G_DISABLE_CHECKS
#undef G_DISABLE_CAST_CHECKS
#include <glib-object.h>
#include "testcommon.h"
#include "testmodule.h"
/* This test tests the macros for defining dynamic types.
*/
static gboolean loaded = FALSE;
struct _TestIfaceClass
{
GTypeInterface base_iface;
guint val;
};
static GType test_iface_get_type (void);
#define TEST_TYPE_IFACE (test_iface_get_type ())
#define TEST_IFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE, TestIfaceClass))
typedef struct _TestIface TestIface;
typedef struct _TestIfaceClass TestIfaceClass;
static void test_iface_base_init (TestIfaceClass *iface);
static void test_iface_default_init (TestIfaceClass *iface, gpointer class_data);
static DEFINE_IFACE(TestIface, test_iface, test_iface_base_init, test_iface_default_init)
static void
test_iface_default_init (TestIfaceClass *iface,
gpointer class_data)
{
}
static void
test_iface_base_init (TestIfaceClass *iface)
{
}
GType dynamic_object_get_type (void);
#define DYNAMIC_OBJECT_TYPE (dynamic_object_get_type ())
typedef GObject DynamicObject;
typedef struct _DynamicObjectClass DynamicObjectClass;
struct _DynamicObjectClass
{
GObjectClass parent_class;
guint val;
};
static void dynamic_object_iface_init (TestIface *iface);
G_DEFINE_DYNAMIC_TYPE_EXTENDED(DynamicObject, dynamic_object, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC (TEST_TYPE_IFACE,
dynamic_object_iface_init));
static void
dynamic_object_class_init (DynamicObjectClass *class)
{
class->val = 42;
loaded = TRUE;
}
static void
dynamic_object_class_finalize (DynamicObjectClass *class)
{
loaded = FALSE;
}
static void
dynamic_object_iface_init (TestIface *iface)
{
}
static void
dynamic_object_init (DynamicObject *dynamic_object)
{
}
static void
module_register (GTypeModule *module)
{
dynamic_object_register_type (module);
}
static void
test_dynamic_type (void)
{
DynamicObjectClass *class;
test_module_new (module_register);
/* Not loaded until we call ref for the first time */
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
g_assert (class == NULL);
g_assert (!loaded);
/* Make sure interfaces work */
g_assert (g_type_is_a (DYNAMIC_OBJECT_TYPE,
TEST_TYPE_IFACE));
/* Ref loads */
class = g_type_class_ref (DYNAMIC_OBJECT_TYPE);
g_assert (class && class->val == 42);
g_assert (loaded);
/* Peek then works */
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
g_assert (class && class->val == 42);
g_assert (loaded);
/* Make sure interfaces still work */
g_assert (g_type_is_a (DYNAMIC_OBJECT_TYPE,
TEST_TYPE_IFACE));
/* Unref causes finalize */
g_type_class_unref (class);
/* Peek returns NULL */
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
#if 0
g_assert (!class);
g_assert (!loaded);
#endif
/* Ref reloads */
class = g_type_class_ref (DYNAMIC_OBJECT_TYPE);
g_assert (class && class->val == 42);
g_assert (loaded);
/* And Unref causes finalize once more*/
g_type_class_unref (class);
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
#if 0
g_assert (!class);
g_assert (!loaded);
#endif
}
int
main (int argc,
char *argv[])
{
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
G_LOG_LEVEL_WARNING |
G_LOG_LEVEL_CRITICAL);
test_dynamic_type ();
return 0;
}

98
tests/gobject/meson.build Normal file
View file

@ -0,0 +1,98 @@
# We cannot use gnome.genmarshal() here
testmarshal_h = custom_target('testmarshal_h',
output : 'testmarshal.h',
input : 'testmarshal.list',
command : [
python, glib_genmarshal,
'--prefix=test_marshal',
'--output=@OUTPUT@',
'--quiet',
'--header',
'@INPUT@',
],
)
testmarshal_c = custom_target('testmarshal_c',
output : 'testmarshal.c',
input : 'testmarshal.list',
command : [
python, glib_genmarshal,
'--prefix=test_marshal',
'--include-header=testmarshal.h',
'--output=@OUTPUT@',
'--quiet',
'--body',
'@INPUT@',
],
)
gobject_tests = {
'deftype' : {},
'defaultiface' : {
'extra_sources' : ['testmodule.c'],
},
'dynamictype' : {
'extra_sources' : ['testmodule.c'],
},
'override' : {},
'signals' : {},
'singleton' : {},
'references' : {},
'testgobject' : {},
'accumulator' : {
'extra_sources' : [testmarshal_c, testmarshal_h],
},
}
if host_system != 'windows'
gobject_tests += {
'timeloop-closure' : {},
}
endif
common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
common_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
foreach test_name, extra_args : gobject_tests
source = extra_args.get('source', test_name + '.c')
extra_sources = extra_args.get('extra_sources', [])
install = installed_tests_enabled and extra_args.get('install', true)
template = extra_args.get('tap', false) ? installed_tests_template_tap : installed_tests_template
if install
test_conf = configuration_data()
test_conf.set('installed_tests_dir', installed_tests_execdir)
test_conf.set('program', test_name)
test_conf.set('env', '')
configure_file(
input: template,
output: test_name + '.test',
install_dir: installed_tests_metadir,
configuration: test_conf
)
endif
# FIXME? $(GLIB_DEBUG_FLAGS)
exe = executable(test_name, [source, extra_sources],
c_args : common_c_args + extra_args.get('c_args', []),
dependencies : common_deps + extra_args.get('dependencies', []),
install_dir: installed_tests_execdir,
install: install,
)
suite = ['gobject'] + extra_args.get('suite', [])
timeout = suite.contains('slow') ? test_timeout_slow : test_timeout
# FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
endforeach
# Don't install these ones, and keep them out of 'make check' because they take too long...
executable('performance', 'performance.c',
c_args : common_c_args,
dependencies : common_deps,
install : false)
executable('performance-threaded', 'performance-threaded.c',
c_args : common_c_args,
dependencies : common_deps,
install : false)

418
tests/gobject/override.c Normal file
View file

@ -0,0 +1,418 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* override.c: Closure override test program
* Copyright (C) 2001, James Henstridge
* Copyright (C) 2003, 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/>.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "TestOverride"
#undef G_DISABLE_ASSERT
#undef G_DISABLE_CHECKS
#undef G_DISABLE_CAST_CHECKS
#undef VERBOSE
#include <string.h>
#include <glib.h>
#include <glib-object.h>
#include "testcommon.h"
static guint foo_signal_id = 0;
static guint bar_signal_id = 0;
static guint baz_signal_id = 0;
static GType test_i_get_type (void);
static GType test_a_get_type (void);
static GType test_b_get_type (void);
static GType test_c_get_type (void);
static void record (const gchar *str);
#define TEST_TYPE_I (test_i_get_type ())
typedef struct _TestI TestI;
typedef struct _TestIClass TestIClass;
struct _TestIClass
{
GTypeInterface base_iface;
};
static void
test_i_foo (TestI *self)
{
record ("TestI::foo");
}
static void
test_i_default_init (gpointer g_class)
{
foo_signal_id = g_signal_newv ("foo",
TEST_TYPE_I,
G_SIGNAL_RUN_LAST,
g_cclosure_new(G_CALLBACK(test_i_foo),
NULL, NULL),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, NULL);
}
static DEFINE_IFACE (TestI, test_i, NULL, test_i_default_init)
#define TEST_TYPE_A (test_a_get_type())
typedef struct _TestA TestA;
typedef struct _TestAClass TestAClass;
struct _TestA {
GObject parent;
};
struct _TestAClass {
GObjectClass parent_class;
void (* bar) (TestA *self);
};
static void
test_a_foo (TestI *self)
{
GValue args[1] = { G_VALUE_INIT };
record ("TestA::foo");
g_value_init (&args[0], TEST_TYPE_A);
g_value_set_object (&args[0], self);
g_assert (g_signal_get_invocation_hint (self)->signal_id == foo_signal_id);
g_signal_chain_from_overridden (args, NULL);
g_value_unset (&args[0]);
}
static void
test_a_bar (TestA *self)
{
record ("TestA::bar");
}
static gchar *
test_a_baz (TestA *self,
GObject *object,
gpointer pointer)
{
record ("TestA::baz");
g_assert (object == G_OBJECT (self));
g_assert (GPOINTER_TO_INT (pointer) == 23);
return g_strdup ("TestA::baz");
}
static void
test_a_class_init (TestAClass *class)
{
class->bar = test_a_bar;
bar_signal_id = g_signal_new ("bar",
TEST_TYPE_A,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (TestAClass, bar),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, NULL);
baz_signal_id = g_signal_new_class_handler ("baz",
TEST_TYPE_A,
G_SIGNAL_RUN_LAST,
G_CALLBACK (test_a_baz),
NULL, NULL,
g_cclosure_marshal_STRING__OBJECT_POINTER,
G_TYPE_STRING, 2,
G_TYPE_OBJECT,
G_TYPE_POINTER);
}
static void
test_a_interface_init (TestIClass *iface)
{
g_signal_override_class_closure (foo_signal_id,
TEST_TYPE_A,
g_cclosure_new (G_CALLBACK (test_a_foo),
NULL, NULL));
}
static DEFINE_TYPE_FULL (TestA, test_a,
test_a_class_init, NULL, NULL,
G_TYPE_OBJECT,
INTERFACE (test_a_interface_init, TEST_TYPE_I))
#define TEST_TYPE_B (test_b_get_type())
typedef struct _TestB TestB;
typedef struct _TestBClass TestBClass;
struct _TestB {
TestA parent;
};
struct _TestBClass {
TestAClass parent_class;
};
static void
test_b_foo (TestI *self)
{
GValue args[1] = { G_VALUE_INIT };
record ("TestB::foo");
g_value_init (&args[0], TEST_TYPE_A);
g_value_set_object (&args[0], self);
g_assert (g_signal_get_invocation_hint (self)->signal_id == foo_signal_id);
g_signal_chain_from_overridden (args, NULL);
g_value_unset (&args[0]);
}
static void
test_b_bar (TestA *self)
{
GValue args[1] = { G_VALUE_INIT };
record ("TestB::bar");
g_value_init (&args[0], TEST_TYPE_A);
g_value_set_object (&args[0], self);
g_assert (g_signal_get_invocation_hint (self)->signal_id == bar_signal_id);
g_signal_chain_from_overridden (args, NULL);
g_value_unset (&args[0]);
}
static gchar *
test_b_baz (TestA *self,
GObject *object,
gpointer pointer)
{
gchar *retval = NULL;
record ("TestB::baz");
g_assert (object == G_OBJECT (self));
g_assert (GPOINTER_TO_INT (pointer) == 23);
g_signal_chain_from_overridden_handler (self, object, pointer, &retval);
if (retval)
{
gchar *tmp = g_strconcat (retval , ",TestB::baz", NULL);
g_free (retval);
retval = tmp;
}
return retval;
}
static void
test_b_class_init (TestBClass *class)
{
g_signal_override_class_closure (foo_signal_id,
TEST_TYPE_B,
g_cclosure_new (G_CALLBACK (test_b_foo),
NULL, NULL));
g_signal_override_class_closure (bar_signal_id,
TEST_TYPE_B,
g_cclosure_new (G_CALLBACK (test_b_bar),
NULL, NULL));
g_signal_override_class_handler ("baz",
TEST_TYPE_B,
G_CALLBACK (test_b_baz));
}
static DEFINE_TYPE (TestB, test_b,
test_b_class_init, NULL, NULL,
TEST_TYPE_A)
#define TEST_TYPE_C (test_c_get_type())
typedef struct _TestC TestC;
typedef struct _TestCClass TestCClass;
struct _TestC {
TestB parent;
};
struct _TestCClass {
TestBClass parent_class;
};
static void
test_c_foo (TestI *self)
{
GValue args[1] = { G_VALUE_INIT };
record ("TestC::foo");
g_value_init (&args[0], TEST_TYPE_A);
g_value_set_object (&args[0], self);
g_assert (g_signal_get_invocation_hint (self)->signal_id == foo_signal_id);
g_signal_chain_from_overridden (args, NULL);
g_value_unset (&args[0]);
}
static void
test_c_bar (TestA *self)
{
GValue args[1] = { G_VALUE_INIT };
record ("TestC::bar");
g_value_init (&args[0], TEST_TYPE_A);
g_value_set_object (&args[0], self);
g_assert (g_signal_get_invocation_hint (self)->signal_id == bar_signal_id);
g_signal_chain_from_overridden (args, NULL);
g_value_unset (&args[0]);
}
static gchar *
test_c_baz (TestA *self,
GObject *object,
gpointer pointer)
{
gchar *retval = NULL;
record ("TestC::baz");
g_assert (object == G_OBJECT (self));
g_assert (GPOINTER_TO_INT (pointer) == 23);
g_signal_chain_from_overridden_handler (self, object, pointer, &retval);
if (retval)
{
gchar *tmp = g_strconcat (retval , ",TestC::baz", NULL);
g_free (retval);
retval = tmp;
}
return retval;
}
static void
test_c_class_init (TestBClass *class)
{
g_signal_override_class_closure (foo_signal_id,
TEST_TYPE_C,
g_cclosure_new (G_CALLBACK (test_c_foo),
NULL, NULL));
g_signal_override_class_closure (bar_signal_id,
TEST_TYPE_C,
g_cclosure_new (G_CALLBACK (test_c_bar),
NULL, NULL));
g_signal_override_class_handler ("baz",
TEST_TYPE_C,
G_CALLBACK (test_c_baz));
}
static DEFINE_TYPE (TestC, test_c,
test_c_class_init, NULL, NULL,
TEST_TYPE_B)
static GString *test_string = NULL;
gboolean failed = FALSE;
static void
record (const gchar *str)
{
if (test_string->len)
g_string_append_c (test_string, ',');
g_string_append (test_string, str);
}
static void
test (GType type,
const gchar *signal,
const gchar *expected,
const gchar *expected_retval)
{
GObject *self = g_object_new (type, NULL);
test_string = g_string_new (NULL);
if (strcmp (signal, "baz"))
{
g_signal_emit_by_name (self, signal);
}
else
{
gchar *ret;
g_signal_emit_by_name (self, signal, self, GINT_TO_POINTER (23), &ret);
if (strcmp (ret, expected_retval) != 0)
failed = TRUE;
g_free (ret);
}
#ifndef VERBOSE
if (strcmp (test_string->str, expected) != 0)
#endif
{
g_printerr ("*** emitting %s on a %s instance\n"
" Expecting: %s\n"
" Got: %s\n",
signal, g_type_name (type),
expected,
test_string->str);
if (strcmp (test_string->str, expected) != 0)
failed = TRUE;
}
g_string_free (test_string, TRUE);
g_object_unref (self);
}
int
main (int argc, char **argv)
{
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
G_LOG_LEVEL_WARNING |
G_LOG_LEVEL_CRITICAL);
test (TEST_TYPE_A, "foo", "TestA::foo,TestI::foo", NULL);
test (TEST_TYPE_A, "bar", "TestA::bar", NULL);
test (TEST_TYPE_A, "baz", "TestA::baz", "TestA::baz");
test (TEST_TYPE_B, "foo", "TestB::foo,TestA::foo,TestI::foo", NULL);
test (TEST_TYPE_B, "bar", "TestB::bar,TestA::bar", NULL);
test (TEST_TYPE_B, "baz", "TestB::baz,TestA::baz", "TestA::baz,TestB::baz");
test (TEST_TYPE_C, "foo", "TestC::foo,TestB::foo,TestA::foo,TestI::foo", NULL);
test (TEST_TYPE_C, "bar", "TestC::bar,TestB::bar,TestA::bar", NULL);
test (TEST_TYPE_C, "baz", "TestC::baz,TestB::baz,TestA::baz", "TestA::baz,TestB::baz,TestC::baz");
return failed ? 1 : 0;
}

View file

@ -0,0 +1,375 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2009 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/>.
*/
#include <math.h>
#include <string.h>
#include <glib-object.h>
#include "testcommon.h"
#define DEFAULT_TEST_TIME 2 /* seconds */
static GType
simple_register_class (const char *name, GType parent, ...)
{
GInterfaceInfo interface_info = { NULL, NULL, NULL };
va_list args;
GType type, interface;
va_start (args, parent);
type = g_type_register_static_simple (parent, name, sizeof (GObjectClass),
NULL, parent == G_TYPE_INTERFACE ? 0 : sizeof (GObject), NULL, 0);
for (;;)
{
interface = va_arg (args, GType);
if (interface == 0)
break;
g_type_add_interface_static (type, interface, &interface_info);
}
va_end (args);
return type;
}
/* test emulating liststore behavior for interface lookups */
static GType liststore;
static GType liststore_interfaces[6];
static gpointer
register_types (void)
{
static gsize inited = 0;
if (g_once_init_enter (&inited))
{
liststore_interfaces[0] = simple_register_class ("GtkBuildable", G_TYPE_INTERFACE, 0);
liststore_interfaces[1] = simple_register_class ("GtkTreeDragDest", G_TYPE_INTERFACE, 0);
liststore_interfaces[2] = simple_register_class ("GtkTreeModel", G_TYPE_INTERFACE, 0);
liststore_interfaces[3] = simple_register_class ("GtkTreeDragSource", G_TYPE_INTERFACE, 0);
liststore_interfaces[4] = simple_register_class ("GtkTreeSortable", G_TYPE_INTERFACE, 0);
liststore_interfaces[5] = simple_register_class ("UnrelatedInterface", G_TYPE_INTERFACE, 0);
liststore = simple_register_class ("GtkListStore", G_TYPE_OBJECT,
liststore_interfaces[0], liststore_interfaces[1], liststore_interfaces[2],
liststore_interfaces[3], liststore_interfaces[4], (GType) 0);
g_once_init_leave (&inited, 1);
}
return NULL;
}
static void
liststore_is_a_run (gpointer data)
{
guint i;
for (i = 0; i < 1000; i++)
{
g_assert (g_type_is_a (liststore, liststore_interfaces[0]));
g_assert (g_type_is_a (liststore, liststore_interfaces[1]));
g_assert (g_type_is_a (liststore, liststore_interfaces[2]));
g_assert (g_type_is_a (liststore, liststore_interfaces[3]));
g_assert (g_type_is_a (liststore, liststore_interfaces[4]));
g_assert (!g_type_is_a (liststore, liststore_interfaces[5]));
}
}
static gpointer
liststore_get_class (void)
{
register_types ();
return g_type_class_ref (liststore);
}
static void
liststore_interface_peek_run (gpointer klass)
{
guint i;
gpointer iface;
for (i = 0; i < 1000; i++)
{
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
g_assert (iface);
iface = g_type_interface_peek (klass, liststore_interfaces[1]);
g_assert (iface);
iface = g_type_interface_peek (klass, liststore_interfaces[2]);
g_assert (iface);
iface = g_type_interface_peek (klass, liststore_interfaces[3]);
g_assert (iface);
iface = g_type_interface_peek (klass, liststore_interfaces[4]);
g_assert (iface);
}
}
static void
liststore_interface_peek_same_run (gpointer klass)
{
guint i;
gpointer iface;
for (i = 0; i < 1000; i++)
{
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
g_assert (iface);
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
g_assert (iface);
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
g_assert (iface);
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
g_assert (iface);
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
g_assert (iface);
}
}
#if 0
/* DUMB test doing nothing */
static gpointer
no_setup (void)
{
return NULL;
}
static void
no_run (gpointer data)
{
}
#endif
static void
no_reset (gpointer data)
{
}
static void
no_teardown (gpointer data)
{
}
typedef struct _PerformanceTest PerformanceTest;
struct _PerformanceTest {
const char *name;
gpointer (*setup) (void);
void (*run) (gpointer data);
void (*reset) (gpointer data);
void (*teardown) (gpointer data);
};
static const PerformanceTest tests[] = {
{ "liststore-is-a",
register_types,
liststore_is_a_run,
no_reset,
no_teardown },
{ "liststore-interface-peek",
liststore_get_class,
liststore_interface_peek_run,
no_reset,
g_type_class_unref },
{ "liststore-interface-peek-same",
liststore_get_class,
liststore_interface_peek_same_run,
no_reset,
g_type_class_unref },
#if 0
{ "nothing",
no_setup,
no_run,
no_reset,
no_teardown }
#endif
};
static gboolean verbose = FALSE;
static guint n_threads = 0;
static gboolean list = FALSE;
static int test_length = DEFAULT_TEST_TIME;
static GOptionEntry cmd_entries[] = {
{"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
"Print extra information", NULL},
{"threads", 't', 0, G_OPTION_ARG_INT, &n_threads,
"number of threads to run in parallel", NULL},
{"seconds", 's', 0, G_OPTION_ARG_INT, &test_length,
"Time to run each test in seconds", NULL},
{"list", 'l', 0, G_OPTION_ARG_NONE, &list,
"List all available tests and exit", NULL},
G_OPTION_ENTRY_NULL
};
static gpointer
run_test_thread (gpointer user_data)
{
const PerformanceTest *test = user_data;
gpointer data;
double elapsed;
GTimer *timer, *total;
GArray *results;
total = g_timer_new ();
g_timer_start (total);
/* Set up test */
timer = g_timer_new ();
data = test->setup ();
results = g_array_new (FALSE, FALSE, sizeof (double));
/* Run the test */
while (g_timer_elapsed (total, NULL) < test_length)
{
g_timer_reset (timer);
g_timer_start (timer);
test->run (data);
g_timer_stop (timer);
elapsed = g_timer_elapsed (timer, NULL);
g_array_append_val (results, elapsed);
test->reset (data);
}
/* Tear down */
test->teardown (data);
g_timer_destroy (timer);
g_timer_destroy (total);
return results;
}
static int
compare_doubles (gconstpointer a, gconstpointer b)
{
double d = *(double *) a - *(double *) b;
if (d < 0)
return -1;
if (d > 0)
return 1;
return 0;
}
static void
print_results (GArray *array)
{
double min, max, avg;
guint i;
g_array_sort (array, compare_doubles);
/* FIXME: discard outliers */
min = g_array_index (array, double, 0) * 1000;
max = g_array_index (array, double, array->len - 1) * 1000;
avg = 0;
for (i = 0; i < array->len; i++)
{
avg += g_array_index (array, double, i);
}
avg = avg / array->len * 1000;
g_print (" %u runs, min/avg/max = %.3f/%.3f/%.3f ms\n", array->len, min, avg, max);
}
static void
run_test (const PerformanceTest *test)
{
GArray *results;
g_print ("Running test \"%s\"\n", test->name);
if (n_threads == 0) {
results = run_test_thread ((gpointer) test);
} else {
guint i;
GThread **threads;
GArray *thread_results;
threads = g_new (GThread *, n_threads);
for (i = 0; i < n_threads; i++) {
threads[i] = g_thread_create (run_test_thread, (gpointer) test, TRUE, NULL);
g_assert (threads[i] != NULL);
}
results = g_array_new (FALSE, FALSE, sizeof (double));
for (i = 0; i < n_threads; i++) {
thread_results = g_thread_join (threads[i]);
g_array_append_vals (results, thread_results->data, thread_results->len);
g_array_free (thread_results, TRUE);
}
g_free (threads);
}
print_results (results);
g_array_free (results, TRUE);
}
static const PerformanceTest *
find_test (const char *name)
{
gsize i;
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
if (strcmp (tests[i].name, name) == 0)
return &tests[i];
}
return NULL;
}
int
main (int argc,
char *argv[])
{
const PerformanceTest *test;
GOptionContext *context;
GError *error = NULL;
gsize i;
context = g_option_context_new ("GObject performance tests");
g_option_context_add_main_entries (context, cmd_entries, NULL);
if (!g_option_context_parse (context, &argc, &argv, &error))
{
g_printerr ("%s: %s\n", argv[0], error->message);
return 1;
}
if (list)
{
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
g_print ("%s\n", tests[i].name);
}
return 0;
}
if (argc > 1)
{
int k;
for (k = 1; k < argc; k++)
{
test = find_test (argv[k]);
if (test)
run_test (test);
}
}
else
{
for (i = 0; i < G_N_ELEMENTS (tests); i++)
run_test (&tests[i]);
}
return 0;
}

1060
tests/gobject/performance.c Normal file

File diff suppressed because it is too large Load diff

280
tests/gobject/references.c Normal file
View file

@ -0,0 +1,280 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2005 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/>.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "TestReferences"
#undef G_DISABLE_ASSERT
#undef G_DISABLE_CHECKS
#undef G_DISABLE_CAST_CHECKS
#include <glib-object.h>
/* This test tests weak and toggle references
*/
static GObject *global_object;
static gboolean object_destroyed;
static gboolean weak_ref1_notified;
static gboolean weak_ref2_notified;
static gboolean toggle_ref1_weakened;
static gboolean toggle_ref1_strengthened;
static gboolean toggle_ref2_weakened;
static gboolean toggle_ref2_strengthened;
static gboolean toggle_ref3_weakened;
static gboolean toggle_ref3_strengthened;
/*
* TestObject, a parent class for TestObject
*/
static GType test_object_get_type (void);
#define TEST_TYPE_OBJECT (test_object_get_type ())
typedef struct _TestObject TestObject;
typedef struct _TestObjectClass TestObjectClass;
struct _TestObject
{
GObject parent_instance;
};
struct _TestObjectClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
static void
test_object_finalize (GObject *object)
{
object_destroyed = TRUE;
G_OBJECT_CLASS (test_object_parent_class)->finalize (object);
}
static void
test_object_class_init (TestObjectClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = test_object_finalize;
}
static void
test_object_init (TestObject *test_object)
{
}
static void
clear_flags (void)
{
object_destroyed = FALSE;
weak_ref1_notified = FALSE;
weak_ref2_notified = FALSE;
toggle_ref1_weakened = FALSE;
toggle_ref1_strengthened = FALSE;
toggle_ref2_weakened = FALSE;
toggle_ref2_strengthened = FALSE;
toggle_ref3_weakened = FALSE;
toggle_ref3_strengthened = FALSE;
}
static void
weak_ref1 (gpointer data,
GObject *object)
{
g_assert (object == global_object);
g_assert (data == GUINT_TO_POINTER (42));
weak_ref1_notified = TRUE;
}
static void
weak_ref2 (gpointer data,
GObject *object)
{
g_assert (object == global_object);
g_assert (data == GUINT_TO_POINTER (24));
weak_ref2_notified = TRUE;
}
static void
toggle_ref1 (gpointer data,
GObject *object,
gboolean is_last_ref)
{
g_assert (object == global_object);
g_assert (data == GUINT_TO_POINTER (42));
if (is_last_ref)
toggle_ref1_weakened = TRUE;
else
toggle_ref1_strengthened = TRUE;
}
static void
toggle_ref2 (gpointer data,
GObject *object,
gboolean is_last_ref)
{
g_assert (object == global_object);
g_assert (data == GUINT_TO_POINTER (24));
if (is_last_ref)
toggle_ref2_weakened = TRUE;
else
toggle_ref2_strengthened = TRUE;
}
static void
toggle_ref3 (gpointer data,
GObject *object,
gboolean is_last_ref)
{
g_assert (object == global_object);
g_assert (data == GUINT_TO_POINTER (34));
if (is_last_ref)
{
toggle_ref3_weakened = TRUE;
g_object_remove_toggle_ref (object, toggle_ref3, GUINT_TO_POINTER (34));
}
else
toggle_ref3_strengthened = TRUE;
}
int
main (int argc,
char *argv[])
{
GObject *object;
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
G_LOG_LEVEL_WARNING |
G_LOG_LEVEL_CRITICAL);
/* Test basic weak reference operation
*/
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
clear_flags ();
g_object_unref (object);
g_assert (weak_ref1_notified == TRUE);
g_assert (object_destroyed == TRUE);
/* Test two weak references at once
*/
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
g_object_weak_ref (object, weak_ref2, GUINT_TO_POINTER (24));
clear_flags ();
g_object_unref (object);
g_assert (weak_ref1_notified == TRUE);
g_assert (weak_ref2_notified == TRUE);
g_assert (object_destroyed == TRUE);
/* Test remove weak references
*/
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
g_object_weak_ref (object, weak_ref2, GUINT_TO_POINTER (24));
g_object_weak_unref (object, weak_ref1, GUINT_TO_POINTER (42));
clear_flags ();
g_object_unref (object);
g_assert (weak_ref1_notified == FALSE);
g_assert (weak_ref2_notified == TRUE);
g_assert (object_destroyed == TRUE);
/* Test basic toggle reference operation
*/
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
g_object_add_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
clear_flags ();
g_object_unref (object);
g_assert (toggle_ref1_weakened == TRUE);
g_assert (toggle_ref1_strengthened == FALSE);
g_assert (object_destroyed == FALSE);
clear_flags ();
g_object_ref (object);
g_assert (toggle_ref1_weakened == FALSE);
g_assert (toggle_ref1_strengthened == TRUE);
g_assert (object_destroyed == FALSE);
g_object_unref (object);
clear_flags ();
g_object_remove_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
g_assert (toggle_ref1_weakened == FALSE);
g_assert (toggle_ref1_strengthened == FALSE);
g_assert (object_destroyed == TRUE);
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
/* Test two toggle references at once
*/
g_object_add_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
g_object_add_toggle_ref (object, toggle_ref2, GUINT_TO_POINTER (24));
clear_flags ();
g_object_unref (object);
g_assert (toggle_ref1_weakened == FALSE);
g_assert (toggle_ref1_strengthened == FALSE);
g_assert (toggle_ref2_weakened == FALSE);
g_assert (toggle_ref2_strengthened == FALSE);
g_assert (object_destroyed == FALSE);
clear_flags ();
g_object_remove_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
g_assert (toggle_ref1_weakened == FALSE);
g_assert (toggle_ref1_strengthened == FALSE);
g_assert (toggle_ref2_weakened == TRUE);
g_assert (toggle_ref2_strengthened == FALSE);
g_assert (object_destroyed == FALSE);
clear_flags ();
/* Check that removing a toggle ref with %NULL data works fine. */
g_object_remove_toggle_ref (object, toggle_ref2, NULL);
g_assert (toggle_ref1_weakened == FALSE);
g_assert (toggle_ref1_strengthened == FALSE);
g_assert (toggle_ref2_weakened == FALSE);
g_assert (toggle_ref2_strengthened == FALSE);
g_assert (object_destroyed == TRUE);
/* Test a toggle reference that removes itself
*/
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
g_object_add_toggle_ref (object, toggle_ref3, GUINT_TO_POINTER (34));
clear_flags ();
g_object_unref (object);
g_assert (toggle_ref3_weakened == TRUE);
g_assert (toggle_ref3_strengthened == FALSE);
g_assert (object_destroyed == TRUE);
return 0;
}

134
tests/gobject/signals.c Normal file
View file

@ -0,0 +1,134 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2013 Red Hat, Inc.
* Copy and pasted from accumulator.c and modified.
*
* 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/>.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "TestSignals"
#undef G_DISABLE_ASSERT
#undef G_DISABLE_CHECKS
#undef G_DISABLE_CAST_CHECKS
#include <glib-object.h>
#include "testcommon.h"
/* What this test tests is the behavior of signal disconnection
* from within a signal handler for the signal being disconnected.
*
* The test demonstrates that signal handlers disconnected from a signal
* from an earlier handler in the same emission will not be run.
*
* It also demonstrates that signal handlers connected from a signal
* from an earlier handler in the same emission will not be run.
*/
/*
* TestObject, a parent class for TestObject
*/
#define TEST_TYPE_OBJECT (test_object_get_type ())
typedef struct _TestObject TestObject;
typedef struct _TestObjectClass TestObjectClass;
static gboolean callback1_ran = FALSE, callback2_ran = FALSE, callback3_ran = FALSE, default_handler_ran = FALSE;
struct _TestObject
{
GObject parent_instance;
};
struct _TestObjectClass
{
GObjectClass parent_class;
void (*test_signal) (TestObject *object);
};
static GType test_object_get_type (void);
static void
test_object_real_signal (TestObject *object)
{
default_handler_ran = TRUE;
}
static void
test_object_signal_callback3 (TestObject *object,
gpointer data)
{
callback3_ran = TRUE;
}
static void
test_object_signal_callback2 (TestObject *object,
gpointer data)
{
callback2_ran = TRUE;
}
static void
test_object_signal_callback1 (TestObject *object,
gpointer data)
{
callback1_ran = TRUE;
g_signal_handlers_disconnect_by_func (G_OBJECT (object),
test_object_signal_callback2,
data);
g_signal_connect (object, "test-signal",
G_CALLBACK (test_object_signal_callback3), NULL);
}
static void
test_object_class_init (TestObjectClass *class)
{
class->test_signal = test_object_real_signal;
g_signal_new ("test-signal",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (TestObjectClass, test_signal),
NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static DEFINE_TYPE(TestObject, test_object,
test_object_class_init, NULL, NULL,
G_TYPE_OBJECT)
int
main (int argc,
char *argv[])
{
TestObject *object;
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
G_LOG_LEVEL_WARNING |
G_LOG_LEVEL_CRITICAL);
object = g_object_new (TEST_TYPE_OBJECT, NULL);
g_signal_connect (object, "test-signal",
G_CALLBACK (test_object_signal_callback1), NULL);
g_signal_connect (object, "test-signal",
G_CALLBACK (test_object_signal_callback2), NULL);
g_signal_emit_by_name (object, "test-signal");
g_assert (callback1_ran);
g_assert (!callback2_ran);
g_assert (!callback3_ran);
g_assert (default_handler_ran);
g_object_unref (object);
return 0;
}

84
tests/gobject/singleton.c Normal file
View file

@ -0,0 +1,84 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2006 Imendio AB
*
* 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/>.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "TestSingleton"
#include <glib-object.h>
#include <string.h>
/* --- MySingleton class --- */
typedef struct {
GObject parent_instance;
} MySingleton;
typedef struct {
GObjectClass parent_class;
} MySingletonClass;
static GType my_singleton_get_type (void);
#define MY_TYPE_SINGLETON (my_singleton_get_type ())
#define MY_SINGLETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MY_TYPE_SINGLETON, MySingleton))
#define MY_IS_SINGLETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_SINGLETON))
#define MY_SINGLETON_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), MY_TYPE_SINGLETON, MySingletonClass))
#define MY_IS_SINGLETON_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), MY_TYPE_SINGLETON))
#define MY_SINGLETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MY_TYPE_SINGLETON, MySingletonClass))
G_DEFINE_TYPE (MySingleton, my_singleton, G_TYPE_OBJECT)
static MySingleton *the_one_and_only = NULL;
/* --- methods --- */
static GObject*
my_singleton_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
if (the_one_and_only)
return g_object_ref (G_OBJECT (the_one_and_only));
else
return G_OBJECT_CLASS (my_singleton_parent_class)->constructor (type, n_construct_properties, construct_properties);
}
static void
my_singleton_init (MySingleton *self)
{
g_assert (the_one_and_only == NULL);
the_one_and_only = self;
}
static void
my_singleton_class_init (MySingletonClass *klass)
{
G_OBJECT_CLASS (klass)->constructor = my_singleton_constructor;
}
/* --- test program --- */
int
main (int argc,
char *argv[])
{
MySingleton *singleton, *obj;
/* create the singleton */
singleton = g_object_new (MY_TYPE_SINGLETON, NULL);
g_assert (singleton != NULL);
/* assert _singleton_ creation */
obj = g_object_new (MY_TYPE_SINGLETON, NULL);
g_assert (singleton == obj);
g_object_unref (obj);
/* shutdown */
g_object_unref (singleton);
return 0;
}

105
tests/gobject/testcommon.h Normal file
View file

@ -0,0 +1,105 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2003 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/>.
*/
#ifndef __TEST_COMMON_H__
#define __TEST_COMMON_H__
G_BEGIN_DECLS
#define DEFINE_TYPE_FULL(name, prefix, \
class_init, base_init, instance_init, \
parent_type, interface_decl) \
GType \
prefix ## _get_type (void) \
{ \
static GType object_type = 0; \
\
if (!object_type) \
{ \
const GTypeInfo object_info = \
{ \
sizeof (name ## Class), \
(GBaseInitFunc) base_init, \
(GBaseFinalizeFunc) NULL, \
(GClassInitFunc) class_init, \
(GClassFinalizeFunc) NULL, \
NULL, /* class_data */ \
sizeof (name), \
0, /* n_prelocs */ \
(GInstanceInitFunc) instance_init, \
(const GTypeValueTable *) NULL, \
}; \
\
object_type = g_type_register_static (parent_type, \
# name, \
&object_info, 0); \
interface_decl \
} \
\
return object_type; \
}
#define DEFINE_TYPE(name, prefix, \
class_init, base_init, instance_init, \
parent_type) \
DEFINE_TYPE_FULL(name, prefix, class_init, base_init, \
instance_init, parent_type, {})
#define DEFINE_IFACE(name, prefix, base_init, dflt_init) \
GType \
prefix ## _get_type (void) \
{ \
static GType iface_type = 0; \
\
if (!iface_type) \
{ \
const GTypeInfo iface_info = \
{ \
sizeof (name ## Class), \
(GBaseInitFunc) base_init, \
(GBaseFinalizeFunc) NULL, \
(GClassInitFunc) dflt_init, \
(GClassFinalizeFunc) NULL, \
(gconstpointer) NULL, \
(guint16) 0, \
(guint16) 0, \
(GInstanceInitFunc) NULL, \
(const GTypeValueTable*) NULL, \
}; \
\
iface_type = g_type_register_static (G_TYPE_INTERFACE, \
# name, \
&iface_info, 0); \
} \
return iface_type; \
}
#define INTERFACE_FULL(type, init_func, iface_type) \
{ \
GInterfaceInfo const iface = \
{ \
(GInterfaceInitFunc) init_func, NULL, NULL \
}; \
\
g_type_add_interface_static (type, iface_type, &iface); \
}
#define INTERFACE(init_func, iface_type) \
INTERFACE_FULL(object_type, init_func, iface_type)
G_END_DECLS
#endif /* __TEST_COMMON_H__ */

445
tests/gobject/testgobject.c Normal file
View file

@ -0,0 +1,445 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001 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/>.
*/
#include <string.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "TestObject"
#include <glib-object.h>
/* --- TestIface --- */
#define TEST_TYPE_IFACE (test_iface_get_type ())
#define TEST_IFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_IFACE, TestIface))
#define TEST_IS_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_IFACE))
#define TEST_IFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE, TestIfaceClass))
typedef struct _TestIface TestIface;
typedef struct _TestIfaceClass TestIfaceClass;
struct _TestIfaceClass
{
GTypeInterface base_iface;
void (*print_string) (TestIface *tiobj,
const gchar *string);
};
static void iface_base_init (TestIfaceClass *iface);
static void iface_base_finalize (TestIfaceClass *iface);
static void print_foo (TestIface *tiobj,
const gchar *string);
static GType
test_iface_get_type (void)
{
static GType test_iface_type = 0;
if (!test_iface_type)
{
const GTypeInfo test_iface_info =
{
sizeof (TestIfaceClass),
(GBaseInitFunc) iface_base_init, /* base_init */
(GBaseFinalizeFunc) iface_base_finalize, /* base_finalize */
NULL,
NULL,
NULL,
0,
0,
NULL,
NULL
};
test_iface_type = g_type_register_static (G_TYPE_INTERFACE, "TestIface", &test_iface_info, 0);
g_type_interface_add_prerequisite (test_iface_type, G_TYPE_OBJECT);
}
return test_iface_type;
}
static guint iface_base_init_count = 0;
static void
iface_base_init (TestIfaceClass *iface)
{
iface_base_init_count++;
if (iface_base_init_count == 1)
{
/* add signals here */
}
}
static void
iface_base_finalize (TestIfaceClass *iface)
{
iface_base_init_count--;
if (iface_base_init_count == 0)
{
/* destroy signals here */
}
}
static void
print_foo (TestIface *tiobj,
const gchar *string)
{
if (!string)
string = "<NULL>";
g_print ("Iface-FOO: \"%s\" from %p\n", string, tiobj);
}
static void
test_object_test_iface_init (gpointer giface,
gpointer iface_data)
{
TestIfaceClass *iface = giface;
g_assert (iface_data == GUINT_TO_POINTER (42));
g_assert (G_TYPE_FROM_INTERFACE (iface) == TEST_TYPE_IFACE);
/* assert iface_base_init() was already called */
g_assert (iface_base_init_count > 0);
/* initialize stuff */
iface->print_string = print_foo;
}
static void
iface_print_string (TestIface *tiobj,
const gchar *string)
{
TestIfaceClass *iface;
g_return_if_fail (TEST_IS_IFACE (tiobj));
g_return_if_fail (G_IS_OBJECT (tiobj)); /* ensured through prerequisite */
iface = TEST_IFACE_GET_CLASS (tiobj);
g_object_ref (tiobj);
iface->print_string (tiobj, string);
g_object_unref (tiobj);
}
/* --- TestObject --- */
#define TEST_TYPE_OBJECT (test_object_get_type ())
#define TEST_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TEST_TYPE_OBJECT, TestObject))
#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
#define TEST_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TEST_TYPE_OBJECT))
#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
#define TEST_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_OBJECT, TestObjectPrivate))
typedef struct _TestObject TestObject;
typedef struct _TestObjectClass TestObjectClass;
typedef struct _TestObjectPrivate TestObjectPrivate;
struct _TestObject
{
GObject parent_instance;
};
struct _TestObjectClass
{
GObjectClass parent_class;
gchar* (*test_signal) (TestObject *tobject,
TestIface *iface_object,
gpointer tdata);
};
struct _TestObjectPrivate
{
int dummy1;
gdouble dummy2;
};
static void test_object_class_init (TestObjectClass *class);
static void test_object_init (TestObject *tobject);
static gboolean test_signal_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data);
static gchar* test_object_test_signal (TestObject *tobject,
TestIface *iface_object,
gpointer tdata);
static gint TestObject_private_offset;
static inline gpointer
test_object_get_instance_private (TestObject *self)
{
return (G_STRUCT_MEMBER_P (self, TestObject_private_offset));
}
static GType
test_object_get_type (void)
{
static GType test_object_type = 0;
if (!test_object_type)
{
const GTypeInfo test_object_info =
{
sizeof (TestObjectClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) test_object_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (TestObject),
5, /* n_preallocs */
(GInstanceInitFunc) test_object_init,
NULL
};
GInterfaceInfo iface_info = { test_object_test_iface_init, NULL, GUINT_TO_POINTER (42) };
test_object_type = g_type_register_static (G_TYPE_OBJECT, "TestObject", &test_object_info, 0);
g_type_add_interface_static (test_object_type, TEST_TYPE_IFACE, &iface_info);
TestObject_private_offset =
g_type_add_instance_private (test_object_type, sizeof (TestObjectPrivate));
}
return test_object_type;
}
static void
test_object_class_init (TestObjectClass *class)
{
/* GObjectClass *gobject_class = G_OBJECT_CLASS (class); */
g_type_class_adjust_private_offset (class, &TestObject_private_offset);
class->test_signal = test_object_test_signal;
g_signal_new ("test-signal",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP,
G_STRUCT_OFFSET (TestObjectClass, test_signal),
test_signal_accumulator, NULL,
g_cclosure_marshal_STRING__OBJECT_POINTER,
G_TYPE_STRING, 2, TEST_TYPE_IFACE, G_TYPE_POINTER);
}
static void
test_object_init (TestObject *tobject)
{
TestObjectPrivate *priv = test_object_get_instance_private (tobject);
g_assert (priv);
priv->dummy1 = 54321;
}
/* Check to see if private data initialization in the
* instance init function works.
*/
static void
test_object_check_private_init (TestObject *tobject)
{
TestObjectPrivate *priv = test_object_get_instance_private (tobject);
g_print ("private data during initialization: %u == %u\n", priv->dummy1, 54321);
g_assert (priv->dummy1 == 54321);
}
static gboolean
test_signal_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data)
{
const gchar *accu_string = g_value_get_string (return_accu);
const gchar *new_string = g_value_get_string (handler_return);
gchar *result_string;
if (accu_string)
result_string = g_strconcat (accu_string, new_string, NULL);
else if (new_string)
result_string = g_strdup (new_string);
else
result_string = NULL;
g_value_take_string (return_accu, result_string);
return TRUE;
}
static gchar*
test_object_test_signal (TestObject *tobject,
TestIface *iface_object,
gpointer tdata)
{
g_message ("::test_signal default_handler called");
g_return_val_if_fail (TEST_IS_IFACE (iface_object), NULL);
return g_strdup ("<default_handler>");
}
/* --- TestIface for DerivedObject --- */
static void
print_bar (TestIface *tiobj,
const gchar *string)
{
TestIfaceClass *parent_iface;
g_return_if_fail (TEST_IS_IFACE (tiobj));
if (!string)
string = "<NULL>";
g_print ("Iface-BAR: \"%s\" from %p\n", string, tiobj);
g_print ("chaining: ");
parent_iface = g_type_interface_peek_parent (TEST_IFACE_GET_CLASS (tiobj));
parent_iface->print_string (tiobj, string);
g_assert (g_type_interface_peek_parent (parent_iface) == NULL);
}
static void
derived_object_test_iface_init (gpointer giface,
gpointer iface_data)
{
TestIfaceClass *iface = giface;
g_assert (iface_data == GUINT_TO_POINTER (87));
g_assert (G_TYPE_FROM_INTERFACE (iface) == TEST_TYPE_IFACE);
/* assert test_object_test_iface_init() was already called */
g_assert (iface->print_string == print_foo);
/* override stuff */
iface->print_string = print_bar;
}
/* --- DerivedObject --- */
#define DERIVED_TYPE_OBJECT (derived_object_get_type ())
#define DERIVED_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), DERIVED_TYPE_OBJECT, DerivedObject))
#define DERIVED_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DERIVED_TYPE_OBJECT, DerivedObjectClass))
#define DERIVED_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), DERIVED_TYPE_OBJECT))
#define DERIVED_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DERIVED_TYPE_OBJECT))
#define DERIVED_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DERIVED_TYPE_OBJECT, DerivedObjectClass))
#define DERIVED_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DERIVED_TYPE_OBJECT, DerivedObjectPrivate))
typedef struct _DerivedObject DerivedObject;
typedef struct _TestObjectClass DerivedObjectClass;
typedef struct _DerivedObjectPrivate DerivedObjectPrivate;
struct _DerivedObject
{
TestObject parent_instance;
int dummy1;
int dummy2;
};
struct _DerivedObjectPrivate
{
char dummy;
};
static void derived_object_class_init (DerivedObjectClass *class);
static void derived_object_init (DerivedObject *dobject);
static gint DerivedObject_private_offset;
static inline gpointer
derived_object_get_instance_private (DerivedObject *self)
{
return (G_STRUCT_MEMBER_P (self, DerivedObject_private_offset));
}
static GType
derived_object_get_type (void)
{
static GType derived_object_type = 0;
if (!derived_object_type)
{
const GTypeInfo derived_object_info =
{
sizeof (DerivedObjectClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) derived_object_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (DerivedObject),
5, /* n_preallocs */
(GInstanceInitFunc) derived_object_init,
NULL
};
GInterfaceInfo iface_info = { derived_object_test_iface_init, NULL, GUINT_TO_POINTER (87) };
derived_object_type = g_type_register_static (TEST_TYPE_OBJECT, "DerivedObject", &derived_object_info, 0);
g_type_add_interface_static (derived_object_type, TEST_TYPE_IFACE, &iface_info);
DerivedObject_private_offset =
g_type_add_instance_private (derived_object_type, sizeof (DerivedObjectPrivate));
}
return derived_object_type;
}
static void
derived_object_class_init (DerivedObjectClass *class)
{
g_type_class_adjust_private_offset (class, &DerivedObject_private_offset);
}
static void
derived_object_init (DerivedObject *dobject)
{
TestObjectPrivate *test_priv;
DerivedObjectPrivate *derived_priv;
derived_priv = derived_object_get_instance_private (dobject);
g_assert (derived_priv);
test_priv = test_object_get_instance_private (TEST_OBJECT (dobject));
g_assert (test_priv);
}
/* --- main --- */
int
main (int argc,
char *argv[])
{
GTypeInfo info = { 0, };
GTypeFundamentalInfo finfo = { 0, };
GType type;
TestObject *sigarg;
DerivedObject *dobject;
TestObjectPrivate *priv;
gchar *string = NULL;
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
G_LOG_LEVEL_WARNING |
G_LOG_LEVEL_CRITICAL);
/* test new fundamentals */
g_assert (G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST) == g_type_fundamental_next ());
type = g_type_register_fundamental (g_type_fundamental_next (), "FooShadow1", &info, &finfo, 0);
g_assert (type == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST));
g_assert (G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 1) == g_type_fundamental_next ());
type = g_type_register_fundamental (g_type_fundamental_next (), "FooShadow2", &info, &finfo, 0);
g_assert (type == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 1));
g_assert (G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 2) == g_type_fundamental_next ());
g_assert (g_type_from_name ("FooShadow1") == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST));
g_assert (g_type_from_name ("FooShadow2") == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 1));
/* to test past class initialization interface setups, create the class here */
g_type_class_ref (TEST_TYPE_OBJECT);
dobject = g_object_new (DERIVED_TYPE_OBJECT, NULL);
test_object_check_private_init (TEST_OBJECT (dobject));
sigarg = g_object_new (TEST_TYPE_OBJECT, NULL);
g_print ("MAIN: emit test-signal:\n");
g_signal_emit_by_name (dobject, "test-signal", sigarg, NULL, &string);
g_message ("signal return: \"%s\"", string);
g_assert_cmpstr (string, ==, "<default_handler><default_handler><default_handler>");
g_free (string);
g_print ("MAIN: call iface print-string on test and derived object:\n");
iface_print_string (TEST_IFACE (sigarg), "iface-string-from-test-type");
iface_print_string (TEST_IFACE (dobject), "iface-string-from-derived-type");
priv = test_object_get_instance_private (TEST_OBJECT (dobject));
g_print ("private data after initialization: %u == %u\n", priv->dummy1, 54321);
g_assert (priv->dummy1 == 54321);
g_object_unref (sigarg);
g_object_unref (dobject);
g_message ("%s done", argv[0]);
return 0;
}

View file

@ -0,0 +1,4 @@
# Marshallers used in tests
BOOLEAN:INT
STRING:INT
VARIANT:POINTER

View file

@ -0,0 +1,66 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* testmodule.c: Dummy dynamic type module
* Copyright (C) 2003 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/>.
*/
#include "testmodule.h"
#include "testcommon.h"
static gboolean test_module_load (GTypeModule *module);
static void test_module_unload (GTypeModule *module);
static void
test_module_class_init (TestModuleClass *class)
{
GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
module_class->load = test_module_load;
module_class->unload = test_module_unload;
}
DEFINE_TYPE (TestModule, test_module,
test_module_class_init, NULL, NULL,
G_TYPE_TYPE_MODULE)
static gboolean
test_module_load (GTypeModule *module)
{
TestModule *test_module = TEST_MODULE (module);
test_module->register_func (module);
return TRUE;
}
static void
test_module_unload (GTypeModule *module)
{
}
GTypeModule *
test_module_new (TestModuleRegisterFunc register_func)
{
TestModule *test_module = g_object_new (TEST_TYPE_MODULE, NULL);
GTypeModule *module = G_TYPE_MODULE (test_module);
test_module->register_func = register_func;
/* Register the types initially */
g_type_module_use (module);
g_type_module_unuse (module);
return G_TYPE_MODULE (module);
}

View file

@ -0,0 +1,55 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* testmodule.h: Dummy dynamic type module
* Copyright (C) 2003 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/>.
*/
#ifndef __TEST_MODULE_H__
#define __TEST_MODULE_H__
#include <glib-object.h>
G_BEGIN_DECLS
typedef struct _TestModule TestModule;
typedef struct _TestModuleClass TestModuleClass;
#define TEST_TYPE_MODULE (test_module_get_type ())
#define TEST_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), TEST_TYPE_MODULE, TestModule))
#define TEST_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), TEST_TYPE_MODULE, TestModuleClass))
#define TEST_IS_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE ((module), TEST_TYPE_MODULE))
#define TEST_IS_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), TEST_TYPE_MODULE))
#define TEST_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS ((module), TEST_TYPE_MODULE, TestModuleClass))
typedef void (*TestModuleRegisterFunc) (GTypeModule *module);
struct _TestModule
{
GTypeModule parent_instance;
TestModuleRegisterFunc register_func;
};
struct _TestModuleClass
{
GTypeModuleClass parent_class;
};
GType test_module_get_type (void);
GTypeModule *test_module_new (TestModuleRegisterFunc register_func);
G_END_DECLS
#endif /* __TEST_MODULE_H__ */

View file

@ -0,0 +1,228 @@
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <glib.h>
#include <glib-object.h>
static int n_children = 3;
static int n_active_children;
static int n_iters = 10000;
static GMainLoop *loop;
static void
io_pipe (GIOChannel **channels)
{
int fds[2];
if (pipe(fds) < 0)
{
int errsv = errno;
fprintf (stderr, "Cannot create pipe %s\n", g_strerror (errsv));
exit (1);
}
channels[0] = g_io_channel_unix_new (fds[0]);
channels[1] = g_io_channel_unix_new (fds[1]);
}
static gboolean
read_all (GIOChannel *channel, char *buf, gsize len)
{
gsize bytes_read = 0;
gsize count;
GIOError err;
while (bytes_read < len)
{
err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
if (err)
{
if (err != G_IO_ERROR_AGAIN)
return FALSE;
}
else if (count == 0)
return FALSE;
bytes_read += count;
}
return TRUE;
}
static gboolean
write_all (GIOChannel *channel, char *buf, gsize len)
{
gsize bytes_written = 0;
gsize count;
GIOError err;
while (bytes_written < len)
{
err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
if (err && err != G_IO_ERROR_AGAIN)
return FALSE;
bytes_written += count;
}
return TRUE;
}
static void
run_child (GIOChannel *in_channel, GIOChannel *out_channel)
{
int i;
int val = 1;
GTimer *timer = g_timer_new();
for (i = 0; i < n_iters; i++)
{
write_all (out_channel, (char *)&val, sizeof (val));
read_all (in_channel, (char *)&val, sizeof (val));
}
val = 0;
write_all (out_channel, (char *)&val, sizeof (val));
val = g_timer_elapsed (timer, NULL) * 1000;
write_all (out_channel, (char *)&val, sizeof (val));
g_timer_destroy (timer);
exit (0);
}
static gboolean
input_callback (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
int val;
GIOChannel *dest = (GIOChannel *)data;
if (!read_all (source, (char *)&val, sizeof(val)))
{
fprintf (stderr, "Unexpected EOF\n");
exit (1);
}
if (val)
{
write_all (dest, (char *)&val, sizeof(val));
return TRUE;
}
else
{
g_io_channel_close (source);
g_io_channel_close (dest);
n_active_children--;
if (n_active_children == 0)
g_main_loop_quit (loop);
return FALSE;
}
}
static void
create_child (void)
{
int pid, errsv;
GIOChannel *in_channels[2];
GIOChannel *out_channels[2];
GSource *source;
io_pipe (in_channels);
io_pipe (out_channels);
pid = fork ();
errsv = errno;
if (pid > 0) /* Parent */
{
g_io_channel_close (in_channels[0]);
g_io_channel_close (out_channels[1]);
source = g_io_create_watch (out_channels[0], G_IO_IN | G_IO_HUP);
g_source_set_closure (source,
g_cclosure_new (G_CALLBACK (input_callback), in_channels[1],
(GClosureNotify)g_io_channel_unref));
g_source_attach (source, NULL);
g_source_unref (source);
g_io_channel_unref (in_channels[0]);
g_io_channel_unref (out_channels[0]);
g_io_channel_unref (out_channels[1]);
}
else if (pid == 0) /* Child */
{
g_io_channel_close (in_channels[1]);
g_io_channel_close (out_channels[0]);
setsid ();
run_child (in_channels[0], out_channels[1]);
}
else /* Error */
{
fprintf (stderr, "Cannot fork: %s\n", g_strerror (errsv));
exit (1);
}
}
static double
difftimeval (struct timeval *old, struct timeval *new)
{
return
(new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
}
int
main (int argc, char **argv)
{
int i;
struct rusage old_usage;
struct rusage new_usage;
if (argc > 1)
n_children = atoi(argv[1]);
if (argc > 2)
n_iters = atoi(argv[2]);
printf ("Children: %d Iters: %d\n", n_children, n_iters);
n_active_children = n_children;
for (i = 0; i < n_children; i++)
create_child ();
getrusage (RUSAGE_SELF, &old_usage);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
getrusage (RUSAGE_SELF, &new_usage);
printf ("Elapsed user: %g\n",
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
printf ("Elapsed system: %g\n",
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
printf ("Elapsed total: %g\n",
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
printf ("total / iteration: %g\n",
(difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
(n_iters * n_children));
g_main_loop_unref (loop);
return 0;
}

View file

@ -0,0 +1,75 @@
/* libgplugin_a.c - test plugin for testgmodule
* Copyright (C) 1998 Tim Janik
*
* 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/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <gmodule.h>
#include <stdlib.h>
G_MODULE_EXPORT void gplugin_a_func (void);
G_MODULE_EXPORT void gplugin_clash_func (void);
G_MODULE_EXPORT void g_clash_func (void);
G_MODULE_EXPORT void gplugin_say_boo_func (void);
G_MODULE_EXPORT void gplugin_a_module_func (GModule *module);
G_MODULE_EXPORT gchar* gplugin_a_state;
G_MODULE_EXPORT void
gplugin_a_func (void)
{
gplugin_a_state = "Hello world";
}
G_MODULE_EXPORT void
gplugin_clash_func (void)
{
gplugin_a_state = "plugin clash";
}
G_MODULE_EXPORT void
g_clash_func (void)
{
gplugin_a_state = "global clash";
}
G_MODULE_EXPORT void
gplugin_say_boo_func (void)
{
gplugin_a_state = "BOOH";
}
G_MODULE_EXPORT void
gplugin_a_module_func (GModule *module)
{
void *f = NULL;
if (!g_module_symbol (module, "gplugin_say_boo_func", &f ))
{
g_print ("error: %s\n", g_module_error ());
exit (1);
}
((void(*)(void)) f) ();
}

View file

@ -0,0 +1,76 @@
/* libgplugin_b.c - test plugin for testgmodule
* Copyright (C) 1998 Tim Janik
*
* 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/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <gmodule.h>
G_MODULE_EXPORT gchar* gplugin_b_state;
G_MODULE_EXPORT const gchar* g_module_check_init (GModule *module);
G_MODULE_EXPORT void g_module_unload (GModule *module);
G_MODULE_EXPORT void gplugin_b_func (void);
G_MODULE_EXPORT void gplugin_clash_func (void);
G_MODULE_EXPORT void g_clash_func (void);
G_MODULE_EXPORT void gplugin_say_boo_func (void);
G_MODULE_EXPORT const gchar*
g_module_check_init (GModule *module)
{
gplugin_b_state = "check-init";
return NULL;
}
G_MODULE_EXPORT void
g_module_unload (GModule *module)
{
gplugin_b_state = "unloaded";
}
G_MODULE_EXPORT void
gplugin_b_func (void)
{
gplugin_b_state = "Hello world";
}
G_MODULE_EXPORT void
gplugin_clash_func (void)
{
gplugin_b_state = "plugin clash";
}
G_MODULE_EXPORT void
g_clash_func (void)
{
gplugin_b_state = "global clash";
}
G_MODULE_EXPORT void
gplugin_say_boo_func (void)
{
gplugin_b_state = "BOOH";
}

442
tests/mainloop-test.c Normal file
View file

@ -0,0 +1,442 @@
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <errno.h>
#include <glib.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef G_OS_WIN32
#include <fcntl.h> /* For _O_BINARY used by pipe() macro */
#include <io.h> /* for _pipe() */
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
#endif
#define ITERS 10000
#define INCREMENT 10
#define NTHREADS 4
#define NCRAWLERS 4
#define CRAWLER_TIMEOUT_RANGE 40
#define RECURSER_TIMEOUT 50
/* The partial ordering between the context array mutex and
* crawler array mutex is that the crawler array mutex cannot
* be locked while the context array mutex is locked
*/
GPtrArray *context_array;
GMutex context_array_mutex;
GCond context_array_cond;
GMainLoop *main_loop;
G_LOCK_DEFINE_STATIC (crawler_array_lock);
GPtrArray *crawler_array;
typedef struct _AddrData AddrData;
typedef struct _TestData TestData;
struct _AddrData
{
GMainLoop *loop;
GIOChannel *dest;
gint count;
};
struct _TestData
{
gint current_val;
gint iters;
GIOChannel *in;
};
static void cleanup_crawlers (GMainContext *context);
static gboolean
read_all (GIOChannel *channel, char *buf, gsize len)
{
gsize bytes_read = 0;
gsize count;
GIOError err;
while (bytes_read < len)
{
err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
if (err)
{
if (err != G_IO_ERROR_AGAIN)
return FALSE;
}
else if (count == 0)
return FALSE;
bytes_read += count;
}
return TRUE;
}
static gboolean
write_all (GIOChannel *channel, char *buf, gsize len)
{
gsize bytes_written = 0;
gsize count;
GIOError err;
while (bytes_written < len)
{
err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
if (err && err != G_IO_ERROR_AGAIN)
return FALSE;
bytes_written += count;
}
return TRUE;
}
static gboolean
adder_callback (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
char buf1[32];
char buf2[32];
char result[32] = { 0, };
AddrData *addr_data = data;
if (!read_all (source, buf1, 32) ||
!read_all (source, buf2, 32))
{
g_main_loop_quit (addr_data->loop);
return FALSE;
}
sprintf (result, "%d", atoi(buf1) + atoi(buf2));
write_all (addr_data->dest, result, 32);
return TRUE;
}
static gboolean
timeout_callback (gpointer data)
{
AddrData *addr_data = data;
addr_data->count++;
return TRUE;
}
static gpointer
adder_thread (gpointer data)
{
GMainContext *context;
GSource *adder_source;
GSource *timeout_source;
GIOChannel **channels = data;
AddrData addr_data;
context = g_main_context_new ();
g_mutex_lock (&context_array_mutex);
g_ptr_array_add (context_array, context);
if (context_array->len == NTHREADS)
g_cond_broadcast (&context_array_cond);
g_mutex_unlock (&context_array_mutex);
addr_data.dest = channels[1];
addr_data.loop = g_main_loop_new (context, FALSE);
addr_data.count = 0;
adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
g_source_set_static_name (adder_source, "Adder I/O");
g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
g_source_attach (adder_source, context);
g_source_unref (adder_source);
timeout_source = g_timeout_source_new (10);
g_source_set_static_name (timeout_source, "Adder timeout");
g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
g_source_attach (timeout_source, context);
g_source_unref (timeout_source);
g_main_loop_run (addr_data.loop);
g_io_channel_unref (channels[0]);
g_io_channel_unref (channels[1]);
g_free (channels);
g_main_loop_unref (addr_data.loop);
#ifdef VERBOSE
g_print ("Timeout run %d times\n", addr_data.count);
#endif
g_mutex_lock (&context_array_mutex);
g_ptr_array_remove (context_array, context);
if (context_array->len == 0)
g_main_loop_quit (main_loop);
g_mutex_unlock (&context_array_mutex);
cleanup_crawlers (context);
g_main_context_unref (context);
return NULL;
}
static void
io_pipe (GIOChannel **channels)
{
gint fds[2];
if (pipe(fds) < 0)
{
int errsv = errno;
g_warning ("Cannot create pipe %s", g_strerror (errsv));
exit (1);
}
channels[0] = g_io_channel_unix_new (fds[0]);
channels[1] = g_io_channel_unix_new (fds[1]);
g_io_channel_set_close_on_unref (channels[0], TRUE);
g_io_channel_set_close_on_unref (channels[1], TRUE);
}
static void
do_add (GIOChannel *in, gint a, gint b)
{
char buf1[32] = { 0, };
char buf2[32] = { 0, };
sprintf (buf1, "%d", a);
sprintf (buf2, "%d", b);
write_all (in, buf1, 32);
write_all (in, buf2, 32);
}
static gboolean
adder_response (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
char result[32];
TestData *test_data = data;
if (!read_all (source, result, 32))
return FALSE;
test_data->current_val = atoi (result);
test_data->iters--;
if (test_data->iters == 0)
{
if (test_data->current_val != ITERS * INCREMENT)
{
g_print ("Addition failed: %d != %d\n",
test_data->current_val, ITERS * INCREMENT);
exit (1);
}
g_io_channel_unref (source);
g_io_channel_unref (test_data->in);
g_free (test_data);
return FALSE;
}
do_add (test_data->in, test_data->current_val, INCREMENT);
return TRUE;
}
static GThread *
create_adder_thread (void)
{
GThread *thread;
TestData *test_data;
GIOChannel *in_channels[2];
GIOChannel *out_channels[2];
GIOChannel **sub_channels;
sub_channels = g_new (GIOChannel *, 2);
io_pipe (in_channels);
io_pipe (out_channels);
sub_channels[0] = in_channels[0];
sub_channels[1] = out_channels[1];
thread = g_thread_new ("adder", adder_thread, sub_channels);
test_data = g_new (TestData, 1);
test_data->in = in_channels[1];
test_data->current_val = 0;
test_data->iters = ITERS;
g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
adder_response, test_data);
do_add (test_data->in, test_data->current_val, INCREMENT);
return thread;
}
static void create_crawler (void);
static void
remove_crawler (void)
{
GSource *other_source;
if (crawler_array->len > 0)
{
other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)];
g_source_destroy (other_source);
g_assert (g_ptr_array_remove_fast (crawler_array, other_source));
}
}
static gint
crawler_callback (gpointer data)
{
GSource *source = data;
G_LOCK (crawler_array_lock);
if (!g_ptr_array_remove_fast (crawler_array, source))
remove_crawler();
remove_crawler();
G_UNLOCK (crawler_array_lock);
create_crawler();
create_crawler();
return FALSE;
}
static void
create_crawler (void)
{
GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
g_source_set_static_name (source, "Crawler timeout");
g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);
G_LOCK (crawler_array_lock);
g_ptr_array_add (crawler_array, source);
g_mutex_lock (&context_array_mutex);
g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
g_source_unref (source);
g_mutex_unlock (&context_array_mutex);
G_UNLOCK (crawler_array_lock);
}
static void
cleanup_crawlers (GMainContext *context)
{
guint i;
G_LOCK (crawler_array_lock);
for (i = 0; i < crawler_array->len; i++)
{
if (g_source_get_context (crawler_array->pdata[i]) == context)
{
g_source_destroy (g_ptr_array_remove_index (crawler_array, i));
i--;
}
}
G_UNLOCK (crawler_array_lock);
}
static gboolean
recurser_idle (gpointer data)
{
GMainContext *context = data;
gint i;
for (i = 0; i < 10; i++)
g_main_context_iteration (context, FALSE);
return FALSE;
}
static gboolean
recurser_start (gpointer data)
{
GMainContext *context;
GSource *source;
g_mutex_lock (&context_array_mutex);
if (context_array->len > 0)
{
context = context_array->pdata[g_random_int_range (0, context_array->len)];
source = g_idle_source_new ();
g_source_set_static_name (source, "Recursing idle source");
g_source_set_callback (source, recurser_idle, context, NULL);
g_source_attach (source, context);
g_source_unref (source);
}
g_mutex_unlock (&context_array_mutex);
return TRUE;
}
int
main (int argc,
char *argv[])
{
gint i;
GThread *threads[NTHREADS];
context_array = g_ptr_array_new ();
crawler_array = g_ptr_array_new ();
main_loop = g_main_loop_new (NULL, FALSE);
for (i = 0; i < NTHREADS; i++)
threads[i] = create_adder_thread ();
/* Wait for all threads to start
*/
g_mutex_lock (&context_array_mutex);
while (context_array->len < NTHREADS)
g_cond_wait (&context_array_cond, &context_array_mutex);
g_mutex_unlock (&context_array_mutex);
for (i = 0; i < NCRAWLERS; i++)
create_crawler ();
g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL);
g_main_loop_run (main_loop);
g_main_loop_unref (main_loop);
for (i = 0; i < NTHREADS; i++)
g_thread_join (threads[i]);
g_ptr_array_unref (crawler_array);
g_ptr_array_unref (context_array);
return 0;
}

321
tests/mapping-test.c Normal file
View file

@ -0,0 +1,321 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 2005 Matthias Clasen
*
* 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/>.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include "glib.h"
#include "gstdio.h"
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#ifdef G_OS_WIN32
#include <process.h>
#endif
static gchar *dir, *global_filename, *global_displayname, *childname;
static gboolean stop = FALSE;
static gint parent_pid;
#ifndef G_OS_WIN32
static void
handle_usr1 (int signum)
{
stop = TRUE;
}
#endif
static gboolean
check_stop (gpointer data)
{
GMainLoop *loop = data;
#ifdef G_OS_WIN32
stop = g_file_test ("STOP", G_FILE_TEST_EXISTS);
#endif
if (stop)
g_main_loop_quit (loop);
return TRUE;
}
static void
write_or_die (const gchar *filename,
const gchar *contents,
gssize length)
{
GError *error = NULL;
gchar *displayname;
if (!g_file_set_contents (filename, contents, length, &error))
{
displayname = g_filename_display_name (childname);
g_print ("failed to write '%s': %s\n",
displayname, error->message);
exit (1);
}
}
static GMappedFile *
map_or_die (const gchar *filename,
gboolean writable)
{
GError *error = NULL;
GMappedFile *map;
gchar *displayname;
map = g_mapped_file_new (filename, writable, &error);
if (!map)
{
displayname = g_filename_display_name (childname);
g_print ("failed to map '%s' non-writable, shared: %s\n",
displayname, error->message);
exit (1);
}
return map;
}
static gboolean
signal_parent (gpointer data)
{
#ifndef G_OS_WIN32
kill (parent_pid, SIGUSR1);
#endif
return G_SOURCE_REMOVE;
}
static int
child_main (int argc, char *argv[])
{
GMappedFile *map;
GMainLoop *loop;
parent_pid = atoi (argv[2]);
map = map_or_die (global_filename, FALSE);
#ifndef G_OS_WIN32
signal (SIGUSR1, handle_usr1);
#endif
loop = g_main_loop_new (NULL, FALSE);
g_idle_add (check_stop, loop);
g_idle_add (signal_parent, NULL);
g_main_loop_run (loop);
g_message ("test_child_private: received parent signal");
write_or_die (childname,
g_mapped_file_get_contents (map),
g_mapped_file_get_length (map));
signal_parent (NULL);
return 0;
}
static void
test_mapping (void)
{
GMappedFile *map;
write_or_die (global_filename, "ABC", -1);
map = map_or_die (global_filename, FALSE);
g_assert (g_mapped_file_get_length (map) == 3);
g_mapped_file_free (map);
map = map_or_die (global_filename, TRUE);
g_assert (g_mapped_file_get_length (map) == 3);
g_mapped_file_free (map);
g_message ("test_mapping: ok");
}
static void
test_private (void)
{
GError *error = NULL;
GMappedFile *map;
gchar *buffer;
gsize len;
write_or_die (global_filename, "ABC", -1);
map = map_or_die (global_filename, TRUE);
buffer = (gchar *)g_mapped_file_get_contents (map);
buffer[0] = '1';
buffer[1] = '2';
buffer[2] = '3';
g_mapped_file_free (map);
if (!g_file_get_contents (global_filename, &buffer, &len, &error))
{
g_print ("failed to read '%s': %s\n",
global_displayname, error->message);
exit (1);
}
g_assert (len == 3);
g_assert (strcmp (buffer, "ABC") == 0);
g_free (buffer);
g_message ("test_private: ok");
}
static void
test_child_private (gchar *argv0)
{
GError *error = NULL;
GMappedFile *map;
gchar *buffer;
gsize len;
gchar *child_argv[4];
GPid child_pid;
#ifndef G_OS_WIN32
GMainLoop *loop;
#endif
gchar pid[100];
#ifdef G_OS_WIN32
g_remove ("STOP");
g_assert (!g_file_test ("STOP", G_FILE_TEST_EXISTS));
#endif
write_or_die (global_filename, "ABC", -1);
map = map_or_die (global_filename, TRUE);
#ifndef G_OS_WIN32
signal (SIGUSR1, handle_usr1);
#endif
g_snprintf (pid, sizeof(pid), "%d", getpid ());
child_argv[0] = argv0;
child_argv[1] = "mapchild";
child_argv[2] = pid;
child_argv[3] = NULL;
if (!g_spawn_async (dir, child_argv, NULL,
0, NULL, NULL, &child_pid, &error))
{
g_print ("failed to spawn child: %s\n",
error->message);
exit (1);
}
g_message ("test_child_private: child spawned");
#ifndef G_OS_WIN32
loop = g_main_loop_new (NULL, FALSE);
g_idle_add (check_stop, loop);
g_main_loop_run (loop);
stop = FALSE;
#else
g_usleep (2000000);
#endif
g_message ("test_child_private: received first child signal");
buffer = (gchar *)g_mapped_file_get_contents (map);
buffer[0] = '1';
buffer[1] = '2';
buffer[2] = '3';
g_mapped_file_free (map);
#ifndef G_OS_WIN32
kill (child_pid, SIGUSR1);
#else
g_file_set_contents ("STOP", "Hey there\n", -1, NULL);
#endif
#ifndef G_OS_WIN32
g_idle_add (check_stop, loop);
g_main_loop_run (loop);
#else
g_usleep (2000000);
#endif
g_message ("test_child_private: received second child signal");
if (!g_file_get_contents (childname, &buffer, &len, &error))
{
gchar *name;
name = g_filename_display_name (childname);
g_print ("failed to read '%s': %s\n", name, error->message);
exit (1);
}
g_assert (len == 3);
g_assert (strcmp (buffer, "ABC") == 0);
g_free (buffer);
g_message ("test_child_private: ok");
}
static int
parent_main (int argc,
char *argv[])
{
/* test mapping with various flag combinations */
test_mapping ();
/* test private modification */
test_private ();
/* test multiple clients, non-shared */
test_child_private (argv[0]);
return 0;
}
int
main (int argc,
char *argv[])
{
int ret;
#ifndef G_OS_WIN32
sigset_t sig_mask, old_mask;
sigemptyset (&sig_mask);
sigaddset (&sig_mask, SIGUSR1);
if (sigprocmask (SIG_UNBLOCK, &sig_mask, &old_mask) == 0)
{
if (sigismember (&old_mask, SIGUSR1))
g_message ("SIGUSR1 was blocked, unblocking it");
}
#endif
dir = g_get_current_dir ();
global_filename = g_build_filename (dir, "maptest", NULL);
global_displayname = g_filename_display_name (global_filename);
childname = g_build_filename (dir, "mapchild", NULL);
if (argc > 1)
ret = child_main (argc, argv);
else
ret = parent_main (argc, argv);
g_free (childname);
g_free (global_filename);
g_free (global_displayname);
g_free (dir);
return ret;
}

603
tests/memchunks.c Normal file
View file

@ -0,0 +1,603 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* 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/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* MT safe
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "glib.h"
/* notes on macros:
* if ENABLE_GC_FRIENDLY is defined, freed memory should be 0-wiped.
*/
#define MEM_PROFILE_TABLE_SIZE 4096
#define MEM_AREA_SIZE 4L
static guint mem_chunk_recursion = 0;
# define MEM_CHUNK_ROUTINE_COUNT() (mem_chunk_recursion)
# define ENTER_MEM_CHUNK_ROUTINE() (mem_chunk_recursion = MEM_CHUNK_ROUTINE_COUNT () + 1)
# define LEAVE_MEM_CHUNK_ROUTINE() (mem_chunk_recursion = MEM_CHUNK_ROUTINE_COUNT () - 1)
/* --- old memchunk prototypes --- */
GMemChunk* old_mem_chunk_new (const gchar *name,
gulong atom_size,
gulong area_size,
gint type);
void old_mem_chunk_destroy (GMemChunk *mem_chunk);
gpointer old_mem_chunk_alloc (GMemChunk *mem_chunk);
gpointer old_mem_chunk_alloc0 (GMemChunk *mem_chunk);
void old_mem_chunk_free (GMemChunk *mem_chunk,
gpointer mem);
void old_mem_chunk_clean (GMemChunk *mem_chunk);
void old_mem_chunk_reset (GMemChunk *mem_chunk);
void old_mem_chunk_print (GMemChunk *mem_chunk);
void old_mem_chunk_info (void);
/* --- MemChunks --- */
#ifndef G_ALLOC_AND_FREE
typedef struct _GAllocator GAllocator;
typedef struct _GMemChunk GMemChunk;
#define G_ALLOC_ONLY 1
#define G_ALLOC_AND_FREE 2
#endif
typedef struct _GFreeAtom GFreeAtom;
typedef struct _GMemArea GMemArea;
struct _GFreeAtom
{
GFreeAtom *next;
};
struct _GMemArea
{
GMemArea *next; /* the next mem area */
GMemArea *prev; /* the previous mem area */
gulong index; /* the current index into the "mem" array */
gulong free; /* the number of free bytes in this mem area */
gulong allocated; /* the number of atoms allocated from this area */
gulong mark; /* is this mem area marked for deletion */
gchar mem[MEM_AREA_SIZE]; /* the mem array from which atoms get allocated
* the actual size of this array is determined by
* the mem chunk "area_size". ANSI says that it
* must be declared to be the maximum size it
* can possibly be (even though the actual size
* may be less).
*/
};
struct _GMemChunk
{
const gchar *name; /* name of this MemChunk...used for debugging output */
gint type; /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */
gint num_mem_areas; /* the number of memory areas */
gint num_marked_areas; /* the number of areas marked for deletion */
guint atom_size; /* the size of an atom */
gulong area_size; /* the size of a memory area */
GMemArea *mem_area; /* the current memory area */
GMemArea *mem_areas; /* a list of all the mem areas owned by this chunk */
GMemArea *free_mem_area; /* the free area...which is about to be destroyed */
GFreeAtom *free_atoms; /* the free atoms list */
GTree *mem_tree; /* tree of mem areas sorted by memory address */
GMemChunk *next; /* pointer to the next chunk */
GMemChunk *prev; /* pointer to the previous chunk */
};
static gulong old_mem_chunk_compute_size (gulong size,
gulong min_size) G_GNUC_CONST;
static gint old_mem_chunk_area_compare (GMemArea *a,
GMemArea *b);
static gint old_mem_chunk_area_search (GMemArea *a,
gchar *addr);
/* here we can't use StaticMutexes, as they depend upon a working
* g_malloc, the same holds true for StaticPrivate
*/
static GMutex mem_chunks_lock;
static GMemChunk *mem_chunks = NULL;
GMemChunk*
old_mem_chunk_new (const gchar *name,
gulong atom_size,
gulong area_size,
gint type)
{
GMemChunk *mem_chunk;
gulong rarea_size;
g_return_val_if_fail (atom_size > 0, NULL);
g_return_val_if_fail (area_size >= atom_size, NULL);
ENTER_MEM_CHUNK_ROUTINE ();
area_size = (area_size + atom_size - 1) / atom_size;
area_size *= atom_size;
mem_chunk = g_new (GMemChunk, 1);
mem_chunk->name = name;
mem_chunk->type = type;
mem_chunk->num_mem_areas = 0;
mem_chunk->num_marked_areas = 0;
mem_chunk->mem_area = NULL;
mem_chunk->free_mem_area = NULL;
mem_chunk->free_atoms = NULL;
mem_chunk->mem_tree = NULL;
mem_chunk->mem_areas = NULL;
mem_chunk->atom_size = atom_size;
if (mem_chunk->type == G_ALLOC_AND_FREE)
mem_chunk->mem_tree = g_tree_new ((GCompareFunc) old_mem_chunk_area_compare);
if (mem_chunk->atom_size % G_MEM_ALIGN)
mem_chunk->atom_size += G_MEM_ALIGN - (mem_chunk->atom_size % G_MEM_ALIGN);
rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE;
rarea_size = old_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE);
mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE);
g_mutex_lock (&mem_chunks_lock);
mem_chunk->next = mem_chunks;
mem_chunk->prev = NULL;
if (mem_chunks)
mem_chunks->prev = mem_chunk;
mem_chunks = mem_chunk;
g_mutex_unlock (&mem_chunks_lock);
LEAVE_MEM_CHUNK_ROUTINE ();
return mem_chunk;
}
void
old_mem_chunk_destroy (GMemChunk *mem_chunk)
{
GMemArea *mem_areas;
GMemArea *temp_area;
g_return_if_fail (mem_chunk != NULL);
ENTER_MEM_CHUNK_ROUTINE ();
mem_areas = mem_chunk->mem_areas;
while (mem_areas)
{
temp_area = mem_areas;
mem_areas = mem_areas->next;
g_free (temp_area);
}
g_mutex_lock (&mem_chunks_lock);
if (mem_chunk->next)
mem_chunk->next->prev = mem_chunk->prev;
if (mem_chunk->prev)
mem_chunk->prev->next = mem_chunk->next;
if (mem_chunk == mem_chunks)
mem_chunks = mem_chunks->next;
g_mutex_unlock (&mem_chunks_lock);
if (mem_chunk->type == G_ALLOC_AND_FREE)
g_tree_destroy (mem_chunk->mem_tree);
g_free (mem_chunk);
LEAVE_MEM_CHUNK_ROUTINE ();
}
gpointer
old_mem_chunk_alloc (GMemChunk *mem_chunk)
{
GMemArea *temp_area;
gpointer mem;
ENTER_MEM_CHUNK_ROUTINE ();
g_return_val_if_fail (mem_chunk != NULL, NULL);
while (mem_chunk->free_atoms)
{
/* Get the first piece of memory on the "free_atoms" list.
* We can go ahead and destroy the list node we used to keep
* track of it with and to update the "free_atoms" list to
* point to its next element.
*/
mem = mem_chunk->free_atoms;
mem_chunk->free_atoms = mem_chunk->free_atoms->next;
/* Determine which area this piece of memory is allocated from */
temp_area = g_tree_search (mem_chunk->mem_tree,
(GCompareFunc) old_mem_chunk_area_search,
mem);
/* If the area has been marked, then it is being destroyed.
* (ie marked to be destroyed).
* We check to see if all of the segments on the free list that
* reference this area have been removed. This occurs when
* the amount of free memory is less than the allocatable size.
* If the chunk should be freed, then we place it in the "free_mem_area".
* This is so we make sure not to free the mem area here and then
* allocate it again a few lines down.
* If we don't allocate a chunk a few lines down then the "free_mem_area"
* will be freed.
* If there is already a "free_mem_area" then we'll just free this mem area.
*/
if (temp_area->mark)
{
/* Update the "free" memory available in that area */
temp_area->free += mem_chunk->atom_size;
if (temp_area->free == mem_chunk->area_size)
{
if (temp_area == mem_chunk->mem_area)
mem_chunk->mem_area = NULL;
if (mem_chunk->free_mem_area)
{
mem_chunk->num_mem_areas -= 1;
if (temp_area->next)
temp_area->next->prev = temp_area->prev;
if (temp_area->prev)
temp_area->prev->next = temp_area->next;
if (temp_area == mem_chunk->mem_areas)
mem_chunk->mem_areas = mem_chunk->mem_areas->next;
if (mem_chunk->type == G_ALLOC_AND_FREE)
g_tree_remove (mem_chunk->mem_tree, temp_area);
g_free (temp_area);
}
else
mem_chunk->free_mem_area = temp_area;
mem_chunk->num_marked_areas -= 1;
}
}
else
{
/* Update the number of allocated atoms count.
*/
temp_area->allocated += 1;
/* The area wasn't marked...return the memory
*/
goto outa_here;
}
}
/* If there isn't a current mem area or the current mem area is out of space
* then allocate a new mem area. We'll first check and see if we can use
* the "free_mem_area". Otherwise we'll just malloc the mem area.
*/
if ((!mem_chunk->mem_area) ||
((mem_chunk->mem_area->index + mem_chunk->atom_size) > mem_chunk->area_size))
{
if (mem_chunk->free_mem_area)
{
mem_chunk->mem_area = mem_chunk->free_mem_area;
mem_chunk->free_mem_area = NULL;
}
else
{
#ifdef ENABLE_GC_FRIENDLY
mem_chunk->mem_area = (GMemArea*) g_malloc0 (sizeof (GMemArea) -
MEM_AREA_SIZE +
mem_chunk->area_size);
#else /* !ENABLE_GC_FRIENDLY */
mem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) -
MEM_AREA_SIZE +
mem_chunk->area_size);
#endif /* ENABLE_GC_FRIENDLY */
mem_chunk->num_mem_areas += 1;
mem_chunk->mem_area->next = mem_chunk->mem_areas;
mem_chunk->mem_area->prev = NULL;
if (mem_chunk->mem_areas)
mem_chunk->mem_areas->prev = mem_chunk->mem_area;
mem_chunk->mem_areas = mem_chunk->mem_area;
if (mem_chunk->type == G_ALLOC_AND_FREE)
g_tree_insert (mem_chunk->mem_tree, mem_chunk->mem_area, mem_chunk->mem_area);
}
mem_chunk->mem_area->index = 0;
mem_chunk->mem_area->free = mem_chunk->area_size;
mem_chunk->mem_area->allocated = 0;
mem_chunk->mem_area->mark = 0;
}
/* Get the memory and modify the state variables appropriately.
*/
mem = (gpointer) &mem_chunk->mem_area->mem[mem_chunk->mem_area->index];
mem_chunk->mem_area->index += mem_chunk->atom_size;
mem_chunk->mem_area->free -= mem_chunk->atom_size;
mem_chunk->mem_area->allocated += 1;
outa_here:
LEAVE_MEM_CHUNK_ROUTINE ();
return mem;
}
gpointer
old_mem_chunk_alloc0 (GMemChunk *mem_chunk)
{
gpointer mem;
mem = old_mem_chunk_alloc (mem_chunk);
if (mem)
{
memset (mem, 0, mem_chunk->atom_size);
}
return mem;
}
void
old_mem_chunk_free (GMemChunk *mem_chunk,
gpointer mem)
{
GMemArea *temp_area;
GFreeAtom *free_atom;
g_return_if_fail (mem_chunk != NULL);
g_return_if_fail (mem != NULL);
ENTER_MEM_CHUNK_ROUTINE ();
#ifdef ENABLE_GC_FRIENDLY
memset (mem, 0, mem_chunk->atom_size);
#endif /* ENABLE_GC_FRIENDLY */
/* Don't do anything if this is an ALLOC_ONLY chunk
*/
if (mem_chunk->type == G_ALLOC_AND_FREE)
{
/* Place the memory on the "free_atoms" list
*/
free_atom = (GFreeAtom*) mem;
free_atom->next = mem_chunk->free_atoms;
mem_chunk->free_atoms = free_atom;
temp_area = g_tree_search (mem_chunk->mem_tree,
(GCompareFunc) old_mem_chunk_area_search,
mem);
temp_area->allocated -= 1;
if (temp_area->allocated == 0)
{
temp_area->mark = 1;
mem_chunk->num_marked_areas += 1;
}
}
LEAVE_MEM_CHUNK_ROUTINE ();
}
/* This doesn't free the free_area if there is one */
void
old_mem_chunk_clean (GMemChunk *mem_chunk)
{
GMemArea *mem_area;
GFreeAtom *prev_free_atom;
GFreeAtom *temp_free_atom;
gpointer mem;
g_return_if_fail (mem_chunk != NULL);
ENTER_MEM_CHUNK_ROUTINE ();
if (mem_chunk->type == G_ALLOC_AND_FREE)
{
prev_free_atom = NULL;
temp_free_atom = mem_chunk->free_atoms;
while (temp_free_atom)
{
mem = (gpointer) temp_free_atom;
mem_area = g_tree_search (mem_chunk->mem_tree,
(GCompareFunc) old_mem_chunk_area_search,
mem);
/* If this mem area is marked for destruction then delete the
* area and list node and decrement the free mem.
*/
if (mem_area->mark)
{
if (prev_free_atom)
prev_free_atom->next = temp_free_atom->next;
else
mem_chunk->free_atoms = temp_free_atom->next;
temp_free_atom = temp_free_atom->next;
mem_area->free += mem_chunk->atom_size;
if (mem_area->free == mem_chunk->area_size)
{
mem_chunk->num_mem_areas -= 1;
mem_chunk->num_marked_areas -= 1;
if (mem_area->next)
mem_area->next->prev = mem_area->prev;
if (mem_area->prev)
mem_area->prev->next = mem_area->next;
if (mem_area == mem_chunk->mem_areas)
mem_chunk->mem_areas = mem_chunk->mem_areas->next;
if (mem_area == mem_chunk->mem_area)
mem_chunk->mem_area = NULL;
if (mem_chunk->type == G_ALLOC_AND_FREE)
g_tree_remove (mem_chunk->mem_tree, mem_area);
g_free (mem_area);
}
}
else
{
prev_free_atom = temp_free_atom;
temp_free_atom = temp_free_atom->next;
}
}
}
LEAVE_MEM_CHUNK_ROUTINE ();
}
void
old_mem_chunk_reset (GMemChunk *mem_chunk)
{
GMemArea *mem_areas;
GMemArea *temp_area;
g_return_if_fail (mem_chunk != NULL);
ENTER_MEM_CHUNK_ROUTINE ();
mem_areas = mem_chunk->mem_areas;
mem_chunk->num_mem_areas = 0;
mem_chunk->mem_areas = NULL;
mem_chunk->mem_area = NULL;
while (mem_areas)
{
temp_area = mem_areas;
mem_areas = mem_areas->next;
g_free (temp_area);
}
mem_chunk->free_atoms = NULL;
if (mem_chunk->mem_tree)
{
g_tree_destroy (mem_chunk->mem_tree);
mem_chunk->mem_tree = g_tree_new ((GCompareFunc) old_mem_chunk_area_compare);
}
LEAVE_MEM_CHUNK_ROUTINE ();
}
void
old_mem_chunk_print (GMemChunk *mem_chunk)
{
GMemArea *mem_areas;
gulong mem;
g_return_if_fail (mem_chunk != NULL);
mem_areas = mem_chunk->mem_areas;
mem = 0;
while (mem_areas)
{
mem += mem_chunk->area_size - mem_areas->free;
mem_areas = mem_areas->next;
}
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
"%s: %ld bytes using %d mem areas",
mem_chunk->name, mem, mem_chunk->num_mem_areas);
}
void
old_mem_chunk_info (void)
{
GMemChunk *mem_chunk;
gint count;
count = 0;
g_mutex_lock (&mem_chunks_lock);
mem_chunk = mem_chunks;
while (mem_chunk)
{
count += 1;
mem_chunk = mem_chunk->next;
}
g_mutex_unlock (&mem_chunks_lock);
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%d mem chunks", count);
g_mutex_lock (&mem_chunks_lock);
mem_chunk = mem_chunks;
g_mutex_unlock (&mem_chunks_lock);
while (mem_chunk)
{
old_mem_chunk_print ((GMemChunk*) mem_chunk);
mem_chunk = mem_chunk->next;
}
}
static gulong
old_mem_chunk_compute_size (gulong size,
gulong min_size)
{
gulong power_of_2;
gulong lower, upper;
power_of_2 = 16;
while (power_of_2 < size)
power_of_2 <<= 1;
lower = power_of_2 >> 1;
upper = power_of_2;
if (size - lower < upper - size && lower >= min_size)
return lower;
else
return upper;
}
static gint
old_mem_chunk_area_compare (GMemArea *a,
GMemArea *b)
{
if (a->mem > b->mem)
return 1;
else if (a->mem < b->mem)
return -1;
return 0;
}
static gint
old_mem_chunk_area_search (GMemArea *a,
gchar *addr)
{
if (a->mem <= addr)
{
if (addr < &a->mem[a->index])
return 0;
return 1;
}
return -1;
}

139
tests/meson.build Normal file
View file

@ -0,0 +1,139 @@
# tests
# Not entirely random of course, but at least it changes over time
random_number = minor_version + meson.version().split('.').get(1).to_int()
test_env = environment()
test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
test_env.set('G_DEBUG', 'gc-friendly')
test_env.set('MALLOC_CHECK_', '2')
test_env.set('MALLOC_PERTURB_', '@0@'.format(random_number % 256))
test_cargs = ['-DG_LOG_DOMAIN="GLib"', '-UG_DISABLE_ASSERT']
subdir('gobject')
subdir('refcount')
tests = {
'gio-test' : {},
'mainloop-test' : {},
'mapping-test' : {},
'onceinit' : {},
'slice-threadinit' : {
'dependencies' : [libgthread_dep],
},
'spawn-test' : {},
'thread-test' : {},
'threadpool-test' : {'suite' : ['slow']},
'unicode-encoding' : {},
'module-test-library' : {
'dependencies' : [libgmodule_dep],
'export_dynamic' : true,
'source': 'module-test.c',
'c_args': ['-DMODULE_TYPE="library"'],
},
'module-test-plugin' : {
'dependencies' : [libgmodule_dep],
'export_dynamic' : true,
'source': 'module-test.c',
'c_args': ['-DMODULE_TYPE="plugin"'],
},
}
test_extra_programs = {
'slice-test' : {
'extra_sources' : ['memchunks.c'],
},
'assert-msg-test' : {},
}
if host_machine.system() != 'windows'
tests += {
'timeloop' : {},
}
else
test_extra_programs += {
'spawn-test-win32-gui' : {'gui_app' : true}
}
endif
if installed_tests_enabled
install_data(
'utf8.txt',
install_dir : installed_tests_execdir,
)
endif
module_suffix = []
# Keep the autotools convention for shared module suffix because GModule
# depends on it: https://gitlab.gnome.org/GNOME/glib/issues/520
if ['darwin', 'ios'].contains(host_machine.system())
module_suffix = 'so'
endif
foreach module : ['moduletestplugin_a', 'moduletestplugin_b']
shared_module(module + '_plugin', 'lib@0@.c'.format(module),
dependencies : [libglib_dep, libgmodule_dep],
install_dir : installed_tests_execdir,
install : installed_tests_enabled,
name_suffix : module_suffix
)
shared_library(module + '_library', 'lib@0@.c'.format(module),
dependencies : [libglib_dep, libgmodule_dep],
install_dir : installed_tests_execdir,
install : installed_tests_enabled,
name_suffix : module_suffix
)
endforeach
common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
common_deps = [libm, thread_dep, libglib_dep]
foreach test_name, extra_args : tests
source = extra_args.get('source', test_name + '.c')
extra_sources = extra_args.get('extra_sources', [])
install = installed_tests_enabled and extra_args.get('install', true)
template = extra_args.get('tap', false) ? installed_tests_template_tap : installed_tests_template
if install
test_conf = configuration_data()
test_conf.set('installed_tests_dir', installed_tests_execdir)
test_conf.set('program', test_name)
test_conf.set('env', '')
configure_file(
input: template,
output: test_name + '.test',
install_dir: installed_tests_metadir,
configuration: test_conf
)
endif
# FIXME? $(GLIB_DEBUG_FLAGS)
exe = executable(test_name, [source, extra_sources],
c_args : common_c_args + extra_args.get('c_args', []),
dependencies : common_deps + extra_args.get('dependencies', []),
export_dynamic : extra_args.get('export_dynamic', false),
include_directories : extra_args.get('include_directories', []),
install_dir: installed_tests_execdir,
install: install,
)
suite = ['glib'] + extra_args.get('suite', [])
timeout = suite.contains('slow') ? test_timeout_slow : test_timeout
# FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
endforeach
foreach program_name, extra_args : test_extra_programs
source = extra_args.get('source', program_name + '.c')
extra_sources = extra_args.get('extra_sources', [])
install = installed_tests_enabled and extra_args.get('install', true)
executable(program_name, [source, extra_sources],
c_args : common_c_args,
dependencies : common_deps + extra_args.get('dependencies', []),
install_dir : installed_tests_execdir,
install : install,
gui_app : extra_args.get('gui_app', false),
)
endforeach

214
tests/module-test.c Normal file
View file

@ -0,0 +1,214 @@
/* module-test.c - test program for GMODULE
* Copyright (C) 1998 Tim Janik
*
* 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/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <gmodule.h>
#include <string.h>
#ifdef _MSC_VER
# define MODULE_FILENAME_PREFIX ""
#else
# define MODULE_FILENAME_PREFIX "lib"
#endif
gchar* global_state;
G_MODULE_EXPORT void g_clash_func (void);
G_MODULE_EXPORT void
g_clash_func (void)
{
global_state = "global clash";
}
typedef void (*SimpleFunc) (void);
typedef void (*GModuleFunc) (GModule *);
static gchar **gplugin_a_state;
static gchar **gplugin_b_state;
static void
compare (const gchar *desc, const gchar *expected, const gchar *found)
{
if (!expected && !found)
return;
if (expected && found && strcmp (expected, found) == 0)
return;
g_error ("error: %s state should have been \"%s\", but is \"%s\"",
desc, expected ? expected : "NULL", found ? found : "NULL");
}
static void
test_states (const gchar *global, const gchar *gplugin_a,
const gchar *gplugin_b)
{
compare ("global", global, global_state);
compare ("Plugin A", gplugin_a, *gplugin_a_state);
compare ("Plugin B", gplugin_b, *gplugin_b_state);
global_state = *gplugin_a_state = *gplugin_b_state = NULL;
}
static SimpleFunc plugin_clash_func = NULL;
int
main (int argc,
char **argv)
{
GModule *module_self, *module_a, *module_b;
gchar *plugin_a, *plugin_b;
SimpleFunc f_a, f_b, f_self;
GModuleFunc gmod_f;
GError *error = NULL;
g_test_init (&argc, &argv, NULL);
if (!g_module_supported ())
g_error ("dynamic modules not supported");
plugin_a = g_test_build_filename (G_TEST_BUILT, MODULE_FILENAME_PREFIX "moduletestplugin_a_" MODULE_TYPE, NULL);
plugin_b = g_test_build_filename (G_TEST_BUILT, MODULE_FILENAME_PREFIX "moduletestplugin_b_" MODULE_TYPE, NULL);
/* module handles */
module_self = g_module_open_full (NULL, G_MODULE_BIND_LAZY, &error);
g_assert_no_error (error);
if (!module_self)
g_error ("error: %s", g_module_error ());
/* On Windows static compilation mode, glib API symbols are not
* exported dynamically by definition. */
#if !defined(G_PLATFORM_WIN32) || !defined(GLIB_STATIC_COMPILATION)
if (!g_module_symbol (module_self, "g_module_close", (gpointer *) &f_self))
g_error ("error: %s", g_module_error ());
#endif
module_a = g_module_open_full (plugin_a, G_MODULE_BIND_LAZY, &error);
g_assert_no_error (error);
if (!module_a)
g_error ("error: %s", g_module_error ());
module_b = g_module_open_full (plugin_b, G_MODULE_BIND_LAZY, &error);
g_assert_no_error (error);
if (!module_b)
g_error ("error: %s", g_module_error ());
/* get plugin state vars */
if (!g_module_symbol (module_a, "gplugin_a_state",
(gpointer *) &gplugin_a_state))
g_error ("error: %s", g_module_error ());
if (!g_module_symbol (module_b, "gplugin_b_state",
(gpointer *) &gplugin_b_state))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, "check-init");
/* get plugin specific symbols and call them
*/
if (!g_module_symbol (module_a, "gplugin_a_func", (gpointer *) &f_a))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, NULL);
if (!g_module_symbol (module_b, "gplugin_b_func", (gpointer *) &f_b))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, NULL);
f_a ();
test_states (NULL, "Hello world", NULL);
f_b ();
test_states (NULL, NULL, "Hello world");
/* get and call globally clashing functions
*/
if (!g_module_symbol (module_self, "g_clash_func", (gpointer *) &f_self))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, NULL);
if (!g_module_symbol (module_a, "g_clash_func", (gpointer *) &f_a))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, NULL);
if (!g_module_symbol (module_b, "g_clash_func", (gpointer *) &f_b))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, NULL);
f_self ();
test_states ("global clash", NULL, NULL);
f_a ();
test_states (NULL, "global clash", NULL);
f_b ();
test_states (NULL, NULL, "global clash");
/* get and call clashing plugin functions */
if (!g_module_symbol (module_a, "gplugin_clash_func", (gpointer *) &f_a))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, NULL);
if (!g_module_symbol (module_b, "gplugin_clash_func", (gpointer *) &f_b))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, NULL);
plugin_clash_func = f_a;
plugin_clash_func ();
test_states (NULL, "plugin clash", NULL);
plugin_clash_func = f_b;
plugin_clash_func ();
test_states (NULL, NULL, "plugin clash");
/* call gmodule function from A */
if (!g_module_symbol (module_a, "gplugin_a_module_func", (gpointer *) &gmod_f))
g_error ("error: %s", g_module_error ());
test_states (NULL, NULL, NULL);
gmod_f (module_b);
test_states (NULL, NULL, "BOOH");
gmod_f (module_a);
test_states (NULL, "BOOH", NULL);
/* unload plugins */
if (!g_module_close (module_a))
g_error ("error: %s", g_module_error ());
if (!g_module_close (module_b))
g_error ("error: %s", g_module_error ());
g_free (plugin_a);
g_free (plugin_b);
g_module_close (module_self);
return 0;
}

273
tests/onceinit.c Normal file
View file

@ -0,0 +1,273 @@
/* g_once_init_*() test
* Copyright (C) 2007 Tim Janik
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
* This work 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.
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <glib.h>
#include <stdlib.h>
#define N_THREADS (13)
static GMutex tmutex;
static GCond tcond;
static int thread_call_count = 0; /* (atomic) */
static char dummy_value = 'x';
static void
assert_singleton_execution1 (void)
{
static int seen_execution = 0; /* (atomic) */
int old_seen_execution = g_atomic_int_add (&seen_execution, 1);
if (old_seen_execution != 0)
g_error ("%s: function executed more than once", G_STRFUNC);
}
static void
assert_singleton_execution2 (void)
{
static int seen_execution = 0; /* (atomic) */
int old_seen_execution = g_atomic_int_add (&seen_execution, 1);
if (old_seen_execution != 0)
g_error ("%s: function executed more than once", G_STRFUNC);
}
static void
assert_singleton_execution3 (void)
{
static int seen_execution = 0; /* (atomic) */
int old_seen_execution = g_atomic_int_add (&seen_execution, 1);
if (old_seen_execution != 0)
g_error ("%s: function executed more than once", G_STRFUNC);
}
static void
initializer1 (void)
{
static gsize initialized = 0;
if (g_once_init_enter (&initialized))
{
gsize initval = 42;
assert_singleton_execution1();
g_once_init_leave (&initialized, initval);
}
}
static gpointer
initializer2 (void)
{
static gsize initialized = 0;
if (g_once_init_enter (&initialized))
{
void *pointer_value = &dummy_value;
assert_singleton_execution2();
g_once_init_leave (&initialized, (gsize) pointer_value);
}
return (void*) initialized;
}
static void
initializer3 (void)
{
static gsize initialized = 0;
if (g_once_init_enter (&initialized))
{
gsize initval = 42;
assert_singleton_execution3();
g_usleep (25 * 1000); /* waste time for multiple threads to wait */
g_once_init_leave (&initialized, initval);
}
}
static gpointer
tmain_call_initializer3 (gpointer user_data)
{
g_mutex_lock (&tmutex);
g_cond_wait (&tcond, &tmutex);
g_mutex_unlock (&tmutex);
//g_printf ("[");
initializer3();
//g_printf ("]\n");
g_atomic_int_add (&thread_call_count, 1);
return NULL;
}
static void* stress_concurrent_initializers (void*);
int
main (int argc,
char *argv[])
{
G_GNUC_UNUSED GThread *threads[N_THREADS];
int i;
void *p;
/* test simple initializer */
initializer1();
initializer1();
/* test pointer initializer */
p = initializer2();
g_assert (p == &dummy_value);
p = initializer2();
g_assert (p == &dummy_value);
/* start multiple threads for initializer3() */
g_mutex_lock (&tmutex);
for (i = 0; i < N_THREADS; i++)
threads[i] = g_thread_create (tmain_call_initializer3, 0, FALSE, NULL);
g_mutex_unlock (&tmutex);
/* concurrently call initializer3() */
g_cond_broadcast (&tcond);
/* loop until all threads passed the call to initializer3() */
while (g_atomic_int_get (&thread_call_count) < i)
{
if (rand() % 2)
g_thread_yield(); /* concurrent shuffling for single core */
else
g_usleep (1000); /* concurrent shuffling for multi core */
g_cond_broadcast (&tcond);
}
/* call multiple (unoptimized) initializers from multiple threads */
g_mutex_lock (&tmutex);
g_atomic_int_set (&thread_call_count, 0);
for (i = 0; i < N_THREADS; i++)
g_thread_create (stress_concurrent_initializers, 0, FALSE, NULL);
g_mutex_unlock (&tmutex);
while (g_atomic_int_get (&thread_call_count) < 256 * 4 * N_THREADS)
g_usleep (50 * 1000); /* wait for all 5 threads to complete */
return 0;
}
/* get rid of g_once_init_enter-optimizations in the below definitions
* to uncover possible races in the g_once_init_enter_impl()/
* g_once_init_leave() implementations
*/
#undef g_once_init_enter
#undef g_once_init_leave
/* define 16 * 16 simple initializers */
#define DEFINE_TEST_INITIALIZER(N) \
static void \
test_initializer_##N (void) \
{ \
static gsize initialized = 0; \
if (g_once_init_enter (&initialized)) \
{ \
g_free (g_strdup_printf ("cpuhog%5d", 1)); \
g_free (g_strdup_printf ("cpuhog%6d", 2)); \
g_free (g_strdup_printf ("cpuhog%7d", 3)); \
g_once_init_leave (&initialized, 1); \
} \
}
#define DEFINE_16_TEST_INITIALIZERS(P) \
DEFINE_TEST_INITIALIZER (P##0) \
DEFINE_TEST_INITIALIZER (P##1) \
DEFINE_TEST_INITIALIZER (P##2) \
DEFINE_TEST_INITIALIZER (P##3) \
DEFINE_TEST_INITIALIZER (P##4) \
DEFINE_TEST_INITIALIZER (P##5) \
DEFINE_TEST_INITIALIZER (P##6) \
DEFINE_TEST_INITIALIZER (P##7) \
DEFINE_TEST_INITIALIZER (P##8) \
DEFINE_TEST_INITIALIZER (P##9) \
DEFINE_TEST_INITIALIZER (P##a) \
DEFINE_TEST_INITIALIZER (P##b) \
DEFINE_TEST_INITIALIZER (P##c) \
DEFINE_TEST_INITIALIZER (P##d) \
DEFINE_TEST_INITIALIZER (P##e) \
DEFINE_TEST_INITIALIZER (P##f)
#define DEFINE_256_TEST_INITIALIZERS(P) \
DEFINE_16_TEST_INITIALIZERS (P##_0) \
DEFINE_16_TEST_INITIALIZERS (P##_1) \
DEFINE_16_TEST_INITIALIZERS (P##_2) \
DEFINE_16_TEST_INITIALIZERS (P##_3) \
DEFINE_16_TEST_INITIALIZERS (P##_4) \
DEFINE_16_TEST_INITIALIZERS (P##_5) \
DEFINE_16_TEST_INITIALIZERS (P##_6) \
DEFINE_16_TEST_INITIALIZERS (P##_7) \
DEFINE_16_TEST_INITIALIZERS (P##_8) \
DEFINE_16_TEST_INITIALIZERS (P##_9) \
DEFINE_16_TEST_INITIALIZERS (P##_a) \
DEFINE_16_TEST_INITIALIZERS (P##_b) \
DEFINE_16_TEST_INITIALIZERS (P##_c) \
DEFINE_16_TEST_INITIALIZERS (P##_d) \
DEFINE_16_TEST_INITIALIZERS (P##_e) \
DEFINE_16_TEST_INITIALIZERS (P##_f)
/* list 16 * 16 simple initializers */
#define LIST_16_TEST_INITIALIZERS(P) \
test_initializer_##P##0, \
test_initializer_##P##1, \
test_initializer_##P##2, \
test_initializer_##P##3, \
test_initializer_##P##4, \
test_initializer_##P##5, \
test_initializer_##P##6, \
test_initializer_##P##7, \
test_initializer_##P##8, \
test_initializer_##P##9, \
test_initializer_##P##a, \
test_initializer_##P##b, \
test_initializer_##P##c, \
test_initializer_##P##d, \
test_initializer_##P##e, \
test_initializer_##P##f
#define LIST_256_TEST_INITIALIZERS(P) \
LIST_16_TEST_INITIALIZERS (P##_0), \
LIST_16_TEST_INITIALIZERS (P##_1), \
LIST_16_TEST_INITIALIZERS (P##_2), \
LIST_16_TEST_INITIALIZERS (P##_3), \
LIST_16_TEST_INITIALIZERS (P##_4), \
LIST_16_TEST_INITIALIZERS (P##_5), \
LIST_16_TEST_INITIALIZERS (P##_6), \
LIST_16_TEST_INITIALIZERS (P##_7), \
LIST_16_TEST_INITIALIZERS (P##_8), \
LIST_16_TEST_INITIALIZERS (P##_9), \
LIST_16_TEST_INITIALIZERS (P##_a), \
LIST_16_TEST_INITIALIZERS (P##_b), \
LIST_16_TEST_INITIALIZERS (P##_c), \
LIST_16_TEST_INITIALIZERS (P##_d), \
LIST_16_TEST_INITIALIZERS (P##_e), \
LIST_16_TEST_INITIALIZERS (P##_f)
/* define 4 * 256 initializers */
DEFINE_256_TEST_INITIALIZERS (stress1);
DEFINE_256_TEST_INITIALIZERS (stress2);
DEFINE_256_TEST_INITIALIZERS (stress3);
DEFINE_256_TEST_INITIALIZERS (stress4);
/* call the above 1024 initializers */
static void*
stress_concurrent_initializers (void *user_data)
{
static void (*initializers[]) (void) = {
LIST_256_TEST_INITIALIZERS (stress1),
LIST_256_TEST_INITIALIZERS (stress2),
LIST_256_TEST_INITIALIZERS (stress3),
LIST_256_TEST_INITIALIZERS (stress4),
};
gsize i;
/* sync to main thread */
g_mutex_lock (&tmutex);
g_mutex_unlock (&tmutex);
/* initialize concurrently */
for (i = 0; i < G_N_ELEMENTS (initializers); i++)
{
initializers[i]();
g_atomic_int_add (&thread_call_count, 1);
}
return NULL;
}

View file

@ -0,0 +1,60 @@
refcount_tests = {
'objects' : {},
'objects2' : {'suite' : ['slow']},
'properties' : {},
'properties2' : {'suite' : ['slow']},
'properties3' : {'suite' : ['slow']},
'properties4' : {},
'signal1' : {
'source' : 'signals.c',
'c_args' : ['-DTESTNUM=1'],
},
'signal2' : {
'source' : 'signals.c',
'c_args' : ['-DTESTNUM=2'],
},
'signal3' : {
'source' : 'signals.c',
'c_args' : ['-DTESTNUM=3'],
},
'signal4' : {
'source' : 'signals.c',
'c_args' : ['-DTESTNUM=4'],
},
}
common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
common_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
foreach test_name, extra_args : refcount_tests
source = extra_args.get('source', test_name + '.c')
extra_sources = extra_args.get('extra_sources', [])
install = installed_tests_enabled and extra_args.get('install', true)
if install
test_conf = configuration_data()
test_conf.set('installed_tests_dir', installed_tests_execdir)
test_conf.set('program', test_name)
test_conf.set('env', '')
configure_file(
input: installed_tests_template,
output: test_name + '.test',
install_dir: installed_tests_metadir,
configuration: test_conf
)
endif
# FIXME? $(GLIB_DEBUG_FLAGS)
exe = executable(test_name, [source, extra_sources],
c_args : common_c_args + extra_args.get('c_args', []),
dependencies : common_deps + extra_args.get('dependencies', []),
install_dir: installed_tests_execdir,
install: install,
)
suite = ['refcount'] + extra_args.get('suite', [])
timeout = suite.contains('slow') ? test_timeout_slow : test_timeout
# FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
endforeach

163
tests/refcount/objects.c Normal file
View file

@ -0,0 +1,163 @@
#include <glib.h>
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#define G_TYPE_TEST (my_test_get_type ())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
typedef struct _GTest GTest;
typedef struct _GTestClass GTestClass;
struct _GTest
{
GObject object;
};
struct _GTestClass
{
GObjectClass parent_class;
};
static GType my_test_get_type (void);
static gint stopping; /* (atomic) */
static void my_test_class_init (GTestClass * klass);
static void my_test_init (GTest * test);
static void my_test_dispose (GObject * object);
static GObjectClass *parent_class = NULL;
static GType
my_test_get_type (void)
{
static GType test_type = 0;
if (!test_type) {
const GTypeInfo test_info = {
sizeof (GTestClass),
NULL,
NULL,
(GClassInitFunc) my_test_class_init,
NULL,
NULL,
sizeof (GTest),
0,
(GInstanceInitFunc) my_test_init,
NULL
};
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest",
&test_info, 0);
}
return test_type;
}
static void
my_test_class_init (GTestClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
parent_class = g_type_class_ref (G_TYPE_OBJECT);
gobject_class->dispose = my_test_dispose;
}
static void
my_test_init (GTest * test)
{
g_print ("init %p\n", test);
}
static void
my_test_dispose (GObject * object)
{
GTest *test;
test = MY_TEST (object);
g_print ("dispose %p!\n", test);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
my_test_do_refcount (GTest * test)
{
g_object_ref (test);
g_object_unref (test);
}
static gpointer
run_thread (GTest * test)
{
gint i = 1;
while (!g_atomic_int_get (&stopping)) {
my_test_do_refcount (test);
if ((i++ % 10000) == 0) {
g_print (".");
g_thread_yield(); /* force context switch */
}
}
return NULL;
}
int
main (int argc, char **argv)
{
guint i;
GTest *test1, *test2;
GArray *test_threads;
const guint n_threads = 5;
g_print ("START: %s\n", argv[0]);
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
test1 = g_object_new (G_TYPE_TEST, NULL);
test2 = g_object_new (G_TYPE_TEST, NULL);
test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *));
g_atomic_int_set (&stopping, 0);
for (i = 0; i < n_threads; i++) {
GThread *thread;
thread = g_thread_create ((GThreadFunc) run_thread, test1, TRUE, NULL);
g_array_append_val (test_threads, thread);
thread = g_thread_create ((GThreadFunc) run_thread, test2, TRUE, NULL);
g_array_append_val (test_threads, thread);
}
g_usleep (5000000);
g_atomic_int_set (&stopping, 1);
g_print ("\nstopping\n");
/* join all threads */
for (i = 0; i < 2 * n_threads; i++) {
GThread *thread;
thread = g_array_index (test_threads, GThread *, i);
g_thread_join (thread);
}
g_object_unref (test1);
g_object_unref (test2);
g_array_unref (test_threads);
g_print ("stopped\n");
return 0;
}

121
tests/refcount/objects2.c Normal file
View file

@ -0,0 +1,121 @@
#include <glib.h>
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#define G_TYPE_TEST (my_test_get_type ())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
typedef struct _GTest GTest;
typedef struct _GTestClass GTestClass;
struct _GTest
{
GObject object;
};
struct _GTestClass
{
GObjectClass parent_class;
};
static GType my_test_get_type (void);
static void my_test_class_init (GTestClass * klass);
static void my_test_init (GTest * test);
static void my_test_dispose (GObject * object);
static GObjectClass *parent_class = NULL;
static GType
my_test_get_type (void)
{
static GType test_type = 0;
if (!test_type) {
const GTypeInfo test_info = {
sizeof (GTestClass),
NULL,
NULL,
(GClassInitFunc) my_test_class_init,
NULL,
NULL,
sizeof (GTest),
0,
(GInstanceInitFunc) my_test_init,
NULL
};
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest",
&test_info, 0);
}
return test_type;
}
static void
my_test_class_init (GTestClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
parent_class = g_type_class_ref (G_TYPE_OBJECT);
gobject_class->dispose = my_test_dispose;
}
static void
my_test_init (GTest * test)
{
g_print ("init %p\n", test);
}
static void
my_test_dispose (GObject * object)
{
GTest *test;
test = MY_TEST (object);
g_print ("dispose %p!\n", test);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
my_test_do_refcount (GTest * test)
{
static guint i = 1;
if (i++ % 100000 == 0)
g_print (".");
g_object_ref (test);
g_object_unref (test);
}
int
main (int argc, char **argv)
{
gint i;
GTest *test;
g_print ("START: %s\n", argv[0]);
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
test = g_object_new (G_TYPE_TEST, NULL);
for (i=0; i<100000000; i++) {
my_test_do_refcount (test);
}
g_object_unref (test);
g_print ("\n");
return 0;
}

237
tests/refcount/properties.c Normal file
View file

@ -0,0 +1,237 @@
#include <glib.h>
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#define G_TYPE_TEST (my_test_get_type ())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
enum {
PROP_0,
PROP_DUMMY
};
typedef struct _GTest GTest;
typedef struct _GTestClass GTestClass;
struct _GTest
{
GObject object;
gint id;
gint dummy;
gint count;
};
struct _GTestClass
{
GObjectClass parent_class;
};
static GType my_test_get_type (void);
static gboolean stopping;
static void my_test_class_init (GTestClass * klass);
static void my_test_init (GTest * test);
static void my_test_dispose (GObject * object);
static void my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static GObjectClass *parent_class = NULL;
static GType
my_test_get_type (void)
{
static GType test_type = 0;
if (!test_type) {
const GTypeInfo test_info = {
sizeof (GTestClass),
NULL,
NULL,
(GClassInitFunc) my_test_class_init,
NULL,
NULL,
sizeof (GTest),
0,
(GInstanceInitFunc) my_test_init,
NULL
};
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest", &test_info, 0);
}
return test_type;
}
static void
my_test_class_init (GTestClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
parent_class = g_type_class_ref (G_TYPE_OBJECT);
gobject_class->dispose = my_test_dispose;
gobject_class->get_property = my_test_get_property;
gobject_class->set_property = my_test_set_property;
g_object_class_install_property (gobject_class,
PROP_DUMMY,
g_param_spec_int ("dummy",
NULL,
NULL,
0, G_MAXINT, 0,
G_PARAM_READWRITE));
}
static void
my_test_init (GTest * test)
{
static guint static_id = 1;
test->id = static_id++;
}
static void
my_test_dispose (GObject * object)
{
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GTest *test;
test = MY_TEST (object);
switch (prop_id)
{
case PROP_DUMMY:
g_value_set_int (value, test->dummy);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GTest *test;
test = MY_TEST (object);
switch (prop_id)
{
case PROP_DUMMY:
test->dummy = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
dummy_notify (GObject *object,
GParamSpec *pspec)
{
GTest *test;
test = MY_TEST (object);
test->count++;
}
static void
my_test_do_property (GTest * test)
{
gint dummy;
g_object_get (test, "dummy", &dummy, NULL);
g_object_set (test, "dummy", dummy + 1, NULL);
}
static gpointer
run_thread (GTest * test)
{
gint i = 1;
while (!g_atomic_int_get (&stopping)) {
my_test_do_property (test);
if ((i++ % 10000) == 0)
{
g_print (".%c", 'a' + test->id);
g_thread_yield(); /* force context switch */
}
}
return NULL;
}
int
main (int argc, char **argv)
{
#define N_THREADS 5
GThread *test_threads[N_THREADS];
GTest *test_objects[N_THREADS];
gint i;
g_print ("START: %s\n", argv[0]);
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
for (i = 0; i < N_THREADS; i++) {
GTest *test;
test = g_object_new (G_TYPE_TEST, NULL);
test_objects[i] = test;
g_assert (test->count == test->dummy);
g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL);
}
g_atomic_int_set (&stopping, FALSE);
for (i = 0; i < N_THREADS; i++)
test_threads[i] = g_thread_create ((GThreadFunc) run_thread, test_objects[i], TRUE, NULL);
g_usleep (3000000);
g_atomic_int_set (&stopping, TRUE);
g_print ("\nstopping\n");
/* join all threads */
for (i = 0; i < N_THREADS; i++)
g_thread_join (test_threads[i]);
g_print ("stopped\n");
for (i = 0; i < N_THREADS; i++) {
GTest *test = test_objects[i];
g_assert (test->count == test->dummy);
g_object_unref (test);
}
return 0;
}

View file

@ -0,0 +1,202 @@
#include <glib.h>
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#define G_TYPE_TEST (my_test_get_type ())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
enum {
PROP_0,
PROP_DUMMY
};
typedef struct _GTest GTest;
typedef struct _GTestClass GTestClass;
struct _GTest
{
GObject object;
gint dummy;
};
struct _GTestClass
{
GObjectClass parent_class;
};
static GType my_test_get_type (void);
static void my_test_class_init (GTestClass * klass);
static void my_test_init (GTest * test);
static void my_test_dispose (GObject * object);
static void my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static GObjectClass *parent_class = NULL;
static GType
my_test_get_type (void)
{
static GType test_type = 0;
if (!test_type) {
const GTypeInfo test_info = {
sizeof (GTestClass),
NULL,
NULL,
(GClassInitFunc) my_test_class_init,
NULL,
NULL,
sizeof (GTest),
0,
(GInstanceInitFunc) my_test_init,
NULL
};
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest",
&test_info, 0);
}
return test_type;
}
static void
my_test_class_init (GTestClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
parent_class = g_type_class_ref (G_TYPE_OBJECT);
gobject_class->dispose = my_test_dispose;
gobject_class->get_property = my_test_get_property;
gobject_class->set_property = my_test_set_property;
g_object_class_install_property (gobject_class,
PROP_DUMMY,
g_param_spec_int ("dummy",
NULL,
NULL,
0, G_MAXINT, 0,
G_PARAM_READWRITE));
}
static void
my_test_init (GTest * test)
{
g_print ("init %p\n", test);
}
static void
my_test_dispose (GObject * object)
{
GTest *test;
test = MY_TEST (object);
g_print ("dispose %p!\n", test);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GTest *test;
test = MY_TEST (object);
switch (prop_id)
{
case PROP_DUMMY:
g_value_set_int (value, test->dummy);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GTest *test;
test = MY_TEST (object);
switch (prop_id)
{
case PROP_DUMMY:
test->dummy = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gint count = 0;
static void
dummy_notify (GObject *object,
GParamSpec *pspec)
{
count++;
if (count % 10000 == 0)
g_print (".");
}
static void
my_test_do_property (GTest * test)
{
gint dummy;
g_object_get (test, "dummy", &dummy, NULL);
g_object_set (test, "dummy", dummy + 1, NULL);
}
int
main (int argc, char **argv)
{
gint i;
GTest *test;
g_print ("START: %s\n", argv[0]);
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
test = g_object_new (G_TYPE_TEST, NULL);
g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL);
g_assert (count == test->dummy);
for (i=0; i<1000000; i++) {
my_test_do_property (test);
}
g_assert (count == test->dummy);
g_object_unref (test);
return 0;
}

View file

@ -0,0 +1,202 @@
#include <glib.h>
#include <glib-object.h>
#define G_TYPE_TEST (my_test_get_type ())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
enum {
PROP_0,
PROP_DUMMY
};
typedef struct _GTest GTest;
typedef struct _GTestClass GTestClass;
struct _GTest
{
GObject object;
gint id;
gint dummy;
gint count;
gint setcount;
};
struct _GTestClass
{
GObjectClass parent_class;
};
static GType my_test_get_type (void);
G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT)
static gint stopping; /* (atomic) */
static void my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void
my_test_class_init (GTestClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
gobject_class->get_property = my_test_get_property;
gobject_class->set_property = my_test_set_property;
g_object_class_install_property (gobject_class,
PROP_DUMMY,
g_param_spec_int ("dummy",
NULL,
NULL,
0, G_MAXINT, 0,
G_PARAM_READWRITE));
}
static void
my_test_init (GTest * test)
{
static guint static_id = 1;
test->id = static_id++;
}
static void
my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GTest *test;
test = MY_TEST (object);
switch (prop_id)
{
case PROP_DUMMY:
g_value_set_int (value, g_atomic_int_get (&test->dummy));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GTest *test;
test = MY_TEST (object);
switch (prop_id)
{
case PROP_DUMMY:
g_atomic_int_set (&test->dummy, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
dummy_notify (GObject *object,
GParamSpec *pspec)
{
GTest *test;
test = MY_TEST (object);
g_atomic_int_inc (&test->count);
}
static void
my_test_do_property (GTest * test)
{
gint dummy;
g_atomic_int_inc (&test->setcount);
g_object_get (test, "dummy", &dummy, NULL);
g_object_set (test, "dummy", dummy + 1, NULL);
}
static gpointer
run_thread (GTest * test)
{
gint i = 1;
while (!g_atomic_int_get (&stopping)) {
my_test_do_property (test);
if ((i++ % 10000) == 0)
{
g_print (".%c", 'a' + test->id);
g_thread_yield(); /* force context switch */
}
}
return NULL;
}
int
main (int argc, char **argv)
{
gint i;
GTest *test;
GArray *test_threads;
const gint n_threads = 5;
g_print ("START: %s\n", argv[0]);
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
test = g_object_new (G_TYPE_TEST, NULL);
g_assert (test->count == test->dummy);
g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL);
test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *));
g_atomic_int_set (&stopping, 0);
for (i = 0; i < n_threads; i++) {
GThread *thread;
thread = g_thread_create ((GThreadFunc) run_thread, test, TRUE, NULL);
g_array_append_val (test_threads, thread);
}
g_usleep (30000000);
g_atomic_int_set (&stopping, 1);
g_print ("\nstopping\n");
/* join all threads */
for (i = 0; i < n_threads; i++) {
GThread *thread;
thread = g_array_index (test_threads, GThread *, i);
g_thread_join (thread);
}
g_print ("stopped\n");
g_print ("%d %d\n", test->setcount, test->count);
g_array_free (test_threads, TRUE);
g_object_unref (test);
return 0;
}

View file

@ -0,0 +1,173 @@
#include <glib.h>
#include <glib-object.h>
#define MY_TYPE_BADGER (my_badger_get_type ())
#define MY_BADGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_BADGER, MyBadger))
#define MY_IS_BADGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_BADGER))
#define MY_BADGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MY_TYPE_BADGER, MyBadgerClass))
#define MY_IS_BADGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_TYPE_BADGER))
#define MY_BADGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MY_TYPE_BADGER, MyBadgerClass))
enum {
PROP_0,
PROP_MAMA
};
typedef struct _MyBadger MyBadger;
typedef struct _MyBadgerClass MyBadgerClass;
struct _MyBadger
{
GObject parent_instance;
MyBadger * mama;
guint mama_notify_count;
};
struct _MyBadgerClass
{
GObjectClass parent_class;
};
static GType my_badger_get_type (void);
G_DEFINE_TYPE (MyBadger, my_badger, G_TYPE_OBJECT)
static void my_badger_dispose (GObject * object);
static void my_badger_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void my_badger_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void my_badger_mama_notify (GObject *object,
GParamSpec *pspec);
static void
my_badger_class_init (MyBadgerClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
gobject_class->dispose = my_badger_dispose;
gobject_class->get_property = my_badger_get_property;
gobject_class->set_property = my_badger_set_property;
g_object_class_install_property (gobject_class,
PROP_MAMA,
g_param_spec_object ("mama",
NULL,
NULL,
MY_TYPE_BADGER,
G_PARAM_READWRITE));
}
static void
my_badger_init (MyBadger * self)
{
g_signal_connect (self, "notify::mama", G_CALLBACK (my_badger_mama_notify),
NULL);
}
static void
my_badger_dispose (GObject * object)
{
MyBadger * self;
self = MY_BADGER (object);
if (self->mama != NULL)
{
g_object_unref (self->mama);
self->mama = NULL;
}
G_OBJECT_CLASS (my_badger_parent_class)->dispose (object);
}
static void
my_badger_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MyBadger *self;
self = MY_BADGER (object);
switch (prop_id)
{
case PROP_MAMA:
g_value_set_object (value, self->mama);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_badger_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MyBadger *self;
self = MY_BADGER (object);
switch (prop_id)
{
case PROP_MAMA:
if (self->mama)
g_object_unref (self->mama);
self->mama = g_value_dup_object (value);
if (self->mama)
g_object_set (self->mama, "mama", NULL, NULL); /* another notify */
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_badger_mama_notify (GObject *object,
GParamSpec *pspec)
{
MyBadger *self;
self = MY_BADGER (object);
self->mama_notify_count++;
}
int
main (int argc, char **argv)
{
MyBadger * badger1, * badger2;
gpointer test;
g_print ("START: %s\n", argv[0]);
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
badger1 = g_object_new (MY_TYPE_BADGER, NULL);
badger2 = g_object_new (MY_TYPE_BADGER, NULL);
g_object_set (badger1, "mama", badger2, NULL);
g_assert_cmpuint (badger1->mama_notify_count, ==, 1);
g_assert_cmpuint (badger2->mama_notify_count, ==, 1);
g_object_get (badger1, "mama", &test, NULL);
g_assert (test == badger2);
g_object_unref (test);
g_object_unref (badger1);
g_object_unref (badger2);
return 0;
}

308
tests/refcount/signals.c Normal file
View file

@ -0,0 +1,308 @@
#include <glib.h>
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#define G_TYPE_TEST (my_test_get_type ())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
typedef struct _GTest GTest;
typedef struct _GTestClass GTestClass;
struct _GTest
{
GObject object;
gint value;
};
struct _GTestClass
{
GObjectClass parent_class;
void (*test_signal1) (GTest * test, gint an_int);
void (*test_signal2) (GTest * test, gint an_int);
gchar * (*test_signal3) (GTest * test, gint an_int);
};
static GType my_test_get_type (void);
static gboolean stopping;
/* Element signals and args */
enum
{
TEST_SIGNAL1,
TEST_SIGNAL2,
TEST_SIGNAL3,
/* add more above */
LAST_SIGNAL
};
enum
{
ARG_0,
ARG_TEST_PROP
};
static void my_test_class_init (GTestClass * klass);
static void my_test_init (GTest * test);
static void my_test_dispose (GObject * object);
static void signal2_handler (GTest * test, gint anint);
static gchar * signal3_handler (GTest * test, gint anint);
static void my_test_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void my_test_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GObjectClass *parent_class = NULL;
static guint my_test_signals[LAST_SIGNAL] = { 0 };
static GType
my_test_get_type (void)
{
static GType test_type = 0;
if (!test_type) {
const GTypeInfo test_info = {
sizeof (GTestClass),
NULL,
NULL,
(GClassInitFunc) my_test_class_init,
NULL,
NULL,
sizeof (GTest),
0,
(GInstanceInitFunc) my_test_init,
NULL
};
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest",
&test_info, 0);
}
return test_type;
}
static void
my_test_class_init (GTestClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
parent_class = g_type_class_ref (G_TYPE_OBJECT);
gobject_class->dispose = my_test_dispose;
gobject_class->set_property = my_test_set_property;
gobject_class->get_property = my_test_get_property;
my_test_signals[TEST_SIGNAL1] =
g_signal_new ("test-signal1", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GTestClass, test_signal1), NULL,
NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
my_test_signals[TEST_SIGNAL2] =
g_signal_new ("test-signal2", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GTestClass, test_signal2), NULL,
NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
my_test_signals[TEST_SIGNAL3] =
g_signal_new ("test-signal3", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GTestClass, test_signal3), NULL,
NULL, g_cclosure_marshal_generic, G_TYPE_STRING, 1, G_TYPE_INT);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TEST_PROP,
g_param_spec_int ("test-prop", "Test Prop", "Test property",
0, 1, 0, G_PARAM_READWRITE));
klass->test_signal2 = signal2_handler;
klass->test_signal3 = signal3_handler;
}
static void
my_test_init (GTest * test)
{
g_print ("init %p\n", test);
test->value = 0;
}
static void
my_test_dispose (GObject * object)
{
GTest *test;
test = MY_TEST (object);
g_print ("dispose %p!\n", test);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
my_test_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GTest *test;
test = MY_TEST (object);
switch (prop_id) {
case ARG_TEST_PROP:
test->value = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GTest *test;
test = MY_TEST (object);
switch (prop_id) {
case ARG_TEST_PROP:
g_value_set_int (value, test->value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_do_signal1 (GTest * test)
{
g_signal_emit (G_OBJECT (test), my_test_signals[TEST_SIGNAL1], 0, 0);
}
static void
signal2_handler (GTest * test, gint anint)
{
}
static void
my_test_do_signal2 (GTest * test)
{
g_signal_emit (G_OBJECT (test), my_test_signals[TEST_SIGNAL2], 0, 0);
}
static gchar *
signal3_handler (GTest * test, gint anint)
{
return g_strdup ("test");
}
static void
my_test_do_signal3 (GTest * test)
{
gchar *res;
g_signal_emit (G_OBJECT (test), my_test_signals[TEST_SIGNAL3], 0, 0, &res);
g_assert (res);
g_free (res);
}
static void
my_test_do_prop (GTest * test)
{
test->value = g_random_int ();
g_object_notify (G_OBJECT (test), "test-prop");
}
static gpointer
run_thread (GTest * test)
{
gint i = 1;
while (!g_atomic_int_get (&stopping)) {
if (TESTNUM == 1)
my_test_do_signal1 (test);
if (TESTNUM == 2)
my_test_do_signal2 (test);
if (TESTNUM == 3)
my_test_do_prop (test);
if (TESTNUM == 4)
my_test_do_signal3 (test);
if ((i++ % 10000) == 0) {
g_print (".");
g_thread_yield(); /* force context switch */
}
}
return NULL;
}
static void
notify (GObject *object, GParamSpec *spec, gpointer user_data)
{
gint value;
g_object_get (object, "test-prop", &value, NULL);
/*g_print ("+ %d", value);*/
}
int
main (int argc, char **argv)
{
gint i;
GTest *test1, *test2;
GArray *test_threads;
const gint n_threads = 1;
g_print ("START: %s\n", argv[0]);
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
test1 = g_object_new (G_TYPE_TEST, NULL);
test2 = g_object_new (G_TYPE_TEST, NULL);
g_signal_connect (test1, "notify::test-prop", G_CALLBACK (notify), NULL);
g_signal_connect (test1, "test-signal1", G_CALLBACK (notify), NULL);
g_signal_connect (test1, "test-signal2", G_CALLBACK (notify), NULL);
test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *));
stopping = FALSE;
for (i = 0; i < n_threads; i++) {
GThread *thread;
thread = g_thread_create ((GThreadFunc) run_thread, test1, TRUE, NULL);
g_array_append_val (test_threads, thread);
thread = g_thread_create ((GThreadFunc) run_thread, test2, TRUE, NULL);
g_array_append_val (test_threads, thread);
}
g_usleep (5000000);
g_atomic_int_set (&stopping, TRUE);
g_print ("\nstopping\n");
/* join all threads */
for (i = 0; i < 2 * n_threads; i++) {
GThread *thread;
thread = g_array_index (test_threads, GThread *, i);
g_thread_join (thread);
}
g_print ("stopped\n");
g_array_free (test_threads, TRUE);
g_object_unref (test1);
g_object_unref (test2);
return 0;
}

49
tests/run-assert-msg-test.sh Executable file
View file

@ -0,0 +1,49 @@
#! /bin/sh
fail ()
{
echo "Test failed: $*"
exit 1
}
echo_v ()
{
if [ "$verbose" = "1" ]; then
echo "$*"
fi
}
error_out=/dev/null
if [ "$1" = "-v" ]; then
verbose=1
error_out=/dev/stderr
fi
if [ -z "$LIBTOOL" ]; then
if [ -f ../libtool ]; then
LIBTOOL=../libtool
else
LIBTOOL=libtool
fi
fi
echo_v "Running assert-msg-test"
OUT=$(./assert-msg-test 2>&1) && fail "assert-msg-test should abort"
echo "$OUT" | grep -q '^GLib:ERROR:.*assert-msg-test.c:.*:.*main.*: assertion failed: (42 < 0)' || \
fail "does not print assertion message"
if ! type gdb >/dev/null 2>&1; then
echo_v "Skipped (no gdb installed)"
exit 0
fi
echo_v "Running gdb on assert-msg-test"
OUT=$($LIBTOOL --mode=execute gdb --batch -x "${srcdir:-.}/assert-msg-test.gdb" ./assert-msg-test 2> $error_out) || fail "failed to run gdb"
echo_v "Checking if assert message is in __glib_assert_msg"
# shellcheck disable=SC2016
if ! echo "$OUT" | grep -q '^$1.*"GLib:ERROR:.*assert-msg-test.c:.*:.*main.*: assertion failed: (42 < 0)"'; then
fail "__glib_assert_msg does not have assertion message"
fi
echo_v "All tests passed."

303
tests/slice-test.c Normal file
View file

@ -0,0 +1,303 @@
/* GLIB sliced memory - fast threaded memory chunk allocator
* Copyright (C) 2005 Tim Janik
*
* 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/>.
*/
#include <glib.h>
#include <stdio.h>
#include <string.h>
#define quick_rand32() (rand_accu = 1664525 * rand_accu + 1013904223, rand_accu)
static guint prime_size = 1021; /* 769; 509 */
static gboolean clean_memchunks = FALSE;
static guint number_of_blocks = 10000; /* total number of blocks allocated */
static guint number_of_repetitions = 10000; /* number of alloc+free repetitions */
static gboolean want_corruption = FALSE;
/* --- old memchunk prototypes (memchunks.c) --- */
GMemChunk* old_mem_chunk_new (const gchar *name,
gulong atom_size,
gulong area_size,
gint type);
void old_mem_chunk_destroy (GMemChunk *mem_chunk);
gpointer old_mem_chunk_alloc (GMemChunk *mem_chunk);
gpointer old_mem_chunk_alloc0 (GMemChunk *mem_chunk);
void old_mem_chunk_free (GMemChunk *mem_chunk,
gpointer mem);
void old_mem_chunk_clean (GMemChunk *mem_chunk);
void old_mem_chunk_reset (GMemChunk *mem_chunk);
void old_mem_chunk_print (GMemChunk *mem_chunk);
void old_mem_chunk_info (void);
#ifndef G_ALLOC_AND_FREE
#define G_ALLOC_AND_FREE 2
#endif
/* --- functions --- */
static inline int
corruption (void)
{
if (G_UNLIKELY (want_corruption))
{
/* corruption per call likelyness is about 1:4000000 */
guint32 r = g_random_int() % 8000009;
return r == 277 ? +1 : r == 281 ? -1 : 0;
}
return 0;
}
static inline gpointer
memchunk_alloc (GMemChunk **memchunkp,
guint size)
{
size = MAX (size, 1);
if (G_UNLIKELY (!*memchunkp))
*memchunkp = old_mem_chunk_new ("", size, 4096, G_ALLOC_AND_FREE);
return old_mem_chunk_alloc (*memchunkp);
}
static inline void
memchunk_free (GMemChunk *memchunk,
gpointer chunk)
{
old_mem_chunk_free (memchunk, chunk);
if (clean_memchunks)
old_mem_chunk_clean (memchunk);
}
static gpointer
test_memchunk_thread (gpointer data)
{
GMemChunk **memchunks;
guint i, j;
guint8 **ps;
guint *ss;
guint32 rand_accu = 2147483563;
/* initialize random numbers */
if (data)
rand_accu = *(guint32*) data;
else
{
GTimeVal rand_tv;
g_get_current_time (&rand_tv);
rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
}
/* prepare for memchunk creation */
memchunks = g_newa0 (GMemChunk*, prime_size);
ps = g_new (guint8*, number_of_blocks);
ss = g_new (guint, number_of_blocks);
/* create number_of_blocks random sizes */
for (i = 0; i < number_of_blocks; i++)
ss[i] = quick_rand32() % prime_size;
/* allocate number_of_blocks blocks */
for (i = 0; i < number_of_blocks; i++)
ps[i] = memchunk_alloc (&memchunks[ss[i]], ss[i]);
for (j = 0; j < number_of_repetitions; j++)
{
/* free number_of_blocks/2 blocks */
for (i = 0; i < number_of_blocks; i += 2)
memchunk_free (memchunks[ss[i]], ps[i]);
/* allocate number_of_blocks/2 blocks with new sizes */
for (i = 0; i < number_of_blocks; i += 2)
{
ss[i] = quick_rand32() % prime_size;
ps[i] = memchunk_alloc (&memchunks[ss[i]], ss[i]);
}
}
/* free number_of_blocks blocks */
for (i = 0; i < number_of_blocks; i++)
memchunk_free (memchunks[ss[i]], ps[i]);
/* alloc and free many equally sized chunks in a row */
for (i = 0; i < number_of_repetitions; i++)
{
guint sz = quick_rand32() % prime_size;
guint k = number_of_blocks / 100;
for (j = 0; j < k; j++)
ps[j] = memchunk_alloc (&memchunks[sz], sz);
for (j = 0; j < k; j++)
memchunk_free (memchunks[sz], ps[j]);
}
/* cleanout memchunks */
for (i = 0; i < prime_size; i++)
if (memchunks[i])
old_mem_chunk_destroy (memchunks[i]);
g_free (ps);
g_free (ss);
return NULL;
}
static gpointer
test_sliced_mem_thread (gpointer data)
{
guint32 rand_accu = 2147483563;
guint i, j;
guint8 **ps;
guint *ss;
/* initialize random numbers */
if (data)
rand_accu = *(guint32*) data;
else
{
GTimeVal rand_tv;
g_get_current_time (&rand_tv);
rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
}
ps = g_new (guint8*, number_of_blocks);
ss = g_new (guint, number_of_blocks);
/* create number_of_blocks random sizes */
for (i = 0; i < number_of_blocks; i++)
ss[i] = quick_rand32() % prime_size;
/* allocate number_of_blocks blocks */
for (i = 0; i < number_of_blocks; i++)
ps[i] = g_slice_alloc (ss[i] + corruption());
for (j = 0; j < number_of_repetitions; j++)
{
/* free number_of_blocks/2 blocks */
for (i = 0; i < number_of_blocks; i += 2)
g_slice_free1 (ss[i] + corruption(), ps[i] + corruption());
/* allocate number_of_blocks/2 blocks with new sizes */
for (i = 0; i < number_of_blocks; i += 2)
{
ss[i] = quick_rand32() % prime_size;
ps[i] = g_slice_alloc (ss[i] + corruption());
}
}
/* free number_of_blocks blocks */
for (i = 0; i < number_of_blocks; i++)
g_slice_free1 (ss[i] + corruption(), ps[i] + corruption());
/* alloc and free many equally sized chunks in a row */
for (i = 0; i < number_of_repetitions; i++)
{
guint sz = quick_rand32() % prime_size;
guint k = number_of_blocks / 100;
for (j = 0; j < k; j++)
ps[j] = g_slice_alloc (sz + corruption());
for (j = 0; j < k; j++)
g_slice_free1 (sz + corruption(), ps[j] + corruption());
}
g_free (ps);
g_free (ss);
return NULL;
}
static void
usage (void)
{
g_print ("Usage: slice-test [n_threads] [G|S|M|O][f][c][~] [maxblocksize] [seed]\n");
}
int
main (int argc,
char *argv[])
{
guint seed32, *seedp = NULL;
gboolean ccounters = FALSE, use_memchunks = FALSE;
guint n_threads = 1;
const gchar *mode = "slab allocator + magazine cache", *emode = " ";
if (argc > 1)
n_threads = g_ascii_strtoull (argv[1], NULL, 10);
if (argc > 2)
{
guint i, l = strlen (argv[2]);
for (i = 0; i < l; i++)
switch (argv[2][i])
{
case 'G': /* GLib mode */
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, FALSE);
mode = "slab allocator + magazine cache";
break;
case 'S': /* slab mode */
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, TRUE);
mode = "slab allocator";
break;
case 'M': /* malloc mode */
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
mode = "system malloc";
break;
case 'O': /* old memchunks */
use_memchunks = TRUE;
mode = "old memchunks";
break;
case 'f': /* eager freeing */
g_slice_set_config (G_SLICE_CONFIG_WORKING_SET_MSECS, 0);
clean_memchunks = TRUE;
emode = " with eager freeing";
break;
case 'c': /* print contention counters */
ccounters = TRUE;
break;
case '~':
want_corruption = TRUE; /* force occasional corruption */
break;
default:
usage();
return 1;
}
}
if (argc > 3)
prime_size = g_ascii_strtoull (argv[3], NULL, 10);
if (argc > 4)
{
seed32 = g_ascii_strtoull (argv[4], NULL, 10);
seedp = &seed32;
}
if (argc <= 1)
usage();
{
gchar strseed[64] = "<random>";
GThread **threads;
guint i;
if (seedp)
g_snprintf (strseed, 64, "%u", *seedp);
g_print ("Starting %d threads allocating random blocks <= %u bytes with seed=%s using %s%s\n", n_threads, prime_size, strseed, mode, emode);
threads = g_alloca (sizeof(GThread*) * n_threads);
if (!use_memchunks)
for (i = 0; i < n_threads; i++)
threads[i] = g_thread_create (test_sliced_mem_thread, seedp, TRUE, NULL);
else
{
for (i = 0; i < n_threads; i++)
threads[i] = g_thread_create (test_memchunk_thread, seedp, TRUE, NULL);
}
for (i = 0; i < n_threads; i++)
g_thread_join (threads[i]);
if (ccounters)
{
guint n, n_chunks = g_slice_get_config (G_SLICE_CONFIG_CHUNK_SIZES);
g_print (" ChunkSize | MagazineSize | Contention\n");
for (i = 0; i < n_chunks; i++)
{
gint64 *vals = g_slice_get_config_state (G_SLICE_CONFIG_CONTENTION_COUNTER, i, &n);
g_print (" %9" G_GINT64_FORMAT " | %9" G_GINT64_FORMAT " | %9" G_GINT64_FORMAT "\n", vals[0], vals[2], vals[1]);
g_free (vals);
}
}
else
g_print ("Done.\n");
return 0;
}
}

166
tests/slice-threadinit.c Normal file
View file

@ -0,0 +1,166 @@
/* slice-threadinit.c - test GSlice across g_thread_init
* Copyright (C) 2007 Tim Janik
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
*
* This work 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.
*
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <glib.h>
#define N_PAGES (101) /* number of pages to sample */
#define SAMPLE_SIZE (7)
#define PAGE_SIZE (128) /* must be <= minimum GSlice alignment block */
#define MAGAZINE_PROBES { 97, 265, 347 } /* block sizes hopefully unused by g_thread_init */
#define MAX_PROBE_TRIALS (1031) /* must be >= maximum magazine size */
#define ALIGN(size, base) ((base) * (gsize) (((size) + (base) - 1) / (base)))
static struct {
void *page;
void *sample;
} pages[N_PAGES] = { { NULL, NULL }, };
static const guint magazine_probes[] = MAGAZINE_PROBES;
#define N_MAGAZINE_PROBES G_N_ELEMENTS (magazine_probes)
static void
release_trash_list (GSList **trash_list,
gsize block_size)
{
while (*trash_list)
{
g_slice_free1 (block_size, (*trash_list)->data);
*trash_list = g_slist_delete_link (*trash_list, *trash_list);
}
}
static GSList *free_list = NULL;
static gboolean
allocate_from_known_page (void)
{
guint i, j, n_trials = N_PAGES * PAGE_SIZE / SAMPLE_SIZE; /* upper bound */
for (i = 0; i < n_trials; i++)
{
void *b = g_slice_alloc (SAMPLE_SIZE);
void *p = (void*) (PAGE_SIZE * ((gsize) b / PAGE_SIZE));
free_list = g_slist_prepend (free_list, b);
/* find page */
for (j = 0; j < N_PAGES; j++)
if (pages[j].page == p)
return TRUE;
}
return FALSE;
}
int
main (int argc,
char *argv[])
{
gsize j, n_pages = 0;
void *mps[N_MAGAZINE_PROBES];
/* probe some magazine sizes */
for (j = 0; j < N_MAGAZINE_PROBES; j++)
mps[j] = g_slice_alloc (magazine_probes[j]);
/* mps[*] now contains pointers to allocated slices */
/* allocate blocks from N_PAGES different pages */
while (n_pages < N_PAGES)
{
void *b = g_slice_alloc (SAMPLE_SIZE);
void *p = (void*) (PAGE_SIZE * ((gsize) b / PAGE_SIZE));
for (j = 0; j < N_PAGES; j++)
if (pages[j].page == p)
break;
if (j < N_PAGES) /* known page */
free_list = g_slist_prepend (free_list, b);
else /* new page */
{
j = n_pages++;
pages[j].page = p;
pages[j].sample = b;
}
}
/* release intermediate allocations */
release_trash_list (&free_list, SAMPLE_SIZE);
/* ensure that we can allocate from known pages */
if (!allocate_from_known_page())
g_error ("failed to allocate from magazine/page cache (before g_thread_init)");
/* release intermediate allocations */
release_trash_list (&free_list, SAMPLE_SIZE);
/* release magazine probes to be retained */
for (j = 0; j < N_MAGAZINE_PROBES; j++)
g_slice_free1 (magazine_probes[j], mps[j]);
/* mps[*] now contains pointers to releaed slices */
/* ensure probes were retained */
for (j = 0; j < N_MAGAZINE_PROBES; j++)
{
GSList *trash = NULL;
guint k;
for (k = 0; k < MAX_PROBE_TRIALS; k++)
{
void *mem = g_slice_alloc (magazine_probes[j]);
if (mem == mps[j])
break; /* reallocated previously freed slice */
trash = g_slist_prepend (trash, mem);
}
release_trash_list (&trash, magazine_probes[j]);
if (k >= MAX_PROBE_TRIALS) /* failed to reallocate slice */
g_error ("failed to reallocate slice from magazine (before g_thread_init): size=%d", magazine_probes[j]);
}
/* mps[*] now contains pointers to reallocated slices */
/* release magazine probes to be retained across g_thread_init */
for (j = 0; j < N_MAGAZINE_PROBES; j++)
g_slice_free1 (magazine_probes[j], mps[j]);
/* mps[*] now contains pointers to released slices */
/* initialize threading (should retain allocator state) */
g_thread_init (NULL);
/* ensure probes were retained */
for (j = 0; j < N_MAGAZINE_PROBES; j++)
{
GSList *trash = NULL;
guint k;
for (k = 0; k < MAX_PROBE_TRIALS; k++)
{
void *mem = g_slice_alloc (magazine_probes[j]);
if (mem == mps[j])
break; /* reallocated previously freed slice */
trash = g_slist_prepend (trash, mem);
}
release_trash_list (&trash, magazine_probes[j]);
if (k >= MAX_PROBE_TRIALS) /* failed to reallocate slice */
g_error ("failed to reallocate slice from magazine (after g_thread_init): size=%d", magazine_probes[j]);
}
/* mps[*] now contains pointers to reallocated slices */
/* ensure that we can allocate from known pages */
if (!allocate_from_known_page())
g_error ("failed to allocate from magazine/page cache (after g_thread_init)");
/* some cleanups */
for (j = 0; j < N_MAGAZINE_PROBES; j++)
g_slice_free1 (magazine_probes[j], mps[j]);
release_trash_list (&free_list, SAMPLE_SIZE);
return 0;
}

View file

@ -0,0 +1,88 @@
#include <windows.h>
#include <io.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __CYGWIN__
/* For read() and write() */
#include <unistd.h>
/* Cygwin does not prototype __argc and __argv in stdlib.h */
extern int __argc;
extern char** __argv;
#endif
int _stdcall
WinMain (struct HINSTANCE__ *hInstance,
struct HINSTANCE__ *hPrevInstance,
char *lpszCmdLine,
int nCmdShow)
{
if (__argc >= 2 && strcmp (__argv[1], "print_argv0") == 0)
{
printf ("%s", __argv[0]);
}
else if (__argc <= 2)
{
printf ("This is stdout\n");
fflush (stdout);
fprintf (stderr, "This is stderr\n");
fflush (stderr);
}
else if (__argc == 4 && strcmp (__argv[1], "pipes") == 0)
{
int infd = atoi (__argv[2]);
int outfd = atoi (__argv[3]);
int k, n;
char buf[100] = {0};
if (infd < 0 || outfd < 0)
{
printf ("spawn-test-win32-gui: illegal fds on command line %s",
lpszCmdLine);
exit (1);
}
n = strlen ("Hello there");
if (write (outfd, &n, sizeof (n)) == -1 ||
write (outfd, "Hello there", n) == -1)
{
int errsv = errno;
printf ("spawn-test-win32-gui: Write error: %s", strerror (errsv));
exit (1);
}
if ((k = read (infd, &n, sizeof (n))) != sizeof (n))
{
printf ("spawn-test-win32-gui: Got only %d bytes, wanted %d",
k, (int)sizeof (n));
exit (1);
}
printf ("spawn-test-win32-gui: Parent says %d bytes to read", n);
if ((k = read (infd, buf, n)) != n)
{
int errsv = errno;
if (k == -1)
printf ("spawn-test-win32-gui: Read error: %s", strerror (errsv));
else
printf ("spawn-test-win32-gui: Got only %d bytes", k);
exit (1);
}
n = strlen ("See ya");
if (write (outfd, &n, sizeof (n)) == -1 ||
write (outfd, "See ya", n) == -1)
{
int errsv = errno;
printf ("spawn-test-win32-gui: Write error: %s", strerror (errsv));
exit (1);
}
}
return 0;
}

352
tests/spawn-test.c Normal file
View file

@ -0,0 +1,352 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* 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/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <glib.h>
#include <glib/gstdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef G_OS_WIN32
#include <fcntl.h>
#include <io.h>
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
#endif
static void
run_tests (const gchar* argv0)
{
GError *err = NULL;
gchar *output = NULL;
gchar *erroutput = NULL;
gchar *dirname = g_path_get_dirname (argv0);
#ifdef G_OS_WIN32
int pipedown[2], pipeup[2];
gchar **argv = 0;
gchar spawn_binary[1000] = {0};
gchar full_cmdline[1000] = {0};
g_snprintf (spawn_binary, sizeof (spawn_binary), "%s\\spawn-test-win32-gui.exe", dirname);
#endif
g_free (dirname);
err = NULL;
if (!g_spawn_command_line_sync ("nonexistent_application foo 'bar baz' blah blah",
NULL, NULL, NULL,
&err))
{
g_error_free (err);
}
else
{
g_warning ("no error for sync spawn of nonexistent application");
exit (1);
}
err = NULL;
if (!g_spawn_command_line_async ("nonexistent_application foo bar baz \"blah blah\"",
&err))
{
g_error_free (err);
}
else
{
g_warning ("no error for async spawn of nonexistent application");
exit (1);
}
err = NULL;
#ifdef G_OS_UNIX
if (!g_spawn_command_line_sync ("/bin/sh -c 'echo hello'",
&output, NULL, NULL,
&err))
{
fprintf (stderr, "Error: %s\n", err->message);
g_error_free (err);
exit (1);
}
else
{
g_assert (output != NULL);
if (strcmp (output, "hello\n") != 0)
{
printf ("output was '%s', should have been 'hello'\n",
output);
exit (1);
}
g_free (output);
output = NULL;
}
#endif
/* Running sort synchronously, collecting its output. 'sort' command is selected
* because it is non-builtin command on both unix and win32 with well-defined stdout behaviour.
*/
g_file_set_contents ("spawn-test-created-file.txt", "line first\nline 2\nline last\n", -1, &err);
g_assert_no_error(err);
if (!g_spawn_command_line_sync ("sort spawn-test-created-file.txt",
&output, &erroutput, NULL,
&err))
{
fprintf (stderr, "Error: %s\n", err->message);
g_error_free (err);
exit (1);
}
else
{
g_assert (output != NULL);
g_assert (erroutput != NULL);
if (strstr (output, "\nline first") == 0)
{
printf ("output was '%s', should have contained 'line first' in second line\n",
output);
exit (1);
}
if (erroutput[0] != '\0')
{
printf ("error output was '%s', should have been empty\n",
erroutput);
exit (1);
}
g_free (output);
output = NULL;
g_free (erroutput);
erroutput = NULL;
g_unlink ("spawn-test-created-file.txt");
}
if (!g_spawn_command_line_sync ("sort non-existing-file.txt",
NULL, &erroutput, NULL,
&err))
{
fprintf (stderr, "Error: %s\n", err->message);
g_error_free (err);
exit (1);
}
else
{
g_assert (erroutput != NULL);
if (erroutput[0] == '\0')
{
printf ("erroutput was empty, expected contain error message about non-existing-file.txt\n");
exit (1);
}
g_free (erroutput);
erroutput = NULL;
}
#ifdef G_OS_WIN32
printf ("Running spawn-test-win32-gui in various ways.\n");
printf ("First asynchronously (without wait).\n");
g_snprintf (full_cmdline, sizeof (full_cmdline), "'%s' 1", spawn_binary);
if (!g_spawn_command_line_async (full_cmdline, &err))
{
fprintf (stderr, "Error: %s\n", err->message);
g_error_free (err);
exit (1);
}
printf ("Now synchronously, collecting its output.\n");
g_snprintf (full_cmdline, sizeof (full_cmdline), "'%s' 2", spawn_binary);
if (!g_spawn_command_line_sync (full_cmdline,
&output, &erroutput, NULL,
&err))
{
fprintf (stderr, "Error: %s\n", err->message);
g_error_free (err);
exit (1);
}
else
{
g_assert (output != NULL);
g_assert (erroutput != NULL);
if (strcmp (output, "This is stdout\r\n") != 0)
{
printf ("output was '%s', should have been 'This is stdout'\n",
g_strescape (output, NULL));
exit (1);
}
if (strcmp (erroutput, "This is stderr\r\n") != 0)
{
printf ("error output was '%s', should have been 'This is stderr'\n",
g_strescape (erroutput, NULL));
exit (1);
}
g_free (output);
output = NULL;
g_free (erroutput);
erroutput = NULL;
}
printf ("Now with G_SPAWN_FILE_AND_ARGV_ZERO.\n");
g_snprintf (full_cmdline, sizeof (full_cmdline), "'%s' this-should-be-argv-zero print_argv0", spawn_binary);
if (!g_shell_parse_argv (full_cmdline, NULL, &argv, &err))
{
fprintf (stderr, "Error parsing command line? %s\n", err->message);
g_error_free (err);
exit (1);
}
if (!g_spawn_sync (NULL, argv, NULL,
G_SPAWN_FILE_AND_ARGV_ZERO,
NULL, NULL, &output, NULL, NULL,
&err))
{
fprintf (stderr, "Error: %s\n", err->message);
g_error_free (err);
exit (1);
}
else
{
if (strcmp (output, "this-should-be-argv-zero") != 0)
{
printf ("output was '%s', should have been 'this-should-be-argv-zero'\n", output);
exit (1);
}
g_free (output);
output = NULL;
}
printf ("Now talking to it through pipes.\n");
if (pipe (pipedown) < 0 ||
pipe (pipeup) < 0)
{
fprintf (stderr, "Could not create pipes\n");
exit (1);
}
g_snprintf (full_cmdline, sizeof (full_cmdline), "'%s' pipes %d %d", spawn_binary, pipedown[0], pipeup[1]);
if (!g_shell_parse_argv (full_cmdline,
NULL, &argv,
&err))
{
fprintf (stderr, "Error parsing command line? %s\n", err->message);
g_error_free (err);
exit (1);
}
if (!g_spawn_async (NULL, argv, NULL,
G_SPAWN_LEAVE_DESCRIPTORS_OPEN |
G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL, NULL,
&err))
{
fprintf (stderr, "Error: %s\n", err->message);
g_error_free (err);
exit (1);
}
else
{
int k, n;
char buf[100];
if ((k = read (pipeup[0], &n, sizeof (n))) != sizeof (n))
{
int errsv = errno;
if (k == -1)
fprintf (stderr, "Read error: %s\n", g_strerror (errsv));
else
fprintf (stderr, "Wanted to read %d bytes, got %d\n",
(int)sizeof (n), k);
exit (1);
}
if ((k = read (pipeup[0], buf, n)) != n)
{
int errsv = errno;
if (k == -1)
fprintf (stderr, "Read error: %s\n", g_strerror (errsv));
else
fprintf (stderr, "Wanted to read %d bytes, got %d\n",
n, k);
exit (1);
}
n = strlen ("Bye then");
if (write (pipedown[1], &n, sizeof (n)) == -1 ||
write (pipedown[1], "Bye then", n) == -1)
{
int errsv = errno;
fprintf (stderr, "Write error: %s\n", g_strerror (errsv));
exit (1);
}
if ((k = read (pipeup[0], &n, sizeof (n))) != sizeof (n))
{
int errsv = errno;
if (k == -1)
fprintf (stderr, "Read error: %s\n", g_strerror (errsv));
else
fprintf (stderr, "Wanted to read %d bytes, got %d\n",
(int)sizeof (n), k);
exit (1);
}
if (n != strlen ("See ya"))
{
printf ("child wrote %d bytes, expected %d", n, (int) strlen ("See ya"));
exit (1);
}
if ((k = read (pipeup[0], buf, n)) != n)
{
int errsv = errno;
if (k == -1)
fprintf (stderr, "Read error: %s\n", g_strerror (errsv));
else
fprintf (stderr, "Wanted to read %d bytes, got %d\n",
n, k);
exit (1);
}
buf[n] = '\0';
if (strcmp (buf, "See ya") != 0)
{
printf ("output was '%s', should have been 'See ya'\n", buf);
exit (1);
}
}
#endif
}
int
main (int argc,
char *argv[])
{
run_tests (argv[0]);
return 0;
}

400
tests/thread-test.c Normal file
View file

@ -0,0 +1,400 @@
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <glib.h>
/* GMutex */
static GMutex test_g_mutex_mutex;
static guint test_g_mutex_int = 0;
static gboolean test_g_mutex_thread_ready;
G_LOCK_DEFINE_STATIC (test_g_mutex);
static gpointer
test_g_mutex_thread (gpointer data)
{
g_assert (GPOINTER_TO_INT (data) == 42);
g_assert (g_mutex_trylock (&test_g_mutex_mutex) == FALSE);
g_assert (G_TRYLOCK (test_g_mutex) == FALSE);
test_g_mutex_thread_ready = TRUE;
g_mutex_lock (&test_g_mutex_mutex);
g_assert (test_g_mutex_int == 42);
g_mutex_unlock (&test_g_mutex_mutex);
return GINT_TO_POINTER (41);
}
static void
test_g_mutex (void)
{
GThread *thread;
g_assert (g_mutex_trylock (&test_g_mutex_mutex));
g_assert (G_TRYLOCK (test_g_mutex));
test_g_mutex_thread_ready = FALSE;
thread = g_thread_create (test_g_mutex_thread, GINT_TO_POINTER (42),
TRUE, NULL);
/* This busy wait is only for testing purposes and not an example of
* good code!*/
while (!test_g_mutex_thread_ready)
g_usleep (G_USEC_PER_SEC / 5);
test_g_mutex_int = 42;
G_UNLOCK (test_g_mutex);
g_mutex_unlock (&test_g_mutex_mutex);
g_assert (GPOINTER_TO_INT (g_thread_join (thread)) == 41);
}
/* GStaticRecMutex */
static GStaticRecMutex test_g_static_rec_mutex_mutex = G_STATIC_REC_MUTEX_INIT;
static guint test_g_static_rec_mutex_int = 0;
static gboolean test_g_static_rec_mutex_thread_ready;
static gpointer
test_g_static_rec_mutex_thread (gpointer data)
{
g_assert (GPOINTER_TO_INT (data) == 42);
g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex)
== FALSE);
test_g_static_rec_mutex_thread_ready = TRUE;
g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex);
g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex);
g_assert (test_g_static_rec_mutex_int == 42);
test_g_static_rec_mutex_thread_ready = FALSE;
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
g_thread_exit (GINT_TO_POINTER (43));
g_assert_not_reached ();
return NULL;
}
static void
test_g_static_rec_mutex (void)
{
GThread *thread;
g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex));
test_g_static_rec_mutex_thread_ready = FALSE;
thread = g_thread_create (test_g_static_rec_mutex_thread,
GINT_TO_POINTER (42), TRUE, NULL);
/* This busy wait is only for testing purposes and not an example of
* good code!*/
while (!test_g_static_rec_mutex_thread_ready)
g_usleep (G_USEC_PER_SEC / 5);
g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex));
test_g_static_rec_mutex_int = 41;
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
test_g_static_rec_mutex_int = 42;
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
/* This busy wait is only for testing purposes and not an example of
* good code!*/
while (test_g_static_rec_mutex_thread_ready)
g_usleep (G_USEC_PER_SEC / 5);
g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex);
test_g_static_rec_mutex_int = 0;
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
g_assert (GPOINTER_TO_INT (g_thread_join (thread)) == 43);
}
/* GStaticPrivate */
#define THREADS 10
static GStaticPrivate test_g_static_private_private1 = G_STATIC_PRIVATE_INIT;
static GStaticPrivate test_g_static_private_private2 = G_STATIC_PRIVATE_INIT;
static GMutex test_g_static_private_mutex;
static guint test_g_static_private_counter = 0;
static guint test_g_static_private_ready = 0;
static gpointer
test_g_static_private_constructor (void)
{
g_mutex_lock (&test_g_static_private_mutex);
test_g_static_private_counter++;
g_mutex_unlock (&test_g_static_private_mutex);
return g_new (guint,1);
}
static void
test_g_static_private_destructor (gpointer data)
{
g_mutex_lock (&test_g_static_private_mutex);
test_g_static_private_counter--;
g_mutex_unlock (&test_g_static_private_mutex);
g_free (data);
}
static gpointer
test_g_static_private_thread (gpointer data)
{
guint number = GPOINTER_TO_INT (data);
guint i;
guint *private1, *private2;
for (i = 0; i < 10; i++)
{
number = number * 11 + 1; /* A very simple and bad RNG ;-) */
private1 = g_static_private_get (&test_g_static_private_private1);
if (!private1 || number % 7 > 3)
{
private1 = test_g_static_private_constructor ();
g_static_private_set (&test_g_static_private_private1, private1,
test_g_static_private_destructor);
}
*private1 = number;
private2 = g_static_private_get (&test_g_static_private_private2);
if (!private2 || number % 13 > 5)
{
private2 = test_g_static_private_constructor ();
g_static_private_set (&test_g_static_private_private2, private2,
test_g_static_private_destructor);
}
*private2 = number * 2;
g_usleep (G_USEC_PER_SEC / 5);
g_assert (number == *private1);
g_assert (number * 2 == *private2);
}
g_mutex_lock (&test_g_static_private_mutex);
test_g_static_private_ready++;
g_mutex_unlock (&test_g_static_private_mutex);
/* Busy wait is not nice but that's just a test */
while (test_g_static_private_ready != 0)
g_usleep (G_USEC_PER_SEC / 5);
for (i = 0; i < 10; i++)
{
private2 = g_static_private_get (&test_g_static_private_private2);
number = number * 11 + 1; /* A very simple and bad RNG ;-) */
if (!private2 || number % 13 > 5)
{
private2 = test_g_static_private_constructor ();
g_static_private_set (&test_g_static_private_private2, private2,
test_g_static_private_destructor);
}
*private2 = number * 2;
g_usleep (G_USEC_PER_SEC / 5);
g_assert (number * 2 == *private2);
}
return GINT_TO_POINTER (GPOINTER_TO_INT (data) * 3);
}
static void
test_g_static_private (void)
{
GThread *threads[THREADS];
guint i;
test_g_static_private_ready = 0;
for (i = 0; i < THREADS; i++)
{
threads[i] = g_thread_create (test_g_static_private_thread,
GINT_TO_POINTER (i), TRUE, NULL);
}
/* Busy wait is not nice but that's just a test */
while (test_g_static_private_ready != THREADS)
g_usleep (G_USEC_PER_SEC / 5);
/* Reuse the static private */
g_static_private_free (&test_g_static_private_private2);
g_static_private_init (&test_g_static_private_private2);
test_g_static_private_ready = 0;
for (i = 0; i < THREADS; i++)
g_assert (GPOINTER_TO_UINT (g_thread_join (threads[i])) == i * 3);
g_assert (test_g_static_private_counter == 0);
}
/* GStaticRWLock */
/* -1 = writing; >0 = # of readers */
static gint test_g_static_rw_lock_state = 0;
G_LOCK_DEFINE (test_g_static_rw_lock_state);
static gboolean test_g_static_rw_lock_run = TRUE;
static GStaticRWLock test_g_static_rw_lock_lock = G_STATIC_RW_LOCK_INIT;
static gpointer
test_g_static_rw_lock_thread (gpointer data)
{
while (test_g_static_rw_lock_run)
{
if (g_random_double() > .2) /* I'm a reader */
{
if (g_random_double() > .2) /* I'll block */
g_static_rw_lock_reader_lock (&test_g_static_rw_lock_lock);
else /* I'll only try */
if (!g_static_rw_lock_reader_trylock (&test_g_static_rw_lock_lock))
continue;
G_LOCK (test_g_static_rw_lock_state);
g_assert (test_g_static_rw_lock_state >= 0);
test_g_static_rw_lock_state++;
G_UNLOCK (test_g_static_rw_lock_state);
g_usleep (g_random_int_range (20,1000));
G_LOCK (test_g_static_rw_lock_state);
test_g_static_rw_lock_state--;
G_UNLOCK (test_g_static_rw_lock_state);
g_static_rw_lock_reader_unlock (&test_g_static_rw_lock_lock);
}
else /* I'm a writer */
{
if (g_random_double() > .2) /* I'll block */
g_static_rw_lock_writer_lock (&test_g_static_rw_lock_lock);
else /* I'll only try */
if (!g_static_rw_lock_writer_trylock (&test_g_static_rw_lock_lock))
continue;
G_LOCK (test_g_static_rw_lock_state);
g_assert (test_g_static_rw_lock_state == 0);
test_g_static_rw_lock_state = -1;
G_UNLOCK (test_g_static_rw_lock_state);
g_usleep (g_random_int_range (20,1000));
G_LOCK (test_g_static_rw_lock_state);
test_g_static_rw_lock_state = 0;
G_UNLOCK (test_g_static_rw_lock_state);
g_static_rw_lock_writer_unlock (&test_g_static_rw_lock_lock);
}
}
return NULL;
}
static void
test_g_static_rw_lock (void)
{
GThread *threads[THREADS];
guint i;
for (i = 0; i < THREADS; i++)
{
threads[i] = g_thread_create (test_g_static_rw_lock_thread,
NULL, TRUE, NULL);
}
g_usleep (G_USEC_PER_SEC * 5);
test_g_static_rw_lock_run = FALSE;
for (i = 0; i < THREADS; i++)
{
g_thread_join (threads[i]);
}
g_assert (test_g_static_rw_lock_state == 0);
}
#define G_ONCE_SIZE 100
#define G_ONCE_THREADS 10
G_LOCK_DEFINE (test_g_once);
static guint test_g_once_guint_array[G_ONCE_SIZE];
static GOnce test_g_once_array[G_ONCE_SIZE];
static gpointer
test_g_once_init_func(gpointer arg)
{
guint *count = arg;
g_usleep (g_random_int_range (20,1000));
(*count)++;
g_usleep (g_random_int_range (20,1000));
return arg;
}
static gpointer
test_g_once_thread (gpointer ignore)
{
guint i;
G_LOCK (test_g_once);
/* Don't start before all threads are created */
G_UNLOCK (test_g_once);
for (i = 0; i < 1000; i++)
{
guint pos = g_random_int_range (0, G_ONCE_SIZE);
gpointer ret = g_once (test_g_once_array + pos, test_g_once_init_func,
test_g_once_guint_array + pos);
g_assert (ret == test_g_once_guint_array + pos);
}
/* Make sure, that all counters are touched at least once */
for (i = 0; i < G_ONCE_SIZE; i++)
{
gpointer ret = g_once (test_g_once_array + i, test_g_once_init_func,
test_g_once_guint_array + i);
g_assert (ret == test_g_once_guint_array + i);
}
return NULL;
}
static void
test_g_thread_once (void)
{
static GOnce once_init = G_ONCE_INIT;
GThread *threads[G_ONCE_THREADS];
guint i;
for (i = 0; i < G_ONCE_SIZE; i++)
{
test_g_once_array[i] = once_init;
test_g_once_guint_array[i] = i;
}
G_LOCK (test_g_once);
for (i = 0; i < G_ONCE_THREADS; i++)
{
threads[i] = g_thread_create (test_g_once_thread, GUINT_TO_POINTER(i%2),
TRUE, NULL);
}
G_UNLOCK (test_g_once);
for (i = 0; i < G_ONCE_THREADS; i++)
{
g_thread_join (threads[i]);
}
for (i = 0; i < G_ONCE_SIZE; i++)
{
g_assert (test_g_once_guint_array[i] == i + 1);
}
}
/* run all the tests */
static void
run_all_tests (void)
{
test_g_mutex ();
test_g_static_rec_mutex ();
test_g_static_private ();
test_g_static_rw_lock ();
test_g_thread_once ();
}
int
main (int argc,
char *argv[])
{
run_all_tests ();
/* Now we rerun all tests, but this time we fool the system into
* thinking, that the available thread system is not native, but
* userprovided. */
g_thread_use_default_impl = FALSE;
run_all_tests ();
/* XXX: And this shows how silly the above non-native tests are */
g_static_rw_lock_free (&test_g_static_rw_lock_lock);
g_static_rec_mutex_free (&test_g_static_rec_mutex_mutex);
g_static_private_free (&test_g_static_private_private2);
return 0;
}

465
tests/threadpool-test.c Normal file
View file

@ -0,0 +1,465 @@
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include "config.h"
#include <glib.h>
/* #define DEBUG 1 */
#ifdef DEBUG
# define DEBUG_MSG(args) g_printerr args ; g_printerr ("\n");
#else
# define DEBUG_MSG(x)
#endif
#define WAIT 5 /* seconds */
#define MAX_THREADS 10
/* if > 0 the test will run continuously (since the test ends when
* thread count is 0), if -1 it means no limit to unused threads
* if 0 then no unused threads are possible */
#define MAX_UNUSED_THREADS -1
G_LOCK_DEFINE_STATIC (thread_counter_pools);
static gulong abs_thread_counter = 0;
static gulong running_thread_counter = 0;
static gulong leftover_task_counter = 0;
G_LOCK_DEFINE_STATIC (last_thread);
static guint last_thread_id = 0;
G_LOCK_DEFINE_STATIC (thread_counter_sort);
static gulong sort_thread_counter = 0;
static GThreadPool *idle_pool = NULL;
static GMainLoop *main_loop = NULL;
static void
test_thread_functions (void)
{
gint max_unused_threads;
guint max_idle_time;
/* This function attempts to call functions which don't need a
* threadpool to operate to make sure no uninitialised pointers
* accessed and no errors occur.
*/
max_unused_threads = 3;
DEBUG_MSG (("[funcs] Setting max unused threads to %d",
max_unused_threads));
g_thread_pool_set_max_unused_threads (max_unused_threads);
DEBUG_MSG (("[funcs] Getting max unused threads = %d",
g_thread_pool_get_max_unused_threads ()));
g_assert (g_thread_pool_get_max_unused_threads() == max_unused_threads);
DEBUG_MSG (("[funcs] Getting num unused threads = %d",
g_thread_pool_get_num_unused_threads ()));
g_assert (g_thread_pool_get_num_unused_threads () == 0);
DEBUG_MSG (("[funcs] Stopping unused threads"));
g_thread_pool_stop_unused_threads ();
max_idle_time = 10 * G_USEC_PER_SEC;
DEBUG_MSG (("[funcs] Setting max idle time to %d",
max_idle_time));
g_thread_pool_set_max_idle_time (max_idle_time);
DEBUG_MSG (("[funcs] Getting max idle time = %d",
g_thread_pool_get_max_idle_time ()));
g_assert (g_thread_pool_get_max_idle_time () == max_idle_time);
DEBUG_MSG (("[funcs] Setting max idle time to 0"));
g_thread_pool_set_max_idle_time (0);
DEBUG_MSG (("[funcs] Getting max idle time = %d",
g_thread_pool_get_max_idle_time ()));
g_assert (g_thread_pool_get_max_idle_time () == 0);
}
static void
test_thread_stop_unused (void)
{
GThreadPool *pool;
guint i;
guint limit = 100;
/* Spawn a few threads. */
g_thread_pool_set_max_unused_threads (-1);
pool = g_thread_pool_new ((GFunc) g_usleep, NULL, -1, FALSE, NULL);
for (i = 0; i < limit; i++)
g_thread_pool_push (pool, GUINT_TO_POINTER (1000), NULL);
DEBUG_MSG (("[unused] ===> pushed %d threads onto the idle pool",
limit));
/* Wait for the threads to migrate. */
g_usleep (G_USEC_PER_SEC);
DEBUG_MSG (("[unused] stopping unused threads"));
g_thread_pool_stop_unused_threads ();
for (i = 0; i < 5; i++)
{
if (g_thread_pool_get_num_unused_threads () == 0)
break;
DEBUG_MSG (("[unused] waiting ONE second for threads to die"));
/* Some time for threads to die. */
g_usleep (G_USEC_PER_SEC);
}
DEBUG_MSG (("[unused] stopped idle threads, %d remain",
g_thread_pool_get_num_unused_threads ()));
g_assert (g_thread_pool_get_num_unused_threads () == 0);
g_thread_pool_set_max_unused_threads (MAX_THREADS);
DEBUG_MSG (("[unused] cleaning up thread pool"));
g_thread_pool_free (pool, FALSE, TRUE);
}
static void
test_thread_pools_entry_func (gpointer data, gpointer user_data)
{
#ifdef DEBUG
guint id = 0;
id = GPOINTER_TO_UINT (data);
#endif
DEBUG_MSG (("[pool] ---> [%3.3d] entered thread.", id));
G_LOCK (thread_counter_pools);
abs_thread_counter++;
running_thread_counter++;
G_UNLOCK (thread_counter_pools);
g_usleep (g_random_int_range (0, 4000));
G_LOCK (thread_counter_pools);
running_thread_counter--;
leftover_task_counter--;
DEBUG_MSG (("[pool] ---> [%3.3d] exiting thread (abs count:%ld, "
"running count:%ld, left over:%ld)",
id, abs_thread_counter,
running_thread_counter, leftover_task_counter));
G_UNLOCK (thread_counter_pools);
}
static void
test_thread_pools (void)
{
GThreadPool *pool1, *pool2, *pool3;
guint runs;
guint i;
pool1 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 3, FALSE, NULL);
pool2 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 5, TRUE, NULL);
pool3 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 7, TRUE, NULL);
runs = 300;
for (i = 0; i < runs; i++)
{
g_thread_pool_push (pool1, GUINT_TO_POINTER (i + 1), NULL);
g_thread_pool_push (pool2, GUINT_TO_POINTER (i + 1), NULL);
g_thread_pool_push (pool3, GUINT_TO_POINTER (i + 1), NULL);
G_LOCK (thread_counter_pools);
leftover_task_counter += 3;
G_UNLOCK (thread_counter_pools);
}
g_thread_pool_free (pool1, TRUE, TRUE);
g_thread_pool_free (pool2, FALSE, TRUE);
g_thread_pool_free (pool3, FALSE, TRUE);
g_assert (runs * 3 == abs_thread_counter + leftover_task_counter);
g_assert (running_thread_counter == 0);
}
static gint
test_thread_sort_compare_func (gconstpointer a, gconstpointer b, gpointer user_data)
{
guint32 id1, id2;
id1 = GPOINTER_TO_UINT (a);
id2 = GPOINTER_TO_UINT (b);
return (id1 > id2 ? +1 : id1 == id2 ? 0 : -1);
}
static void
test_thread_sort_entry_func (gpointer data, gpointer user_data)
{
guint thread_id;
gboolean is_sorted;
G_LOCK (last_thread);
thread_id = GPOINTER_TO_UINT (data);
is_sorted = GPOINTER_TO_INT (user_data);
DEBUG_MSG (("%s ---> entered thread:%2.2d, last thread:%2.2d",
is_sorted ? "[ sorted]" : "[unsorted]",
thread_id, last_thread_id));
if (is_sorted) {
static gboolean last_failed = FALSE;
if (last_thread_id > thread_id) {
if (last_failed) {
g_assert (last_thread_id <= thread_id);
}
/* Here we remember one fail and if it concurrently fails, it
* can not be sorted. the last thread id might be < this thread
* id if something is added to the queue since threads were
* created
*/
last_failed = TRUE;
} else {
last_failed = FALSE;
}
last_thread_id = thread_id;
}
G_UNLOCK (last_thread);
g_usleep (WAIT * 1000);
}
static void
test_thread_sort (gboolean sort)
{
GThreadPool *pool;
guint limit;
guint max_threads;
guint i;
limit = MAX_THREADS * 10;
if (sort) {
max_threads = 1;
} else {
max_threads = MAX_THREADS;
}
/* It is important that we only have a maximum of 1 thread for this
* test since the results can not be guaranteed to be sorted if > 1.
*
* Threads are scheduled by the operating system and are executed at
* random. It cannot be assumed that threads are executed in the
* order they are created. This was discussed in bug #334943.
*/
pool = g_thread_pool_new (test_thread_sort_entry_func,
GINT_TO_POINTER (sort),
max_threads,
FALSE,
NULL);
g_thread_pool_set_max_unused_threads (MAX_UNUSED_THREADS);
if (sort) {
g_thread_pool_set_sort_function (pool,
test_thread_sort_compare_func,
GUINT_TO_POINTER (69));
}
for (i = 0; i < limit; i++) {
guint id;
id = g_random_int_range (1, limit) + 1;
g_thread_pool_push (pool, GUINT_TO_POINTER (id), NULL);
DEBUG_MSG (("%s ===> pushed new thread with id:%d, number "
"of threads:%d, unprocessed:%d",
sort ? "[ sorted]" : "[unsorted]",
id,
g_thread_pool_get_num_threads (pool),
g_thread_pool_unprocessed (pool)));
}
g_assert (g_thread_pool_get_max_threads (pool) == (gint) max_threads);
g_assert (g_thread_pool_get_num_threads (pool) == (guint) g_thread_pool_get_max_threads (pool));
g_thread_pool_free (pool, TRUE, TRUE);
}
static void
test_thread_idle_time_entry_func (gpointer data, gpointer user_data)
{
#ifdef DEBUG
guint thread_id;
thread_id = GPOINTER_TO_UINT (data);
#endif
DEBUG_MSG (("[idle] ---> entered thread:%2.2d", thread_id));
g_usleep (WAIT * 1000);
DEBUG_MSG (("[idle] <--- exiting thread:%2.2d", thread_id));
}
static gboolean
test_thread_idle_timeout (gpointer data)
{
gint i;
for (i = 0; i < 2; i++) {
g_thread_pool_push (idle_pool, GUINT_TO_POINTER (100 + i), NULL);
DEBUG_MSG (("[idle] ===> pushed new thread with id:%d, number "
"of threads:%d, unprocessed:%d",
100 + i,
g_thread_pool_get_num_threads (idle_pool),
g_thread_pool_unprocessed (idle_pool)));
}
return FALSE;
}
static void
test_thread_idle_time (void)
{
guint limit = 50;
guint interval = 10000;
guint i;
idle_pool = g_thread_pool_new (test_thread_idle_time_entry_func,
NULL,
0,
FALSE,
NULL);
g_thread_pool_set_max_threads (idle_pool, MAX_THREADS, NULL);
g_thread_pool_set_max_unused_threads (MAX_UNUSED_THREADS);
g_thread_pool_set_max_idle_time (interval);
g_assert (g_thread_pool_get_max_threads (idle_pool) == MAX_THREADS);
g_assert (g_thread_pool_get_max_unused_threads () == MAX_UNUSED_THREADS);
g_assert (g_thread_pool_get_max_idle_time () == interval);
for (i = 0; i < limit; i++) {
g_thread_pool_push (idle_pool, GUINT_TO_POINTER (i + 1), NULL);
DEBUG_MSG (("[idle] ===> pushed new thread with id:%d, "
"number of threads:%d, unprocessed:%d",
i,
g_thread_pool_get_num_threads (idle_pool),
g_thread_pool_unprocessed (idle_pool)));
}
g_assert_cmpint (g_thread_pool_unprocessed (idle_pool), <=, limit);
g_timeout_add ((interval - 1000),
test_thread_idle_timeout,
GUINT_TO_POINTER (interval));
}
static gboolean
test_check_start_and_stop (gpointer user_data)
{
static guint test_number = 0;
static gboolean run_next = FALSE;
gboolean continue_timeout = TRUE;
gboolean quit = TRUE;
if (test_number == 0) {
run_next = TRUE;
DEBUG_MSG (("***** RUNNING TEST %2.2d *****", test_number));
}
if (run_next) {
test_number++;
switch (test_number) {
case 1:
test_thread_functions ();
break;
case 2:
test_thread_stop_unused ();
break;
case 3:
test_thread_pools ();
break;
case 4:
test_thread_sort (FALSE);
break;
case 5:
test_thread_sort (TRUE);
break;
case 6:
test_thread_stop_unused ();
break;
case 7:
test_thread_idle_time ();
break;
default:
DEBUG_MSG (("***** END OF TESTS *****"));
g_main_loop_quit (main_loop);
continue_timeout = FALSE;
break;
}
run_next = FALSE;
return continue_timeout;
}
if (test_number == 3) {
G_LOCK (thread_counter_pools);
quit &= running_thread_counter <= 0;
DEBUG_MSG (("***** POOL RUNNING THREAD COUNT:%ld",
running_thread_counter));
G_UNLOCK (thread_counter_pools);
}
if (test_number == 4 || test_number == 5) {
G_LOCK (thread_counter_sort);
quit &= sort_thread_counter <= 0;
DEBUG_MSG (("***** POOL SORT THREAD COUNT:%ld",
sort_thread_counter));
G_UNLOCK (thread_counter_sort);
}
if (test_number == 7) {
guint idle;
idle = g_thread_pool_get_num_unused_threads ();
quit &= idle < 1;
DEBUG_MSG (("***** POOL IDLE THREAD COUNT:%d, UNPROCESSED JOBS:%d",
idle, g_thread_pool_unprocessed (idle_pool)));
}
if (quit) {
run_next = TRUE;
}
return continue_timeout;
}
int
main (int argc, char *argv[])
{
DEBUG_MSG (("Starting... (in one second)"));
g_timeout_add (1000, test_check_start_and_stop, NULL);
main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (main_loop);
g_main_loop_unref (main_loop);
g_thread_pool_free (idle_pool, FALSE, TRUE);
return 0;
}

236
tests/timeloop-basic.c Normal file
View file

@ -0,0 +1,236 @@
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <poll.h>
#define TRUE 1
#define FALSE 0
static int n_children = 3;
static int n_active_children;
static int n_iters = 10000;
static int write_fds[1024];
static struct pollfd poll_fds[1024];
void
my_pipe (int *fds)
{
if (pipe(fds) < 0)
{
int errsv = errno;
fprintf (stderr, "Cannot create pipe %s\n", strerror (errsv));
exit (1);
}
}
int
read_all (int fd, char *buf, int len)
{
size_t bytes_read = 0;
gssize count;
while (bytes_read < len)
{
count = read (fd, buf + bytes_read, len - bytes_read);
if (count < 0)
{
if (errno != EAGAIN)
return FALSE;
}
else if (count == 0)
return FALSE;
bytes_read += count;
}
return TRUE;
}
int
write_all (int fd, char *buf, int len)
{
size_t bytes_written = 0;
gssize count;
while (bytes_written < len)
{
count = write (fd, buf + bytes_written, len - bytes_written);
if (count < 0)
{
if (errno != EAGAIN)
return FALSE;
}
bytes_written += count;
}
return TRUE;
}
void
run_child (int in_fd, int out_fd)
{
int i;
int val = 1;
for (i = 0; i < n_iters; i++)
{
write_all (out_fd, (char *)&val, sizeof (val));
read_all (in_fd, (char *)&val, sizeof (val));
}
val = 0;
write_all (out_fd, (char *)&val, sizeof (val));
exit (0);
}
int
input_callback (int source, int dest)
{
int val;
if (!read_all (source, (char *)&val, sizeof(val)))
{
fprintf (stderr,"Unexpected EOF\n");
exit (1);
}
if (val)
{
write_all (dest, (char *)&val, sizeof(val));
return TRUE;
}
else
{
close (source);
close (dest);
n_active_children--;
return FALSE;
}
}
void
create_child (int pos)
{
int pid, errsv;
int in_fds[2];
int out_fds[2];
my_pipe (in_fds);
my_pipe (out_fds);
pid = fork ();
errsv = errno;
if (pid > 0) /* Parent */
{
close (in_fds[0]);
close (out_fds[1]);
write_fds[pos] = in_fds[1];
poll_fds[pos].fd = out_fds[0];
poll_fds[pos].events = POLLIN;
}
else if (pid == 0) /* Child */
{
close (in_fds[1]);
close (out_fds[0]);
setsid ();
run_child (in_fds[0], out_fds[1]);
}
else /* Error */
{
fprintf (stderr,"Cannot fork: %s\n", strerror (errsv));
exit (1);
}
}
static double
difftimeval (struct timeval *old, struct timeval *new)
{
return
(new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
}
int
main (int argc, char **argv)
{
int i, j;
struct rusage old_usage;
struct rusage new_usage;
if (argc > 1)
n_children = atoi(argv[1]);
if (argc > 2)
n_iters = atoi(argv[2]);
printf ("Children: %d Iters: %d\n", n_children, n_iters);
n_active_children = n_children;
for (i = 0; i < n_children; i++)
create_child (i);
getrusage (RUSAGE_SELF, &old_usage);
while (n_active_children > 0)
{
int old_n_active_children = n_active_children;
poll (poll_fds, n_active_children, -1);
for (i=0; i<n_active_children; i++)
{
if (poll_fds[i].events & (POLLIN | POLLHUP))
{
if (!input_callback (poll_fds[i].fd, write_fds[i]))
write_fds[i] = -1;
}
}
if (old_n_active_children > n_active_children)
{
j = 0;
for (i=0; i<old_n_active_children; i++)
{
if (write_fds[i] != -1)
{
if (j < i)
{
poll_fds[j] = poll_fds[i];
write_fds[j] = write_fds[i];
}
j++;
}
}
}
}
getrusage (RUSAGE_SELF, &new_usage);
printf ("Elapsed user: %g\n",
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
printf ("Elapsed system: %g\n",
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
printf ("Elapsed total: %g\n",
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
printf ("total / iteration: %g\n",
(difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
(n_iters * n_children));
return 0;
}

221
tests/timeloop.c Normal file
View file

@ -0,0 +1,221 @@
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <glib.h>
static int n_children = 3;
static int n_active_children;
static int n_iters = 10000;
static GMainLoop *loop;
static void
io_pipe (GIOChannel **channels)
{
int fds[2];
if (pipe(fds) < 0)
{
int errsv = errno;
fprintf (stderr, "Cannot create pipe %s\n", g_strerror (errsv));
exit (1);
}
channels[0] = g_io_channel_unix_new (fds[0]);
channels[1] = g_io_channel_unix_new (fds[1]);
}
static gboolean
read_all (GIOChannel *channel, char *buf, gsize len)
{
gsize bytes_read = 0;
gsize count;
GIOError err;
while (bytes_read < len)
{
err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
if (err)
{
if (err != G_IO_ERROR_AGAIN)
return FALSE;
}
else if (count == 0)
return FALSE;
bytes_read += count;
}
return TRUE;
}
static gboolean
write_all (GIOChannel *channel, char *buf, gsize len)
{
gsize bytes_written = 0;
gsize count;
GIOError err;
while (bytes_written < len)
{
err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
if (err && err != G_IO_ERROR_AGAIN)
return FALSE;
bytes_written += count;
}
return TRUE;
}
static void
run_child (GIOChannel *in_channel, GIOChannel *out_channel)
{
int i;
int val = 1;
GTimer *timer = g_timer_new();
for (i = 0; i < n_iters; i++)
{
write_all (out_channel, (char *)&val, sizeof (val));
read_all (in_channel, (char *)&val, sizeof (val));
}
val = 0;
write_all (out_channel, (char *)&val, sizeof (val));
val = g_timer_elapsed (timer, NULL) * 1000;
write_all (out_channel, (char *)&val, sizeof (val));
g_timer_destroy (timer);
exit (0);
}
static gboolean
input_callback (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
int val;
GIOChannel *dest = (GIOChannel *)data;
if (!read_all (source, (char *)&val, sizeof(val)))
{
fprintf (stderr, "Unexpected EOF\n");
exit (1);
}
if (val)
{
write_all (dest, (char *)&val, sizeof(val));
return TRUE;
}
else
{
g_io_channel_close (source);
g_io_channel_close (dest);
g_io_channel_unref (source);
g_io_channel_unref (dest);
n_active_children--;
if (n_active_children == 0)
g_main_loop_quit (loop);
return FALSE;
}
}
static void
create_child (void)
{
int pid, errsv;
GIOChannel *in_channels[2];
GIOChannel *out_channels[2];
io_pipe (in_channels);
io_pipe (out_channels);
pid = fork ();
errsv = errno;
if (pid > 0) /* Parent */
{
g_io_channel_close (in_channels[0]);
g_io_channel_unref (in_channels[0]);
g_io_channel_close (out_channels[1]);
g_io_channel_unref (out_channels[1]);
g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
input_callback, in_channels[1]);
}
else if (pid == 0) /* Child */
{
g_io_channel_close (in_channels[1]);
g_io_channel_close (out_channels[0]);
setsid ();
run_child (in_channels[0], out_channels[1]);
}
else /* Error */
{
fprintf (stderr, "Cannot fork: %s\n", g_strerror (errsv));
exit (1);
}
}
static double
difftimeval (struct timeval *old, struct timeval *new)
{
return
(new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
}
int
main (int argc, char **argv)
{
int i;
struct rusage old_usage;
struct rusage new_usage;
if (argc > 1)
n_children = atoi(argv[1]);
if (argc > 2)
n_iters = atoi(argv[2]);
printf ("Children: %d Iters: %d\n", n_children, n_iters);
n_active_children = n_children;
for (i = 0; i < n_children; i++)
create_child ();
getrusage (RUSAGE_SELF, &old_usage);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
getrusage (RUSAGE_SELF, &new_usage);
printf ("Elapsed user: %g\n",
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
printf ("Elapsed system: %g\n",
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
printf ("Elapsed total: %g\n",
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
printf ("total / iteration: %g\n",
(difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
(n_iters * n_children));
g_main_loop_unref (loop);
return 0;
}

444
tests/unicode-encoding.c Normal file
View file

@ -0,0 +1,444 @@
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
static gint exit_status = 0;
G_GNUC_PRINTF (1, 2)
static void
croak (char *format, ...)
{
va_list va;
va_start (va, format);
vfprintf (stderr, format, va);
va_end (va);
exit (1);
}
G_GNUC_PRINTF (1, 2)
static void
fail (char *format, ...)
{
va_list va;
va_start (va, format);
vfprintf (stderr, format, va);
va_end (va);
exit_status |= 1;
}
typedef enum
{
VALID,
INCOMPLETE,
NOTUNICODE,
OVERLONG,
MALFORMED
} Status;
static gboolean
ucs4_equal (gunichar *a, gunichar *b)
{
while (*a && *b && (*a == *b))
{
a++;
b++;
}
return (*a == *b);
}
static gboolean
utf16_equal (gunichar2 *a, gunichar2 *b)
{
while (*a && *b && (*a == *b))
{
a++;
b++;
}
return (*a == *b);
}
static gint
utf16_count (gunichar2 *a)
{
gint result = 0;
while (a[result])
result++;
return result;
}
static void
print_ucs4 (const gchar *prefix, gunichar *ucs4, gint ucs4_len)
{
gint i;
g_print ("%s ", prefix);
for (i = 0; i < ucs4_len; i++)
g_print ("%x ", ucs4[i]);
g_print ("\n");
}
static void
process (gint line,
gchar *utf8,
Status status,
gunichar *ucs4,
gint ucs4_len)
{
const gchar *end;
gboolean is_valid = g_utf8_validate (utf8, -1, &end);
GError *error = NULL;
glong items_read, items_written;
switch (status)
{
case VALID:
if (!is_valid)
{
fail ("line %d: valid but g_utf8_validate returned FALSE\n", line);
return;
}
break;
case NOTUNICODE:
case INCOMPLETE:
case OVERLONG:
case MALFORMED:
if (is_valid)
{
fail ("line %d: invalid but g_utf8_validate returned TRUE\n", line);
return;
}
break;
}
if (status == INCOMPLETE)
{
gunichar *ucs4_result;
ucs4_result = g_utf8_to_ucs4 (utf8, -1, NULL, NULL, &error);
if (!error || !g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT))
{
fail ("line %d: incomplete input not properly detected\n", line);
return;
}
g_clear_error (&error);
ucs4_result = g_utf8_to_ucs4 (utf8, -1, &items_read, NULL, &error);
if (!ucs4_result || items_read == (glong) strlen (utf8))
{
fail ("line %d: incomplete input not properly detected\n", line);
return;
}
g_free (ucs4_result);
}
if (status == VALID || status == NOTUNICODE)
{
gunichar *ucs4_result;
ucs4_result = g_utf8_to_ucs4 (utf8, -1, &items_read, &items_written, &error);
if (!ucs4_result)
{
fail ("line %d: conversion with status %d to ucs4 failed: %s\n", line, status, error->message);
return;
}
if (!ucs4_equal (ucs4_result, ucs4) ||
items_read != (glong) strlen (utf8) ||
items_written != ucs4_len)
{
fail ("line %d: results of conversion with status %d to ucs4 do not match expected.\n", line, status);
print_ucs4 ("expected: ", ucs4, ucs4_len);
print_ucs4 ("received: ", ucs4_result, items_written);
return;
}
g_free (ucs4_result);
}
if (status == VALID)
{
gunichar *ucs4_result;
gchar *utf8_result;
ucs4_result = g_utf8_to_ucs4_fast (utf8, -1, &items_written);
if (!ucs4_equal (ucs4_result, ucs4) ||
items_written != ucs4_len)
{
fail ("line %d: results of fast conversion with status %d to ucs4 do not match expected.\n", line, status);
print_ucs4 ("expected: ", ucs4, ucs4_len);
print_ucs4 ("received: ", ucs4_result, items_written);
return;
}
utf8_result = g_ucs4_to_utf8 (ucs4_result, -1, &items_read, &items_written, &error);
if (!utf8_result)
{
fail ("line %d: conversion back to utf8 failed: %s", line, error->message);
return;
}
if (strcmp (utf8_result, utf8) != 0 ||
items_read != ucs4_len ||
items_written != (glong) strlen (utf8))
{
fail ("line %d: conversion back to utf8 did not match original\n", line);
return;
}
g_free (utf8_result);
g_free (ucs4_result);
}
if (status == VALID)
{
gunichar2 *utf16_expected_tmp;
gunichar2 *utf16_expected;
gunichar2 *utf16_from_utf8;
gunichar2 *utf16_from_ucs4;
gunichar *ucs4_result;
gsize bytes_written;
gint n_chars;
gchar *utf8_result;
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define TARGET "UTF-16LE"
#else
#define TARGET "UTF-16"
#endif
if (!(utf16_expected_tmp = (gunichar2 *)g_convert (utf8, -1, TARGET, "UTF-8",
NULL, &bytes_written, NULL)))
{
fail ("line %d: could not convert to UTF-16 via g_convert\n", line);
return;
}
/* zero-terminate and remove BOM
*/
n_chars = bytes_written / 2;
if (utf16_expected_tmp[0] == 0xfeff) /* BOM */
{
n_chars--;
utf16_expected = g_new (gunichar2, n_chars + 1);
memcpy (utf16_expected, utf16_expected_tmp + 1, sizeof(gunichar2) * n_chars);
}
else if (utf16_expected_tmp[0] == 0xfffe) /* ANTI-BOM */
{
fail ("line %d: conversion via iconv to \"UTF-16\" is not native-endian\n", line);
return;
}
else
{
utf16_expected = g_new (gunichar2, n_chars + 1);
memcpy (utf16_expected, utf16_expected_tmp, sizeof(gunichar2) * n_chars);
}
utf16_expected[n_chars] = '\0';
if (!(utf16_from_utf8 = g_utf8_to_utf16 (utf8, -1, &items_read, &items_written, &error)))
{
fail ("line %d: conversion to ucs16 failed: %s\n", line, error->message);
return;
}
if (items_read != (glong) strlen (utf8) ||
utf16_count (utf16_from_utf8) != items_written)
{
fail ("line %d: length error in conversion to ucs16\n", line);
return;
}
if (!(utf16_from_ucs4 = g_ucs4_to_utf16 (ucs4, -1, &items_read, &items_written, &error)))
{
fail ("line %d: conversion to ucs16 failed: %s\n", line, error->message);
return;
}
if (items_read != ucs4_len ||
utf16_count (utf16_from_ucs4) != items_written)
{
fail ("line %d: length error in conversion to ucs16\n", line);
return;
}
if (!utf16_equal (utf16_from_utf8, utf16_expected) ||
!utf16_equal (utf16_from_ucs4, utf16_expected))
{
fail ("line %d: results of conversion to ucs16 do not match\n", line);
return;
}
if (!(utf8_result = g_utf16_to_utf8 (utf16_from_utf8, -1, &items_read, &items_written, &error)))
{
fail ("line %d: conversion back to utf8 failed: %s\n", line, error->message);
return;
}
if (items_read != utf16_count (utf16_from_utf8) ||
items_written != (glong) strlen (utf8))
{
fail ("line %d: length error in conversion from ucs16 to utf8\n", line);
return;
}
if (!(ucs4_result = g_utf16_to_ucs4 (utf16_from_ucs4, -1, &items_read, &items_written, &error)))
{
fail ("line %d: conversion back to utf8/ucs4 failed\n", line);
return;
}
if (items_read != utf16_count (utf16_from_utf8) ||
items_written != ucs4_len)
{
fail ("line %d: length error in conversion from ucs16 to ucs4\n", line);
return;
}
if (strcmp (utf8, utf8_result) != 0 ||
!ucs4_equal (ucs4, ucs4_result))
{
fail ("line %d: conversion back to utf8/ucs4 did not match original\n", line);
return;
}
g_free (utf16_expected_tmp);
g_free (utf16_expected);
g_free (utf16_from_utf8);
g_free (utf16_from_ucs4);
g_free (utf8_result);
g_free (ucs4_result);
}
}
int
main (int argc, char **argv)
{
gchar *testfile;
gchar *contents;
GError *error = NULL;
gchar *p, *end;
char *tmp;
gint state = 0;
gint line = 1;
gint start_line = 0; /* Quiet GCC */
gchar *utf8 = NULL; /* Quiet GCC */
GArray *ucs4;
Status status = VALID; /* Quiet GCC */
g_test_init (&argc, &argv, NULL);
testfile = g_test_build_filename (G_TEST_DIST, "utf8.txt", NULL);
g_file_get_contents (testfile, &contents, NULL, &error);
if (error)
croak ("Cannot open utf8.txt: %s", error->message);
ucs4 = g_array_new (TRUE, FALSE, sizeof(gunichar));
p = contents;
/* Loop over lines */
while (*p)
{
while (*p && (*p == ' ' || *p == '\t'))
p++;
end = p;
while (*end && (*end != '\r' && *end != '\n'))
end++;
if (!*p || *p == '#' || *p == '\r' || *p == '\n')
goto next_line;
tmp = g_strstrip (g_strndup (p, end - p));
switch (state)
{
case 0:
/* UTF-8 string */
start_line = line;
utf8 = tmp;
tmp = NULL;
break;
case 1:
/* Status */
if (!strcmp (tmp, "VALID"))
status = VALID;
else if (!strcmp (tmp, "INCOMPLETE"))
status = INCOMPLETE;
else if (!strcmp (tmp, "NOTUNICODE"))
status = NOTUNICODE;
else if (!strcmp (tmp, "OVERLONG"))
status = OVERLONG;
else if (!strcmp (tmp, "MALFORMED"))
status = MALFORMED;
else
croak ("Invalid status on line %d\n", line);
if (status != VALID && status != NOTUNICODE)
state++; /* No UCS-4 data */
break;
case 2:
/* UCS-4 version */
p = strtok (tmp, " \t");
while (p)
{
gchar *endptr;
gunichar ch = strtoul (p, &endptr, 16);
if (*endptr != '\0')
croak ("Invalid UCS-4 character on line %d\n", line);
g_array_append_val (ucs4, ch);
p = strtok (NULL, " \t");
}
break;
}
g_free (tmp);
state = (state + 1) % 3;
if (state == 0)
{
process (start_line, utf8, status, (gunichar *)ucs4->data, ucs4->len);
g_array_set_size (ucs4, 0);
g_free (utf8);
}
next_line:
p = end;
if (*p && *p == '\r')
p++;
if (*p && *p == '\n')
p++;
line++;
}
g_free (testfile);
g_array_free (ucs4, TRUE);
g_free (contents);
return exit_status;
}

210
tests/unicode-normalize.c Normal file
View file

@ -0,0 +1,210 @@
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
gboolean success = TRUE;
static char *
decode (const gchar *input)
{
unsigned ch;
int offset = 0;
GString *result = g_string_new (NULL);
do
{
if (sscanf (input + offset, "%x", &ch) != 1)
{
fprintf (stderr, "Error parsing character string %s\n", input);
exit (1);
}
g_string_append_unichar (result, ch);
while (input[offset] && input[offset] != ' ')
offset++;
while (input[offset] && input[offset] == ' ')
offset++;
}
while (input[offset]);
return g_string_free (result, FALSE);
}
const char *names[4] = {
"NFD",
"NFC",
"NFKD",
"NFKC"
};
static char *
encode (const gchar *input)
{
GString *result = g_string_new(NULL);
const gchar *p = input;
while (*p)
{
gunichar c = g_utf8_get_char (p);
g_string_append_printf (result, "%04X ", c);
p = g_utf8_next_char(p);
}
return g_string_free (result, FALSE);
}
static void
test_form (int line,
GNormalizeMode mode,
gboolean do_compat,
int expected,
char **c,
char **raw)
{
int i;
gboolean mode_is_compat = (mode == G_NORMALIZE_NFKC ||
mode == G_NORMALIZE_NFKD);
if (mode_is_compat || !do_compat)
{
for (i = 0; i < 3; i++)
{
char *result = g_utf8_normalize (c[i], -1, mode);
if (strcmp (result, c[expected]) != 0)
{
char *result_raw = encode(result);
fprintf (stderr, "\nFailure: %d/%d: %s\n", line, i + 1, raw[5]);
fprintf (stderr, " g_utf8_normalize (%s, %s) != %s but %s\n",
raw[i], names[mode], raw[expected], result_raw);
g_free (result_raw);
success = FALSE;
}
g_free (result);
}
}
if (mode_is_compat || do_compat)
{
for (i = 3; i < 5; i++)
{
char *result = g_utf8_normalize (c[i], -1, mode);
if (strcmp (result, c[expected]) != 0)
{
char *result_raw = encode(result);
fprintf (stderr, "\nFailure: %d/%d: %s\n", line, i, raw[5]);
fprintf (stderr, " g_utf8_normalize (%s, %s) != %s but %s\n",
raw[i], names[mode], raw[expected], result_raw);
g_free (result_raw);
success = FALSE;
}
g_free (result);
}
}
}
static gboolean
process_one (int line, gchar **columns)
{
char *c[5];
int i;
gboolean skip = FALSE;
for (i=0; i < 5; i++)
{
c[i] = decode(columns[i]);
if (!c[i])
skip = TRUE;
}
if (!skip)
{
test_form (line, G_NORMALIZE_NFD, FALSE, 2, c, columns);
test_form (line, G_NORMALIZE_NFD, TRUE, 4, c, columns);
test_form (line, G_NORMALIZE_NFC, FALSE, 1, c, columns);
test_form (line, G_NORMALIZE_NFC, TRUE, 3, c, columns);
test_form (line, G_NORMALIZE_NFKD, TRUE, 4, c, columns);
test_form (line, G_NORMALIZE_NFKC, TRUE, 3, c, columns);
}
for (i=0; i < 5; i++)
g_free (c[i]);
return TRUE;
}
int main (int argc, char **argv)
{
GIOChannel *in;
GError *error = NULL;
GString *buffer = g_string_new (NULL);
int line_to_do = 0;
int line = 1;
if (argc != 2 && argc != 3)
{
fprintf (stderr, "Usage: unicode-normalize NormalizationTest.txt LINE\n");
return 1;
}
if (argc == 3)
line_to_do = atoi(argv[2]);
in = g_io_channel_new_file (argv[1], "r", &error);
if (!in)
{
fprintf (stderr, "Cannot open %s: %s\n", argv[1], error->message);
return 1;
}
while (TRUE)
{
gsize term_pos;
gchar **columns;
if (g_io_channel_read_line_string (in, buffer, &term_pos, &error) != G_IO_STATUS_NORMAL)
break;
if (line_to_do && line != line_to_do)
goto next;
buffer->str[term_pos] = '\0';
if (buffer->str[0] == '#') /* Comment */
goto next;
if (buffer->str[0] == '@') /* Part */
{
fprintf (stderr, "\nProcessing %s\n", buffer->str + 1);
goto next;
}
columns = g_strsplit (buffer->str, ";", -1);
if (!columns[0])
goto next;
if (!process_one (line, columns))
return 1;
g_strfreev (columns);
next:
g_string_truncate (buffer, 0);
line++;
}
if (error)
{
fprintf (stderr, "Error reading test file, %s\n", error->message);
return 1;
}
g_io_channel_unref (in);
g_string_free (buffer, TRUE);
return !success;
}

291
tests/utf8.txt Normal file
View file

@ -0,0 +1,291 @@
# This file is derived from
#
# http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
#
# Which was created by Markus Kuhn <mkuhn@acm.org> - 2000-09-02
#
# lines begining with # and blank lines are ignored
#
# Beyond that, this file consists of a series of test cases. Each test case consists of
# 2 or 3 lines:
#
# 1. A UTF-8 string
# 2. A status
# VALID : The string is a valid UTF-8 representation of valid Unicode
# INCOMPLETE : The string has a partial character at the end
# NOTUNICODE : The string is valid UTF-8, but the characters represented
# are not valid unicode (
# OVERLONG : The string includes overlong sequences
# MALFORMED : The string is not valid UTF-8
# 3. If the status is VALID or NOTUNICODE, the UCS-4 representation of the string,
# as a series of hex numbers.
# 1 Some correct UTF-8 text
κόσμε
VALID
03ba 1f79 03c3 03bc 03b5
# 2.1 First possible sequence of a certain length
#
# FIXME - handle NULLS?
#
# [ NULL BYTE ]
#VALID
#0000
€
VALID
0080
à €
VALID
0800
ð<EFBFBD>€€
VALID
00010000
øˆ€€€
NOTUNICODE
00200000
ü„€€€€
NOTUNICODE
04000000

VALID
0000007f
ß¿
VALID
000007ff
ï¿¿
VALID
0000ffff
÷¿¿¿
NOTUNICODE
001fffff
û¿¿¿¿
NOTUNICODE
03ffffff
ý¿¿¿¿¿
NOTUNICODE
7fffffff
# 2.3 Other boundary conditions
퟿
VALID
d7ff

VALID
e000
�
VALID
fffd
ô<EFBFBD>¿½
VALID
0010fffd
ô<EFBFBD>¿¿
VALID
0010ffff
ô<EFBFBD>€€
NOTUNICODE
00110000
# 3.1 Unexpected continuation bytes
MALFORMED
¿
MALFORMED
€¿
MALFORMED
€¿€
MALFORMED
€¿€¿
MALFORMED
€¿€¿€
MALFORMED
€¿€¿€¿
MALFORMED
€¿€¿€¿€
MALFORMED
<EFBFBD>ƒ„…†‡ˆ‰ŠŒ<EFBFBD>Ž<EFBFBD><EFBFBD>“”•˜™šœ<EFBFBD>žŸ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿
MALFORMED
# 3.2 Lonely start characters
À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß
MALFORMED
à á â ã ä å æ ç è é ê ë ì í î ï
MALFORMED
ð ñ ò ó ô õ ö ÷
MALFORMED
ø ù ú û
MALFORMED
ü ý
MALFORMED
# 3.3 Sequences with last continuation byte missing
À
INCOMPLETE
à€
INCOMPLETE
ð€€
INCOMPLETE
ø€€€
INCOMPLETE
ü€€€€
INCOMPLETE
ß
INCOMPLETE
ï¿
INCOMPLETE
÷¿¿
INCOMPLETE
û¿¿¿
INCOMPLETE
ý¿¿¿¿
INCOMPLETE
# 3.4 Concatenation of incomplete sequences
Àà€ð€€ø€€€ü€€€€ßï¿÷¿¿û¿¿¿ý¿¿¿¿
MALFORMED
# 3.5 Impossible bytes
þ
MALFORMED
ÿ
MALFORMED
þþÿÿ
MALFORMED
# Examples of an overlong ASCII character
À¯
OVERLONG
à€¯
OVERLONG
ð€€¯
OVERLONG
ø€€€¯
OVERLONG
ü€€€€¯
OVERLONG
# Maximum overlong sequences
Á¿
OVERLONG
àŸ¿
OVERLONG
ð<EFBFBD>¿¿
OVERLONG
ø‡¿¿¿
OVERLONG
üƒ¿¿¿¿
OVERLONG
# Overlong representation of the NUL character
À€
OVERLONG
à€€
OVERLONG
ð€€€
OVERLONG
ø€€€€
OVERLONG
ü€€€€€
OVERLONG
# Illegal code positions
# Single UTF-16 surrogates
í €
NOTUNICODE
d800
í­¿
NOTUNICODE
db7f
í®€
NOTUNICODE
db80
í¯¿
NOTUNICODE
dbff
í°€
NOTUNICODE
dc00
í¾€
NOTUNICODE
df80
í¿¿
NOTUNICODE
dfff
# Paired UTF-16 surrogates
𐀀
NOTUNICODE
d800 dc00
𐏿
NOTUNICODE
d800 dfff
í­¿í°€
NOTUNICODE
db7f dc00
í­¿í¿¿
NOTUNICODE
db7f dfff
󰀀
NOTUNICODE
db80 dc00
󰏿
NOTUNICODE
db80 dfff
􏰀
NOTUNICODE
dbff dc00
􏿿
NOTUNICODE
dbff dfff
################
#
# Some more tests, not from Markus Kuhn's file
#
# Mixed plane 0 and higher planes
<EFBFBD>€€Bô<EFBFBD>¿½C
VALID
41 00010000 42 10fffd 43