Browse Source

ctest: Show test's output on error

Serj Kalichev 4 years ago
parent
commit
4bbf897d11
11 changed files with 288 additions and 29 deletions
  1. 2 1
      faux/base/Makefile.am
  2. 94 0
      faux/base/io.c
  3. 0 0
      faux/base/mem.c
  4. 5 0
      faux/faux.h
  5. 2 22
      faux/file/file.c
  6. 1 0
      faux/ini/testc_ini.c
  7. 2 1
      testc/Makefile.am
  8. 0 1
      testc/base/base.c
  9. 1 0
      testc/base/io.c
  10. 1 0
      testc/base/mem.c
  11. 180 4
      testc/testc.c

+ 2 - 1
faux/base/Makefile.am

@@ -1,3 +1,4 @@
 libfaux_la_SOURCES += \
-	faux/base/base.c
+	faux/base/mem.c \
+	faux/base/io.c
 

+ 94 - 0
faux/base/io.c

@@ -0,0 +1,94 @@
+/** @file io.c
+ * @brief Enchanced base IO functions.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+
+/** @brief Writes data to file.
+ *
+ * The system write() can be interrupted by signal or can write less bytes
+ * than specified. This function will continue to write data until all data
+ * will be written or error occured.
+ *
+ * @param [in] fd File descriptor.
+ * @param [in] buf Buffer to write.
+ * @param [in] n Number of bytes to write.
+ * @return Number of bytes written or < 0 on error.
+ */
+ssize_t faux_write(int fd, const void *buf, size_t n) {
+
+	ssize_t bytes_written = 0;
+	size_t left = n;
+	const void *data = buf;
+
+	assert(fd != -1);
+	assert(buf);
+	if ((-1 == fd) || !buf)
+		return -1;
+	if (0 == n)
+		return 0;
+
+	do {
+		bytes_written = write(fd, data, left);
+		if (bytes_written < 0) {
+			if (EINTR == errno)
+				continue;
+			return -1;
+		}
+		if (0 == bytes_written) // Insufficient space
+			return -1;
+		data += bytes_written;
+		left = left - bytes_written;
+	} while (left > 0);
+
+	return n;
+}
+
+
+/** @brief Reads data from file.
+ *
+ * The system read() can be interrupted by signal or can read less bytes
+ * than specified. This function will continue to read data until all data
+ * will be readed or error occured.
+ *
+ * @param [in] fd File descriptor.
+ * @param [in] buf Buffer to write.
+ * @param [in] n Number of bytes to write.
+ * @return Number of bytes readed or < 0 on error.
+ */
+ssize_t faux_read(int fd, void *buf, size_t n) {
+
+	ssize_t bytes_readed = 0;
+	ssize_t total_readed = 0;
+	size_t left = n;
+	void *data = buf;
+
+	assert(fd != -1);
+	assert(buf);
+	if ((-1 == fd) || !buf)
+		return -1;
+	if (0 == n)
+		return 0;
+
+	do {
+		bytes_readed = read(fd, data, left);
+		if (bytes_readed < 0) {
+			if (EINTR == errno)
+				continue;
+			if (total_readed > 0)
+				return total_readed;
+			return -1;
+		}
+		if (0 == bytes_readed) // EOF
+			return total_readed;
+		data += bytes_readed;
+		total_readed += bytes_readed;
+		left = left - bytes_readed;
+	} while (left > 0);
+
+	return total_readed;
+}

+ 0 - 0
faux/base/base.c → faux/base/mem.c


+ 5 - 0
faux/faux.h

@@ -58,11 +58,16 @@ typedef enum {
 
 C_DECL_BEGIN
 
+// Memory
 void faux_free(void *ptr);
 void *faux_malloc(size_t size);
 void faux_bzero(void *ptr, size_t size);
 void *faux_zmalloc(size_t size);
 
+// IO
+ssize_t faux_write(int fd, const void *buf, size_t n);
+ssize_t faux_read(int fd, void *buf, size_t n);
+
 C_DECL_END
 
 #endif /* _faux_types_h */

+ 2 - 22
faux/file/file.c

@@ -343,29 +343,9 @@ char *faux_file_getline(faux_file_t *f) {
  */
 ssize_t faux_file_write(faux_file_t *f, const void *buf, size_t n) {
 
-	ssize_t bytes_written = 0;
-	size_t left = n;
-	const void *data = buf;
-
 	assert(f);
-	assert(buf);
-	if (!f || !buf)
+	if (!f)
 		return -1;
-	if (0 == n)
-		return 0;
-
-	do {
-		bytes_written = write(f->fd, data, left);
-		if (bytes_written < 0) {
-			if (EINTR == errno)
-				continue;
-			return -1;
-		}
-		if (0 == bytes_written) // Insufficient space
-			return -1;
-		data += bytes_written;
-		left = left - bytes_written;
-	} while (left > 0);
 
-	return n;
+	return faux_write(f->fd, buf, n);
 }

+ 1 - 0
faux/ini/testc_ini.c

@@ -14,6 +14,7 @@ int testc_faux_ini_good(void) {
 
 int testc_faux_ini_bad(void) {
 
+	printf("Some debug information here\n");
 	return -1;
 }
 

+ 2 - 1
testc/Makefile.am

@@ -9,7 +9,8 @@ testc_testc_SOURCES = \
 # to test not-installed libraries. This LD_LIBRARY_PATH can influence testc
 # itself. So build necessary parts of faux library statically.
 testc_testc_SOURCES += \
-	testc/base/base.c \
+	testc/base/mem.c \
+	testc/base/io.c \
 	testc/ctype/ctype.c \
 	testc/str/str.c \
 	testc/list/list.c

+ 0 - 1
testc/base/base.c

@@ -1 +0,0 @@
-../../faux/base/base.c

+ 1 - 0
testc/base/io.c

@@ -0,0 +1 @@
+../../faux/base/io.c

+ 1 - 0
testc/base/mem.c

@@ -0,0 +1 @@
+../../faux/base/mem.c

+ 180 - 4
testc/testc.c

@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <assert.h>
 #include <string.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <dlfcn.h>
 #include <sys/types.h>
@@ -55,7 +56,128 @@ typedef struct opts_s opts_t;
 static opts_t *opts_parse(int argc, char *argv[]);
 static void opts_free(opts_t *opts);
 static void help(int status, const char *argv0);
-static int exec_test(int (*test_sym)(void));
+static int exec_test(int (*test_sym)(void), faux_list_t *buf_list);
+
+struct faux_chunk_s {
+	void *data;
+	size_t size;
+	size_t len;
+};
+
+typedef struct faux_chunk_s faux_chunk_t;
+
+faux_chunk_t *faux_chunk_new(size_t size) {
+
+	faux_chunk_t *chunk = NULL;
+
+	if (0 == size) // Illegal 0 size
+		return NULL;
+
+	chunk = faux_zmalloc(sizeof(*chunk));
+	if (!chunk)
+		return NULL;
+
+	// Init
+	chunk->data = faux_zmalloc(size);
+	if (!chunk->data) {
+		faux_free(chunk);
+		return NULL;
+	}
+	chunk->size = size;
+	chunk->len = 0;
+
+	return chunk;
+}
+
+
+void faux_chunk_free(faux_chunk_t *chunk) {
+
+	// Without assert()
+	if (!chunk)
+		return;
+
+	faux_free(chunk->data);
+	faux_free(chunk);
+}
+
+
+ssize_t faux_chunk_len(faux_chunk_t *chunk) {
+
+	assert(chunk);
+	if (!chunk)
+		return -1;
+
+	return chunk->len;
+}
+
+ssize_t faux_chunk_set_len(faux_chunk_t *chunk, size_t len) {
+
+	assert(chunk);
+	if (!chunk)
+		return -1;
+
+	return (chunk->len = len);
+}
+
+ssize_t faux_chunk_inc_len(faux_chunk_t *chunk, size_t inc_len) {
+
+	assert(chunk);
+	if (!chunk)
+		return -1;
+	assert((chunk->len + inc_len) <= chunk->size);
+	if ((chunk->len + inc_len) > chunk->size)
+		return -1;
+
+	return (chunk->len += inc_len);
+}
+
+ssize_t faux_chunk_dec_len(faux_chunk_t *chunk, size_t dec_len) {
+
+	assert(chunk);
+	if (!chunk)
+		return -1;
+	assert(chunk->len >= dec_len);
+	if (chunk->len < dec_len)
+		return -1;
+
+	return (chunk->len -= dec_len);
+}
+
+ssize_t faux_chunk_size(faux_chunk_t *chunk) {
+
+	assert(chunk);
+	if (!chunk)
+		return -1;
+
+	return chunk->size;
+}
+
+void *faux_chunk_data(faux_chunk_t *chunk) {
+
+	assert(chunk);
+	if (!chunk)
+		return NULL;
+
+	return chunk->data;
+}
+
+void *faux_chunk_pos(faux_chunk_t *chunk) {
+
+	assert(chunk);
+	if (!chunk)
+		return NULL;
+
+	return (chunk->data + chunk->len);
+}
+
+ssize_t faux_chunk_left(faux_chunk_t *chunk) {
+
+	assert(chunk);
+	if (!chunk)
+		return -1;
+
+	return chunk->size - chunk->len;
+}
 
 
 int main(int argc, char *argv[]) {
@@ -143,6 +265,9 @@ int main(int argc, char *argv[]) {
 			int wstatus = 0;
 			char *result_str = NULL;
 			char *attention_str = NULL;
+			faux_list_t *buf_list = NULL;
+			faux_list_node_t *iter = NULL;
+			faux_chunk_t *chunk = NULL;
 
 			test_name = (*testc_module)[0];
 			test_desc = (*testc_module)[1];
@@ -158,7 +283,9 @@ int main(int argc, char *argv[]) {
 				continue;
 			}
 
-			wstatus = exec_test(test_sym);
+			buf_list = faux_list_new(BOOL_FALSE, BOOL_FALSE, NULL, NULL, (void (*)(void *))faux_chunk_free);
+
+			wstatus = exec_test(test_sym, buf_list);
 
 			if (WIFEXITED(wstatus)) {
 				if (WEXITSTATUS(wstatus) == 0) {
@@ -184,6 +311,16 @@ int main(int argc, char *argv[]) {
 			printf("%sTest #%03u %s() %s: %s\n", attention_str, module_tests, test_name, test_desc, result_str);
 			faux_str_free(result_str);
 			faux_str_free(attention_str);
+
+			// Print test output if error
+			if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
+				iter = faux_list_head(buf_list);
+				while ((chunk = faux_list_each(&iter))) {
+					faux_write(STDOUT_FILENO, faux_chunk_data(chunk), faux_chunk_len(chunk));
+				}
+			}
+
+			faux_list_free(buf_list);
 		}
 
 
@@ -212,10 +349,41 @@ int main(int argc, char *argv[]) {
 }
 
 
-static int exec_test(int (*test_sym)(void)) {
+
+
+
+#define CHUNK_SIZE 1024
+
+static faux_list_t *read_test_output(int fd, size_t limit, faux_list_t *buf_list) {
+
+	faux_chunk_t *chunk = NULL;
+	size_t total_len = 0;
+
+	do {
+		ssize_t bytes_readed = 0;
+
+		chunk = faux_chunk_new(CHUNK_SIZE);
+		bytes_readed = faux_read(fd, faux_chunk_pos(chunk), faux_chunk_left(chunk));
+		if (bytes_readed <= 0)
+			break;
+		faux_chunk_inc_len(chunk, bytes_readed);
+		faux_list_add(buf_list, chunk);
+		total_len += faux_chunk_len(chunk);
+
+	} while((!faux_chunk_left(chunk)) && (total_len < limit));
+
+	return buf_list;
+}
+
+
+static int exec_test(int (*test_sym)(void), faux_list_t *buf_list) {
 
 	pid_t pid = -1;
 	int wstatus = -1;
+	int pipefd[2];
+
+	if (pipe(pipefd))
+		return -1;
 
 	pid = fork();
 	assert(pid != -1);
@@ -223,8 +391,16 @@ static int exec_test(int (*test_sym)(void)) {
 		return -1;
 
 	// Child
-	if (pid == 0)
+	if (pid == 0) {
+		dup2(pipefd[1], 1);
+		dup2(pipefd[1], 2);
+		close(pipefd[0]);
+		close(pipefd[1]);
 		_exit(test_sym());
+	}
+
+	close(pipefd[1]);
+	read_test_output(pipefd[0], 4096, buf_list);
 
 	// Parent
 	while (waitpid(pid, &wstatus, 0) != pid);