New upstream version 2017.8

This commit is contained in:
Simon McVittie committed 2017-07-18 22:18:02 +01:00
commit b6c0af922c
158 files changed
+12692 -2420

No files matched your search

+7 -3
View File
@@ -342,8 +342,11 @@ cd ${test_tmpdir}
cat > test-statoverride.txt <<EOF
+1048 /a/nested/2
2048 /a/nested/3
=384 /a/readable-only
EOF
cd ${test_tmpdir}/checkout-test2-4
echo readable only > a/readable-only
chmod 664 a/readable-only
$OSTREE commit ${COMMIT_ARGS} -b test2-override -s "with statoverride" --statoverride=../test-statoverride.txt
cd ${test_tmpdir}
$OSTREE checkout test2-override checkout-test2-override
@@ -354,6 +357,7 @@ else
test '!' -g checkout-test2-override/a/nested/2
test '!' -u checkout-test2-override/a/nested/3
fi
assert_file_has_mode checkout-test2-override/a/readable-only 600
echo "ok commit statoverride"
cd ${test_tmpdir}
@@ -492,7 +496,7 @@ echo "ok pull-local with --remote arg"
cd ${test_tmpdir}
${CMD_PREFIX} ostree --repo=repo3 prune
find repo3/objects -name '*.commit' > objlist-before-prune
rm repo3/refs/heads/* repo3/refs/remotes/* -rf
rm repo3/refs/heads/* repo3/refs/mirrors/* repo3/refs/remotes/* -rf
${CMD_PREFIX} ostree --repo=repo3 prune --refs-only
find repo3/objects -name '*.commit' > objlist-after-prune
if cmp -s objlist-before-prune objlist-after-prune; then
@@ -512,7 +516,7 @@ find repo3/objects -name '*.filez' > file-objects
if test -s file-objects; then
assert_not_reached "prune didn't delete all objects"
fi
echo "ok prune in archive-z2 deleted everything"
echo "ok prune in archive deleted everything"
cd ${test_tmpdir}
rm -rf test2-checkout
@@ -730,7 +734,7 @@ $OSTREE commit ${COMMIT_ARGS} -b test2 -s "Unfsynced commit" --fsync=false
if test "$(id -u)" != "0"; then
cd ${test_tmpdir}
rm -f expected-fail error-message
$OSTREE init --mode=archive-z2 --repo=repo-noperm
$OSTREE init --mode=archive --repo=repo-noperm
chmod -w repo-noperm/objects
$OSTREE --repo=repo-noperm pull-local repo 2> error-message || touch expected-fail
chmod +w repo-noperm/objects
+17 -6
View File
@@ -117,15 +117,14 @@ else
fi
files_are_hardlinked() {
f1=$(stat -c %i $1)
f2=$(stat -c %i $2)
[ "$f1" == "$f2" ]
inode1=$(stat -c %i $1)
inode2=$(stat -c %i $2)
test -n "${inode1}" && test -n "${inode2}"
[ "${inode1}" == "${inode2}" ]
}
assert_files_hardlinked() {
f1=$(stat -c %i $1)
f2=$(stat -c %i $2)
if ! files_are_hardlinked "$f1" "$f2"; then
if ! files_are_hardlinked "$1" "$2"; then
fatal "Files '$1' and '$2' are not hardlinked"
fi
}
@@ -537,3 +536,15 @@ ostree_file_path_to_object_path() {
relpath=$(ostree_file_path_to_relative_object_path $repo $ref $path)
echo ${repo}/${relpath}
}
# Assert ref $2 in repo $1 has checksum $3.
assert_ref () {
assert_streq $(${CMD_PREFIX} ostree rev-parse --repo=$1 $2) $3
}
# Assert no ref named $2 is present in repo $1.
assert_not_ref () {
if ${CMD_PREFIX} ostree rev-parse --repo=$1 $2 2>/dev/null; then
fatal "rev-parse $2 unexpectedly succeeded!"
fi
}
+30 -8
View File
@@ -35,12 +35,14 @@ function verify_initial_contents() {
assert_file_has_content baz/cow '^moo$'
}
echo "1..25"
echo "1..26"
# Try both syntaxes
repo_init --no-gpg-verify
${CMD_PREFIX} ostree --repo=repo pull origin main
${CMD_PREFIX} ostree --repo=repo pull origin:main
${CMD_PREFIX} ostree --repo=repo pull origin main >out.txt
assert_file_has_content out.txt "[1-9][0-9]* metadata, [1-9][0-9]* content objects fetched"
${CMD_PREFIX} ostree --repo=repo pull origin:main > out.txt
assert_not_file_has_content out.txt "content objects fetched"
${CMD_PREFIX} ostree --repo=repo fsck
echo "ok pull"
@@ -50,7 +52,7 @@ echo "ok pull contents"
cd ${test_tmpdir}
mkdir mirrorrepo
ostree_repo_init mirrorrepo --mode=archive-z2
ostree_repo_init mirrorrepo --mode=archive
${CMD_PREFIX} ostree --repo=mirrorrepo remote add --set=gpg-verify=false origin $(cat httpd-address)/ostree/gnomerepo
${CMD_PREFIX} ostree --repo=mirrorrepo pull --mirror origin main
${CMD_PREFIX} ostree --repo=mirrorrepo fsck
@@ -63,7 +65,7 @@ ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo commit -b otherbranch --tree=di
${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u
rm mirrorrepo -rf
# All refs
ostree_repo_init mirrorrepo --mode=archive-z2
ostree_repo_init mirrorrepo --mode=archive
${CMD_PREFIX} ostree --repo=mirrorrepo remote add --set=gpg-verify=false origin $(cat httpd-address)/ostree/gnomerepo
${CMD_PREFIX} ostree --repo=mirrorrepo pull --mirror origin
${CMD_PREFIX} ostree --repo=mirrorrepo fsck
@@ -73,7 +75,7 @@ done
echo "ok pull mirror (all refs)"
rm mirrorrepo -rf
ostree_repo_init mirrorrepo --mode=archive-z2
ostree_repo_init mirrorrepo --mode=archive
${CMD_PREFIX} ostree --repo=mirrorrepo remote add --set=gpg-verify=false origin $(cat httpd-address)/ostree/gnomerepo
# Generate a summary in the mirror
${CMD_PREFIX} ostree --repo=mirrorrepo summary -u
@@ -152,13 +154,33 @@ echo "ok pull commit metadata only (should not apply deltas)"
cd ${test_tmpdir}
mkdir mirrorrepo-local
ostree_repo_init mirrorrepo-local --mode=archive-z2
ostree_repo_init mirrorrepo-local --mode=archive
${CMD_PREFIX} ostree --repo=mirrorrepo-local remote add --set=gpg-verify=false origin file://$(pwd)/ostree-srv/gnomerepo
${CMD_PREFIX} ostree --repo=mirrorrepo-local pull --mirror origin main
${CMD_PREFIX} ostree --repo=mirrorrepo-local fsck
$OSTREE show main >/dev/null
echo "ok pull local mirror"
cd ${test_tmpdir}
# This is more of a known issue; test that we give a clean error right now
rm otherrepo -rf
ostree_repo_init otherrepo --mode=archive
rm checkout-origin-main -rf
${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo checkout main checkout-origin-main
${CMD_PREFIX} ostree --repo=otherrepo commit -b localbranch --tree=dir=checkout-origin-main
${CMD_PREFIX} ostree --repo=otherrepo remote add --set=gpg-verify=false origin file://$(pwd)/ostree-srv/gnomerepo
${CMD_PREFIX} ostree --repo=otherrepo pull origin main
rm mirrorrepo-local -rf
ostree_repo_init mirrorrepo-local --mode=archive
if ${CMD_PREFIX} ostree --repo=mirrorrepo-local pull-local otherrepo 2>err.txt; then
fatal "pull with mixed refs succeeded?"
fi
assert_file_has_content err.txt "error: Invalid ref name origin:main"
${CMD_PREFIX} ostree --repo=mirrorrepo-local pull-local otherrepo localbranch
${CMD_PREFIX} ostree --repo=mirrorrepo-local rev-parse localbranch
${CMD_PREFIX} ostree --repo=mirrorrepo-local fsck
echo "ok pull-local mirror errors with mixed refs"
cd ${test_tmpdir}
${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo commit -b main -s "Metadata string" --add-detached-metadata-string=SIGNATURE=HANCOCK --tree=ref=main
${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u
@@ -170,7 +192,7 @@ echo "ok pull detached metadata"
cd ${test_tmpdir}
mkdir parentpullrepo
ostree_repo_init parentpullrepo --mode=archive-z2
ostree_repo_init parentpullrepo --mode=archive
${CMD_PREFIX} ostree --repo=parentpullrepo remote add --set=gpg-verify=false origin file://$(pwd)/ostree-srv/gnomerepo
parent_rev=$(ostree --repo=ostree-srv/gnomerepo rev-parse main^)
rev=$(ostree --repo=ostree-srv/gnomerepo rev-parse main)
+20
View File
@@ -185,6 +185,25 @@ static gboolean hi_content_stream_new (GInputStream **out_stream,
return ostree_raw_file_to_content_stream ((GInputStream*)hi_memstream, finfo, NULL, out_stream, out_length, NULL, error);
}
static void
test_validate_remotename (void)
{
const char *valid[] = {"foo", "hello-world"};
const char *invalid[] = {"foo/bar", ""};
for (guint i = 0; i < G_N_ELEMENTS(valid); i++)
{
g_autoptr(GError) error = NULL;
g_assert (ostree_validate_remote_name (valid[i], &error));
g_assert_no_error (error);
}
for (guint i = 0; i < G_N_ELEMENTS(invalid); i++)
{
g_autoptr(GError) error = NULL;
g_assert (!ostree_validate_remote_name (invalid[i], &error));
g_assert (error != NULL);
}
}
static void
test_object_writes (gconstpointer data)
{
@@ -232,6 +251,7 @@ int main (int argc, char **argv)
g_test_add_data_func ("/repo-not-system", repo, test_repo_is_not_system);
g_test_add_data_func ("/raw-file-to-archive-z2-stream", repo, test_raw_file_to_archive_z2_stream);
g_test_add_data_func ("/objectwrites", repo, test_object_writes);
g_test_add_func ("/remotename", test_validate_remotename);
return g_test_run();
out:
+14 -1
View File
@@ -25,7 +25,7 @@ skip_without_user_xattrs
setup_test_repository "bare-user"
extra_basic_tests=3
extra_basic_tests=4
. $(dirname $0)/basic-test.sh
# Reset things so we don't inherit a lot of state from earlier tests
@@ -64,3 +64,16 @@ $OSTREE checkout -U -H test2-unwritable test2-checkout
cd test2-checkout
assert_file_has_mode unwritable 400
echo "ok bare-user unwritable"
rm test2-checkout -rf
$OSTREE checkout -U -H test2 test2-checkout
cat > statoverride.txt <<EOF
=0 /unreadable
EOF
touch test2-checkout/unreadable
$OSTREE commit -b test2-unreadable --statoverride=statoverride.txt --tree=dir=test2-checkout
$OSTREE fsck
rm test2-checkout -rf
$OSTREE checkout -U -H test2-unreadable test2-checkout
assert_file_has_mode test2-checkout/unreadable 400
echo "ok bare-user handled unreadable file"
+154
View File
@@ -0,0 +1,154 @@
/* -*- 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 <withnall@endlessm.com>
*/
#include "config.h"
#include <gio/gio.h>
#include <glib.h>
#include "ostree-bloom-private.h"
/* Test the two different constructors work at a basic level. */
static void
test_bloom_init (void)
{
g_autoptr(OstreeBloom) bloom = NULL;
g_autoptr(GBytes) bytes = NULL;
bloom = ostree_bloom_new (1, 1, ostree_str_bloom_hash);
g_assert_cmpuint (ostree_bloom_get_size (bloom), ==, 1);
g_assert_cmpuint (ostree_bloom_get_k (bloom), ==, 1);
g_assert (ostree_bloom_get_hash_func (bloom) == ostree_str_bloom_hash);
g_clear_pointer (&bloom, ostree_bloom_unref);
bytes = g_bytes_new_take (g_malloc0 (4), 4);
bloom = ostree_bloom_new_from_bytes (bytes, 1, ostree_str_bloom_hash);
g_assert_cmpuint (ostree_bloom_get_size (bloom), ==, 4);
g_assert_cmpuint (ostree_bloom_get_k (bloom), ==, 1);
g_assert (ostree_bloom_get_hash_func (bloom) == ostree_str_bloom_hash);
g_clear_pointer (&bloom, ostree_bloom_unref);
}
/* Test that building a bloom filter, marshalling it through GBytes, and loading
* it again, gives the same element membership. */
static void
test_bloom_construction (void)
{
g_autoptr(OstreeBloom) bloom = NULL;
g_autoptr(OstreeBloom) immutable_bloom = NULL;
g_autoptr(GBytes) bytes = NULL;
gsize i;
const gchar *members[] =
{
"hello", "there", "these", "are", "test", "strings"
};
const gchar *non_members[] =
{
"not", "an", "element"
};
const gsize n_bytes = 256;
const guint8 k = 8;
const OstreeBloomHashFunc hash = ostree_str_bloom_hash;
/* Build a bloom filter. */
bloom = ostree_bloom_new (n_bytes, k, hash);
for (i = 0; i < G_N_ELEMENTS (members); i++)
ostree_bloom_add_element (bloom, members[i]);
bytes = ostree_bloom_seal (bloom);
/* Read it back from the GBytes. */
immutable_bloom = ostree_bloom_new_from_bytes (bytes, k, hash);
for (i = 0; i < G_N_ELEMENTS (members); i++)
g_assert_true (ostree_bloom_maybe_contains (bloom, members[i]));
/* This should never fail in future, as we guarantee the hash function will
* never change. But given the definition of a bloom filter, it would also
* be valid for these calls to return %TRUE. */
for (i = 0; i < G_N_ELEMENTS (non_members); i++)
g_assert_false (ostree_bloom_maybe_contains (bloom, non_members[i]));
}
/* Test that an empty bloom filter definitely contains no elements. */
static void
test_bloom_empty (void)
{
g_autoptr(OstreeBloom) bloom = NULL;
const gsize n_bytes = 256;
const guint8 k = 8;
const OstreeBloomHashFunc hash = ostree_str_bloom_hash;
/* Build an empty bloom filter. */
bloom = ostree_bloom_new (n_bytes, k, hash);
g_assert_false (ostree_bloom_maybe_contains (bloom, "hello"));
g_assert_false (ostree_bloom_maybe_contains (bloom, "there"));
}
/* Build a bloom filter, and check the membership of the members as they are
* added. */
static void
test_bloom_membership_during_construction (void)
{
g_autoptr(OstreeBloom) bloom = NULL;
gsize i, j;
const gchar *members[] =
{
"hello", "there", "these", "are", "test", "strings"
};
const gsize n_bytes = 256;
const guint8 k = 8;
const OstreeBloomHashFunc hash = ostree_str_bloom_hash;
/* These membership checks should never fail in future, as we guarantee
* the hash function will never change. But given the definition of a bloom
* filter, it would also be valid for these checks to fail. */
bloom = ostree_bloom_new (n_bytes, k, hash);
for (i = 0; i < G_N_ELEMENTS (members); i++)
{
ostree_bloom_add_element (bloom, members[i]);
for (j = 0; j < G_N_ELEMENTS (members); j++)
{
if (j <= i)
g_assert_true (ostree_bloom_maybe_contains (bloom, members[j]));
else
g_assert_false (ostree_bloom_maybe_contains (bloom, members[j]));
}
}
}
int main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/bloom/init", test_bloom_init);
g_test_add_func ("/bloom/construction", test_bloom_construction);
g_test_add_func ("/bloom/empty", test_bloom_empty);
g_test_add_func ("/bloom/membership-during-construction", test_bloom_membership_during_construction);
return g_test_run();
}
+2 -2
View File
@@ -84,13 +84,13 @@ if ${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-sum
assert_not_reached "GPG summary verification with no summary unexpectedly succeeded"
fi
${OSTREE} summary -u update
${OSTREE} summary --update
if ${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-summary repo test2 2>&1; then
assert_not_reached "GPG summary verification with signed no summary unexpectedly succeeded"
fi
${OSTREE} summary -u update --gpg-sign=${TEST_GPG_KEYID_1} --gpg-homedir=${TEST_GPG_KEYHOME}
${OSTREE} summary --update --gpg-sign=${TEST_GPG_KEYID_1} --gpg-homedir=${TEST_GPG_KEYHOME}
${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-summary repo test2 2>&1
+400
View File
@@ -0,0 +1,400 @@
/* -*- 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 <withnall@endlessm.com>
*/
#include "config.h"
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include <libglnx.h>
#include "test-mock-gio.h"
/**
* SECTION:mock-gio
* @title: Mock GIO volume interfaces
* @short_description: Mock implementations of GIO volume, mount and drive
* interfaces
* @stability: Unstable
* @include: tests/test-mock-gio.h
*
* A set of classes implementing GIO interfaces for volumes, mounts, drives
* and volume monitoring, which return mock data to the caller when used. These
* are designed for use in unit tests, to mock up removable drives when testing
* code which monitors such drives being added and removed and then queries
* properties of them.
*
* By returning mock drive locations to the caller, for example, the contents of
* a removable drive may be mocked up using temporary files.
*
* Currently, all the mock data returned by these classes to callers is static,
* set at construction time.
*
* Since: 2017.8
*/
/* Mock volume monitor class. This returns a static set of data to the caller,
* which it was initialised with. */
struct _OstreeMockVolumeMonitor
{
GVolumeMonitor parent_instance;
GList *mounts; /* (element-type OstreeMockMount) */
GList *volumes; /* (element-type OstreeMockVolume) */
};
G_DEFINE_TYPE (OstreeMockVolumeMonitor, ostree_mock_volume_monitor, G_TYPE_VOLUME_MONITOR)
static GList *
ostree_mock_volume_monitor_get_mounts (GVolumeMonitor *monitor)
{
OstreeMockVolumeMonitor *self = OSTREE_MOCK_VOLUME_MONITOR (monitor);
return g_list_copy_deep (self->mounts, (GCopyFunc) g_object_ref, NULL);
}
static GList *
ostree_mock_volume_monitor_get_volumes (GVolumeMonitor *monitor)
{
OstreeMockVolumeMonitor *self = OSTREE_MOCK_VOLUME_MONITOR (monitor);
return g_list_copy_deep (self->volumes, (GCopyFunc) g_object_ref, NULL);
}
static void
ostree_mock_volume_monitor_init (OstreeMockVolumeMonitor *self)
{
/* Nothing to see here. */
}
static void
ostree_mock_volume_monitor_dispose (GObject *object)
{
OstreeMockVolumeMonitor *self = OSTREE_MOCK_VOLUME_MONITOR (object);
g_list_free_full (self->volumes, g_object_unref);
self->volumes = NULL;
g_list_free_full (self->mounts, g_object_unref);
self->mounts = NULL;
G_OBJECT_CLASS (ostree_mock_volume_monitor_parent_class)->dispose (object);
}
static void
ostree_mock_volume_monitor_class_init (OstreeMockVolumeMonitorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
object_class->dispose = ostree_mock_volume_monitor_dispose;
monitor_class->get_mounts = ostree_mock_volume_monitor_get_mounts;
monitor_class->get_volumes = ostree_mock_volume_monitor_get_volumes;
}
/**
* ostree_mock_volume_monitor_new:
* @mounts: (element-type GMount) (transfer none): list of current #GMounts
* @volumes: (element-type GVolume) (transfer none): list of current #GVolumes
*
* Create a new mock #GVolumeMonitor which will return the given static lists of
* #GMounts and #GVolumes to any caller of g_volume_monitor_get_mounts() or
* g_volume_monitor_get_volumes().
*
* Typically, the elements of @mounts will be #OstreeMockMount objects and the
* elements of @volumes will be #OstreeMockVolume objects; but this does not
* have to be the case.
*
* Returns: (transfer full): a new #GVolumeMonitor object
* Since: 2017.8
*/
GVolumeMonitor *
ostree_mock_volume_monitor_new (GList *mounts,
GList *volumes)
{
g_autoptr(OstreeMockVolumeMonitor) monitor = NULL;
monitor = g_object_new (OSTREE_TYPE_MOCK_VOLUME_MONITOR, NULL);
monitor->mounts = g_list_copy_deep (mounts, (GCopyFunc) g_object_ref, NULL);
monitor->volumes = g_list_copy_deep (volumes, (GCopyFunc) g_object_ref, NULL);
return g_steal_pointer (&monitor);
}
/* Mock volume class. This returns a static set of data to the caller, which it
* was initialised with. */
struct _OstreeMockVolume
{
GObject parent_instance;
gchar *name;
GDrive *drive; /* (owned) (nullable) */
GMount *mount; /* (owned) (nullable) */
};
static void ostree_mock_volume_iface_init (GVolumeIface *iface);
G_DEFINE_TYPE_WITH_CODE (OstreeMockVolume, ostree_mock_volume, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, ostree_mock_volume_iface_init))
static gchar *
ostree_mock_volume_get_name (GVolume *volume)
{
OstreeMockVolume *self = OSTREE_MOCK_VOLUME (volume);
return g_strdup (self->name);
}
static GDrive *
ostree_mock_volume_get_drive (GVolume *volume)
{
OstreeMockVolume *self = OSTREE_MOCK_VOLUME (volume);
return (self->drive != NULL) ? g_object_ref (self->drive) : NULL;
}
static GMount *
ostree_mock_volume_get_mount (GVolume *volume)
{
OstreeMockVolume *self = OSTREE_MOCK_VOLUME (volume);
return (self->mount != NULL) ? g_object_ref (self->mount) : NULL;
}
static void
ostree_mock_volume_init (OstreeMockVolume *self)
{
/* Nothing to see here. */
}
static void
ostree_mock_volume_dispose (GObject *object)
{
OstreeMockVolume *self = OSTREE_MOCK_VOLUME (object);
g_clear_pointer (&self->name, g_free);
g_clear_object (&self->drive);
g_clear_object (&self->mount);
G_OBJECT_CLASS (ostree_mock_volume_parent_class)->dispose (object);
}
static void
ostree_mock_volume_class_init (OstreeMockVolumeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = ostree_mock_volume_dispose;
}
static void
ostree_mock_volume_iface_init (GVolumeIface *iface)
{
iface->get_name = ostree_mock_volume_get_name;
iface->get_drive = ostree_mock_volume_get_drive;
iface->get_mount = ostree_mock_volume_get_mount;
}
/**
* ostree_mock_volume_new:
* @name: volume name
* @drive: (transfer none) (nullable): drive for the volume, or %NULL if none
* should be associated
* @mount: (transfer none) (nullable): mount for the volume, or %NULL if it’s
* not mounted
*
* Create a new mock #GVolume which will return the given static @name, @drive
* and @mount to any caller of its getter methods. There is currently no
* provision for changing these values dynamically. There is also currently no
* provision for mocking the other getters of #GVolume.
*
* Typically, @drive will be an #OstreeMockDrive object and @mount will be an
* #OstreeMockMount object; but this does not have to be the case.
*
* Returns: (transfer full): a new #GVolume object
* Since: 2017.8
*/
OstreeMockVolume *
ostree_mock_volume_new (const gchar *name,
GDrive *drive,
GMount *mount)
{
g_autoptr(OstreeMockVolume) volume = NULL;
volume = g_object_new (OSTREE_TYPE_MOCK_VOLUME, NULL);
volume->name = g_strdup (name);
volume->drive = (drive != NULL) ? g_object_ref (drive) : NULL;
volume->mount = (mount != NULL) ? g_object_ref (mount) : NULL;
return g_steal_pointer (&volume);
}
/* Mock drive class. This returns a static set of data to the caller, which it
* was initialised with. */
struct _OstreeMockDrive
{
GObject parent_instance;
gboolean is_removable;
};
static void ostree_mock_drive_iface_init (GDriveIface *iface);
G_DEFINE_TYPE_WITH_CODE (OstreeMockDrive, ostree_mock_drive, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_DRIVE, ostree_mock_drive_iface_init))
#if GLIB_CHECK_VERSION(2, 50, 0)
static gboolean
ostree_mock_drive_is_removable (GDrive *drive)
{
OstreeMockDrive *self = OSTREE_MOCK_DRIVE (drive);
return self->is_removable;
}
#endif
static void
ostree_mock_drive_init (OstreeMockDrive *self)
{
/* Nothing to see here. */
}
static void
ostree_mock_drive_class_init (OstreeMockDriveClass *klass)
{
/* Nothing to see here. */
}
static void
ostree_mock_drive_iface_init (GDriveIface *iface)
{
#if GLIB_CHECK_VERSION(2, 50, 0)
iface->is_removable = ostree_mock_drive_is_removable;
#endif
}
/**
* ostree_mock_drive_new:
* @is_removable: %TRUE if the drive is removable; %FALSE otherwise
*
* Create a new mock #GDrive which will return the given static @is_removable to
* any caller of its getter methods. There is currently no provision for mocking
* the other getters of #GDrive.
*
* Returns: (transfer full): a new #GDrive object
* Since: 2017.8
*/
OstreeMockDrive *
ostree_mock_drive_new (gboolean is_removable)
{
g_autoptr(OstreeMockDrive) drive = NULL;
drive = g_object_new (OSTREE_TYPE_MOCK_DRIVE, NULL);
drive->is_removable = is_removable;
return g_steal_pointer (&drive);
}
/* Mock mount class. This returns a static set of data to the caller, which it
* was initialised with. */
struct _OstreeMockMount
{
GObject parent_instance;
gchar *name; /* (owned) */
GFile *root; /* (owned) */
};
static void ostree_mock_mount_iface_init (GMountIface *iface);
G_DEFINE_TYPE_WITH_CODE (OstreeMockMount, ostree_mock_mount, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT, ostree_mock_mount_iface_init))
static gchar *
ostree_mock_mount_get_name (GMount *mount)
{
OstreeMockMount *self = OSTREE_MOCK_MOUNT (mount);
return g_strdup (self->name);
}
static GFile *
ostree_mock_mount_get_root (GMount *mount)
{
OstreeMockMount *self = OSTREE_MOCK_MOUNT (mount);
return g_object_ref (self->root);
}
static void
ostree_mock_mount_init (OstreeMockMount *self)
{
/* Nothing to see here. */
}
static void
ostree_mock_mount_dispose (GObject *object)
{
OstreeMockMount *self = OSTREE_MOCK_MOUNT (object);
g_clear_pointer (&self->name, g_free);
g_clear_object (&self->root);
G_OBJECT_CLASS (ostree_mock_mount_parent_class)->dispose (object);
}
static void
ostree_mock_mount_class_init (OstreeMockMountClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = ostree_mock_mount_dispose;
}
static void
ostree_mock_mount_iface_init (GMountIface *iface)
{
iface->get_name = ostree_mock_mount_get_name;
iface->get_root = ostree_mock_mount_get_root;
}
/**
* ostree_mock_mount_new:
* @name: mount name
* @root: (transfer none): root path for the mounted file system
*
* Create a new mock #GMount which will return the given static @name and @root
* to any caller of its getter methods. There is currently no provision for
* mocking the other getters of #GMount.
*
* Typically, @root will point to a temporary directory where a mocked file
* system is present; but this does not have to be the case.
*
* Returns: (transfer full): a new #GMount object
* Since: 2017.8
*/
OstreeMockMount *
ostree_mock_mount_new (const gchar *name,
GFile *root)
{
g_autoptr(OstreeMockMount) mount = NULL;
mount = g_object_new (OSTREE_TYPE_MOCK_MOUNT, NULL);
mount->name = g_strdup (name);
mount->root = g_object_ref (root);
return g_steal_pointer (&mount);
}
+127
View File
@@ -0,0 +1,127 @@
/* -*- 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 <withnall@endlessm.com>
*/
#pragma once
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include <libglnx.h>
#include "ostree-types.h"
G_BEGIN_DECLS
#define OSTREE_TYPE_MOCK_VOLUME_MONITOR (ostree_mock_volume_monitor_get_type ())
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
G_GNUC_INTERNAL
G_DECLARE_FINAL_TYPE (OstreeMockVolumeMonitor, ostree_mock_volume_monitor, OSTREE, MOCK_VOLUME_MONITOR, GVolumeMonitor) */
G_GNUC_INTERNAL
GType ostree_mock_volume_monitor_get_type (void);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct _OstreeMockVolumeMonitor OstreeMockVolumeMonitor;
typedef struct { GVolumeMonitorClass parent_class; } OstreeMockVolumeMonitorClass;
static inline OstreeMockVolumeMonitor *OSTREE_MOCK_VOLUME_MONITOR (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_mock_volume_monitor_get_type (), OstreeMockVolumeMonitor); }
static inline gboolean OSTREE_IS_MOCK_VOLUME_MONITOR (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_mock_volume_monitor_get_type ()); }
G_GNUC_END_IGNORE_DEPRECATIONS
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeMockVolumeMonitor, g_object_unref)
G_GNUC_INTERNAL
GVolumeMonitor *ostree_mock_volume_monitor_new (GList *mounts,
GList *volumes);
#define OSTREE_TYPE_MOCK_VOLUME (ostree_mock_volume_get_type ())
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
G_GNUC_INTERNAL
G_DECLARE_FINAL_TYPE (OstreeMockVolume, ostree_mock_volume, OSTREE, MOCK_VOLUME, GObject) */
G_GNUC_INTERNAL
GType ostree_mock_volume_get_type (void);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct _OstreeMockVolume OstreeMockVolume;
typedef struct { GObjectClass parent_class; } OstreeMockVolumeClass;
static inline OstreeMockVolume *OSTREE_MOCK_VOLUME (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_mock_volume_get_type (), OstreeMockVolume); }
static inline gboolean OSTREE_IS_MOCK_VOLUME (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_mock_volume_get_type ()); }
G_GNUC_END_IGNORE_DEPRECATIONS
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeMockVolume, g_object_unref)
G_GNUC_INTERNAL
OstreeMockVolume *ostree_mock_volume_new (const gchar *name,
GDrive *drive,
GMount *mount);
#define OSTREE_TYPE_MOCK_DRIVE (ostree_mock_drive_get_type ())
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
G_GNUC_INTERNAL
G_DECLARE_FINAL_TYPE (OstreeMockDrive, ostree_mock_drive, OSTREE, MOCK_DRIVE, GObject) */
G_GNUC_INTERNAL
GType ostree_mock_drive_get_type (void);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct _OstreeMockDrive OstreeMockDrive;
typedef struct { GObjectClass parent_class; } OstreeMockDriveClass;
static inline OstreeMockDrive *OSTREE_MOCK_DRIVE (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_mock_drive_get_type (), OstreeMockDrive); }
static inline gboolean OSTREE_IS_MOCK_DRIVE (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_mock_drive_get_type ()); }
G_GNUC_END_IGNORE_DEPRECATIONS
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeMockDrive, g_object_unref)
G_GNUC_INTERNAL
OstreeMockDrive *ostree_mock_drive_new (gboolean is_removable);
#define OSTREE_TYPE_MOCK_MOUNT (ostree_mock_mount_get_type ())
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
G_GNUC_INTERNAL
G_DECLARE_FINAL_TYPE (OstreeMockMount, ostree_mock_mount, OSTREE, MOCK_MOUNT, GObject) */
G_GNUC_INTERNAL
GType ostree_mock_mount_get_type (void);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct _OstreeMockMount OstreeMockMount;
typedef struct { GObjectClass parent_class; } OstreeMockMountClass;
static inline OstreeMockMount *OSTREE_MOCK_MOUNT (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_mock_mount_get_type (), OstreeMockMount); }
static inline gboolean OSTREE_IS_MOCK_MOUNT (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_mock_mount_get_type ()); }
G_GNUC_END_IGNORE_DEPRECATIONS
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeMockMount, g_object_unref)
G_GNUC_INTERNAL
OstreeMockMount *ostree_mock_mount_new (const gchar *name,
GFile *root);
G_END_DECLS
@@ -21,6 +21,6 @@ set -euo pipefail
. $(dirname $0)/libtest.sh
setup_fake_remote_repo1 "archive-z2"
setup_fake_remote_repo1 "archive"
. ${test_srcdir}/pull-test.sh
+1 -1
View File
@@ -43,7 +43,7 @@ test_data_init (TestData *td)
if (!td->repo)
goto out;
if (!ot_test_run_libtest ("setup_fake_remote_repo1 archive-z2", error))
if (!ot_test_run_libtest ("setup_fake_remote_repo1 archive", error))
goto out;
if (!g_file_get_contents ("httpd-address", &http_address, NULL, error))
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
#
# Copyright (C) 2017 Red Hat, 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.
set -euo pipefail
. $(dirname $0)/libtest.sh
setup_fake_remote_repo1 "archive"
echo '1..1'
cd ${test_tmpdir}
gnomerepo_url="$(cat httpd-address)/ostree/gnomerepo"
ostree_repo_init repo --mode "archive"
ostree_repo_init repo-local --mode "archive"
for repo in repo{,-local}; do
${CMD_PREFIX} ostree --repo=${repo} remote add --set=gpg-verify=false origin ${gnomerepo_url}
done
# Pull the contents to our local cache
${CMD_PREFIX} ostree --repo=repo-local pull origin main
rm files -rf
${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo checkout main files
echo anewfile > files/anewfile
${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo commit -b main --tree=dir=files
${CMD_PREFIX} ostree --repo=repo pull -L repo-local origin main >out.txt
assert_file_has_content out.txt '3 metadata, 1 content objects fetched (4 meta, 5 content local)'
echo "ok pull --reference"
+228
View File
@@ -0,0 +1,228 @@
/* -*- 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 <withnall@endlessm.com>
*/
#include "config.h"
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
#include <string.h>
#include "ostree-autocleanups.h"
#include "ostree-repo-finder.h"
#include "ostree-repo-finder-avahi.h"
#include "ostree-repo-finder-avahi-private.h"
/* FIXME: Upstream this */
G_DEFINE_AUTOPTR_CLEANUP_FUNC (AvahiStringList, avahi_string_list_free)
/* Test the object constructor works at a basic level. */
static void
test_repo_finder_avahi_init (void)
{
g_autoptr(OstreeRepoFinderAvahi) finder = NULL;
g_autoptr(GMainContext) context = NULL;
/* Default main context. */
finder = ostree_repo_finder_avahi_new (NULL);
g_clear_object (&finder);
/* Explicit main context. */
context = g_main_context_new ();
finder = ostree_repo_finder_avahi_new (context);
g_clear_object (&finder);
}
/* Test parsing valid and invalid TXT records. */
static void
test_repo_finder_avahi_txt_records_parse (void)
{
struct
{
const guint8 *txt;
gsize txt_len;
const gchar *expected_key; /* (nullable) to indicate parse failure */
const guint8 *expected_value; /* (nullable) to allow for valueless keys */
gsize expected_value_len;
}
vectors[] =
{
{ (const guint8 *) "", 0, NULL, NULL, 0 },
{ (const guint8 *) "\x00", 1, NULL, NULL, 0 },
{ (const guint8 *) "\xff", 1, NULL, NULL, 0 },
{ (const guint8 *) "k\x00", 2, NULL, NULL, 0 },
{ (const guint8 *) "k\xff", 2, NULL, NULL, 0 },
{ (const guint8 *) "=", 1, NULL, NULL, 0 },
{ (const guint8 *) "=value", 6, NULL, NULL, 0 },
{ (const guint8 *) "k=v", 3, "k", (const guint8 *) "v", 1 },
{ (const guint8 *) "key=value", 9, "key", (const guint8 *) "value", 5 },
{ (const guint8 *) "k=v=", 4, "k", (const guint8 *) "v=", 2 },
{ (const guint8 *) "k=", 2, "k", (const guint8 *) "", 0 },
{ (const guint8 *) "k", 1, "k", NULL, 0 },
{ (const guint8 *) "k==", 3, "k", (const guint8 *) "=", 1 },
{ (const guint8 *) "k=\x00\x01\x02", 5, "k", (const guint8 *) "\x00\x01\x02", 3 },
};
gsize i;
for (i = 0; i < G_N_ELEMENTS (vectors); i++)
{
g_autoptr(AvahiStringList) string_list = NULL;
g_autoptr(GHashTable) attributes = NULL;
g_test_message ("Vector %" G_GSIZE_FORMAT, i);
string_list = avahi_string_list_add_arbitrary (NULL, vectors[i].txt, vectors[i].txt_len);
attributes = _ostree_txt_records_parse (string_list);
if (vectors[i].expected_key != NULL)
{
GBytes *value;
g_autoptr(GBytes) expected_value = NULL;
g_assert_true (g_hash_table_lookup_extended (attributes,
vectors[i].expected_key,
NULL,
(gpointer *) &value));
g_assert_cmpuint (g_hash_table_size (attributes), ==, 1);
if (vectors[i].expected_value != NULL)
{
g_assert_nonnull (value);
expected_value = g_bytes_new_static (vectors[i].expected_value, vectors[i].expected_value_len);
g_assert_true (g_bytes_equal (value, expected_value));
}
else
{
g_assert_null (value);
}
}
else
{
g_assert_cmpuint (g_hash_table_size (attributes), ==, 0);
}
}
}
/* Test that the first value for a set of duplicate records is returned.
* See RFC 6763, §6.4. */
static void
test_repo_finder_avahi_txt_records_duplicates (void)
{
g_autoptr(AvahiStringList) string_list = NULL;
g_autoptr(GHashTable) attributes = NULL;
GBytes *value;
g_autoptr(GBytes) expected_value = NULL;
/* Reverse the list before using it, as they are built in reverse order.
* (See the #AvahiStringList documentation.) */
string_list = avahi_string_list_new ("k=value1", "k=value2", "k=value3", NULL);
string_list = avahi_string_list_reverse (string_list);
attributes = _ostree_txt_records_parse (string_list);
g_assert_cmpuint (g_hash_table_size (attributes), ==, 1);
value = g_hash_table_lookup (attributes, "k");
g_assert_nonnull (value);
expected_value = g_bytes_new_static ("value1", strlen ("value1"));
g_assert_true (g_bytes_equal (value, expected_value));
}
/* Test that keys are parsed and looked up case insensitively.
* See RFC 6763, §6.4. */
static void
test_repo_finder_avahi_txt_records_case_sensitivity (void)
{
g_autoptr(AvahiStringList) string_list = NULL;
g_autoptr(GHashTable) attributes = NULL;
GBytes *value1, *value2;
g_autoptr(GBytes) expected_value1 = NULL, expected_value2 = NULL;
/* Reverse the list before using it, as they are built in reverse order.
* (See the #AvahiStringList documentation.) */
string_list = avahi_string_list_new ("k=value1",
"K=value2",
"KeY2=v",
NULL);
string_list = avahi_string_list_reverse (string_list);
attributes = _ostree_txt_records_parse (string_list);
g_assert_cmpuint (g_hash_table_size (attributes), ==, 2);
value1 = g_hash_table_lookup (attributes, "k");
g_assert_nonnull (value1);
expected_value1 = g_bytes_new_static ("value1", strlen ("value1"));
g_assert_true (g_bytes_equal (value1, expected_value1));
g_assert_null (g_hash_table_lookup (attributes, "K"));
value2 = g_hash_table_lookup (attributes, "key2");
g_assert_nonnull (value2);
expected_value2 = g_bytes_new_static ("v", 1);
g_assert_true (g_bytes_equal (value2, expected_value2));
g_assert_null (g_hash_table_lookup (attributes, "KeY2"));
}
/* Test that keys which have an empty value can be distinguished from those
* which have no value. See RFC 6763, §6.4. */
static void
test_repo_finder_avahi_txt_records_empty_and_missing (void)
{
g_autoptr(AvahiStringList) string_list = NULL;
g_autoptr(GHashTable) attributes = NULL;
GBytes *value1, *value2;
g_autoptr(GBytes) expected_value1 = NULL;
string_list = avahi_string_list_new ("empty=",
"missing",
NULL);
attributes = _ostree_txt_records_parse (string_list);
g_assert_cmpuint (g_hash_table_size (attributes), ==, 2);
value1 = g_hash_table_lookup (attributes, "empty");
g_assert_nonnull (value1);
expected_value1 = g_bytes_new_static ("", 0);
g_assert_true (g_bytes_equal (value1, expected_value1));
g_assert_true (g_hash_table_lookup_extended (attributes, "missing", NULL, (gpointer *) &value2));
g_assert_null (value2);
}
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/repo-finder-avahi/init", test_repo_finder_avahi_init);
g_test_add_func ("/repo-finder-avahi/txt-records/parse", test_repo_finder_avahi_txt_records_parse);
g_test_add_func ("/repo-finder-avahi/txt-records/duplicates", test_repo_finder_avahi_txt_records_duplicates);
g_test_add_func ("/repo-finder-avahi/txt-records/case-sensitivity", test_repo_finder_avahi_txt_records_case_sensitivity);
g_test_add_func ("/repo-finder-avahi/txt-records/empty-and-missing", test_repo_finder_avahi_txt_records_empty_and_missing);
/* FIXME: Add tests for service processing, probably by splitting the
* code in OstreeRepoFinderAvahi around found_services. */
return g_test_run();
}
+327
View File
@@ -0,0 +1,327 @@
/* -*- 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 <withnall@endlessm.com>
*/
#include "config.h"
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include <libglnx.h>
#include <locale.h>
#include <string.h>
#include "libostreetest.h"
#include "ostree-autocleanups.h"
#include "ostree-repo-finder.h"
#include "ostree-repo-finder-config.h"
/* Test fixture. Creates a temporary directory. */
typedef struct
{
OstreeRepo *parent_repo; /* owned */
int working_dfd; /* owned */
GFile *working_dir; /* owned */
} Fixture;
static void
setup (Fixture *fixture,
gconstpointer test_data)
{
g_autofree gchar *tmp_name = NULL;
g_autoptr(GError) error = NULL;
tmp_name = g_strdup ("test-repo-finder-config-XXXXXX");
glnx_mkdtempat_open_in_system (tmp_name, 0700, &fixture->working_dfd, &error);
g_assert_no_error (error);
g_test_message ("Using temporary directory: %s", tmp_name);
glnx_shutil_mkdir_p_at (fixture->working_dfd, "repo", 0700, NULL, &error);
g_assert_no_error (error);
g_autoptr(GFile) tmp_dir = g_file_new_for_path (g_get_tmp_dir ());
fixture->working_dir = g_file_get_child (tmp_dir, tmp_name);
fixture->parent_repo = ot_test_setup_repo (NULL, &error);
g_assert_no_error (error);
}
static void
teardown (Fixture *fixture,
gconstpointer test_data)
{
glnx_fd_close int parent_repo_dfd = -1;
g_autoptr(GError) error = NULL;
/* Recursively remove the temporary directory. */
glnx_shutil_rm_rf_at (fixture->working_dfd, ".", NULL, NULL);
close (fixture->working_dfd);
fixture->working_dfd = -1;
/* The repo also needs its source files to be removed. This is the inverse
* of setup_test_repository() in libtest.sh. */
g_autofree gchar *parent_repo_path = g_file_get_path (ostree_repo_get_path (fixture->parent_repo));
glnx_opendirat (-1, parent_repo_path, TRUE, &parent_repo_dfd, &error);
g_assert_no_error (error);
glnx_shutil_rm_rf_at (parent_repo_dfd, "../files", NULL, NULL);
glnx_shutil_rm_rf_at (parent_repo_dfd, "../repo", NULL, NULL);
g_clear_object (&fixture->working_dir);
g_clear_object (&fixture->parent_repo);
}
/* Test the object constructor works at a basic level. */
static void
test_repo_finder_config_init (void)
{
g_autoptr(OstreeRepoFinderConfig) finder = NULL;
/* Default everything. */
finder = ostree_repo_finder_config_new ();
}
static void
result_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GAsyncResult **result_out = user_data;
*result_out = g_object_ref (result);
}
/* Test that no remotes are found if there are no config files in the refs
* directory. */
static void
test_repo_finder_config_no_configs (Fixture *fixture,
gconstpointer test_data)
{
g_autoptr(OstreeRepoFinderConfig) finder = NULL;
g_autoptr(GMainContext) context = NULL;
g_autoptr(GAsyncResult) result = NULL;
g_autoptr(GPtrArray) results = NULL; /* (element-type OstreeRepoFinderResult) */
g_autoptr(GError) error = NULL;
const OstreeCollectionRef ref1 = { "org.example.Os", "exampleos/x86_64/standard" };
const OstreeCollectionRef ref2 = { "org.example.Os", "exampleos/x86_64/buildmaster/standard" };
const OstreeCollectionRef * const refs[] = { &ref1, &ref2, NULL };
context = g_main_context_new ();
g_main_context_push_thread_default (context);
finder = ostree_repo_finder_config_new ();
ostree_repo_finder_resolve_async (OSTREE_REPO_FINDER (finder), refs,
fixture->parent_repo, NULL, result_cb, &result);
while (result == NULL)
g_main_context_iteration (context, TRUE);
results = ostree_repo_finder_resolve_finish (OSTREE_REPO_FINDER (finder),
result, &error);
g_assert_no_error (error);
g_assert_nonnull (results);
g_assert_cmpuint (results->len, ==, 0);
g_main_context_pop_thread_default (context);
}
/* Add configuration for a remote named @remote_name, at @remote_uri, with a
* remote collection ID of @collection_id, to the given @repo. */
static void
assert_create_remote_config (OstreeRepo *repo,
const gchar *remote_name,
const gchar *remote_uri,
const gchar *collection_id)
{
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) options = NULL;
if (collection_id != NULL)
options = g_variant_new_parsed ("@a{sv} { 'collection-id': <%s> }",
collection_id);
ostree_repo_remote_add (repo, remote_name, remote_uri, options, NULL, &error);
g_assert_no_error (error);
}
static gchar *assert_create_remote (Fixture *fixture,
const gchar *collection_id,
...) G_GNUC_NULL_TERMINATED;
/* Create a new repository in a temporary directory with its collection ID set
* to @collection_id, and containing the refs given in @... (which must be
* %NULL-terminated). Return the `file://` URI of the new repository. */
static gchar *
assert_create_remote (Fixture *fixture,
const gchar *collection_id,
...)
{
va_list args;
g_autoptr(GError) error = NULL;
const gchar *repo_name = (collection_id != NULL) ? collection_id : "no-collection";
glnx_shutil_mkdir_p_at (fixture->working_dfd, repo_name, 0700, NULL, &error);
g_assert_no_error (error);
g_autoptr(GFile) repo_path = g_file_get_child (fixture->working_dir, repo_name);
g_autoptr(OstreeRepo) repo = ostree_repo_new (repo_path);
ostree_repo_set_collection_id (repo, collection_id, &error);
g_assert_no_error (error);
ostree_repo_create (repo, OSTREE_REPO_MODE_ARCHIVE_Z2, NULL, &error);
g_assert_no_error (error);
/* Set up the refs from @.... */
va_start (args, collection_id);
for (const gchar *ref_name = va_arg (args, const gchar *);
ref_name != NULL;
ref_name = va_arg (args, const gchar *))
{
OstreeCollectionRef collection_ref = { (gchar *) collection_id, (gchar *) ref_name };
g_autofree gchar *checksum = NULL;
g_autoptr(OstreeMutableTree) mtree = NULL;
g_autoptr(OstreeRepoFile) repo_file = NULL;
mtree = ostree_mutable_tree_new ();
ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, ".", mtree, NULL, NULL, &error);
g_assert_no_error (error);
ostree_repo_write_mtree (repo, mtree, (GFile **) &repo_file, NULL, &error);
g_assert_no_error (error);
ostree_repo_write_commit (repo, NULL /* no parent */, ref_name, ref_name,
NULL /* no metadata */, repo_file, &checksum,
NULL, &error);
g_assert_no_error (error);
if (collection_id != NULL)
ostree_repo_set_collection_ref_immediate (repo, &collection_ref, checksum, NULL, &error);
else
ostree_repo_set_ref_immediate (repo, NULL, ref_name, checksum, NULL, &error);
g_assert_no_error (error);
}
va_end (args);
/* Update the summary. */
ostree_repo_regenerate_summary (repo, NULL /* no metadata */, NULL, &error);
g_assert_no_error (error);
return g_file_get_uri (repo_path);
}
/* Test resolving the refs against a collection of config files, which contain
* valid, invalid or duplicate repo information. */
static void
test_repo_finder_config_mixed_configs (Fixture *fixture,
gconstpointer test_data)
{
g_autoptr(OstreeRepoFinderConfig) finder = NULL;
g_autoptr(GMainContext) context = NULL;
g_autoptr(GAsyncResult) result = NULL;
g_autoptr(GPtrArray) results = NULL; /* (element-type OstreeRepoFinderResult) */
g_autoptr(GError) error = NULL;
gsize i;
const OstreeCollectionRef ref0 = { "org.example.Collection0", "exampleos/x86_64/ref0" };
const OstreeCollectionRef ref1 = { "org.example.Collection0", "exampleos/x86_64/ref1" };
const OstreeCollectionRef ref2 = { "org.example.Collection1", "exampleos/x86_64/ref1" };
const OstreeCollectionRef ref3 = { "org.example.Collection1", "exampleos/x86_64/ref2" };
const OstreeCollectionRef ref4 = { "org.example.Collection2", "exampleos/x86_64/ref3" };
const OstreeCollectionRef * const refs[] = { &ref0, &ref1, &ref2, &ref3, &ref4, NULL };
context = g_main_context_new ();
g_main_context_push_thread_default (context);
/* Put together various ref configuration files. */
g_autofree gchar *collection0_uri = assert_create_remote (fixture, "org.example.Collection0",
"exampleos/x86_64/ref0",
"exampleos/x86_64/ref1",
NULL);
g_autofree gchar *collection1_uri = assert_create_remote (fixture, "org.example.Collection1",
"exampleos/x86_64/ref2",
NULL);
g_autofree gchar *no_collection_uri = assert_create_remote (fixture, NULL,
"exampleos/x86_64/ref3",
NULL);
assert_create_remote_config (fixture->parent_repo, "remote0", collection0_uri, "org.example.Collection0");
assert_create_remote_config (fixture->parent_repo, "remote1", collection1_uri, "org.example.Collection1");
assert_create_remote_config (fixture->parent_repo, "remote0-copy", collection0_uri, "org.example.Collection0");
assert_create_remote_config (fixture->parent_repo, "remote1-bad-copy", collection1_uri, "org.example.NotCollection1");
assert_create_remote_config (fixture->parent_repo, "remote2", no_collection_uri, NULL);
finder = ostree_repo_finder_config_new ();
/* Resolve the refs. */
ostree_repo_finder_resolve_async (OSTREE_REPO_FINDER (finder), refs,
fixture->parent_repo, NULL, result_cb, &result);
while (result == NULL)
g_main_context_iteration (context, TRUE);
results = ostree_repo_finder_resolve_finish (OSTREE_REPO_FINDER (finder),
result, &error);
g_assert_no_error (error);
g_assert_nonnull (results);
g_assert_cmpuint (results->len, ==, 3);
/* Check that the results are correct: the invalid refs should have been
* ignored, and the valid results canonicalised and deduplicated. */
for (i = 0; i < results->len; i++)
{
const OstreeRepoFinderResult *result = g_ptr_array_index (results, i);
if (g_strcmp0 (ostree_remote_get_name (result->remote), "remote0") == 0 ||
g_strcmp0 (ostree_remote_get_name (result->remote), "remote0-copy") == 0)
{
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 2);
g_assert_true (g_hash_table_contains (result->ref_to_checksum, &ref0));
g_assert_true (g_hash_table_contains (result->ref_to_checksum, &ref1));
}
else if (g_strcmp0 (ostree_remote_get_name (result->remote), "remote1") == 0)
{
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 1);
g_assert_true (g_hash_table_contains (result->ref_to_checksum, &ref3));
}
else
{
g_assert_not_reached ();
}
}
g_main_context_pop_thread_default (context);
}
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/repo-finder-config/init", test_repo_finder_config_init);
g_test_add ("/repo-finder-config/no-configs", Fixture, NULL, setup,
test_repo_finder_config_no_configs, teardown);
g_test_add ("/repo-finder-config/mixed-configs", Fixture, NULL, setup,
test_repo_finder_config_mixed_configs, teardown);
return g_test_run();
}
+497
View File
@@ -0,0 +1,497 @@
/* -*- 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 <withnall@endlessm.com>
*/
#include "config.h"
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
#include "libostreetest.h"
#include "ostree-autocleanups.h"
#include "ostree-remote-private.h"
#include "ostree-repo-finder.h"
#include "ostree-repo-finder-mount.h"
#include "ostree-types.h"
#include "test-mock-gio.h"
/* Test fixture. Creates a temporary directory and repository. */
typedef struct
{
OstreeRepo *parent_repo;
int working_dfd; /* owned */
GFile *working_dir; /* owned */
} Fixture;
static void
setup (Fixture *fixture,
gconstpointer test_data)
{
g_autofree gchar *tmp_name = NULL;
g_autoptr(GError) error = NULL;
tmp_name = g_strdup ("test-repo-finder-mount-XXXXXX");
glnx_mkdtempat_open_in_system (tmp_name, 0700, &fixture->working_dfd, &error);
g_assert_no_error (error);
g_test_message ("Using temporary directory: %s", tmp_name);
glnx_shutil_mkdir_p_at (fixture->working_dfd, "repo", 0700, NULL, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
g_clear_error (&error);
g_assert_no_error (error);
g_autoptr(GFile) tmp_dir = g_file_new_for_path (g_get_tmp_dir ());
fixture->working_dir = g_file_get_child (tmp_dir, tmp_name);
fixture->parent_repo = ot_test_setup_repo (NULL, &error);
g_assert_no_error (error);
}
static void
teardown (Fixture *fixture,
gconstpointer test_data)
{
glnx_fd_close int parent_repo_dfd = -1;
g_autoptr(GError) error = NULL;
/* Recursively remove the temporary directory. */
glnx_shutil_rm_rf_at (fixture->working_dfd, ".", NULL, NULL);
close (fixture->working_dfd);
fixture->working_dfd = -1;
/* The repo also needs its source files to be removed. This is the inverse
* of setup_test_repository() in libtest.sh. */
g_autofree gchar *parent_repo_path = g_file_get_path (ostree_repo_get_path (fixture->parent_repo));
glnx_opendirat (-1, parent_repo_path, TRUE, &parent_repo_dfd, &error);
g_assert_no_error (error);
glnx_shutil_rm_rf_at (parent_repo_dfd, "../files", NULL, NULL);
glnx_shutil_rm_rf_at (parent_repo_dfd, "../repo", NULL, NULL);
g_clear_object (&fixture->working_dir);
g_clear_object (&fixture->parent_repo);
}
/* Test the object constructor works at a basic level. */
static void
test_repo_finder_mount_init (void)
{
g_autoptr(OstreeRepoFinderMount) finder = NULL;
g_autoptr(GVolumeMonitor) monitor = NULL;
/* Default #GVolumeMonitor. */
finder = ostree_repo_finder_mount_new (NULL);
g_clear_object (&finder);
/* Explicit #GVolumeMonitor. */
monitor = ostree_mock_volume_monitor_new (NULL, NULL);
finder = ostree_repo_finder_mount_new (monitor);
g_clear_object (&finder);
}
static void
result_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GAsyncResult **result_out = user_data;
*result_out = g_object_ref (result);
}
/* Test that no remotes are found if the #GVolumeMonitor returns no mounts. */
static void
test_repo_finder_mount_no_mounts (Fixture *fixture,
gconstpointer test_data)
{
g_autoptr(OstreeRepoFinderMount) finder = NULL;
g_autoptr(GVolumeMonitor) monitor = NULL;
g_autoptr(GMainContext) context = NULL;
g_autoptr(GAsyncResult) result = NULL;
g_autoptr(GPtrArray) results = NULL; /* (element-type OstreeRepoFinderResult) */
g_autoptr(GError) error = NULL;
const OstreeCollectionRef ref1 = { "org.example.Collection1", "exampleos/x86_64/standard" };
const OstreeCollectionRef ref2 = { "org.example.Collection1", "exampleos/x86_64/buildmaster/standard" };
const OstreeCollectionRef ref3 = { "org.example.Collection2", "exampleos/x86_64/standard" };
const OstreeCollectionRef ref4 = { "org.example.Collection2", "exampleos/arm64/standard" };
const OstreeCollectionRef * const refs[] = { &ref1, &ref2, &ref3, &ref4, NULL };
context = g_main_context_new ();
g_main_context_push_thread_default (context);
monitor = ostree_mock_volume_monitor_new (NULL, NULL);
finder = ostree_repo_finder_mount_new (monitor);
ostree_repo_finder_resolve_async (OSTREE_REPO_FINDER (finder), refs,
fixture->parent_repo,
NULL, result_cb, &result);
while (result == NULL)
g_main_context_iteration (context, TRUE);
results = ostree_repo_finder_resolve_finish (OSTREE_REPO_FINDER (finder),
result, &error);
g_assert_no_error (error);
g_assert_nonnull (results);
g_assert_cmpuint (results->len, ==, 0);
g_main_context_pop_thread_default (context);
}
/* Create a .ostree/repos directory under the given @mount_root, or abort. */
static gboolean
assert_create_repos_dir (Fixture *fixture,
const gchar *mount_root_name,
int *out_repos_dfd,
GMount **out_mount)
{
glnx_fd_close int repos_dfd = -1;
g_autoptr(GError) error = NULL;
g_autofree gchar *path = g_build_filename (mount_root_name, ".ostree", "repos", NULL);
glnx_shutil_mkdir_p_at_open (fixture->working_dfd, path, 0700, &repos_dfd, NULL, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
g_clear_error (&error);
g_assert_no_error (error);
*out_repos_dfd = glnx_steal_fd (&repos_dfd);
g_autoptr(GFile) mount_root = g_file_get_child (fixture->working_dir, mount_root_name);
*out_mount = G_MOUNT (ostree_mock_mount_new (mount_root_name, mount_root));
return TRUE;
}
/* Create a new repository in @repo_dir with its collection ID unset, and
* containing the refs given in @... (which must be %NULL-terminated). Each
* #OstreeCollectionRef in @... is followed by a gchar** return address for the
* checksum committed for that ref. Return the new repository. */
static OstreeRepo *
assert_create_remote_va (Fixture *fixture,
GFile *repo_dir,
va_list args)
{
g_autoptr(GError) error = NULL;
g_autoptr(OstreeRepo) repo = ostree_repo_new (repo_dir);
ostree_repo_create (repo, OSTREE_REPO_MODE_ARCHIVE_Z2, NULL, &error);
g_assert_no_error (error);
/* Set up the refs from @.... */
for (const OstreeCollectionRef *ref = va_arg (args, const OstreeCollectionRef *);
ref != NULL;
ref = va_arg (args, const OstreeCollectionRef *))
{
g_autofree gchar *checksum = NULL;
g_autoptr(OstreeMutableTree) mtree = NULL;
g_autoptr(OstreeRepoFile) repo_file = NULL;
gchar **out_checksum = va_arg (args, gchar **);
mtree = ostree_mutable_tree_new ();
ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, ".", mtree, NULL, NULL, &error);
g_assert_no_error (error);
ostree_repo_write_mtree (repo, mtree, (GFile **) &repo_file, NULL, &error);
g_assert_no_error (error);
ostree_repo_write_commit (repo, NULL /* no parent */, ref->ref_name, ref->ref_name,
NULL /* no metadata */, repo_file, &checksum,
NULL, &error);
g_assert_no_error (error);
if (ref->collection_id != NULL)
ostree_repo_set_collection_ref_immediate (repo, ref, checksum, NULL, &error);
else
ostree_repo_set_ref_immediate (repo, NULL, ref->ref_name, checksum, NULL, &error);
g_assert_no_error (error);
if (out_checksum != NULL)
*out_checksum = g_steal_pointer (&checksum);
}
/* Update the summary. */
ostree_repo_regenerate_summary (repo, NULL /* no metadata */, NULL, &error);
g_assert_no_error (error);
return g_steal_pointer (&repo);
}
static OstreeRepo *
assert_create_repo_dir (Fixture *fixture,
int repos_dfd,
GMount *repos_mount,
const OstreeCollectionRef *ref,
gchar **out_uri,
...) G_GNUC_NULL_TERMINATED;
/* Create a @ref directory under the given @repos_dfd, or abort. Create a new
* repository in it with the refs given in @..., as per assert_create_remote_va().
* Return the URI of the repository. */
static OstreeRepo *
assert_create_repo_dir (Fixture *fixture,
int repos_dfd,
GMount *repos_mount,
const OstreeCollectionRef *ref,
gchar **out_uri,
...)
{
glnx_fd_close int ref_dfd = -1;
g_autoptr(OstreeRepo) repo = NULL;
g_autoptr(GError) error = NULL;
va_list args;
g_autofree gchar *path = g_build_filename (ref->collection_id, ref->ref_name, NULL);
glnx_shutil_mkdir_p_at_open (repos_dfd, path, 0700, &ref_dfd, NULL, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
g_clear_error (&error);
g_assert_no_error (error);
g_autoptr(GFile) mount_root = g_mount_get_root (repos_mount);
g_autoptr(GFile) repos_dir = g_file_get_child (mount_root, ".ostree/repos");
g_autoptr(GFile) repo_dir = g_file_get_child (repos_dir, path);
va_start (args, out_uri);
repo = assert_create_remote_va (fixture, repo_dir, args);
va_end (args);
*out_uri = g_file_get_uri (repo_dir);
return g_steal_pointer (&repo);
}
/* Create a @ref symlink under the given @repos_dfd, pointing to
* @symlink_target, or abort. */
static int
assert_create_repo_symlink (int repos_dfd,
const OstreeCollectionRef *ref,
const gchar *symlink_target_path)
{
glnx_fd_close int symlink_target_dfd = -1;
g_autoptr(GError) error = NULL;
/* The @ref_parent_dir is not necessarily @collection_dir, since @ref may
* contain slashes. */
g_autofree gchar *path = g_build_filename (ref->collection_id, ref->ref_name, NULL);
g_autofree gchar *path_parent = g_path_get_dirname (path);
glnx_shutil_mkdir_p_at (repos_dfd, path_parent, 0700, NULL, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
g_clear_error (&error);
g_assert_no_error (error);
if (TEMP_FAILURE_RETRY (symlinkat (symlink_target_path, repos_dfd, path)) != 0)
{
g_autoptr(GError) error = NULL;
glnx_throw_errno_prefix (&error, "symlinkat");
g_assert_no_error (error);
}
/* Return a dir FD for the symlink target. */
glnx_opendirat (repos_dfd, path, TRUE, &symlink_target_dfd, &error);
g_assert_no_error (error);
return glnx_steal_fd (&symlink_target_dfd);
}
/* Add configuration for a remote named @remote_name, at @remote_uri, with a
* remote collection ID of @collection_id, to the given @repo. */
static void
assert_create_remote_config (OstreeRepo *repo,
const gchar *remote_name,
const gchar *remote_uri,
const gchar *collection_id)
{
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) options = NULL;
if (collection_id != NULL)
options = g_variant_new_parsed ("@a{sv} { 'collection-id': <%s> }",
collection_id);
ostree_repo_remote_add (repo, remote_name, remote_uri, options, NULL, &error);
g_assert_no_error (error);
}
/* Test resolving the refs against a collection of mock volumes, some of which
* are mounted, some of which are removable, some of which contain valid or
* invalid repo information on the file system, etc. */
static void
test_repo_finder_mount_mixed_mounts (Fixture *fixture,
gconstpointer test_data)
{
g_autoptr(OstreeRepoFinderMount) finder = NULL;
g_autoptr(GVolumeMonitor) monitor = NULL;
g_autoptr(GMainContext) context = NULL;
g_autoptr(GAsyncResult) result = NULL;
g_autoptr(GPtrArray) results = NULL; /* (element-type OstreeRepoFinderResult) */
g_autoptr(GError) error = NULL;
g_autoptr(GList) mounts = NULL; /* (element-type OstreeMockMount) */
g_autoptr(GMount) non_removable_mount = NULL;
g_autoptr(GMount) no_repos_mount = NULL;
g_autoptr(GMount) repo1_mount = NULL;
g_autoptr(GMount) repo2_mount = NULL;
g_autoptr(GFile) non_removable_root = NULL;
glnx_fd_close int no_repos_repos = -1;
glnx_fd_close int repo1_repos = -1;
glnx_fd_close int repo2_repos = -1;
g_autoptr(OstreeRepo) repo1_repo_a = NULL, repo1_repo_b = NULL;
g_autoptr(OstreeRepo) repo2_repo_a = NULL;
g_autofree gchar *repo1_repo_a_uri = NULL, *repo1_repo_b_uri = NULL;
g_autofree gchar *repo2_repo_a_uri = NULL;
g_autofree gchar *repo1_ref0_checksum = NULL, *repo1_ref1_checksum = NULL, *repo1_ref2_checksum = NULL;
g_autofree gchar *repo2_ref0_checksum = NULL, *repo2_ref1_checksum = NULL, *repo2_ref2_checksum = NULL;
g_autofree gchar *repo1_ref5_checksum = NULL;
gsize i;
const OstreeCollectionRef ref0 = { "org.example.Collection1", "exampleos/x86_64/ref0" };
const OstreeCollectionRef ref1 = { "org.example.Collection1", "exampleos/x86_64/ref1" };
const OstreeCollectionRef ref2 = { "org.example.Collection1", "exampleos/x86_64/ref2" };
const OstreeCollectionRef ref3 = { "org.example.Collection1", "exampleos/x86_64/ref3" };
const OstreeCollectionRef ref4 = { "org.example.UnconfiguredCollection", "exampleos/x86_64/ref4" };
const OstreeCollectionRef ref5 = { "org.example.Collection3", "exampleos/x86_64/ref0" };
const OstreeCollectionRef * const refs[] = { &ref0, &ref1, &ref2, &ref3, &ref4, &ref5, NULL };
context = g_main_context_new ();
g_main_context_push_thread_default (context);
/* Build the various mock drives/volumes/mounts, and some repositories with
* refs within them. We use "/" under the assumption that it’s on a separate
* file system from /tmp, so it’s an example of a symlink pointing outside
* its mount point. */
non_removable_root = g_file_get_child (fixture->working_dir, "non-removable-mount");
non_removable_mount = G_MOUNT (ostree_mock_mount_new ("non-removable", non_removable_root));
assert_create_repos_dir (fixture, "no-repos-mount", &no_repos_repos, &no_repos_mount);
assert_create_repos_dir (fixture, "repo1-mount", &repo1_repos, &repo1_mount);
repo1_repo_a = assert_create_repo_dir (fixture, repo1_repos, repo1_mount, refs[0], &repo1_repo_a_uri,
refs[0], &repo1_ref0_checksum,
refs[2], &repo1_ref2_checksum,
refs[5], &repo1_ref5_checksum,
NULL);
repo1_repo_b = assert_create_repo_dir (fixture, repo1_repos, repo1_mount, refs[1], &repo1_repo_b_uri,
refs[1], &repo1_ref1_checksum,
NULL);
assert_create_repo_symlink (repo1_repos, refs[2], "ref0"); /* repo1_repo_a */
assert_create_repo_symlink (repo1_repos, refs[5], "../../../org.example.Collection1/exampleos/x86_64/ref0"); /* repo1_repo_a */
assert_create_repos_dir (fixture, "repo2-mount", &repo2_repos, &repo2_mount);
repo2_repo_a = assert_create_repo_dir (fixture, repo2_repos, repo2_mount, refs[0], &repo2_repo_a_uri,
refs[0], &repo2_ref0_checksum,
refs[1], &repo2_ref1_checksum,
refs[2], &repo2_ref2_checksum,
refs[3], NULL,
NULL);
assert_create_repo_symlink (repo2_repos, refs[1], "ref0"); /* repo2_repo_a */
assert_create_repo_symlink (repo2_repos, refs[2], "ref1"); /* repo2_repo_b */
assert_create_repo_symlink (repo2_repos, refs[3], "/");
mounts = g_list_prepend (mounts, non_removable_mount);
mounts = g_list_prepend (mounts, no_repos_mount);
mounts = g_list_prepend (mounts, repo1_mount);
mounts = g_list_prepend (mounts, repo2_mount);
monitor = ostree_mock_volume_monitor_new (mounts, NULL);
finder = ostree_repo_finder_mount_new (monitor);
assert_create_remote_config (fixture->parent_repo, "remote1", "https://nope1", "org.example.Collection1");
assert_create_remote_config (fixture->parent_repo, "remote2", "https://nope2", "org.example.Collection2");
/* don’t configure org.example.UnconfiguredCollection */
assert_create_remote_config (fixture->parent_repo, "remote3", "https://nope3", "org.example.Collection3");
/* Resolve the refs. */
ostree_repo_finder_resolve_async (OSTREE_REPO_FINDER (finder), refs,
fixture->parent_repo,
NULL, result_cb, &result);
while (result == NULL)
g_main_context_iteration (context, TRUE);
results = ostree_repo_finder_resolve_finish (OSTREE_REPO_FINDER (finder),
result, &error);
g_assert_no_error (error);
g_assert_nonnull (results);
g_assert_cmpuint (results->len, ==, 4);
/* Check that the results are correct: the invalid refs should have been
* ignored, and the valid results canonicalised and deduplicated. */
for (i = 0; i < results->len; i++)
{
g_autofree gchar *uri = NULL;
const gchar *keyring;
const OstreeRepoFinderResult *result = g_ptr_array_index (results, i);
uri = g_key_file_get_string (result->remote->options, result->remote->group, "url", &error);
g_assert_no_error (error);
keyring = result->remote->keyring;
if (g_strcmp0 (uri, repo1_repo_a_uri) == 0 &&
g_strcmp0 (keyring, "remote1.trustedkeys.gpg") == 0)
{
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 2);
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[0]), ==, repo1_ref0_checksum);
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[2]), ==, repo1_ref2_checksum);
}
else if (g_strcmp0 (uri, repo1_repo_a_uri) == 0 &&
g_strcmp0 (keyring, "remote3.trustedkeys.gpg") == 0)
{
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 1);
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[5]), ==, repo1_ref5_checksum);
}
else if (g_strcmp0 (uri, repo1_repo_b_uri) == 0 &&
g_strcmp0 (keyring, "remote1.trustedkeys.gpg") == 0)
{
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 1);
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[1]), ==, repo1_ref1_checksum);
}
else if (g_strcmp0 (uri, repo2_repo_a_uri) == 0 &&
g_strcmp0 (keyring, "remote1.trustedkeys.gpg") == 0)
{
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 3);
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[0]), ==, repo2_ref0_checksum);
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[1]), ==, repo2_ref1_checksum);
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[2]), ==, repo2_ref2_checksum);
}
else
{
g_test_message ("Unknown result ‘%s’ with keyring ‘%s’.",
result->remote->name, result->remote->keyring);
g_assert_not_reached ();
}
}
g_main_context_pop_thread_default (context);
}
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/repo-finder-mount/init", test_repo_finder_mount_init);
g_test_add ("/repo-finder-mount/no-mounts", Fixture, NULL, setup,
test_repo_finder_mount_no_mounts, teardown);
g_test_add ("/repo-finder-mount/mixed-mounts", Fixture, NULL, setup,
test_repo_finder_mount_mixed_mounts, teardown);
return g_test_run();
}
+94
View File
@@ -0,0 +1,94 @@
#!/bin/bash
#
# 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 <withnall@endlessm.com>
set -euo pipefail
. $(dirname $0)/libtest.sh
echo "1..2"
cd ${test_tmpdir}
mkdir repo
ostree_repo_init repo
mkdir -p tree/root
touch tree/root/a
# Add a few commits
seq 5 | while read i; do
echo a >> tree/root/a
${CMD_PREFIX} ostree --repo=repo commit --branch=test-$i -m test -s test tree
done
# Generate a plain summary file.
${CMD_PREFIX} ostree --repo=repo summary --update
# Generate a signed summary file.
${CMD_PREFIX} ostree --repo=repo summary --update --gpg-homedir=${TEST_GPG_KEYHOME} --gpg-sign=${TEST_GPG_KEYID_1}
# Try various ways of adding additional data.
${CMD_PREFIX} ostree --repo=repo summary --update --add-metadata key="'value'" --add-metadata=key2=true
${CMD_PREFIX} ostree --repo=repo summary --update -m some-int='@t 123'
${CMD_PREFIX} ostree --repo=repo summary --update --add-metadata=map='@a{sv} {}'
# Check the additional metadata turns up in the output.
${CMD_PREFIX} ostree --repo=repo summary --view > summary
assert_file_has_content summary "^map: {}$"
echo "ok 1 update summary"
# Test again, but with collections enabled in the repository (if supported).
if ! ostree --version | grep -q -e '- experimental'; then
echo "ok 2 # skip No experimental API is compiled in"
exit 0
fi
cd ${test_tmpdir}
rm -rf repo
ostree_repo_init repo --collection-id org.example.Collection1
mkdir -p tree/root
touch tree/root/a
# Add a few commits
seq 5 | while read i; do
echo a >> tree/root/a
${CMD_PREFIX} ostree --repo=repo commit --branch=test-$i -m test -s test tree
${CMD_PREFIX} ostree --repo=repo refs --collections --create=org.example.Collection2:test-$i test-$i
done
# Generate a plain summary file.
${CMD_PREFIX} ostree --repo=repo summary --update
# Generate a signed summary file.
${CMD_PREFIX} ostree --repo=repo summary --update --gpg-homedir=${TEST_GPG_KEYHOME} --gpg-sign=${TEST_GPG_KEYID_1}
# Try various ways of adding additional data.
${CMD_PREFIX} ostree --repo=repo summary --update --add-metadata key="'value'" --add-metadata=key2=true
${CMD_PREFIX} ostree --repo=repo summary --update -m some-int='@t 123'
${CMD_PREFIX} ostree --repo=repo summary --update --add-metadata=map='@a{sv} {}'
# Check the additional metadata turns up in the output.
${CMD_PREFIX} ostree --repo=repo summary --view > summary
assert_file_has_content summary "^map: {}$"
echo "ok 2 update summary with collections"
+2
View File
@@ -127,6 +127,8 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
. $(dirname $0)/libtest.sh
unshare -m true || \
skip "this test needs to set up mount namespaces, rerun as root"
[ -f /bin/busybox ] || \
skip "this test needs busybox"
echo "1..3"
test_that_prepare_root_sets_sysroot_up_correctly_with_initrd
+6 -2
View File
@@ -22,7 +22,11 @@ set -xeuo pipefail
echo '1..3'
released_syms=${G_TEST_SRCDIR}/src/libostree/libostree-released.sym
devel_syms=${G_TEST_SRCDIR}/src/libostree/libostree-devel.sym
if echo "$OSTREE_FEATURES" | grep --quiet --no-messages "devel"; then
devel_syms=${G_TEST_SRCDIR}/src/libostree/libostree-devel.sym
else
devel_syms=
fi
if echo "$OSTREE_FEATURES" | grep --quiet --no-messages "experimental"; then
experimental_sym="${G_TEST_SRCDIR}/src/libostree/libostree-experimental.sym"
experimental_sections="${G_TEST_SRCDIR}/apidoc/ostree-experimental-sections.txt"
@@ -48,7 +52,7 @@ echo 'ok documented symbols'
# ONLY update this checksum in release commits!
cat > released-sha256.txt <<EOF
42f98d8816acbe22e925d18bf76ea2c6f7e12ee5d7a381979e6ed07bb6a1f755 ${released_syms}
d3f3d564e4679d603220da0cea940867fde28622857d088a8287db730bcc8001 ${released_syms}
EOF
sha256sum -c released-sha256.txt