From: Juergen Perlinger Date: Tue, 25 Jan 2011 20:08:29 +0000 (+0100) Subject: Refactoring of struct timeval string conversion, extended regression tests for struct... X-Git-Tag: NTP_4_2_7P125~1^2~2^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b94c57af360822d9fc1fedfaa2dd6128e2e81dc6;p=thirdparty%2Fntp.git Refactoring of struct timeval string conversion, extended regression tests for struct timeval processing bk: 4d3f2dbdh3s4SofA3nqDe_OecpljgQ --- diff --git a/include/timespecops.h b/include/timespecops.h index 29a1cf26d..6b1e2eeea 100644 --- a/include/timespecops.h +++ b/include/timespecops.h @@ -53,7 +53,7 @@ /* predicate: returns TRUE if the nanoseconds are in nominal range */ #define timespec_isnormal(x) \ - ((u_long)(x)->tv_nsec < 1000000000) + ((x)->tv_nsec >= 0 && (x)->tv_nsec < 1000000000) /* predicate: returns TRUE if the nanoseconds are out-of-bounds */ #define timespec_isdenormal(x) (!timespec_isnormal(x)) diff --git a/include/timetoa.h b/include/timetoa.h new file mode 100644 index 000000000..edcd05b16 --- /dev/null +++ b/include/timetoa.h @@ -0,0 +1,85 @@ +/* + * timetoa.h -- time_t related string formatting + * + * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. + * The contents of 'html/copyright.html' apply. + * + * Printing a 'time_t' has some portability pitfalls, due to it's opaque + * base type. The only requirement imposed by the standard is that it + * must be a numeric type. For all practical purposes it's a signed int, + * and 32 bits are common. + * + * Since the UN*X time epoch will cause a signed integer overflow for + * 32-bit signed int values in the year 2038, implementations slowly + * move to 64bit base types for time_t, even in 32-bit environments. In + * such an environment sizeof(time_t) could be bigger than sizeof(long) + * and the commonly used idiom of casting to long leads to truncation. + * + * As the printf() family has no standardised type specifier for time_t, + * guessing the right output format specifier is a bit troublesome and + * best done with the help of the preprocessor and "config.h". + */ +#ifndef TIMETOA_H +#define TIMETOA_H + +#include "config.h" +#include "ntp_fp.h" +#include "ntp_stdlib.h" +#include "ntp_unixtime.h" + +/* + * Given the size of time_t, guess what can be used as an unsigned value + * to hold a time_t and the printf() format specifcation. + * + * These should be used with the string constant concatenation feature + * of the compiler like this: + * + * printf("a time stamp: %" TIME_FORMAT " and more\n", a_time_t_value); + * + * It's not exactly nice, but there's not much leeway once we want to + * use the printf() family on time_t values. + */ + +#if SIZEOF_TIME_T <= SIZEOF_INT + +typedef unsigned int u_time; +#define TIME_FORMAT "d" +#define UTIME_FORMAT "u" + +#elif SIZEOF_TIME_T <= SIZEOF_LONG + +typedef unsigned long u_time; +#define TIME_FORMAT "ld" +#define UTIME_FORMAT "lu" + +#elif defined(SIZEOF_LONG_LONG) && SIZEOF_TIME_T <= SIZEOF_LONG_LONG + +typedef unsigned long long u_time; +#define TIME_FORMAT "lld" +#define UTIME_FORMAT "llu" + +#else +#include "GRONK: what size has a time_t here?" +#endif + +/* general fractional time stamp formatting. + * + * secs - integral seconds of time stamp + * frac - fractional units + * prec - log10 of units per second (3=miliseconds, 6=microseconds,..) + * or in other words: the number decimal digits required. + * If prec is < 0, abs(prec) is taken and for the precision + * and 'secs' is treated as an unsigned value. + * + * abs(prec) must be in [1 .. 9], or only the seconds are formatted. + * + * The function will eventually normalise the fraction and adjust the + * seconds accordingly. + * + * This function uses the string buffer library for the return value, + * so do not keep the resulting pointers around. + */ +extern const char * +format_time_fraction(time_t secs, long frac, int prec); + +#endif /* !defined(TIMETOA_H) */ diff --git a/include/timevalops.h b/include/timevalops.h index 2aec5fb48..7b475a8d4 100644 --- a/include/timevalops.h +++ b/include/timevalops.h @@ -27,7 +27,7 @@ * use like: int timeval_isnormal(const struct timeval *x) */ #define timeval_isnormal(x) \ - ((u_long)(x)->tv_usec < 1000000) + ((x)->tv_usec >= 0 && (x)->tv_usec < 1000000) /* * predicate: returns TRUE if the microseconds are out-of-bounds diff --git a/libntp/Makefile.am b/libntp/Makefile.am index 17b32266b..52177abb2 100644 --- a/libntp/Makefile.am +++ b/libntp/Makefile.am @@ -65,6 +65,7 @@ libntp_a_SRCS = \ strdup.c \ syssignal.c \ timespecops.c \ + timetoa.c \ timevalops.c \ tsftomsu.c \ tstotv.c \ diff --git a/libntp/timetoa.c b/libntp/timetoa.c new file mode 100644 index 000000000..c8e01a68d --- /dev/null +++ b/libntp/timetoa.c @@ -0,0 +1,112 @@ +/* + * timetoa.c -- time_t related string formatting + * + * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. + * The contents of 'html/copyright.html' apply. + * + * Printing a 'time_t' has a lot of portability pitfalls, due to it's + * opaque base type. The only requirement imposed by the standard is + * that it must be a numeric type. For all practical purposes it's a + * signed int, and 32 bits are common. + * + * Since the UN*X time epoch will cause a signed integer overflow for + * 32-bit signed int in the year 2038, implementations slowly move to + * 64bit base types for time_t, even in 32-bit environments. + * + * As the printf() family has no standardised type specifier for time_t, + * guessing the right output format specifier is a bit troublesome and + * best done with the help of the preprocessor and "config.h". + */ + +#include "config.h" + +#include "timetoa.h" +#include "lib_strbuf.h" +#include + +/* + * Formatting to string needs at max 40 bytes (even with 64 bit time_t), + * so we check LIB_BUFLENGTH is big enough for our pupose. + */ +#if LIB_BUFLENGTH < 40 +# include "GRONK: LIB_BUFLENGTH is not sufficient" +#endif + +/* + * general fractional timestamp formatting + * + * Many pieces of ntpd require a machine with two's complement + * representation of signed integers, so we don't go through the whole + * rigamarole of creating fully portable code here. But we have to stay + * away from signed integer overflow, as this might cause trouble even + * with two's complement representation. + */ +const char * +format_time_fraction( + time_t secs, + long frac, + int prec + ) +{ + static const long limit[10] = { + 1, + 10, 100, 1000, + 10000, 100000, 1000000, + 10000000, 100000000, 1000000000 + }; + + char * cp; + u_time ttmp; /* unsigned storage for seconds */ + int notneg; /* flag for non-negative value */ + const char * fmt; + + LIB_GETBUF(cp); + ttmp = (u_time)secs; + fmt = "-%" UTIME_FORMAT ".%0*ld"; + + /* check if we need signed or unsigned mode */ + notneg = (prec < 0); + prec = abs(prec); + if (prec <= 0 || prec > 9) { + if (notneg) + fmt = "%" UTIME_FORMAT; + else + fmt = "%" TIME_FORMAT; + snprintf(cp, LIB_BUFLENGTH, fmt, secs); + return (cp); + } + + /* + * Since conversion to string uses lots of divisions anyway, + * there's no big extra penalty for normalisation. We do it for + * consistency. + */ + if (frac < 0 || frac >= limit[prec]) { + ldiv_t qr; + qr = ldiv(frac, limit[prec]); + if (qr.rem < 0) { + qr.quot--; + qr.rem += limit[prec]; + } + ttmp += (time_t)qr.quot; + frac = qr.rem; + } + + /* + * Get the absolute value of the time stamp. + */ + notneg = notneg || ((time_t)ttmp >= 0); + if (notneg == 0) { + ttmp = ~ttmp; + if (frac != 0) + frac = limit[prec] - frac; + else + ttmp += 1; + } else + fmt++; /* skip sign char in format string */ + + /* finally format the data and return the result */ + snprintf(cp, LIB_BUFLENGTH, fmt, ttmp, prec, frac); + + return (cp); +} diff --git a/libntp/timevalops.c b/libntp/timevalops.c index 623c2f978..63a842f70 100644 --- a/libntp/timevalops.c +++ b/libntp/timevalops.c @@ -8,17 +8,10 @@ #include #include -#include "lib_strbuf.h" -#include "ntp_calendar.h" - #include "timevalops.h" -/* formatting to string needs at max 29 bytes (even with 64 bit time_t), - * so we check LIB_BUFLENGTH is big enough for our pupose. - */ -#if LIB_BUFLENGTH < 29 -#error LIB_BUFLENGTH not big enough -#endif +#include "timetoa.h" +#include "ntp_calendar.h" /* make sure we have the right definition for MICROSECONDS */ #undef MICROSECONDS @@ -38,20 +31,6 @@ # define MYTVUTOF(tvu, tsf) TVUTOTSF(tvu, tsf) #endif -/* using snprintf is troublesome with time_t. Try to resolve it. */ -#if SIZEOF_TIME_T <= SIZEOF_INT -typedef unsigned int u_time; -#define TIMEFMT "" -#elif SIZEOF_TIME_T <= SIZEOF_LONG -typedef unsigned long u_time; -#define TIMEFMT "l" -#elif defined(SIZEOF_LONG_LONG) && SIZEOF_TIME_T <= SIZEOF_LONG_LONG -typedef unsigned long long u_time; -#define TIMEFMT "ll" -#else -#include "GRONK: what size has a time_t here?" -#endif - /* copy and normalise. Used often enough to warrant a macro. */ #define COPYNORM(dst, src) \ do { \ @@ -70,7 +49,7 @@ timeval_norm( * to do first partial normalisation. The normalisation loops * following will do the remaining cleanup. Since the size of * tv_usec has a peculiar definition by the standard the range - * check is coded manualla. + * check is coded manually. */ if (x->tv_usec < -3l * MICROSECONDS || x->tv_usec > 3l * MICROSECONDS ) { @@ -290,40 +269,7 @@ timeval_tostr( const struct timeval *x ) { - /* see timespecops.c for rationale -- this needs refactoring - * - * Even with 64 bit time_t, 32 chars will suffice. Hopefully, - * LIB_BUFLENGTH is big enough; the current definiton checks - * this by the preprocessor just at the top of this file. */ - static const char *fmt = "-%" TIMEFMT "u.%06lu"; - - struct timeval v; - char * cp; - int notneg; - u_time itmp; - u_long ftmp; - - /* normalise and get absolute value into unsigned values. Since - * the negation of TIME_T_MIN (if it existed) is implementation - * defined, we try to avoid it. */ - COPYNORM(&v, x); - notneg = v.tv_sec >= 0; - if (notneg != 0) { - itmp = (u_time)v.tv_sec; - ftmp = (u_long)v.tv_usec; - } else if (v.tv_usec != 0) { - itmp = (u_time)-(v.tv_sec + 1); - ftmp = (u_long)(MICROSECONDS - v.tv_usec); - } else { - itmp = ((u_time) -(v.tv_sec + 1)) + 1; - ftmp = 0; - } - - /* get buffer and format data */ - LIB_GETBUF(cp); - snprintf(cp, LIB_BUFLENGTH, fmt + notneg, itmp, ftmp); - - return cp; + return format_time_fraction(x->tv_sec, x->tv_usec, 6); } void diff --git a/tests/libntp/Makefile.am b/tests/libntp/Makefile.am index 3439f5757..e9efb2a99 100644 --- a/tests/libntp/Makefile.am +++ b/tests/libntp/Makefile.am @@ -45,6 +45,7 @@ tests_SOURCES = $(top_srcdir)/sntp/tests_main.cpp \ ssl_init.cpp \ statestr.cpp \ strtolfp.cpp \ + timestructs.cpp \ tspecops.cpp \ tvalops.cpp \ tsftomsu.cpp \ diff --git a/tests/libntp/timestructs.cpp b/tests/libntp/timestructs.cpp new file mode 100644 index 000000000..28f0668db --- /dev/null +++ b/tests/libntp/timestructs.cpp @@ -0,0 +1,127 @@ +/* + * timestructs.cpp -- test bed adaptors for time structs. + * + * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. + * The contents of 'html/copyright.html' apply. + */ +#include "libntptest.h" +#include "timestructs.h" + +extern "C" { +#include "ntp_unixtime.h" +#include "timetoa.h" +#include "ntp_fp.h" + +#include "timevalops.h" +} + +std::ostream& +operator << (std::ostream& os, const timeStruct::l_fp_wrap& val) +{ + // raw data formatting + os << "0x" << std::hex << val.V.l_ui << ':' + << std::setfill('0') << std::setw(8) << val.V.l_uf + << std::dec; + // human-readable format + os << '[' << dolfptoa(val.V.l_ui, val.V.l_uf, 0, 10, 0) << ']'; + return os; +} + +std::ostream& +operator << (std::ostream& os, const timeStruct::timeval_wrap& val) +{ + // raw data formatting + os << val.V.tv_sec << ':' << val.V.tv_usec; + // human-readable format + os << '[' + << format_time_fraction(val.V.tv_sec, val.V.tv_usec, 6) + << ']'; + return os; +} + +std::ostream& +operator << (std::ostream& os, const timeStruct::timespec_wrap& val) +{ + // raw data formatting + os << val.V.tv_sec << ':' << val.V.tv_nsec; + // human-readable format + os << '[' + << format_time_fraction(val.V.tv_sec, val.V.tv_nsec, 9) + << ']'; + return os; +} + +namespace timeStruct { + +// Implementation of the l_fp closeness predicate + +AssertFpClose::AssertFpClose( + u_int32 hi, + u_int32 lo + ) +{ + limit.l_ui = hi; + limit.l_uf = lo; +} + +::testing::AssertionResult +AssertFpClose::operator()( + const char* m_expr, + const char* n_expr, + const l_fp & m, + const l_fp & n + ) +{ + l_fp diff; + + if (L_ISGEQ(&m, &n)) { + diff = m; + L_SUB(&diff, &n); + } else { + diff = n; + L_SUB(&diff, &m); + } + if (L_ISGEQ(&limit, &diff)) + return ::testing::AssertionSuccess(); + + return ::testing::AssertionFailure() + << m_expr << " which is " << l_fp_wrap(m) + << "\nand\n" + << n_expr << " which is " << l_fp_wrap(n) + << "\nare not close; diff=" << l_fp_wrap(diff); +} + +// Implementation of the timeval closeness predicate + +AssertTimevalClose::AssertTimevalClose( + time_t hi, + int32 lo + ) +{ + limit.tv_sec = hi; + limit.tv_usec = lo; +} + +::testing::AssertionResult +AssertTimevalClose::operator()( + const char* m_expr, + const char* n_expr, + const struct timeval & m, + const struct timeval & n + ) +{ + struct timeval diff; + + timeval_sub(&diff, &m, &n); + timeval_abs(&diff, &diff); + if (timeval_cmp(&limit, &diff) >= 0) + return ::testing::AssertionSuccess(); + + return ::testing::AssertionFailure() + << m_expr << " which is " << timeval_wrap(m) + << "\nand\n" + << n_expr << " which is " << timeval_wrap(n) + << "\nare not close; diff=" << timeval_wrap(diff); +} + +} // namespace timeStruct diff --git a/tests/libntp/timestructs.h b/tests/libntp/timestructs.h new file mode 100644 index 000000000..14e2c44d4 --- /dev/null +++ b/tests/libntp/timestructs.h @@ -0,0 +1,159 @@ +/* + * timestructs.h -- test bed adaptors for time structs. + * + * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. + * The contents of 'html/copyright.html' apply. + * + * Some wrapper classes and a closeness predicate that are used to + * bridge the gap between the goggletest framework and the structs used + * for representing time stamps (l_fp, struct timeval, struct timespec). + * + * Some ostream conversion operators are provided to give diagnostic + * output on errors. The normal string conversion functions will give + * HRVs (human readable values) but we might also be interested in the + * machine representation for diagnostic purposes. + */ +#ifndef TIMESTRUCTS_H +#define TIMESTRUCTS_H + +extern "C" { +#include "ntp_types.h" +#include "ntp_fp.h" +} + +namespace timeStruct { + +// wrap a l_fp struct with common operations +class l_fp_wrap { + public: + l_fp V; + + l_fp_wrap() + { ZERO(V); } + l_fp_wrap(u_int32 hi, u_int32 lo) + { V.l_ui = hi; V.l_uf = lo; } + l_fp_wrap(const l_fp &rhs) + { V = rhs; } + bool operator == (const l_fp_wrap& rhs) const + { return L_ISEQU(&V, &rhs.V); } + operator l_fp* () + { return &V; } + operator l_fp& () + { return V; } + l_fp_wrap & operator = (const l_fp_wrap& rhs) + { V = rhs.V; return *this; } + l_fp_wrap& operator = (const l_fp& rhs) + { V = rhs; return *this; } + }; + +// wrap a 'struct timeval' with common operations +class timeval_wrap { +public: + struct timeval V; + + timeval_wrap() + { ZERO(V); } + timeval_wrap(time_t hi, long lo) + { V.tv_sec = hi; V.tv_usec = lo; } + timeval_wrap(const struct timeval & rhs) + { V = rhs; } + bool operator == (const timeval_wrap& rhs) const + { return V.tv_sec == rhs.V.tv_sec && + V.tv_usec == rhs.V.tv_usec ; } + bool valid() const + { return V.tv_usec >= 0 && V.tv_usec < 1000000; } + operator struct timeval* () + { return &V; } + operator struct timeval& () + { return V; } + timeval_wrap& operator = (const timeval_wrap& rhs) + { V = rhs.V; return *this; } + timeval_wrap& operator = (const struct timeval& rhs) + { V = rhs; return *this; } +}; + +// wrap a 'struct timespec' with common operations +class timespec_wrap { +public: + struct timespec V; + + timespec_wrap() + { ZERO(V); } + timespec_wrap(time_t hi, long lo) + { V.tv_sec = hi; V.tv_nsec = lo; } + timespec_wrap(const struct timespec & rhs) + { V = rhs; } + bool operator == (const timespec_wrap& rhs) const + { return V.tv_sec == rhs.V.tv_sec && + V.tv_nsec == rhs.V.tv_nsec ; } + bool valid() const + { return V.tv_nsec >= 0 && V.tv_nsec < 1000000000; } + operator struct timespec* () + { return &V; } + operator struct timespec& () + { return V; } + timespec_wrap& operator = (const timespec_wrap& rhs) + { V = rhs.V; return *this; } + timespec_wrap& operator = (const struct timespec& rhs) + { V = rhs; return *this; } +}; + +// l_fp closeness testing predicate +// +// This predicate is used for the closeness ('near') testing of l_fp +// values. Once constructed with a limit, it can be used to check the +// absolute difference of two l_fp structs against that limit; if the +// difference is less or equal to this limit, the test passes. +class AssertFpClose { +private: + l_fp limit; + +public: + AssertFpClose(u_int32 hi, u_int32 lo); + + ::testing::AssertionResult + operator()(const char* m_expr, const char* n_expr, + const l_fp & m, const l_fp & n); +}; + + +// timeval closeness testing predicate +// +// CAVEAT: This class uses the timevalops functions +// - timeval_sub +// - timeval_abs +// - timeval_cmp +// +// This creates a dependency loop of sorts. The loop is defused by the +// fact that these basic operations can be tested by exact value tests, +// so once the basic timeval operations passed it's safe to use this +// predicate. +class AssertTimevalClose { +private: + struct timeval limit; + +public: + // note: (hi,lo) should be a positive normalised timeval; + // the constructor does not normalise the values! + AssertTimevalClose(time_t hi, int32 lo); + + ::testing::AssertionResult + operator()(const char* m_expr, const char* n_expr, + const struct timeval & m, const struct timeval & n); +}; + + +} // namespace timeStruct + +// since googletest wants to string format items, we declare the +// necessary operators. Since all adaptors have only public members +// there is need for friend declarations anywhere. + +extern std::ostream& operator << (std::ostream& os, + const timeStruct::l_fp_wrap& val); +extern std::ostream& operator << (std::ostream& os, + const timeStruct::timeval_wrap& val); +extern std::ostream& operator << (std::ostream& os, + const timeStruct::timespec_wrap& val); + +#endif // TIMESTRUCTS_H diff --git a/tests/libntp/tvalops.cpp b/tests/libntp/tvalops.cpp index 2a8950b01..5fb5f7a77 100644 --- a/tests/libntp/tvalops.cpp +++ b/tests/libntp/tvalops.cpp @@ -1,4 +1,5 @@ #include "libntptest.h" +#include "timestructs.h" extern "C" { #include @@ -8,8 +9,14 @@ extern "C" { #include #include +using namespace timeStruct; + class timevalTest : public libntptest { protected: + + static u_int32 my_tick_to_tsf(u_int32 ticks); + static u_int32 my_tsf_to_tick(u_int32 tsf); + static const long MICROSECONDS; // that's it... struct lfpfracdata { @@ -19,6 +26,39 @@ protected: static const lfpfracdata fdata[]; }; +u_int32 +timevalTest::my_tick_to_tsf( + u_int32 ticks + ) +{ + // convert microseconds to l_fp fractional units, using double + // precision float calculations or, if available, 64bit integer + // arithmetic. This should give the precise fraction, rounded to + // the nearest representation. +#if SIZEOF_LONG >= 8 + return (u_int32)((((u_long)ticks << 32) + 500000) / 1000000); +#else + return (u_int32)floor((double)ticks * 4294.967296 + 0.5); +#endif + // And before you ask: if ticks >= 1000000, the result is + // truncated nonsense, so don't use it out-of-bounds. +} + +u_int32 +timevalTest::my_tsf_to_tick( + u_int32 tsf + ) +{ + // Inverse operation: converts fraction to microseconds. +#if SIZEOF_LONG >= 8 + return (u_int32)(((u_long)tsf * 1000000 + 0x80000000) >> 32); +#else + return (u_int32)floor((double)ticks / 4294.967296 + 0.5); +#endif + // Beware: The result might be 10^6 due to rounding! +} + + const long timevalTest::MICROSECONDS = 1000000; const timevalTest::lfpfracdata timevalTest::fdata [] = { { 0, 0x00000000 }, { 7478, 0x01ea1405 }, @@ -38,63 +78,11 @@ const timevalTest::lfpfracdata timevalTest::fdata [] = { }; -class TVAL { -public: - struct timeval V; - - TVAL() - { ZERO(V); } - TVAL(time_t hi, long lo) - { V.tv_sec = hi; V.tv_usec = lo; } - bool operator == (const TVAL& rhs) const - { return timeval_cmp(&V, &rhs.V) == 0; } - bool valid() const - { return timeval_isnormal(&V); } - operator struct timeval* () - { return &V; } - operator struct timeval& () - { return V; } - TVAL& operator = (const TVAL& rhs) - { V = rhs.V; return *this; } - TVAL& operator = (const struct timeval& rhs) - { V = rhs; return *this; } -}; - -std::ostream& -operator << (std::ostream& os, const TVAL& val) -{ - os << timeval_tostr(&val.V); - return os; -} +// and the global predicate instances we're using here +static AssertFpClose FpClose(0, 1); -class LFP { -public: - l_fp V; - - LFP() - { ZERO(V); } - LFP(u_int32 hi, u_int32 lo) - { V.l_ui = hi; V.l_uf = lo; } - bool operator == (const LFP& rhs) const - { return L_ISEQU(&V, &rhs.V); } - operator l_fp* () - { return &V; } - operator l_fp& () - { return V; } - LFP& operator = (const LFP& rhs) - { V = rhs.V; return *this; } - LFP& operator = (const l_fp& rhs) - { V = rhs; return *this; } -}; - -static std::ostream& -operator << (std::ostream& os, const LFP& val) -{ - os << ulfptoa(&val.V, 10); - return os; -} - +static AssertTimevalClose TimevalClose(0, 1); // --------------------------------------------------------------------- // test support stuff @@ -102,7 +90,7 @@ operator << (std::ostream& os, const LFP& val) TEST_F(timevalTest, Normalise) { for (long ns = -2000000000; ns <= 2000000000; ns += 10000000) { - TVAL x(0, ns); + timeval_wrap x(0, ns); timeval_norm(x); ASSERT_TRUE(x.valid()); @@ -112,9 +100,9 @@ TEST_F(timevalTest, Normalise) { TEST_F(timevalTest, SignNoFrac) { // sign test, no fraction for (int i = -4; i <= 4; ++i) { - TVAL a(i, 0); - int E = (i > 0) - (i < 0); - int r = timeval_test(a); + timeval_wrap a(i, 0); + int E = (i > 0) - (i < 0); + int r = timeval_test(a); ASSERT_EQ(E, r); } @@ -123,9 +111,9 @@ TEST_F(timevalTest, SignNoFrac) { TEST_F(timevalTest, SignWithFrac) { // sign test, with fraction for (int i = -4; i <= 4; ++i) { - TVAL a(i, 10); - int E = (i >= 0) - (i < 0); - int r = timeval_test(a); + timeval_wrap a(i, 10); + int E = (i >= 0) - (i < 0); + int r = timeval_test(a); ASSERT_EQ(E, r); } @@ -136,10 +124,10 @@ TEST_F(timevalTest, CmpFracEQ) { // fractions are equal for (int i = -4; i <= 4; ++i) for (int j = -4; j <= 4; ++j) { - TVAL a(i, 200); - TVAL b(j, 200); - int E = (i > j) - (i < j); - int r = timeval_cmp(a, b); + timeval_wrap a(i, 200); + timeval_wrap b(j, 200); + int E = (i > j) - (i < j); + int r = timeval_cmp(a, b); ASSERT_EQ(E, r); } @@ -149,10 +137,10 @@ TEST_F(timevalTest, CmpFracGT) { // fraction a bigger fraction b for (int i = -4; i <= 4; ++i) for (int j = -4; j <= 4; ++j) { - TVAL a( i , 999800); - TVAL b( j , 200); - int E = (i >= j) - (i < j); - int r = timeval_cmp(a, b); + timeval_wrap a( i , 999800); + timeval_wrap b( j , 200); + int E = (i >= j) - (i < j); + int r = timeval_cmp(a, b); ASSERT_EQ(E, r); } @@ -162,10 +150,10 @@ TEST_F(timevalTest, CmpFracLT) { // fraction a less fraction b for (int i = -4; i <= 4; ++i) for (int j = -4; j <= 4; ++j) { - TVAL a(i, 200); - TVAL b(j, 999800); - int E = (i > j) - (i <= j); - int r = timeval_cmp(a, b); + timeval_wrap a(i, 200); + timeval_wrap b(j, 999800); + int E = (i > j) - (i <= j); + int r = timeval_cmp(a, b); ASSERT_EQ(E, r); } @@ -175,10 +163,10 @@ TEST_F(timevalTest, CmpFracLT) { TEST_F(timevalTest, AddFullNorm) { for (int i = -4; i <= 4; ++i) for (int j = -4; j <= 4; ++j) { - TVAL a(i, 200); - TVAL b(j, 400); - TVAL E(i + j, 200 + 400); - TVAL c; + timeval_wrap a(i, 200); + timeval_wrap b(j, 400); + timeval_wrap E(i + j, 200 + 400); + timeval_wrap c; timeval_add(c, a, b); ASSERT_EQ(E, c); @@ -188,10 +176,10 @@ TEST_F(timevalTest, AddFullNorm) { TEST_F(timevalTest, AddFullOflow1) { for (int i = -4; i <= 4; ++i) for (int j = -4; j <= 4; ++j) { - TVAL a(i, 200); - TVAL b(j, 999900); - TVAL E(i + j + 1, 100); - TVAL c; + timeval_wrap a(i, 200); + timeval_wrap b(j, 999900); + timeval_wrap E(i + j + 1, 100); + timeval_wrap c; timeval_add(c, a, b); ASSERT_EQ(E, c); } @@ -199,9 +187,9 @@ TEST_F(timevalTest, AddFullOflow1) { TEST_F(timevalTest, AddUsecNorm) { for (int i = -4; i <= 4; ++i) { - TVAL a(i, 200); - TVAL E(i, 600); - TVAL c; + timeval_wrap a(i, 200); + timeval_wrap E(i, 600); + timeval_wrap c; timeval_addus(c, a, 400); ASSERT_EQ(E, c); @@ -210,9 +198,9 @@ TEST_F(timevalTest, AddUsecNorm) { TEST_F(timevalTest, AddUsecOflow1) { for (int i = -4; i <= 4; ++i) { - TVAL a(i, 200); - TVAL E(i + 1, 100); - TVAL c; + timeval_wrap a(i, 200); + timeval_wrap E(i + 1, 100); + timeval_wrap c; timeval_addus(c, a, MICROSECONDS - 100); ASSERT_EQ(E, c); @@ -223,10 +211,10 @@ TEST_F(timevalTest, AddUsecOflow1) { TEST_F(timevalTest, SubFullNorm) { for (int i = -4; i <= 4; ++i) for (int j = -4; j <= 4; ++j) { - TVAL a(i, 600); - TVAL b(j, 400); - TVAL E(i - j, 600 - 400); - TVAL c; + timeval_wrap a(i, 600); + timeval_wrap b(j, 400); + timeval_wrap E(i - j, 600 - 400); + timeval_wrap c; timeval_sub(c, a, b); ASSERT_EQ(E, c); @@ -236,10 +224,10 @@ TEST_F(timevalTest, SubFullNorm) { TEST_F(timevalTest, SubFullOflow) { for (int i = -4; i <= 4; ++i) for (int j = -4; j <= 4; ++j) { - TVAL a(i, 100); - TVAL b(j, 999900); - TVAL E(i - j - 1, 200); - TVAL c; + timeval_wrap a(i, 100); + timeval_wrap b(j, 999900); + timeval_wrap E(i - j - 1, 200); + timeval_wrap c; timeval_sub(c, a, b); ASSERT_EQ(E, c); @@ -248,9 +236,9 @@ TEST_F(timevalTest, SubFullOflow) { TEST_F(timevalTest, SubUsecNorm) { for (int i = -4; i <= 4; ++i) { - TVAL a(i, 600); - TVAL E(i, 200); - TVAL c; + timeval_wrap a(i, 600); + timeval_wrap E(i, 200); + timeval_wrap c; timeval_subus(c, a, 600 - 200); ASSERT_EQ(E, c); @@ -259,9 +247,9 @@ TEST_F(timevalTest, SubUsecNorm) { TEST_F(timevalTest, SubUsecOflow) { for (int i = -4; i <= 4; ++i) { - TVAL a(i, 100); - TVAL E(i - 1, 200); - TVAL c; + timeval_wrap a(i, 100); + timeval_wrap E(i - 1, 200); + timeval_wrap c; timeval_subus(c, a, MICROSECONDS - 100); ASSERT_EQ(E, c); @@ -271,9 +259,10 @@ TEST_F(timevalTest, SubUsecOflow) { // test negation TEST_F(timevalTest, Neg) { for (int i = -4; i <= 4; ++i) { - TVAL a(i, 100); - TVAL b; - TVAL c; + timeval_wrap a(i, 100); + timeval_wrap b; + timeval_wrap c; + timeval_neg(b, a); timeval_add(c, a, b); ASSERT_EQ(0, timeval_test(c)); @@ -283,9 +272,9 @@ TEST_F(timevalTest, Neg) { // test abs value TEST_F(timevalTest, AbsNoFrac) { for (int i = -4; i <= 4; ++i) { - TVAL a(i, 0); - TVAL b; - int c; + timeval_wrap a(i, 0); + timeval_wrap b; + int c; c = timeval_abs(b, a); ASSERT_EQ((i < 0), c); @@ -295,9 +284,9 @@ TEST_F(timevalTest, AbsNoFrac) { TEST_F(timevalTest, AbsWithFrac) { for (int i = -4; i <= 4; ++i) { - TVAL a(i, 100); - TVAL b; - int c; + timeval_wrap a(i, 100); + timeval_wrap b; + int c; c = timeval_abs(b, a); ASSERT_EQ((i < 0), c); @@ -306,56 +295,88 @@ TEST_F(timevalTest, AbsWithFrac) { } // conversion to l_fp +TEST_F(timevalTest, ToLFPbittest) { + for (u_int32 i = 0; i < 1000000; i++) { + timeval_wrap a(1, i); + l_fp_wrap E(1, my_tick_to_tsf(i)); + l_fp_wrap r; + + timeval_reltolfp(r, a); + ASSERT_PRED_FORMAT2(FpClose, E, r); + } +} + TEST_F(timevalTest, ToLFPrelPos) { for (int i = 0; i < COUNTOF(fdata); i++) { - TVAL a(1, fdata[i].usec); - LFP E(1, fdata[i].frac); - LFP r; - double Ef; - double rf; + timeval_wrap a(1, fdata[i].usec); + l_fp_wrap E(1, fdata[i].frac); + l_fp_wrap r; timeval_reltolfp(r, a); - LFPTOD(&E.V, Ef); - LFPTOD(&r.V, rf); - ASSERT_NEAR(Ef, rf, 1. / MICROSECONDS); + ASSERT_PRED_FORMAT2(FpClose, E, r); } } TEST_F(timevalTest, ToLFPrelNeg) { for (int i = 0; i < COUNTOF(fdata); i++) { - TVAL a(-1, fdata[i].usec); - LFP E(~0, fdata[i].frac); - LFP r; - double Ef; - double rf; + timeval_wrap a(-1, fdata[i].usec); + l_fp_wrap E(~0, fdata[i].frac); + l_fp_wrap r; timeval_reltolfp(r, a); - LFPTOD(&E.V, Ef); - LFPTOD(&r.V, rf); - ASSERT_NEAR(Ef, rf, 1. / MICROSECONDS); + ASSERT_PRED_FORMAT2(FpClose, E, r); } } TEST_F(timevalTest, ToLFPabs) { for (int i = 0; i < COUNTOF(fdata); i++) { - TVAL a(1, fdata[i].usec); - LFP E(1 + JAN_1970, fdata[i].frac); - LFP r; - double Ef; - double rf; + timeval_wrap a(1, fdata[i].usec); + l_fp_wrap E(1 + JAN_1970, fdata[i].frac); + l_fp_wrap r; timeval_abstolfp(r, a); - LFPTOD(&E.V, Ef); - LFPTOD(&r.V, rf); - ASSERT_NEAR(Ef, rf, 1. / MICROSECONDS); + ASSERT_PRED_FORMAT2(FpClose, E, r); + } +} + +// conversion from l_fp +TEST_F(timevalTest, FromLFPbittest) { + // Not *exactly* a bittest, because 2**32 tests would take a + // really long time even on very fast machines! So we do test + // every 1000 fractional units. + for (u_int32 tsf = 0; tsf < ~u_int32(1000); tsf += 1000) { + timeval_wrap E(1, my_tsf_to_tick(tsf)); + l_fp_wrap a(1, tsf); + timeval_wrap r; + + timeval_relfromlfp(r, a); + + // The conversion might be off by one microsecond when + // comparing to calculated value; the table-driven + // conversion does not use all bits from the FP fraction + // and this truncation will affect the rounding. + ASSERT_PRED_FORMAT2(TimevalClose, E, r); + } +} + +// usec -> frac -> usec roundtrip +TEST_F(timevalTest, LFProundtrip) { + for (u_int32 i = 0; i < 1000000; i++) { + timeval_wrap E(1, i); + l_fp_wrap a; + timeval_wrap r; + + timeval_reltolfp(a, E); + timeval_relfromlfp(r, a); + ASSERT_EQ(E, r); } } TEST_F(timevalTest, ToString) { static const struct { - time_t sec; - long usec; - const char * repr; + time_t sec; + long usec; + const char * repr; } data [] = { { 0, 0, "0.000000" }, { 2, 0, "2.000000" }, @@ -365,9 +386,9 @@ TEST_F(timevalTest, ToString) { {-1, 1, "-0.999999" } }; for (int i = 0; i < COUNTOF(data); ++i) { - TVAL a(data[i].sec, data[i].usec); - std::string E(data[i].repr); - std::string r(timeval_tostr(a)); + timeval_wrap a(data[i].sec, data[i].usec); + std::string E(data[i].repr); + std::string r(timeval_tostr(a)); ASSERT_EQ(E, r); }