Commit Diff


commit - 68c450f8c58e81bc478c2ed12bc6b5b4170af4cd
commit + d6e96558852535185d7b78a859b770a7a4fe8823
blob - a2e34dfcea0eddc4ca1a490c5cbf4a533c44b487
blob + b3369c380f2cb9d8f913c3d7ccb099fc2ca362d6
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -1,6 +1,22 @@
 cmake_minimum_required (VERSION 3.9.2)
 project(testres)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -W -Wall -Wextra -Wfloat-equal")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wundef -Wpointer-arith -Wcast-align -Wshadow")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-overflow=5 -Wwrite-strings -Waggregate-return")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch-enum -Wunreachable-code -Winit-self")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter -Werror -pedantic")
+
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O3 -fsanitize=address")
+set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O0 -g")
+
+include(CTest)
+
+if(BUILD_TESTING)
+        enable_testing()
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function -Wno-unused-variable")
+        set(TESTING_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Testing")
+endif()
+
+add_subdirectory(libtestoutput)
 add_subdirectory(src)
-add_subdirectory(tests)
-enable_testing()
-add_test(NAME unit COMMAND runtest)
blob - /dev/null
blob + fbf66df99ecb39aceaeff386c1862daefee6e535 (mode 644)
--- /dev/null
+++ libtestoutput/CMakeLists.txt
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.9.2)
+
+set(SOURCE_FILES
+parse_common.h
+parse_common.c
+parse_subunit_v1.h
+parse_subunit_v1.c
+parse_subunit_v2.h
+parse_subunit_v2.c
+parse_junit.h
+parse_junit.c
+parse_subunit_v2.c
+parse_testanything.h
+parse_testanything.c
+sha1.h
+sha1.c
+)
+
+include(FindEXPAT)
+find_package(EXPAT REQUIRED)
+
+include_directories(${EXPAT_INCLUDE_DIRS})
+add_library(testoutput ${SOURCE_FILES})
+
+if(BUILD_TESTING)
+	add_subdirectory(tests)
+endif()
blob - /dev/null
blob + 173ffe007ce19f3787b19b22e14b092a67ca7295 (mode 644)
--- /dev/null
+++ libtestoutput/parse_common.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright © 2018-2019 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <dirent.h>
+#include <stdlib.h>
+
+#include "parse_common.h"
+#include "parse_junit.h"
+#include "parse_subunit_v1.h"
+#include "parse_subunit_v2.h"
+#include "parse_testanything.h"
+#include "sha1.h"
+
+void
+free_reports(struct reportq * reports)
+{
+	tailq_report *report_item = NULL;
+	while ((report_item = TAILQ_FIRST(reports))) {
+		if (report_item->suites != NULL) {
+			free_suites(report_item->suites);
+		}
+		TAILQ_REMOVE(reports, report_item, entries);
+		free_report(report_item);
+	}
+}
+
+void 
+free_report(tailq_report *report)
+{
+	if (report->suites != NULL) {
+		free_suites(report->suites);
+	}
+	free(report->path);
+	free(report->id);
+	free(report);
+}
+
+void 
+free_suites(struct suiteq * suites)
+{
+	tailq_suite *suite_item = NULL;
+	while ((suite_item = TAILQ_FIRST(suites))) {
+		TAILQ_REMOVE(suites, suite_item, entries);
+		free_suite(suite_item);
+	}
+}
+
+void 
+free_suite(tailq_suite * suite)
+{
+	if (suite->name) {
+	   free((char*)suite->name);
+        }
+	if (suite->hostname) {
+	   free((char*)suite->hostname);
+        }
+	if (suite->timestamp) {
+	   free((char*)suite->timestamp);
+        }
+	if (!TAILQ_EMPTY(suite->tests)) {
+		free_tests(suite->tests);
+	}
+
+	free(suite);
+}
+
+void 
+free_tests(struct testq * tests)
+{
+	tailq_test *test_item;
+	while ((test_item = TAILQ_FIRST(tests))) {
+		TAILQ_REMOVE(tests, test_item, entries);
+		free_test(test_item);
+	}
+}
+
+void 
+free_test(tailq_test * test)
+{
+	if (test->name) {
+	   free((char*)test->name);
+        }
+	if (test->time) {
+	   free((char*)test->time);
+        }
+	if (test->comment) {
+	   free((char*)test->comment);
+        }
+	if (test->error) {
+	   free((char*)test->error);
+        }
+	if (test->system_out) {
+	   free((char*)test->system_out);
+        }
+	if (test->system_err) {
+	   free((char*)test->system_err);
+        }
+	free(test);
+}
+
+char *
+get_filename_ext(const char *filename)
+{
+	char *dot = strrchr(filename, '.');
+	if (!dot || dot == filename)
+	   return (char *) NULL;
+
+	return dot + 1;
+}
+
+enum test_format 
+detect_format(char *path)
+{
+	char *file_ext;
+	file_ext = get_filename_ext(basename(path));
+	if (file_ext == NULL) {
+	   return FORMAT_UNKNOWN;
+	}
+
+	if (strcasecmp("xml", file_ext) == 0) {
+		return FORMAT_JUNIT;
+	} else if (strcasecmp("tap", file_ext) == 0) {
+		return FORMAT_TAP13;
+	} else if (strcasecmp("subunit", file_ext) == 0) {
+		if (is_subunit_v2(path) == 0) {
+		   return FORMAT_SUBUNIT_V2;
+		} else {
+		   return FORMAT_SUBUNIT_V1;
+		}
+	} else {
+		return FORMAT_UNKNOWN;
+	}
+}
+
+unsigned char *digest_to_str(unsigned char *str, unsigned char digest[], unsigned int n) {
+	int r;
+	if (n == 0) return 0;
+	if (n == 1) r = sprintf((char*)str, "%x", digest[0]);
+	else        r = sprintf((char*)str, "%x", digest[0]);
+	digest_to_str(str + r, digest + 1, n - 1);
+
+	return str;
+}
+
+tailq_report *
+process_file(char *path)
+{
+	FILE *file;
+	file = fopen(path, "r");
+	if (file == NULL) {
+		printf("failed to open file %s\n", path);
+		return NULL;
+	}
+	tailq_report *report = NULL;
+	report = calloc(1, sizeof(tailq_report));
+	if (report == NULL) {
+		perror("malloc failed");
+		fclose(file);
+		return NULL;
+	}
+	enum test_format format;
+	format = detect_format(path);
+	switch (format) {
+	case FORMAT_JUNIT:
+		report->format = FORMAT_JUNIT;
+		report->suites = parse_junit(file);
+		break;
+	case FORMAT_TAP13:
+		report->format = FORMAT_TAP13;
+		report->suites = parse_testanything(file);
+		break;
+	case FORMAT_SUBUNIT_V1:
+		report->format = FORMAT_SUBUNIT_V1;
+		report->suites = parse_subunit_v1(file);
+		break;
+	case FORMAT_SUBUNIT_V2:
+		report->format = FORMAT_SUBUNIT_V2;
+		report->suites = parse_subunit_v2(file);
+		break;
+	case FORMAT_UNKNOWN:
+		report->format = FORMAT_UNKNOWN;
+		return report;
+	}
+	fclose(file);
+
+	report->path = (unsigned char*)strdup(path);
+
+	int length = 20;
+	unsigned char digest[length];
+	SHA1_CTX ctx;
+	SHA1Init(&ctx);
+	SHA1Update(&ctx, report->path, strlen(path));
+	SHA1Final(digest, &ctx);
+
+	report->id = calloc(length, sizeof(unsigned char*));
+	digest_to_str(report->id, digest, length);
+
+	struct stat sb;
+	if (stat(path, &sb) == -1) {
+		perror("cannot open specified path");
+		return NULL;
+	}
+	report->time = sb.st_mtime;
+
+	return report;
+}
+
+struct tailq_report *is_report_exists(struct reportq *reports, const char* report_id) {
+
+	tailq_report *report_item = NULL;
+	TAILQ_FOREACH(report_item, reports, entries) {
+	    if (strcmp(report_id, (char*)report_item->id) == 0) {
+		break;
+	    }
+	}
+
+	return report_item;
+}
+
+/*
+static int cmp_date(const void *p1, const void *p2) {
+   return strcmp(* (char * const *) p1, * (char * const *) p2);
+}
+
+struct reportq *sort_reports(struct reportq *reports) {
+   return reports;
+}
+*/
+
+int num_by_status_class(struct tailq_report *report, enum test_status_class c) {
+
+   int number = 0;
+   if (report->suites != NULL) {
+      tailq_suite *suite_item = NULL;
+      TAILQ_FOREACH(suite_item, report->suites, entries) {
+         if (!TAILQ_EMPTY(suite_item->tests)) {
+            tailq_test *test_item = NULL;
+            TAILQ_FOREACH(test_item, suite_item->tests, entries) {
+               if (class_by_status(test_item->status) == c) number++;
+            }
+         }
+      }
+   }
+
+   return number;
+}
+
+enum test_status_class class_by_status(enum test_status status) {
+
+   switch (status) {
+   case STATUS_OK:
+     return STATUS_CLASS_PASS;
+   case STATUS_PASS:
+     return STATUS_CLASS_PASS;
+   case STATUS_SUCCESS:
+     return STATUS_CLASS_PASS;
+   case STATUS_NOTOK:
+     return STATUS_CLASS_FAIL;
+   case STATUS_ERROR:
+     return STATUS_CLASS_FAIL;
+   case STATUS_FAILURE:
+     return STATUS_CLASS_FAIL;
+   case STATUS_FAILED:
+     return STATUS_CLASS_FAIL;
+   case STATUS_XFAILURE:
+     return STATUS_CLASS_FAIL;
+   case STATUS_UXSUCCESS:
+     return STATUS_CLASS_FAIL;
+   case STATUS_MISSING:
+     return STATUS_CLASS_SKIP;
+   case STATUS_TODO:
+     return STATUS_CLASS_SKIP;
+   case STATUS_SKIP:
+     return STATUS_CLASS_SKIP;
+   case STATUS_SKIPPED:
+     return STATUS_CLASS_SKIP;
+   case STATUS_UNDEFINED:
+     return STATUS_CLASS_SKIP;
+   case STATUS_ENUMERATION:
+     return STATUS_CLASS_SKIP;
+   case STATUS_INPROGRESS:
+     return STATUS_CLASS_SKIP;
+   default:
+     return STATUS_CLASS_SKIP;
+   }
+}
+
+struct reportq*
+process_dir(char *path) {
+
+	DIR *d;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if ((d = fdopendir(fd)) == NULL) {
+		printf("failed to open dir %s\n", path);
+		close(fd);
+		return NULL;
+	}
+
+	struct reportq *reports;
+	reports = calloc(1, sizeof(struct reportq));
+	if (reports == NULL) {
+	   return NULL;
+	}
+	TAILQ_INIT(reports);
+
+	struct dirent *dir;
+	char *path_file = (char *) NULL;
+	tailq_report *report_item;
+	while ((dir = readdir(d)) != NULL) {
+		char *basename;
+		basename = dir->d_name;
+		if ((strcmp("..", basename) == 0) || (strcmp(".", basename) == 0)) {
+		   continue;
+		}
+		/* TODO: recursive search in directories */
+		int path_len = strlen(path) + strlen(basename) + 2;
+		path_file = calloc(path_len, sizeof(char));
+		if (path_file == NULL) {
+			return NULL;
+		}
+		snprintf(path_file, path_len, "%s/%s", path, basename);
+
+		struct stat path_st;
+		if (stat(path, &path_st) == -1) {
+		   perror("cannot open specified path");
+		   return NULL;
+		}
+		if (S_ISREG(path_st.st_mode)) {
+		   continue;
+		}
+		report_item = process_file(path_file);
+		if (report_item->format != FORMAT_UNKNOWN) {
+		   TAILQ_INSERT_TAIL(reports, report_item, entries);
+        }
+		free(path_file);
+	}
+	close(fd);
+	closedir(d);
+
+	return reports;
+}
+
+struct reportq*
+process_db(char *path)
+{
+	/* not implemented */
+	return NULL;
+}
+
+int
+check_sqlite(char *path)
+{
+	/* not implemented */
+	/* see https://www.sqlite.org/fileformat.html */
+	return 1;
+}
+
+struct reportq *filter_reports(struct reportq *reports, const char *qsearch) {
+
+	if (qsearch == NULL || reports == NULL) {
+		return NULL;
+	}
+
+	struct reportq *filtered;
+	filtered = calloc(1, sizeof(struct reportq));
+	if (filtered == NULL) {
+		return NULL;
+	}
+	TAILQ_INIT(filtered);
+
+	tailq_report *report_item = NULL;
+	TAILQ_FOREACH(report_item, reports, entries) {
+		if (report_item->suites != NULL) {
+			tailq_suite *suite_item = NULL;
+			int matched = 0;
+			TAILQ_FOREACH(suite_item, report_item->suites, entries) {
+				if (matched == 1) {
+					break;
+				}
+				if (!TAILQ_EMPTY(suite_item->tests)) {
+					tailq_test *test_item = NULL;
+					TAILQ_FOREACH(test_item, suite_item->tests, entries) {
+						if (strstr(test_item->name, qsearch) != NULL) {
+							TAILQ_INSERT_TAIL(filtered, report_item, entries);
+							matched = 1;
+							break;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return reports;
+}
blob - /dev/null
blob + 869dde5a9a1065c5df51305ce12b4fd0ca0d612b (mode 644)
--- /dev/null
+++ libtestoutput/parse_common.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef PARSE_COMMON_H
+#define PARSE_COMMON_H
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+enum test_format {
+	FORMAT_UNKNOWN,
+	FORMAT_TAP13,
+	FORMAT_JUNIT,
+	FORMAT_SUBUNIT_V1,
+	FORMAT_SUBUNIT_V2
+};
+
+enum test_status {
+	STATUS_OK,			/* TestAnythingProtocol	*/
+	STATUS_NOTOK,		/* TestAnythingProtocol	*/
+	STATUS_MISSING,		/* TestAnythingProtocol	*/
+	STATUS_TODO,		/* TestAnythingProtocol	*/
+	STATUS_SKIP,		/* TestAnythingProtocol	*/
+
+	STATUS_UNDEFINED,	/* Subunit */
+	STATUS_ENUMERATION,	/* Subunit */
+	STATUS_INPROGRESS,	/* Subunit */
+	STATUS_SUCCESS,		/* Subunit */
+	STATUS_UXSUCCESS,	/* Subunit */
+	STATUS_SKIPPED,		/* Subunit */
+	STATUS_FAILED,		/* Subunit */
+	STATUS_XFAILURE,	/* Subunit */
+
+	STATUS_ERROR,		/* JUnit */
+	STATUS_FAILURE,		/* JUnit */
+	STATUS_PASS			/* JUnit */
+};
+
+enum test_status_class {
+	STATUS_CLASS_PASS,
+	STATUS_CLASS_FAIL,
+	STATUS_CLASS_SKIP
+};
+
+struct tailq_test {
+    const char *name;
+    const char *time;
+    const char *comment;
+    const char *error;
+    const char *system_out;
+    const char *system_err;
+    enum test_status status;
+    TAILQ_ENTRY(tailq_test) entries;
+};
+
+TAILQ_HEAD(testq, tailq_test);
+
+struct tailq_suite {
+    const char *name;
+    const char *hostname;
+    const char *timestamp;
+    int n_failures;
+    int n_errors;
+    double time;
+    struct testq *tests;
+    TAILQ_ENTRY(tailq_suite) entries;
+};
+
+TAILQ_HEAD(suiteq, tailq_suite);
+
+struct tailq_report {
+    enum test_format format;
+    struct suiteq *suites;
+    time_t time;
+    unsigned char *id;
+    unsigned char *path;
+    TAILQ_ENTRY(tailq_report) entries;
+};
+
+TAILQ_HEAD(reportq, tailq_report);
+
+typedef struct tailq_test tailq_test;
+typedef struct tailq_suite tailq_suite;
+typedef struct tailq_report tailq_report;
+
+/* cleanup */
+void free_reports(struct reportq *reports);
+void free_suites(struct suiteq *suites);
+void free_tests(struct testq *tests);
+
+void free_report(tailq_report * report);
+void free_suite(tailq_suite * suite);
+void free_test(tailq_test * test);
+
+
+char *get_filename_ext(const char *filename);
+enum test_format detect_format(char *path);
+int check_sqlite(char *path);
+struct reportq *process_db(char *path);
+struct reportq *process_dir(char *path);
+tailq_report *process_file(char *path);
+tailq_test *make_test(char *name, char *time, char *comment);
+unsigned char *digest_to_str(unsigned char *str, unsigned char digest[], unsigned int n);
+struct tailq_report *is_report_exists(struct reportq *reports, const char* report_id);
+
+/*
+static int cmp_date(const void *p1, const void *p2);
+struct reportq *sort_reports(struct reportq *reports);
+*/
+
+int num_by_status_class(struct tailq_report *report, enum test_status_class c);
+enum test_status_class class_by_status(enum test_status status);
+struct reportq *filter_reports(struct reportq *reports, const char *qsearch);
+
+#endif				/* PARSE_COMMON_H */
blob - /dev/null
blob + 42eb77426f9c77c82a6a034f977e2c22673f7782 (mode 644)
--- /dev/null
+++ libtestoutput/parse_junit.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "parse_junit.h"
+
+#ifdef XML_LARGE_SIZE
+#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400
+#define XML_FMT_INT_MOD "I64"
+#else
+#define XML_FMT_INT_MOD "ll"
+#endif
+#else
+#define XML_FMT_INT_MOD "l"
+#endif
+
+#ifdef XML_UNICODE_WCHAR_T
+#define XML_FMT_STR "ls"
+#else
+#define XML_FMT_STR "s"
+#endif
+
+#define BUFFSIZE        8192
+
+/* https://github.com/kristapsdz/divecmd/blob/master/parser.c */
+
+char buf[BUFFSIZE];
+
+tailq_test *test_item;
+tailq_suite *suite_item;
+struct suiteq *suites;
+
+int system_out_flag = 0;
+int system_err_flag = 0;
+int error_flag = 0;
+
+const XML_Char *
+name_to_value(const XML_Char ** attr, const char attr_name[])
+{
+	XML_Char *attr_value = NULL;
+	int i;
+	for (i = 0; attr[i]; i += 2) {
+		if (strcmp(attr[i], attr_name) == 0) {
+			attr_value = calloc(strlen(attr[i + 1]) + 1, sizeof(XML_Char));
+			if (attr_value == NULL) {
+				perror("malloc failed");
+				return (char *) NULL;
+			}
+			strcpy(attr_value, attr[i + 1]);
+			break;
+		}
+	}
+	return attr_value;
+}
+
+static void XMLCALL
+start_handler(void *data, const XML_Char * elem, const XML_Char ** attr)
+{
+	(void) data;
+	if (strcmp(elem, "testsuite") == 0) {
+		suite_item = calloc(1, sizeof(tailq_suite));
+		if (suite_item == NULL) {
+			perror("malloc failed");
+		}
+		suite_item->name = name_to_value(attr, "name");
+		suite_item->hostname = name_to_value(attr, "hostname");
+		suite_item->n_errors = atoi(name_to_value(attr, "errors"));
+		suite_item->n_failures = atoi(name_to_value(attr, "failures"));
+		suite_item->time = atof(name_to_value(attr, "time"));
+		suite_item->timestamp = name_to_value(attr, "timestamp");
+		suite_item->tests = calloc(1, sizeof(struct testq));
+		if (suite_item->tests == NULL) {
+			perror("malloc failed");
+		}
+		TAILQ_INIT(suite_item->tests);
+	} else if (strcmp(elem, "testcase") == 0) {
+		test_item = calloc(1, sizeof(tailq_test));
+		if (test_item == NULL) {
+			perror("malloc failed");
+		};
+		test_item->name = name_to_value(attr, "name");
+		test_item->time = name_to_value(attr, "time");
+		test_item->status = STATUS_PASS;
+	} else if (strcmp(elem, "error") == 0) {
+		error_flag = 1;
+		test_item->status = STATUS_ERROR;
+		test_item->comment = name_to_value(attr, "comment");
+	} else if (strcmp(elem, "failure") == 0) {
+		test_item->status = STATUS_FAILURE;
+		test_item->comment = name_to_value(attr, "comment");
+	} else if (strcmp(elem, "skipped") == 0) {
+		test_item->status = STATUS_SKIPPED;
+		test_item->comment = name_to_value(attr, "comment");
+	} else if (strcmp(elem, "system-out") == 0) {
+		system_out_flag = 1;
+	} else if (strcmp(elem, "system-err") == 0) {
+		system_err_flag = 1;
+	}
+}
+
+static void XMLCALL
+end_handler(void *data, const XML_Char * elem)
+{
+	(void) data;
+	(void) elem;
+
+	if (strcmp(elem, "testsuite") == 0) {
+		/* TODO: check a number of failures and errors */
+		TAILQ_INSERT_TAIL(suites, suite_item, entries);
+	} else if (strcmp(elem, "testcase") == 0) {
+		TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
+	} else if (strcmp(elem, "error") == 0) {
+		error_flag = 0;
+	} else if (strcmp(elem, "system-out") == 0) {
+		system_out_flag = 0;
+	} else if (strcmp(elem, "system-err") == 0) {
+		system_err_flag = 0;
+	}
+}
+
+void
+data_handler(void *data, const char *txt, int txtlen) {
+  (void)data;
+  
+  if (error_flag == 1) {
+     /* TODO */
+     test_item->error = (char*)NULL;
+  };
+  if (system_out_flag == 1) {
+     /* TODO */
+     test_item->system_out = (char*)NULL;
+  };
+  if (system_err_flag == 1) {
+     /* TODO */
+     test_item->system_err = (char*)NULL;
+  };
+}
+
+struct suiteq *
+parse_junit(FILE * f)
+{
+	XML_Parser p = XML_ParserCreate(NULL);
+	if (!p) {
+		fprintf(stderr, "Couldn't allocate memory for parser\n");
+		return NULL;
+	}
+	suites = calloc(1, sizeof(struct suiteq));
+	if (suites == NULL) {
+		perror("malloc failed");
+	}
+	TAILQ_INIT(suites);
+
+	XML_UseParserAsHandlerArg(p);
+	XML_SetElementHandler(p, start_handler, end_handler);
+	XML_SetCharacterDataHandler(p, data_handler);
+
+	for (;;) {
+		int len, done;
+		len = fread(buf, 1, BUFFSIZE, f);
+		if (ferror(f)) {
+			fprintf(stderr, "Read error\n");
+			exit(-1);
+		}
+		done = feof(f);
+
+		if (XML_Parse(p, buf, len, done) == XML_STATUS_ERROR) {
+			fprintf(stderr,
+			    "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n",
+			    XML_GetCurrentLineNumber(p),
+			    XML_ErrorString(XML_GetErrorCode(p)));
+			free(test_item);
+			free(suite_item);
+			free_suites(suites);
+			exit(-1);
+		}
+		if (done) {
+			break;
+		}
+	}
+	XML_ParserFree(p);
+
+	return suites;
+}
blob - /dev/null
blob + afe24652dfd93b0aeb5b1d7b1e9dfa0d0336489c (mode 644)
--- /dev/null
+++ libtestoutput/parse_junit.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef PARSE_JUNIT_H
+#define PARSE_JUNIT_H
+
+#include <expat.h>
+
+#include "parse_common.h"
+
+struct suiteq *parse_junit(FILE *f);
+
+#endif				/* PARSE_JUNIT_H */
blob - /dev/null
blob + bceeb32256adc179553703fb69365c11573cb15c (mode 644)
--- /dev/null
+++ libtestoutput/parse_subunit_v1.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include "parse_subunit_v1.h"
+
+const char *
+directive_string(enum directive dir) {
+
+	switch (dir) {
+	case DIR_TEST:
+		return "DIR_TEST";
+	case DIR_SUCCESS:
+		return "DIR_SUCCESS";
+	case DIR_FAILURE:
+		return "DIR_FAILURE";
+	case DIR_ERROR:
+		return "DIR_ERROR";
+	case DIR_SKIP:
+		return "DIR_SKIP";
+	case DIR_XFAIL:
+		return "DIR_XFAIL";
+	case DIR_UXSUCCESS:
+		return "DIR_UXSUCCESS";
+	case DIR_PROGRESS:
+		return "DIR_PROGRESS";
+	case DIR_TAGS:
+		return "DIR_TAGS";
+	case DIR_TIME:
+		return "DIR_TIME";
+	default:
+		return "DIR_UNKNOWN";
+	}
+}
+
+enum directive
+resolve_directive(char * string) {
+
+	assert(string != (char*)NULL);
+
+	if ((strcasecmp(string, "test") == 0) ||
+	    (strcasecmp(string, "testing") == 0) ||
+	    (strcasecmp(string, "test:") == 0) ||
+	    (strcasecmp(string, "testing:") == 0)) {
+		return DIR_TEST;
+	} else if ((strcasecmp(string, "success") == 0) ||
+		   (strcasecmp(string, "success:") == 0) ||
+		   (strcasecmp(string, "successful") == 0) ||
+		   (strcasecmp(string, "successful:") == 0)) {
+		return DIR_SUCCESS;
+	} else if (strcasecmp(string, "failure:") == 0) {
+		return DIR_FAILURE;
+	} else if (strcasecmp(string, "error:") == 0) {
+		return DIR_ERROR;
+	} else if ((strcasecmp(string, "skip") == 0) ||
+		   (strcasecmp(string, "skip:") == 0)) {
+		return DIR_SKIP;
+	} else if ((strcasecmp(string, "xfail") == 0) ||
+		   (strcasecmp(string, "xfail:") == 0)) {
+		return DIR_XFAIL;
+	} else if ((strcasecmp(string, "uxsuccess") == 0) ||
+		   (strcasecmp(string, "uxsuccess:") == 0)) {
+		return DIR_UXSUCCESS;
+	} else if (strcasecmp(string, "progress:") == 0) {
+		return DIR_PROGRESS;
+	} else if (strcasecmp(string, "tags:") == 0) {
+		return DIR_TAGS;
+	} else if (strcasecmp(string, "time:") == 0) {
+		return DIR_TIME;
+	} else {
+		/* unknown directive */
+	}
+
+	return DIR_TEST;
+}
+
+struct tm* parse_iso8601_time(char* date_str, char* time_str) {
+	assert(date_str != (char*)NULL);
+	assert(time_str != (char*)NULL);
+
+	struct tm * t;
+	t = malloc(sizeof(struct tm));
+	if (t == NULL) {
+		perror("failed to malloc");
+		return NULL;
+	}
+	if (sscanf(date_str, "%d-%d-%d", &t->tm_year, &t->tm_mon, &t->tm_mday) == 3) {
+		assert(t->tm_year > 2000);
+		assert((t->tm_mon <= 12) && (t->tm_mon >= 0));
+		assert((t->tm_mday <= 31) && (t->tm_mday >= 0));
+	}
+
+	if (sscanf(time_str, "%d:%d:%dZ", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3) {
+		assert((t->tm_hour <= 23) && (t->tm_hour >= 0));
+		assert((t->tm_min <= 60) && (t->tm_min >= 0));
+		assert((t->tm_sec <= 60) && (t->tm_sec >= 0));
+	}
+
+	return t;
+}
+
+void read_tok() {
+	char* token = (char*)NULL;
+	while (token != NULL) { token = strtok(NULL, " \t"); };
+}
+
+tailq_test* read_test() {
+
+	tailq_test *test_item = NULL;
+	test_item = calloc(1, sizeof(tailq_test));
+	if (test_item == NULL) {
+		perror("failed to malloc");
+		return NULL;
+	}
+
+	char *token, *name;
+	token = strtok(NULL, " \t");
+	if (strcmp(token, "test") == 0) {
+	   token = strtok(NULL, " \t");
+	   assert(token != NULL);
+	}
+	name = (char*)calloc(strlen(token) + 1, sizeof(char));
+	strcpy(name, token);
+	test_item->name = name;
+
+	read_tok();
+
+	return test_item;
+}
+
+tailq_test* parse_line_subunit_v1(char* string) {
+
+	assert(string != (char*)NULL);
+
+	char *dir;
+	char buffer[1024];
+	strcpy(buffer, string);
+	dir = strtok(buffer, " \t");
+
+	tailq_test *test_item = NULL;
+	enum directive d;
+	switch (d = resolve_directive(dir)) {
+	case DIR_TEST:
+		/* testline is useless, but we should check conformance to spec */
+		read_tok();
+		break;
+	case DIR_SUCCESS:
+		test_item = read_test();
+		test_item->status = STATUS_SUCCESS;
+		break;
+	case DIR_FAILURE:
+		test_item = read_test();
+		test_item->status = STATUS_FAILURE;
+		break;
+	case DIR_ERROR:
+		test_item = read_test();
+		test_item->status = STATUS_FAILED;
+		break;
+	case DIR_SKIP:
+		test_item = read_test();
+		test_item->status = STATUS_SKIPPED;
+		break;
+	case DIR_XFAIL:
+		test_item = read_test();
+		test_item->status = STATUS_XFAILURE;
+		break;
+	case DIR_UXSUCCESS:
+		test_item = read_test();
+		test_item->status = STATUS_UXSUCCESS;
+		break;
+	case DIR_PROGRESS:
+		/* testline is useless, but we should check conformance to spec */
+		read_tok();
+		break;
+	case DIR_TAGS:
+		/* testline is useless, but we should check conformance to spec */
+		read_tok();
+		break;
+	case DIR_TIME:
+		/* testline is useless, but we should check conformance to spec */
+		/*
+		char *date = strtok(NULL, " \t");
+		assert(date != (char*)NULL);
+		char *time = strtok(NULL, " \t");
+		assert(time != (char*)NULL);
+		struct tm *t = parse_iso8601_time(date, time);
+		printf("Time: %s\n", asctime(t));
+		*/
+
+		read_tok();
+		break;
+	default:
+		read_tok();
+		return NULL;
+	}
+
+	return test_item;
+}
+
+struct suiteq* parse_subunit_v1(FILE *stream) {
+
+	tailq_suite *suite_item;
+	suite_item = calloc(1, sizeof(tailq_suite));
+	if (suite_item == NULL) {
+		perror("malloc failed");
+		return NULL;
+	}
+	/* TODO: n_errors, n_failures */
+	suite_item->tests = calloc(1, sizeof(struct testq));
+	if (suite_item->tests == NULL) {
+		perror("malloc failed");
+		free(suite_item);
+		return NULL;
+	};
+	TAILQ_INIT(suite_item->tests);
+
+    	char line[1024];
+	tailq_test *test_item = NULL;
+    	while (fgets(line, sizeof(line), stream)) {
+		test_item = parse_line_subunit_v1(line);
+		if (test_item != NULL) {
+		   TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
+		}
+		if (feof(stream)) {
+			break;
+		}
+    	}
+
+	struct suiteq *suites = NULL;
+	suites = calloc(1, sizeof(struct suiteq));
+	if (suites == NULL) {
+		perror("malloc failed");
+	};
+	TAILQ_INIT(suites);
+	TAILQ_INSERT_TAIL(suites, suite_item, entries);
+
+	return suites;
+}
blob - /dev/null
blob + 48343a4ba6c96a3cee80604105d629445af8fa21 (mode 644)
--- /dev/null
+++ libtestoutput/parse_subunit_v1.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef PARSE_SUBUNIT_V1_H
+#define PARSE_SUBUNIT_V1_H
+
+#include "parse_common.h"
+
+enum directive {
+	DIR_TEST,
+	DIR_SUCCESS,
+	DIR_FAILURE,
+	DIR_ERROR,
+	DIR_SKIP,
+	DIR_XFAIL,
+	DIR_UXSUCCESS,
+	DIR_PROGRESS,
+	DIR_TAGS,
+	DIR_TIME
+};
+
+tailq_test* parse_line_subunit_v1(char* string);
+struct suiteq* parse_subunit_v1(FILE* stream);
+struct tm* parse_iso8601_time(char* date_str, char* time_str);
+enum directive resolve_directive(char* string);
+const char* directive_string(enum directive dir);
+void read_tok();
+tailq_test* read_test();
+
+#endif				/* PARSE_SUBUNIT_V1_H */
blob - /dev/null
blob + 36b2df8bee0459bc4ddb56d66a00278479cfd188 (mode 644)
--- /dev/null
+++ libtestoutput/parse_subunit_v2.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <zlib.h>
+
+#include "parse_subunit_v2.h"
+
+// https://github.com/testing-cabal/subunit/blob/master/python/subunit/v2.py#L412
+// https://github.com/testing-cabal/subunit
+
+#define HI(x)  ((x) >> 8)
+#define LO(x)  ((x) & 0xFF)
+
+int is_subunit_v2(char* path)
+{
+	FILE *file;
+	file = fopen(path, "r");
+	if (file == NULL) {
+		printf("failed to open file %s\n", path);
+		return -1;
+	}
+
+	uint8_t signature = 0;
+	int n_bytes = 0;
+	n_bytes = fread(&signature, 1, 1, file);
+	fclose(file);
+	if (n_bytes == 0) {
+		return -1;
+	}
+	if (signature == SUBUNIT_SIGNATURE) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+uint32_t read_field(FILE * stream)
+{
+
+	uint32_t field_value = 0;
+	uint8_t byte = 0, byte0 = 0;
+	uint16_t buf = 0;
+	uint8_t prefix = 0;
+
+	int n_bytes = 0;
+
+	n_bytes = fread(&byte, 1, 1, stream);
+	if (n_bytes == 0) {
+		return 0;
+	}
+	prefix = byte >> 6;
+	byte0 = byte & 0x3f;
+	if (prefix == 0x00) {
+		field_value = byte0;
+	} else if (prefix == 0x40) {
+		n_bytes = fread(&byte, 1, 1, stream);
+		if (n_bytes == 0) {
+			return 0;
+		}
+		field_value = (byte0 << 8) | byte;
+	} else if (prefix == 0x80) {
+		n_bytes = fread(&buf, 2, 1, stream);
+		if (n_bytes == 0) {
+			return 0;
+		}
+		field_value = (byte << 16) | buf;
+	} else {
+		n_bytes = fread(&buf, 1, 2, stream);
+		if (n_bytes == 0) {
+			return 0;
+		}
+		field_value = (byte0 << 24) | buf << 8;
+
+		n_bytes = fread(&byte, 1, 1, stream);
+		if (n_bytes == 0) {
+			return 0;
+		}
+		field_value = field_value | byte;
+	};
+
+	return field_value;
+}
+
+struct suiteq *
+parse_subunit_v2(FILE * stream)
+{
+	tailq_suite *suite_item;
+	suite_item = calloc(1, sizeof(tailq_suite));
+	if (suite_item == NULL) {
+		perror("malloc failed");
+		return NULL;
+	}
+
+	suite_item->tests = calloc(1, sizeof(struct testq));
+	if (suite_item->tests == NULL) {
+		perror("malloc failed");
+		free_suite(suite_item);
+		return NULL;
+	}
+
+	TAILQ_INIT(suite_item->tests);
+	tailq_test *test_item = NULL;
+
+	test_item = read_subunit_v2_packet(stream);
+	if (test_item != NULL)
+		TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
+
+	/*
+	while (!feof(stream)) {
+		test_item = read_packet(stream);
+		if (test_item != NULL)
+			TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
+		else
+		{
+			free_tests(suite_item->tests);
+			free_suite(suite_item);
+			return NULL;
+		}
+	}
+	*/
+
+	struct suiteq *suites = NULL;
+	suites = calloc(1, sizeof(struct suiteq));
+	if (suites == NULL) {
+		perror("malloc failed");
+		free_suite(suite_item);
+	}
+	TAILQ_INIT(suites);
+	TAILQ_INSERT_TAIL(suites, suite_item, entries);
+
+	return suites;
+}
+
+tailq_test *
+read_subunit_v2_packet(FILE * stream)
+{
+	subunit_header header;
+	int n_bytes = 0;
+	n_bytes = fread(&header, sizeof(subunit_header), 1, stream);
+	if ((n_bytes == 0) || (n_bytes < (int)sizeof(subunit_header))) {
+		return NULL;
+	}
+	tailq_test *test_item;
+	test_item = calloc(1, sizeof(tailq_test));
+	if (test_item == NULL) {
+		perror("malloc failed");
+		return NULL;
+	}
+
+	uint16_t flags = htons(header.flags);
+	printf("SIGNATURE: %02hhX\n", header.signature);
+	printf("FLAGS: %02hX\n", flags);
+	assert(header.signature == SUBUNIT_SIGNATURE);
+
+	int8_t version;
+	version = HI(flags) >> 4;
+	printf("\tVERSION: %d\n", version);
+	assert(version == SUBUNIT_VERSION);
+
+	/*
+	int8_t status;
+	status = flags & 0x0007;
+	printf("\tSTATUS: %d\n", status);
+	test_item->status = status;
+	assert(status <= 0x0007);
+
+	uint32_t field_value;
+	field_value = read_field(stream);
+	printf("TOTAL LENGTH: %u\n", field_value);
+	assert(field_value < PACKET_MAX_LENGTH);
+
+	if (flags & FLAG_TIMESTAMP) {
+		printf("FLAG_TIMESTAMP ");
+		field_value = read_field(stream);
+		printf("%08X\n", field_value);
+	};
+	if (flags & FLAG_TEST_ID) {
+		printf("FLAG_TEST_ID ");
+		field_value = read_field(stream);
+		printf("%08X\n", field_value);
+	};
+	if (flags & FLAG_TAGS) {
+		printf("FLAG_TAGS ");
+		field_value = read_field(stream);
+		printf("%02X\n", field_value);
+	};
+	if (flags & FLAG_MIME_TYPE) {
+		printf("FLAG_MIME_TYPE ");
+		field_value = read_field(stream);
+		printf("%02X\n", field_value);
+	};
+	if (flags & FLAG_FILE_CONTENT) {
+		printf("FLAG_FILE_CONTENT ");
+		field_value = read_field(stream);
+		printf("%08X\n", field_value);
+	};
+	if (flags & FLAG_ROUTE_CODE) {
+		printf("FLAG_ROUTE_CODE ");
+		field_value = read_field(stream);
+		printf("%08X\n", field_value);
+	};
+	if (flags & FLAG_EOF) {
+		printf("FLAG_EOF\n");
+	};
+	if (flags & FLAG_RUNNABLE) {
+		printf("FLAG_RUNNABLE\n");
+	};
+	printf("CRC32: ");
+	field_value = read_field(stream);
+	printf("%08X\n", field_value);
+	*/
+
+	return test_item;
+}
+
+
+/*
+
+CRC32
+const char *s = "0xb30x2901b329010c03666f6f";
+printf("%lX, should be %X\n", crc32(0, (const void*)s, strlen(s)), sample_crc32);
+http://bxr.su/OpenBSD/bin/md5/crc.c
+
+https://rosettacode.org/wiki/CRC-32#C
+http://csbruce.com/software/crc32.c
+
+Parse timestamp
+int y, M, d, h, m;
+float sec;
+char *dateStr = "2014-11-12T19:12:14.505Z";
+sscanf(dateStr, "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &sec);
+https://github.com/mlabbe/c_date_parse
+
+UTF-8
+https://github.com/benkasminbullock/unicode-c/blob/master/unicode.c
+https://github.com/clibs/cutef8
+
+*/
blob - /dev/null
blob + dab7dd56d19d153daff3ed9ed2453733fa616559 (mode 644)
--- /dev/null
+++ libtestoutput/parse_subunit_v2.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef PARSE_SUBUNIT_V2_H
+#define PARSE_SUBUNIT_V2_H
+
+#include <stdint.h>
+
+#include "parse_common.h"
+
+#define SUBUNIT_SIGNATURE 	0xB3
+#define SUBUNIT_VERSION 	0x02
+#define PACKET_MAX_LENGTH 	4194303
+
+#define FLAG_TEST_ID		0x0800
+#define FLAG_ROUTE_CODE		0x0400
+#define FLAG_TIMESTAMP		0x0200
+#define FLAG_RUNNABLE		0x0100
+#define FLAG_TAGS		0x0080
+#define FLAG_MIME_TYPE		0x0020
+#define FLAG_EOF		0x0010
+#define FLAG_FILE_CONTENT	0x0040
+
+struct subunit_header {
+    uint8_t  signature;
+    uint16_t flags;
+} __attribute__ ((packed));
+
+typedef struct subunit_header subunit_header;
+
+typedef uint32_t timestamp;
+
+uint32_t read_field(FILE *stream);
+tailq_test *read_subunit_v2_packet(FILE *stream);
+struct suiteq *parse_subunit_v2(FILE *stream);
+int is_subunit_v2(char* path);
+
+#endif				/* PARSE_SUBUNIT_V2_H */
blob - /dev/null
blob + b00ba88cb5f99aba4c095d5b51e2a9adc60f6831 (mode 644)
--- /dev/null
+++ libtestoutput/parse_testanything.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright © 2015-2017 Katherine Flavel <kate@elide.org>
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <string.h>
+#include <strings.h>
+
+#include "parse_common.h"
+#include "parse_testanything.h"
+
+const char *
+ast_status(enum ast_status status)
+{
+	switch (status) {
+		case AST_OK:return "ok";
+	case AST_NOTOK:
+		return "not ok";
+	case AST_MISSING:
+		return "missing";
+	case AST_TODO:
+		return "todo";
+	case AST_SKIP:
+		return "skip";
+
+	default:
+		return "?";
+	}
+}
+
+enum test_status 
+test_status(enum ast_status status)
+{
+	switch (status) {
+		case AST_OK:return STATUS_OK;
+	case AST_NOTOK:
+		return STATUS_NOTOK;
+	case AST_MISSING:
+		return STATUS_MISSING;
+	case AST_TODO:
+		return STATUS_TODO;
+	case AST_SKIP:
+		return STATUS_SKIP;
+	default:
+		return STATUS_MISSING;
+	}
+}
+
+struct ast_line *
+ast_line(struct ast_line ** head, const char *text)
+{
+	struct ast_line **tail, *new;
+	size_t z;
+
+	assert(head != NULL);
+	assert(text != NULL);
+
+	z = strlen(text);
+
+	new = malloc(sizeof *new + z + 1);
+	if (new == NULL) {
+		return NULL;
+	}
+	new->text = strcpy((char *) new + sizeof *new, text);
+
+	for (tail = head; *tail != NULL; tail = &(*tail)->next);
+
+	new->next = *tail;
+	*tail = new;
+
+	return new;
+}
+
+struct ast_test *
+ast_test(struct ast_test ** head, enum ast_status status, const char *name)
+{
+	struct ast_test **tail, *new;
+
+	assert(head != NULL);
+
+	new = malloc(sizeof *new +
+	    (name == NULL ? 0 : strlen(name) + 1));
+	if (new == NULL) {
+		return NULL;
+	}
+	if (name == NULL) {
+		new->name = NULL;
+	} else {
+		new->name = strcpy((char *) new + sizeof *new, name);
+	}
+
+	new->rep = 1;
+	new->line = NULL;
+	new->status = status;
+
+	for (tail = head; *tail != NULL; tail = &(*tail)->next);
+
+	new->next = *tail;
+	*tail = new;
+
+	return new;
+}
+
+static void
+rtrim(char *s)
+{
+	char *p;
+
+	assert(s != NULL);
+
+	if (*s == '\0') {
+		return;
+	}
+	p = s + strlen(s) - 1;
+
+	assert(strlen(s) > 0);
+
+	while (p >= s && isspace((unsigned char) *p)) {
+		*p-- = '\0';
+	}
+}
+
+static void
+plan(const char *line, int *a, int *b)
+{
+	assert(line != NULL);
+	assert(a != NULL);
+	assert(b != NULL);
+
+	if (*b != -1) {
+		fprintf(stderr, "syntax error: duplicate plan: %s\n", line);
+		exit(1);
+	}
+	if (2 != sscanf(line, "%d..%d", a, b)) {
+		fprintf(stderr, "syntax error: missing a..b\n");
+		exit(1);
+	}
+	if (*a < 0 && *b < *a) {
+		fprintf(stderr, "error: invalid plan: %d..%d\n", *a, *b);
+		exit(1);
+	}
+}
+
+static void
+yaml(struct ast_test * test, const char *line)
+{
+	assert(test != NULL);
+
+	while (test->next != NULL)
+		test = test->next;
+
+	if (!ast_line(&test->line, line)) {
+		perror("ast_line");
+		exit(1);
+	}
+}
+
+static void
+gap(struct ast_test ** head, int *a, int b)
+{
+	struct ast_test *new;
+
+	assert(head != NULL);
+	assert(a != NULL);
+	assert(*a <= b);
+
+	while (*a < b) {
+		new = ast_test(head, AST_MISSING, NULL);
+		if (new == NULL) {
+			perror("ast_test");
+			exit(1);
+		}
+		*a += 1;
+	}
+}
+
+static void
+starttest(struct ast_test ** head, const char *line, int *a, int b)
+{
+	struct ast_test *new;
+	enum ast_status status;
+	int i;
+	int n;
+
+	assert(a != NULL);
+	assert(head != NULL);
+	assert(line != NULL);
+	assert(b == -1 || *a <= b);
+
+	if (0 == strncmp(line, "not ", 4)) {
+		line += 4;
+		status = AST_NOTOK;
+	} else {
+		status = AST_OK;
+	}
+
+	if (1 != sscanf(line, "ok %d - %n", &i, &n)) {
+		fprintf(stderr, "syntax error: expected 'ok - ': %s\n", line);
+		exit(1);
+	}
+	line += n;
+
+	if (i < *a || (b != -1 && i > b)) {
+		fprintf(stderr, "error: test %d out of order; expected %d\n", i, *a);
+		exit(1);
+	}
+	gap(head, a, i);
+
+	new = ast_test(head, status, line);
+	if (new == NULL) {
+		perror("ast_test");
+		exit(1);
+	}
+	*a += 1;
+}
+
+void
+print(FILE * f, const struct ast_test * tests)
+{
+	const struct ast_test *test;
+	const struct ast_line *line;
+	unsigned int n;
+
+	assert(f != NULL);
+	assert(tests != NULL);
+
+	for (test = tests, n = 1; test != NULL; test = test->next, n++) {
+		fprintf(f, "\t<test status='%s'",
+		    ast_status(test->status));
+
+		fprintf(f, " n='%u'", n);
+
+		if (test->rep > 1) {
+			fprintf(f, " rep='%u'", test->rep);
+		}
+		if (test->name != NULL) {
+			fprintf(f, " name='");
+			fprintf(f, "%s", test->name);
+			fprintf(f, "'");
+		}
+		fprintf(f, "%s>\n", test->line != NULL ? "" : "/");
+
+		if (test->line == NULL) {
+			continue;
+		}
+		for (line = test->line; line != NULL; line = line->next) {
+			fprintf(f, "%s%s",
+			    line->text,
+			    line->next != NULL ? "\n" : "");
+		}
+	}
+}
+
+struct ast_test *
+parse_testanything_raw(FILE * f)
+{
+	struct ast_test *tests = NULL;
+	int a, b;
+	int fold = 0;
+
+	{
+		char *line, *comment;
+		size_t n;
+
+		line = NULL;
+		a = 1;
+		b = -1;		/* no plan */
+		n = 0;
+
+		while (-1 != getline(&line, &n, f)) {
+			line[strcspn(line, "\n")] = '\0';
+
+			comment = strchr(line, '#');
+			if (comment != NULL) {
+				*comment++ = '\0';
+			}
+			rtrim(line);
+
+			if (comment != NULL) {
+				comment += strspn(comment, " \t");
+
+				/* TODO: only if we're actually in a test */
+
+				if (0 == strncasecmp(comment, "todo", 4)) {
+					tests->status = AST_TODO;
+				}
+				if (0 == strncasecmp(comment, "skip", 4)) {
+					tests->status = AST_SKIP;
+				}
+				/* TODO: add comment line anyway */
+				/*
+				 * TODO: add as yaml line printf("\t<!-- %s
+				 * -->\n", comment);
+				 */
+			}
+			switch (line[0]) {
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				plan(line, &a, &b);
+				continue;
+
+			case 'n':
+			case 'o':
+				starttest(&tests, line, &a, b);
+				continue;
+
+			case '\v':
+			case '\t':
+			case '\f':
+			case '\r':
+			case '\n':
+			case ' ':
+				if (tests == NULL) {
+					fprintf(stderr, "stray text: %s\n", line);
+					continue;
+				}
+				yaml(tests, line);
+				continue;
+			}
+		}
+
+		gap(&tests, &a, b + 1);
+	}
+
+	if (fold) {
+		struct ast_test *test, *next;
+
+		for (test = tests; test != NULL; test = next) {
+			next = test->next;
+			if (next == NULL) {
+				continue;
+			}
+			if (0 != strcmp(test->name, next->name)) {
+				continue;
+			}
+			if (test->status != next->status) {
+				continue;
+			}
+			if (test->line != NULL || next->line != NULL) {
+				continue;
+			}
+			test->rep++;
+			test->next = next->next;
+			next = test;
+
+			/* TODO: free next */
+		}
+	}
+	/* TODO: warn about duplicate test names */
+	/* TODO: remove ast_test * tests here */
+
+	/* print(stdout, tests); */
+	return tests;
+}
+
+struct suiteq *
+parse_testanything(FILE * f)
+{
+	tailq_suite *suite_item = NULL;
+	suite_item = calloc(1, sizeof(tailq_suite));
+	if (suite_item == NULL) {
+		perror("malloc failed");
+	}
+	suite_item->n_errors = 0;
+	suite_item->n_failures = 0;
+	suite_item->tests = calloc(1, sizeof(struct testq));
+	if (suite_item->tests == NULL) {
+		perror("malloc failed");
+		free(suite_item);
+	}
+	TAILQ_INIT(suite_item->tests);
+
+	struct ast_test *tests, *current;
+	tests = parse_testanything_raw(f);
+	tailq_test *test_item;
+	current = tests;
+	while (current != NULL) {
+		test_item = calloc(1, sizeof(tailq_test));
+		if (test_item == NULL) {
+			perror("malloc failed");
+			free_tests(suite_item->tests);
+			free(suite_item);
+			return NULL;
+		}
+		char *name = calloc(strlen(current->name) + 1, sizeof(char));
+		if (name == NULL) {
+			perror("malloc failed");
+			free_tests(suite_item->tests);
+			free(suite_item);
+			free(test_item);
+			return NULL;
+		}
+		test_item->name = strcpy(name, current->name);
+		test_item->status = test_status(current->status);
+		TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
+		current = current->next;
+		/* TODO: remove ast_test item */
+	}
+
+	struct suiteq *suites;
+	suites = calloc(1, sizeof(struct suiteq));
+	if (suites == NULL) {
+		free_tests(suite_item->tests);
+		free(suite_item);
+		perror("malloc failed");
+		return NULL;
+	}
+	TAILQ_INIT(suites);
+	TAILQ_INSERT_TAIL(suites, suite_item, entries);
+
+	return suites;
+}
blob - /dev/null
blob + be3329125d21b053e7d6935519d45bb48347f934 (mode 644)
--- /dev/null
+++ libtestoutput/parse_testanything.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2015-2017 Katherine Flavel <kate@elide.org>
+ * Copyright © 2018 Sergey Bronnikov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef PARSE_TESTANYTHING_H
+#define PARSE_TESTANYTHING_H
+
+enum ast_status {
+	AST_OK,
+	AST_NOTOK,
+	AST_MISSING,
+	AST_TODO,
+	AST_SKIP
+};
+
+struct ast_line {
+	/* TODO: comment flag */
+	const char *text;
+	struct ast_line *next;
+};
+
+struct ast_test {
+	const char *name;
+	enum ast_status status;
+	unsigned int rep;
+	struct ast_line *line;
+	struct ast_test *next;
+};
+
+const char *
+ast_status(enum ast_status status);
+
+struct ast_line *
+ast_line(struct ast_line **head, const char *text);
+
+struct ast_test *
+ast_test(struct ast_test **head, enum ast_status status, const char *name);
+
+void
+print(FILE *f, const struct ast_test *tests);
+
+struct ast_test *parse_testanything_raw(FILE *f);
+struct suiteq *parse_testanything(FILE *f);
+
+#endif				/* PARSE_TESTANYTHING_H */
blob - /dev/null
blob + cc52dcf96354ebde280ad8a5f144a841070257ff (mode 644)
--- /dev/null
+++ libtestoutput/sha1.c
@@ -0,0 +1,201 @@
+/* from valgrind tests */
+
+/* ================ sha1.c ================ */
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#if defined __x86_64__
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#define SHA1HANDSOFF
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <sys/param.h>
+
+#include "sha1.h"
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#elif BYTE_ORDER == BIG_ENDIAN
+#define blk0(i) block->l[i]
+#else
+#error "Endianness not defined!"
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+    uint32_t a, b, c, d, e;
+    typedef union {
+        unsigned char c[64];
+        uint32_t l[16];
+    } CHAR64LONG16;
+#ifdef SHA1HANDSOFF
+    CHAR64LONG16 block[1];  /* use array to appear as a pointer */
+    memcpy(block, buffer, 64);
+#else
+    /* The following had better never be used because it causes the
+     * pointer-to-const buffer to be cast into a pointer to non-const.
+     * And the result is written through.  I threw a "const" in, hoping
+     * this will cause a diagnostic.
+     */
+    CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+    memset(block, '\0', sizeof(block));
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
+{
+    uint32_t i, j;
+
+    j = context->count[0];
+    if ((context->count[0] += len << 3) < j)
+        context->count[1]++;
+    context->count[1] += (len>>29);
+    j = (j >> 3) & 63;
+    if ((j + len) > 63) {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1Transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+    unsigned i;
+    unsigned char finalcount[8];
+    unsigned char c;
+
+#if 0	/* untested "improvement" by DHR */
+    /* Convert context->count to a sequence of bytes
+     * in finalcount.  Second element first, but
+     * big-endian order within element.
+     * But we do it all backwards.
+     */
+    unsigned char *fcp = &finalcount[8];
+
+    for (i = 0; i < 2; i++)
+       {
+        uint32_t t = context->count[i];
+        int j;
+
+        for (j = 0; j < 4; t >>= 8, j++)
+	          *--fcp = (unsigned char) t;
+    }
+#else
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+#endif
+    c = 0200;
+    SHA1Update(context, &c, 1);
+    while ((context->count[0] & 504) != 448) {
+	c = 0000;
+        SHA1Update(context, &c, 1);
+    }
+    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+    for (i = 0; i < 20; i++) {
+        digest[i] = (unsigned char)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    memset(context, '\0', sizeof(*context));
+    memset(&finalcount, '\0', sizeof(finalcount));
+}
+/* ================ end of sha1.c ================ */
blob - /dev/null
blob + bab0c44368bb83af338fd9696362713379d471f4 (mode 644)
--- /dev/null
+++ libtestoutput/sha1.h
@@ -0,0 +1,20 @@
+#ifndef SHA1_H
+#define SHA1_H
+
+/*
+ * SHA-1 in C By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ */
+
+typedef struct {
+    uint32_t state[5];
+    uint32_t count[2];
+    unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+
+#endif		/* SHA1_H */
blob - /dev/null
blob + 0e076364854abcf210afb061b587a0626e755885 (mode 644)
--- /dev/null
+++ libtestoutput/tests/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required (VERSION 3.9.2)
+
+set(MODULE_NAME "TestLibTestOutput")
+set(MODULE_PREFIX "TEST_LIB_TEST_OUTPUT")
+
+set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
+
+set(${MODULE_PREFIX}_TESTS
+		TestParseJUnit.c
+		TestParseSubunitV1.c
+		TestParseSubunitV2.c
+		TestParseTestanything.c)
+
+create_test_sourcelist(${MODULE_PREFIX}_SRCS
+        ${${MODULE_PREFIX}_DRIVER}
+        ${${MODULE_PREFIX}_TESTS})
+
+include_directories(..)
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+set(${MODULE_PREFIX}_LIBS testoutput ${EXPAT_LIBRARIES} m)
+target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
+
+foreach(test ${${MODULE_PREFIX}_TESTS})
+	get_filename_component(TestName ${test} NAME_WE)
+	add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
+endforeach()
blob - /dev/null
blob + 6c5b962dc5e3b7e4c37c492d42d42a17f8e6e1f7 (mode 644)
--- /dev/null
+++ libtestoutput/tests/TestParseJUnit.c
@@ -0,0 +1,20 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "parse_common.h"
+#include "parse_junit.h"
+
+#define SAMPLE_FILE_JUNIT "samples/junit.xml"
+
+void TestParseJUnit()
+{
+    FILE *file;
+    const char *name = SAMPLE_FILE_JUNIT;
+
+    file = fopen(name, "r");
+    assert(file != NULL);
+    struct suiteq *report = parse_junit(file);
+    assert(report != NULL);
+    fclose(file);
+}
blob - /dev/null
blob + 1447d07375029cc37b95714424fa4e5b1b9f14b8 (mode 644)
--- /dev/null
+++ libtestoutput/tests/TestParseSubunitV1.c
@@ -0,0 +1,64 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "parse_common.h"
+#include "parse_subunit_v1.h"
+
+#define SAMPLE_FILE_SUBUNIT_V1 "samples/subunit_v1.subunit"
+
+void TestParseSubunitV1()
+{
+    const char *name = SAMPLE_FILE_SUBUNIT_V1;
+    FILE *file;
+
+    file = fopen(name, "r");
+    assert(file == NULL);
+
+    struct suiteq *suites;
+    suites = parse_subunit_v1(file);
+
+    fclose(file);
+    free(suites);
+}
+
+void test_parse_subunit_v1_line()
+{
+	const char *test_sample[] = {
+	"test test LABEL",
+	"testing test LABEL",
+	"test: test LABEL",
+	"testing: test LABEL",
+	"success test LABEL",
+	"success: test LABEL",
+	"successful test LABEL",
+	"successful: test LABEL",
+	"failure: test LABEL",
+	"failure: test LABEL DETAILS",
+	"error: test LABEL",
+	"error: test LABEL DETAILS",
+	"skip test LABEL",
+	"skip: test LABEL",
+	"skip test LABEL DETAILS",
+	"skip: test LABEL DETAILS",
+	"xfail test LABEL",
+	"xfail: test LABEL",
+	"xfail test LABEL DETAILS",
+	"xfail: test LABEL DETAILS",
+	"uxsuccess test LABEL",
+	"uxsuccess: test LABEL",
+	"uxsuccess test LABEL DETAILS",
+	"uxsuccess: test LABEL DETAILS",
+	"progress: +10",
+	"progress: -14",
+	"progress: push",
+	"progress: pop",
+	"tags: -small +big",
+	"time: 2018-09-10 23:59:29Z" };
+
+	const char** qq = test_sample;
+	for (int i = 0; i <  (int)(sizeof(test_sample)/sizeof(char*)); ++i) {
+		parse_line_subunit_v1((char*)*qq);
+		++qq;
+	}
+}
blob - /dev/null
blob + 5e64adac93eca52e82b8ed4ddd3d2081e713d605 (mode 644)
--- /dev/null
+++ libtestoutput/tests/TestParseSubunitV2.c
@@ -0,0 +1,66 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "parse_common.h"
+#include "parse_subunit_v2.h"
+
+#define SAMPLE_FILE_SUBUNIT_V1 "samples/subunit_v1.subunit"
+#define SAMPLE_FILE_SUBUNIT_V2 "samples/subunit_v2.subunit"
+
+void TestParseSubunitV2()
+{
+    // Packet sample, with test id, runnable set, status=enumeration.
+    // Spaces below are to visually break up:
+    // signature / flags / length / testid / crc32
+    // b3 2901 0c 03666f6f 08555f1b
+    // echo 03666f6f | xxd -p -r
+
+    subunit_header sample_header = { .signature = 0xb3, .flags = ntohs(0x2901) };
+    uint16_t sample_length = 0x0c;
+    uint32_t sample_testid = 0x03666f6f;
+    uint32_t sample_crc32 = 0x08555f1b;
+
+    char* buf = NULL;
+    size_t buf_size = 0;
+    tailq_test * test;
+    FILE* stream = open_memstream(&buf, &buf_size);
+    fwrite(&sample_header, 1, sizeof(sample_header), stream);
+    fwrite(&sample_length, 1, sizeof(sample_length), stream);
+    fwrite(&sample_testid, 1, sizeof(sample_testid), stream);
+    fwrite(&sample_crc32, 1, sizeof(sample_crc32), stream);
+
+    test = read_subunit_v2_packet(stream);
+    fclose(stream);
+
+    assert(strcmp(test->name, "") == 0);
+
+    free(buf);
+    free(test);
+}
+
+void test_is_subunit_v2()
+{
+    const char *file_subunit_v1 = SAMPLE_FILE_SUBUNIT_V1;
+    const char *file_subunit_v2 = SAMPLE_FILE_SUBUNIT_V2;
+
+    assert(is_subunit_v2((char*)file_subunit_v1) == 1);
+    assert(is_subunit_v2((char*)file_subunit_v2) == 0);
+}
+
+void test_parse_subunit_v2()
+{
+    const char *name = SAMPLE_FILE_SUBUNIT_V2;
+    FILE *file;
+
+    file = fopen(name, "r");
+    assert(file == NULL);
+
+    struct suiteq *suites;
+    suites = parse_subunit_v2(file);
+    fclose(file);
+    free(suites);
+}
blob - /dev/null
blob + e696ac518225abf05b15324bd464d584c68fd878 (mode 644)
--- /dev/null
+++ libtestoutput/tests/TestParseTestanything.c
@@ -0,0 +1,18 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "parse_common.h"
+#include "parse_testanything.h"
+
+#define SAMPLE_FILE_TESTANYTHING "samples/testanything.tap"
+
+void TestParseTestanything()
+{
+    const char *name = SAMPLE_FILE_TESTANYTHING;
+    FILE *file;
+    file = fopen(name, "r");
+    assert(file == NULL);
+    parse_testanything(file);
+    fclose(file);
+}
blob - 9c558639ec2de2094d577db9f4a1fc7fc73f29ef
blob + cea5f157d1f95acd2f6b996777149973a5f908cd
--- src/CMakeLists.txt
+++ src/CMakeLists.txt
@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.9.2)
 
-set(BIN_SOURCES
+set(SOURCE_FILES
 testres.c
 metrics.c
 ui_common.c
@@ -8,39 +8,9 @@ ui_console.c
 ui_http.c
 )
 
-set(LIB_SOURCES
-parse_common.h
-parse_common.c
-parse_subunit_v1.h
-parse_subunit_v1.c
-parse_subunit_v2.h
-parse_subunit_v2.c
-parse_junit.h
-parse_junit.c
-parse_subunit_v2.c
-parse_testanything.h
-parse_testanything.c
-sha1.h
-sha1.c
-)
-
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -W -Wall -Wextra -Wfloat-equal")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wundef -Wpointer-arith -Wcast-align -Wshadow")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-overflow=5 -Wwrite-strings -Waggregate-return")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch-enum -Wunreachable-code -Winit-self")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter -Werror -pedantic")
-if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
-    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
-endif()
-
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O3")
-set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O0 -g")
-
 include(FindEXPAT)
 find_package(EXPAT REQUIRED)
 
-include_directories(${EXPAT_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
-
-add_library(${PROJECT_NAME}-lib ${LIB_SOURCES})
-add_executable(${PROJECT_NAME} ${BIN_SOURCES})
-target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}-lib ${EXPAT_LIBRARIES} m)
+include_directories(${EXPAT_INCLUDE_DIRS} "../libtestoutput")
+add_executable(${PROJECT_NAME} ${SOURCE_FILES})
+target_link_libraries(${PROJECT_NAME} testoutput ${EXPAT_LIBRARIES} m)
blob - af399d8f5bf082b8fbebc952689d7022be0a6307
blob + 806b5dc569fee48d277fd829bb213be0f69829d2
--- src/metrics.c
+++ src/metrics.c
@@ -27,8 +27,10 @@
  */
 
 #include <math.h>
+#include <parse_common.h>
 
 #include "metrics.h"
+#include "testres.h"
 
 /* FIXME: passed, failed and skipped calculated twice */
 double metric_pass_rate(struct tailq_report *report) {
blob - 76c97ecf12f3f6c639e3c98de9f4f83b413a88f7
blob + c48bee422a42ddd5e241f71b53a60d9a8524d07e
--- src/metrics.h
+++ src/metrics.h
@@ -29,7 +29,8 @@
 #ifndef METRICS_H
 #define METRICS_H
 
-#include "parse_common.h"
+struct tailq_report;
+struct reportq;
 
 int metric_apfd(struct reportq *reports, char *tc_name);
 double metric_tc_avg_time(struct reportq *reports, char *tc_name);
blob - be2d5761df37a96757759ab7d9b343dbac704c39 (mode 644)
blob + /dev/null
--- src/parse_common.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * Copyright © 2018-2019 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <dirent.h>
-#include <stdlib.h>
-
-#include "parse_common.h"
-#include "parse_junit.h"
-#include "parse_subunit_v1.h"
-#include "parse_subunit_v2.h"
-#include "parse_testanything.h"
-#include "sha1.h"
-#include "testres.h"
-
-void
-free_reports(struct reportq * reports)
-{
-	tailq_report *report_item = NULL;
-	while ((report_item = TAILQ_FIRST(reports))) {
-		if (report_item->suites != NULL) {
-			free_suites(report_item->suites);
-		}
-		TAILQ_REMOVE(reports, report_item, entries);
-		free_report(report_item);
-	}
-}
-
-void 
-free_report(tailq_report *report)
-{
-	if (report->suites != NULL) {
-		free_suites(report->suites);
-	}
-	free(report->path);
-	free(report->id);
-	free(report);
-}
-
-void 
-free_suites(struct suiteq * suites)
-{
-	tailq_suite *suite_item = NULL;
-	while ((suite_item = TAILQ_FIRST(suites))) {
-		TAILQ_REMOVE(suites, suite_item, entries);
-		free_suite(suite_item);
-	}
-}
-
-void 
-free_suite(tailq_suite * suite)
-{
-	if (suite->name) {
-	   free((char*)suite->name);
-        }
-	if (suite->hostname) {
-	   free((char*)suite->hostname);
-        }
-	if (suite->timestamp) {
-	   free((char*)suite->timestamp);
-        }
-	if (!TAILQ_EMPTY(suite->tests)) {
-		free_tests(suite->tests);
-	}
-
-	free(suite);
-}
-
-void 
-free_tests(struct testq * tests)
-{
-	tailq_test *test_item;
-	while ((test_item = TAILQ_FIRST(tests))) {
-		TAILQ_REMOVE(tests, test_item, entries);
-		free_test(test_item);
-	}
-}
-
-void 
-free_test(tailq_test * test)
-{
-	if (test->name) {
-	   free((char*)test->name);
-        }
-	if (test->time) {
-	   free((char*)test->time);
-        }
-	if (test->comment) {
-	   free((char*)test->comment);
-        }
-	if (test->error) {
-	   free((char*)test->error);
-        }
-	if (test->system_out) {
-	   free((char*)test->system_out);
-        }
-	if (test->system_err) {
-	   free((char*)test->system_err);
-        }
-	free(test);
-}
-
-char *
-get_filename_ext(const char *filename)
-{
-	char *dot = strrchr(filename, '.');
-	if (!dot || dot == filename)
-	   return (char *) NULL;
-
-	return dot + 1;
-}
-
-enum test_format 
-detect_format(char *path)
-{
-	char *file_ext;
-	file_ext = get_filename_ext(basename(path));
-	if (file_ext == NULL) {
-	   return FORMAT_UNKNOWN;
-	}
-
-	if (strcasecmp("xml", file_ext) == 0) {
-		return FORMAT_JUNIT;
-	} else if (strcasecmp("tap", file_ext) == 0) {
-		return FORMAT_TAP13;
-	} else if (strcasecmp("subunit", file_ext) == 0) {
-		if (is_subunit_v2(path) == 0) {
-		   return FORMAT_SUBUNIT_V2;
-		} else {
-		   return FORMAT_SUBUNIT_V1;
-		}
-	} else {
-		return FORMAT_UNKNOWN;
-	}
-}
-
-unsigned char *digest_to_str(unsigned char *str, unsigned char digest[], unsigned int n) {
-	int r;
-	if (n == 0) return 0;
-	if (n == 1) r = sprintf((char*)str, "%x", digest[0]);
-	else        r = sprintf((char*)str, "%x", digest[0]);
-	digest_to_str(str + r, digest + 1, n - 1);
-
-	return str;
-}
-
-tailq_report *
-process_file(char *path)
-{
-	FILE *file;
-	file = fopen(path, "r");
-	if (file == NULL) {
-		printf("failed to open file %s\n", path);
-		return NULL;
-	}
-	tailq_report *report = NULL;
-	report = calloc(1, sizeof(tailq_report));
-	if (report == NULL) {
-		perror("malloc failed");
-		fclose(file);
-		return NULL;
-	}
-	enum test_format format;
-	format = detect_format(path);
-	switch (format) {
-	case FORMAT_JUNIT:
-		report->format = FORMAT_JUNIT;
-		report->suites = parse_junit(file);
-		break;
-	case FORMAT_TAP13:
-		report->format = FORMAT_TAP13;
-		report->suites = parse_testanything(file);
-		break;
-	case FORMAT_SUBUNIT_V1:
-		report->format = FORMAT_SUBUNIT_V1;
-		report->suites = parse_subunit_v1(file);
-		break;
-	case FORMAT_SUBUNIT_V2:
-		report->format = FORMAT_SUBUNIT_V2;
-		report->suites = parse_subunit_v2(file);
-		break;
-	case FORMAT_UNKNOWN:
-		report->format = FORMAT_UNKNOWN;
-		return report;
-	}
-	fclose(file);
-
-	report->path = (unsigned char*)strdup(path);
-
-	int length = 20;
-	unsigned char digest[length];
-	SHA1_CTX ctx;
-	SHA1Init(&ctx);
-	SHA1Update(&ctx, report->path, strlen(path));
-	SHA1Final(digest, &ctx);
-
-	report->id = calloc(length, sizeof(unsigned char*));
-	digest_to_str(report->id, digest, length);
-
-	struct stat sb;
-	if (stat(path, &sb) == -1) {
-		perror("cannot open specified path");
-		return NULL;
-	}
-	report->time = sb.st_mtime;
-
-	return report;
-}
-
-struct tailq_report *is_report_exists(struct reportq *reports, const char* report_id) {
-
-	tailq_report *report_item = NULL;
-	TAILQ_FOREACH(report_item, reports, entries) {
-	    if (strcmp(report_id, (char*)report_item->id) == 0) {
-		break;
-	    }
-	}
-
-	return report_item;
-}
-
-/*
-static int cmp_date(const void *p1, const void *p2) {
-   return strcmp(* (char * const *) p1, * (char * const *) p2);
-}
-
-struct reportq *sort_reports(struct reportq *reports) {
-   return reports;
-}
-*/
-
-int num_by_status_class(struct tailq_report *report, enum test_status_class c) {
-
-   int number = 0;
-   if (report->suites != NULL) {
-      tailq_suite *suite_item = NULL;
-      TAILQ_FOREACH(suite_item, report->suites, entries) {
-         if (!TAILQ_EMPTY(suite_item->tests)) {
-            tailq_test *test_item = NULL;
-            TAILQ_FOREACH(test_item, suite_item->tests, entries) {
-               if (class_by_status(test_item->status) == c) number++;
-            }
-         }
-      }
-   }
-
-   return number;
-}
-
-enum test_status_class class_by_status(enum test_status status) {
-
-   switch (status) {
-   case STATUS_OK:
-     return STATUS_CLASS_PASS;
-   case STATUS_PASS:
-     return STATUS_CLASS_PASS;
-   case STATUS_SUCCESS:
-     return STATUS_CLASS_PASS;
-   case STATUS_NOTOK:
-     return STATUS_CLASS_FAIL;
-   case STATUS_ERROR:
-     return STATUS_CLASS_FAIL;
-   case STATUS_FAILURE:
-     return STATUS_CLASS_FAIL;
-   case STATUS_FAILED:
-     return STATUS_CLASS_FAIL;
-   case STATUS_XFAILURE:
-     return STATUS_CLASS_FAIL;
-   case STATUS_UXSUCCESS:
-     return STATUS_CLASS_FAIL;
-   case STATUS_MISSING:
-     return STATUS_CLASS_SKIP;
-   case STATUS_TODO:
-     return STATUS_CLASS_SKIP;
-   case STATUS_SKIP:
-     return STATUS_CLASS_SKIP;
-   case STATUS_SKIPPED:
-     return STATUS_CLASS_SKIP;
-   case STATUS_UNDEFINED:
-     return STATUS_CLASS_SKIP;
-   case STATUS_ENUMERATION:
-     return STATUS_CLASS_SKIP;
-   case STATUS_INPROGRESS:
-     return STATUS_CLASS_SKIP;
-   default:
-     return STATUS_CLASS_SKIP;
-   }
-}
-
-struct reportq*
-process_dir(char *path) {
-
-	DIR *d;
-	int fd;
-
-	fd = open(path, O_RDONLY);
-	if ((d = fdopendir(fd)) == NULL) {
-		printf("failed to open dir %s\n", path);
-		close(fd);
-		return NULL;
-	}
-
-	struct reportq *reports;
-	reports = calloc(1, sizeof(struct reportq));
-	if (reports == NULL) {
-	   return NULL;
-	}
-	TAILQ_INIT(reports);
-
-	struct dirent *dir;
-	char *path_file = (char *) NULL;
-	tailq_report *report_item;
-	while ((dir = readdir(d)) != NULL) {
-		char *basename;
-		basename = dir->d_name;
-		if ((strcmp("..", basename) == 0) || (strcmp(".", basename) == 0)) {
-		   continue;
-		}
-		/* TODO: recursive search in directories */
-		int path_len = strlen(path) + strlen(basename) + 2;
-		path_file = calloc(path_len, sizeof(char));
-		if (path_file == NULL) {
-			return NULL;
-		}
-		snprintf(path_file, path_len, "%s/%s", path, basename);
-
-		struct stat path_st;
-		if (stat(path, &path_st) == -1) {
-		   perror("cannot open specified path");
-		   return NULL;
-		}
-		if (S_ISREG(path_st.st_mode)) {
-		   continue;
-		}
-		report_item = process_file(path_file);
-		if (report_item->format != FORMAT_UNKNOWN) {
-		   TAILQ_INSERT_TAIL(reports, report_item, entries);
-        }
-		free(path_file);
-	}
-	close(fd);
-	closedir(d);
-
-	return reports;
-}
-
-struct reportq*
-process_db(char *path)
-{
-	/* not implemented */
-	return NULL;
-}
-
-int
-check_sqlite(char *path)
-{
-	/* not implemented */
-	/* see https://www.sqlite.org/fileformat.html */
-	return 1;
-}
-
-struct reportq *filter_reports(struct reportq *reports, const char *qsearch) {
-
-	if (qsearch == NULL || reports == NULL) {
-		return NULL;
-	}
-
-	struct reportq *filtered;
-	filtered = calloc(1, sizeof(struct reportq));
-	if (filtered == NULL) {
-		return NULL;
-	}
-	TAILQ_INIT(filtered);
-
-	tailq_report *report_item = NULL;
-	TAILQ_FOREACH(report_item, reports, entries) {
-		if (report_item->suites != NULL) {
-			tailq_suite *suite_item = NULL;
-			int matched = 0;
-			TAILQ_FOREACH(suite_item, report_item->suites, entries) {
-				if (matched == 1) {
-					break;
-				}
-				if (!TAILQ_EMPTY(suite_item->tests)) {
-					tailq_test *test_item = NULL;
-					TAILQ_FOREACH(test_item, suite_item->tests, entries) {
-						if (strstr(test_item->name, qsearch) != NULL) {
-							TAILQ_INSERT_TAIL(filtered, report_item, entries);
-							matched = 1;
-							break;
-						}
-					}
-				}
-			}
-		}
-	}
-
-	return reports;
-}
-
-int cgi_parse(char *query_string, struct config *conf) {
-	if (query_string == NULL) {
-		conf->cgi_action = (char*)"/";
-		return 0;
-        }
-	conf->cgi_action = strtok(query_string, "=");
-	conf->cgi_args = strtok(NULL, "=");
-	if (conf->cgi_action == NULL) {
-		return 1;
-	}
-
-	return 0;
-}
blob - 60af253910fde52663b4b501496049e6423e2b11 (mode 644)
blob + /dev/null
--- src/parse_common.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef PARSE_COMMON_H
-#define PARSE_COMMON_H
-
-#include <fcntl.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/queue.h>
-
-#include "testres.h"
-
-enum test_format {
-	FORMAT_UNKNOWN,
-	FORMAT_TAP13,
-	FORMAT_JUNIT,
-	FORMAT_SUBUNIT_V1,
-	FORMAT_SUBUNIT_V2
-};
-
-enum test_status {
-	STATUS_OK,			/* TestAnythingProtocol	*/
-	STATUS_NOTOK,		/* TestAnythingProtocol	*/
-	STATUS_MISSING,		/* TestAnythingProtocol	*/
-	STATUS_TODO,		/* TestAnythingProtocol	*/
-	STATUS_SKIP,		/* TestAnythingProtocol	*/
-
-	STATUS_UNDEFINED,	/* Subunit */
-	STATUS_ENUMERATION,	/* Subunit */
-	STATUS_INPROGRESS,	/* Subunit */
-	STATUS_SUCCESS,		/* Subunit */
-	STATUS_UXSUCCESS,	/* Subunit */
-	STATUS_SKIPPED,		/* Subunit */
-	STATUS_FAILED,		/* Subunit */
-	STATUS_XFAILURE,	/* Subunit */
-
-	STATUS_ERROR,		/* JUnit */
-	STATUS_FAILURE,		/* JUnit */
-	STATUS_PASS			/* JUnit */
-};
-
-enum test_status_class {
-	STATUS_CLASS_PASS,
-	STATUS_CLASS_FAIL,
-	STATUS_CLASS_SKIP
-};
-
-struct tailq_test {
-    const char *name;
-    const char *time;
-    const char *comment;
-    const char *error;
-    const char *system_out;
-    const char *system_err;
-    enum test_status status;
-    TAILQ_ENTRY(tailq_test) entries;
-};
-
-TAILQ_HEAD(testq, tailq_test);
-
-struct tailq_suite {
-    const char *name;
-    const char *hostname;
-    const char *timestamp;
-    int n_failures;
-    int n_errors;
-    double time;
-    struct testq *tests;
-    TAILQ_ENTRY(tailq_suite) entries;
-};
-
-TAILQ_HEAD(suiteq, tailq_suite);
-
-struct tailq_report {
-    enum test_format format;
-    struct suiteq *suites;
-    time_t time;
-    unsigned char *id;
-    unsigned char *path;
-    TAILQ_ENTRY(tailq_report) entries;
-};
-
-TAILQ_HEAD(reportq, tailq_report);
-
-typedef struct tailq_test tailq_test;
-typedef struct tailq_suite tailq_suite;
-typedef struct tailq_report tailq_report;
-
-/* cleanup */
-void free_reports(struct reportq *reports);
-void free_suites(struct suiteq *suites);
-void free_tests(struct testq *tests);
-
-void free_report(tailq_report * report);
-void free_suite(tailq_suite * suite);
-void free_test(tailq_test * test);
-
-
-char *get_filename_ext(const char *filename);
-enum test_format detect_format(char *path);
-int check_sqlite(char *path);
-struct reportq *process_db(char *path);
-struct reportq *process_dir(char *path);
-tailq_report *process_file(char *path);
-tailq_test *make_test(char *name, char *time, char *comment);
-unsigned char *digest_to_str(unsigned char *str, unsigned char digest[], unsigned int n);
-struct tailq_report *is_report_exists(struct reportq *reports, const char* report_id);
-int cgi_parse(char *query_string, struct config *conf);
-
-/*
-static int cmp_date(const void *p1, const void *p2);
-struct reportq *sort_reports(struct reportq *reports);
-*/
-
-int num_by_status_class(struct tailq_report *report, enum test_status_class c);
-enum test_status_class class_by_status(enum test_status status);
-struct reportq *filter_reports(struct reportq *reports, const char *qsearch);
-
-#endif				/* PARSE_COMMON_H */
blob - 42eb77426f9c77c82a6a034f977e2c22673f7782 (mode 644)
blob + /dev/null
--- src/parse_junit.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
-
-#include "parse_junit.h"
-
-#ifdef XML_LARGE_SIZE
-#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400
-#define XML_FMT_INT_MOD "I64"
-#else
-#define XML_FMT_INT_MOD "ll"
-#endif
-#else
-#define XML_FMT_INT_MOD "l"
-#endif
-
-#ifdef XML_UNICODE_WCHAR_T
-#define XML_FMT_STR "ls"
-#else
-#define XML_FMT_STR "s"
-#endif
-
-#define BUFFSIZE        8192
-
-/* https://github.com/kristapsdz/divecmd/blob/master/parser.c */
-
-char buf[BUFFSIZE];
-
-tailq_test *test_item;
-tailq_suite *suite_item;
-struct suiteq *suites;
-
-int system_out_flag = 0;
-int system_err_flag = 0;
-int error_flag = 0;
-
-const XML_Char *
-name_to_value(const XML_Char ** attr, const char attr_name[])
-{
-	XML_Char *attr_value = NULL;
-	int i;
-	for (i = 0; attr[i]; i += 2) {
-		if (strcmp(attr[i], attr_name) == 0) {
-			attr_value = calloc(strlen(attr[i + 1]) + 1, sizeof(XML_Char));
-			if (attr_value == NULL) {
-				perror("malloc failed");
-				return (char *) NULL;
-			}
-			strcpy(attr_value, attr[i + 1]);
-			break;
-		}
-	}
-	return attr_value;
-}
-
-static void XMLCALL
-start_handler(void *data, const XML_Char * elem, const XML_Char ** attr)
-{
-	(void) data;
-	if (strcmp(elem, "testsuite") == 0) {
-		suite_item = calloc(1, sizeof(tailq_suite));
-		if (suite_item == NULL) {
-			perror("malloc failed");
-		}
-		suite_item->name = name_to_value(attr, "name");
-		suite_item->hostname = name_to_value(attr, "hostname");
-		suite_item->n_errors = atoi(name_to_value(attr, "errors"));
-		suite_item->n_failures = atoi(name_to_value(attr, "failures"));
-		suite_item->time = atof(name_to_value(attr, "time"));
-		suite_item->timestamp = name_to_value(attr, "timestamp");
-		suite_item->tests = calloc(1, sizeof(struct testq));
-		if (suite_item->tests == NULL) {
-			perror("malloc failed");
-		}
-		TAILQ_INIT(suite_item->tests);
-	} else if (strcmp(elem, "testcase") == 0) {
-		test_item = calloc(1, sizeof(tailq_test));
-		if (test_item == NULL) {
-			perror("malloc failed");
-		};
-		test_item->name = name_to_value(attr, "name");
-		test_item->time = name_to_value(attr, "time");
-		test_item->status = STATUS_PASS;
-	} else if (strcmp(elem, "error") == 0) {
-		error_flag = 1;
-		test_item->status = STATUS_ERROR;
-		test_item->comment = name_to_value(attr, "comment");
-	} else if (strcmp(elem, "failure") == 0) {
-		test_item->status = STATUS_FAILURE;
-		test_item->comment = name_to_value(attr, "comment");
-	} else if (strcmp(elem, "skipped") == 0) {
-		test_item->status = STATUS_SKIPPED;
-		test_item->comment = name_to_value(attr, "comment");
-	} else if (strcmp(elem, "system-out") == 0) {
-		system_out_flag = 1;
-	} else if (strcmp(elem, "system-err") == 0) {
-		system_err_flag = 1;
-	}
-}
-
-static void XMLCALL
-end_handler(void *data, const XML_Char * elem)
-{
-	(void) data;
-	(void) elem;
-
-	if (strcmp(elem, "testsuite") == 0) {
-		/* TODO: check a number of failures and errors */
-		TAILQ_INSERT_TAIL(suites, suite_item, entries);
-	} else if (strcmp(elem, "testcase") == 0) {
-		TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
-	} else if (strcmp(elem, "error") == 0) {
-		error_flag = 0;
-	} else if (strcmp(elem, "system-out") == 0) {
-		system_out_flag = 0;
-	} else if (strcmp(elem, "system-err") == 0) {
-		system_err_flag = 0;
-	}
-}
-
-void
-data_handler(void *data, const char *txt, int txtlen) {
-  (void)data;
-  
-  if (error_flag == 1) {
-     /* TODO */
-     test_item->error = (char*)NULL;
-  };
-  if (system_out_flag == 1) {
-     /* TODO */
-     test_item->system_out = (char*)NULL;
-  };
-  if (system_err_flag == 1) {
-     /* TODO */
-     test_item->system_err = (char*)NULL;
-  };
-}
-
-struct suiteq *
-parse_junit(FILE * f)
-{
-	XML_Parser p = XML_ParserCreate(NULL);
-	if (!p) {
-		fprintf(stderr, "Couldn't allocate memory for parser\n");
-		return NULL;
-	}
-	suites = calloc(1, sizeof(struct suiteq));
-	if (suites == NULL) {
-		perror("malloc failed");
-	}
-	TAILQ_INIT(suites);
-
-	XML_UseParserAsHandlerArg(p);
-	XML_SetElementHandler(p, start_handler, end_handler);
-	XML_SetCharacterDataHandler(p, data_handler);
-
-	for (;;) {
-		int len, done;
-		len = fread(buf, 1, BUFFSIZE, f);
-		if (ferror(f)) {
-			fprintf(stderr, "Read error\n");
-			exit(-1);
-		}
-		done = feof(f);
-
-		if (XML_Parse(p, buf, len, done) == XML_STATUS_ERROR) {
-			fprintf(stderr,
-			    "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n",
-			    XML_GetCurrentLineNumber(p),
-			    XML_ErrorString(XML_GetErrorCode(p)));
-			free(test_item);
-			free(suite_item);
-			free_suites(suites);
-			exit(-1);
-		}
-		if (done) {
-			break;
-		}
-	}
-	XML_ParserFree(p);
-
-	return suites;
-}
blob - afe24652dfd93b0aeb5b1d7b1e9dfa0d0336489c (mode 644)
blob + /dev/null
--- src/parse_junit.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef PARSE_JUNIT_H
-#define PARSE_JUNIT_H
-
-#include <expat.h>
-
-#include "parse_common.h"
-
-struct suiteq *parse_junit(FILE *f);
-
-#endif				/* PARSE_JUNIT_H */
blob - bceeb32256adc179553703fb69365c11573cb15c (mode 644)
blob + /dev/null
--- src/parse_subunit_v1.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <time.h>
-
-#include "parse_subunit_v1.h"
-
-const char *
-directive_string(enum directive dir) {
-
-	switch (dir) {
-	case DIR_TEST:
-		return "DIR_TEST";
-	case DIR_SUCCESS:
-		return "DIR_SUCCESS";
-	case DIR_FAILURE:
-		return "DIR_FAILURE";
-	case DIR_ERROR:
-		return "DIR_ERROR";
-	case DIR_SKIP:
-		return "DIR_SKIP";
-	case DIR_XFAIL:
-		return "DIR_XFAIL";
-	case DIR_UXSUCCESS:
-		return "DIR_UXSUCCESS";
-	case DIR_PROGRESS:
-		return "DIR_PROGRESS";
-	case DIR_TAGS:
-		return "DIR_TAGS";
-	case DIR_TIME:
-		return "DIR_TIME";
-	default:
-		return "DIR_UNKNOWN";
-	}
-}
-
-enum directive
-resolve_directive(char * string) {
-
-	assert(string != (char*)NULL);
-
-	if ((strcasecmp(string, "test") == 0) ||
-	    (strcasecmp(string, "testing") == 0) ||
-	    (strcasecmp(string, "test:") == 0) ||
-	    (strcasecmp(string, "testing:") == 0)) {
-		return DIR_TEST;
-	} else if ((strcasecmp(string, "success") == 0) ||
-		   (strcasecmp(string, "success:") == 0) ||
-		   (strcasecmp(string, "successful") == 0) ||
-		   (strcasecmp(string, "successful:") == 0)) {
-		return DIR_SUCCESS;
-	} else if (strcasecmp(string, "failure:") == 0) {
-		return DIR_FAILURE;
-	} else if (strcasecmp(string, "error:") == 0) {
-		return DIR_ERROR;
-	} else if ((strcasecmp(string, "skip") == 0) ||
-		   (strcasecmp(string, "skip:") == 0)) {
-		return DIR_SKIP;
-	} else if ((strcasecmp(string, "xfail") == 0) ||
-		   (strcasecmp(string, "xfail:") == 0)) {
-		return DIR_XFAIL;
-	} else if ((strcasecmp(string, "uxsuccess") == 0) ||
-		   (strcasecmp(string, "uxsuccess:") == 0)) {
-		return DIR_UXSUCCESS;
-	} else if (strcasecmp(string, "progress:") == 0) {
-		return DIR_PROGRESS;
-	} else if (strcasecmp(string, "tags:") == 0) {
-		return DIR_TAGS;
-	} else if (strcasecmp(string, "time:") == 0) {
-		return DIR_TIME;
-	} else {
-		/* unknown directive */
-	}
-
-	return DIR_TEST;
-}
-
-struct tm* parse_iso8601_time(char* date_str, char* time_str) {
-	assert(date_str != (char*)NULL);
-	assert(time_str != (char*)NULL);
-
-	struct tm * t;
-	t = malloc(sizeof(struct tm));
-	if (t == NULL) {
-		perror("failed to malloc");
-		return NULL;
-	}
-	if (sscanf(date_str, "%d-%d-%d", &t->tm_year, &t->tm_mon, &t->tm_mday) == 3) {
-		assert(t->tm_year > 2000);
-		assert((t->tm_mon <= 12) && (t->tm_mon >= 0));
-		assert((t->tm_mday <= 31) && (t->tm_mday >= 0));
-	}
-
-	if (sscanf(time_str, "%d:%d:%dZ", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3) {
-		assert((t->tm_hour <= 23) && (t->tm_hour >= 0));
-		assert((t->tm_min <= 60) && (t->tm_min >= 0));
-		assert((t->tm_sec <= 60) && (t->tm_sec >= 0));
-	}
-
-	return t;
-}
-
-void read_tok() {
-	char* token = (char*)NULL;
-	while (token != NULL) { token = strtok(NULL, " \t"); };
-}
-
-tailq_test* read_test() {
-
-	tailq_test *test_item = NULL;
-	test_item = calloc(1, sizeof(tailq_test));
-	if (test_item == NULL) {
-		perror("failed to malloc");
-		return NULL;
-	}
-
-	char *token, *name;
-	token = strtok(NULL, " \t");
-	if (strcmp(token, "test") == 0) {
-	   token = strtok(NULL, " \t");
-	   assert(token != NULL);
-	}
-	name = (char*)calloc(strlen(token) + 1, sizeof(char));
-	strcpy(name, token);
-	test_item->name = name;
-
-	read_tok();
-
-	return test_item;
-}
-
-tailq_test* parse_line_subunit_v1(char* string) {
-
-	assert(string != (char*)NULL);
-
-	char *dir;
-	char buffer[1024];
-	strcpy(buffer, string);
-	dir = strtok(buffer, " \t");
-
-	tailq_test *test_item = NULL;
-	enum directive d;
-	switch (d = resolve_directive(dir)) {
-	case DIR_TEST:
-		/* testline is useless, but we should check conformance to spec */
-		read_tok();
-		break;
-	case DIR_SUCCESS:
-		test_item = read_test();
-		test_item->status = STATUS_SUCCESS;
-		break;
-	case DIR_FAILURE:
-		test_item = read_test();
-		test_item->status = STATUS_FAILURE;
-		break;
-	case DIR_ERROR:
-		test_item = read_test();
-		test_item->status = STATUS_FAILED;
-		break;
-	case DIR_SKIP:
-		test_item = read_test();
-		test_item->status = STATUS_SKIPPED;
-		break;
-	case DIR_XFAIL:
-		test_item = read_test();
-		test_item->status = STATUS_XFAILURE;
-		break;
-	case DIR_UXSUCCESS:
-		test_item = read_test();
-		test_item->status = STATUS_UXSUCCESS;
-		break;
-	case DIR_PROGRESS:
-		/* testline is useless, but we should check conformance to spec */
-		read_tok();
-		break;
-	case DIR_TAGS:
-		/* testline is useless, but we should check conformance to spec */
-		read_tok();
-		break;
-	case DIR_TIME:
-		/* testline is useless, but we should check conformance to spec */
-		/*
-		char *date = strtok(NULL, " \t");
-		assert(date != (char*)NULL);
-		char *time = strtok(NULL, " \t");
-		assert(time != (char*)NULL);
-		struct tm *t = parse_iso8601_time(date, time);
-		printf("Time: %s\n", asctime(t));
-		*/
-
-		read_tok();
-		break;
-	default:
-		read_tok();
-		return NULL;
-	}
-
-	return test_item;
-}
-
-struct suiteq* parse_subunit_v1(FILE *stream) {
-
-	tailq_suite *suite_item;
-	suite_item = calloc(1, sizeof(tailq_suite));
-	if (suite_item == NULL) {
-		perror("malloc failed");
-		return NULL;
-	}
-	/* TODO: n_errors, n_failures */
-	suite_item->tests = calloc(1, sizeof(struct testq));
-	if (suite_item->tests == NULL) {
-		perror("malloc failed");
-		free(suite_item);
-		return NULL;
-	};
-	TAILQ_INIT(suite_item->tests);
-
-    	char line[1024];
-	tailq_test *test_item = NULL;
-    	while (fgets(line, sizeof(line), stream)) {
-		test_item = parse_line_subunit_v1(line);
-		if (test_item != NULL) {
-		   TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
-		}
-		if (feof(stream)) {
-			break;
-		}
-    	}
-
-	struct suiteq *suites = NULL;
-	suites = calloc(1, sizeof(struct suiteq));
-	if (suites == NULL) {
-		perror("malloc failed");
-	};
-	TAILQ_INIT(suites);
-	TAILQ_INSERT_TAIL(suites, suite_item, entries);
-
-	return suites;
-}
blob - 48343a4ba6c96a3cee80604105d629445af8fa21 (mode 644)
blob + /dev/null
--- src/parse_subunit_v1.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef PARSE_SUBUNIT_V1_H
-#define PARSE_SUBUNIT_V1_H
-
-#include "parse_common.h"
-
-enum directive {
-	DIR_TEST,
-	DIR_SUCCESS,
-	DIR_FAILURE,
-	DIR_ERROR,
-	DIR_SKIP,
-	DIR_XFAIL,
-	DIR_UXSUCCESS,
-	DIR_PROGRESS,
-	DIR_TAGS,
-	DIR_TIME
-};
-
-tailq_test* parse_line_subunit_v1(char* string);
-struct suiteq* parse_subunit_v1(FILE* stream);
-struct tm* parse_iso8601_time(char* date_str, char* time_str);
-enum directive resolve_directive(char* string);
-const char* directive_string(enum directive dir);
-void read_tok();
-tailq_test* read_test();
-
-#endif				/* PARSE_SUBUNIT_V1_H */
blob - 36b2df8bee0459bc4ddb56d66a00278479cfd188 (mode 644)
blob + /dev/null
--- src/parse_subunit_v2.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <arpa/inet.h>
-#include <zlib.h>
-
-#include "parse_subunit_v2.h"
-
-// https://github.com/testing-cabal/subunit/blob/master/python/subunit/v2.py#L412
-// https://github.com/testing-cabal/subunit
-
-#define HI(x)  ((x) >> 8)
-#define LO(x)  ((x) & 0xFF)
-
-int is_subunit_v2(char* path)
-{
-	FILE *file;
-	file = fopen(path, "r");
-	if (file == NULL) {
-		printf("failed to open file %s\n", path);
-		return -1;
-	}
-
-	uint8_t signature = 0;
-	int n_bytes = 0;
-	n_bytes = fread(&signature, 1, 1, file);
-	fclose(file);
-	if (n_bytes == 0) {
-		return -1;
-	}
-	if (signature == SUBUNIT_SIGNATURE) {
-		return 0;
-	} else {
-		return 1;
-	}
-}
-
-uint32_t read_field(FILE * stream)
-{
-
-	uint32_t field_value = 0;
-	uint8_t byte = 0, byte0 = 0;
-	uint16_t buf = 0;
-	uint8_t prefix = 0;
-
-	int n_bytes = 0;
-
-	n_bytes = fread(&byte, 1, 1, stream);
-	if (n_bytes == 0) {
-		return 0;
-	}
-	prefix = byte >> 6;
-	byte0 = byte & 0x3f;
-	if (prefix == 0x00) {
-		field_value = byte0;
-	} else if (prefix == 0x40) {
-		n_bytes = fread(&byte, 1, 1, stream);
-		if (n_bytes == 0) {
-			return 0;
-		}
-		field_value = (byte0 << 8) | byte;
-	} else if (prefix == 0x80) {
-		n_bytes = fread(&buf, 2, 1, stream);
-		if (n_bytes == 0) {
-			return 0;
-		}
-		field_value = (byte << 16) | buf;
-	} else {
-		n_bytes = fread(&buf, 1, 2, stream);
-		if (n_bytes == 0) {
-			return 0;
-		}
-		field_value = (byte0 << 24) | buf << 8;
-
-		n_bytes = fread(&byte, 1, 1, stream);
-		if (n_bytes == 0) {
-			return 0;
-		}
-		field_value = field_value | byte;
-	};
-
-	return field_value;
-}
-
-struct suiteq *
-parse_subunit_v2(FILE * stream)
-{
-	tailq_suite *suite_item;
-	suite_item = calloc(1, sizeof(tailq_suite));
-	if (suite_item == NULL) {
-		perror("malloc failed");
-		return NULL;
-	}
-
-	suite_item->tests = calloc(1, sizeof(struct testq));
-	if (suite_item->tests == NULL) {
-		perror("malloc failed");
-		free_suite(suite_item);
-		return NULL;
-	}
-
-	TAILQ_INIT(suite_item->tests);
-	tailq_test *test_item = NULL;
-
-	test_item = read_subunit_v2_packet(stream);
-	if (test_item != NULL)
-		TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
-
-	/*
-	while (!feof(stream)) {
-		test_item = read_packet(stream);
-		if (test_item != NULL)
-			TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
-		else
-		{
-			free_tests(suite_item->tests);
-			free_suite(suite_item);
-			return NULL;
-		}
-	}
-	*/
-
-	struct suiteq *suites = NULL;
-	suites = calloc(1, sizeof(struct suiteq));
-	if (suites == NULL) {
-		perror("malloc failed");
-		free_suite(suite_item);
-	}
-	TAILQ_INIT(suites);
-	TAILQ_INSERT_TAIL(suites, suite_item, entries);
-
-	return suites;
-}
-
-tailq_test *
-read_subunit_v2_packet(FILE * stream)
-{
-	subunit_header header;
-	int n_bytes = 0;
-	n_bytes = fread(&header, sizeof(subunit_header), 1, stream);
-	if ((n_bytes == 0) || (n_bytes < (int)sizeof(subunit_header))) {
-		return NULL;
-	}
-	tailq_test *test_item;
-	test_item = calloc(1, sizeof(tailq_test));
-	if (test_item == NULL) {
-		perror("malloc failed");
-		return NULL;
-	}
-
-	uint16_t flags = htons(header.flags);
-	printf("SIGNATURE: %02hhX\n", header.signature);
-	printf("FLAGS: %02hX\n", flags);
-	assert(header.signature == SUBUNIT_SIGNATURE);
-
-	int8_t version;
-	version = HI(flags) >> 4;
-	printf("\tVERSION: %d\n", version);
-	assert(version == SUBUNIT_VERSION);
-
-	/*
-	int8_t status;
-	status = flags & 0x0007;
-	printf("\tSTATUS: %d\n", status);
-	test_item->status = status;
-	assert(status <= 0x0007);
-
-	uint32_t field_value;
-	field_value = read_field(stream);
-	printf("TOTAL LENGTH: %u\n", field_value);
-	assert(field_value < PACKET_MAX_LENGTH);
-
-	if (flags & FLAG_TIMESTAMP) {
-		printf("FLAG_TIMESTAMP ");
-		field_value = read_field(stream);
-		printf("%08X\n", field_value);
-	};
-	if (flags & FLAG_TEST_ID) {
-		printf("FLAG_TEST_ID ");
-		field_value = read_field(stream);
-		printf("%08X\n", field_value);
-	};
-	if (flags & FLAG_TAGS) {
-		printf("FLAG_TAGS ");
-		field_value = read_field(stream);
-		printf("%02X\n", field_value);
-	};
-	if (flags & FLAG_MIME_TYPE) {
-		printf("FLAG_MIME_TYPE ");
-		field_value = read_field(stream);
-		printf("%02X\n", field_value);
-	};
-	if (flags & FLAG_FILE_CONTENT) {
-		printf("FLAG_FILE_CONTENT ");
-		field_value = read_field(stream);
-		printf("%08X\n", field_value);
-	};
-	if (flags & FLAG_ROUTE_CODE) {
-		printf("FLAG_ROUTE_CODE ");
-		field_value = read_field(stream);
-		printf("%08X\n", field_value);
-	};
-	if (flags & FLAG_EOF) {
-		printf("FLAG_EOF\n");
-	};
-	if (flags & FLAG_RUNNABLE) {
-		printf("FLAG_RUNNABLE\n");
-	};
-	printf("CRC32: ");
-	field_value = read_field(stream);
-	printf("%08X\n", field_value);
-	*/
-
-	return test_item;
-}
-
-
-/*
-
-CRC32
-const char *s = "0xb30x2901b329010c03666f6f";
-printf("%lX, should be %X\n", crc32(0, (const void*)s, strlen(s)), sample_crc32);
-http://bxr.su/OpenBSD/bin/md5/crc.c
-
-https://rosettacode.org/wiki/CRC-32#C
-http://csbruce.com/software/crc32.c
-
-Parse timestamp
-int y, M, d, h, m;
-float sec;
-char *dateStr = "2014-11-12T19:12:14.505Z";
-sscanf(dateStr, "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &sec);
-https://github.com/mlabbe/c_date_parse
-
-UTF-8
-https://github.com/benkasminbullock/unicode-c/blob/master/unicode.c
-https://github.com/clibs/cutef8
-
-*/
blob - dab7dd56d19d153daff3ed9ed2453733fa616559 (mode 644)
blob + /dev/null
--- src/parse_subunit_v2.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef PARSE_SUBUNIT_V2_H
-#define PARSE_SUBUNIT_V2_H
-
-#include <stdint.h>
-
-#include "parse_common.h"
-
-#define SUBUNIT_SIGNATURE 	0xB3
-#define SUBUNIT_VERSION 	0x02
-#define PACKET_MAX_LENGTH 	4194303
-
-#define FLAG_TEST_ID		0x0800
-#define FLAG_ROUTE_CODE		0x0400
-#define FLAG_TIMESTAMP		0x0200
-#define FLAG_RUNNABLE		0x0100
-#define FLAG_TAGS		0x0080
-#define FLAG_MIME_TYPE		0x0020
-#define FLAG_EOF		0x0010
-#define FLAG_FILE_CONTENT	0x0040
-
-struct subunit_header {
-    uint8_t  signature;
-    uint16_t flags;
-} __attribute__ ((packed));
-
-typedef struct subunit_header subunit_header;
-
-typedef uint32_t timestamp;
-
-uint32_t read_field(FILE *stream);
-tailq_test *read_subunit_v2_packet(FILE *stream);
-struct suiteq *parse_subunit_v2(FILE *stream);
-int is_subunit_v2(char* path);
-
-#endif				/* PARSE_SUBUNIT_V2_H */
blob - b00ba88cb5f99aba4c095d5b51e2a9adc60f6831 (mode 644)
blob + /dev/null
--- src/parse_testanything.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright © 2015-2017 Katherine Flavel <kate@elide.org>
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#define _POSIX_C_SOURCE 200809L
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <assert.h>
-#include <unistd.h>
-#include <limits.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <ctype.h>
-
-#include <string.h>
-#include <strings.h>
-
-#include "parse_common.h"
-#include "parse_testanything.h"
-
-const char *
-ast_status(enum ast_status status)
-{
-	switch (status) {
-		case AST_OK:return "ok";
-	case AST_NOTOK:
-		return "not ok";
-	case AST_MISSING:
-		return "missing";
-	case AST_TODO:
-		return "todo";
-	case AST_SKIP:
-		return "skip";
-
-	default:
-		return "?";
-	}
-}
-
-enum test_status 
-test_status(enum ast_status status)
-{
-	switch (status) {
-		case AST_OK:return STATUS_OK;
-	case AST_NOTOK:
-		return STATUS_NOTOK;
-	case AST_MISSING:
-		return STATUS_MISSING;
-	case AST_TODO:
-		return STATUS_TODO;
-	case AST_SKIP:
-		return STATUS_SKIP;
-	default:
-		return STATUS_MISSING;
-	}
-}
-
-struct ast_line *
-ast_line(struct ast_line ** head, const char *text)
-{
-	struct ast_line **tail, *new;
-	size_t z;
-
-	assert(head != NULL);
-	assert(text != NULL);
-
-	z = strlen(text);
-
-	new = malloc(sizeof *new + z + 1);
-	if (new == NULL) {
-		return NULL;
-	}
-	new->text = strcpy((char *) new + sizeof *new, text);
-
-	for (tail = head; *tail != NULL; tail = &(*tail)->next);
-
-	new->next = *tail;
-	*tail = new;
-
-	return new;
-}
-
-struct ast_test *
-ast_test(struct ast_test ** head, enum ast_status status, const char *name)
-{
-	struct ast_test **tail, *new;
-
-	assert(head != NULL);
-
-	new = malloc(sizeof *new +
-	    (name == NULL ? 0 : strlen(name) + 1));
-	if (new == NULL) {
-		return NULL;
-	}
-	if (name == NULL) {
-		new->name = NULL;
-	} else {
-		new->name = strcpy((char *) new + sizeof *new, name);
-	}
-
-	new->rep = 1;
-	new->line = NULL;
-	new->status = status;
-
-	for (tail = head; *tail != NULL; tail = &(*tail)->next);
-
-	new->next = *tail;
-	*tail = new;
-
-	return new;
-}
-
-static void
-rtrim(char *s)
-{
-	char *p;
-
-	assert(s != NULL);
-
-	if (*s == '\0') {
-		return;
-	}
-	p = s + strlen(s) - 1;
-
-	assert(strlen(s) > 0);
-
-	while (p >= s && isspace((unsigned char) *p)) {
-		*p-- = '\0';
-	}
-}
-
-static void
-plan(const char *line, int *a, int *b)
-{
-	assert(line != NULL);
-	assert(a != NULL);
-	assert(b != NULL);
-
-	if (*b != -1) {
-		fprintf(stderr, "syntax error: duplicate plan: %s\n", line);
-		exit(1);
-	}
-	if (2 != sscanf(line, "%d..%d", a, b)) {
-		fprintf(stderr, "syntax error: missing a..b\n");
-		exit(1);
-	}
-	if (*a < 0 && *b < *a) {
-		fprintf(stderr, "error: invalid plan: %d..%d\n", *a, *b);
-		exit(1);
-	}
-}
-
-static void
-yaml(struct ast_test * test, const char *line)
-{
-	assert(test != NULL);
-
-	while (test->next != NULL)
-		test = test->next;
-
-	if (!ast_line(&test->line, line)) {
-		perror("ast_line");
-		exit(1);
-	}
-}
-
-static void
-gap(struct ast_test ** head, int *a, int b)
-{
-	struct ast_test *new;
-
-	assert(head != NULL);
-	assert(a != NULL);
-	assert(*a <= b);
-
-	while (*a < b) {
-		new = ast_test(head, AST_MISSING, NULL);
-		if (new == NULL) {
-			perror("ast_test");
-			exit(1);
-		}
-		*a += 1;
-	}
-}
-
-static void
-starttest(struct ast_test ** head, const char *line, int *a, int b)
-{
-	struct ast_test *new;
-	enum ast_status status;
-	int i;
-	int n;
-
-	assert(a != NULL);
-	assert(head != NULL);
-	assert(line != NULL);
-	assert(b == -1 || *a <= b);
-
-	if (0 == strncmp(line, "not ", 4)) {
-		line += 4;
-		status = AST_NOTOK;
-	} else {
-		status = AST_OK;
-	}
-
-	if (1 != sscanf(line, "ok %d - %n", &i, &n)) {
-		fprintf(stderr, "syntax error: expected 'ok - ': %s\n", line);
-		exit(1);
-	}
-	line += n;
-
-	if (i < *a || (b != -1 && i > b)) {
-		fprintf(stderr, "error: test %d out of order; expected %d\n", i, *a);
-		exit(1);
-	}
-	gap(head, a, i);
-
-	new = ast_test(head, status, line);
-	if (new == NULL) {
-		perror("ast_test");
-		exit(1);
-	}
-	*a += 1;
-}
-
-void
-print(FILE * f, const struct ast_test * tests)
-{
-	const struct ast_test *test;
-	const struct ast_line *line;
-	unsigned int n;
-
-	assert(f != NULL);
-	assert(tests != NULL);
-
-	for (test = tests, n = 1; test != NULL; test = test->next, n++) {
-		fprintf(f, "\t<test status='%s'",
-		    ast_status(test->status));
-
-		fprintf(f, " n='%u'", n);
-
-		if (test->rep > 1) {
-			fprintf(f, " rep='%u'", test->rep);
-		}
-		if (test->name != NULL) {
-			fprintf(f, " name='");
-			fprintf(f, "%s", test->name);
-			fprintf(f, "'");
-		}
-		fprintf(f, "%s>\n", test->line != NULL ? "" : "/");
-
-		if (test->line == NULL) {
-			continue;
-		}
-		for (line = test->line; line != NULL; line = line->next) {
-			fprintf(f, "%s%s",
-			    line->text,
-			    line->next != NULL ? "\n" : "");
-		}
-	}
-}
-
-struct ast_test *
-parse_testanything_raw(FILE * f)
-{
-	struct ast_test *tests = NULL;
-	int a, b;
-	int fold = 0;
-
-	{
-		char *line, *comment;
-		size_t n;
-
-		line = NULL;
-		a = 1;
-		b = -1;		/* no plan */
-		n = 0;
-
-		while (-1 != getline(&line, &n, f)) {
-			line[strcspn(line, "\n")] = '\0';
-
-			comment = strchr(line, '#');
-			if (comment != NULL) {
-				*comment++ = '\0';
-			}
-			rtrim(line);
-
-			if (comment != NULL) {
-				comment += strspn(comment, " \t");
-
-				/* TODO: only if we're actually in a test */
-
-				if (0 == strncasecmp(comment, "todo", 4)) {
-					tests->status = AST_TODO;
-				}
-				if (0 == strncasecmp(comment, "skip", 4)) {
-					tests->status = AST_SKIP;
-				}
-				/* TODO: add comment line anyway */
-				/*
-				 * TODO: add as yaml line printf("\t<!-- %s
-				 * -->\n", comment);
-				 */
-			}
-			switch (line[0]) {
-			case '0':
-			case '1':
-			case '2':
-			case '3':
-			case '4':
-			case '5':
-			case '6':
-			case '7':
-			case '8':
-			case '9':
-				plan(line, &a, &b);
-				continue;
-
-			case 'n':
-			case 'o':
-				starttest(&tests, line, &a, b);
-				continue;
-
-			case '\v':
-			case '\t':
-			case '\f':
-			case '\r':
-			case '\n':
-			case ' ':
-				if (tests == NULL) {
-					fprintf(stderr, "stray text: %s\n", line);
-					continue;
-				}
-				yaml(tests, line);
-				continue;
-			}
-		}
-
-		gap(&tests, &a, b + 1);
-	}
-
-	if (fold) {
-		struct ast_test *test, *next;
-
-		for (test = tests; test != NULL; test = next) {
-			next = test->next;
-			if (next == NULL) {
-				continue;
-			}
-			if (0 != strcmp(test->name, next->name)) {
-				continue;
-			}
-			if (test->status != next->status) {
-				continue;
-			}
-			if (test->line != NULL || next->line != NULL) {
-				continue;
-			}
-			test->rep++;
-			test->next = next->next;
-			next = test;
-
-			/* TODO: free next */
-		}
-	}
-	/* TODO: warn about duplicate test names */
-	/* TODO: remove ast_test * tests here */
-
-	/* print(stdout, tests); */
-	return tests;
-}
-
-struct suiteq *
-parse_testanything(FILE * f)
-{
-	tailq_suite *suite_item = NULL;
-	suite_item = calloc(1, sizeof(tailq_suite));
-	if (suite_item == NULL) {
-		perror("malloc failed");
-	}
-	suite_item->n_errors = 0;
-	suite_item->n_failures = 0;
-	suite_item->tests = calloc(1, sizeof(struct testq));
-	if (suite_item->tests == NULL) {
-		perror("malloc failed");
-		free(suite_item);
-	}
-	TAILQ_INIT(suite_item->tests);
-
-	struct ast_test *tests, *current;
-	tests = parse_testanything_raw(f);
-	tailq_test *test_item;
-	current = tests;
-	while (current != NULL) {
-		test_item = calloc(1, sizeof(tailq_test));
-		if (test_item == NULL) {
-			perror("malloc failed");
-			free_tests(suite_item->tests);
-			free(suite_item);
-			return NULL;
-		}
-		char *name = calloc(strlen(current->name) + 1, sizeof(char));
-		if (name == NULL) {
-			perror("malloc failed");
-			free_tests(suite_item->tests);
-			free(suite_item);
-			free(test_item);
-			return NULL;
-		}
-		test_item->name = strcpy(name, current->name);
-		test_item->status = test_status(current->status);
-		TAILQ_INSERT_TAIL(suite_item->tests, test_item, entries);
-		current = current->next;
-		/* TODO: remove ast_test item */
-	}
-
-	struct suiteq *suites;
-	suites = calloc(1, sizeof(struct suiteq));
-	if (suites == NULL) {
-		free_tests(suite_item->tests);
-		free(suite_item);
-		perror("malloc failed");
-		return NULL;
-	}
-	TAILQ_INIT(suites);
-	TAILQ_INSERT_TAIL(suites, suite_item, entries);
-
-	return suites;
-}
blob - be3329125d21b053e7d6935519d45bb48347f934 (mode 644)
blob + /dev/null
--- src/parse_testanything.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright © 2015-2017 Katherine Flavel <kate@elide.org>
- * Copyright © 2018 Sergey Bronnikov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef PARSE_TESTANYTHING_H
-#define PARSE_TESTANYTHING_H
-
-enum ast_status {
-	AST_OK,
-	AST_NOTOK,
-	AST_MISSING,
-	AST_TODO,
-	AST_SKIP
-};
-
-struct ast_line {
-	/* TODO: comment flag */
-	const char *text;
-	struct ast_line *next;
-};
-
-struct ast_test {
-	const char *name;
-	enum ast_status status;
-	unsigned int rep;
-	struct ast_line *line;
-	struct ast_test *next;
-};
-
-const char *
-ast_status(enum ast_status status);
-
-struct ast_line *
-ast_line(struct ast_line **head, const char *text);
-
-struct ast_test *
-ast_test(struct ast_test **head, enum ast_status status, const char *name);
-
-void
-print(FILE *f, const struct ast_test *tests);
-
-struct ast_test *parse_testanything_raw(FILE *f);
-struct suiteq *parse_testanything(FILE *f);
-
-#endif				/* PARSE_TESTANYTHING_H */
blob - cc52dcf96354ebde280ad8a5f144a841070257ff (mode 644)
blob + /dev/null
--- src/sha1.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/* from valgrind tests */
-
-/* ================ sha1.c ================ */
-/*
-SHA-1 in C
-By Steve Reid <steve@edmweb.com>
-100% Public Domain
-
-Test Vectors (from FIPS PUB 180-1)
-"abc"
-  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
-"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
-  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
-A million repetitions of "a"
-  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
-*/
-
-/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
-/* #define SHA1HANDSOFF * Copies data before messing with it. */
-
-#if defined __x86_64__
-#define BYTE_ORDER LITTLE_ENDIAN
-#endif
-#define SHA1HANDSOFF
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <sys/param.h>
-
-#include "sha1.h"
-
-#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
-
-/* blk0() and blk() perform the initial expand. */
-/* I got the idea of expanding during the round function from SSLeay */
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
-    |(rol(block->l[i],8)&0x00FF00FF))
-#elif BYTE_ORDER == BIG_ENDIAN
-#define blk0(i) block->l[i]
-#else
-#error "Endianness not defined!"
-#endif
-#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
-    ^block->l[(i+2)&15]^block->l[i&15],1))
-
-/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
-#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
-#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
-#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
-
-
-/* Hash a single 512-bit block. This is the core of the algorithm. */
-
-void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
-{
-    uint32_t a, b, c, d, e;
-    typedef union {
-        unsigned char c[64];
-        uint32_t l[16];
-    } CHAR64LONG16;
-#ifdef SHA1HANDSOFF
-    CHAR64LONG16 block[1];  /* use array to appear as a pointer */
-    memcpy(block, buffer, 64);
-#else
-    /* The following had better never be used because it causes the
-     * pointer-to-const buffer to be cast into a pointer to non-const.
-     * And the result is written through.  I threw a "const" in, hoping
-     * this will cause a diagnostic.
-     */
-    CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
-#endif
-    /* Copy context->state[] to working vars */
-    a = state[0];
-    b = state[1];
-    c = state[2];
-    d = state[3];
-    e = state[4];
-    /* 4 rounds of 20 operations each. Loop unrolled. */
-    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
-    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
-    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
-    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
-    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
-    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
-    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
-    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
-    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
-    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
-    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
-    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
-    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
-    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
-    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
-    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
-    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
-    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
-    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
-    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
-    /* Add the working vars back into context.state[] */
-    state[0] += a;
-    state[1] += b;
-    state[2] += c;
-    state[3] += d;
-    state[4] += e;
-    /* Wipe variables */
-    a = b = c = d = e = 0;
-#ifdef SHA1HANDSOFF
-    memset(block, '\0', sizeof(block));
-#endif
-}
-
-
-/* SHA1Init - Initialize new context */
-
-void SHA1Init(SHA1_CTX* context)
-{
-    /* SHA1 initialization constants */
-    context->state[0] = 0x67452301;
-    context->state[1] = 0xEFCDAB89;
-    context->state[2] = 0x98BADCFE;
-    context->state[3] = 0x10325476;
-    context->state[4] = 0xC3D2E1F0;
-    context->count[0] = context->count[1] = 0;
-}
-
-
-/* Run your data through this. */
-
-void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
-{
-    uint32_t i, j;
-
-    j = context->count[0];
-    if ((context->count[0] += len << 3) < j)
-        context->count[1]++;
-    context->count[1] += (len>>29);
-    j = (j >> 3) & 63;
-    if ((j + len) > 63) {
-        memcpy(&context->buffer[j], data, (i = 64-j));
-        SHA1Transform(context->state, context->buffer);
-        for ( ; i + 63 < len; i += 64) {
-            SHA1Transform(context->state, &data[i]);
-        }
-        j = 0;
-    }
-    else i = 0;
-    memcpy(&context->buffer[j], &data[i], len - i);
-}
-
-
-/* Add padding and return the message digest. */
-
-void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
-{
-    unsigned i;
-    unsigned char finalcount[8];
-    unsigned char c;
-
-#if 0	/* untested "improvement" by DHR */
-    /* Convert context->count to a sequence of bytes
-     * in finalcount.  Second element first, but
-     * big-endian order within element.
-     * But we do it all backwards.
-     */
-    unsigned char *fcp = &finalcount[8];
-
-    for (i = 0; i < 2; i++)
-       {
-        uint32_t t = context->count[i];
-        int j;
-
-        for (j = 0; j < 4; t >>= 8, j++)
-	          *--fcp = (unsigned char) t;
-    }
-#else
-    for (i = 0; i < 8; i++) {
-        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
-         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
-    }
-#endif
-    c = 0200;
-    SHA1Update(context, &c, 1);
-    while ((context->count[0] & 504) != 448) {
-	c = 0000;
-        SHA1Update(context, &c, 1);
-    }
-    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
-    for (i = 0; i < 20; i++) {
-        digest[i] = (unsigned char)
-         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
-    }
-    /* Wipe variables */
-    memset(context, '\0', sizeof(*context));
-    memset(&finalcount, '\0', sizeof(finalcount));
-}
-/* ================ end of sha1.c ================ */
blob - bab0c44368bb83af338fd9696362713379d471f4 (mode 644)
blob + /dev/null
--- src/sha1.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef SHA1_H
-#define SHA1_H
-
-/*
- * SHA-1 in C By Steve Reid <steve@edmweb.com>
- * 100% Public Domain
- */
-
-typedef struct {
-    uint32_t state[5];
-    uint32_t count[2];
-    unsigned char buffer[64];
-} SHA1_CTX;
-
-void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
-void SHA1Init(SHA1_CTX* context);
-void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
-void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
-
-#endif		/* SHA1_H */
blob - 8ed6d72e0fa32ea9a9f7a7024703807968db0505
blob + e495103a2bb4c1a2bf2c2651d66106754e8e7ae4
--- src/testres.c
+++ src/testres.c
@@ -35,9 +35,9 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/stat.h>
+#include <parse_common.h>
 
 #include "metrics.h"
-#include "parse_common.h"
 #include "testres.h"
 #include "ui_console.h"
 #include "ui_http.h"
blob - 9a2bad9bb570ac49e9ee2267a0dc3db3fe6d900a
blob + a1277d70f7ec91de84b48b5ef142a82c7212b306
--- src/ui_console.c
+++ src/ui_console.c
@@ -27,6 +27,7 @@
  */
 
 #include "metrics.h"
+#include "testres.h"
 #include "parse_common.h"
 #include "ui_console.h"
 #include "ui_common.h"
blob - 1ba93c8ea848033593eb1be781853d04c087608a
blob + becfa9a6440abd7e18ed012cef46bd52ecc6f9c6
--- src/ui_http.c
+++ src/ui_http.c
@@ -194,3 +194,17 @@ void print_html_env() {
     }
     printf("</pre>\n");
 }
+
+int cgi_parse(char *query_string, struct config *conf) {
+	if (query_string == NULL) {
+		conf->cgi_action = (char*)"/";
+		return 0;
+        }
+	conf->cgi_action = strtok(query_string, "=");
+	conf->cgi_args = strtok(NULL, "=");
+	if (conf->cgi_action == NULL) {
+		return 1;
+	}
+
+	return 0;
+}
blob - 8653417e0381286810e03b84494672e5c2551fbf
blob + 3baccae5e830aea7458c4d9ad6d481a52fefe56f
--- src/ui_http.h
+++ src/ui_http.h
@@ -36,7 +36,8 @@ void print_html_report(struct tailq_report *report);
 void print_html_suites(struct suiteq * suites);
 void print_html_tests(struct testq * tests);
 void print_html_env();
-
 void print_plot(struct reportq *reports);
 
+int cgi_parse(char *query_string, struct config *conf);
+
 #endif				/* UI_HTTP_H */
blob - 23b8afb7d729882f888f330c997293b09127dd4c (mode 644)
blob + /dev/null
--- tests/CMakeLists.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-cmake_minimum_required (VERSION 3.9.2)
-
-set(CMAKE_C_FLAGS "-std=c99 -O0 -Wall -Wextra -Wimplicit")
-
-include(FindEXPAT)
-find_package(EXPAT REQUIRED)
-
-include_directories(${${PROJECT_NAME}_SOURCE_DIR}/src
-					${EXPAT_INCLUDE_DIRS}
-					"/usr/local/include")
-link_directories("/usr/local/lib/")
-add_executable(${PROJECT_NAME}-tests testres_tests.c)
-target_link_libraries(${PROJECT_NAME}-tests
-					${EXPAT_LIBRARIES}
-					${PROJECT_NAME}-lib
-					expat
-					cunit
-					m)
-
-add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}-tests WORKING_DIRECTORY tests/)
blob - 47ba471743272616ea98829b4f693871e6fb2b04 (mode 644)
blob + /dev/null
--- tests/testres_tests.c
+++ /dev/null
@@ -1,221 +0,0 @@
-#define _POSIX_C_SOURCE 200809L
-
-#include <stdio.h>
-#include <string.h>
-#include <arpa/inet.h>
-
-#include "CUnit/Basic.h"
-
-#include "parse_common.h"
-#include "parse_junit.h"
-#include "parse_testanything.h"
-#include "parse_subunit_v1.h"
-#include "parse_subunit_v2.h"
-#include "sha1.h"
-
-#define SAMPLE_FILE_JUNIT "samples/junit.xml"
-#define SAMPLE_FILE_SUBUNIT_V1 "samples/subunit_v1.subunit"
-#define SAMPLE_FILE_SUBUNIT_V2 "samples/subunit_v2.subunit"
-#define SAMPLE_FILE_TESTANYTHING "samples/testanything.tap"
-
-static void test_parse_testanything()
-{
-    char *name = SAMPLE_FILE_TESTANYTHING;
-    FILE *file;
-    file = fopen(name, "r");
-    CU_ASSERT_NOT_EQUAL_FATAL(file, NULL);
-    parse_testanything(file);
-    fclose(file);
-}
-
-static void test_read_subunit_v2_packet()
-{
-    // Packet sample, with test id, runnable set, status=enumeration.
-    // Spaces below are to visually break up:
-    // signature / flags / length / testid / crc32
-    // b3 2901 0c 03666f6f 08555f1b
-    // echo 03666f6f | xxd -p -r
-
-    CU_FAIL_FATAL("not implemented");
-
-    subunit_header sample_header = { .signature = 0xb3, .flags = ntohs(0x2901) };
-    uint16_t sample_length = 0x0c;
-    uint32_t sample_testid = 0x03666f6f;
-    uint32_t sample_crc32 = 0x08555f1b;
-
-    char* buf = NULL;
-    size_t buf_size = 0;
-    tailq_test * test;
-    FILE* stream = open_memstream(&buf, &buf_size);
-    fwrite(&sample_header, 1, sizeof(sample_header), stream);
-    fwrite(&sample_length, 1, sizeof(sample_length), stream);
-    fwrite(&sample_testid, 1, sizeof(sample_testid), stream);
-    fwrite(&sample_crc32, 1, sizeof(sample_crc32), stream);
-
-    test = read_subunit_v2_packet(stream);
-    fclose(stream);
-
-    CU_ASSERT_STRING_EQUAL(test->name, "");
-
-    free(buf);
-    free(test);
-}
-
-static void test_is_subunit_v2()
-{
-    char *file_subunit_v1 = SAMPLE_FILE_SUBUNIT_V1;
-    char *file_subunit_v2 = SAMPLE_FILE_SUBUNIT_V2;
-
-    CU_ASSERT(is_subunit_v2(file_subunit_v1) == 1);
-    CU_ASSERT(is_subunit_v2(file_subunit_v2) == 0);
-}
-
-static void test_parse_subunit_v2()
-{
-    CU_FAIL_FATAL("not implemented");
-
-    char *name = SAMPLE_FILE_SUBUNIT_V2;
-    FILE *file;
-
-    file = fopen(name, "r");
-    CU_ASSERT_NOT_EQUAL_FATAL(file, NULL);
-
-    struct suiteq *suites;
-    suites = parse_subunit_v2(file);
-    fclose(file);
-    free(suites);
-}
-
-static void test_parse_subunit_v1()
-{
-    char *name = SAMPLE_FILE_SUBUNIT_V1;
-    FILE *file;
-
-    file = fopen(name, "r");
-    CU_ASSERT_NOT_EQUAL_FATAL(file, NULL);
-
-    struct suiteq *suites;
-    suites = parse_subunit_v1(file);
-
-    fclose(file);
-    free(suites);
-}
-
-static void test_parse_subunit_v1_line()
-{
-	char *test_sample[] = {
-	"test test LABEL",
-	"testing test LABEL",
-	"test: test LABEL",
-	"testing: test LABEL",
-	"success test LABEL",
-	"success: test LABEL",
-	"successful test LABEL",
-	"successful: test LABEL",
-	"failure: test LABEL",
-	"failure: test LABEL DETAILS",
-	"error: test LABEL",
-	"error: test LABEL DETAILS",
-	"skip test LABEL",
-	"skip: test LABEL",
-	"skip test LABEL DETAILS",
-	"skip: test LABEL DETAILS",
-	"xfail test LABEL",
-	"xfail: test LABEL",
-	"xfail test LABEL DETAILS",
-	"xfail: test LABEL DETAILS",
-	"uxsuccess test LABEL",
-	"uxsuccess: test LABEL",
-	"uxsuccess test LABEL DETAILS",
-	"uxsuccess: test LABEL DETAILS",
-	"progress: +10",
-	"progress: -14",
-	"progress: push",
-	"progress: pop",
-	"tags: -small +big",
-	"time: 2018-09-10 23:59:29Z" };
-
-	char** qq = test_sample;
-	for (int i = 0; i <  (int)(sizeof(test_sample)/sizeof(char*)); ++i) {
-		parse_line_subunit_v1(*qq);
-		++qq;
-	}
-}
-
-static void test_parse_junit()
-{
-    FILE *file;
-    char *name = SAMPLE_FILE_JUNIT;
-
-    file = fopen(name, "r");
-    CU_ASSERT_NOT_EQUAL_FATAL(file, NULL);
-    parse_junit(file);
-    fclose(file);
-}
-
-static void test_sha1()
-{
-
-    struct {
-      char *word;
-      char *digest;
-      } tests[] = {
-        /* Test Vectors (from FIPS PUB 180-1) */
-        { "abc", "a9993e364706816aba3e25717850c26c9cd0d89d" },
-        { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1" },
-        { NULL, NULL },
-    };
-
-    int i, length = 20;
-    unsigned char digest[length];
-    char *str = calloc(length, sizeof(unsigned char*));
-    for (i = 0; tests[i].word != NULL; i++) {
-	const char *word = tests[i].word;
-        SHA1_CTX ctx;
-        SHA1Init(&ctx);
-        SHA1Update(&ctx, (const unsigned char*)word, strlen(word));
-        SHA1Final(digest, &ctx);
-		/*
-        if (digest_to_str(str, digest, length) != tests[i].digest) {
-           CU_get_error();
-        }
-		*/
-    }
-    free(str);
-}
-
-int main()
-{
-   CU_pSuite pSuite = NULL;
-
-   /* initialize the CUnit test registry */
-   if (CUE_SUCCESS != CU_initialize_registry())
-      return CU_get_error();
-
-   /* add a suite to the registry */
-   pSuite = CU_add_suite("Suite_1", NULL, NULL);
-   if (NULL == pSuite) {
-      CU_cleanup_registry();
-      return CU_get_error();
-   }
-
-   /* add the tests to the suite */
-   if ((NULL == CU_add_test(pSuite, "test_parse_junit()", test_parse_junit)) ||
-       (NULL == CU_add_test(pSuite, "test_parse_subunit_v1()", test_parse_subunit_v1)) ||
-       (NULL == CU_add_test(pSuite, "test_parse_subunit_v1_line()", test_parse_subunit_v1_line)) ||
-       (NULL == CU_add_test(pSuite, "test_parse_subunit_v2()", test_parse_subunit_v2)) ||
-       (NULL == CU_add_test(pSuite, "test_read_subunit_v2_packet()", test_read_subunit_v2_packet)) ||
-       (NULL == CU_add_test(pSuite, "test_is_subunit_v2()", test_is_subunit_v2)) ||
-       (NULL == CU_add_test(pSuite, "test_parse_testanything()", test_parse_testanything)) ||
-       (NULL == CU_add_test(pSuite, "test_sha1()", test_sha1)))
-   {
-      CU_cleanup_registry();
-      return CU_get_error();
-   }
-
-   /* Run all tests using the CUnit Basic interface */
-   CU_basic_set_mode(CU_BRM_VERBOSE);
-   CU_basic_run_tests();
-   CU_cleanup_registry();
-   return CU_get_error();
-}