From d6522be9b5a92491fb0902d8f75a8ec5bdf0e1db Mon Sep 17 00:00:00 2001 From: Bowen Wang Date: Wed, 1 Nov 2023 12:44:28 +0800 Subject: [PATCH] testing/epoll: add epoll test case This commid add an epoll testcase to test the epoll Test case 1: epoll_wait timeout test. Test case 2: epoll_wait with driver call poll_notify() many times simultaneously. Test case 3: epoll_wait with some driver/socket need call poll_setup() again when it's internal state changed. Signed-off-by: Bowen Wang --- testing/epoll/Kconfig | 23 ++ testing/epoll/Make.defs | 23 ++ testing/epoll/Makefile | 29 +++ testing/epoll/epoll.c | 488 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 563 insertions(+) create mode 100644 testing/epoll/Kconfig create mode 100644 testing/epoll/Make.defs create mode 100644 testing/epoll/Makefile create mode 100644 testing/epoll/epoll.c diff --git a/testing/epoll/Kconfig b/testing/epoll/Kconfig new file mode 100644 index 000000000..1249f5c3e --- /dev/null +++ b/testing/epoll/Kconfig @@ -0,0 +1,23 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config TESTING_EPOLL + tristate "cmocka epoll test" + default n + depends on TESTING_CMOCKA && TIMER_FD && NET_LOCAL + ---help--- + Enable the cmocka driver test + +if TESTING_EPOLL + +config TESTING_EPOLL_PRIORITY + int "Task priority" + default 100 + +config TESTING_EPOLL_STACKSIZE + int "Stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/testing/epoll/Make.defs b/testing/epoll/Make.defs new file mode 100644 index 000000000..00467a4f9 --- /dev/null +++ b/testing/epoll/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/testing/epoll/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_TESTING_EPOLL),) +CONFIGURED_APPS += $(APPDIR)/testing/epoll +endif diff --git a/testing/epoll/Makefile b/testing/epoll/Makefile new file mode 100644 index 000000000..6d79215cc --- /dev/null +++ b/testing/epoll/Makefile @@ -0,0 +1,29 @@ +############################################################################ +# apps/testing/epoll/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PRIORITY = $(CONFIG_TESTING_EPOLL_PRIORITY) +STACKSIZE = $(CONFIG_TESTING_EPOLL_STACKSIZE) +MODULE = $(CONFIG_TESTING_EPOLL) +MAINSRC += epoll.c +PROGNAME += cmocka_epoll + +include $(APPDIR)/Application.mk diff --git a/testing/epoll/epoll.c b/testing/epoll/epoll.c new file mode 100644 index 000000000..ad6ab09fe --- /dev/null +++ b/testing/epoll/epoll.c @@ -0,0 +1,488 @@ +/**************************************************************************** + * apps/testing/epoll/epoll.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TIME_ERROR_MS (MSEC_PER_TICK * 2) +#define TIME_ASSERT_RANGE(e,t) \ + assert_in_range(e, (t) - TIME_ERROR_MS, (t) + TIME_ERROR_MS) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct epoll_args_s +{ + int efd; + struct epoll_event ev; + int fd[2]; + unsigned long start; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct sockaddr_un g_epoll03_addr = +{ + .sun_family = AF_UNIX, + .sun_path = "epoll_test03" +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: show_usage + ****************************************************************************/ + +static void show_usage(FAR const char *progname, int exitcode) +{ + printf("Usage: %s ?\n", progname); + printf(" This is an epoll testcase to test the epoll.\n"); + printf(" Test case 1: epoll_wait timeout test.\n"); + printf(" Test case 2: epoll_wait with driver call poll_notify()\n"); + printf(" many times simultaneously.\n"); + printf(" Test case 3: epoll_wait with some driver/socket need call\n"); + printf(" poll_setup() again when it's internal state\n"); + printf(" changed.\n"); + exit(exitcode); +} + +/**************************************************************************** + * Name: get_time_ms + ****************************************************************************/ + +static unsigned long get_time_ms(void) +{ + struct timespec tm; + + clock_gettime(CLOCK_MONOTONIC, &tm); + return tm.tv_sec * MSEC_PER_SEC + tm.tv_nsec / NSEC_PER_MSEC; +} + +/**************************************************************************** + * Name: get_elapse + ****************************************************************************/ + +static unsigned long get_elapse(unsigned long start) +{ + return get_time_ms() - start; +} + +/**************************************************************************** + * Name: timerfd_stop + ****************************************************************************/ + +static int timerfd_stop(int fd) +{ + struct itimerspec tms; + + tms.it_value.tv_sec = 0; + tms.it_value.tv_nsec = 0; + return timerfd_settime(fd, 0, &tms, NULL); +} + +/**************************************************************************** + * Name: epoll01_setup + ****************************************************************************/ + +static int epoll01_setup(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + struct itimerspec tms; + int ret; + int fd; + + /* Create timer fd */ + + fd = timerfd_create(CLOCK_MONOTONIC, 0); + assert_true(fd >= 0); + args->fd[0] = fd; + + /* Create epoll fd */ + + fd = epoll_create(1); + assert_true(fd >= 0); + args->efd = fd; + + args->ev.events = EPOLLIN; + args->ev.data.fd = args->fd[0]; + ret = epoll_ctl(fd, EPOLL_CTL_ADD, args->fd[0], &args->ev); + assert_true(ret >= 0); + + /* Start the timer */ + + tms.it_value.tv_sec = 1; + tms.it_value.tv_nsec = 0; + tms.it_interval.tv_sec = 0; + tms.it_interval.tv_nsec = 0; + args->start = get_time_ms(); + ret = timerfd_settime(args->fd[0], 0, &tms, NULL); + assert_true(ret >= 0); + + return 0; +} + +/**************************************************************************** + * Name: epoll01 + ****************************************************************************/ + +static void epoll01(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + struct epoll_event evs; + int ret; + + /* 1st wait should timeout, timer is 1s */ + + ret = epoll_wait(args->efd, &evs, 1, 700); + TIME_ASSERT_RANGE(get_elapse(args->start), 700); + assert_true(ret == 0); + + /* 2nd wait should success and return 1 at 1s */ + + ret = epoll_wait(args->efd, &evs, 1, 700); + TIME_ASSERT_RANGE(get_elapse(args->start), 1000); + assert_true(ret == 1); + assert_true(evs.data.fd = args->fd[0]); + + ret = timerfd_stop(args->fd[0]); + assert_true(ret == 0); + + /* 3rd wait should directly return 0, timer has stoped */ + + ret = epoll_wait(args->efd, &evs, 1, 0); + TIME_ASSERT_RANGE(get_elapse(args->start), 1000); + assert_true(ret == 0); +} + +/**************************************************************************** + * Name: epoll01_teardown + ****************************************************************************/ + +static int epoll01_teardown(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + int ret; + + ret = epoll_ctl(args->efd, EPOLL_CTL_DEL, args->fd[0], &args->ev); + assert_true(ret >= 0); + + close(args->efd); + close(args->fd[0]); + + return 0; +} + +/**************************************************************************** + * Name: epoll02_setup + ****************************************************************************/ + +static int epoll02_setup(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + struct itimerspec tms; + int ret; + int fd; + int i; + + /* Create epoll fd */ + + fd = epoll_create(2); + assert_true(fd >= 0); + args->efd = fd; + + for (i = 0; i < 2; i++) + { + fd = timerfd_create(CLOCK_MONOTONIC, 0); + assert_true(fd >= 0); + args->fd[i] = fd; + + args->ev.events = EPOLLIN; + args->ev.data.fd = fd; + ret = epoll_ctl(args->efd, EPOLL_CTL_ADD, fd, &args->ev); + assert_true(ret >= 0); + } + + /* Start timer */ + + tms.it_value.tv_sec = 1; + tms.it_value.tv_nsec = 0; + tms.it_interval.tv_sec = 0; + tms.it_interval.tv_nsec = 0; + args->start = get_time_ms(); + for (i = 0; i < 2; i++) + { + ret = timerfd_settime(args->fd[i], 0, &tms, NULL); + assert_true(ret >= 0); + } + + return 0; +} + +/**************************************************************************** + * Name: epoll02 + ****************************************************************************/ + +static void epoll02(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + struct epoll_event evs[2]; + int ret; + + /* 1st wait, shoud return after 1000ms */ + + ret = epoll_wait(args->efd, evs, 2, 1100); + TIME_ASSERT_RANGE(get_elapse(args->start), 1000); + assert_true(ret == 2); + assert_true((evs[0].data.fd == args->fd[0] && + evs[1].data.fd == args->fd[1]) || + (evs[0].data.fd == args->fd[1] && + evs[1].data.fd == args->fd[0])); + + /* Stop the timers */ + + ret = timerfd_stop(args->fd[0]); + assert_true(ret >= 0); + ret = timerfd_stop(args->fd[1]); + assert_true(ret >= 0); + + /* 2nd wait, should timeout until 200ms */ + + args->start = get_time_ms(); + ret = epoll_wait(args->efd, evs, 2, 200); + TIME_ASSERT_RANGE(get_elapse(args->start), 200); + assert_true(ret == 0); +} + +/**************************************************************************** + * Name: epoll02_teardown + ****************************************************************************/ + +static int epoll02_teardown(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + int ret; + + ret = epoll_ctl(args->efd, EPOLL_CTL_DEL, args->fd[0], &args->ev); + assert_true(ret >= 0); + ret = epoll_ctl(args->efd, EPOLL_CTL_DEL, args->fd[1], &args->ev); + assert_true(ret >= 0); + + close(args->efd); + close(args->fd[0]); + close(args->fd[1]); + + return 0; +} + +/**************************************************************************** + * Name: epoll03_setup + ****************************************************************************/ + +static int epoll03_setup(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + int fd; + + fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); + assert_true(fd >= 0); + args->fd[0] = fd; + + fd = epoll_create(1); + assert_true(fd >= 0); + args->efd = fd; + + return 0; +} + +/**************************************************************************** + * Name: epoll03_server + ****************************************************************************/ + +static void *epoll03_server(FAR void *arg) +{ + int ret; + int new; + int fd; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + assert_true(fd >= 0); + + ret = bind(fd, (const struct sockaddr *)&g_epoll03_addr, + sizeof(g_epoll03_addr)); + assert_true(ret >= 0); + + ret = listen(fd, SOMAXCONN); + assert_true(ret >= 0); + + new = accept(fd, NULL, NULL); + assert_true(new >= 0); + usleep(500 * USEC_PER_MSEC); + + ret = write(new, "1234567890", 10); + assert_true(ret == 10); + usleep(500 * USEC_PER_MSEC); + + close(new); + close(fd); + + return NULL; +} + +/**************************************************************************** + * Name: epoll03 + ****************************************************************************/ + +static void epoll03(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + struct epoll_event evs; + pthread_t thread; + char buf[10]; + int ret; + + ret = pthread_create(&thread, NULL, epoll03_server, args); + assert_true(ret == 0); + + /* Sleep to make server run */ + + usleep(100 * USEC_PER_MSEC); + + ret = connect(args->fd[0], (const struct sockaddr *)&g_epoll03_addr, + sizeof(g_epoll03_addr)); + assert_true(ret == -1 && errno == EINPROGRESS); + + /* Add socket fd to epoll */ + + args->ev.events = EPOLLIN; + args->ev.data.fd = args->fd[0]; + ret = epoll_ctl(args->efd, EPOLL_CTL_ADD, args->fd[0], &args->ev); + assert_true(ret >= 0); + + /* 1st wait should timeout, because server accept will trigger + * EPOLLOUT event, but we do not set EPOLLOUT in ev.events. + * Even the server has writen the data, but to check the EPOLLIN event, + * client need call epoll_wait again, because local socket internal state + * changed, we need setup it again to get the EPOLLIN event. + */ + + args->start = get_time_ms(); + ret = epoll_wait(args->efd, &evs, 1, 1000); + TIME_ASSERT_RANGE(get_elapse(args->start), 1000); + assert_true(ret == 0); + + /* 2nd wait shoud return immediately, because server has send + * data to client. + */ + + ret = epoll_wait(args->efd, &evs, 1, 1000); + TIME_ASSERT_RANGE(get_elapse(args->start), 1000); + assert_true(ret == 1); + assert_true(args->fd[0] == evs.data.fd); + + ret = read(args->fd[0], buf, 10); + assert_true(ret == 10); + assert_true(memcmp(buf, "1234567890", 10) == 0); + + ret = pthread_join(thread, NULL); + assert_true(ret == 0); +} + +/**************************************************************************** + * Name: epoll03_teardown + ****************************************************************************/ + +static int epoll03_teardown(FAR void **state) +{ + FAR struct epoll_args_s *args = *state; + int ret; + + ret = epoll_ctl(args->efd, EPOLL_CTL_DEL, args->fd[0], &args->ev); + assert_true(ret >= 0); + + close(args->efd); + close(args->fd[0]); + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct epoll_args_s epoll_args; + + memset(&epoll_args, 0, sizeof(epoll_args)); + + if (argc >= 2 && *argv[1] == '?') + { + show_usage(argv[0], 0); + } + + const struct CMUnitTest tests[] = + { + cmocka_unit_test_prestate_setup_teardown(epoll01, + epoll01_setup, + epoll01_teardown, + &epoll_args), + cmocka_unit_test_prestate_setup_teardown(epoll02, + epoll02_setup, + epoll02_teardown, + &epoll_args), + cmocka_unit_test_prestate_setup_teardown(epoll03, + epoll03_setup, + epoll03_teardown, + &epoll_args), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +}