From 0e714302c43baf593afe160a2a4016ed38f2922c Mon Sep 17 00:00:00 2001
From: Alberto Bertogli <albertito@blitiri.com.ar>
Date: Fri, 11 Oct 2013 01:48:13 +0100
Subject: [PATCH 05/14] tests: Introduce three new tests

This patch introduces three new tests:

 - test-manyfps.py: Test the creation of many failure points.
 - test-onetime.py: Test that we fail ONETIME points only one time.
 - test-parallel.c: Test creation and removal of failure points while checking
   them on a different thread.

The latter, in particular, stresses thread safety for most of the common code.

Signed-off-by: Alberto Bertogli <albertito@blitiri.com.ar>
---
 bindings/python/fiu.py |    7 +++
 tests/Makefile         |    2 +-
 tests/test-manyfps.py  |   25 +++++++++
 tests/test-onetime.py  |   17 ++++++
 tests/test-parallel.c  |  142 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 192 insertions(+), 1 deletions(-)
 create mode 100644 tests/test-manyfps.py
 create mode 100644 tests/test-onetime.py
 create mode 100644 tests/test-parallel.c

diff --git a/bindings/python/fiu.py b/bindings/python/fiu.py
index fda5639..0b0a308 100644
--- a/bindings/python/fiu.py
+++ b/bindings/python/fiu.py
@@ -25,6 +25,13 @@ def failinfo(name):
 	Python."""
 	return _ll.failinfo()
 
+class Flags:
+	"""Contains the valid flag constants.
+
+	ONETIME: This point of failure is disabled immediately after failing once.
+	"""
+	ONETIME = _ll.FIU_ONETIME
+
 
 # To be sure failinfo doesn't dissapear from under our feet, we keep a
 # name -> failinfo table. See fiu_ll's comments for more details.
diff --git a/tests/Makefile b/tests/Makefile
index 41d63d3..cdc478d 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -38,7 +38,7 @@ C_BINS := $(patsubst %.c,%,$(C_SRCS))
 c-tests: $(patsubst %.c,c-run-%,$(C_SRCS))
 
 test-%: test-%.o build-flags
-	$(NICE_CC) $(ALL_CFLAGS) $< -lfiu -o $@
+	$(NICE_CC) $(ALL_CFLAGS) $< -lfiu -lpthread -o $@
 
 c-run-%: %
 	$(NICE_RUN) ./$<
diff --git a/tests/test-manyfps.py b/tests/test-manyfps.py
new file mode 100644
index 0000000..5679993
--- /dev/null
+++ b/tests/test-manyfps.py
@@ -0,0 +1,25 @@
+
+"""
+Test the creation of many failure points.
+"""
+
+import fiu
+
+N = 1000
+
+for i in range(N):
+    fiu.enable(str(i))
+
+for i in range(N):
+    assert fiu.fail(str(i))
+
+# Remove only half and check again; this will stress the shrinking of our data
+# structures.
+for i in range(N / 2):
+    fiu.disable(str(i))
+
+for i in range(N / 2, N):
+    assert fiu.fail(str(i))
+
+for i in range(N / 2, N):
+    fiu.disable(str(i))
diff --git a/tests/test-onetime.py b/tests/test-onetime.py
new file mode 100644
index 0000000..182a4a8
--- /dev/null
+++ b/tests/test-onetime.py
@@ -0,0 +1,17 @@
+
+"""
+Test that we fail ONETIME points only one time.
+"""
+
+import fiu
+
+fiu.enable('p1', flags = fiu.Flags.ONETIME)
+fiu.enable('p2')
+
+assert fiu.fail('p1')
+for i in range(100):
+    assert not fiu.fail('p1')
+
+for i in range(100):
+    assert fiu.fail('p2')
+
diff --git a/tests/test-parallel.c b/tests/test-parallel.c
new file mode 100644
index 0000000..2d5a9dd
--- /dev/null
+++ b/tests/test-parallel.c
@@ -0,0 +1,142 @@
+
+/* Test creation and removal of failure points while checking them on a
+ * different thread.
+ *
+ * Please note this is a non-deterministic test. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include <fiu.h>
+#include <fiu-control.h>
+
+#define NPOINTS 10000
+
+/* How many seconds to run the test */
+#define TEST_TIME 5
+
+/* Signal for the threads to stop. */
+bool stop_threads = false;
+
+char *make_point_name(char *name, int i)
+{
+	sprintf(name, "fp-%d", i);
+	return name;
+}
+
+/* Calls all the points all the time. */
+void *no_check_caller(void *unused)
+{
+	int i;
+	char name[200];
+
+	while (!stop_threads) {
+		for (i = 0; i < NPOINTS; i++) {
+			fiu_fail(make_point_name(name, i));
+		}
+	}
+
+	return NULL;
+}
+
+bool rand_bool(void) {
+	return (rand() % 2) == 0;
+}
+
+/* Used too know if a point is enabled or not. */
+bool enabled[NPOINTS];
+pthread_mutex_t enabled_lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+
+/* Calls all the *enabled* points all the time. */
+void *checking_caller(void *unused)
+{
+	int i = 0;
+	int failed;
+	char name[200];
+
+	while (!stop_threads) {
+		pthread_mutex_lock(&enabled_lock);
+		if (enabled[i]) {
+			failed = fiu_fail(make_point_name(name, i)) != 0;
+			if (!failed) {
+				printf("ERROR: fp %d did not fail\n", i);
+				assert(false);
+			}
+		}
+		pthread_mutex_unlock(&enabled_lock);
+
+		i = (i + 1) % NPOINTS;
+	}
+
+	return NULL;
+}
+
+/* Enable and disable points all the time. */
+void *enabler(void *unused)
+{
+	int i = 0;
+	char name[200];
+
+	while (!stop_threads) {
+		pthread_mutex_lock(&enabled_lock);
+		if (rand_bool()) {
+			make_point_name(name, i);
+			if (enabled[i]) {
+				assert(fiu_disable(name) == 0);
+				enabled[i] = false;
+			} else {
+				assert(fiu_enable(name, 1, NULL, 0) == 0);
+				enabled[i] = true;
+			}
+		}
+		pthread_mutex_unlock(&enabled_lock);
+
+		i = (i + 1) % NPOINTS;
+	}
+
+	return NULL;
+}
+
+void disable_all()
+{
+	int i = 0;
+	char name[200];
+
+	pthread_mutex_lock(&enabled_lock);
+	for (i = 0; i < NPOINTS; i++) {
+		/* Note this could fail as we don't check if they're active or
+		 * not, but here we don't care. */
+		fiu_disable(make_point_name(name, i));
+	}
+	pthread_mutex_unlock(&enabled_lock);
+}
+
+int main(void)
+{
+	pthread_t t1, t2, t3;
+
+	fiu_init(0);
+
+	pthread_create(&t1, NULL, no_check_caller, NULL);
+	pthread_create(&t2, NULL, checking_caller, NULL);
+	pthread_create(&t3, NULL, enabler, NULL);
+
+	sleep(TEST_TIME);
+
+	stop_threads = 1;
+
+	pthread_join(t1, NULL);
+	pthread_join(t2, NULL);
+	pthread_join(t3, NULL);
+
+	disable_all();
+
+	return 0;
+}
+
-- 
1.6.2.2.646.gb214

