#include #include #include #include #include "test.h" // ============================================================================ // Helper macros // ============================================================================ #define S (1000 * 1000 * 1000) #define MS (1000 * 1000) #define US (1000) #define NS (1) // ============================================================================ // Helper functions // ============================================================================ static inline void validate_timespec(const struct timespec* tv) { assert(tv->tv_sec >= 0 && tv->tv_nsec >= 0 && tv->tv_nsec < S); } // retval = (a < b) ? -1 : ((a > b) ? 1 : 0) static int timespec_cmp(const struct timespec *a, const struct timespec *b) { validate_timespec(a); validate_timespec(b); if (a->tv_sec < b->tv_sec) { return -1; } else if (a->tv_sec > b->tv_sec) { return 1; } else { return a->tv_nsec < b->tv_nsec ? -1 : (a->tv_nsec > b->tv_nsec ? 1 : 0); } } // diff = | a - b | static void timespec_diff(const struct timespec *a, const struct timespec *b, struct timespec *diff) { validate_timespec(a); validate_timespec(b); const struct timespec *begin, *end; if (timespec_cmp(a, b) <= 0) { begin = a; end = b; } else { begin = b; end = a; } diff->tv_nsec = end->tv_nsec - begin->tv_nsec; diff->tv_sec = end->tv_sec - begin->tv_sec; if (diff->tv_nsec < 0) { diff->tv_nsec += S; diff->tv_sec -= 1; } validate_timespec(diff); } // retval = | a - b | <= precision static int timespec_equal(const struct timespec *a, const struct timespec *b, const struct timespec *precision) { struct timespec diff; timespec_diff(a, b, &diff); return timespec_cmp(&diff, precision) <= 0; } static int check_nanosleep(struct timespec* expected_sleep_period) { // The time obtained from Occlum is not very precise. // Here we take 1 millisecond as the time precision of Occlum. static struct timespec OS_TIME_PRECISION = { .tv_sec = 0, .tv_nsec = 1 * MS, }; struct timespec begin_timestamp, end_timestamp; clock_gettime(CLOCK_MONOTONIC, &begin_timestamp); if (nanosleep(expected_sleep_period, NULL) != 0) { THROW_ERROR("nanosleep failed"); } clock_gettime(CLOCK_MONOTONIC, &end_timestamp); struct timespec actual_sleep_period; timespec_diff(&begin_timestamp, &end_timestamp, &actual_sleep_period); return timespec_equal(expected_sleep_period, &actual_sleep_period, &OS_TIME_PRECISION); } // ============================================================================ // Test cases // ============================================================================ int test_nanosleep_0_second() { struct timespec period_of_0s = { .tv_sec = 0, .tv_nsec = 0 }; return check_nanosleep(&period_of_0s); } int test_nanosleep_1_second() { struct timespec period_of_1s = { .tv_sec = 1, .tv_nsec = 0 }; return check_nanosleep(&period_of_1s); } int test_nanosleep_10ms() { struct timespec period_of_10ms = { .tv_sec = 0, .tv_nsec = 10 * MS }; return check_nanosleep(&period_of_10ms); } // ============================================================================ // Test cases with invalid arguments // ============================================================================ int test_nanosleep_with_null_req() { if (nanosleep(NULL, NULL) != -1 && errno != EINVAL) { THROW_ERROR("nanosleep should report error"); } return 0; } int test_nanosleep_with_negative_tv_sec() { // nanosleep returns EINVAL if the value in the tv_sec field is negative struct timespec invalid_period = { .tv_sec = -1, .tv_nsec = 0}; if (nanosleep(&invalid_period, NULL) != -1 && errno != EINVAL) { THROW_ERROR("nanosleep should report EINVAL error"); } return 0; } int test_nanosleep_with_negative_tv_nsec() { // nanosleep returns EINVAL if the value in the tv_nsec field // was not in the range 0 to 999999999. struct timespec invalid_period = { .tv_sec = 0, .tv_nsec = -1}; if (nanosleep(&invalid_period, NULL) != -1 && errno != EINVAL) { THROW_ERROR("nanosleep should report EINVAL error"); } return 0; } int test_nanosleep_with_too_large_tv_nsec() { // nanosleep returns EINVAL if the value in the tv_nsec field // was not in the range 0 to 999999999 (10^6 - 1). struct timespec invalid_period = { .tv_sec = 0, .tv_nsec = S}; if (nanosleep(&invalid_period, NULL) != -1 && errno != EINVAL) { THROW_ERROR("nanosleep should report EINVAL error"); } return 0; } // ============================================================================ // Test suite main // ============================================================================ // TODO: test interruption static test_case_t test_cases[] = { TEST_CASE(test_nanosleep_0_second), TEST_CASE(test_nanosleep_1_second), TEST_CASE(test_nanosleep_10ms), TEST_CASE(test_nanosleep_with_null_req), TEST_CASE(test_nanosleep_with_negative_tv_sec), TEST_CASE(test_nanosleep_with_negative_tv_nsec), TEST_CASE(test_nanosleep_with_too_large_tv_nsec) }; int main() { return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); }