From 3c89d990f7afcf3b8ff5eeffe1b12191b918fb70 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Thu, 25 Apr 2024 22:44:51 -0500 Subject: [PATCH] Feature: Add f_time_sleep_spec() for providing nanosleep(). --- level_0/f_time/c/time.c | 15 +++++ level_0/f_time/c/time.h | 24 ++++++++ level_0/f_time/data/build/settings-mocks | 1 + level_0/f_time/data/build/settings-tests | 2 +- level_0/f_time/tests/unit/c/mock-time.c | 17 ++++++ level_0/f_time/tests/unit/c/mock-time.h | 2 + level_0/f_time/tests/unit/c/test-time-sleep_spec.c | 68 ++++++++++++++++++++++ level_0/f_time/tests/unit/c/test-time-sleep_spec.h | 27 +++++++++ level_0/f_time/tests/unit/c/test-time.c | 5 +- level_0/f_time/tests/unit/c/test-time.h | 1 + 10 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 level_0/f_time/tests/unit/c/test-time-sleep_spec.c create mode 100644 level_0/f_time/tests/unit/c/test-time-sleep_spec.h diff --git a/level_0/f_time/c/time.c b/level_0/f_time/c/time.c index 41b7549..be6be87 100644 --- a/level_0/f_time/c/time.c +++ b/level_0/f_time/c/time.c @@ -115,6 +115,21 @@ extern "C" { } #endif // _di_f_time_of_day_set_ +#ifndef _di_f_time_sleep_spec_ + f_status_t f_time_sleep_spec(const f_time_spec_t time, f_time_spec_t * const remaining) { + + if (nanosleep(&time, remaining) == -1) { + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EINTR) return F_interrupt; + if (errno == EINVAL) return F_status_set_error(F_parameter); + + return F_status_set_error(F_failure); + } + + return F_okay; + } +#endif // _di_f_time_sleep_spec_ + #ifndef _di_f_time_spec_millisecond_ extern f_status_t f_time_spec_millisecond(const f_number_unsigned_t second, const f_number_unsigned_t millisecond, f_time_spec_t * const time) { #ifndef _di_level_0_parameter_checking_ diff --git a/level_0/f_time/c/time.h b/level_0/f_time/c/time.h index 84fc487..b7bfe36 100644 --- a/level_0/f_time/c/time.h +++ b/level_0/f_time/c/time.h @@ -173,6 +173,30 @@ extern "C" { #endif // _di_f_time_of_day_set_ /** + * Sleep a given amount of time specified by the f_time_spec_t. + * + * @param time + * The time to sleep (in seconds and nanoseconds). + * @param remaining + * (optional) Contains the remaining time if stopped on interrupt. + * Set to NULL to not use. + * + * @return + * F_okay on success. + * F_interrupt if interrupted by a signal. + * + * F_buffer (with error bit) if failed to copy data from user space. + * F_parameter (with error bit) if a parameter is invalid (such as tv_nsec is not in range 0 to 999999999 or tv_sec is negative). + * + * F_failure (with error bit) on any other error. + * + * @see nanosleep() + */ +#ifndef _di_f_time_sleep_spec_ + extern f_status_t f_time_sleep_spec(const f_time_spec_t time, f_time_spec_t * const remaining); +#endif // _di_f_time_sleep_spec_ + +/** * Create a timespec representing the given seconds and milliseconds. * * The "f_time_spec_t" may have different lengths and so this function provides overflow and underflow protection. diff --git a/level_0/f_time/data/build/settings-mocks b/level_0/f_time/data/build/settings-mocks index c9281a1..5929c49 100644 --- a/level_0/f_time/data/build/settings-mocks +++ b/level_0/f_time/data/build/settings-mocks @@ -71,5 +71,6 @@ flags -Wl,--wrap=f_string_append flags -Wl,--wrap=gettimeofday flags -Wl,--wrap=gmtime_r flags -Wl,--wrap=localtime_r +flags -Wl,--wrap=nanosleep flags -Wl,--wrap=settimeofday flags -Wl,--wrap=time diff --git a/level_0/f_time/data/build/settings-tests b/level_0/f_time/data/build/settings-tests index f46307c..d1afe9b 100644 --- a/level_0/f_time/data/build/settings-tests +++ b/level_0/f_time/data/build/settings-tests @@ -28,7 +28,7 @@ build_libraries-individual -lf_memory -lf_string -lf_time build_sources_program test-time-calendar_string.c test-time-calendar_string_part.c test-time-epoch_get.c build_sources_program test-time-of_day_get.c test-time-of_day_set.c build_sources_program test-time-local_get.c test-time-utc_get.c -build_sources_program test-time-spec_millisecond.c test-time-spec_nanosecond.c +build_sources_program test-time-sleep_spec.c test-time-spec_millisecond.c test-time-spec_nanosecond.c build_sources_program test-time.c build_script no diff --git a/level_0/f_time/tests/unit/c/mock-time.c b/level_0/f_time/tests/unit/c/mock-time.c index 49a2a66..72cf1ea 100644 --- a/level_0/f_time/tests/unit/c/mock-time.c +++ b/level_0/f_time/tests/unit/c/mock-time.c @@ -117,6 +117,23 @@ struct tm *__wrap_localtime_r(const time_t *timep, struct tm *result) { return res; } +int __wrap_nanosleep(const struct timespec *req, struct timespec *rem) { + + const bool failure = mock_type(bool); + + if (failure) { + errno = mock_type(int); + + return -1; + } + + if (rem) { + *rem = *mock_type(struct timespec *); + } + + return 0; +} + int __wrap_settimeofday(const struct timeval *tv, const struct timezone *tz) { const bool failure = mock_type(bool); diff --git a/level_0/f_time/tests/unit/c/mock-time.h b/level_0/f_time/tests/unit/c/mock-time.h index 6cd8cbb..3681a3b 100644 --- a/level_0/f_time/tests/unit/c/mock-time.h +++ b/level_0/f_time/tests/unit/c/mock-time.h @@ -17,6 +17,7 @@ #include #include #include +#include // cmocka includes. #include @@ -42,6 +43,7 @@ extern char *__wrap_ctime_r(const time_t *timep, char *buf); extern int __wrap_gettimeofday(struct timeval *tv, struct timezone *tz); extern struct tm *__wrap_gmtime_r(const time_t *timep, struct tm *result); extern struct tm *__wrap_localtime_r(const time_t *timep, struct tm *result); +extern int __wrap_nanosleep(const struct timespec *req, struct timespec *rem); extern int __wrap_settimeofday(const struct timeval *tv, const struct timezone *tz); extern time_t __wrap_time(time_t *tloc); diff --git a/level_0/f_time/tests/unit/c/test-time-sleep_spec.c b/level_0/f_time/tests/unit/c/test-time-sleep_spec.c new file mode 100644 index 0000000..37d529e --- /dev/null +++ b/level_0/f_time/tests/unit/c/test-time-sleep_spec.c @@ -0,0 +1,68 @@ +#include "test-time.h" +#include "test-time-sleep_spec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_time_sleep_spec__fails(void **state) { + + const f_time_spec_t spec = f_time_spec_t_initialize; + + { + int errnos[] = { + EFAULT, + EINTR, + EINVAL, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_status_set_error(F_buffer), + F_interrupt, + F_status_set_error(F_parameter), + F_status_set_error(F_failure), + }; + + for (int i = 0; i < 4; ++i) { + + will_return(__wrap_nanosleep, true); + will_return(__wrap_nanosleep, errnos[i]); + + const f_status_t status = f_time_sleep_spec(spec, 0); + + assert_int_equal(status, statuss[i]); + } // for + } +} + +void test__f_time_sleep_spec__works(void **state) { + + const f_time_spec_t spec = { .tv_sec = 1, .tv_nsec = 1 }; + const f_time_spec_t expect = { .tv_sec = 3, .tv_nsec = 4 }; + + { + will_return(__wrap_nanosleep, false); + + const f_status_t status = f_time_sleep_spec(spec, 0); + + assert_int_equal(status, F_okay); + } + + { + f_time_spec_t remaining = f_time_spec_t_initialize; + + will_return(__wrap_nanosleep, false); + will_return(__wrap_nanosleep, &expect); + + const f_status_t status = f_time_sleep_spec(spec, &remaining); + + assert_int_equal(status, F_okay); + assert_int_equal(remaining.tv_sec, expect.tv_sec); + assert_int_equal(remaining.tv_nsec, expect.tv_nsec); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_time/tests/unit/c/test-time-sleep_spec.h b/level_0/f_time/tests/unit/c/test-time-sleep_spec.h new file mode 100644 index 0000000..c66a655 --- /dev/null +++ b/level_0/f_time/tests/unit/c/test-time-sleep_spec.h @@ -0,0 +1,27 @@ +/** + * FLL - Level 0 + * + * Project: Time + * API Version: 0.7 + * Licenses: lgpl-2.1-or-later + * + * Test the time project. + */ +#ifndef _TEST__F_time_sleep_spec_h +#define _TEST__F_time_sleep_spec_h + +/** + * Test that function fails. + * + * @see f_time_sleep_spec() + */ +extern void test__f_time_sleep_spec__fails(void **state); + +/** + * Test that function works. + * + * @see f_time_sleep_spec() + */ +extern void test__f_time_sleep_spec__works(void **state); + +#endif // _TEST__F_time_sleep_spec_h diff --git a/level_0/f_time/tests/unit/c/test-time.c b/level_0/f_time/tests/unit/c/test-time.c index d578958..e2fe635 100644 --- a/level_0/f_time/tests/unit/c/test-time.c +++ b/level_0/f_time/tests/unit/c/test-time.c @@ -25,6 +25,7 @@ int main(void) { cmocka_unit_test(test__f_time_local_get__fails), cmocka_unit_test(test__f_time_of_day_get__fails), cmocka_unit_test(test__f_time_of_day_set__fails), + cmocka_unit_test(test__f_time_sleep_spec__fails), cmocka_unit_test(test__f_time_utc_get__fails), cmocka_unit_test(test__f_time_calendar_string__works), @@ -33,6 +34,7 @@ int main(void) { cmocka_unit_test(test__f_time_local_get__works), cmocka_unit_test(test__f_time_of_day_get__works), cmocka_unit_test(test__f_time_of_day_set__works), + cmocka_unit_test(test__f_time_sleep_spec__works), cmocka_unit_test(test__f_time_utc_get__works), cmocka_unit_test(test__f_time_spec_millisecond__number_overflow), @@ -50,9 +52,10 @@ int main(void) { cmocka_unit_test(test__f_time_local_get__parameter_checking), cmocka_unit_test(test__f_time_of_day_get__parameter_checking), // f_time_of_day_set() doesn't use parameter checking. - cmocka_unit_test(test__f_time_utc_get__parameter_checking), + // f_time_sleep_spec() doesn't use parameter checking. cmocka_unit_test(test__f_time_spec_millisecond__parameter_checking), cmocka_unit_test(test__f_time_spec_nanosecond__parameter_checking), + cmocka_unit_test(test__f_time_utc_get__parameter_checking), #endif // _di_level_0_parameter_checking_ }; diff --git a/level_0/f_time/tests/unit/c/test-time.h b/level_0/f_time/tests/unit/c/test-time.h index 3c3eae6..787929f 100644 --- a/level_0/f_time/tests/unit/c/test-time.h +++ b/level_0/f_time/tests/unit/c/test-time.h @@ -33,6 +33,7 @@ #include "test-time-of_day_get.h" #include "test-time-of_day_set.h" #include "test-time-utc_get.h" +#include "test-time-sleep_spec.h" #include "test-time-spec_millisecond.h" #include "test-time-spec_nanosecond.h" -- 1.8.3.1