[PATCH/RFC 7/7] t7006-pager: if stdout is not a terminal, make a new one

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Test t7006-pager skips most of its tests except when running with
--verbose, for lack of a terminal to use.  Thus many of the tests
would not get much coverage, since whole testsuite runs with --verbose
are rare.  Redirecting output to /dev/tty would be problematic because
(1) there really could be no controlling terminal and (2) test
breakage might manifest itself by the producing extraneous output.
Luckily, there is a way around this: Unix98-style pseudo-terminals
allow us to create a fake terminal on the fly and capture its output.

Do so.  The new test-terminal command to accomplish this uses
posix_openpt (from SuSv3) to create a terminal because that is good
enough on Linux.  I would like some feedback on what platforms are
missing that function and thus what alternate interfaces are worth
supporting.  The perl IO::Tty module could take care of this for us,
but I do not think it is worth the extra dependency.

The test-terminal facility can be disabled by passing
NO_POSIX_OPENPT=YesPlease to the makefile, in which case the test
will revert to its old behavior (skipping most tests unless
GIT_TEST_OPTS includes --verbose).

Signed-off-by: Jonathan Nieder <jrnieder@xxxxxxxxx>
---
Requiring users to add NO_POSIX_OPENPT themselves doesn’t seem so
great.  I did it that way just so I could send something and get
some feedback; a more suitable version would need a way to detect
missing openpt on the fly, for example by noticing the compilation
failure and continuing anyway.

 .gitignore       |    1 +
 Makefile         |    6 +++++
 t/t7006-pager.sh |   28 +++++++++++++++--------
 test-terminal.c  |   62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+), 10 deletions(-)
 create mode 100644 test-terminal.c

diff --git a/.gitignore b/.gitignore
index 8df8f88..6b405ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -169,6 +169,7 @@
 /test-run-command
 /test-sha1
 /test-sigchain
+/test-terminal
 /common-cmds.h
 *.tar.gz
 *.dsc
diff --git a/Makefile b/Makefile
index 7bf2fca..9c900a7 100644
--- a/Makefile
+++ b/Makefile
@@ -45,6 +45,8 @@ all::
 #
 # Define NO_MEMMEM if you don't have memmem.
 #
+# Define NO_POSIX_OPENPT if you don't have posix_openpt (for tests).
+#
 # Define NO_STRLCPY if you don't have strlcpy.
 #
 # Define NO_STRTOUMAX if you don't have strtoumax in the C library.
@@ -1727,6 +1729,10 @@ TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
 TEST_PROGRAMS_NEED_X += test-index-version
 
+ifndef NO_POSIX_OPENPT
+TEST_PROGRAMS_NEED_X += test-terminal
+endif
+
 TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index 2e9cb9d..21788e3 100644
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -5,18 +5,26 @@ test_description='Test automatic use of a pager.'
 . ./test-lib.sh
 
 rm -f stdout_is_tty
-test_expect_success 'is stdout a terminal?' '
+test_expect_success 'set up terminal for tests' '
 	if test -t 1
 	then
 		: > stdout_is_tty
+	elif test-terminal sh -c "test -t 1"
+	then
+		: > test_terminal_works
 	fi
 '
 
 if test -e stdout_is_tty
 then
+	test_terminal() { "$@"; }
+	test_set_prereq TTY
+elif test -e test_terminal_works
+then
+	test_terminal() { test-terminal "$@"; }
 	test_set_prereq TTY
 else
-	say stdout is not a terminal, so skipping some tests.
+	say no usable terminal, so skipping some tests
 fi
 
 unset GIT_PAGER GIT_PAGER_IN_USE
@@ -30,13 +38,13 @@ test_expect_success 'setup' '
 
 rm -f paginated.out
 test_expect_success TTY 'some commands use a pager' '
-	git log &&
+	test_terminal git log &&
 	test -e paginated.out
 '
 
 rm -f paginated.out
 test_expect_success TTY 'some commands do not use a pager' '
-	git rev-list HEAD &&
+	test_terminal git rev-list HEAD &&
 	! test -e paginated.out
 '
 
@@ -54,7 +62,7 @@ test_expect_success 'no pager when stdout is a regular file' '
 
 rm -f paginated.out
 test_expect_success TTY 'git --paginate rev-list uses a pager' '
-	git --paginate rev-list HEAD  &&
+	test_terminal git --paginate rev-list HEAD &&
 	test -e paginated.out
 '
 
@@ -66,7 +74,7 @@ test_expect_success 'no pager even with --paginate when stdout is a pipe' '
 
 rm -f paginated.out
 test_expect_success TTY 'no pager with --no-pager' '
-	git --no-pager log &&
+	test_terminal git --no-pager log &&
 	! test -e paginated.out
 '
 
@@ -127,7 +135,7 @@ test_expect_success SIMPLEPAGER 'default pager is used by default' '
 	: > default_pager_used
 	EOF
 	chmod +x $less &&
-	PATH=.:$PATH git log &&
+	PATH=.:$PATH test_terminal git log &&
 	test -e default_pager_used
 '
 
@@ -137,7 +145,7 @@ rm -f PAGER_used
 test_expect_success TTY 'PAGER overrides default pager' '
 	PAGER=": > PAGER_used" &&
 	export PAGER &&
-	git log &&
+	test_terminal git log &&
 	test -e PAGER_used
 '
 
@@ -147,7 +155,7 @@ test_expect_success TTY 'core.pager overrides PAGER' '
 	PAGER=: &&
 	export PAGER &&
 	git config core.pager ": > core.pager_used" &&
-	git log &&
+	test_terminal git log &&
 	test -e core.pager_used
 '
 
@@ -156,7 +164,7 @@ test_expect_success TTY 'GIT_PAGER overrides core.pager' '
 	git config core.pager : &&
 	GIT_PAGER=": > GIT_PAGER_used" &&
 	export GIT_PAGER &&
-	git log &&
+	test_terminal git log &&
 	test -e GIT_PAGER_used
 '
 
diff --git a/test-terminal.c b/test-terminal.c
new file mode 100644
index 0000000..f4d6a71
--- /dev/null
+++ b/test-terminal.c
@@ -0,0 +1,62 @@
+#include "cache.h"
+#include "run-command.h"
+
+static void newtty(int *master, int *slave)
+{
+	int fd;
+	const char *term;
+
+	fd = posix_openpt(O_RDWR|O_NOCTTY);
+	if (fd == -1 || grantpt(fd) || unlockpt(fd) || !(term = ptsname(fd)))
+		die_errno("Could not allocate a new pseudo-terminal");
+	*master = fd;
+	*slave = open(term, O_RDWR|O_NOCTTY);
+	if (*slave == -1)
+		die_errno("Could not open pseudo-terminal: %s", term);
+}
+
+static void setup_child(struct child_process *chld,
+			const char **args, int out)
+{
+	memset(chld, 0, sizeof(*chld));
+	chld->argv = args;
+	chld->out = out;
+}
+
+static void xsendfile(int out_fd, int in_fd)
+{
+	char buf[BUFSIZ];
+
+	/* Note: the real sendfile() cannot read from a terminal. */
+	for (;;) {
+		ssize_t n = xread(in_fd, buf, sizeof(buf));
+
+		/*
+		 * It is unspecified by POSIX whether reads
+		 * from a disconnected terminal will return
+		 * EIO (as in AIX 4.x, IRIX, and Linux) or
+		 * end-of-file.  Either is fine.
+		 */
+		if (n == 0 || (n == -1 && errno == EIO))
+			break;
+		if (n == -1)
+			die_errno("cannot read from child");
+		write_or_die(out_fd, buf, n);
+	}
+}
+
+int main(int argc, const char **argv)
+{
+	int masterfd, slavefd;
+	struct child_process chld;
+
+	if (argc < 2 || (argc == 2 && !strcmp(argv[1], "-h")))
+		usage("test-terminal program args");
+
+	newtty(&masterfd, &slavefd);
+	setup_child(&chld, argv + 1, slavefd);
+	if (start_command(&chld))
+		return error("failed to execute child");
+	xsendfile(1, masterfd);
+	return finish_command(&chld);
+}
-- 
1.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]