1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <time.h>
8 #include <unistd.h>
9 
10 #include <zephyr/ztest.h>
11 #include <zephyr/logging/log.h>
12 
13 #define SECS_TO_SLEEP 2
14 #define DURATION_SECS 1
15 #define DURATION_NSECS 0
16 #define PERIOD_SECS 0
17 #define PERIOD_NSECS 100000000
18 
19 #define TEST_SIGNAL_VAL SIGTSTP
20 
21 LOG_MODULE_REGISTER(timer_test);
22 
23 static int exp_count;
24 static timer_t timerid = -1;
25 
handler(union sigval val)26 void handler(union sigval val)
27 {
28 	++exp_count;
29 	LOG_DBG("Handler Signal value %d for %d times", val.sival_int, exp_count);
30 	zassert_equal(val.sival_int, TEST_SIGNAL_VAL);
31 }
32 
test_timer(clockid_t clock_id,int sigev_notify)33 void test_timer(clockid_t clock_id, int sigev_notify)
34 {
35 	struct sigevent sig = {0};
36 	struct itimerspec value, ovalue;
37 	struct timespec ts, te;
38 	int64_t nsecs_elapsed, secs_elapsed;
39 
40 	exp_count = 0;
41 	sig.sigev_notify = sigev_notify;
42 	sig.sigev_notify_function = handler;
43 	sig.sigev_value.sival_int = TEST_SIGNAL_VAL;
44 
45 	/*TESTPOINT: Check if timer is created successfully*/
46 	zassert_ok(timer_create(clock_id, &sig, &timerid));
47 
48 	value.it_value.tv_sec = DURATION_SECS;
49 	value.it_value.tv_nsec = DURATION_NSECS;
50 	value.it_interval.tv_sec = PERIOD_SECS;
51 	value.it_interval.tv_nsec = PERIOD_NSECS;
52 	zassert_ok(timer_settime(timerid, 0, &value, &ovalue));
53 	usleep(100 * USEC_PER_MSEC);
54 	/*TESTPOINT: Check if timer has started successfully*/
55 	zassert_ok(timer_gettime(timerid, &value));
56 
57 	LOG_DBG("Timer fires every %d secs and  %d nsecs", (int)value.it_interval.tv_sec,
58 		(int)value.it_interval.tv_nsec);
59 	LOG_DBG("Time remaining to fire %d secs and  %d nsecs", (int)value.it_value.tv_sec,
60 		(int)value.it_value.tv_nsec);
61 
62 	clock_gettime(clock_id, &ts);
63 	sleep(SECS_TO_SLEEP);
64 	clock_gettime(clock_id, &te);
65 
66 	if (te.tv_nsec >= ts.tv_nsec) {
67 		secs_elapsed = te.tv_sec - ts.tv_sec;
68 		nsecs_elapsed = te.tv_nsec - ts.tv_nsec;
69 	} else {
70 		nsecs_elapsed = NSEC_PER_SEC + te.tv_nsec - ts.tv_nsec;
71 		secs_elapsed = (te.tv_sec - ts.tv_sec - 1);
72 	}
73 
74 	uint64_t elapsed = secs_elapsed*NSEC_PER_SEC + nsecs_elapsed;
75 	uint64_t first_sig = value.it_value.tv_sec * NSEC_PER_SEC + value.it_value.tv_nsec;
76 	uint64_t sig_interval = value.it_interval.tv_sec * NSEC_PER_SEC + value.it_interval.tv_nsec;
77 	int expected_signal_count = (elapsed - first_sig) / sig_interval + 1;
78 
79 	/*TESTPOINT: Check if POSIX timer test passed*/
80 	zassert_within(exp_count, expected_signal_count, 1,
81 		       "POSIX timer test has failed %i != %i",
82 		       exp_count, expected_signal_count);
83 }
84 
ZTEST(timer,test_CLOCK_REALTIME__SIGEV_SIGNAL)85 ZTEST(timer, test_CLOCK_REALTIME__SIGEV_SIGNAL)
86 {
87 	test_timer(CLOCK_REALTIME, SIGEV_SIGNAL);
88 }
89 
ZTEST(timer,test_CLOCK_REALTIME__SIGEV_THREAD)90 ZTEST(timer, test_CLOCK_REALTIME__SIGEV_THREAD)
91 {
92 	test_timer(CLOCK_REALTIME, SIGEV_THREAD);
93 }
94 
ZTEST(timer,test_CLOCK_MONOTONIC__SIGEV_SIGNAL)95 ZTEST(timer, test_CLOCK_MONOTONIC__SIGEV_SIGNAL)
96 {
97 	test_timer(CLOCK_MONOTONIC, SIGEV_SIGNAL);
98 }
99 
ZTEST(timer,test_CLOCK_MONOTONIC__SIGEV_THREAD)100 ZTEST(timer, test_CLOCK_MONOTONIC__SIGEV_THREAD)
101 {
102 	test_timer(CLOCK_MONOTONIC, SIGEV_THREAD);
103 }
104 
ZTEST(timer,test_timer_overrun)105 ZTEST(timer, test_timer_overrun)
106 {
107 	struct sigevent sig = { 0 };
108 	struct itimerspec value;
109 
110 	sig.sigev_notify = SIGEV_NONE;
111 
112 	zassert_ok(timer_create(CLOCK_MONOTONIC, &sig, &timerid));
113 
114 	/*Set the timer to expire every 500 milliseconds*/
115 	value.it_interval.tv_sec = 0;
116 	value.it_interval.tv_nsec = 500000000;
117 	value.it_value.tv_sec = 0;
118 	value.it_value.tv_nsec = 500000000;
119 	zassert_ok(timer_settime(timerid, 0, &value, NULL));
120 	k_sleep(K_MSEC(2500));
121 
122 	zassert_equal(timer_getoverrun(timerid), 4, "Number of overruns is incorrect");
123 }
124 
after(void * arg)125 static void after(void *arg)
126 {
127 	ARG_UNUSED(arg);
128 
129 	if (timerid != -1) {
130 		(void)timer_delete(timerid);
131 		timerid = -1;
132 	}
133 }
134 
before(void * arg)135 static void before(void *arg)
136 {
137 	ARG_UNUSED(arg);
138 
139 	if (!IS_ENABLED(CONFIG_DYNAMIC_THREAD)) {
140 		/* skip redundant testing if there is no thread pool / heap allocation */
141 		ztest_test_skip();
142 	}
143 }
144 
145 ZTEST_SUITE(timer, NULL, NULL, before, after, NULL);
146