diff --git a/Makefile-tests.am b/Makefile-tests.am index 6a52faeb..c2186707 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -212,7 +212,7 @@ endif _installed_or_uninstalled_test_programs = tests/test-varint tests/test-ot-unix-utils tests/test-bsdiff tests/test-mutable-tree \ tests/test-keyfile-utils tests/test-ot-opt-utils tests/test-ot-tool-util \ tests/test-gpg-verify-result tests/test-checksum tests/test-lzma tests/test-rollsum \ - tests/test-basic-c tests/test-sysroot-c tests/test-pull-c + tests/test-basic-c tests/test-sysroot-c tests/test-pull-c tests/test-repo if ENABLE_EXPERIMENTAL_API test_programs += \ @@ -282,6 +282,9 @@ tests_test_sysroot_c_LDADD = $(TESTS_LDADD) tests_test_pull_c_CFLAGS = $(TESTS_CFLAGS) tests_test_pull_c_LDADD = $(TESTS_LDADD) +tests_test_repo_CFLAGS = $(TESTS_CFLAGS) +tests_test_repo_LDADD = $(TESTS_LDADD) + tests_test_ot_unix_utils_CFLAGS = $(TESTS_CFLAGS) tests_test_ot_unix_utils_LDADD = $(TESTS_LDADD) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 547e1509..e5ce157c 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -287,6 +287,7 @@ ostree_repo_get_path ostree_repo_get_mode ostree_repo_get_config ostree_repo_get_dfd +ostree_repo_hash ostree_repo_equal ostree_repo_copy_config ostree_repo_remote_add diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index a416b7c1..1462a647 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -21,6 +21,7 @@ LIBOSTREE_2017.12 { global: ostree_repo_equal; + ostree_repo_hash; } LIBOSTREE_2017.11; diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index b1d88e48..7554bc44 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -2609,6 +2609,36 @@ ostree_repo_get_dfd (OstreeRepo *self) return self->repo_dir_fd; } +/** + * ostree_repo_hash: + * @self: an #OstreeRepo + * + * Calculate a hash value for the given open repository, suitable for use when + * putting it into a hash table. It is an error to call this on an #OstreeRepo + * which is not yet open, as a persistent hash value cannot be calculated until + * the repository is open and the inode of its root directory has been loaded. + * + * This function does no I/O. + * + * Returns: hash value for the #OstreeRepo + * Since: 2017.12 + */ +guint +ostree_repo_hash (OstreeRepo *self) +{ + g_return_val_if_fail (OSTREE_IS_REPO (self), 0); + + /* We cannot hash non-open repositories, since their hash value would change + * once they’re opened, resulting in false lookup misses and the inability to + * remove them from a hash table. */ + g_assert (self->repo_dir_fd >= 0); + + /* device and inode numbers are distributed fairly uniformly, so we can’t + * do much better than just combining them. No need to rehash to even out + * the distribution. */ + return (self->device ^ self->inode); +} + /** * ostree_repo_equal: * @a: an #OstreeRepo diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index a2a46d4b..a22c1ade 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -123,6 +123,8 @@ GFile * ostree_repo_get_path (OstreeRepo *self); _OSTREE_PUBLIC int ostree_repo_get_dfd (OstreeRepo *self); +_OSTREE_PUBLIC +guint ostree_repo_hash (OstreeRepo *self); _OSTREE_PUBLIC gboolean ostree_repo_equal (OstreeRepo *a, OstreeRepo *b); diff --git a/tests/.gitignore b/tests/.gitignore index 9bec67a3..e56a8393 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -15,6 +15,7 @@ test-mutable-tree test-ot-opt-utils test-ot-tool-util test-ot-unix-utils +test-repo test-repo-finder-avahi test-repo-finder-config test-repo-finder-mount diff --git a/tests/test-repo.c b/tests/test-repo.c new file mode 100644 index 00000000..217ca23c --- /dev/null +++ b/tests/test-repo.c @@ -0,0 +1,168 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright © 2017 Endless Mobile, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * - Philip Withnall + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "ostree-autocleanups.h" +#include "ostree-types.h" + +/* Test fixture. Creates a temporary directory. */ +typedef struct +{ + GLnxTmpDir tmpdir; /* (owned) */ +} Fixture; + +static void +setup (Fixture *fixture, + gconstpointer test_data) +{ + g_autoptr(GError) error = NULL; + + (void) glnx_mkdtemp ("test-repo-XXXXXX", 0700, &fixture->tmpdir, &error); + g_assert_no_error (error); + + g_test_message ("Using temporary directory: %s", fixture->tmpdir.path); +} + +static void +teardown (Fixture *fixture, + gconstpointer test_data) +{ + /* Recursively remove the temporary directory. */ + (void) glnx_tmpdir_delete (&fixture->tmpdir, NULL, NULL); +} + +/* Test that the hash values for two #OstreeRepo instances pointing at the same + * repository are equal. We can’t test anything else, since hash collisions are + * always a possibility. */ +static void +test_repo_hash (Fixture *fixture, + gconstpointer test_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(OstreeRepo) repo1 = ostree_repo_create_at (fixture->tmpdir.fd, ".", + OSTREE_REPO_MODE_ARCHIVE_Z2, + NULL, + NULL, &error); + g_assert_no_error (error); + + g_autoptr(OstreeRepo) repo2 = ostree_repo_open_at (fixture->tmpdir.fd, ".", + NULL, &error); + g_assert_no_error (error); + + g_assert_cmpuint (ostree_repo_hash (repo1), ==, ostree_repo_hash (repo2)); +} + +/* Test that trying to hash a closed repo results in an assertion failure. */ +static void +test_repo_hash_closed (Fixture *fixture, + gconstpointer test_data) +{ + if (g_test_subprocess ()) + { + g_autoptr(GFile) repo_path = g_file_new_for_path (fixture->tmpdir.path); + g_autoptr(OstreeRepo) repo = ostree_repo_new (repo_path); + + ostree_repo_hash (repo); + + return; + } + + g_test_trap_subprocess (NULL, 0, 0); + g_test_trap_assert_failed (); + g_test_trap_assert_stderr ("*ERROR*ostree_repo_hash: assertion failed:*"); +} + +/* Test that various repositories test equal (or not) with each other. */ +static void +test_repo_equal (Fixture *fixture, + gconstpointer test_data) +{ + g_autoptr(GError) error = NULL; + + /* Create a few separate repos and some #OstreeRepo objects for them. */ + glnx_ensure_dir (fixture->tmpdir.fd, "repo1", 0755, &error); + g_assert_no_error (error); + glnx_ensure_dir (fixture->tmpdir.fd, "repo2", 0755, &error); + g_assert_no_error (error); + + g_autoptr(OstreeRepo) repo1 = ostree_repo_create_at (fixture->tmpdir.fd, "repo1", + OSTREE_REPO_MODE_ARCHIVE_Z2, + NULL, + NULL, &error); + g_assert_no_error (error); + + g_autoptr(OstreeRepo) repo1_alias = ostree_repo_open_at (fixture->tmpdir.fd, "repo1", + NULL, &error); + g_assert_no_error (error); + + g_autoptr(OstreeRepo) repo2 = ostree_repo_create_at (fixture->tmpdir.fd, "repo2", + OSTREE_REPO_MODE_ARCHIVE_Z2, + NULL, + NULL, &error); + g_assert_no_error (error); + + g_autoptr(GFile) closed_repo_path = g_file_new_for_path (fixture->tmpdir.path); + g_autoptr(OstreeRepo) closed_repo = ostree_repo_new (closed_repo_path); + + /* Test various equalities. */ + g_assert_true (ostree_repo_equal (repo1, repo1)); + g_assert_true (ostree_repo_equal (repo1_alias, repo1_alias)); + g_assert_true (ostree_repo_equal (repo1, repo1_alias)); + g_assert_true (ostree_repo_equal (repo1_alias, repo1)); + g_assert_true (ostree_repo_equal (repo2, repo2)); + g_assert_false (ostree_repo_equal (repo1, repo2)); + g_assert_false (ostree_repo_equal (repo1_alias, repo2)); + g_assert_false (ostree_repo_equal (repo2, repo1)); + g_assert_false (ostree_repo_equal (repo2, repo1_alias)); + g_assert_false (ostree_repo_equal (repo1, closed_repo)); + g_assert_false (ostree_repo_equal (repo1_alias, closed_repo)); + g_assert_false (ostree_repo_equal (closed_repo, repo1)); + g_assert_false (ostree_repo_equal (closed_repo, repo1_alias)); + g_assert_false (ostree_repo_equal (repo2, closed_repo)); + g_assert_false (ostree_repo_equal (closed_repo, repo2)); + g_assert_false (ostree_repo_equal (closed_repo, closed_repo)); +} + +int +main (int argc, + char **argv) +{ + setlocale (LC_ALL, ""); + g_test_init (&argc, &argv, NULL); + + g_test_add ("/repo/hash", Fixture, NULL, setup, + test_repo_hash, teardown); + g_test_add ("/repo/hash/closed", Fixture, NULL, setup, + test_repo_hash_closed, teardown); + g_test_add ("/repo/equal", Fixture, NULL, setup, + test_repo_equal, teardown); + + return g_test_run (); +}