From f98f2c5d0fa608d2300d3a6e60e55918d09efa27 Mon Sep 17 00:00:00 2001 From: Niels De Graef Date: Sun, 1 Nov 2020 12:03:04 +0100 Subject: [PATCH] gtestutils: Add g_assert_cmpstrv() Add a test util function that helps asserting two string arrays are the same, and which adds some useful information if they're not. Fixes: #2015 --- docs/reference/glib/glib-sections.txt | 1 + glib/gtestutils.c | 49 ++++++++++++++++++++++ glib/gtestutils.h | 55 +++++++++++++++++++++++++ glib/tests/testing.c | 59 +++++++++++++++++++++++++++ 4 files changed, 164 insertions(+) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 02bb2ba50..68574040d 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -3547,6 +3547,7 @@ g_assert g_assert_not_reached g_assert_cmpstr +g_assert_cmpstrv g_assert_cmpint g_assert_cmpuint g_assert_cmphex diff --git a/glib/gtestutils.c b/glib/gtestutils.c index 974785a44..1bc6aa2f3 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -576,6 +576,30 @@ * Since: 2.16 */ +/** + * g_assert_cmpstrv: + * @strv1: (nullable): a string array (may be %NULL) + * @strv2: (nullable): another string array (may be %NULL) + * + * Debugging macro to check if two %NULL-terminated string arrays (i.e. 2 + * #GStrv) are equal. If they are not equal, an error message is logged and the + * application is either terminated or the testcase marked as failed. + * If both arrays are %NULL, the check passes. If one array is %NULL but the + * other is not, an error message is logged. + * + * The effect of `g_assert_cmpstrv (strv1, strv2)` is the same as + * `g_assert_true (g_strv_equal (strv1, strv2))` (if both arrays are not + * %NULL). The advantage of this macro is that it can produce a message that + * includes how @strv1 and @strv2 are different. + * + * |[ + * const char *expected[] = { "one", "two", "three", NULL }; + * g_assert_cmpstrv (mystrv, expected); + * ]| + * + * Since: 2.68 + */ + /** * g_assert_cmpint: * @n1: an integer @@ -3018,6 +3042,31 @@ g_assertion_message_cmpstr (const char *domain, g_free (s); } +void +g_assertion_message_cmpstrv (const char *domain, + const char *file, + int line, + const char *func, + const char *expr, + const char * const *arg1, + const char * const *arg2, + gsize first_wrong_idx) +{ + const char *s1 = arg1[first_wrong_idx], *s2 = arg2[first_wrong_idx]; + char *a1, *a2, *s, *t1 = NULL, *t2 = NULL; + + a1 = arg1 ? g_strconcat ("\"", t1 = g_strescape (s1, NULL), "\"", NULL) : g_strdup ("NULL"); + a2 = arg2 ? g_strconcat ("\"", t2 = g_strescape (s2, NULL), "\"", NULL) : g_strdup ("NULL"); + g_free (t1); + g_free (t2); + s = g_strdup_printf ("assertion failed (%s): first differing element at index %" G_GSIZE_FORMAT ": %s does not equal %s", + expr, first_wrong_idx, a1, a2); + g_free (a1); + g_free (a2); + g_assertion_message (domain, file, line, func, s); + g_free (s); +} + void g_assertion_message_error (const char *domain, const char *file, diff --git a/glib/gtestutils.h b/glib/gtestutils.h index 10b65c164..c7a0c5969 100644 --- a/glib/gtestutils.h +++ b/glib/gtestutils.h @@ -111,6 +111,51 @@ typedef void (*GTestFixtureFunc) (gpointer fixture, } \ } \ G_STMT_END +#define g_assert_cmpstrv(strv1, strv2) \ + G_STMT_START \ + { \ + const char * const *__strv1 = (const char * const *) (strv1); \ + const char * const *__strv2 = (const char * const *) (strv2); \ + if (!__strv1 || !__strv2) \ + { \ + if (__strv1) \ + { \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #strv1 " == " #strv2 "): " #strv2 " is NULL, but " #strv1 " is not"); \ + } \ + else if (__strv2) \ + { \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #strv1 " == " #strv2 "): " #strv1 " is NULL, but " #strv2 " is not"); \ + } \ + } \ + else \ + { \ + guint __l1 = g_strv_length ((char **) __strv1); \ + guint __l2 = g_strv_length ((char **) __strv2); \ + if (__l1 != __l2) \ + { \ + char *__msg; \ + __msg = g_strdup_printf ("assertion failed (" #strv1 " == " #strv2 "): length %u does not equal length %u", __l1, __l2); \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ + g_free (__msg); \ + } \ + else \ + { \ + guint __i; \ + for (__i = 0; __i < __l1; __i++) \ + { \ + if (g_strcmp0 (__strv1[__i], __strv2[__i]) != 0) \ + { \ + g_assertion_message_cmpstrv (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + #strv1 " == " #strv2, \ + __strv1, __strv2, __i); \ + } \ + } \ + } \ + } \ + } \ + G_STMT_END #define g_assert_no_errno(expr) G_STMT_START { \ int __ret, __errsv; \ errno = 0; \ @@ -483,6 +528,16 @@ void g_assertion_message_cmpstr (const char *domain, const char *arg1, const char *cmp, const char *arg2) G_ANALYZER_NORETURN; + +GLIB_AVAILABLE_IN_2_68 +void g_assertion_message_cmpstrv (const char *domain, + const char *file, + int line, + const char *func, + const char *expr, + const char * const *arg1, + const char * const *arg2, + gsize first_wrong_idx) G_ANALYZER_NORETURN; GLIB_AVAILABLE_IN_ALL void g_assertion_message_cmpnum (const char *domain, const char *file, diff --git a/glib/tests/testing.c b/glib/tests/testing.c index ed4c8d6f7..1e6a33742 100644 --- a/glib/tests/testing.c +++ b/glib/tests/testing.c @@ -66,6 +66,40 @@ test_assertions_bad_cmpvariant_values (void) exit (0); } +static void +test_assertions_bad_cmpstrv_null1 (void) +{ + const char *strv[] = { "one", "two", "three", NULL }; + g_assert_cmpstrv (strv, NULL); + exit (0); +} + +static void +test_assertions_bad_cmpstrv_null2 (void) +{ + const char *strv[] = { "one", "two", "three", NULL }; + g_assert_cmpstrv (NULL, strv); + exit (0); +} + +static void +test_assertions_bad_cmpstrv_length (void) +{ + const char *strv1[] = { "one", "two", "three", NULL }; + const char *strv2[] = { "one", "two", NULL }; + g_assert_cmpstrv (strv1, strv2); + exit (0); +} + +static void +test_assertions_bad_cmpstrv_values (void) +{ + const char *strv1[] = { "one", "two", "three", NULL }; + const char *strv2[] = { "one", "too", "three", NULL }; + g_assert_cmpstrv (strv1, strv2); + exit (0); +} + static void test_assertions_bad_cmpstr (void) { @@ -132,6 +166,8 @@ test_assertions_bad_no_errno (void) static void test_assertions (void) { + const char *strv1[] = { "one", "two", "three", NULL }; + const char *strv2[] = { "one", "two", "three", NULL }; GVariant *v1, *v2; gchar *fuu; @@ -160,6 +196,9 @@ test_assertions (void) g_assert_cmpmem ("foo", 0, NULL, 0); g_assert_no_errno (return_no_errno ()); + g_assert_cmpstrv (NULL, NULL); + g_assert_cmpstrv (strv1, strv2); + v1 = g_variant_new_parsed ("['hello', 'there']"); v2 = g_variant_new_parsed ("['hello', 'there']"); @@ -181,6 +220,22 @@ test_assertions (void) g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*assertion failed*"); + g_test_trap_subprocess ("/misc/assertions/subprocess/bad_cmpstrv_null1", 0, 0); + g_test_trap_assert_failed (); + g_test_trap_assert_stderr ("*assertion failed*"); + + g_test_trap_subprocess ("/misc/assertions/subprocess/bad_cmpstrv_null2", 0, 0); + g_test_trap_assert_failed (); + g_test_trap_assert_stderr ("*assertion failed*"); + + g_test_trap_subprocess ("/misc/assertions/subprocess/bad_cmpstrv_length", 0, 0); + g_test_trap_assert_failed (); + g_test_trap_assert_stderr ("*assertion failed*"); + + g_test_trap_subprocess ("/misc/assertions/subprocess/bad_cmpstrv_values", 0, 0); + g_test_trap_assert_failed (); + g_test_trap_assert_stderr ("*assertion failed*"); + g_test_trap_subprocess ("/misc/assertions/subprocess/bad_cmpint", 0, 0); g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*assertion failed*"); @@ -1303,6 +1358,10 @@ main (int argc, g_test_add_func ("/misc/assertions/subprocess/bad_cmpvariant_types", test_assertions_bad_cmpvariant_types); g_test_add_func ("/misc/assertions/subprocess/bad_cmpvariant_values", test_assertions_bad_cmpvariant_values); g_test_add_func ("/misc/assertions/subprocess/bad_cmpstr", test_assertions_bad_cmpstr); + g_test_add_func ("/misc/assertions/subprocess/bad_cmpstrv_null1", test_assertions_bad_cmpstrv_null1); + g_test_add_func ("/misc/assertions/subprocess/bad_cmpstrv_null2", test_assertions_bad_cmpstrv_null2); + g_test_add_func ("/misc/assertions/subprocess/bad_cmpstrv_length", test_assertions_bad_cmpstrv_length); + g_test_add_func ("/misc/assertions/subprocess/bad_cmpstrv_values", test_assertions_bad_cmpstrv_values); g_test_add_func ("/misc/assertions/subprocess/bad_cmpint", test_assertions_bad_cmpint); g_test_add_func ("/misc/assertions/subprocess/bad_cmpmem_len", test_assertions_bad_cmpmem_len); g_test_add_func ("/misc/assertions/subprocess/bad_cmpmem_data", test_assertions_bad_cmpmem_data);