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.
This commit is contained in:
Thomas Martitz 2019-08-14 23:56:28 +02:00
parent 9c91c28780
commit 64a32db5e1
4 changed files with 389 additions and 8 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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)

367
tests/test_utils.c Normal file
View File

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