aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDavid Bright <dab@FreeBSD.org>2018-07-27 13:49:17 +0000
committerDavid Bright <dab@FreeBSD.org>2018-07-27 13:49:17 +0000
commit95c05062ec15cf323488d4c5e1986f5866bf7464 (patch)
treeebe8783f79d72be2806abc21685e6bee7508dc0e /tests
parent33dc803991ba1f243a36e7a91e321a0211e96677 (diff)
Notes
Diffstat (limited to 'tests')
-rw-r--r--tests/sys/kqueue/libkqueue/common.h4
-rw-r--r--tests/sys/kqueue/libkqueue/main.c19
-rw-r--r--tests/sys/kqueue/libkqueue/timer.c333
3 files changed, 346 insertions, 10 deletions
diff --git a/tests/sys/kqueue/libkqueue/common.h b/tests/sys/kqueue/libkqueue/common.h
index 89a402986021..46f6f54d81dd 100644
--- a/tests/sys/kqueue/libkqueue/common.h
+++ b/tests/sys/kqueue/libkqueue/common.h
@@ -19,6 +19,7 @@
#ifndef _COMMON_H
#define _COMMON_H
+#include "config.h" /* Needed for HAVE_* defines */
#if HAVE_ERR_H
# include <err.h>
@@ -39,8 +40,6 @@
#include <sys/event.h>
-#include "config.h"
-
extern char *cur_test_id;
int vnode_fd;
@@ -72,6 +71,7 @@ kevent_add(int kqfd, struct kevent *kev,
/* Checks if any events are pending, which is an error. */
extern void test_no_kevents(void);
+extern void test_no_kevents_quietly(void);
extern void test_begin(const char *);
extern void success(void);
diff --git a/tests/sys/kqueue/libkqueue/main.c b/tests/sys/kqueue/libkqueue/main.c
index aaf88bdc9d5a..616eb8ddd696 100644
--- a/tests/sys/kqueue/libkqueue/main.c
+++ b/tests/sys/kqueue/libkqueue/main.c
@@ -52,6 +52,25 @@ test_no_kevents(void)
}
}
+/* Checks if any events are pending, which is an error. Do not print
+ * out anything unless events are found.
+*/
+void
+test_no_kevents_quietly(void)
+{
+ int nfds;
+ struct timespec timeo;
+ struct kevent kev;
+
+ memset(&timeo, 0, sizeof(timeo));
+ nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
+ if (nfds != 0) {
+ puts("\nUnexpected event:");
+ puts(kevent_to_str(&kev));
+ errx(1, "%d event(s) pending, but none expected:", nfds);
+ }
+}
+
/* Retrieve a single kevent */
struct kevent *
kevent_get(int kqfd)
diff --git a/tests/sys/kqueue/libkqueue/timer.c b/tests/sys/kqueue/libkqueue/timer.c
index 12b324b4eef8..14167214b834 100644
--- a/tests/sys/kqueue/libkqueue/timer.c
+++ b/tests/sys/kqueue/libkqueue/timer.c
@@ -19,8 +19,58 @@
#include "common.h"
#include <sys/time.h>
+#define MILLION 1000000
+#define THOUSAND 1000
+#define SEC_TO_MS(t) ((t) * THOUSAND) /* Convert seconds to milliseconds. */
+#define SEC_TO_US(t) ((t) * MILLION) /* Convert seconds to microseconds. */
+#define MS_TO_US(t) ((t) * THOUSAND) /* Convert milliseconds to microseconds. */
+#define US_TO_NS(t) ((t) * THOUSAND) /* Convert microseconds to nanoseconds. */
+
int kqfd;
+/* Get the current time with microsecond precision. Used for
+ * sub-second timing to make some timer tests run faster.
+ */
+static long
+now(void)
+{
+
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return SEC_TO_US(tv.tv_sec) + tv.tv_usec;
+}
+
+/* Sleep for a given number of milliseconds. The timeout is assumed to
+ * be less than 1 second.
+ */
+void
+mssleep(int t)
+{
+
+ struct timespec stime = {
+ .tv_sec = 0,
+ .tv_nsec = US_TO_NS(MS_TO_US(t)),
+ };
+
+ nanosleep(&stime, NULL);
+}
+
+/* Sleep for a given number of microseconds. The timeout is assumed to
+ * be less than 1 second.
+ */
+void
+ussleep(int t)
+{
+
+ struct timespec stime = {
+ .tv_sec = 0,
+ .tv_nsec = US_TO_NS(t),
+ };
+
+ nanosleep(&stime, NULL);
+}
+
void
test_kevent_timer_add(void)
{
@@ -189,7 +239,7 @@ test_abstime(void)
kev.fflags = 0;
kevent_cmp(&kev, kevent_get(kqfd));
if (time(NULL) < when + timeout)
- err(1, "too early %jd %jd", time(), when + timeout);
+ err(1, "too early %jd %jd", time(NULL), when + timeout);
/* Check if the event occurs again */
sleep(3);
@@ -198,16 +248,283 @@ test_abstime(void)
success();
}
+static void
+test_update(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ long start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* First set the timer to 1 second */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, SEC_TO_US(1), (void *)1);
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Now reduce the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), (void *)2);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms, but less than
+ * 1 second. This check is to make sure that the original 1 second
+ * timeout was not used.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+ if (elapsed > SEC_TO_US(1))
+ errx(1, "late timer expiration: %ld us", elapsed);
+
+ success();
+}
+
+static void
+test_update_equal(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ long start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* First set the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Sleep for a significant fraction of the timeout. */
+ ussleep(600);
+
+ /* Now re-add the timer with the same parameters */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms. This check is
+ * to make sure that the timer re-started and that the event is
+ * not from the original add of the timer.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ success();
+}
+
+static void
+test_update_expired(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ long start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Set the timer to 1ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for 2 ms to give the timer plenty of time to expire. */
+ mssleep(2);
+
+ /* Now re-add the timer */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms. This check
+ * is to make sure that the timer re-started and that the event is
+ * not from the original add (and expiration) of the timer.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Make sure the re-added timer does not fire. In other words,
+ * test that the event received above was the only event from the
+ * add and re-add of the timer.
+ */
+ mssleep(2);
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_update_periodic(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)";
+ struct kevent kev;
+ long elapsed;
+ long start;
+ long stop;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Check if the event occurs again */
+ sleep(1);
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Re-add with new timeout. */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL);
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ stop = now();
+ elapsed = stop - start;
+
+ /* Check that the timer expired after at least 2 ms.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(2))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Delete the event */
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_update_timing(void)
+{
+#define MIN_SLEEP 500
+#define MAX_SLEEP 1500
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ int iteration;
+ int sleeptime;
+ long elapsed;
+ long start;
+ long stop;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Re-try the update tests with a variety of delays between the
+ * original timer activation and the update of the timer. The goal
+ * is to show that in all cases the only timer event that is
+ * received is from the update and not the original timer add.
+ */
+ for (sleeptime = MIN_SLEEP, iteration = 1;
+ sleeptime < MAX_SLEEP;
+ ++sleeptime, ++iteration) {
+
+ /* First set the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Delay; the delay ranges from less than to greater than the
+ * timer period.
+ */
+ ussleep(sleeptime);
+
+ /* Now re-add the timer with the same parameters */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ stop = now();
+ elapsed = stop - start;
+
+ /* Check that the timer expired after at least 1 ms. This
+ * check is to make sure that the timer re-started and that
+ * the event is not from the original add of the timer.
+ */
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Make sure the re-added timer does not fire. In other words,
+ * test that the event received above was the only event from
+ * the add and re-add of the timer.
+ */
+ mssleep(2);
+ test_no_kevents_quietly();
+ }
+
+ success();
+}
+
void
test_evfilt_timer()
{
kqfd = kqueue();
- test_kevent_timer_add();
- test_kevent_timer_del();
- test_kevent_timer_get();
- test_oneshot();
- test_periodic();
- test_abstime();
- disable_and_enable();
+ test_kevent_timer_add();
+ test_kevent_timer_del();
+ test_kevent_timer_get();
+ test_oneshot();
+ test_periodic();
+ test_abstime();
+ test_update();
+ test_update_equal();
+ test_update_expired();
+ test_update_timing();
+ test_update_periodic();
+ disable_and_enable();
close(kqfd);
}