Created
March 28, 2013 11:51
-
-
Save dvdhrm/5262589 to your computer and use it in GitHub Desktop.
linux-input self-tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <limits.h> | |
| #include <linux/input.h> | |
| #include <linux/uinput.h> | |
| #include <stdarg.h> | |
| #include <stdbool.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| /* name of the uinput device to create */ | |
| static const char *conf_devname = "kernel_selftest_device"; | |
| /* max number of /dev/input/eventX devices to test for $conf_devname */ | |
| static unsigned int conf_maxdev = 100; | |
| /* event bits for uinput device */ | |
| static __u16 *conf_bits[EV_CNT] = { | |
| [EV_KEY] = (__u16[]) { KEY_A, KEY_B, KEY_C, KEY_D, ~0 }, | |
| [EV_ABS] = (__u16[]) { ABS_X, ABS_Y, ABS_Z, ~0 }, | |
| }; | |
| /* ABS parameters for uinput device */ | |
| static const __s32 conf_absmax[ABS_CNT] = { }; | |
| static const __s32 conf_absmin[ABS_CNT] = { }; | |
| static const __s32 conf_absfuzz[ABS_CNT] = { }; | |
| static const __s32 conf_absflat[ABS_CNT] = { }; | |
| static void die(const char *format, ...) | |
| { | |
| va_list list; | |
| va_start(list, format); | |
| vfprintf(stderr, format, list); | |
| va_end(list); | |
| exit(1); | |
| } | |
| static long type2ioc(__u16 type) | |
| { | |
| switch (type) { | |
| case EV_KEY: return UI_SET_KEYBIT; | |
| case EV_REL: return UI_SET_RELBIT; | |
| case EV_ABS: return UI_SET_ABSBIT; | |
| case EV_MSC: return UI_SET_MSCBIT; | |
| case EV_LED: return UI_SET_LEDBIT; | |
| case EV_SND: return UI_SET_SNDBIT; | |
| case EV_FF: return UI_SET_FFBIT; | |
| case EV_SW: return UI_SET_SWBIT; | |
| default: return 0; | |
| } | |
| } | |
| static int uinput_create(__u16 **bits, const __s32 *absmax, | |
| const __s32 *absmin, const __s32 *absfuzz, | |
| const __s32 *absflat) | |
| { | |
| int ret, j, i, fd; | |
| struct uinput_user_dev udev; | |
| long ioc; | |
| fd = open("/dev/uinput", O_RDWR | O_CLOEXEC | O_NONBLOCK); | |
| if (fd < 0) | |
| die("cannot open uinput device (%d:%d): %m\n", fd, errno); | |
| memset(&udev, 0, sizeof(udev)); | |
| strncpy(udev.name, conf_devname, UINPUT_MAX_NAME_SIZE); | |
| udev.id.bustype = BUS_VIRTUAL; | |
| memcpy(udev.absmax, absmax, sizeof(udev.absmax)); | |
| memcpy(udev.absmin, absmin, sizeof(udev.absmin)); | |
| memcpy(udev.absfuzz, absfuzz, sizeof(udev.absfuzz)); | |
| memcpy(udev.absflat, absflat, sizeof(udev.absflat)); | |
| ret = write(fd, &udev, sizeof(udev)); | |
| if (ret != sizeof(udev)) | |
| die("cannot register uinput device (%d:%d): %m\n", ret, errno); | |
| for (j = 0; j <= EV_MAX; ++j) { | |
| if (!bits[j]) | |
| continue; | |
| ioc = type2ioc(j); | |
| if (!ioc) | |
| die("invalid uinput EV flag %d\n", j); | |
| ret = ioctl(fd, UI_SET_EVBIT, j); | |
| if (ret) | |
| die("cannot set uinput EV flag %d (%d:%d): %m\n", | |
| j, ret, errno); | |
| for (i = 0; bits[j][i] != (__u16)~0; ++i) { | |
| ret = ioctl(fd, ioc, bits[j][i]); | |
| if (ret) | |
| die("cannot set uinput bit %d:%d (%d:%d): %m\n", | |
| j, (int)bits[j][i], ret, errno); | |
| } | |
| } | |
| ret = ioctl(fd, UI_DEV_CREATE); | |
| if (ret) | |
| die("cannot create uinput device (%d:%d): %m\n", ret, errno); | |
| return fd; | |
| } | |
| static int uinput_find() | |
| { | |
| char buf[128]; | |
| unsigned int i; | |
| int fd, ret; | |
| for (i = 0; i < conf_maxdev; ++i) { | |
| sprintf(buf, "/dev/input/event%u", i); | |
| fd = open(buf, O_RDWR | O_CLOEXEC | O_NONBLOCK); | |
| if (fd < 0) | |
| continue; | |
| ret = ioctl(fd, EVIOCGNAME(sizeof(buf)), buf); | |
| if (ret > 0 && !strcmp(buf, conf_devname)) | |
| return fd; | |
| close(fd); | |
| } | |
| die("cannot find own uinput device in /etc/input/\n"); | |
| return -1; | |
| } | |
| static void test_send_one(int ufd, const struct input_event *ev) | |
| { | |
| int ret; | |
| ret = write(ufd, (void*)ev, sizeof(*ev)); | |
| if (ret != sizeof(*ev)) | |
| die("cannot send event %d:%d:%d (%d:%d): %m\n", | |
| ev->type, ev->code, ev->value, ret, errno); | |
| } | |
| static void test_send(int ufd, const struct input_event *buf, unsigned int num) | |
| { | |
| for ( ; num--; ++buf) | |
| test_send_one(ufd, buf); | |
| } | |
| static void test_recv_one(int fd, const struct input_event *ev) | |
| { | |
| int ret; | |
| struct input_event buf; | |
| ret = read(fd, (void*)&buf, sizeof(buf)); | |
| if (ret != sizeof(buf)) | |
| die("cannot recv event %d:%d:%d (%d:%d): %m\n", | |
| ev->type, ev->code, ev->value, ret, errno); | |
| if (ev->type != buf.type || | |
| ev->code != buf.code || | |
| ev->value != buf.value) | |
| die("received unexpected event %d:%d:%d != %d:%d:%d\n", | |
| buf.type, buf.code, buf.value, | |
| ev->type, ev->code, ev->value); | |
| } | |
| static void test_recv(int fd, const struct input_event *buf, unsigned int num) | |
| { | |
| for ( ; num--; ++buf) | |
| test_recv_one(fd, buf); | |
| } | |
| static void test_EVIOCGKEY(int fd, bool invalid) | |
| { | |
| unsigned char buf[(KEY_CNT - 1) / CHAR_BIT + 1]; | |
| int ret; | |
| ret = ioctl(fd, EVIOCGKEY(sizeof(buf)), invalid ? NULL : (void*)buf); | |
| if (!invalid && ret != sizeof(buf)) | |
| die("unexpected EVIOCGKEY return value %d != %d (%d): %m\n", | |
| ret, sizeof(buf), errno); | |
| } | |
| static const struct input_event ev_test1[] = { | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test1 = sizeof(ev_test1) / sizeof(*ev_test1); | |
| static const struct input_event ev_test2[] = { | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| /* 63 events up to here */ | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_D, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| /* 126 events up to here */ | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test2 = sizeof(ev_test2) / sizeof(*ev_test2); | |
| static const struct input_event ev_test3[] = { | |
| { .type = EV_SYN, .code = SYN_DROPPED }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test3 = sizeof(ev_test3) / sizeof(*ev_test3); | |
| static const struct input_event ev_test4[] = { | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test4 = sizeof(ev_test4) / sizeof(*ev_test4); | |
| static const struct input_event ev_test5[] = { | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test5 = sizeof(ev_test5) / sizeof(*ev_test5); | |
| static const struct input_event ev_test6[] = { | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_ABS, .code = ABS_X, .value = 100 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test6 = sizeof(ev_test6) / sizeof(*ev_test6); | |
| static const struct input_event ev_test7[] = { | |
| { .type = EV_ABS, .code = ABS_X, .value = 100 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test7 = sizeof(ev_test7) / sizeof(*ev_test7); | |
| static const struct input_event ev_test8[] = { | |
| { .type = EV_ABS, .code = ABS_X, .value = 1000 }, | |
| { .type = EV_ABS, .code = ABS_Y, .value = 1000 }, | |
| { .type = EV_ABS, .code = ABS_Z, .value = 1000 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_ABS, .code = ABS_X, .value = 0 }, | |
| { .type = EV_ABS, .code = ABS_Y, .value = 0 }, | |
| { .type = EV_ABS, .code = ABS_Z, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test8 = sizeof(ev_test8) / sizeof(*ev_test8); | |
| static const struct input_event ev_test9[] = { | |
| { .type = EV_ABS, .code = ABS_X, .value = 1000 }, | |
| { .type = EV_ABS, .code = ABS_Y, .value = 1000 }, | |
| { .type = EV_ABS, .code = ABS_Z, .value = 1000 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_ABS, .code = ABS_X, .value = 0 }, | |
| { .type = EV_ABS, .code = ABS_Y, .value = 0 }, | |
| { .type = EV_ABS, .code = ABS_Z, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test9 = sizeof(ev_test9) / sizeof(*ev_test9); | |
| static const struct input_event ev_test10[] = { | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| { .type = EV_SYN, .code = SYN_DROPPED }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 1 }, | |
| { .type = EV_KEY, .code = KEY_A, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_B, .value = 0 }, | |
| { .type = EV_KEY, .code = KEY_C, .value = 0 }, | |
| { .type = EV_SYN, .code = SYN_REPORT }, | |
| }; | |
| static const unsigned int num_test10 = sizeof(ev_test10) / sizeof(*ev_test10); | |
| int main(int argc, char **argv) | |
| { | |
| int ufd, fd, i; | |
| fprintf(stderr, "creating uinput device...\n"); | |
| ufd = uinput_create(conf_bits, conf_absmax, conf_absmin, | |
| conf_absfuzz, conf_absflat); | |
| fprintf(stderr, "opening own uinput device...\n"); | |
| fd = uinput_find(); | |
| fprintf(stderr, "testing device...\n"); | |
| for (i = 0; i < 10; ++i) { | |
| test_send(ufd, ev_test1, num_test1); | |
| test_recv(fd, ev_test1, num_test1); | |
| test_send(ufd, ev_test2, num_test2); | |
| test_recv(fd, ev_test3, num_test3); | |
| test_send(ufd, ev_test1, num_test1); | |
| test_EVIOCGKEY(fd, false); | |
| test_recv(fd, ev_test4, num_test4); | |
| test_send(ufd, ev_test5, num_test5); | |
| test_EVIOCGKEY(fd, false); | |
| test_recv(fd, ev_test4, num_test4); | |
| test_send(ufd, ev_test6, num_test6); | |
| test_EVIOCGKEY(fd, false); | |
| test_recv(fd, ev_test7, num_test7); | |
| test_send(ufd, ev_test8, num_test8); | |
| test_EVIOCGKEY(fd, false); | |
| test_recv(fd, ev_test9, num_test9); | |
| test_send(ufd, ev_test1, num_test1); | |
| test_send(ufd, ev_test1, num_test1); | |
| test_recv(fd, ev_test1, num_test1); | |
| test_recv(fd, ev_test1, num_test1); | |
| test_send(ufd, ev_test1, num_test1); | |
| test_EVIOCGKEY(fd, true); | |
| test_send(ufd, ev_test1, num_test1); | |
| test_recv(fd, ev_test10, num_test10); | |
| } | |
| fprintf(stderr, "exit...\n"); | |
| close(fd); | |
| close(ufd); | |
| fprintf(stderr, "tests passed\n"); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment