changeset 16831:f9fc08c5e5c2

Tests for module 'sh-quote'. * modules/sh-quote-tests: New file. * tests/test-sh-quote.c: New file.
author Bruno Haible <bruno@clisp.org>
date Mon, 07 May 2012 03:34:22 +0200
parents d137600880f6
children 82db61e229ef
files ChangeLog modules/sh-quote-tests tests/test-sh-quote.c
diffstat 3 files changed, 217 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2012-05-06  Bruno Haible  <bruno@clisp.org>
+
+	Tests for module 'sh-quote'.
+	* modules/sh-quote-tests: New file.
+	* tests/test-sh-quote.c: New file.
+
 2012-05-06  Bruno Haible  <bruno@clisp.org>
 
 	sh-quote: Improve shell_quote_argv's signature.
new file mode 100644
--- /dev/null
+++ b/modules/sh-quote-tests
@@ -0,0 +1,10 @@
+Files:
+tests/test-sh-quote.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-sh-quote
+check_PROGRAMS += test-sh-quote
new file mode 100644
--- /dev/null
+++ b/tests/test-sh-quote.c
@@ -0,0 +1,201 @@
+/* Test of sh-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 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, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2012.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sh-quote.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include "macros.h"
+
+static void
+check_one (const char *input, const char *expected)
+{
+  char buf[1000];
+  size_t output_len;
+  char *output;
+  char *bufend;
+
+  output_len = shell_quote_length (input);
+
+  output = shell_quote (input);
+  ASSERT (strlen (output) == output_len);
+
+  ASSERT (output_len <= sizeof (buf) - 2);
+  memset (buf, '\0', output_len + 1);
+  buf[output_len + 1] = '%';
+  bufend = shell_quote_copy (buf, input);
+  ASSERT (memcmp (buf, output, output_len + 1) == 0);
+  ASSERT (buf[output_len + 1] == '%');
+
+  ASSERT (strcmp (output, expected) == 0);
+
+  free (output);
+}
+
+int
+main (void)
+{
+  /* Check the shell_quote_length, shell_quote_copy, shell_quote functions.  */
+  {
+    int c;
+
+    /* Empty argument.  */
+    check_one ("", "''");
+
+    /* Identifier or number.  */
+    check_one ("foo", "foo");
+    check_one ("phr0ck", "phr0ck");
+
+    /* Whitespace would be interpreted as argument separator by the shell.  */
+    check_one ("foo\tbar", "'foo\tbar'");
+    check_one ("foo\nbar", "'foo\nbar'");
+    check_one ("foo bar", "'foo bar'");
+
+    /* '!' at the beginning of argv[0] would introduce a negated command.  */
+    check_one ("!foo", "'!foo'");
+
+    /* '"' would be interpreted as the start of a string.  */
+    check_one ("\"foo\"bar", "'\"foo\"bar'");
+
+    /* '#' at the beginning of an argument would be interpreted as the start
+       of a comment.  */
+    check_one ("#foo", "'#foo'");
+
+    /* '$' at the beginning of an argument would be interpreted as a variable
+       reference.  */
+    check_one ("$foo", "'$foo'");
+
+    /* '&' at the beginning of an argument would be interpreted as a background
+       task indicator.  */
+    check_one ("&", "'&'");
+
+    /* "'" would be interpreted as the start of a string.  */
+    check_one ("'foo'bar", "''\\''foo'\\''bar'"); /* or "\"'foo'bar\"" */
+
+    /* '(' at the beginning of argv[0] would introduce a subshell command.  */
+    check_one ("(", "'('");
+
+    /* ')' at the beginning of an argument would be interpreted as the end of
+       the command.  */
+    check_one (")", "')'");
+
+    /* '*' would be interpreted as a wildcard character.  */
+    check_one ("*", "'*'");
+    check_one ("*foo", "'*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 (";", "';'");
+    check_one ("foo;", "'foo;'");
+
+    /* '<' would be interpreted as a redirection of stdin.  */
+    check_one ("<", "'<'");
+
+    /* '=' inside argv[0] would be interpreted as an environment variable
+       assignment.  */
+    check_one ("foo=bar", "'foo=bar'");
+
+    /* '>' would be interpreted as a redirection of stdout.  */
+    check_one (">", "'>'");
+
+    /* '?' would be interpreted as a wildcard character.  */
+    check_one ("?", "'?'");
+    check_one ("foo?bar", "'foo?bar'");
+
+    /* '^' would be interpreted in old /bin/sh, e.g. SunOS 4.1.4.  */
+    check_one ("^", "'^'");
+
+    /* "[...]" would be interpreted as a wildcard pattern.  */
+    check_one ("[", "'['");
+    check_one ("]", "]"); /* or "']'" */
+
+    /* '\' would be interpreted as an escape character.  */
+    check_one ("\\foo", "'\\foo'");
+
+    /* '`' would be interpreted as the start of a command substitution.  */
+    check_one ("`foo", "'`foo'");
+
+    /* '{' at the beginning of argv[0] would introduce a complex command.  */
+    check_one ("{", "'{'");
+
+    /* '|' at the beginning of an argument would be interpreted as a pipe
+       between commands.  */
+    check_one ("|", "'|'");
+
+    /* '}' at the beginning of an argument would be interpreted as the end of
+       the command.  */
+    check_one ("}", "'}'");
+
+    /* '~' at the beginning of an argument would be interpreted as a reference
+       to a user's home directory.  */
+    check_one ("~", "'~'");
+    check_one ("~foo", "'~foo'");
+
+    /* A string that contains both ' and ".  */
+    check_one ("foo'bar\"baz", "'foo'\\''bar\"baz'"); /* or "\"foo'bar\\\"baz\"" */
+
+    /* 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 (s, s);
+        }
+  }
+
+  /* Check the shell_quote_argv function.  */
+  {
+    char *argv[1];
+    char *result;
+    argv[0] = NULL;
+    result = shell_quote_argv (argv);
+    ASSERT (strcmp (result, "") == 0);
+    free (result);
+  }
+  {
+    char *argv[2];
+    char *result;
+    argv[0] = (char *) "foo bar/baz";
+    argv[1] = NULL;
+    result = shell_quote_argv (argv);
+    ASSERT (strcmp (result, "'foo bar/baz'") == 0); /* or "\"foo bar/baz\"" */
+    free (result);
+  }
+  {
+    char *argv[3];
+    char *result;
+    argv[0] = (char *) "foo bar/baz";
+    argv[1] = (char *) "$";
+    argv[2] = NULL;
+    result = shell_quote_argv (argv);
+    ASSERT (strcmp (result, "'foo bar/baz' '$'") == 0); /* or "\"foo bar/baz\" \"\\$\"" */
+    free (result);
+  }
+
+  return 0;
+}