summaryrefslogtreecommitdiffstats
path: root/tests/knot/test_fdset.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/knot/test_fdset.c')
-rw-r--r--tests/knot/test_fdset.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/tests/knot/test_fdset.c b/tests/knot/test_fdset.c
new file mode 100644
index 0000000..d0282f0
--- /dev/null
+++ b/tests/knot/test_fdset.c
@@ -0,0 +1,150 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+#include <tap/basic.h>
+#include <unistd.h>
+
+#include "knot/common/fdset.h"
+#include "libknot/attribute.h"
+#include "contrib/time.h"
+
+#define PATTERN1 "0x45"
+#define PATTERN2 "0xED"
+
+#define TIMEOUT0 2000
+#define TIMEOUT1 10
+#define TIMEOUT2 400
+#define JITTER (TIMEOUT2 - TIMEOUT1 - 10)
+
+void *thr_action1(void *arg)
+{
+ usleep(1000 * TIMEOUT1);
+ _unused_ int ret = write(*((int *)arg), &PATTERN1, 1);
+ return NULL;
+}
+
+void *thr_action2(void *arg)
+{
+ usleep(1000 * TIMEOUT2);
+ _unused_ int ret = write(*((int *)arg), &PATTERN2, 1);
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ fdset_t fdset;
+ int ret = fdset_init(&fdset, 32);
+ ok(ret == KNOT_EOK, "fdset_init");
+
+ int fds0[2], fds1[2], fds2[2];
+
+ ret = pipe(fds0);
+ ok(ret >= 0, "create pipe 0");
+ ret = fdset_add(&fdset, fds0[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 0 to fdset");
+
+ ret = pipe(fds1);
+ ok(ret >= 0, "create pipe 1");
+ ret = fdset_add(&fdset, fds1[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 1 to fdset");
+
+ ret = pipe(fds2);
+ ok(ret >= 0, "create pipe 2");
+ ret = fdset_add(&fdset, fds2[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 2 to fdset");
+
+ ok(fdset_get_length(&fdset) == 3, "fdset size full");
+
+ struct timespec time0 = time_now();
+
+ pthread_t t1, t2;
+ ret = pthread_create(&t1, 0, thr_action1, &fds1[1]);
+ ok(ret == 0, "create thread 1");
+ ret = pthread_create(&t2, 0, thr_action2, &fds2[1]);
+ ok(ret == 0, "create thread 2");
+
+ fdset_it_t it;
+ ret = fdset_poll(&fdset, &it, 0, TIMEOUT0);
+ struct timespec time1 = time_now();
+ double diff1 = time_diff_ms(&time0, &time1);
+ ok(ret == 1, "fdset_poll return 1");
+ ok(diff1 >= TIMEOUT1 && diff1 < TIMEOUT1 + JITTER, "fdset_poll timeout 1 (%f)", diff1);
+ for(; !fdset_it_is_done(&it); fdset_it_next(&it)) {
+ ok(!fdset_it_is_error(&it), "fdset no error");
+ ok(fdset_it_is_pollin(&it), "fdset can read");
+
+ int fd = fdset_it_get_fd(&it);
+ ok(fd == fds1[0], "fdset_it fd check");
+
+ char buf = 0x00;
+ ret = read(fd, &buf, sizeof(buf));
+ ok(ret == 1 && buf == PATTERN1[0], "fdset_it value check");
+
+ fdset_it_remove(&it);
+ }
+ fdset_it_commit(&it);
+ ok(fdset_get_length(&fdset) == 2, "fdset size 2");
+ close(fds1[1]);
+
+ int fd2_dup = dup(fds2[0]);
+ ok(fd2_dup >= 0, "duplicate fd");
+
+ ret = fdset_poll(&fdset, &it, 0, TIMEOUT0);
+ struct timespec time2 = time_now();
+ double diff2 = time_diff_ms(&time0, &time2);
+ ok(ret == 1, "fdset_poll return 2");
+ ok(diff2 >= TIMEOUT2 && diff2 < TIMEOUT2 + JITTER, "fdset_poll timeout 2 (%f)", diff2);
+ for(; !fdset_it_is_done(&it); fdset_it_next(&it)) {
+ ok(!fdset_it_is_error(&it), "fdset no error");
+ ok(fdset_it_is_pollin(&it), "fdset can read");
+
+ int fd = fdset_it_get_fd(&it);
+ ok(fd == fds2[0], "fdset_it fd check");
+
+ char buf = 0x00;
+ ret = read(fd, &buf, sizeof(buf));
+ ok(ret == 1 && buf == PATTERN2[0], "fdset_it value check");
+
+ fdset_it_remove(&it);
+ }
+ fdset_it_commit(&it);
+ ok(fdset_get_length(&fdset) == 1, "fdset size 1");
+
+ pthread_join(t1, 0);
+ pthread_join(t2, 0);
+
+ ret = fdset_remove(&fdset, 0);
+ ok(ret == KNOT_EOK, "fdset remove");
+ close(fds0[1]);
+ ok(fdset_get_length(&fdset) == 0, "fdset size 0");
+
+ ret = write(fds2[1], &PATTERN2, 1);
+ ok(ret == 1, "write to removed fd");
+ ret = fdset_poll(&fdset, &it, 0, 100);
+ ok(ret == 0, "fdset_poll return 3");
+
+
+ close(fds2[1]);
+ if (fd2_dup >= 0) {
+ close(fd2_dup);
+ }
+ fdset_clear(&fdset);
+
+ return 0;
+}