]> granicus.if.org Git - check/commitdiff
Emit only valid XML characters
authorbrarcher <brarcher@64e312b2-a51f-0410-8e61-82d0ca0eb02a>
Mon, 14 Sep 2015 01:25:13 +0000 (01:25 +0000)
committerbrarcher <brarcher@64e312b2-a51f-0410-8e61-82d0ca0eb02a>
Mon, 14 Sep 2015 01:25:13 +0000 (01:25 +0000)
Previously output XML files could contain illegal characters.
Some of these characters could be encoded, and others should
not be output at all.

This change will attempt to encode characters if they are not
printable or are special. Note that if a test contains a UTF-8
charater it will not be printed out accurately.

In order to get the tests for encoding XML characters to work,
the characters separating fields in a shell variable needed to
be changed. Originally white space would separate fields,
meaning shell would eat the extra whitespace in the tests.
Because of the change, the xml output test needed to be
reworked to not rely on white space field separators.
Additionally, the printf program is used to properly un-escape
strings used in the tests.

git-svn-id: svn+ssh://svn.code.sf.net/p/check/code/trunk@1225 64e312b2-a51f-0410-8e61-82d0ca0eb02a

NEWS
src/check_print.c
tests/ex_output.c
tests/test_output_strings
tests/test_xml_output.sh

diff --git a/NEWS b/NEWS
index e69ac1559e5cc1d7011f36a436ef6696e71c7ef7..7eebca04d2c40ad0175b3b1171731fc3d5ebabb7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
 In Development:
 # Mentioning Check 0.10.0 for now, to fix distcheck target until next release
 
+* Emit only valid XML characters in XML logging (assumes ASCII encoding).
+  Bug #103
+
 * Add LGPL header to files where it was missing; update FSF address in LGPL headers
   Bug #110
 
index d56e17073548d94a11164c4aa04032e551fa6fdd..35f13b64aa7d30584b8e92bd31c520e907323ac9 100644 (file)
@@ -95,34 +95,53 @@ static void srunner_fprint_results(FILE * file, SRunner * sr,
 
 void fprint_xml_esc(FILE * file, const char *str)
 {
+    /* The valid XML characters are as follows:
+     *   #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+     * Characters that are outside of ASCII must be encoded. Further, the
+     * following special characters:
+     *   " ' < > &
+     * must be encoded. We assume that the incoming string may be a multibyte
+     * character.
+     */
+
     for(; *str != '\0'; str++)
     {
+        char next = *str;
 
-        switch (*str)
+        /* handle special characters that must be escaped */
+        if(next == '"' || next == '\'' || next == '<' || next == '>' || next == '&')
         {
-
-                /* handle special characters that must be escaped */
-            case '"':
-                fputs("&quot;", file);
-                break;
-            case '\'':
-                fputs("&apos;", file);
-                break;
-            case '<':
-                fputs("&lt;", file);
-                break;
-            case '>':
-                fputs("&gt;", file);
-                break;
-            case '&':
-                fputs("&amp;", file);
-                break;
-
-                /* regular characters, print as is */
-            default:
-                fputc(*str, file);
-                break;
+            switch (next)
+            {
+                case '"':
+                    fputs("&quot;", file);
+                    break;
+                case '\'':
+                    fputs("&apos;", file);
+                    break;
+                case '<':
+                    fputs("&lt;", file);
+                    break;
+                case '>':
+                    fputs("&gt;", file);
+                    break;
+                case '&':
+                    fputs("&amp;", file);
+                    break;
+            }
+        }
+        /* printable ASCII */
+        else if(next >= ' ' && next <= '~')
+        {
+            fputc(next, file);
+        }
+        /* Non-printable character */
+        else if(next == 0x9 || next == 0xA || next == 0xD ||
+                next >= 0x20)
+        {
+            fprintf(file, "&#x%X;", next);
         }
+        /* If it did not get printed, it is not a valid XML character*/
     }
 }
 
index 6d0aad270b86d6ed11ae81acff856be1723b43e8..9e4e7467b3706a3b0488af5ca620f44573b88a00 100644 (file)
@@ -75,7 +75,7 @@ END_TEST
 
 START_TEST(test_xml_esc_fail_msg)
 {
-    ck_abort_msg("fail \" ' < > & message");
+    ck_abort_msg("fail \" ' < > & \x9 \xA" "X""\x08"" message"); /* backspace char \x08 deletes the X */
 }
 END_TEST
 
@@ -120,8 +120,8 @@ static Suite *make_xml_esc_suite(void)
     Suite *s;
     TCase *tc;
 
-    s = suite_create("XML escape \" ' < > & tests");
-    tc = tcase_create("description \" ' < > &");
+    s = suite_create("XML escape \" ' < > & \x9 \xA" "X""\x08"" tests"); /* backspace char \x08 deletes the X */
+    tc = tcase_create("description \" ' < > & \x9 \xA" "X""\x08"" end"); /* backspace char \x08 deletes the X */
     suite_add_tcase(s, tc);
 
     tcase_add_test(tc, test_xml_esc_fail_msg);
index b23da03f69a0b6329ea6e7915551db1d5dfe576a..ccb3861faacd906d9cf988c899430ffaa9972a2a 100644 (file)
@@ -2,12 +2,17 @@
 
 . ./test_vars
 
+# Set the 'internal field separator' character to
+# something besides whitespace so that the string
+# comparisons will work
+IFS="~"
+
 ##################
 # stdout output
 ##################
-suite_output="Running suite(s): S1
+suite_output=`printf "Running suite(s): S1
  S2
- XML escape \" ' < > & tests"
+ XML escape \" ' < > & \x9 \x0AX\x08 tests"`
 
 exp_silent=""
 
@@ -20,25 +25,25 @@ exp_minimal="$suite_output
 $exp_minimal_result"
 
 if [ $HAVE_FORK -eq 1 ]; then
-exp_normal_result="37%: Checks: 8, Failures: 4, Errors: 1
+exp_normal_result=`printf "37%%: Checks: 8, Failures: 4, Errors: 1
 ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure
 ${SRCDIR}ex_output.c:46:E:Core:test_exit:0: (after this point) Early exit with return value 1
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed
-${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message"
+${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message"`
 else
-exp_normal_result="42%: Checks: 7, Failures: 4, Errors: 0
+exp_normal_result=`printf "42%%: Checks: 7, Failures: 4, Errors: 0
 ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed
-${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message"
+${SRCDIR}ex_output.c:78:F:description \" ' < > &\x9 \xA end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message"`
 fi
 exp_normal="$suite_output
 $exp_normal_result"
 
 
 if [ $HAVE_FORK -eq 1 ]; then
-exp_verbose_result="37%: Checks: 8, Failures: 4, Errors: 1
+exp_verbose_result=`printf "37%%: Checks: 8, Failures: 4, Errors: 1
 ${SRCDIR}ex_output.c:31:P:Core:test_pass:0: Passed
 ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure
 ${SRCDIR}ex_output.c:46:E:Core:test_exit:0: (after this point) Early exit with return value 1
@@ -46,22 +51,22 @@ ${SRCDIR}ex_output.c:66:P:Core:test_pass2:0: Passed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed
 ${SRCDIR}ex_output.c:72:P:Core:test_loop:1: Passed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed
-${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message"
+${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message"`
 else
-exp_verbose_result="42%: Checks: 7, Failures: 4, Errors: 0
+exp_verbose_result=`printf "42%%: Checks: 7, Failures: 4, Errors: 0
 ${SRCDIR}ex_output.c:31:P:Core:test_pass:0: Passed
 ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure
 ${SRCDIR}ex_output.c:66:P:Core:test_pass2:0: Passed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed
 ${SRCDIR}ex_output.c:72:P:Core:test_loop:1: Passed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed
-${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message"
+${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message"`
 fi
 exp_verbose="$suite_output
 $exp_verbose_result"
 
 if [ $HAVE_FORK -eq 1 ]; then
-exp_subunit="test: Core:test_pass
+exp_subunit=`printf "test: Core:test_pass
 success: Core:test_pass
 test: Core:test_fail
 failure: Core:test_fail [
@@ -83,12 +88,12 @@ test: Core:test_loop
 failure: Core:test_loop [
 ${SRCDIR}ex_output.c:72: Iteration 2 failed
 ]
-test: description \" ' < > &:test_xml_esc_fail_msg
-failure: description \" ' < > &:test_xml_esc_fail_msg [
-${SRCDIR}ex_output.c:78: fail \" ' < > & message
-]"
+test: description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg
+failure: description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg [
+${SRCDIR}ex_output.c:78: fail \" ' < > & \x9 \x0AX\x08 message
+]"`
 else
-exp_subunit="test: Core:test_pass
+exp_subunit=`printf "test: Core:test_pass
 success: Core:test_pass
 test: Core:test_fail
 failure: Core:test_fail [
@@ -106,17 +111,17 @@ test: Core:test_loop
 failure: Core:test_loop [
 ${SRCDIR}ex_output.c:72: Iteration 2 failed
 ]
-test: description \" ' < > &:test_xml_esc_fail_msg
-failure: description \" ' < > &:test_xml_esc_fail_msg [
-${SRCDIR}ex_output.c:78: fail \" ' < > & message
-]"
+test: description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg
+failure: description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg [
+${SRCDIR}ex_output.c:78: fail \" ' < > & \x9 \x0AX\x08 message
+]"`
 fi
 
 ##################
 # log output
 ##################
 if [ $HAVE_FORK -eq 1 ]; then
-expected_log_log="Running suite S1
+expected_log_log=`printf "Running suite S1
 ${SRCDIR}ex_output.c:31:P:Core:test_pass:0: Passed
 ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure
 ${SRCDIR}ex_output.c:46:E:Core:test_exit:0: (after this point) Early exit with return value 1
@@ -125,12 +130,12 @@ ${SRCDIR}ex_output.c:66:P:Core:test_pass2:0: Passed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed
 ${SRCDIR}ex_output.c:72:P:Core:test_loop:1: Passed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed
-Running suite XML escape \" ' < > & tests
-${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message
+Running suite XML escape \" ' < > & \x9 \x0AX\x08 tests
+${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message
 Results for all suites run:
-37%: Checks: 8, Failures: 4, Errors: 1"
+37%%: Checks: 8, Failures: 4, Errors: 1"`
 else
-expected_log_log="Running suite S1
+expected_log_log=`printf "Running suite S1
 ${SRCDIR}ex_output.c:31:P:Core:test_pass:0: Passed
 ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure
 Running suite S2
@@ -138,10 +143,10 @@ ${SRCDIR}ex_output.c:66:P:Core:test_pass2:0: Passed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed
 ${SRCDIR}ex_output.c:72:P:Core:test_loop:1: Passed
 ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed
-Running suite XML escape \" ' < > & tests
-${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message
+Running suite XML escape \" ' < > & \x9 \x0AX\x08 tests
+${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message
 Results for all suites run:
-42%: Checks: 7, Failures: 4, Errors: 0"
+42%%: Checks: 7, Failures: 4, Errors: 0"`
 fi
 
 ##################
@@ -207,19 +212,19 @@ expected_xml="<?xml version=\"1.0\"?>
     </test>
   </suite>
   <suite>
-    <title>XML escape &quot; &apos; &lt; &gt; &amp; tests</title>
+    <title>XML escape &quot; &apos; &lt; &gt; &amp; &#x9; &#xA;X tests</title>
     <test result=\"failure\">
       <fn>ex_output.c:78</fn>
       <id>test_xml_esc_fail_msg</id>
       <iteration>0</iteration>
-      <description>description &quot; &apos; &lt; &gt; &amp;</description>
-      <message>fail &quot; &apos; &lt; &gt; &amp; message</message>
+      <description>description &quot; &apos; &lt; &gt; &amp; &#x9; &#xA;X end</description>
+      <message>fail &quot; &apos; &lt; &gt; &amp; &#x9; &#xA;X message</message>
     </test>
   </suite>
 </testsuites>"
 expected_duration_count=9
 else
-expected_xml="<?xml version=\"1.0\"?>
+expected_xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
 <?xml-stylesheet type=\"text/xsl\" href=\"http://check.sourceforge.net/xml/check_unittest.xslt\"?>
 <testsuites xmlns=\"http://check.sourceforge.net/ns\">
   <suite>
@@ -271,13 +276,13 @@ expected_xml="<?xml version=\"1.0\"?>
     </test>
   </suite>
   <suite>
-    <title>XML escape &quot; &apos; &lt; &gt; &amp; tests</title>
+    <title>XML escape &quot; &apos; &lt; &gt; &amp; &#x9; &#xA;X tests</title>
     <test result=\"failure\">
       <fn>ex_output.c:78</fn>
       <id>test_xml_esc_fail_msg</id>
       <iteration>0</iteration>
-      <description>description &quot; &apos; &lt; &gt; &amp;</description>
-      <message>fail &quot; &apos; &lt; &gt; &amp; message</message>
+      <description>description &quot; &apos; &lt; &gt; &amp; &#x9; &#xA;X end</description>
+      <message>fail &quot; &apos; &lt; &gt; &amp; &#x9; &#xA;X message</message>
     </test>
   </suite>
 </testsuites>"
@@ -288,16 +293,16 @@ fi
 # tap output
 ##################
 if [ $HAVE_FORK -eq 1 ]; then
-expected_normal_tap="ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed
+expected_normal_tap=`printf "ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed
 not ok 2 - ${SRCDIR}ex_output.c:Core:test_fail: Failure
 not ok 3 - ${SRCDIR}ex_output.c:Core:test_exit: Early exit with return value 1
 ok 4 - ${SRCDIR}ex_output.c:Core:test_pass2: Passed
 not ok 5 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 0 failed
 ok 6 - ${SRCDIR}ex_output.c:Core:test_loop: Passed
 not ok 7 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 2 failed
-not ok 8 - ${SRCDIR}ex_output.c:description \" ' < > &:test_xml_esc_fail_msg: fail \" ' < > & message
-1..8"
-expected_aborted_tap="ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed
+not ok 8 - ${SRCDIR}ex_output.c:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg: fail \" ' < > & \x9 \x0AX\x08 message
+1..8"`
+expected_aborted_tap=`printf "ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed
 not ok 2 - ${SRCDIR}ex_output.c:Core:test_fail: Failure
 not ok 3 - ${SRCDIR}ex_output.c:Core:test_exit: Early exit with return value 1
 not ok 4 - ${SRCDIR}ex_output.c:Core:test_abort: Early exit with return value 1
@@ -305,17 +310,17 @@ ok 5 - ${SRCDIR}ex_output.c:Core:test_pass2: Passed
 not ok 6 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 0 failed
 ok 7 - ${SRCDIR}ex_output.c:Core:test_loop: Passed
 not ok 8 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 2 failed
-not ok 9 - ${SRCDIR}ex_output.c:description \" ' < > &:test_xml_esc_fail_msg: fail \" ' < > & message
-1..9"
+not ok 9 - ${SRCDIR}ex_output.c:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg: fail \" ' < > & \x9 \x0AX\x08 message
+1..9"`
 else
-expected_normal_tap="ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed
+expected_normal_tap=`printf "ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed
 not ok 2 - ${SRCDIR}ex_output.c:Core:test_fail: Failure
 ok 3 - ${SRCDIR}ex_output.c:Core:test_pass2: Passed
 not ok 4 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 0 failed
 ok 5 - ${SRCDIR}ex_output.c:Core:test_loop: Passed
 not ok 6 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 2 failed
-not ok 7 - ${SRCDIR}ex_output.c:description \" ' < > &:test_xml_esc_fail_msg: fail \" ' < > & message
-1..7"
+not ok 7 - ${SRCDIR}ex_output.c:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg: fail \" ' < > & \x9 \x0AX\x08 message
+1..7"`
 # When fork() is unavailable, one of the tests
 # will invoke exit() which will terminate the
 # unit testing program. In that case, the tap
index 7497f376057c60730b6e0949ba0b1391a802adf5..11bb77ad2e44444c4ddedaa1cf5bc9b5c9e0ae9a 100755 (executable)
@@ -25,12 +25,18 @@ if [ x"${expected_duration_count}" != x"${actual_duration_count}" ]; then
     exit 1;
 fi
 
-for duration in `grep "\<duration\>" ${OUTPUT_FILE} | cut -d ">" -f 2 | cut -d "<" -f 1`; do
-int_duration=`echo $duration | cut -d "." -f 1`
-if [ "${int_duration}" -ne "-1" ] && [ "${int_duration}" -gt "${CK_DEFAULT_TIMEOUT}" ]; then
-    echo "Problem with duration ${duration}; is not valid. Should be -1 or in [0, ${CK_DEFAULT_TIMEOUT}]"
-    exit 1
-fi
+num_durations=`grep "\<duration\>" ${OUTPUT_FILE} | wc -l`
+
+i=1
+while [ ${i} -le ${num_durations} ]; do
+   duration=`grep "\<duration\>" ${OUTPUT_FILE} | head -n ${i} | tail -n 1 | cut -d ">" -f 2 | cut -d "<" -f 1`
+   int_duration=`echo $duration | cut -d "." -f 1`
+   if [ "${int_duration}" -ne "-1" ] && [ "${int_duration}" -gt "${CK_DEFAULT_TIMEOUT}" ]; then
+       echo "Problem with duration ${duration}; is not valid. Should be -1 or in [0, ${CK_DEFAULT_TIMEOUT}]"
+       exit 1
+   fi
+
+   i=$((i+1))
 done