aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile20
-rw-r--r--tests/crypt/.gitignore1
-rw-r--r--tests/crypt/GNUmakefile7
-rw-r--r--tests/crypt/Makefile36
-rw-r--r--tests/crypt/README.md8
-rw-r--r--tests/crypt/run-test.c38
-rw-r--r--tests/crypt/test.h32
-rw-r--r--tests/crypt/test_hmac_md5.c209
-rw-r--r--tests/eloop-bench/.gitignore1
-rw-r--r--tests/eloop-bench/Makefile45
-rw-r--r--tests/eloop-bench/README.md53
-rw-r--r--tests/eloop-bench/eloop-bench.c184
12 files changed, 634 insertions, 0 deletions
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 000000000000..16a229ff47af
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,20 @@
+SUBDIRS= crypt eloop-bench
+
+all:
+ for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done
+
+install:
+
+proginstall:
+
+clean:
+ for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done
+
+distclean: clean
+ rm -f *.diff *.patch *.orig *.rej
+ for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done
+
+test:
+ for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done
+
+tests: test
diff --git a/tests/crypt/.gitignore b/tests/crypt/.gitignore
new file mode 100644
index 000000000000..4390dc850728
--- /dev/null
+++ b/tests/crypt/.gitignore
@@ -0,0 +1 @@
+run-test
diff --git a/tests/crypt/GNUmakefile b/tests/crypt/GNUmakefile
new file mode 100644
index 000000000000..2e838d5e7283
--- /dev/null
+++ b/tests/crypt/GNUmakefile
@@ -0,0 +1,7 @@
+# GNU Make does not automagically include .depend
+# Luckily it does read GNUmakefile over Makefile so we can work around it
+
+include Makefile
+ifneq ($(wildcard .depend), )
+include .depend
+endif
diff --git a/tests/crypt/Makefile b/tests/crypt/Makefile
new file mode 100644
index 000000000000..dd676f4b2aa9
--- /dev/null
+++ b/tests/crypt/Makefile
@@ -0,0 +1,36 @@
+TOP= ../..
+include ${TOP}/iconfig.mk
+
+PROG= run-test
+SRCS= run-test.c
+SRCS+= test_hmac_md5.c
+
+CFLAGS?= -O2
+CSTD?= c99
+CFLAGS+= -std=${CSTD}
+
+CPPFLAGS+= -I${TOP} -I${TOP}/src
+
+PCRYPT_SRCS= ${CRYPT_SRCS:compat/%=${TOP}/compat/%}
+OBJS+= ${SRCS:.c=.o} ${PCRYPT_SRCS:.c=.o}
+
+.c.o:
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+all: ${PROG}
+
+clean:
+ rm -f ${OBJS} ${PROG} ${PROG}.core ${CLEANFILES}
+
+distclean: clean
+ rm -f .depend
+ rm -f *.diff *.patch *.orig *.rej
+
+.depend: ${SRCS} ${PCRYPT_SRCS}
+ ${CC} ${CPPFLAGS} -MM ${SRCS} ${PCRYPT_SRCS}
+
+${PROG}: ${DEPEND} ${OBJS}
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
+
+test: ${PROG}
+ ./${PROG}
diff --git a/tests/crypt/README.md b/tests/crypt/README.md
new file mode 100644
index 000000000000..8269e51fee56
--- /dev/null
+++ b/tests/crypt/README.md
@@ -0,0 +1,8 @@
+# dhcpcd Test Suite
+
+Currently this just tests the RFC2202 MD5 implementation in dhcpcd.
+This is important, because dhcpcd will either use the system MD5
+implementation if found, otherwise some compat code.
+
+This test suit ensures that it works in accordance with known standards
+on your platform.
diff --git a/tests/crypt/run-test.c b/tests/crypt/run-test.c
new file mode 100644
index 000000000000..42f52d1fce2d
--- /dev/null
+++ b/tests/crypt/run-test.c
@@ -0,0 +1,38 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "test.h"
+
+int main(void)
+{
+ int r = 0;
+
+ if (test_hmac_md5())
+ r = -1;
+
+ return r;
+}
diff --git a/tests/crypt/test.h b/tests/crypt/test.h
new file mode 100644
index 000000000000..4f716f1220f4
--- /dev/null
+++ b/tests/crypt/test.h
@@ -0,0 +1,32 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef TEST_H
+
+int test_hmac_md5(void);
+
+#endif
diff --git a/tests/crypt/test_hmac_md5.c b/tests/crypt/test_hmac_md5.c
new file mode 100644
index 000000000000..aed6345d5707
--- /dev/null
+++ b/tests/crypt/test_hmac_md5.c
@@ -0,0 +1,209 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "test.h"
+
+#ifdef HAVE_HMAC_H
+#include <hmac.h>
+#endif
+
+static void
+print_hmac(FILE *stream, const uint8_t *hmac)
+{
+ int i;
+
+ fprintf(stream, "digest = 0x");
+ for (i = 0; i < 16; i++)
+ fprintf(stream, "%02x", *hmac++);
+ fprintf(stream, "\n");
+}
+
+static void
+test_hmac(const uint8_t *hmac, const uint8_t *tst)
+{
+ print_hmac(stdout, hmac);
+ if (memcmp(hmac, tst, 16) == 0)
+ return;
+ fprintf(stderr, "FAILED!\nExpected\t\t\t");
+ print_hmac(stderr, tst);
+ exit(EXIT_FAILURE);
+}
+
+static void
+hmac_md5_test1(void)
+{
+ const uint8_t text[] = "Hi There";
+ uint8_t key[16];
+ const uint8_t expect[16] = {
+ 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,
+ 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d,
+ };
+ uint8_t digest[16];
+ int i;
+
+ printf ("HMAC MD5 Test 1:\t\t");
+ for (i = 0; i < 16; i++)
+ key[i] = 0x0b;
+ hmac("md5", key, 16, text, 8, digest, sizeof(digest));
+ test_hmac(digest, expect);
+}
+
+static void
+hmac_md5_test2(void)
+{
+ const uint8_t text[] = "what do ya want for nothing?";
+ const uint8_t key[] = "Jefe";
+ const uint8_t expect[16] = {
+ 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,
+ 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38,
+ };
+ uint8_t digest[16];
+
+ printf("HMAC MD5 Test 2:\t\t");
+ hmac("md5", key, 4, text, 28, digest, sizeof(digest));
+ test_hmac(digest, expect);
+}
+
+static void
+hmac_md5_test3(void)
+{
+ const uint8_t expect[16] = {
+ 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,
+ 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6,
+ };
+ uint8_t digest[16];
+ uint8_t text[50];
+ uint8_t key[16];
+ int i;
+
+ printf ("HMAC MD5 Test 3:\t\t");
+ for (i = 0; i < 50; i++)
+ text[i] = 0xdd;
+ for (i = 0; i < 16; i++)
+ key[i] = 0xaa;
+ hmac("md5", key, 16, text, 50, digest, sizeof(digest));
+ test_hmac(digest, expect);
+}
+
+static void
+hmac_md5_test4(void)
+{
+ const uint8_t expect[16] = {
+ 0x69, 0x7e, 0xaf, 0x0a, 0xca, 0x3a, 0x3a, 0xea,
+ 0x3a, 0x75, 0x16, 0x47, 0x46, 0xff, 0xaa, 0x79,
+ };
+ uint8_t digest[16];
+ uint8_t text[50];
+ uint8_t key[25];
+ uint8_t i;
+
+ printf ("HMAC MD5 Test 4:\t\t");
+ for (i = 0; i < 50; i++)
+ text[i] = 0xcd;
+ for (i = 0; i < 25; i++)
+ key[i] = (uint8_t)(i + 1);
+ hmac("md5", key, 25, text, 50, digest, sizeof(digest));
+ test_hmac(digest, expect);
+}
+
+static void
+hmac_md5_test5(void)
+{
+ const uint8_t text[] = "Test With Truncation";
+ const uint8_t expect[] = {
+ 0x56, 0x46, 0x1e, 0xf2, 0x34, 0x2e, 0xdc, 0x00,
+ 0xf9, 0xba, 0xb9, 0x95, 0x69, 0x0e, 0xfd, 0x4c,
+ };
+ uint8_t digest[16];
+ uint8_t key[16];
+ int i;
+
+ printf ("HMAC MD5 Test 5:\t\t");
+ for (i = 0; i < 16; i++)
+ key[i] = 0x0c;
+ hmac("md5", key, 16, text, 20, digest, sizeof(digest));
+ test_hmac(digest, expect);
+}
+
+static void
+hmac_md5_test6(void)
+{
+ const uint8_t text[] = "Test Using Larger Than Block-Size Key - Hash Key First";
+ const uint8_t expect[] = {
+ 0x6b, 0x1a, 0xb7, 0xfe, 0x4b, 0xd7, 0xbf, 0x8f,
+ 0x0b, 0x62, 0xe6, 0xce, 0x61, 0xb9, 0xd0, 0xcd,
+ };
+ uint8_t digest[16];
+ uint8_t key[80];
+ int i;
+
+ printf ("HMAC MD5 Test 6:\t\t");
+ for (i = 0; i < 80; i++)
+ key[i] = 0xaa;
+ hmac("md5", key, 80, text, 54, digest, sizeof(digest));
+ test_hmac(digest, expect);
+}
+
+static void
+hmac_md5_test7(void)
+{
+ const uint8_t text[] = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data";
+ const uint8_t expect[] = {
+ 0x6f, 0x63, 0x0f, 0xad, 0x67, 0xcd, 0xa0, 0xee,
+ 0x1f, 0xb1, 0xf5, 0x62, 0xdb, 0x3a, 0xa5, 0x3e,
+ };
+ uint8_t digest[16];
+ uint8_t key[80];
+ int i;
+
+ printf ("HMAC MD5 Test 7:\t\t");
+ for (i = 0; i < 80; i++)
+ key[i] = 0xaa;
+ hmac("md5", key, 80, text, 73, digest, sizeof(digest));
+ test_hmac(digest, expect);
+}
+
+int test_hmac_md5(void)
+{
+
+ printf ("Starting RFC2202 HMAC MD5 tests...\n\n");
+ hmac_md5_test1();
+ hmac_md5_test2();
+ hmac_md5_test3();
+ hmac_md5_test4();
+ hmac_md5_test5();
+ hmac_md5_test6();
+ hmac_md5_test7();
+ printf("\nAll tests pass.\n");
+ return 0;
+}
diff --git a/tests/eloop-bench/.gitignore b/tests/eloop-bench/.gitignore
new file mode 100644
index 000000000000..346bbde39b13
--- /dev/null
+++ b/tests/eloop-bench/.gitignore
@@ -0,0 +1 @@
+eloop-bench
diff --git a/tests/eloop-bench/Makefile b/tests/eloop-bench/Makefile
new file mode 100644
index 000000000000..2827c6070422
--- /dev/null
+++ b/tests/eloop-bench/Makefile
@@ -0,0 +1,45 @@
+TOP= ../..
+include ${TOP}/iconfig.mk
+
+PROG= eloop-bench
+SRCS= eloop-bench.c
+SRCS+= ${TOP}/src/eloop.c
+
+CFLAGS?= -O2
+CSTD?= c99
+CFLAGS+= -std=${CSTD}
+
+#CPPFLAGS+= -DNO_CONFIG_H
+#CPPFLAGS+= -DQUEUE_H=../compat/queue.h
+CPPFLAGS+= -I${TOP} -I${TOP}/src
+
+# Default is to let eloop decide
+#CPPFLAGS+= -DHAVE_KQUEUE
+#CPPFLAGS+= -DHAVE_POLLTS
+#CPPFLAGS+= -DHAVE_PSELECT
+#CPPFLAGS+= -DHAVE_EPOLL
+#CPPFLAGS+= -DHAVE_PPOLL
+CPPFLAGS+= -DWARN_SELECT
+
+PCOMPAT_SRCS= ${COMPAT_SRCS:compat/%=${TOP}/compat/%}
+OBJS+= ${SRCS:.c=.o} ${PCOMPAT_SRCS:.c=.o}
+
+.c.o: Makefile
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+all: ${PROG}
+
+clean:
+ rm -f ${OBJS} ${PROG} ${PROG}.core ${CLEANFILES}
+
+distclean: clean
+ rm -f .depend
+ rm -f *.diff *.patch *.orig *.rej
+
+depend:
+
+${PROG}: ${DEPEND} ${OBJS}
+ ${CC} ${LDFLAGS} -o $@ ${OBJS}
+
+test: ${PROG}
+ ./${PROG}
diff --git a/tests/eloop-bench/README.md b/tests/eloop-bench/README.md
new file mode 100644
index 000000000000..6f4a20c7ea81
--- /dev/null
+++ b/tests/eloop-bench/README.md
@@ -0,0 +1,53 @@
+# eloop-bench
+
+eloop is a portable event loop designed to be dropped into the code of a
+program. It is not in any library to date.
+The basic requirement of eloop is a descriptor polling mechanism which
+allows the safe delivery of signals.
+As such, select(2) and poll(2) are not suitable.
+
+This is an eloop benchmark to test the performance of the various
+polling mechanisms. It's inspired by libevent/bench.
+
+eloop needs to be compiled for a specific polling mechanism.
+eloop will try and work out which one to use, but you can influence which one
+by giving one of these CPPFLAGS to the Makefile:
+ * `HAVE_KQUEUE`
+ * `HAVE_EPOLL`
+ * `HAVE_PSELECT`
+ * `HAVE_POLLTS`
+ * `HAVE_PPOLL`
+
+kqueue(2) is found on modern BSD kernels.
+epoll(7) is found on modern Linux and Solaris kernels.
+These two *should* be the best performers.
+
+pselect(2) *should* be found on any POSIX libc.
+This *should* be the worst performer.
+
+pollts(2) and ppoll(2) are NetBSD and Linux specific variants on poll(2),
+but allow safe signal delivery like pselect(2).
+Aside from the function name, the arguments and functionality are identical.
+They are of little use as both platforms have kqueue(2) and epoll(2),
+but there is an edge case where system doesn't have epoll(2) compiled hence
+it's inclusion here.
+
+## using eloop-bench
+
+The benchmark runs by setting up npipes to read/write to and attaching
+an eloop callback for each pipe reader.
+Once setup, it will perform a run by writing to nactive pipes.
+For each successful pipe read, if nwrites >0 then the reader will reduce
+nwrites by one on successful write back to itself.
+Once nwrites is 0, the timed run will end once the last write has been read.
+At the end of run, the time taken in seconds and nanoseconds is printed.
+
+The following arguments can influence the benchmark:
+ * `-a active`
+ The number of active pipes, default 1.
+ * `-n pipes`
+ The number of pipes to create and attach an eloop callback to, defalt 100.
+ * `-r runs`
+ The number of timed runs to make, default 25.
+ * `-w writes`
+ The number of writes to make by the read callback, default 100.
diff --git a/tests/eloop-bench/eloop-bench.c b/tests/eloop-bench/eloop-bench.c
new file mode 100644
index 000000000000..0f90e824c3ad
--- /dev/null
+++ b/tests/eloop-bench/eloop-bench.c
@@ -0,0 +1,184 @@
+/*
+ * eloop benchmark
+ * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/resource.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "eloop.h"
+
+#ifndef timespecsub
+#define timespecsub(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec < 0) { \
+ (vsp)->tv_sec--; \
+ (vsp)->tv_nsec += 1000000000L; \
+ } \
+ } while (/* CONSTCOND */ 0)
+#endif
+
+struct pipe {
+ int fd[2];
+};
+
+static size_t good, bad, writes, fired;
+static size_t npipes = 100, nwrites = 100, nactive = 1;
+static struct pipe *pipes;
+static struct eloop *e;
+
+static void
+read_cb(void *arg)
+{
+ struct pipe *p = arg;
+ unsigned char buf[1];
+
+ if (read(p->fd[0], buf, 1) != 1) {
+ warn("%s: read", __func__);
+ bad++;
+ } else
+ good++;
+
+ if (writes != 0) {
+ writes--;
+ if (write(p->fd[1], "e", 1) != 1) {
+ warn("%s: write", __func__);
+ bad++;
+ } else
+ fired++;
+ }
+
+ if (writes == 0 && fired == good) {
+ //printf("fired %zu, good %zu, bad %zu\n", fired, good, bad);
+ eloop_exit(e, good == fired && bad == 0 ?
+ EXIT_SUCCESS : EXIT_FAILURE);
+ }
+}
+
+static int
+runone(struct timespec *t)
+{
+ size_t i;
+ struct pipe *p;
+ struct timespec ts, te;
+ int result;
+
+ writes = nwrites;
+ fired = good = 0;
+
+ for (i = 0, p = pipes; i < nactive; i++, p++) {
+ if (write(p->fd[1], "e", 1) != 1)
+ err(EXIT_FAILURE, "send");
+ writes--;
+ fired++;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+ err(EXIT_FAILURE, "clock_gettime");
+ result = eloop_start(e, NULL);
+ if (clock_gettime(CLOCK_MONOTONIC, &te) == -1)
+ err(EXIT_FAILURE, "clock_gettime");
+
+ timespecsub(&te, &ts, t);
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, result, exit_code;
+ size_t i, nruns = 25;
+ struct pipe *p;
+ struct timespec ts, te, t;
+
+ while ((c = getopt(argc, argv, "a:n:r:w:")) != -1) {
+ switch (c) {
+ case 'a':
+ nactive = (size_t)atoi(optarg);
+ break;
+ case 'n':
+ npipes = (size_t)atoi(optarg);
+ break;
+ case 'r':
+ nruns = (size_t)atoi(optarg);
+ break;
+ case 'w':
+ nwrites = (size_t)atoi(optarg);
+ break;
+ default:
+ errx(EXIT_FAILURE, "illegal argument `%c'", c);
+ }
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+ err(EXIT_FAILURE, "clock_gettime");
+
+ if ((e = eloop_new()) == NULL)
+ err(EXIT_FAILURE, "eloop_init");
+
+ if (nactive > npipes)
+ nactive = npipes;
+
+ pipes = calloc(npipes, sizeof(*p));
+ if (pipes == NULL)
+ err(EXIT_FAILURE, "malloc");
+
+ for (i = 0, p = pipes; i < npipes; i++, p++) {
+ if (pipe2(p->fd, O_CLOEXEC | O_NONBLOCK) == -1)
+ err(EXIT_FAILURE, "pipe");
+ if (eloop_event_add(e, p->fd[0], read_cb, p) == -1)
+ err(EXIT_FAILURE, "eloop_event_add");
+ }
+
+ printf("active = %zu, pipes = %zu, runs = %zu, writes = %zu\n",
+ nactive, npipes, nruns, nwrites);
+
+ exit_code = EXIT_SUCCESS;
+ for (i = 0; i < nruns; i++) {
+ result = runone(&t);
+ if (result != EXIT_SUCCESS)
+ exit_code = result;
+ printf("run %zu took %lld.%.9ld seconds, result %d\n",
+ i + 1, (long long)t.tv_sec, t.tv_nsec, result);
+ }
+
+ eloop_free(e);
+ free(pipes);
+
+ if (clock_gettime(CLOCK_MONOTONIC, &te) == -1)
+ err(EXIT_FAILURE, "clock_gettime");
+ timespecsub(&te, &ts, &t);
+ printf("total %lld.%.9ld seconds, result %d\n",
+ (long long)t.tv_sec, t.tv_nsec, exit_code);
+ exit(exit_code);
+}