[glib] gtestutils: add g_text_expect_message()
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] gtestutils: add g_text_expect_message()
- Date: Mon, 20 Aug 2012 17:55:36 +0000 (UTC)
commit 25ac137c0a47ccc5214dabeaa41da18dac2b0cee
Author: Dan Winship <danw gnome org>
Date: Mon Jul 30 16:05:08 2012 -0400
gtestutils: add g_text_expect_message()
Add g_test_expect_message() and g_test_assert_expected_messages(), to
allow tests of warnings, error messages, return-if-fails, etc.
https://bugzilla.gnome.org/show_bug.cgi?id=679556
docs/reference/glib/glib-sections.txt | 4 +
glib/glib.symbols | 2 +
glib/gmessages.c | 466 +++++++++++++++++++++------------
glib/gtestutils.h | 10 +
glib/tests/testing.c | 86 ++++++
5 files changed, 397 insertions(+), 171 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index a7f0a65..2d7f232 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -2901,6 +2901,9 @@ g_test_queue_free
g_test_queue_destroy
g_test_queue_unref
+g_test_expect_message
+g_test_assert_expected_messages
+
GTestTrapFlags
g_test_trap_fork
g_test_trap_has_passed
@@ -2945,6 +2948,7 @@ g_assertion_message_expr
g_assertion_message_cmpstr
g_assertion_message_cmpnum
g_assertion_message_error
+g_test_assert_expected_messages_internal
g_test_config_vars
diff --git a/glib/glib.symbols b/glib/glib.symbols
index 49c6442..9609798 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -1187,11 +1187,13 @@ g_strcmp0
g_test_add_data_func
g_test_add_func
g_test_add_vtable
+g_test_assert_expected_messages_internal
g_test_bug
g_test_bug_base
g_test_config_vars
g_test_create_case
g_test_create_suite
+g_test_expect_message
g_test_fail
g_test_get_root
g_test_init
diff --git a/glib/gmessages.c b/glib/gmessages.c
index b1697d6..c83d3e8 100644
--- a/glib/gmessages.c
+++ b/glib/gmessages.c
@@ -72,6 +72,7 @@
#include "gthread.h"
#include "gstrfuncs.h"
#include "gstring.h"
+#include "gpattern.h"
#ifdef G_OS_WIN32
#include <process.h> /* For getpid() */
@@ -641,6 +642,197 @@ g_log_remove_handler (const gchar *log_domain,
G_STRLOC, handler_id, log_domain);
}
+#define CHAR_IS_SAFE(wc) (!((wc < 0x20 && wc != '\t' && wc != '\n' && wc != '\r') || \
+ (wc == 0x7f) || \
+ (wc >= 0x80 && wc < 0xa0)))
+
+static gchar*
+strdup_convert (const gchar *string,
+ const gchar *charset)
+{
+ if (!g_utf8_validate (string, -1, NULL))
+ {
+ GString *gstring = g_string_new ("[Invalid UTF-8] ");
+ guchar *p;
+
+ for (p = (guchar *)string; *p; p++)
+ {
+ if (CHAR_IS_SAFE(*p) &&
+ !(*p == '\r' && *(p + 1) != '\n') &&
+ *p < 0x80)
+ g_string_append_c (gstring, *p);
+ else
+ g_string_append_printf (gstring, "\\x%02x", (guint)(guchar)*p);
+ }
+
+ return g_string_free (gstring, FALSE);
+ }
+ else
+ {
+ GError *err = NULL;
+
+ gchar *result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, &err);
+ if (result)
+ return result;
+ else
+ {
+ /* Not thread-safe, but doesn't matter if we print the warning twice
+ */
+ static gboolean warned = FALSE;
+ if (!warned)
+ {
+ warned = TRUE;
+ _g_fprintf (stderr, "GLib: Cannot convert message: %s\n", err->message);
+ }
+ g_error_free (err);
+
+ return g_strdup (string);
+ }
+ }
+}
+
+/* For a radix of 8 we need at most 3 output bytes for 1 input
+ * byte. Additionally we might need up to 2 output bytes for the
+ * readix prefix and 1 byte for the trailing NULL.
+ */
+#define FORMAT_UNSIGNED_BUFSIZE ((GLIB_SIZEOF_LONG * 3) + 3)
+
+static void
+format_unsigned (gchar *buf,
+ gulong num,
+ guint radix)
+{
+ gulong tmp;
+ gchar c;
+ gint i, n;
+
+ /* we may not call _any_ GLib functions here (or macros like g_return_if_fail()) */
+
+ if (radix != 8 && radix != 10 && radix != 16)
+ {
+ *buf = '\000';
+ return;
+ }
+
+ if (!num)
+ {
+ *buf++ = '0';
+ *buf = '\000';
+ return;
+ }
+
+ if (radix == 16)
+ {
+ *buf++ = '0';
+ *buf++ = 'x';
+ }
+ else if (radix == 8)
+ {
+ *buf++ = '0';
+ }
+
+ n = 0;
+ tmp = num;
+ while (tmp)
+ {
+ tmp /= radix;
+ n++;
+ }
+
+ i = n;
+
+ /* Again we can't use g_assert; actually this check should _never_ fail. */
+ if (n > FORMAT_UNSIGNED_BUFSIZE - 3)
+ {
+ *buf = '\000';
+ return;
+ }
+
+ while (num)
+ {
+ i--;
+ c = (num % radix);
+ if (c < 10)
+ buf[i] = c + '0';
+ else
+ buf[i] = c + 'a' - 10;
+ num /= radix;
+ }
+
+ buf[n] = '\000';
+}
+
+/* string size big enough to hold level prefix */
+#define STRING_BUFFER_SIZE (FORMAT_UNSIGNED_BUFSIZE + 32)
+
+#define ALERT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)
+
+/* these are emitted by the default log handler */
+#define DEFAULT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE)
+/* these are filtered by G_MESSAGES_DEBUG by the default log handler */
+#define INFO_LEVELS (G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG)
+
+static int
+mklevel_prefix (gchar level_prefix[STRING_BUFFER_SIZE],
+ GLogLevelFlags log_level)
+{
+ gboolean to_stdout = TRUE;
+
+ /* we may not call _any_ GLib functions here */
+
+ switch (log_level & G_LOG_LEVEL_MASK)
+ {
+ case G_LOG_LEVEL_ERROR:
+ strcpy (level_prefix, "ERROR");
+ to_stdout = FALSE;
+ break;
+ case G_LOG_LEVEL_CRITICAL:
+ strcpy (level_prefix, "CRITICAL");
+ to_stdout = FALSE;
+ break;
+ case G_LOG_LEVEL_WARNING:
+ strcpy (level_prefix, "WARNING");
+ to_stdout = FALSE;
+ break;
+ case G_LOG_LEVEL_MESSAGE:
+ strcpy (level_prefix, "Message");
+ to_stdout = FALSE;
+ break;
+ case G_LOG_LEVEL_INFO:
+ strcpy (level_prefix, "INFO");
+ break;
+ case G_LOG_LEVEL_DEBUG:
+ strcpy (level_prefix, "DEBUG");
+ break;
+ default:
+ if (log_level)
+ {
+ strcpy (level_prefix, "LOG-");
+ format_unsigned (level_prefix + 4, log_level & G_LOG_LEVEL_MASK, 16);
+ }
+ else
+ strcpy (level_prefix, "LOG");
+ break;
+ }
+ if (log_level & G_LOG_FLAG_RECURSION)
+ strcat (level_prefix, " (recursed)");
+ if (log_level & ALERT_LEVELS)
+ strcat (level_prefix, " **");
+
+#ifdef G_OS_WIN32
+ win32_keep_fatal_message = (log_level & G_LOG_FLAG_FATAL) != 0;
+#endif
+ return to_stdout ? 1 : 2;
+}
+
+typedef struct {
+ gchar *log_domain;
+ GLogLevelFlags log_level;
+ gchar *pattern;
+} GTestExpectedMessage;
+
+static GSList *expected_messages = NULL;
+
/**
* g_logv:
* @log_domain: the log domain
@@ -681,6 +873,37 @@ g_logv (const gchar *log_domain,
else
msg = msg_alloc = g_strdup_vprintf (format, args);
+ if (expected_messages)
+ {
+ GTestExpectedMessage *expected = expected_messages->data;
+
+ expected_messages = g_slist_delete_link (expected_messages,
+ expected_messages);
+ if (strcmp (expected->log_domain, log_domain) == 0 &&
+ ((log_level & expected->log_level) == expected->log_level) &&
+ g_pattern_match_simple (expected->pattern, msg))
+ {
+ g_free (expected->log_domain);
+ g_free (expected->pattern);
+ g_free (expected);
+ g_free (msg_alloc);
+ return;
+ }
+ else
+ {
+ gchar level_prefix[STRING_BUFFER_SIZE];
+ gchar *expected_message;
+
+ mklevel_prefix (level_prefix, expected->log_level);
+ expected_message = g_strdup_printf ("Did not see expected message %s: %s",
+ level_prefix, expected->pattern);
+ g_log_default_handler (log_domain, log_level, expected_message, NULL);
+ g_free (expected_message);
+
+ log_level |= G_LOG_FLAG_FATAL;
+ }
+ }
+
for (i = g_bit_nth_msf (log_level, -1); i >= 0; i = g_bit_nth_msf (log_level, i))
{
register GLogLevelFlags test_level;
@@ -833,189 +1056,90 @@ g_assert_warning (const char *log_domain,
abort ();
}
-#define CHAR_IS_SAFE(wc) (!((wc < 0x20 && wc != '\t' && wc != '\n' && wc != '\r') || \
- (wc == 0x7f) || \
- (wc >= 0x80 && wc < 0xa0)))
-
-static gchar*
-strdup_convert (const gchar *string,
- const gchar *charset)
-{
- if (!g_utf8_validate (string, -1, NULL))
- {
- GString *gstring = g_string_new ("[Invalid UTF-8] ");
- guchar *p;
-
- for (p = (guchar *)string; *p; p++)
- {
- if (CHAR_IS_SAFE(*p) &&
- !(*p == '\r' && *(p + 1) != '\n') &&
- *p < 0x80)
- g_string_append_c (gstring, *p);
- else
- g_string_append_printf (gstring, "\\x%02x", (guint)(guchar)*p);
- }
-
- return g_string_free (gstring, FALSE);
- }
- else
- {
- GError *err = NULL;
-
- gchar *result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, &err);
- if (result)
- return result;
- else
- {
- /* Not thread-safe, but doesn't matter if we print the warning twice
- */
- static gboolean warned = FALSE;
- if (!warned)
- {
- warned = TRUE;
- _g_fprintf (stderr, "GLib: Cannot convert message: %s\n", err->message);
- }
- g_error_free (err);
-
- return g_strdup (string);
- }
- }
-}
-
-/* For a radix of 8 we need at most 3 output bytes for 1 input
- * byte. Additionally we might need up to 2 output bytes for the
- * readix prefix and 1 byte for the trailing NULL.
+/**
+ * g_test_expect_message:
+ * @log_domain: the log domain of the message
+ * @log_level: the log level of the message
+ * @pattern: a glob-style
+ * <link linkend="glib-Glob-style-pattern-matching">pattern</link>
+ *
+ * Indicates that a message with the given @log_domain and @log_level,
+ * with text matching @pattern, is expected to be logged. When this
+ * message is logged, it will not be printed, and the test case will
+ * not abort.
+ *
+ * Use g_test_assert_expected_messages() to assert that all
+ * previously-expected messages have been seen and suppressed.
+ *
+ * You can call this multiple times in a row, if multiple messages are
+ * expected as a result of a single call. (The messages must appear in
+ * the same order as the calls to g_test_expect_message().)
+ *
+ * For example:
+ *
+ * |[
+ * /* g_main_context_push_thread_default() should fail if the
+ * * context is already owned by another thread.
+ * */
+ * g_test_expect_message (G_LOG_DOMAIN,
+ * G_LOG_LEVEL_CRITICIAL,
+ * "assertion.*acquired_context.*failed");
+ * g_main_context_push_thread_default (bad_context);
+ * g_test_assert_expected_messages ();
+ * ]|
+ *
+ * Note that you cannot use this to test g_error() messages, since
+ * g_error() intentionally never returns even if the program doesn't
+ * abort; use g_test_trap_fork() in this case.
+ *
+ * Since: 2.34
*/
-#define FORMAT_UNSIGNED_BUFSIZE ((GLIB_SIZEOF_LONG * 3) + 3)
-
-static void
-format_unsigned (gchar *buf,
- gulong num,
- guint radix)
+void
+g_test_expect_message (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *pattern)
{
- gulong tmp;
- gchar c;
- gint i, n;
+ GTestExpectedMessage *expected;
- /* we may not call _any_ GLib functions here (or macros like g_return_if_fail()) */
+ expected = g_new (GTestExpectedMessage, 1);
+ expected->log_domain = g_strdup (log_domain);
+ expected->log_level = log_level;
+ expected->pattern = g_strdup (pattern);
- if (radix != 8 && radix != 10 && radix != 16)
- {
- *buf = '\000';
- return;
- }
-
- if (!num)
- {
- *buf++ = '0';
- *buf = '\000';
- return;
- }
-
- if (radix == 16)
- {
- *buf++ = '0';
- *buf++ = 'x';
- }
- else if (radix == 8)
- {
- *buf++ = '0';
- }
-
- n = 0;
- tmp = num;
- while (tmp)
- {
- tmp /= radix;
- n++;
- }
-
- i = n;
-
- /* Again we can't use g_assert; actually this check should _never_ fail. */
- if (n > FORMAT_UNSIGNED_BUFSIZE - 3)
- {
- *buf = '\000';
- return;
- }
-
- while (num)
- {
- i--;
- c = (num % radix);
- if (c < 10)
- buf[i] = c + '0';
- else
- buf[i] = c + 'a' - 10;
- num /= radix;
- }
-
- buf[n] = '\000';
+ expected_messages = g_slist_append (expected_messages, expected);
}
-/* string size big enough to hold level prefix */
-#define STRING_BUFFER_SIZE (FORMAT_UNSIGNED_BUFSIZE + 32)
-
-#define ALERT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)
-
-/* these are emitted by the default log handler */
-#define DEFAULT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE)
-/* these are filtered by G_MESSAGES_DEBUG by the default log handler */
-#define INFO_LEVELS (G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG)
-
-static int
-mklevel_prefix (gchar level_prefix[STRING_BUFFER_SIZE],
- GLogLevelFlags log_level)
+void
+g_test_assert_expected_messages_internal (const char *domain,
+ const char *file,
+ int line,
+ const char *func)
{
- gboolean to_stdout = TRUE;
+ if (expected_messages)
+ {
+ GTestExpectedMessage *expected;
+ gchar level_prefix[STRING_BUFFER_SIZE];
+ gchar *message;
- /* we may not call _any_ GLib functions here */
+ expected = expected_messages->data;
- switch (log_level & G_LOG_LEVEL_MASK)
- {
- case G_LOG_LEVEL_ERROR:
- strcpy (level_prefix, "ERROR");
- to_stdout = FALSE;
- break;
- case G_LOG_LEVEL_CRITICAL:
- strcpy (level_prefix, "CRITICAL");
- to_stdout = FALSE;
- break;
- case G_LOG_LEVEL_WARNING:
- strcpy (level_prefix, "WARNING");
- to_stdout = FALSE;
- break;
- case G_LOG_LEVEL_MESSAGE:
- strcpy (level_prefix, "Message");
- to_stdout = FALSE;
- break;
- case G_LOG_LEVEL_INFO:
- strcpy (level_prefix, "INFO");
- break;
- case G_LOG_LEVEL_DEBUG:
- strcpy (level_prefix, "DEBUG");
- break;
- default:
- if (log_level)
- {
- strcpy (level_prefix, "LOG-");
- format_unsigned (level_prefix + 4, log_level & G_LOG_LEVEL_MASK, 16);
- }
- else
- strcpy (level_prefix, "LOG");
- break;
+ mklevel_prefix (level_prefix, expected->log_level);
+ message = g_strdup_printf ("Did not see expected message %s: %s",
+ level_prefix, expected->pattern);
+ g_assertion_message (domain, file, line, func, message);
+ g_free (message);
}
- if (log_level & G_LOG_FLAG_RECURSION)
- strcat (level_prefix, " (recursed)");
- if (log_level & ALERT_LEVELS)
- strcat (level_prefix, " **");
-
-#ifdef G_OS_WIN32
- win32_keep_fatal_message = (log_level & G_LOG_FLAG_FATAL) != 0;
-#endif
- return to_stdout ? 1 : 2;
}
+/**
+ * g_test_assert_expected_messages:
+ *
+ * Asserts that all messages previously indicated via
+ * g_test_expect_message() have been seen and suppressed.
+ *
+ * Since: 2.34
+ */
+
void
_g_log_fallback_handler (const gchar *log_domain,
GLogLevelFlags log_level,
diff --git a/glib/gtestutils.h b/glib/gtestutils.h
index 3cd173c..90287d3 100644
--- a/glib/gtestutils.h
+++ b/glib/gtestutils.h
@@ -296,6 +296,16 @@ void
g_test_log_set_fatal_handler (GTestLogFatalFunc log_func,
gpointer user_data);
+void g_test_expect_message (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *pattern);
+void g_test_assert_expected_messages_internal (const char *domain,
+ const char *file,
+ int line,
+ const char *func);
+
+#define g_test_assert_expected_messages() g_test_assert_expected_messages_internal (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC)
+
G_END_DECLS
#endif /* __G_TEST_UTILS_H__ */
diff --git a/glib/tests/testing.c b/glib/tests/testing.c
index 2b257f2..76e358c 100644
--- a/glib/tests/testing.c
+++ b/glib/tests/testing.c
@@ -236,6 +236,91 @@ test_fatal_log_handler (void)
g_test_trap_assert_failed ();
}
+static void
+expected_messages_helper (void)
+{
+ g_warning ("This is a %d warning", g_random_int ());
+ g_return_if_reached ();
+}
+
+static void
+test_expected_messages (void)
+{
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ {
+ expected_messages_helper ();
+ exit (0);
+ }
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*This is a * warning*");
+ g_test_trap_assert_stderr_unmatched ("*should not be reached*");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ {
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+ "This is a * warning");
+ expected_messages_helper ();
+ exit (0);
+ }
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr_unmatched ("*This is a * warning*");
+ g_test_trap_assert_stderr ("*should not be reached*");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ {
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+ "*should not be *");
+ expected_messages_helper ();
+ exit (0);
+ }
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr_unmatched ("*should not be reached*");
+ g_test_trap_assert_stderr ("*Did not see expected message CRITICAL*should not be *WARNING*This is a * warning*");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ {
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+ "This is a * warning");
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+ "*should not be reached");
+ expected_messages_helper ();
+ g_test_assert_expected_messages ();
+ exit (0);
+ }
+ g_test_trap_assert_passed ();
+ g_test_trap_assert_stderr ("");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ {
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+ "This is a * warning");
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+ "*should not be reached");
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+ "nope");
+ expected_messages_helper ();
+ /* If we don't assert, it won't notice the missing message */
+ exit (0);
+ }
+ g_test_trap_assert_passed ();
+ g_test_trap_assert_stderr ("");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ {
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+ "This is a * warning");
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+ "*should not be reached");
+ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+ "nope");
+ expected_messages_helper ();
+ g_test_assert_expected_messages ();
+ exit (0);
+ }
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*Did not see expected message CRITICAL*nope*");
+}
+
int
main (int argc,
char *argv[])
@@ -255,6 +340,7 @@ main (int argc,
if (g_test_slow())
g_test_add_func ("/forking/timeout", test_fork_timeout);
g_test_add_func ("/misc/fatal-log-handler", test_fatal_log_handler);
+ g_test_add_func ("/misc/expected-messages", test_expected_messages);
return g_test_run();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]