From 98a0f745cc79af11ace6416142bcc6855daeb02a Mon Sep 17 00:00:00 2001
From: Alberto Bertogli <albertito@blitiri.com.ar>
Date: Sat, 25 Aug 2012 16:59:11 +0100
Subject: [PATCH 05/18] Add tests for the posix preloader wrappers

This patch introduces some tests for selected posix preloader wrappers.

The tests are autogenerated based on a small configuration files, to make it
easier to add more later on.

Signed-off-by: Alberto Bertogli <albertito@blitiri.com.ar>
---
 .gitignore                        |    2 +
 Makefile                          |    4 +-
 tests/Makefile                    |   13 +++-
 tests/generated/Makefile          |   79 +++++++++++++++++++
 tests/generated/README            |   18 +++++
 tests/generated/generate-test     |  152 +++++++++++++++++++++++++++++++++++++
 tests/generated/tests/malloc.conf |    9 ++
 tests/generated/tests/open.conf   |   10 +++
 8 files changed, 285 insertions(+), 2 deletions(-)
 create mode 100644 tests/generated/Makefile
 create mode 100644 tests/generated/README
 create mode 100755 tests/generated/generate-test
 create mode 100644 tests/generated/tests/malloc.conf
 create mode 100644 tests/generated/tests/open.conf

diff --git a/.gitignore b/.gitignore
index 8b1881d..fa4df1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@ preload/run/build-needlibdl
 tests/*.o
 tests/build-flags
 tests/test-?
+tests/generated/build-flags
+tests/generated/tests/*.[oc]
diff --git a/Makefile b/Makefile
index bae9e6b..81405ad 100644
--- a/Makefile
+++ b/Makefile
@@ -51,7 +51,9 @@ utils_install: utils
 utils_uninstall:
 	$(MAKE) -C utils uninstall
 
-test: libfiu bindings
+tests: test
+
+test: libfiu bindings preload
 	$(MAKE) -C tests
 
 bindings: python2 python3
diff --git a/tests/Makefile b/tests/Makefile
index 40a22a9..2109ee8 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -25,7 +25,7 @@ default: tests
 
 all: tests
 
-tests: c-tests py-tests
+tests: c-tests py-tests gen-tests
 
 #
 # C tests
@@ -68,6 +68,17 @@ py-tests: $(patsubst %.py,py-run-%,$(PY_TESTS))
 py-run-%: %.py
 	$(NICE_PY) ./$<
 
+#
+# Generated tests
+#
+
+gen-tests:
+	$(MAKE) -C generated
+
+
+#
+# Cleanup
+#
 
 clean:
 	# Normally, $C_OBJS and $C_BINS are removed by make after building,
diff --git a/tests/generated/Makefile b/tests/generated/Makefile
new file mode 100644
index 0000000..201f58b
--- /dev/null
+++ b/tests/generated/Makefile
@@ -0,0 +1,79 @@
+
+CFLAGS += -std=c99 -pedantic -Wall -rdynamic
+ALL_CFLAGS = -I../../libfiu/ -L../../libfiu/ \
+	-D_XOPEN_SOURCE=600 -D_GNU_SOURCE -fPIC -DFIU_ENABLE=1 $(CFLAGS)
+
+ifdef DEBUG
+ALL_CFLAGS += -g
+endif
+
+ifdef PROFILE
+ALL_CFLAGS += -g -pg -fprofile-arcs -ftest-coverage
+endif
+
+ifneq ($(V), 1)
+	NICE_CC = @echo "  CC  $@"; $(CC)
+	NICE_RUN = @echo "  RUN $<"; \
+		   LD_LIBRARY_PATH=../../libfiu/ \
+		   LD_PRELOAD="../../preload/run/fiu_run_preload.so \
+		   	../../preload/posix/fiu_posix_preload.so"
+	NICE_GEN = @echo "  GEN $@"; ./generate-test
+else
+	NICE_CC = $(CC)
+	NICE_RUN = LD_LIBRARY_PATH=../../libfiu/ \
+		   LD_PRELOAD="../../preload/run/fiu_run_preload.so \
+		   	../../preload/posix/fiu_posix_preload.so"
+	NICE_GEN = ./generate-test
+endif
+
+default: tests
+
+all: tests
+
+
+CONF := $(wildcard tests/*.conf)
+GEN_BIN := $(patsubst %.conf,%.bin,$(CONF))
+
+tests: $(patsubst %,%-run,$(GEN_BIN))
+
+%-run: %
+	$(NICE_RUN) ./$<
+
+# .bin from .c
+%.bin: %.c build-flags
+	$(NICE_CC) $(ALL_CFLAGS) $< -lfiu -o $@
+
+# .c from .conf
+%.c: %.conf generate-test
+	$(NICE_GEN) -c $< -o $@
+
+# Useful for manually generating the C-files, that otherwise get deleted as
+# intermediates.
+c-files: $(patsubst %.conf,%.c,$(CONF))
+
+
+BF = $(ALL_CFLAGS) ~ $(PREFIX)
+build-flags: .force-build-flags
+	@if [ x"$(BF)" != x"`cat build-flags 2>/dev/null`" ]; then \
+		if [ -f build-flags ]; then \
+			echo "build flags changed, rebuilding"; \
+		fi; \
+		echo "$(BF)" > build-flags; \
+	fi
+
+
+#
+# Cleanup
+#
+
+clean:
+	rm -f $(GEN_BIN) $(patsubst %.bin,%.c,$(GEN_BIN))
+	rm -f *.bb *.bbg *.da *.gcov *.gcda *.gcno gmon.out build-flags
+
+FORCE:
+
+.PHONY: default all clean \
+	tests c-tests py-tests \
+	.force-build-flags
+
+
diff --git a/tests/generated/README b/tests/generated/README
new file mode 100644
index 0000000..6c672c1
--- /dev/null
+++ b/tests/generated/README
@@ -0,0 +1,18 @@
+
+These are autogenerated tests for testing the posix preload, to make sure the
+functions are getting properly wrapped.
+
+Each test is configured by a .conf file in the conf/ directory.
+
+These are the allowed options ('-' means mandatory, '*' optional):
+ - fp: failure point corresponding to the function.
+ - call: how to call the function.
+ * prep: preparation steps (usually declaring variables).
+ * include: list of files to #include, space separated.
+ * success_cond: condition we expect to be true if the call succeeds.
+ * failure_cond: condition we expect to be true after we simulate a failure
+	(if not set, we will only validate that we simulated the failure).
+ * errno_on_fail: simulate this errno on failure.
+
+It is mandatory that a single section is defined; however, the name is only
+for description purposes.
diff --git a/tests/generated/generate-test b/tests/generated/generate-test
new file mode 100755
index 0000000..23c9fa4
--- /dev/null
+++ b/tests/generated/generate-test
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+
+"""
+Generate C testcases based on the given configuration file.
+
+It can be useful for testing many C functions in the same way, like when
+testing a wrapper.
+"""
+
+import sys
+import ConfigParser
+import argparse
+from string import Template
+
+
+def listify(x):
+	if isinstance(x, (str, unicode)):
+		return [x]
+	return x
+
+
+TEST_SUCCESS_TMPL = r"""
+int success(void) {
+	$prep
+
+	fiu_disable("$fp");
+
+	$call
+
+	if (! ($success_cond) ) {
+		printf("$fp - success condition is false\n");
+		return -1;
+	}
+
+	return 0;
+}
+"""
+
+TEST_FAILURE_TMPL = r"""
+static int external_cb_was_called = 0;
+int external_cb(const char *name, int *failnum,
+		void **failinfo, unsigned int *flags) {
+	external_cb_was_called++;
+
+	*failinfo = (void *) $errno_on_fail;
+
+	return *failnum;
+}
+
+int failure(void) {
+	$prep
+
+	fiu_enable_external("$fp", 1, NULL, 0, external_cb);
+
+	$call
+
+	fiu_disable("$fp");
+
+	if (external_cb_was_called != 1) {
+		printf("$fp - external callback not invoked\n");
+		return -1;
+	}
+
+	if (! ($errno_cond) ) {
+		printf("$fp - errno not set appropriately: ");
+		printf("errno:%d, cond:$errno_cond\n", errno);
+		return -1;
+	}
+
+	if (! ($failure_cond) ) {
+		printf("$fp - failure condition is false\n");
+		return -1;
+	}
+
+	return 0;
+}
+"""
+
+TEST_MAIN_TMPL = r"""
+int main(void) {
+	int s, f;
+
+	s = success();
+	f = failure();
+
+	return s + f;
+}
+"""
+
+def generate(options, outfile):
+	outfile.write("/* AUTOGENERATED FILE - DO NOT EDIT */\n\n")
+	includes = options.get("include", [])
+	if isinstance(includes, (str, unicode)):
+		includes = includes.split()
+	for i in includes:
+		outfile.write("#include <%s>\n" % i)
+	else:
+		outfile.write("\n\n")
+
+	outfile.write("#include <fiu.h>\n")
+	outfile.write("#include <fiu-control.h>\n")
+	outfile.write("#include <stdio.h>\n")
+	outfile.write("#include <errno.h>\n")
+
+	if 'errno_on_fail' in options:
+		options['errno_cond'] = \
+				'errno == %s' % options['errno_on_fail']
+	else:
+		# Default the cond to true, and set failinfo to 0 in case it's
+		# used.
+		options['errno_cond'] = '1'
+		options['errno_on_fail'] = '0'
+
+	outfile.write(Template(TEST_SUCCESS_TMPL).substitute(options))
+	outfile.write(Template(TEST_FAILURE_TMPL).substitute(options))
+	outfile.write(Template(TEST_MAIN_TMPL).substitute(options))
+
+def main():
+	parser = argparse.ArgumentParser(
+			description ="Generate C testcases")
+	parser.add_argument("-c", "--conf", metavar = "C",
+			required = True, type = file,
+			help = "configuration file")
+	parser.add_argument("-o", "--out", metavar = "F",
+			required = True,
+			help = "generated file")
+
+	args = parser.parse_args()
+
+	# Defaults for the optional configuration parameters.
+	conf_defaults = {
+		'include': (),
+		'prep': '',
+
+		# These are C conditions that are always true.
+		'success_cond': '1',
+		'failure_cond': '1',
+
+		# For errno_on_fail, we have a smarter logic so don't do
+		# anything.
+	}
+
+	conf = ConfigParser.SafeConfigParser(conf_defaults)
+	conf.readfp(args.conf)
+
+	section = conf.sections()[0]
+
+	outfile = open(args.out, 'w')
+	generate(dict(conf.items(section)), outfile)
+
+if __name__ == "__main__":
+	main()
diff --git a/tests/generated/tests/malloc.conf b/tests/generated/tests/malloc.conf
new file mode 100644
index 0000000..db5f480
--- /dev/null
+++ b/tests/generated/tests/malloc.conf
@@ -0,0 +1,9 @@
+
+[malloc]
+fp: libc/mm/malloc
+include: stdlib.h
+prep: void *p = NULL;
+call: p = malloc(8000);
+success_cond: p != NULL
+failure_cond: p == NULL
+
diff --git a/tests/generated/tests/open.conf b/tests/generated/tests/open.conf
new file mode 100644
index 0000000..0bf367a
--- /dev/null
+++ b/tests/generated/tests/open.conf
@@ -0,0 +1,10 @@
+
+[open]
+fp: posix/io/oc/open
+include: sys/types.h sys/stat.h fcntl.h
+prep: int fd = -1;
+call: fd = open("/dev/null", O_RDONLY);
+success_cond: fd != -1
+failure_cond: fd == -1
+errno_on_fail: ELOOP
+
-- 
1.6.2.2.646.gb214

