occlum/test/sleep/main.c
2020-05-27 07:09:18 +00:00

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));
}