diff --git a/.gitignore b/.gitignore index e7ab0e90..a016d910 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ xdg-app xdg-app-helper xdg-app-session-helper xdg-document-portal +testdb doc/*.1 *~ profile/xdg-app.sh diff --git a/Makefile.am b/Makefile.am index c23cff8c..5f765e32 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,6 +50,8 @@ include app/Makefile.am.inc include session-helper/Makefile.am.inc include dbus-proxy/Makefile.am.inc include document-portal/Makefile.am.inc +include Makefile.gtester +include tests/Makefile.am.inc completiondir = $(datadir)/bash-completion/completions completion_DATA = completion/xdg-app diff --git a/Makefile.gtester b/Makefile.gtester new file mode 100644 index 00000000..fdc00103 --- /dev/null +++ b/Makefile.gtester @@ -0,0 +1,66 @@ +# initialize variables for unconditional += appending +TEST_PROGS = + +### testing rules + +# test: run all tests in cwd and subdirs +test: test-nonrecursive + @ for subdir in $(SUBDIRS) . ; do \ + test "$$subdir" = "." -o "$$subdir" = "po" || \ + ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ + done + +# test-nonrecursive: run tests only in cwd +test-nonrecursive: ${TEST_PROGS} + @test -z "${TEST_PROGS}" || G_DEBUG=gc-friendly MALLOC_CHECK_=2 MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) gtester --verbose ${TEST_PROGS} + +# test-report: run tests in subdirs and generate report +# perf-report: run tests in subdirs with -m perf and generate report +# full-report: like test-report: with -m perf and -m slow +test-report perf-report full-report: ${TEST_PROGS} + @test -z "${TEST_PROGS}" || { \ + case $@ in \ + test-report) test_options="-k";; \ + perf-report) test_options="-k -m=perf";; \ + full-report) test_options="-k -m=perf -m=slow";; \ + esac ; \ + if test -z "$$GTESTER_LOGDIR" ; then \ + gtester --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \ + elif test -n "${TEST_PROGS}" ; then \ + gtester --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \ + fi ; \ + } + @ ignore_logdir=true ; \ + if test -z "$$GTESTER_LOGDIR" ; then \ + GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \ + ignore_logdir=false ; \ + fi ; \ + if test -d "$(top_srcdir)/.git" ; then \ + REVISION=`git describe` ; \ + else \ + REVISION=$(VERSION) ; \ + fi ; \ + for subdir in $(SUBDIRS) . ; do \ + test "$$subdir" = "." -o "$$subdir" = "po" || \ + ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ + done ; \ + $$ignore_logdir || { \ + echo '' > $@.xml ; \ + echo '' >> $@.xml ; \ + echo '' >> $@.xml ; \ + echo ' $(PACKAGE)' >> $@.xml ; \ + echo ' $(VERSION)' >> $@.xml ; \ + echo " $$REVISION" >> $@.xml ; \ + echo '' >> $@.xml ; \ + for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \ + sed '1,1s/^?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \ + done ; \ + echo >> $@.xml ; \ + echo '' >> $@.xml ; \ + rm -rf "$$GTESTER_LOGDIR"/ ; \ + gtester-report --version 2>/dev/null 1>&2 ; test "$$?" != 0 || gtester-report $@.xml >$@.html ; \ + } +.PHONY: test test-report perf-report full-report test-nonrecursive + +# run tests in cwd as part of make check +check-local: test-nonrecursive diff --git a/tests/Makefile.am.inc b/tests/Makefile.am.inc new file mode 100644 index 00000000..94867658 --- /dev/null +++ b/tests/Makefile.am.inc @@ -0,0 +1,11 @@ +TEST_PROGS += testdb +testdb_CFLAGS = $(BASE_CFLAGS) -DDB_DIR=\"$(abs_srcdir)/tests/dbs\" +testdb_LDADD = \ + $(BASE_LIBS) \ + $(OSTREE_LIBS) \ + libglnx.la \ + libxdgapp.la \ + $(NULL) +testdb_SOURCES = tests/testdb.c + +noinst_PROGRAMS = $(TEST_PROGS) diff --git a/tests/dbs/no_tables b/tests/dbs/no_tables new file mode 100644 index 00000000..c700bdb1 Binary files /dev/null and b/tests/dbs/no_tables differ diff --git a/tests/testdb.c b/tests/testdb.c new file mode 100644 index 00000000..1ce87033 --- /dev/null +++ b/tests/testdb.c @@ -0,0 +1,360 @@ +#include "config.h" + +#include +#include + +/* +static void +dump_db (XdgAppDb *db) +{ + g_autofree char *s = xdg_app_db_print (db); + g_printerr ("\n%s\n", s); +} +*/ + +static XdgAppDb * +create_test_db (gboolean serialized) +{ + XdgAppDb *db; + g_autoptr(XdgAppDbEntry) entry1 = NULL; + g_autoptr(XdgAppDbEntry) entry2 = NULL; + g_autoptr(XdgAppDbEntry) entry3 = NULL; + g_autoptr(XdgAppDbEntry) entry4 = NULL; + g_autoptr(XdgAppDbEntry) entry5 = NULL; + g_autoptr(XdgAppDbEntry) entry6 = NULL; + g_autoptr(XdgAppDbEntry) entry7 = NULL; + GError *error = NULL; + const char *permissions1[] = { "read", "write", NULL }; + const char *permissions2[] = { "read", NULL }; + const char *permissions3[] = { "write", NULL }; + + db = xdg_app_db_new (NULL, FALSE, &error); + g_assert_no_error (error); + g_assert (db != NULL); + + { + glnx_strfreev char **ids = xdg_app_db_list_ids (db); + g_assert (ids != NULL); + g_assert (ids[0] == NULL); + } + + { + glnx_strfreev char **apps = xdg_app_db_list_apps (db); + g_assert (apps != NULL); + g_assert (apps[0] == NULL); + } + + entry1 = xdg_app_db_entry_new (g_variant_new_string ("foo-data")); + entry2 = xdg_app_db_entry_set_app_permissions (entry1, "org.test.bapp", permissions2); + entry3 = xdg_app_db_entry_set_app_permissions (entry2, "org.test.app", permissions1); + entry4 = xdg_app_db_entry_set_app_permissions (entry3, "org.test.capp", permissions1); + + xdg_app_db_set_entry (db, "foo", entry4); + + entry5 = xdg_app_db_entry_new (g_variant_new_string ("bar-data")); + entry6 = xdg_app_db_entry_set_app_permissions (entry5, "org.test.app", permissions2); + entry7 = xdg_app_db_entry_set_app_permissions (entry6, "org.test.dapp", permissions3); + + xdg_app_db_set_entry (db, "bar", entry7); + + if (serialized) + xdg_app_db_update (db); + + return db; +} + +static void +verify_test_db (XdgAppDb *db) +{ + glnx_strfreev char **ids; + g_autofree const char **apps1 = NULL; + g_autofree const char **apps2 = NULL; + glnx_strfreev char **all_apps = NULL; + + ids = xdg_app_db_list_ids (db); + g_assert (g_strv_length (ids) == 2); + g_assert (g_strv_contains ((const char **)ids, "foo")); + g_assert (g_strv_contains ((const char **)ids, "bar")); + + { + g_autoptr(XdgAppDbEntry) entry = NULL; + g_autofree const char **permissions1 = NULL; + g_autofree const char **permissions2 = NULL; + g_autofree const char **permissions3 = NULL; + g_autofree const char **permissions4 = NULL; + g_autoptr(GVariant) data1 = NULL; + + entry = xdg_app_db_lookup (db, "foo"); + g_assert (entry != NULL); + data1 = xdg_app_db_entry_get_data (entry); + g_assert (data1 != NULL); + g_assert_cmpstr (g_variant_get_type_string (data1), ==, "s"); + g_assert_cmpstr (g_variant_get_string (data1, NULL), ==, "foo-data"); + apps1 = xdg_app_db_entry_list_apps (entry); + g_assert (g_strv_length ((char **)apps1) == 3); + g_assert (g_strv_contains (apps1, "org.test.app")); + g_assert (g_strv_contains (apps1, "org.test.bapp")); + g_assert (g_strv_contains (apps1, "org.test.capp")); + permissions1 = xdg_app_db_entry_list_permissions (entry, "org.test.app"); + g_assert (g_strv_length ((char **)permissions1) == 2); + g_assert (g_strv_contains (permissions1, "read")); + g_assert (g_strv_contains (permissions1, "write")); + permissions2 = xdg_app_db_entry_list_permissions (entry, "org.test.bapp"); + g_assert (g_strv_length ((char **)permissions2) == 1); + g_assert (g_strv_contains (permissions2, "read")); + permissions3 = xdg_app_db_entry_list_permissions (entry, "org.test.capp"); + g_assert (g_strv_length ((char **)permissions3) == 2); + g_assert (g_strv_contains (permissions3, "read")); + g_assert (g_strv_contains (permissions3, "write")); + permissions4 = xdg_app_db_entry_list_permissions (entry, "org.test.noapp"); + g_assert (permissions4 != NULL); + g_assert (g_strv_length ((char **)permissions4) == 0); + } + + { + g_autoptr(XdgAppDbEntry) entry = NULL; + g_autofree const char **permissions5 = NULL; + g_autofree const char **permissions6 = NULL; + g_autoptr(GVariant) data2 = NULL; + + entry = xdg_app_db_lookup (db, "bar"); + g_assert (entry != NULL); + data2 = xdg_app_db_entry_get_data (entry); + g_assert (data2 != NULL); + g_assert_cmpstr (g_variant_get_type_string (data2), ==, "s"); + g_assert_cmpstr (g_variant_get_string (data2, NULL), ==, "bar-data"); + apps2 = xdg_app_db_entry_list_apps (entry); + g_assert (g_strv_length ((char **)apps2) == 2); + g_assert (g_strv_contains (apps2, "org.test.app")); + g_assert (g_strv_contains (apps2, "org.test.dapp")); + permissions5 = xdg_app_db_entry_list_permissions (entry, "org.test.app"); + g_assert (g_strv_length ((char **)permissions5) == 1); + g_assert (g_strv_contains (permissions5, "read")); + permissions6 = xdg_app_db_entry_list_permissions (entry, "org.test.dapp"); + g_assert (g_strv_length ((char **)permissions6) == 1); + g_assert (g_strv_contains (permissions6, "write")); + } + + { + g_autoptr(XdgAppDbEntry) entry = NULL; + entry = xdg_app_db_lookup (db, "gazonk"); + g_assert (entry == NULL); + } + + all_apps = xdg_app_db_list_apps (db); + g_assert (g_strv_length (all_apps) == 4); + g_assert (g_strv_contains ((const char **)all_apps, "org.test.app")); + g_assert (g_strv_contains ((const char **)all_apps, "org.test.bapp")); + g_assert (g_strv_contains ((const char **)all_apps, "org.test.capp")); + g_assert (g_strv_contains ((const char **)all_apps, "org.test.dapp")); +} + +static void +test_db_open (void) +{ + GError *error = NULL; + XdgAppDb *db; + + db = xdg_app_db_new (DB_DIR "/does_not_exist", TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT); + g_assert (db == NULL); + g_clear_error (&error); + + db = xdg_app_db_new (DB_DIR "/does_not_exist", FALSE, &error); + g_assert_no_error (error); + g_assert (db != NULL); + g_clear_error (&error); + g_object_unref (db); + + db = xdg_app_db_new (DB_DIR "/no_tables", TRUE, &error); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); + g_assert (db == NULL); + g_clear_error (&error); +} + +static void +test_serialize (void) +{ + g_autoptr(XdgAppDb) db = NULL; + g_autoptr(XdgAppDb) db2 = NULL; + g_autofree char *dump1 = NULL; + g_autofree char *dump2 = NULL; + g_autofree char *dump3 = NULL; + GError *error = NULL; + char tmpfile[] = "/tmp/testdbXXXXXX"; + int fd; + + db = create_test_db (FALSE); + + verify_test_db (db); + + dump1 = xdg_app_db_print (db); + + g_assert (xdg_app_db_is_dirty (db)); + + xdg_app_db_update (db); + + verify_test_db (db); + + g_assert (!xdg_app_db_is_dirty (db)); + + dump2 = xdg_app_db_print (db); + + g_assert_cmpstr (dump1, ==, dump2); + + fd = g_mkstemp (tmpfile); + close (fd); + + xdg_app_db_set_path (db, tmpfile); + + xdg_app_db_save_content (db, &error); + g_assert_no_error (error); + + db2 = xdg_app_db_new (tmpfile, TRUE, &error); + g_assert_no_error (error); + g_assert (db2 != NULL); + + dump3 = xdg_app_db_print (db2); + + g_assert_cmpstr (dump1, ==, dump3); + + unlink (tmpfile); +} + +static void +test_modify (void) +{ + g_autoptr(XdgAppDb) db = NULL; + const char *permissions[] = { "read", "write", "execute", NULL }; + const char *no_permissions[] = { NULL }; + + db = create_test_db (FALSE); + + /* Add permission */ + { + g_autoptr(XdgAppDbEntry) entry1 = NULL; + g_autoptr(XdgAppDbEntry) entry2 = NULL; + + entry1 = xdg_app_db_lookup (db, "foo"); + entry2 = xdg_app_db_entry_set_app_permissions (entry1, "org.test.app", permissions); + xdg_app_db_set_entry (db, "foo", entry2); + } + + /* Add entry */ + { + g_autoptr(XdgAppDbEntry) entry1 = NULL; + g_autoptr(XdgAppDbEntry) entry2 = NULL; + + entry1 = xdg_app_db_entry_new (g_variant_new_string ("gazonk-data")); + entry2 = xdg_app_db_entry_set_app_permissions (entry1, "org.test.eapp", permissions); + xdg_app_db_set_entry (db, "gazonk", entry2); + } + + /* Remove permission */ + { + g_autoptr(XdgAppDbEntry) entry1 = NULL; + g_autoptr(XdgAppDbEntry) entry2 = NULL; + + entry1 = xdg_app_db_lookup (db, "bar"); + entry2 = xdg_app_db_entry_set_app_permissions (entry1, "org.test.dapp", no_permissions); + xdg_app_db_set_entry (db, "bar", entry2); + } + + /* Verify */ + { + g_autoptr(XdgAppDbEntry) entry5 = NULL; + g_autoptr(XdgAppDbEntry) entry6 = NULL; + g_autoptr(XdgAppDbEntry) entry7 = NULL; + g_autofree const char **apps1 = NULL; + g_autofree const char **apps2 = NULL; + glnx_strfreev char **apps3 = NULL; + g_autofree const char **permissions1 = NULL; + g_autofree const char **permissions2 = NULL; + g_autofree const char **permissions3 = NULL; + + entry5 = xdg_app_db_lookup (db, "foo"); + permissions1 = xdg_app_db_entry_list_permissions (entry5, "org.test.app"); + g_assert (g_strv_length ((char **)permissions1) == 3); + g_assert (g_strv_contains (permissions1, "read")); + g_assert (g_strv_contains (permissions1, "write")); + g_assert (g_strv_contains (permissions1, "execute")); + + entry6 = xdg_app_db_lookup (db, "bar"); + permissions2 = xdg_app_db_entry_list_permissions (entry6, "org.test.dapp"); + g_assert (g_strv_length ((char **)permissions2) == 0); + + entry7 = xdg_app_db_lookup (db, "gazonk"); + permissions3 = xdg_app_db_entry_list_permissions (entry7, "org.test.eapp"); + g_assert (g_strv_length ((char **)permissions3) == 3); + g_assert (g_strv_contains (permissions3, "read")); + g_assert (g_strv_contains (permissions3, "write")); + g_assert (g_strv_contains (permissions3, "execute")); + + apps2 = xdg_app_db_entry_list_apps (entry6); + g_assert_cmpint (g_strv_length ((char **)apps2), ==, 1); + g_assert (g_strv_contains (apps2, "org.test.app")); + + apps3 = xdg_app_db_list_apps (db); + g_assert_cmpint (g_strv_length (apps3), ==, 4); + g_assert (g_strv_contains ((const char **)apps3, "org.test.app")); + g_assert (g_strv_contains ((const char **)apps3, "org.test.bapp")); + g_assert (g_strv_contains ((const char **)apps3, "org.test.capp")); + g_assert (g_strv_contains ((const char **)apps3, "org.test.eapp")); + } + + xdg_app_db_update (db); + + /* Verify after serialize */ + { + g_autoptr(XdgAppDbEntry) entry5 = NULL; + g_autoptr(XdgAppDbEntry) entry6 = NULL; + g_autoptr(XdgAppDbEntry) entry7 = NULL; + g_autofree const char **apps1 = NULL; + g_autofree const char **apps2 = NULL; + glnx_strfreev char **apps3 = NULL; + g_autofree const char **permissions1 = NULL; + g_autofree const char **permissions2 = NULL; + g_autofree const char **permissions3 = NULL; + + entry5 = xdg_app_db_lookup (db, "foo"); + permissions1 = xdg_app_db_entry_list_permissions (entry5, "org.test.app"); + g_assert (g_strv_length ((char **)permissions1) == 3); + g_assert (g_strv_contains (permissions1, "read")); + g_assert (g_strv_contains (permissions1, "write")); + g_assert (g_strv_contains (permissions1, "execute")); + + entry6 = xdg_app_db_lookup (db, "bar"); + permissions2 = xdg_app_db_entry_list_permissions (entry6, "org.test.dapp"); + g_assert (g_strv_length ((char **)permissions2) == 0); + + entry7 = xdg_app_db_lookup (db, "gazonk"); + permissions3 = xdg_app_db_entry_list_permissions (entry7, "org.test.eapp"); + g_assert (g_strv_length ((char **)permissions3) == 3); + g_assert (g_strv_contains (permissions3, "read")); + g_assert (g_strv_contains (permissions3, "write")); + g_assert (g_strv_contains (permissions3, "execute")); + + apps2 = xdg_app_db_entry_list_apps (entry6); + g_assert_cmpint (g_strv_length ((char **)apps2), ==, 1); + g_assert (g_strv_contains (apps2, "org.test.app")); + + apps3 = xdg_app_db_list_apps (db); + g_assert_cmpint (g_strv_length (apps3), ==, 4); + g_assert (g_strv_contains ((const char **)apps3, "org.test.app")); + g_assert (g_strv_contains ((const char **)apps3, "org.test.bapp")); + g_assert (g_strv_contains ((const char **)apps3, "org.test.capp")); + g_assert (g_strv_contains ((const char **)apps3, "org.test.eapp")); + } +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/db/open", test_db_open); + g_test_add_func ("/db/serialize", test_serialize); + g_test_add_func ("/db/modify", test_modify); + + return g_test_run (); +}