fuse: Add tests for guestmount.
authorRichard Jones <rjones@redhat.com>
Tue, 17 Nov 2009 17:03:31 +0000 (17:03 +0000)
committerRichard Jones <rjones@redhat.com>
Tue, 17 Nov 2009 20:14:47 +0000 (20:14 +0000)
This script contains non-exhaustive tests for the system calls
implemented by guestmount.

fuse/Makefile.am
fuse/test-fuse.sh [new file with mode: 0755]

index c041058..ebf948a 100644 (file)
@@ -39,6 +39,8 @@ guestmount_LDADD = \
        $(top_builddir)/src/libguestfs.la \
        ../gnulib/lib/libgnu.la
 
+# Documentation.
+
 man_MANS = guestmount.1
 
 guestmount.1: guestmount.pod
@@ -60,4 +62,10 @@ $(top_builddir)/html/guestmount.1.html: guestmount.pod
          --outfile html/guestmount.1.html \
          fuse/guestmount.pod
 
+# Tests.
+
+TESTS = test-fuse.sh
+TESTS_ENVIRONMENT = \
+       top_builddir=..
+
 endif
diff --git a/fuse/test-fuse.sh b/fuse/test-fuse.sh
new file mode 100755 (executable)
index 0000000..fffd851
--- /dev/null
@@ -0,0 +1,218 @@
+#!/bin/bash -
+# libguestfs
+# Copyright (C) 2009 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+unset CDPATH
+set -e
+#set -v
+
+if [ -z "$top_builddir" ]; then
+    echo "$0: error: environment variable \$top_builddir must be set"
+    exit 1
+fi
+
+nr_stages=$(grep "^stage " $0 | wc -l)
+
+# Allow top_builddir to be a relative path, but also make it absolute,
+# and move to that directory for the initial phase of the script.
+top_builddir=$(cd "$top_builddir" > /dev/null; pwd)
+
+# Set libguestfs up for running locally.
+export LIBGUESTFS_PATH="$top_builddir/appliance"
+
+# Paths to the other programs and files.  NB: Must be absolute paths.
+guestfish="$top_builddir/fish/guestfish"
+guestmount="$top_builddir/fuse/guestmount"
+image="$top_builddir/fuse/test.img"
+mp="$top_builddir/fuse/test-mp"
+
+if [ ! -x "$guestfish" -o ! -x "$guestmount" ]; then
+    echo "$0: error: guestfish or guestmount are not available"
+    exit 1
+fi
+
+# Ensure everything is cleaned up on exit.
+rm -f "$image"
+mkdir -p "$mp"
+fusermount -u "$mp" >/dev/null 2>&1 ||:
+function cleanup ()
+{
+    status=$?
+    set +e
+    [ $status = 0 ] || echo "*** FAILED ***"
+    echo "Unmounting filesystem and cleaning up."
+
+    # Move out of the mountpoint (otherwise our cwd will prevent the
+    # mountpoint from being unmounted).
+    cd "$top_builddir"
+
+    # Who's using this?  Should be no one, but see below.
+    if [ -x /sbin/fuser ]; then /sbin/fuser "$mp"; fi
+
+    # If you run this and you have GNOME running at the same time,
+    # then randomly /usr/libexec/gvfs-gdu-volume-monitor will decide
+    # to do whatever it does in the mountpoint directory, preventing
+    # you from unmounting it!  Hence the need for this loop.
+    count=10
+    while ! fusermount -u "$mp" && [ $count -gt 0 ]; do
+       sleep 1
+       ((count--))
+    done
+
+    rm -f "$image"
+    rm -rf "$mp"
+    exit $status
+}
+trap cleanup INT TERM QUIT EXIT
+
+s=1
+function stage ()
+{
+    echo "test-fuse: $s/$nr_stages:" "$@" "..."
+    ((s++))
+}
+
+stage Create filesystem with some inital content
+$guestfish <<EOF
+  sparse "$image" 10M
+  run
+  part-disk /dev/sda mbr
+  mkfs ext2 /dev/sda1
+  mount /dev/sda1 /
+  write-file /hello.txt hello 0
+  write-file /world.txt "hello world" 0
+  touch /empty
+EOF
+
+stage Mounting the filesystem
+$guestmount -a "$image" -m /dev/sda1 "$mp"
+
+stage Changing into mounted directory
+cd "$mp"
+
+stage Checking initial files exist
+[ -n "$(echo *)" ]
+[ "$(ls empty hello.txt world.txt)" = "empty
+hello.txt
+world.txt" ]
+
+stage Checking initial files contain expected content
+[ "$(cat hello.txt)" = "hello" ]
+[ "$(cat world.txt)" = "hello world" ]
+cat empty ;# should print nothing
+[ -z "$(cat empty)" ]
+
+stage Checking file modes of initial content
+[ "$(stat -c %a empty)" = "644" ]
+[ "$(stat -c %a hello.txt)" = "644" ]
+[ "$(stat -c %a world.txt)" = "644" ]
+
+stage Checking sizes of initial content
+[ "$(stat -c %s empty)" -eq 0 ]
+[ "$(stat -c %s hello.txt)" -eq 5 ]
+[ "$(stat -c %s world.txt)" -eq 11 ]
+
+stage Checking unlink
+touch new
+rm -f new ;# force because file is "owned" by root
+
+stage Checking symbolic link
+ln -s hello.txt symlink
+[ -L symlink ]
+
+stage Checking readlink
+[ "$(readlink symlink)" = "hello.txt" ]
+
+stage Checking hard link
+[ "$(stat -c %h hello.txt)" -eq 1 ]
+ln hello.txt link
+[ "$(stat -c %h link)" -eq 2 ]
+[ "$(stat -c %h hello.txt)" -eq 2 ]
+rm -f link
+[ ! -e link ]
+
+# This fails because of caching.  The problem is that the linked file
+# ("hello.txt") is cached with a link count of 2.  unlink("link")
+# invalidates the cache for "link", but _not_ for "hello.txt" which
+# still has the now-incorrect cached value.  However there's not much
+# we can do about this since searching for all linked inodes of a file
+# is an O(n) operation.
+#[ "$(stat -c %h hello.txt)" -eq 1 ]
+
+stage Checking mkdir
+mkdir newdir
+[ -d newdir ]
+
+stage Checking rmdir
+rmdir newdir
+[ ! -e newdir ]
+
+stage Checking rename
+touch old
+mv old new
+[ -f new ]
+[ ! -e old ]
+rm -f new
+
+stage Checking chmod
+touch new
+chmod a+x new
+[ -x new ]
+chmod a-x new
+[ ! -x new ]
+chmod a-w new
+[ ! -w new ]
+chmod a+w new
+[ -w new ]
+chmod a-r new
+[ ! -r new ]
+chmod a+r new
+[ -r new ]
+rm -f new
+
+stage Checking truncate
+truncate -s 10000 truncated
+[ "$(stat -c %s truncated)" -eq 10000 ]
+truncate -c -s 1000 truncated
+[ "$(stat -c %s truncated)" -eq 1000 ]
+truncate -c -s 10 truncated
+[ "$(stat -c %s truncated)" -eq 10 ]
+truncate -c -s 0 truncated
+[ "$(stat -c %s truncated)" -eq 0 ]
+rm -f truncated
+
+stage Checking utimens and timestamps
+for ts in 12345 1234567 987654321; do
+    # NB: It's not possible to set the ctime with touch.
+    touch -a -d @$ts timestamp
+    [ "$(stat -c %X timestamp)" -eq $ts ]
+    touch -m -d @$ts timestamp
+    [ "$(stat -c %Y timestamp)" -eq $ts ]
+    touch    -d @$ts timestamp
+    [ "$(stat -c %X timestamp)" -eq $ts ]
+    [ "$(stat -c %Y timestamp)" -eq $ts ]
+done
+
+# These ones are not yet tested by the current script:
+#stage XXX statfs/statvfs
+#stage XXX xattr operations
+
+# These ones cannot easily be tested by the current script, eg because
+# this script doesn't run as root:
+#stage XXX fsync
+#stage XXX chown
+#stage XXX mknod