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(); +}