[tarantool-patches] [PATCH 2/6] test: introduce breakpoints for swim's event loop

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Wed Mar 20 13:49:15 MSK 2019


Breakpoint as API gives a test writer more control over timing of
condition checks. Breakpoint stops the swim's event loop in a
certain moment of virtual time.

Without breakpoints it is possible, that a condition has failed
its deadline, but it can not be checked properly. For example,
assume that there is a cluster of two members, and after 1 second
they should become fullmesh. It means, that any checks in [0, 1)
time have to fail. But without breakpoints it is not so:

    // event_queue: [round_step, 1 sec]
    // time: 0
    swim_cluster_wait_fullmesh(cluster, 0.5); // Fails.

    // event_queue: []
    // time: 1
    swim_cluster_wait_fullmesh(cluster, 0.1); // Success.

The second test should fail, but it does not, because global time
is already 1 after the first test and the cluster is in fullmesh
already. It looks weird, so such checks should be done not later
than deadline.

Follow-up for 03b9a6e91baf246ee2bb9841d01ba3824b6768a6
---
 test/unit/swim.c            |  6 ++++--
 test/unit/swim.result       |  5 +++--
 test/unit/swim_test_ev.c    | 39 +++++++++++++++++++++++++++++++++++++
 test/unit/swim_test_ev.h    |  8 ++++++++
 test/unit/swim_test_utils.c | 26 ++++++++++++++++++-------
 5 files changed, 73 insertions(+), 11 deletions(-)

diff --git a/test/unit/swim.c b/test/unit/swim.c
index 921fc8f07..3a97aeb18 100644
--- a/test/unit/swim.c
+++ b/test/unit/swim.c
@@ -50,7 +50,7 @@ static int test_result;
 static void
 swim_test_one_link(void)
 {
-	swim_start_test(1);
+	swim_start_test(2);
 	/*
 	 * Run a simple cluster of two elements. One of them
 	 * learns about another explicitly. Another should add the
@@ -58,7 +58,9 @@ swim_test_one_link(void)
 	 */
 	struct swim_cluster *cluster = swim_cluster_new(2);
 	fail_if(swim_cluster_add_link(cluster, 0, 1) != 0);
-	is(swim_cluster_wait_fullmesh(cluster, 1), 0, "one link");
+	is(swim_cluster_wait_fullmesh(cluster, 0.9), -1,
+	   "no rounds - no fullmesh");
+	is(swim_cluster_wait_fullmesh(cluster, 0.1), 0, "one link");
 	swim_cluster_delete(cluster);
 
 	swim_finish_test();
diff --git a/test/unit/swim.result b/test/unit/swim.result
index e8991d8d8..b58325b73 100644
--- a/test/unit/swim.result
+++ b/test/unit/swim.result
@@ -1,8 +1,9 @@
 	*** main_f ***
 1..5
 	*** swim_test_one_link ***
-    1..1
-    ok 1 - one link
+    1..2
+    ok 1 - no rounds - no fullmesh
+    ok 2 - one link
 ok 1 - subtests
 	*** swim_test_one_link: done ***
 	*** swim_test_sequence ***
diff --git a/test/unit/swim_test_ev.c b/test/unit/swim_test_ev.c
index ee1fcdbb7..d4a0a4752 100644
--- a/test/unit/swim_test_ev.c
+++ b/test/unit/swim_test_ev.c
@@ -55,6 +55,7 @@ static int event_id = 0;
 enum swim_event_type {
 	SWIM_EVENT_TIMER,
 	SWIM_EVENT_FD_UNBLOCK,
+	SWIM_EVENT_BRK,
 };
 
 struct swim_event;
@@ -250,6 +251,44 @@ swim_test_ev_block_fd(int fd, double delay)
 	e->fd = fd;
 }
 
+/**
+ * Breakpoint event for debug. It does nothing but stops the event
+ * loop after a timeout to allow highlevel API to check some
+ * cases.
+ */
+struct swim_brk_event {
+	struct swim_event base;
+};
+
+/** Delete a breakpoint event. */
+static void
+swim_brk_event_delete(struct swim_event *e)
+{
+	assert(e->type == SWIM_EVENT_BRK);
+	swim_event_destroy(e);
+	free(e);
+}
+
+/**
+ * Breakpoint event processing is nothing but the event deletion.
+ */
+static void
+swim_brk_event_process(struct swim_event *e, struct ev_loop *loop)
+{
+	(void) loop;
+	assert(e->type == SWIM_EVENT_BRK);
+	swim_brk_event_delete(e);
+}
+
+void
+swim_ev_set_brk(double delay)
+{
+	struct swim_brk_event *e = (struct swim_brk_event *) malloc(sizeof(*e));
+	assert(e != NULL);
+	swim_event_create(&e->base, SWIM_EVENT_BRK, delay,
+			  swim_brk_event_process, swim_brk_event_delete);
+}
+
 /** Implementation of global time visible in SWIM. */
 double
 swim_time(void)
diff --git a/test/unit/swim_test_ev.h b/test/unit/swim_test_ev.h
index 808bc510e..01a1b8868 100644
--- a/test/unit/swim_test_ev.h
+++ b/test/unit/swim_test_ev.h
@@ -51,6 +51,14 @@ swim_test_ev_free(void);
 void
 swim_test_ev_block_fd(int fd, double delay);
 
+/**
+ * Stop the event loop after @a delay fake seconds. It does not
+ * affect other events, so the loop can stop earlier multiple
+ * times.
+ */
+void
+swim_ev_set_brk(double delay);
+
 /** Play one step of event loop, process generated events. */
 void
 swim_do_loop_step(struct ev_loop *loop);
diff --git a/test/unit/swim_test_utils.c b/test/unit/swim_test_utils.c
index a92e55233..0b301333b 100644
--- a/test/unit/swim_test_utils.c
+++ b/test/unit/swim_test_utils.c
@@ -129,16 +129,28 @@ swim_cluster_is_fullmesh(struct swim_cluster *cluster)
 	return true;
 }
 
+/**
+ * A common wrapper for some conditions checking after each event
+ * loop step.
+ */
+#define swim_wait_timeout(timeout, target_cond) ({			\
+	swim_ev_set_brk(timeout);					\
+	double deadline = swim_time() + timeout;			\
+	int rc = 0;							\
+	while (! (target_cond)) {					\
+		if (swim_time() >= deadline) {				\
+			rc = -1;					\
+			break;						\
+		}							\
+		swim_do_loop_step(loop());				\
+	}								\
+	rc;								\
+})
+
 int
 swim_cluster_wait_fullmesh(struct swim_cluster *cluster, double timeout)
 {
-	double deadline = swim_time() + timeout;
-	while (! swim_cluster_is_fullmesh(cluster)) {
-		if (swim_time() >= deadline)
-			return -1;
-		swim_do_loop_step(loop());
-	}
-	return 0;
+	return swim_wait_timeout(timeout, swim_cluster_is_fullmesh(cluster));
 }
 
 bool
-- 
2.17.2 (Apple Git-113)





More information about the Tarantool-patches mailing list