174 lines
5.4 KiB
C
174 lines
5.4 KiB
C
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#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));
|
|
}
|