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