[parted-devel] add integration tests

Jim Meyering jim at meyering.net
Wed Apr 25 21:13:41 UTC 2007


Here's a long-overdue change.

It all started when I tried to run the tests described in
debug/test/HOWTO.  That didn't go well at all, partly because of how
"-s" is broken when it comes to multi-operator command lines.  I haven't
fixed that part yet.  Starting small.  I found that with -s, I wasn't
even getting diagnostics for some very basic failures.  So these changes
and tests attempt to fix a couple of those.

Writing tests to exercise the fixes was not trivial, in part because
parted's behavior depends on "isatty(0)" (whether stdin has a tty),
and when I try to simulate what happens in the interactive case (no -s)
by redirecting stdin from a file, parted would no longer use the code
path I was trying to test.  So part of the change below is to add the
for-debugging-only ---pretend-input-tty option.  With it, I can write
tests that simulate a run with isatty(0).  The alternative would have
been to use expect or Perl's Expect.pm, but people often don't have
those, and they come with their own challenges.

So I've added four test scripts and a few aux. ones in a new directory,
tests/.  Much of the framework is identical to what I use in coreutils.

Feedback welcome.
Unless someone objects, I'll check this in tomorrow or Friday.

2007-04-25  Jim Meyering  <jim at meyering.net>

	Add integration tests, then adjust code so that they pass.
	These changes affect how --script (-s) works, and when diagnostics
	are displayed.
	* Makefile.am (SUBDIRS): Add tests.
	* configure.ac (AC_OUTPUT): Add tests/Makefile.
	* tests/Makefile.am: New file.
	* tests/part-01, tests/label-01: New files.
	* tests/part-02, tests/label-02: New files.
	* parted/parted.c (enum) [PRETEND_INPUT_TTY]: Define.
	Support a new, undocumented option: ---pretend-input-tty.
	It is intended solely for testing.  The leading '---' ensures
	that it will never conflict with a "regular" long option.
	(do_mkpartfs): Prompt only when not in script mode.
	Call ped_exception_leave_all() to decrement global counter.
	Without this, the use of parted in tests/part-01 would still fail,
	but would not issue the diagnostic explaining why.
	* parted/ui.h (pretend_input_tty): Declare new global.
	* parted/ui.c (exception_handler): Let the new "pretend_input_tty"
	option override "!isatty (0)".
	(init_ui): Don't set opt_script_mode.
	* tests/lang-default, tests/priv-check: New files.

diff --git a/Makefile.am b/Makefile.am
index 1c65b5f..1aba177 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
 ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = po lib libparted parted partprobe include doc debug
+SUBDIRS = po lib libparted parted partprobe include doc debug tests

 EXTRA_DIST =				\
   .prev-version				\
diff --git a/configure.ac b/configure.ac
index 5735541..9401d46 100644
--- a/configure.ac
+++ b/configure.ac
@@ -502,6 +502,7 @@ doc/pt_BR/Makefile
 debug/Makefile
 debug/clearfat/Makefile
 debug/test/Makefile
+tests/Makefile
 po/Makefile.in
 ])

diff --git a/parted/parted.c b/parted/parted.c
index ff72dcd..37ed053 100644
--- a/parted/parted.c
+++ b/parted/parted.c
@@ -65,6 +65,13 @@ static int MEGABYTE_SECTORS (PedDevice* dev)
         return PED_MEGABYTE_SIZE / dev->sector_size;
 }

+/* For long options that have no equivalent short option, use a
+   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
+enum
+{
+  PRETEND_INPUT_TTY = CHAR_MAX + 1,
+};
+

 typedef struct {
         time_t  last_update;
@@ -78,6 +85,7 @@ static struct option    options[] = {
         {"machine",     0, NULL, 'm'},
         {"script",      0, NULL, 's'},
         {"version",     0, NULL, 'v'},
+        {"-pretend-input-tty", 0, NULL, PRETEND_INPUT_TTY},
         {NULL,          0, NULL, 0}
 };

@@ -93,6 +101,7 @@ static char*    options_help [][2] = {
 char *program_name;

 int     opt_script_mode = 0;
+int     pretend_input_tty = 0;
 int     opt_machine_mode = 0;
 int     disk_is_modified = 0;
 int     is_toggle_mode = 0;
@@ -592,11 +601,12 @@ do_mklabel (PedDevice** dev)
         ped_exception_leave_all ();

         if (disk) {
-                if (!_disk_warn_busy (disk))
-                        goto error_destroy_disk;
-
-                if (!_disk_warn_loss (disk))
-                        goto error_destroy_disk;
+                if (!opt_script_mode) {
+                        if (!_disk_warn_busy (disk))
+                                goto error_destroy_disk;
+                        if (!_disk_warn_loss (disk))
+                                goto error_destroy_disk;
+                }

                 ped_disk_destroy (disk);
         }
@@ -943,6 +953,7 @@ do_mkpartfs (PedDevice** dev)
                 }
         }
         ped_exception_catch();
+        ped_exception_leave_all();

         /* set LBA flag automatically if available */
         if (ped_partition_is_flag_available (part, PED_PARTITION_LBA))
@@ -2309,6 +2320,9 @@ while (1)
                 case 'm': opt_machine_mode = 1; break;
                 case 's': opt_script_mode = 1; break;
                 case 'v': version = 1; break;
+                case PRETEND_INPUT_TTY:
+                  pretend_input_tty = 1;
+                  break;
                 default:  wrong = 1; break;
         }
 }
diff --git a/parted/ui.c b/parted/ui.c
index 59cb98b..25700ac 100644
--- a/parted/ui.c
+++ b/parted/ui.c
@@ -624,14 +624,15 @@ exception_handler (PedException* ex)
                 return opt;

         /* script-mode: don't handle the exception */
-        if (opt_script_mode)
+        if (opt_script_mode || (!isatty (0) && !pretend_input_tty))
                 return PED_EXCEPTION_UNHANDLED;

         got_ctrl_c = 0;

         do {
                 opt = command_line_get_ex_opt ("", ex->options);
-        } while (opt == PED_EXCEPTION_UNHANDLED && isatty (0) && !got_ctrl_c);
+        } while (opt == PED_EXCEPTION_UNHANDLED
+                 && (isatty (0) || pretend_input_tty) && !got_ctrl_c);

         if (got_ctrl_c) {
                 got_ctrl_c = 0;
@@ -1365,8 +1366,6 @@ init_disk_type_str ()
 int
 init_ui ()
 {
-        opt_script_mode = !isatty (0);
-
         if (!init_ex_opt_str ()
             || !init_state_str ()
             || !init_fs_type_str ()
diff --git a/parted/ui.h b/parted/ui.h
index 3ea0acb..147b170 100644
--- a/parted/ui.h
+++ b/parted/ui.h
@@ -1,6 +1,6 @@
 /*
     parted - a frontend to libparted
-    Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+    Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, 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
@@ -74,10 +74,10 @@ extern void print_using_dev (PedDevice* dev);

 /* in parted.c */
 extern int	opt_script_mode;
+extern int	pretend_input_tty;

 extern void print_options_help ();
 extern void print_commands_help ();


 #endif /* UI_H_INCLUDED */
-
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..3d443ad
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,11 @@
+TESTS = \
+  label-01 \
+  label-02 \
+  part-01 \
+  part-02
+
+TESTS_ENVIRONMENT = \
+  PATH="`pwd`/../parted$(PATH_SEPARATOR)$$PATH"
+
+EXTRA_DIST = \
+  $(TESTS) lang-default priv-check
diff --git a/tests/label-01 b/tests/label-01
new file mode 100755
index 0000000..596ced3
--- /dev/null
+++ b/tests/label-01
@@ -0,0 +1,61 @@
+#!/bin/sh
+# Ensure that a simple command using -s succeeds with no prompt
+
+# Copyright (C) 2007 Free Software Foundation, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  parted --version
+fi
+
+. $srcdir/lang-default
+
+pwd=`pwd`
+t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$
+trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0
+trap '(exit $?); exit $?' 1 2 13 15
+
+framework_failure=0
+mkdir -p $tmp || framework_failure=1
+cd $tmp || framework_failure=1
+
+# create a file of size N bytes
+N=1M
+dev=loop-file
+dd if=/dev/zero of=$dev bs=$N count=1 2> /dev/null || framework_failure=1
+
+# Expect no output for root.
+# Expect the warning for non-root.
+touch exp
+uid=`id -u` || uid=1
+test "$uid" = 0 || cat <<\EOF > exp || fail=1
+WARNING: You are not superuser.  Watch out for permissions.
+EOF
+
+if test $framework_failure = 1; then
+  echo "$0: failure in testing framework" 1>&2
+  (exit 1); exit 1
+fi
+
+fail=0
+parted -s $dev mklabel msdos > out 2>&1 || fail=1
+
+cmp out exp || fail=1
+test $fail = 1 && diff out exp 2> /dev/null
+
+(exit $fail); exit $fail
diff --git a/tests/label-02 b/tests/label-02
new file mode 100755
index 0000000..f26fed5
--- /dev/null
+++ b/tests/label-02
@@ -0,0 +1,70 @@
+#!/bin/sh
+# Ensure that a simple mklabel command succeeds.
+# Since there's no -s option, there are prompts.
+
+# Copyright (C) 2007 Free Software Foundation, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  parted --version
+fi
+
+. $srcdir/lang-default
+
+pwd=`pwd`
+t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$
+trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0
+trap '(exit $?); exit $?' 1 2 13 15
+
+framework_failure=0
+mkdir -p $tmp || framework_failure=1
+cd $tmp || framework_failure=1
+
+# create a file of size N bytes
+N=1M
+dev=loop-file
+dd if=/dev/zero of=$dev bs=$N count=1 2> /dev/null || framework_failure=1
+printf 'y\n\n' > in || framework_failure=1
+
+if test $framework_failure = 1; then
+  echo "$0: failure in testing framework" 1>&2
+  (exit 1); exit 1
+fi
+
+fail=0
+# First iteration works with no prompting, since there is no preexisting label.
+parted $dev mklabel msdos > out 2>&1 || fail=1
+
+# Now that there's a label, rerunning the same command is interactive.
+parted ---pretend-input-tty $dev mklabel msdos < in > o2 2>&1 || fail=1
+sed "s,^/.*$dev ,,;s,
 *
,,;s, $,," o2 >> out
+
+cat <<\EOF > exp || fail=1
+WARNING: You are not superuser.  Watch out for permissions.
+WARNING: You are not superuser.  Watch out for permissions.
+Warning: The existing disk label on
+will be destroyed and all
+data on this disk will be lost. Do you want to continue?
+Yes/No? y
+New disk label type?  [msdos]?
+EOF
+
+cmp out exp || fail=1
+test $fail = 1 && diff -u out exp 2> /dev/null
+
+(exit $fail); exit $fail
diff --git a/tests/lang-default b/tests/lang-default
new file mode 100644
index 0000000..882672f
--- /dev/null
+++ b/tests/lang-default
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Set locale-related environment variables so we get consistent
+# message translations, time formats, sort orderings, etc.
+
+LC_ALL=C
+export LC_ALL
+unset LANGUAGE NLSPATH
+
+# These settings shouldn't matter, but unset them anyway just in case.
+unset LANG LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME
diff --git a/tests/part-01 b/tests/part-01
new file mode 100755
index 0000000..1d6798e
--- /dev/null
+++ b/tests/part-01
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Ensure that mkpartfs fails when the device is too small.
+
+# Copyright (C) 2007 Free Software Foundation, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  parted --version
+fi
+
+. $srcdir/lang-default
+
+pwd=`pwd`
+t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$
+trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0
+trap '(exit $?); exit $?' 1 2 13 15
+
+framework_failure=0
+mkdir -p $tmp || framework_failure=1
+cd $tmp || framework_failure=1
+
+# You need much more than this to create a fat32 file system
+N=1M
+dev=loop-file
+dd if=/dev/zero of=$dev bs=$N count=1 2> /dev/null || framework_failure=1
+parted $dev mklabel msdos > /dev/null 2>&1 || framework_failure=1
+
+cat <<\EOF > exp || framework_failure=1
+WARNING: You are not superuser.  Watch out for permissions.
+Error: Partition too big/small for a fat32 file system.
+EOF
+
+if test $framework_failure = 1; then
+  echo "$0: failure in testing framework" 1>&2
+  (exit 1); exit 1
+fi
+
+fail=0
+# Require nonzero exit status.
+parted -s $dev mkpartfs primary fat32 0 1 > out 2>&1 && fail=1
+
+cmp out exp || fail=1
+test $fail = 1 && diff -u out exp 2> /dev/null
+
+(exit $fail); exit $fail
diff --git a/tests/part-02 b/tests/part-02
new file mode 100755
index 0000000..bef517e
--- /dev/null
+++ b/tests/part-02
@@ -0,0 +1,62 @@
+#!/bin/sh
+# Ensure that mkpartfs works in one small case.
+
+# Copyright (C) 2007 Free Software Foundation, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  parted --version
+fi
+
+. $srcdir/lang-default
+
+pwd=`pwd`
+t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$
+trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0
+trap '(exit $?); exit $?' 1 2 13 15
+
+framework_failure=0
+mkdir -p $tmp || framework_failure=1
+cd $tmp || framework_failure=1
+
+# 40MB is just enough space in which to create a fat32 file system
+N=40M
+dev=loop-file
+dd if=/dev/zero of=$dev bs=$N count=1 2> /dev/null || framework_failure=1
+parted $dev mklabel msdos > /dev/null 2>&1 || framework_failure=1
+
+# Expect no output for root.
+# Expect the warning for non-root.
+touch exp
+uid=`id -u` || uid=1
+test "$uid" = 0 || cat <<\EOF > exp || fail=1
+WARNING: You are not superuser.  Watch out for permissions.
+EOF
+
+if test $framework_failure = 1; then
+  echo "$0: failure in testing framework" 1>&2
+  (exit 1); exit 1
+fi
+
+fail=0
+parted -s $dev mkpartfs primary fat32 0 40 > out 2>&1 || fail=1
+
+cmp out exp || fail=1
+test $fail = 1 && diff -u out exp 2> /dev/null
+
+(exit $fail); exit $fail
diff --git a/tests/priv-check b/tests/priv-check
new file mode 100644
index 0000000..b0d6c92
--- /dev/null
+++ b/tests/priv-check
@@ -0,0 +1,94 @@
+# -*- sh -*-
+# Source this file at the beginning of a test that works
+# only when run as root or as non-root.
+
+# Copyright (C) 2001, 2003, 2004 Free Software Foundation, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+case "$PRIV_CHECK_ARG" in
+  require-root) who='as root';;
+  require-non-root) who='by an unprivileged user';;
+  *) echo "Usage: PRIV_CHECK_ARG={require-root|require-non-root} . priv-check"\
+     1>&2; exit 1;;
+esac
+
+# Make sure id -u succeeds.
+my_uid=`id -u`
+test $? = 0 || {
+  echo "$0: cannot run \`id -u'" 1>&2
+  (exit 1); exit 1
+}
+
+# Make sure it gives valid output.
+case $my_uid in
+  *[!0-9]*)
+    echo "$0: invalid output (\`$my_uid') from \`id -u'" 1>&2
+    (exit 1); exit 1
+    ;;
+  *) ;;
+esac
+
+test $my_uid = 0 && \
+{
+  # When running as root, always ensure that we have a valid non-root username.
+  # As non-root, don't do anything, since we won't be running setuidgid.
+  : ${NON_ROOT_USERNAME=nobody}
+
+  # Ensure that the supplied username is valid and with UID != 0.
+  coreutils_non_root_uid=`id -u $NON_ROOT_USERNAME`
+  test $? = 0 || \
+    {
+      echo "$0: This command failed: \`id -u $NON_ROOT_USERNAME'" 1>&2
+      echo "$0: Skipping this test.  To enable it, set the envvar" 1>&2
+      echo "$0: NON_ROOT_USERNAME to a non-root user name." 1>&2
+      (exit 77); exit 77
+    }
+  test "$coreutils_non_root_uid" = 0 && \
+    {
+      echo "$0: The specified NON_ROOT_USERNAME ($NON_ROOT_USERNAME)" 1>&2
+      echo "$0: is invalid because its UID is 0." 1>&2
+      (exit 1); exit 1
+    }
+}
+
+give_msg=no
+case $PRIV_CHECK_ARG:$my_uid in
+  require-root:0) ;;
+  require-root:*) give_msg=yes ;;
+  require-non-root:0)
+    # `.' must be writable by $NON_ROOT_USERNAME
+    setuidgid $NON_ROOT_USERNAME test -w . ||
+      {
+	echo "$0: `pwd`: not writable by user \`$NON_ROOT_USERNAME'" 1>&2
+	echo "$0: skipping this test" 1>&2
+	(exit 77); exit 77
+      }
+    exec setuidgid $NON_ROOT_USERNAME env PATH="$PATH" $0
+    ;;
+  require-non-root:*) ;;
+esac
+
+test $give_msg = yes && {
+  cat <<EOF
+***************************
+NOTICE:
+$0: This test is being skipped, since it works only
+when run $who.
+***************************
+EOF
+  (exit 77); exit 77
+}




More information about the parted-devel mailing list