cmocka: Add Test Anything Protocol message output

This adds support to print test reporting in the Test Anything Protocol.
See http://testanything.org/tap-specification.html

Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
diff --git a/doc/mainpage.dox b/doc/mainpage.dox
index e747777..5d658e0 100644
--- a/doc/mainpage.dox
+++ b/doc/mainpage.dox
@@ -33,7 +33,7 @@
  - Very well tested
  - Testing of memory leaks, buffer overflows and underflows.
  - A set of assert macros.
- - Several supported output formats (stdout, jUnit XML, Subunit)
+ - Several supported output formats (stdout, TAP, xUnit XML, Subunit)
  - License: Apache License 2.0
 
 @section main-test A cmocka test
@@ -105,8 +105,9 @@
 done using the <tt>CMOCKA_MESSAGE_OUTPUT</tt> environment variable. The
 supported values are:
  - <tt>STDOUT</tt> for the default standard output printer
- - <tt>XML</tt> for jUnit XML format
  - <tt>SUBUNIT</tt> for subunit output
+ - <tt>TAP</tt> for Test Anything Protocol (TAP) output
+ - <tt>XML</tt> for xUnit XML format
 The case doesn't matter.
 
 The XML output goes to stderr by default. If the environment variable
diff --git a/include/cmocka.h b/include/cmocka.h
index 0459d43..85e264f 100644
--- a/include/cmocka.h
+++ b/include/cmocka.h
@@ -1955,6 +1955,7 @@
 enum cm_message_output {
     CM_OUTPUT_STDOUT,
     CM_OUTPUT_SUBUNIT,
+    CM_OUTPUT_TAP,
     CM_OUTPUT_XML,
 };
 
@@ -1964,7 +1965,7 @@
  * The ouput format for the test can either be set globally using this
  * function or overriden with environment variable CMOCKA_MESSAGE_OUTPUT.
  *
- * The environment variable can be set to either STDOUT or SUBUNIT.
+ * The environment variable can be set to either STDOUT, SUBUNIT, TAP or XML.
  *
  * @param[in] output    The output format to use for the test.
  *
diff --git a/src/cmocka.c b/src/cmocka.c
index d7da540..5323fde 100644
--- a/src/cmocka.c
+++ b/src/cmocka.c
@@ -1876,6 +1876,8 @@
             output = CM_OUTPUT_STDOUT;
         } else if (strcasecmp(env, "SUBUNIT") == 0) {
             output = CM_OUTPUT_SUBUNIT;
+        } else if (strcasecmp(env, "TAP") == 0) {
+            output = CM_OUTPUT_TAP;
         } else if (strcasecmp(env, "XML") == 0) {
             output = CM_OUTPUT_XML;
         }
@@ -2030,6 +2032,63 @@
     }
 }
 
+static void cmprintf_group_start_tap(const size_t num_tests)
+{
+    print_message("1..%u\n", (unsigned)num_tests);
+}
+
+static void cmprintf_tap(enum cm_printf_type type,
+                         uint32_t test_number,
+                         const char *test_name,
+                         const char *error_message)
+{
+    switch (type) {
+    case PRINTF_TEST_START:
+        break;
+    case PRINTF_TEST_SUCCESS:
+        print_message("ok %u - %s\n", (unsigned)test_number, test_name);
+        break;
+    case PRINTF_TEST_FAILURE:
+        print_message("not ok %u - %s\n", (unsigned)test_number, test_name);
+        if (error_message != NULL) {
+            char *msg;
+            char *p;
+
+            msg = strdup(error_message);
+            if (msg == NULL) {
+                return;
+            }
+            p = msg;
+
+            while (p[0] != '\0') {
+                char *q = p;
+
+                p = strchr(q, '\n');
+                if (p != NULL) {
+                    p[0] = '\0';
+                }
+
+                print_message("# %s\n", q);
+
+                if (p == NULL) {
+                    libc_free(msg);
+                    break;
+                }
+                p++;
+            }
+            libc_free(msg);
+        }
+        break;
+    case PRINTF_TEST_SKIPPED:
+        print_message("not ok %u # SKIP %s\n", (unsigned)test_number, test_name);
+        break;
+    case PRINTF_TEST_ERROR:
+        print_message("not ok %u - %s %s\n",
+                      (unsigned)test_number, test_name, error_message);
+        break;
+    }
+}
+
 static void cmprintf_subunit(enum cm_printf_type type,
                              const char *test_name,
                              const char *error_message)
@@ -2068,6 +2127,9 @@
         break;
     case CM_OUTPUT_SUBUNIT:
         break;
+    case CM_OUTPUT_TAP:
+        cmprintf_group_start_tap(num_tests);
+        break;
     case CM_OUTPUT_XML:
         break;
     }
@@ -2094,6 +2156,7 @@
                                     cm_tests);
         break;
     case CM_OUTPUT_SUBUNIT:
+    case CM_OUTPUT_TAP:
         break;
     case CM_OUTPUT_XML:
         cmprintf_group_finish_xml(group_name,
@@ -2107,6 +2170,7 @@
 }
 
 static void cmprintf(enum cm_printf_type type,
+                     size_t test_number,
                      const char *test_name,
                      const char *error_message)
 {
@@ -2121,6 +2185,9 @@
     case CM_OUTPUT_SUBUNIT:
         cmprintf_subunit(type, test_name, error_message);
         break;
+    case CM_OUTPUT_TAP:
+        cmprintf_tap(type, test_number, test_name, error_message);
+        break;
     case CM_OUTPUT_XML:
         break;
     }
@@ -2420,8 +2487,9 @@
         /* Execute tests */
         for (i = 0; i < num_tests; i++) {
             struct CMUnitTestState *cmtest = &cm_tests[i];
+            size_t test_number = i + 1;
 
-            cmprintf(PRINTF_TEST_START, cmtest->test->name, NULL);
+            cmprintf(PRINTF_TEST_START, test_number, cmtest->test->name, NULL);
 
             if (group_state != NULL) {
                 cm_tests[i].state = group_state;
@@ -2433,23 +2501,27 @@
                 switch (cmtest->status) {
                     case CM_TEST_PASSED:
                         cmprintf(PRINTF_TEST_SUCCESS,
+                                 test_number,
                                  cmtest->test->name,
                                  cmtest->error_message);
                         total_passed++;
                         break;
                     case CM_TEST_SKIPPED:
                         cmprintf(PRINTF_TEST_SKIPPED,
+                                 test_number,
                                  cmtest->test->name,
                                  cmtest->error_message);
                         break;
                     case CM_TEST_FAILED:
                         cmprintf(PRINTF_TEST_FAILURE,
+                                 test_number,
                                  cmtest->test->name,
                                  cmtest->error_message);
                         total_failed++;
                         break;
                     default:
                         cmprintf(PRINTF_TEST_ERROR,
+                                 test_number,
                                  cmtest->test->name,
                                  "Internal cmocka error");
                         total_errors++;
@@ -2457,13 +2529,14 @@
                 }
             } else {
                 cmprintf(PRINTF_TEST_ERROR,
+                         test_number,
                          cmtest->test->name,
                          "Could not run the test - check test fixtures");
                 total_errors++;
             }
         }
     } else {
-        cmprintf(PRINTF_TEST_ERROR,
+        cmprintf(PRINTF_TEST_ERROR, 0,
                  group_name, "Group setup failed");
         total_errors++;
     }