From 64a32db5e1d202b9055dd90dab335f7c2afa62c0 Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Wed, 14 Aug 2019 23:56:28 +0200 Subject: [PATCH] tests: unit test for some util strv functions We didn't use unit tests so far so I have picked up the glib testing framework. While there are better frameworks out there glib's it gets the job done and doesn't impose extra dependencies. For upcoming fixes and refactorings to utils_strv_find_lcs and utils_strv_shorten_file_list I would like to make sure to not introduce regressions and unit tests are ideal for that. A function to be tested must be exported by libgeany.so. Use GEANY_EXPORT_SYMBOL for that. It's not the same as GEANY_API_SYMBOL to avoid the impression that it's OK to use them in plugins. Also no doxygen comments for those. I resurrected utils_strv_new() because it's convinient to have in the tests. The function has its own test suite since it's otherwise unused. --- src/utils.c | 15 +- src/utils.h | 4 + tests/Makefile.am | 11 ++ tests/test_utils.c | 367 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 389 insertions(+), 8 deletions(-) create mode 100644 tests/test_utils.c diff --git a/src/utils.c b/src/utils.c index 28349e59..95099658 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1308,12 +1308,10 @@ void utils_free_pointers(gsize arg_count, ...) } -/* currently unused */ -#if 0 /* Creates a string array deep copy of a series of non-NULL strings. - * The first argument is nothing special. - * The list must be ended with NULL. - * If first is NULL, NULL is returned. */ + * The first argument is nothing special and must not be NULL. + * The list must be terminated with NULL. */ +GEANY_EXPORT_SYMBOL gchar **utils_strv_new(const gchar *first, ...) { gsize strvlen, i; @@ -1343,7 +1341,6 @@ gchar **utils_strv_new(const gchar *first, ...) strv[i] = NULL; return strv; } -#endif /** @@ -2053,7 +2050,8 @@ gchar **utils_strv_join(gchar **first, gchar **second) * @return The common prefix that is part of all strings (maybe empty), or NULL if an empty list * was passed in. */ -static gchar *utils_strv_find_common_prefix(gchar **strv, gssize strv_len) +GEANY_EXPORT_SYMBOL +gchar *utils_strv_find_common_prefix(gchar **strv, gssize strv_len) { gsize num; @@ -2087,7 +2085,8 @@ static gchar *utils_strv_find_common_prefix(gchar **strv, gssize strv_len) * * @return The common prefix that is part of all strings. */ -static gchar *utils_strv_find_lcs(gchar **strv, gssize strv_len) +GEANY_EXPORT_SYMBOL +gchar *utils_strv_find_lcs(gchar **strv, gssize strv_len) { gchar *first, *_sub, *sub; gsize num; diff --git a/src/utils.h b/src/utils.h index 30af8c78..7fd02d59 100644 --- a/src/utils.h +++ b/src/utils.h @@ -300,6 +300,10 @@ gchar **utils_strv_new(const gchar *first, ...) G_GNUC_NULL_TERMINATED; gchar **utils_strv_join(gchar **first, gchar **second) G_GNUC_WARN_UNUSED_RESULT; +gchar *utils_strv_find_common_prefix(gchar **strv, gssize strv_len) G_GNUC_WARN_UNUSED_RESULT; + +gchar *utils_strv_find_lcs(gchar **strv, gssize strv_len) G_GNUC_WARN_UNUSED_RESULT; + gchar **utils_strv_shorten_file_list(gchar **file_names, gssize file_names_len); GSList *utils_get_config_files(const gchar *subdir); diff --git a/tests/Makefile.am b/tests/Makefile.am index 82465507..484e3d77 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,2 +1,13 @@ SUBDIRS = ctags + +AM_CPPFLAGS = -DGEANY_PRIVATE -DG_LOG_DOMAIN=\""Geany"\" @GTK_CFLAGS@ @GTHREAD_CFLAGS@ +AM_CPPFLAGS += -I$(top_srcdir)/src + +AM_LDFLAGS = $(GTK_LIBS) $(GTHREAD_LIBS) $(INTLLIBS) -no-install + +check_PROGRAMS = test_utils + +test_utils_LDADD = $(top_builddir)/src/libgeany.la + +TESTS = $(check_PROGRAMS) diff --git a/tests/test_utils.c b/tests/test_utils.c new file mode 100644 index 00000000..44b53aa8 --- /dev/null +++ b/tests/test_utils.c @@ -0,0 +1,367 @@ + +#include "utils.h" + +#include "gtkcompat.h" + +#define UTIL_TEST_ADD(path, func) g_test_add_func("/utils/" path, func); + + +static void test_utils_strv_new(void) +{ + gchar **data; + + data = utils_strv_new("1", NULL); + g_assert_nonnull(data); + g_assert_cmpint(g_strv_length(data), ==, 1); + g_assert_cmpstr(data[0], ==, "1"); + g_strfreev(data); + + data = utils_strv_new("1", "2", "3", NULL); + g_assert_nonnull(data); + g_assert_cmpint(g_strv_length(data), ==, 3); + g_assert_cmpstr(data[0], ==, "1"); + g_assert_cmpstr(data[1], ==, "2"); + g_assert_cmpstr(data[2], ==, "3"); + g_strfreev(data); + + data = utils_strv_new("1", "", "", "4", NULL); + g_assert_nonnull(data); + g_assert_cmpint(g_strv_length(data), ==, 4); + g_assert_cmpstr(data[0], ==, "1"); + g_assert_cmpstr(data[1], ==, ""); + g_assert_cmpstr(data[2], ==, ""); + g_assert_cmpstr(data[3], ==, "4"); + g_strfreev(data); +} + +static void test_utils_strv_find_common_prefix(void) +{ + gchar **data, *s; + + s = utils_strv_find_common_prefix(NULL, 0); + g_assert_null(s); + + data = utils_strv_new("", NULL); + s = utils_strv_find_common_prefix(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ""); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("1", "2", "3", NULL); + s = utils_strv_find_common_prefix(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ""); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("abc", "amn", "axy", NULL); + s = utils_strv_find_common_prefix(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "a"); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("abc", "", "axy", NULL); + s = utils_strv_find_common_prefix(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ""); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("22", "23", "33", NULL); + s = utils_strv_find_common_prefix(data, 1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "22"); + g_free(s); + s = utils_strv_find_common_prefix(data, 2); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "2"); + g_free(s); + s = utils_strv_find_common_prefix(data, 3); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ""); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("/home/user/src/geany/src/stash.c", + "/home/user/src/geany/src/sidebar.c", + "/home/user/src/geany/src/sidebar.h", + "/home/user/src/geany/src/sidebar.h", + "/home/user/src/geany/src/main.c", + "/home/user/src/geany-plugins/addons/src/addons.c", + NULL); + s = utils_strv_find_common_prefix(data, 4); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "/home/user/src/geany/src/s"); + g_free(s); + s = utils_strv_find_common_prefix(data, 5); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "/home/user/src/geany/src/"); + g_free(s); + s = utils_strv_find_common_prefix(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "/home/user/src/geany"); + g_free(s); + g_strfreev(data); +} + +void test_utils_strv_find_lcs(void) +{ + gchar **data, *s; + + s = utils_strv_find_lcs(NULL, 0); + g_assert_null(s); + + data = utils_strv_new("", NULL); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ""); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("1", "2", "3", NULL); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ""); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("abc", "amn", "axy", NULL); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "a"); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("abc", "", "axy", NULL); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ""); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("22", "23", "33", NULL); + s = utils_strv_find_lcs(data, 1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "22"); + g_free(s); + s = utils_strv_find_lcs(data, 2); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "2"); + g_free(s); + s = utils_strv_find_lcs(data, 3); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ""); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("/home/user/src/geany/src/stash.c", + "/home/user/src/geany/src/sidebar.c", + "/home/user/src/geany/src/sidebar.h", + "/home/user/src/geany/src/sidebar.h", + "/home/user/src/geany/src/main.c", + "/home/user/src/geany-plugins/addons/src/addons.c", + NULL); + s = utils_strv_find_lcs(data, 4); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "/home/user/src/geany/src/s"); + g_free(s); + s = utils_strv_find_lcs(data, 5); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "/home/user/src/geany/src/"); + g_free(s); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "/home/user/src/geany"); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("/src/a/app-1.2.3/src/lib/module/source.c", + "/src/b/app-2.2.3/src/module/source.c", NULL); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "/module/source.c"); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("/src/a/app-1.2.3/src/lib/module/", + "/src/b/app-2.2.3/src/module/", NULL); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, ".2.3/src/"); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("a123b", "b123c", "c123d", NULL); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "123"); + g_free(s); + g_strfreev(data); + + data = utils_strv_new("/usr/local/bin/geany", "/usr/bin/geany", "/home/user/src/geany/src/geany", NULL); + s = utils_strv_find_lcs(data, -1); + g_assert_nonnull(s); + g_assert_cmpstr(s, ==, "/geany"); + g_free(s); + g_strfreev(data); +} + + +/* g_strv_equal is too recent */ +static gboolean strv_eq(gchar **strv1, gchar **strv2) +{ + while(1) { + gchar *s1 = *strv1++; + gchar *s2 = *strv2++; + + if (!s1 && !s2) + return TRUE; + else if (s1 && !s2) + return FALSE; + else if (s2 && !s1) + return FALSE; + else if (!g_str_equal(s1, s2)) + return FALSE; + } +} + +void test_utils_strv_shorten_file_list(void) +{ + gchar **data, **expected, **result; + gchar *empty[] = { NULL }; + + result = utils_strv_shorten_file_list(NULL, 0); + expected = empty; + g_assert_true(strv_eq(result, expected)); + g_strfreev(result); + + data = utils_strv_new("", NULL); + result = utils_strv_shorten_file_list(data, -1); + expected = data; + g_assert_true(strv_eq(result, expected)); + g_strfreev(result); + g_strfreev(data); + + data = utils_strv_new("1", "2", "3", NULL); + result = utils_strv_shorten_file_list(data, -1); + expected = data; + g_assert_true(strv_eq(result, expected)); + g_strfreev(result); + g_strfreev(data); + + data = utils_strv_new("abc", "amn", "axy", NULL); + result = utils_strv_shorten_file_list(data, -1); + expected = data; + g_assert_true(strv_eq(result, expected)); + g_strfreev(result); + g_strfreev(data); + + data = utils_strv_new("abc", "", "axy", NULL); + result = utils_strv_shorten_file_list(data, -1); + expected = data; + g_assert_true(strv_eq(result, expected)); + g_strfreev(result); + g_strfreev(data); + + data = utils_strv_new("22", "23", "33", NULL); + result = utils_strv_shorten_file_list(data, 1); + expected = utils_strv_new("22", NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + g_strfreev(result); + result = utils_strv_shorten_file_list(data, 2); + expected = utils_strv_new("22", "23", NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + g_strfreev(result); + result = utils_strv_shorten_file_list(data, 3); + expected = utils_strv_new("22", "23", "33", NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + g_strfreev(result); + g_strfreev(data); + + data = utils_strv_new("/home/user/src/geany/src/stash.c", + "/home/user/src/geany/src/sidebar.c", + "/home/user/src/geany/src/sidebar.h", + "/home/user/src/geany/src/sidebar.h", + "/home/user/src/geany/src/main.c", + "/home/user/src/geany-plugins/addons/src/addons.c", + NULL); + result = utils_strv_shorten_file_list(data, 4); + expected = utils_strv_new("stash.c", "sidebar.c", "sidebar.h", "sidebar.h", NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + result = utils_strv_shorten_file_list(data, 5); + expected = utils_strv_new("stash.c", "sidebar.c", "sidebar.h", "sidebar.h", "main.c", NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + result = utils_strv_shorten_file_list(data, -1); + expected = utils_strv_new("geany/src/stash.c", "geany/src/sidebar.c", + "geany/src/sidebar.h", "geany/src/sidebar.h", "geany/src/main.c", + "geany-plugins/addons/src/addons.c", + NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + g_strfreev(data); + + data = utils_strv_new("/home/user1/src/geany/src/stash.c", + "/home/user2/src/geany/src/sidebar.c", + "/home/user3/src/geany/src/sidebar.h", + "/home/user4/src/geany/src/sidebar.h", + "/home/user5/src/geany/src/main.c", + NULL); + result = utils_strv_shorten_file_list(data, -1); + expected = utils_strv_new("user1/.../stash.c", + "user2/.../sidebar.c", + "user3/.../sidebar.h", + "user4/.../sidebar.h", + "user5/.../main.c", + NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + g_strfreev(result); + g_strfreev(data); + + data = utils_strv_new("/aaa/bbb/cc/ccccc/ddddd", "/aaa/bbb/xxx/yyy/cc/ccccc/ddddd", NULL); + result = utils_strv_shorten_file_list(data, -1); + expected = utils_strv_new("cc/.../ddddd", "xxx/yyy/cc/.../ddddd", NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + g_strfreev(result); + g_strfreev(data); + + /* This case shows a weakness. It would be better to elipsize "module". Instead, + * nothing is elipsized as the code determines ".2.3/src/" to be the + * longest common substring. Then it reduces it to directory components, so "src" remains. + * This is shorter than the threshold of 5 chars and consequently not elipsized + * + * Plain utils_strv_find_lcs() actually finds /module/source.c, + * but utils_strv_shorten_file_list() calls it without the file name part, so + * ".2.3/src/" is longer than "/module/". This is illustrated in the test + * test_utils_strv_find_lcs() + */ + data = utils_strv_new("/src/a/app-1.2.3/src/lib/module/source.c", + "/src/b/app-2.2.3/src/module/source.c", NULL); + result = utils_strv_shorten_file_list(data, -1); + expected = utils_strv_new("a/app-1.2.3/src/lib/module/source.c", + "b/app-2.2.3/src/module/source.c", NULL); + g_assert_true(strv_eq(result, expected)); + g_strfreev(expected); + g_strfreev(result); + g_strfreev(data); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + UTIL_TEST_ADD("strv_join", test_utils_strv_new); + UTIL_TEST_ADD("strv_find_common_prefix", test_utils_strv_find_common_prefix); + UTIL_TEST_ADD("strv_find_lcs", test_utils_strv_find_lcs); + UTIL_TEST_ADD("strv_shorten_file_list", test_utils_strv_shorten_file_list); + + return g_test_run(); +}