Commit 7196b4dccaaa796dd96358162fdf143393162a8d

Guillem Jover 2013-05-23T02:05:19

test: Add new fgetln() and fgetwln() unit test

diff --git a/test/.gitignore b/test/.gitignore
index 1c5d1f8..8fda062 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -1,3 +1,4 @@
 endian
+fgetln
 headers
 overlay
diff --git a/test/Makefile.am b/test/Makefile.am
index 1d41b86..f36b408 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -11,6 +11,9 @@ check_PROGRAMS = \
 	headers \
 	overlay \
 	endian \
+	fgetln \
 	$(nil)
 
+fgetln_LDFLAGS = $(top_builddir)/src/libbsd.la
+
 TESTS = $(check_PROGRAMS)
diff --git a/test/fgetln.c b/test/fgetln.c
new file mode 100644
index 0000000..83d120c
--- /dev/null
+++ b/test/fgetln.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright © 2013 Guillem Jover <guillem@hadrons.org>
+ *
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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 <sys/wait.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <wchar.h>
+
+#define skip(msg) \
+	do { \
+		printf("skip: %s\n", (msg)); \
+		return; \
+	} while (0)
+
+#define DATA_LINES 3
+
+static const char *data_ascii[] = {
+	"this represents an ascii sequence of lines to test the\n",
+	"fgetln() family of functions\n",
+	"last line without an ending newline",
+};
+static const wchar_t *data_wide[] = {
+	L"this represénts an utf-8 seqüence of lînes to test the\n",
+	L"«fgetln()» family of functions § but with an extremely long "
+	 "line to test that the reallocation logic works fine, and that "
+	 "the strings end up being equal…\n",
+	L"last line ☭ without an ‽ ending newline ♥",
+};
+
+#define FILE_COUNT 32
+
+#define LINE_COUNT 2
+#define LINE_LEN 2
+
+struct file {
+	FILE *fp;
+	const void **lines;
+
+	const void *got_buf;
+	int got_len;
+};
+
+static FILE *
+pipe_feed(const char *fmt, const void **buf, int buf_nmemb)
+{
+	FILE *fp;
+	int rc;
+	int pipefd[2];
+	pid_t pid;
+
+	rc = pipe(pipefd);
+	assert(rc >= 0);
+
+	pid = fork();
+	assert(pid >= 0);
+
+	if (pid == 0) {
+		int line;
+
+		/* Child writes data to pipe. */
+		rc = close(pipefd[0]);
+		assert(rc >= 0);
+
+		fp = fdopen(pipefd[1], "w");
+		assert(fp);
+
+		for (line = 0; line < buf_nmemb; line++) {
+			rc = fprintf(fp, fmt, buf[line]);
+			assert(rc >= 0);
+		}
+
+		rc = fclose(fp);
+		assert(rc >= 0);
+
+		_exit(0);
+	} else {
+		/* Parent gets a FILE and reads from it. */
+		rc = close(pipefd[1]);
+		assert(rc >= 0);
+
+		fp = fdopen(pipefd[0], "r");
+		assert(fp);
+
+		return fp;
+	}
+}
+
+static void
+pipe_close(FILE *fp)
+{
+	fclose(fp);
+	wait(NULL);
+}
+
+static void
+test_fgetln_single(void)
+{
+	FILE *fp;
+	size_t len;
+	int i;
+
+	fp = pipe_feed("%s", (const void **)data_ascii, DATA_LINES);
+	for (i = 0; i < DATA_LINES; i++) {
+		char *str = fgetln(fp, &len);
+
+		assert(str);
+		assert(memcmp(str, data_ascii[i], len) == 0);
+	}
+	assert(fgetln(fp, &len) == NULL);
+	pipe_close(fp);
+}
+
+static void
+test_fgetln_multi(void)
+{
+	struct file files[FILE_COUNT];
+	int i, l;
+
+	for (i = 0; i < FILE_COUNT; i++) {
+		char *str;
+
+		str = strdup("A\n");
+		str[0] += i;
+
+		files[i].lines = malloc(sizeof(char *) * LINE_COUNT);
+		files[i].lines[0] = str;
+		files[i].lines[1] = str;
+		files[i].fp = pipe_feed("%s", files[i].lines, LINE_COUNT);
+	}
+
+	for (l = 0; l < LINE_COUNT; l++) {
+		for (i = 0; i < FILE_COUNT; i++) {
+			size_t len;
+			char *str;
+
+			str = fgetln(files[i].fp, &len);
+
+			assert(str);
+			assert(len == LINE_LEN);
+
+			files[i].got_len = len;
+			files[i].got_buf = str;
+		}
+
+		for (i = 0; i < FILE_COUNT; i++) {
+			assert(memcmp(files[i].lines[l], files[i].got_buf,
+			              files[i].got_len) == 0);
+		}
+	}
+
+	for (i = 0; i < LINE_COUNT; i++)
+		pipe_close(files[i].fp);
+}
+
+static void
+test_fgetwln_single(void)
+{
+	FILE *fp;
+	size_t len;
+	int i;
+
+	fp = pipe_feed("%ls", (const void **)data_wide, DATA_LINES);
+	for (i = 0; i < DATA_LINES; i++) {
+		wchar_t *wstr;
+
+		wstr = fgetwln(fp, &len);
+		assert(wstr);
+
+		assert(wmemcmp(data_wide[i], wstr, len) == 0);
+	}
+	assert(fgetwln(fp, &len) == NULL);
+	pipe_close(fp);
+}
+
+static void
+test_fgetwln_multi(void)
+{
+	struct file files[FILE_COUNT];
+	int i, l;
+
+	for (i = 0; i < FILE_COUNT; i++) {
+		wchar_t *wstr;
+
+		wstr = wcsdup(L"A\n");
+		wstr[0] += i;
+
+		files[i].lines = malloc(sizeof(char *) * LINE_COUNT);
+		files[i].lines[0] = wstr;
+		files[i].lines[1] = wstr;
+		files[i].fp = pipe_feed("%ls", files[i].lines, LINE_COUNT);
+	}
+
+	for (l = 0; l < LINE_COUNT; l++) {
+		for (i = 0; i < FILE_COUNT; i++) {
+			size_t len;
+			wchar_t *wstr;
+
+			wstr = fgetwln(files[i].fp, &len);
+
+			assert(wstr);
+			assert(len == LINE_LEN);
+
+			files[i].got_len = len;
+			files[i].got_buf = wstr;
+		}
+
+		for (i = 0; i < FILE_COUNT; i++) {
+			assert(wmemcmp(files[i].lines[l], files[i].got_buf,
+			               files[i].got_len) == 0);
+		}
+	}
+
+	for (i = 0; i < LINE_COUNT; i++)
+		pipe_close(files[i].fp);
+}
+
+static void
+test_fgetwln(void)
+{
+	if (setlocale(LC_ALL, "C.UTF-8") == NULL &&
+	    setlocale(LC_ALL, "en_US.UTF-8") == NULL)
+		skip("no default UTF-8 locale found");
+
+	test_fgetwln_single();
+	test_fgetwln_multi();
+}
+
+int
+main(int argc, char **argv)
+{
+	test_fgetln_single();
+	test_fgetln_multi();
+	test_fgetwln();
+
+	return 0;
+}