* apps/testing/sched/schedtest/signaltest.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 <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <stdint.h>
#include <cmocka.h>
#include <nuttx/signal.h>
#include <sys/wait.h>
#include "sched/sched.h"
#include "signal/signal.h"
* Private Function Prototypes
****************************************************************************/
struct test_data_t
{
siginfo_t siginfo;
struct task_group_s group;
struct tcb_s tcb;
};
* Private Functions
****************************************************************************/
static void my_signal_handler(int signo)
{
assert_int_equal(signo, SIGUSR1);
}
static void test_nxsig_action(FAR void **state)
{
FAR struct test_data_t *data;
struct sigaction new_action;
struct sigaction old_action;
sigset_t expected_mask;
int ret;
data = (FAR struct test_data_t *)*state;
memset(&data->group, 0, sizeof(data->group));
sq_init(&data->group.tg_sigactionq);
data->tcb.group = &data->group;
new_action.sa_u._sa_handler = my_signal_handler;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
ret = nxsig_action(SIGUSR1, &new_action, &old_action, false);
raise(SIGUSR1);
assert_int_equal(ret, 0);
assert_ptr_equal(old_action.sa_handler, SIG_IGN);
nxsig_cleanup(&data->tcb);
sigfillset(&expected_mask);
assert_memory_equal(&data->tcb.sigprocmask, &expected_mask,
sizeof(sigset_t));
sigemptyset(&expected_mask);
assert_memory_equal(&data->tcb.sigwaitmask, &expected_mask,
sizeof(sigset_t));
nxsig_release(&data->group);
}
#ifdef CONFIG_SIG_DEFAULT
static void test_nxsig_default_initialize(FAR void **state)
{
FAR struct test_data_t *data;
data = (FAR struct test_data_t *)*state;
data->tcb.group = &data->group;
int result = nxsig_default_initialize(&data->tcb);
assert_int_equal(result, OK);
}
#endif
static void test_nxsig_find_action(FAR void **state)
{
FAR struct test_data_t *data;
sigactq_t sigaction1;
sigactq_t sigaction2;
sigactq_t sigaction3;
FAR sigactq_t *result1 = NULL;
FAR sigactq_t *result2 = NULL;
data = (FAR struct test_data_t *)*state;
sigaction1.signo = 1;
sigaction2.signo = 2;
sigaction3.signo = 3;
sigaction1.flink = &sigaction2;
sigaction2.flink = &sigaction3;
sigaction3.flink = NULL;
data->group.tg_sigactionq.head = (sq_entry_t *)&sigaction1;
result1 = nxsig_find_action(&data->group, 2);
assert_non_null(result1);
assert_int_equal(result1->signo, 2);
result2 = nxsig_find_action(&data->group, 4);
assert_null(result2);
}
static void *thread_nxsig_kill(FAR void *arg)
{
while (1)
{
sleep(1);
}
return NULL;
}
static void test_nxsig_kill(FAR void **state)
{
UNUSED(state);
pthread_t thread;
int ret = pthread_create(&thread, NULL, thread_nxsig_kill, NULL);
assert_true(ret == 0);
sleep(1);
assert_int_equal(nxsig_kill(-1, 1), -ENOSYS);
assert_int_equal(nxsig_kill(thread, MAX_SIGNO + 1), -EINVAL);
assert_int_equal(nxsig_kill(thread, SIGUSR1), 0);
pthread_cancel(thread);
pthread_join(thread, NULL);
}
static void test_nxsig_lowest(FAR void **state)
{
UNUSED(state);
sigset_t set;
sigemptyset(&set);
sigaddset(&set, 5);
sigaddset(&set, 4);
sigaddset(&set, 3);
assert_int_equal(nxsig_lowest(&set), 3);
}
static void test_nxsig_nanosleep(FAR void **state)
{
UNUSED(state);
struct timespec rqtp;
struct timespec rmtp;
struct timespec start;
struct timespec end;
double elapsed_time;
int ret;
rqtp.tv_sec = 0;
rqtp.tv_nsec = 1000000000;
assert_int_equal(nxsig_nanosleep(&rqtp, &rmtp), -EINVAL);
rqtp.tv_sec = 0;
rqtp.tv_nsec = 500000000;
clock_gettime(CLOCK_MONOTONIC, &start);
ret = nxsig_nanosleep(&rqtp, &rmtp);
clock_gettime(CLOCK_MONOTONIC, &end);
assert_int_equal(ret, 0);
elapsed_time = (end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1000000000.0;
assert_true(elapsed_time >= 0.5 && elapsed_time < 0.6);
}
#ifdef CONFIG_SIG_EVTHREAD
static void nxsig_notification_work(union sigval sv)
{
int value = (int)(intptr_t)sv.sival_ptr;
assert_int_equal(value, 42);
}
#endif
static void *thread_nxsig_notification(FAR void *arg)
{
while (1)
{
sleep(1);
}
return NULL;
}
static void test_nxsig_notification(FAR void **state)
{
UNUSED(state);
struct sigevent event;
#ifdef CONFIG_SIG_EVTHREAD
struct sigwork_s work;
#endif
int notify_value = 42;
pthread_t thread;
int ret;
ret = pthread_create(&thread, NULL, thread_nxsig_notification, NULL);
assert_true(ret == 0);
sleep(1);
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGUSR1;
event.sigev_value.sival_ptr = (void *)(intptr_t)notify_value;
ret = nxsig_notification(thread, &event, SI_USER, NULL);
assert_true(ret == 0);
#ifdef CONFIG_SIG_EVTHREAD
event.sigev_notify = SIGEV_THREAD;
event.sigev_value.sival_ptr = (void *)(intptr_t)notify_value;
event.sigev_notify_function = nxsig_notification_work;
memset(&work, 0, sizeof(work));
ret = nxsig_notification(thread, &event, SI_USER, &work);
assert_true(ret == 0);
nxsig_cancel_notification(&work);
#endif
event.sigev_notify = SIGEV_NONE;
assert_int_equal(nxsig_notification(0, &event, 0, NULL), 0);
pthread_cancel(thread);
pthread_join(thread, NULL);
}
static void test_nxsig_pendingset(FAR void **state)
{
FAR struct test_data_t *data;
sigpendq_t mock_sigpend1;
sigpendq_t mock_sigpend2;
data = (FAR struct test_data_t *)*state;
data->tcb.group = &data->group;
data->group.tg_sigpendingq.head = (sq_entry_t *)&mock_sigpend1;
mock_sigpend1.info.si_signo = SIGUSR1;
mock_sigpend1.flink = &mock_sigpend2;
mock_sigpend2.info.si_signo = SIGUSR2;
mock_sigpend2.flink = NULL;
sigset_t result = nxsig_pendingset(&data->tcb);
assert_true(sigismember(&result, SIGUSR1));
assert_true(sigismember(&result, SIGUSR2));
}
static void test_nxsig_procmask(FAR void **state)
{
FAR struct test_data_t *data;
sigset_t initial_mask;
sigset_t new_mask;
sigset_t old_mask;
FAR struct tcb_s *current_task = this_task();
data = (FAR struct test_data_t *)*state;
sigemptyset(&initial_mask);
sigemptyset(&new_mask);
sigaddset(&initial_mask, SIGUSR1);
sigaddset(&new_mask, SIGUSR2);
current_task->sigprocmask = initial_mask;
int ret = nxsig_procmask(SIG_BLOCK, &new_mask, &old_mask);
memcpy(&(data->tcb), current_task, sizeof(struct tcb_s));
assert_int_equal(ret, OK);
assert_true(sigismember(&old_mask, SIGUSR1));
assert_true(sigismember(&(data->tcb.sigprocmask), SIGUSR1));
assert_true(sigismember(&(data->tcb.sigprocmask), SIGUSR2));
}
FAR static void *thread_nxsig_queue(FAR void *arg)
{
struct siginfo signo;
sigset_t set;
int ret;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
ret = nxsig_timedwait(&set, &signo, NULL);
assert_int_equal(ret, SIGUSR1);
return NULL;
}
static void test_nxsig_queue(FAR void **state)
{
UNUSED(state);
union sigval valid_value;
pthread_t thread;
int valid_signo = SIGUSR1;
int invalid_signo = -1;
int ret;
valid_value.sival_int = 42;
ret = pthread_create(&thread, NULL, thread_nxsig_queue, NULL);
assert_true(ret == 0);
sleep(1);
ret = nxsig_queue(thread, invalid_signo, valid_value);
assert_int_equal(ret, -EINVAL);
ret = nxsig_queue(thread, valid_signo, valid_value);
assert_int_equal(ret, 0);
}
static void test_nxsig_sleep(FAR void **state)
{
UNUSED(state);
struct timespec start;
struct timespec end;
double elapsed_time;
unsigned int seconds = 2;
unsigned int ret;
clock_gettime(CLOCK_MONOTONIC, &start);
ret = nxsig_sleep(seconds);
clock_gettime(CLOCK_MONOTONIC, &end);
elapsed_time = (end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1000000000.0;
assert_true(elapsed_time >= 2.0 && elapsed_time < 2.1);
assert_int_equal(ret, 0);
}
FAR static void *thread_nxsig_tgkill(FAR void *arg)
{
while (1)
{
pause();
}
return NULL;
}
static void test_nxsig_tgkill(void **state)
{
UNUSED(state);
pid_t pid;
pthread_t thread;
int signo = SIGUSR1;
int ret;
ret = pthread_create(&thread, NULL, thread_nxsig_tgkill, NULL);
assert_true(ret == 0);
sleep(1);
pid = getpid();
assert_int_equal(nxsig_tgkill(pid, thread, -1), -EINVAL);
assert_int_equal(nxsig_tgkill(pid, thread, signo), 0);
pthread_cancel(thread);
pthread_join(thread, NULL);
}
static void test_nxsig_usleep(void **state)
{
UNUSED(state);
useconds_t usec = 1500000;
struct timespec start;
struct timespec end;
double elapsed_time;
int ret;
clock_gettime(CLOCK_MONOTONIC, &start);
ret = nxsig_usleep(usec);
clock_gettime(CLOCK_MONOTONIC, &end);
assert_int_equal(ret, 0);
elapsed_time = (end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1000000000.0;
assert_true(elapsed_time >= 1.5 && elapsed_time < 1.6);
}
static void test_nxsig_tcbdispatch(FAR void **state)
{
FAR struct test_data_t *data;
data = (FAR struct test_data_t *)*state;
int ret = nxsig_tcbdispatch(&data->tcb, &data->siginfo);
assert_int_equal(ret, OK);
}
static void *thread_nxsig_dispatch(FAR void *arg)
{
FAR struct test_data_t *data = (struct test_data_t *)arg;
data->siginfo.si_signo = SIGUSR1;
data->tcb.pid = getpid();
int ret = nxsig_dispatch(data->tcb.pid, &data->siginfo, false);
assert_int_equal(ret, OK);
return NULL;
}
static void test_nxsig_dispatch(FAR void **state)
{
struct test_data_t *data = (struct test_data_t *)*state;
pthread_t thread;
int err = pthread_create(&thread, NULL, thread_nxsig_dispatch, data);
assert_int_equal(err, 0);
pthread_join(thread, NULL);
}
* Setup and Teardown Functions
****************************************************************************/
static int test_setup(FAR void **state)
{
FAR struct test_data_t *data = malloc(sizeof(struct test_data_t));
assert_non_null(data);
memset(data, 0, sizeof(struct test_data_t));
*state = data;
return 0;
}
static int test_teardown(FAR void **state)
{
assert_non_null(*state);
free(*state);
return 0;
}
* Public Functions
****************************************************************************/
int main(int argc, FAR char *argv[])
{
struct test_data_t *data = NULL;
cmocka_set_message_output(CM_OUTPUT_STDOUT);
const struct CMUnitTest tests[] =
{
cmocka_unit_test(test_nxsig_kill),
cmocka_unit_test(test_nxsig_lowest),
cmocka_unit_test(test_nxsig_nanosleep),
cmocka_unit_test(test_nxsig_queue),
cmocka_unit_test(test_nxsig_sleep),
cmocka_unit_test(test_nxsig_tgkill),
cmocka_unit_test(test_nxsig_usleep),
cmocka_unit_test(test_nxsig_notification),
#ifdef CONFIG_SIG_DEFAULT
cmocka_unit_test_prestate_setup_teardown(test_nxsig_default_initialize,
test_setup,
test_teardown,
data),
#endif
cmocka_unit_test_prestate_setup_teardown(test_nxsig_find_action,
test_setup,
test_teardown,
data),
cmocka_unit_test_prestate_setup_teardown(test_nxsig_pendingset,
test_setup,
test_teardown,
data),
cmocka_unit_test_prestate_setup_teardown(test_nxsig_action,
test_setup,
test_teardown,
data),
cmocka_unit_test_prestate_setup_teardown(test_nxsig_procmask,
test_setup,
test_teardown,
data),
cmocka_unit_test_prestate_setup_teardown(test_nxsig_tcbdispatch,
test_setup,
test_teardown,
data),
cmocka_unit_test_prestate_setup_teardown(test_nxsig_dispatch,
test_setup,
test_teardown,
data),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}