From: Bruno Haible Date: Wed, 9 May 2012 01:38:34 +0000 (+0200) Subject: Tests for module 'system-quote'. X-Git-Tag: v0.1~684 X-Git-Url: http://erislabs.net/gitweb/?p=gnulib.git;a=commitdiff_plain;h=9b3cba021116a2c273ba15564dbcd6fe5dc6ad2a Tests for module 'system-quote'. * modules/system-quote-tests: New file. * tests/test-system-quote.sh: New file. * tests/test-system-quote-main.c: New file. * tests/test-system-quote-child.c: New file. --- diff --git a/ChangeLog b/ChangeLog index 164bbe1ea..d329c3a13 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2012-05-08 Bruno Haible + Tests for module 'system-quote'. + * modules/system-quote-tests: New file. + * tests/test-system-quote.sh: New file. + * tests/test-system-quote-main.c: New file. + * tests/test-system-quote-child.c: New file. + New module 'system-quote'. * lib/system-quote.h: New file. * lib/system-quote.c: New file. diff --git a/modules/system-quote-tests b/modules/system-quote-tests new file mode 100644 index 000000000..7b6967bf7 --- /dev/null +++ b/modules/system-quote-tests @@ -0,0 +1,26 @@ +Status: +longrunning-test + +Files: +tests/test-system-quote.sh +tests/test-system-quote-main.c +tests/test-system-quote-child.c +tests/macros.h + +Depends-on: +stdbool +unistd +progname +popen +pclose + +configure.ac: + +Makefile.am: +TESTS += test-system-quote.sh +check_PROGRAMS += test-system-quote-main test-system-quote-child +test_system_quote_main_LDADD = $(LDADD) @LIBINTL@ +# The test-system-quote-child program must be a real executable, not a libtool +# wrapper script, and should link against as few libraries as possible. +# Therefore don't link it against any libraries other than -lc. +test_system_quote_child_LDADD = diff --git a/tests/test-system-quote-child.c b/tests/test-system-quote-child.c new file mode 100644 index 000000000..9b63de088 --- /dev/null +++ b/tests/test-system-quote-child.c @@ -0,0 +1,59 @@ +/* Child program invoked by test-system-quote-main. + Copyright (C) 2012 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 3, 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, see . */ + +#include + +#include +#include + +#define EXPECTED_DATA_FILE "t-sq-data.tmp" + +int +main (int argc, char *argv[]) +{ + const char *arg; + char expected_data[1000]; + size_t expected_data_len; + + if (argc < 2) + /* Expected one data argument, received none. */ + return 2; + if (argc > 2) + /* Expected one data argument, received more than one. */ + return 3; + arg = argv[1]; + + /* Read the contents of EXPECTED_DATA_FILE. */ + { + FILE *fp = fopen (EXPECTED_DATA_FILE, "rb"); + if (fp == NULL) + return 4; + expected_data_len = fread (expected_data, 1, sizeof (expected_data), fp); + if (fclose (fp)) + return 5; + } + + if (!(strlen (arg) == expected_data_len + && memcmp (arg, expected_data, expected_data_len) == 0)) + { + /* arg is not as expected. */ + fprintf (stderr, "expected: %.*s\nreceived: %s\n", + (int)expected_data_len, expected_data, arg); + return 1; + } + else + return 0; +} diff --git a/tests/test-system-quote-main.c b/tests/test-system-quote-main.c new file mode 100644 index 000000000..10fe82308 --- /dev/null +++ b/tests/test-system-quote-main.c @@ -0,0 +1,344 @@ +/* Test of system-quote module. + Copyright (C) 2012 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 3, 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, see . */ + +/* Written by Bruno Haible , 2012. */ + +#include + +/* Specification. */ +#include "system-quote.h" + +#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__ +# define WINDOWS_NATIVE +#endif + +#include +#include +#include +#include +#include +#include +#ifdef WINDOWS_NATIVE +# define WIN32_LEAN_AND_MEAN +# include +#endif + +#include "progname.h" +#include "macros.h" + +#define EXPECTED_DATA_FILE "t-sq-data.tmp" + +static int failed; + +static void +check_one (enum system_command_interpreter interpreter, const char *prog, + const char *input) +{ + char buf[1000]; + size_t output_len; + char *output; + char *bufend; + + output_len = system_quote_length (interpreter, input); + + output = system_quote (interpreter, input); + ASSERT (strlen (output) == output_len); + + ASSERT (output_len <= sizeof (buf) - 2); + memset (buf, '\0', output_len + 1); + buf[output_len + 1] = '%'; + bufend = system_quote_copy (buf, interpreter, input); + ASSERT (bufend == buf + output_len); + ASSERT (memcmp (buf, output, output_len + 1) == 0); + ASSERT (buf[output_len + 1] == '%'); + + /* Store INPUT in EXPECTED_DATA_FILE, for verification by the child + process. */ + { + FILE *fp = fopen (EXPECTED_DATA_FILE, "wb"); + if (fp == NULL) + exit (3); + if (fwrite (input, 1, strlen (input), fp) != strlen (input)) + exit (4); + if (fclose (fp)) + exit (5); + } + + /* Invoke the child process through system() and popen(). */ + { + char command[1000]; + + sprintf (command, "%s %s", prog, output); + + switch (interpreter) + { + case SCI_SYSTEM: +#ifdef WINDOWS_NATIVE + case SCI_WINDOWS_CMD: +#endif + { + int exitcode = system (command); + if (exitcode != 0) + { + fprintf (stderr, "for input = |%s|: system() command failed with status %d: %s\n", + input, exitcode, command); + failed = 1; + } + } + { + FILE *fp = popen (command, "r"); + int exitcode = pclose (fp); + if (exitcode != 0) + { + fprintf (stderr, "for input = |%s|: popen() command failed with status %d: %s\n", + input, exitcode, command); + failed = 1; + } + } + break; +#ifdef WINDOWS_NATIVE + case SCI_WINDOWS_CREATEPROCESS: + { + PROCESS_INFORMATION pinfo; + STARTUPINFO sinfo; + sinfo.cb = sizeof (STARTUPINFO); + sinfo.lpReserved = NULL; + sinfo.lpDesktop = NULL; + sinfo.lpTitle = NULL; + sinfo.cbReserved2 = 0; + sinfo.lpReserved2 = NULL; + sinfo.dwFlags = STARTF_USESTDHANDLES; + sinfo.hStdInput = GetStdHandle (STD_INPUT_HANDLE); + sinfo.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); + sinfo.hStdError = GetStdHandle (STD_ERROR_HANDLE); + + if (CreateProcess (NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, + &sinfo, &pinfo)) + { + DWORD exitcode; + CloseHandle (pinfo.hThread); + if (WaitForSingleObject (pinfo.hProcess, INFINITE) == WAIT_OBJECT_0) + { + if (GetExitCodeProcess (pinfo.hProcess, &exitcode)) + { + if (exitcode != 0) + { + fprintf (stderr, "for input = |%s|: CreateProcess() command failed with status %d: %s\n", + input, exitcode, command); + failed = 1; + } + } + else + { + fprintf (stderr, "for input = |%s|: GetExitCodeProcess failed, GetLastError() = %u\n", + input, GetLastError ()); + failed = 1; + } + } + else + { + fprintf (stderr, "for input = |%s|: WaitForSingleObject failed\n", + input); + failed = 1; + } + CloseHandle (pinfo.hProcess); + } + else + { + fprintf (stderr, "for input = |%s|: CreateProcess failed, GetLastError() = %u\n", + input, GetLastError ()); + failed = 1; + } + } + break; +#endif + default: + break; + } + } + + free (output); +} + +static void +check_all (enum system_command_interpreter interpreter, + bool windows_cmd_limitations, + const char *prog) +{ + /* Check the system_quote_length, system_quote_copy, system_quote + functions. */ + { + int c; + + /* Empty argument. */ + check_one (interpreter, prog, ""); + + /* Identifier or number. */ + check_one (interpreter, prog, "foo"); + check_one (interpreter, prog, "phr0ck"); + + /* Whitespace would be interpreted as argument separator by the shell. */ + check_one (interpreter, prog, "foo\tbar"); + if (!windows_cmd_limitations) + { + check_one (interpreter, prog, "foo\nbar"); + check_one (interpreter, prog, "foo\rbar"); + } + check_one (interpreter, prog, "foo bar"); + + /* '!' at the beginning of argv[0] would introduce a negated command. */ + check_one (interpreter, prog, "!foo"); + + /* '"' would be interpreted as the start of a string. */ + check_one (interpreter, prog, "\"foo\"bar"); + + /* '#' at the beginning of an argument would be interpreted as the start + of a comment. */ + check_one (interpreter, prog, "#foo"); + + /* '$' at the beginning of an argument would be interpreted as a variable + reference. */ + check_one (interpreter, prog, "$foo"); + + /* '&' at the beginning of an argument would be interpreted as a background + task indicator. */ + check_one (interpreter, prog, "&"); + + /* "'" would be interpreted as the start of a string. */ + check_one (interpreter, prog, "'foo'bar"); + + /* '(' at the beginning of argv[0] would introduce a subshell command. */ + check_one (interpreter, prog, "("); + + /* ')' at the beginning of an argument would be interpreted as the end of + the command. */ + check_one (interpreter, prog, ")"); + + /* '*' would be interpreted as a wildcard character. */ + check_one (interpreter, prog, "*"); + check_one (interpreter, prog, "*foo"); + + /* ';' at the beginning of an argument would be interpreted as an empty + statement in argv[0] and as the end of the command otherwise. */ + check_one (interpreter, prog, ";"); + check_one (interpreter, prog, "foo;"); + + /* '<' would be interpreted as a redirection of stdin. */ + check_one (interpreter, prog, "<"); + + /* '=' inside argv[0] would be interpreted as an environment variable + assignment. */ + check_one (interpreter, prog, "foo=bar"); + + /* '>' would be interpreted as a redirection of stdout. */ + check_one (interpreter, prog, ">"); + + /* '?' would be interpreted as a wildcard character. */ + check_one (interpreter, prog, "?"); + check_one (interpreter, prog, "foo?bar"); + + /* '^' would be interpreted in old /bin/sh, e.g. SunOS 4.1.4. */ + check_one (interpreter, prog, "^"); + + /* "[...]" would be interpreted as a wildcard pattern. */ + check_one (interpreter, prog, "["); + check_one (interpreter, prog, "]"); + + /* '\' would be interpreted as an escape character. */ + check_one (interpreter, prog, "\\foo"); + + /* '`' would be interpreted as the start of a command substitution. */ + check_one (interpreter, prog, "`foo"); + + /* '{' at the beginning of argv[0] would introduce a complex command. */ + check_one (interpreter, prog, "{"); + + /* '|' at the beginning of an argument would be interpreted as a pipe + between commands. */ + check_one (interpreter, prog, "|"); + + /* '}' at the beginning of an argument would be interpreted as the end of + the command. */ + check_one (interpreter, prog, "}"); + + /* '~' at the beginning of an argument would be interpreted as a reference + to a user's home directory. */ + check_one (interpreter, prog, "~"); + check_one (interpreter, prog, "~foo"); + + /* A string that contains both ' and ". */ + check_one (interpreter, prog, "foo'bar\"baz"); + + /* '%' is used for environment variable references in Windows cmd.exe. */ + check_one (interpreter, prog, "%"); + check_one (interpreter, prog, "%%"); + check_one (interpreter, prog, "%foo%"); + check_one (interpreter, prog, "%PATH%"); + + /* All other characters don't need quoting. */ + for (c = 1; c <= UCHAR_MAX; c++) + if (strchr ("\t\n\r !\"#$&'()*;<=>?^[\\]`{|}~", c) == NULL) + { + char s[5]; + s[0] = 'a'; + s[1] = (char) c; + s[2] = 'z'; + s[3] = (char) c; + s[4] = '\0'; + + check_one (interpreter, prog, s); + } + } +} + +int +main (int argc, char *argv[]) +{ + char *prog; + + set_program_name (argv[0]); + + if (argc != 2) + { + fprintf (stderr, "%s: need 1 argument\n", argv[0]); + return 2; + } + prog = argv[1]; + +#ifdef WINDOWS_NATIVE + /* Make PROG suitable for native Windows system calls and cmd.exe: + Replace '/' with '\\'. */ + { + char *p; + for (p = prog; *p != '\0'; p++) + if (*p == '/') + *p = '\\'; + } +#endif + +#ifdef WINDOWS_NATIVE + check_all (SCI_SYSTEM, true, prog); /* equivalent to SCI_WINDOWS_CMD */ + check_all (SCI_WINDOWS_CREATEPROCESS, false, prog); + check_all (SCI_WINDOWS_CMD, true, prog); +#else + check_all (SCI_SYSTEM, false, prog); /* equivalent to SCI_POSIX_SH */ +#endif + + /* Clean up. */ + unlink (EXPECTED_DATA_FILE); + + return failed; +} diff --git a/tests/test-system-quote.sh b/tests/test-system-quote.sh new file mode 100755 index 000000000..d5202865a --- /dev/null +++ b/tests/test-system-quote.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./test-system-quote-main${EXEEXT} ./test-system-quote-child${EXEEXT}