Example #1
0
// ----------------------------------------------------------------------
// add dynamic leap second (like from peer/clock)
void test_addDynamic(void) {
	int            rc;
	leap_result_t  qr;

	static const uint32_t insns[] = {
		2982009600u,	//	29	# 1 Jul 1994
		3029443200u,	//	30	# 1 Jan 1996
		3076704000u,	//	31	# 1 Jul 1997
		3124137600u,	//	32	# 1 Jan 1999
		3345062400u,	//	33	# 1 Jan 2006
		3439756800u,	//	34	# 1 Jan 2009
		3550089600u,	//	35	# 1 Jul 2012
		0 // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	leap_table_t * pt = leapsec_get_table(0);
	int 		idx;

	for (idx=1; insns[idx]; ++idx) {
		rc = leapsec_add_dyn(TRUE, insns[idx] - 20*SECSPERDAY - 100, NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	// try to slip in a previous entry
	rc = leapsec_add_dyn(TRUE, insns[0] - 20*SECSPERDAY - 100, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
Example #2
0
/* ------------------------------------------------------------------ */
int/*BOOL*/
leapsec_add_fix(
	int            total,
	uint32_t       ttime,
	uint32_t       etime,
	const time_t * pivot)
{
	time_t         tpiv;
	leap_table_t * pt;
	vint64         tt64, et64;

	if (pivot == NULL) {
		time(&tpiv);
		pivot = &tpiv;
	}
	
	et64 = ntpcal_ntp_to_ntp(etime, pivot);
	tt64 = ntpcal_ntp_to_ntp(ttime, pivot);
	pt   = leapsec_get_table(TRUE);

	if (   ucmpv64(&et64, &pt->head.expire) <= 0
	   || !leapsec_raw(pt, &tt64, total, FALSE) )
		return FALSE;

	pt->lsig.etime = etime;
	pt->lsig.ttime = ttime;
	pt->lsig.taiof = (int16_t)total;

	pt->head.expire = et64;

	return leapsec_set_table(pt);
}
Example #3
0
/* ------------------------------------------------------------------ */
bool
leapsec_add_fix(
	int            total,
	uint32_t       ttime,
	uint32_t       etime,
	const time_t * pivot)
{
	time_t         tpiv;
	leap_table_t * pt;
	time64_t         tt64, et64;

	if (pivot == NULL) {
		time(&tpiv);
		pivot = &tpiv;
	}
	
	et64 = ntpcal_ntp_to_ntp(etime, pivot);
	tt64 = ntpcal_ntp_to_ntp(ttime, pivot);
	pt   = leapsec_get_table(true);

	if ((et64 <= pt->head.expire)
	   || !leapsec_raw(pt, tt64, total, false) )
		return false;

	pt->lsig.etime = etime;
	pt->lsig.ttime = ttime;
	pt->lsig.taiof = (int16_t)total;

	pt->head.expire = et64;

	return leapsec_set_table(pt);
}
Example #4
0
// ----------------------------------------------------------------------
// load file & check time-to-live
TEST(leapsec, loadFileTTL) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);
	time_t         pivot = 0x70000000;

	const uint32_t limit = 3610569600u;

	rc =   leapsec_load(pt, stringreader, &cp, false)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL(1, rc);

	// exactly 1 day to live
	rc = leapsec_daystolive(limit - 86400, &pivot);
	TEST_ASSERT_EQUAL( 1, rc);
	// less than 1 day to live
	rc = leapsec_daystolive(limit - 86399, &pivot);
	TEST_ASSERT_EQUAL( 0, rc);
	// hit expiration exactly
	rc = leapsec_daystolive(limit, &pivot);
	TEST_ASSERT_EQUAL( 0, rc);
	// expired since 1 sec
	rc = leapsec_daystolive(limit + 1, &pivot);
	TEST_ASSERT_EQUAL(-1, rc);
}
Example #5
0
/* ------------------------------------------------------------------ */
void
leapsec_getsig(
	leap_signature_t * psig)
{
	const leap_table_t * pt;

	pt = leapsec_get_table(FALSE);
	memcpy(psig, &pt->lsig, sizeof(leap_signature_t));
}
Example #6
0
static int/*BOOL*/
setup_clear_table(void)
{
	int            rc;
	leap_table_t * pt = leapsec_get_table(0);
	if (pt)
		leapsec_clear(pt);
	rc = leapsec_set_table(pt);
	return rc;
}
Example #7
0
static bool
setup_clear_table(void)
{
	bool            rc;
	leap_table_t * pt = leapsec_get_table(0);
	if (pt)
		leapsec_clear(pt);
	rc = leapsec_set_table(pt);
	return rc;
}
Example #8
0
static int/*BOOL*/
setup_load_table(
	const char * cp,
	int          blim)
{
	int            rc;
	leap_table_t * pt = leapsec_get_table(0);
	rc = (pt != NULL) && leapsec_load(pt, stringreader, &cp, blim);
	rc = rc && leapsec_set_table(pt);
	return rc;
}
Example #9
0
static bool
setup_load_table(
	const char * cp,
	bool          blim)
{
	bool            rc;
	leap_table_t * pt = leapsec_get_table(0);
	rc = (pt != NULL) && leapsec_load(pt, stringreader, &cp, blim);
	rc = rc && leapsec_set_table(pt);
	return rc;
}
Example #10
0
/* ------------------------------------------------------------------ */
int/*BOOL*/
leapsec_expired(
	uint32_t       when,
	const time_t * tpiv)
{
	const leap_table_t * pt;
	vint64 limit;

	pt = leapsec_get_table(FALSE);
	limit = ntpcal_ntp_to_ntp(when, tpiv);
	return ucmpv64(&limit, &pt->head.expire) >= 0;
}
Example #11
0
/* ------------------------------------------------------------------ */
bool
leapsec_expired(
	uint32_t       when,
	const time_t * tpiv)
{
	const leap_table_t * pt;
	time64_t limit;

	pt = leapsec_get_table(false);
	limit = ntpcal_ntp_to_ntp(when, tpiv);
	return (limit >= pt->head.expire);
}
Example #12
0
/* ------------------------------------------------------------------ */
int32_t
leapsec_daystolive(
	uint32_t       when,
	const time_t * tpiv)
{
	const leap_table_t * pt;
	vint64 limit;

	pt = leapsec_get_table(FALSE);
	limit = ntpcal_ntp_to_ntp(when, tpiv);
	limit = subv64(&pt->head.expire, &limit);
	return ntpcal_daysplit(&limit).hi;
}
Example #13
0
void test_loadFileExpire(void) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);

	rc =   leapsec_load(pt, stringreader, &cp, FALSE)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL_MESSAGE(1, rc,"first");
	rc = leapsec_expired(3439756800u, NULL);
	TEST_ASSERT_EQUAL(0, rc);
	rc = leapsec_expired(3610569601u, NULL);
	TEST_ASSERT_EQUAL(1, rc);
}
Example #14
0
// ----------------------------------------------------------------------
// load file & check expiration
TEST(leapsec, loadFileExpire) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);

	rc =   leapsec_load(pt, stringreader, &cp, false)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL(1, rc);
	rc = leapsec_expired(3439756800u, NULL);
	TEST_ASSERT_EQUAL(0, rc);
	rc = leapsec_expired(3610569601u, NULL);
	TEST_ASSERT_EQUAL(1, rc);
}
Example #15
0
/* ------------------------------------------------------------------ */
int32_t
leapsec_daystolive(
	uint32_t       when,
	const time_t * tpiv)
{
	const leap_table_t * pt;
	time64_t limit;

	pt = leapsec_get_table(false);
	limit = ntpcal_ntp_to_ntp(when, tpiv);
	limit = pt->head.expire - limit;
	return ntpcal_daysplit(limit).hi;
}
Example #16
0
/* ------------------------------------------------------------------ */
int/*BOOL*/
leapsec_add_dyn(
	int            insert,
	uint32_t       ntpnow,
	const time_t * pivot )
{
	leap_table_t * pt;
	vint64         now64;

	pt = leapsec_get_table(TRUE);
	now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
	return (   leapsec_add(pt, &now64, (insert != 0))
		&& leapsec_set_table(pt));
}
Example #17
0
/* ------------------------------------------------------------------ */
int/*BOOL*/
leapsec_query_era(
	leap_era_t *   qr   ,
	uint32_t       ntpts,
	const time_t * pivot)
{
	const leap_table_t * pt;
	vint64               ts64;

	pt   = leapsec_get_table(FALSE);
	ts64 = ntpcal_ntp_to_ntp(ntpts, pivot);
	fetch_leap_era(qr, pt, &ts64);
	return TRUE;
}
Example #18
0
/* ------------------------------------------------------------------ */
bool
leapsec_add_dyn(
	bool           insert,
	uint32_t       ntpnow,
	const time_t * pivot )
{
	leap_table_t * pt;
	time64_t         now64;

	pt = leapsec_get_table(true);
	now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
	return (   leapsec_add(pt, now64, insert)
		&& leapsec_set_table(pt));
}
Example #19
0
//#include "ntpdtest.h"
#include "config.h"


#include "ntp.h"
#include "ntp_calendar.h"
#include "ntp_stdlib.h"
#include "ntp_leapsec.h"

#include "unity.h"

#include <string.h>

#include "test-libntp.h"

static const char leap1 [] =
    "#\n"
    "#@ 	3610569600\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "   \t  \n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	34	# 1 Jan 2009\n"
    "3550089600	35	# 1 Jul 2012\n"
    "#\n"
    "#h	dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n"
    "#\n";

static const char leap2 [] =
    "#\n"
    "#@ 	2950473700\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "#\n";

// Faked table with a leap second removal at 2009 
static const char leap3 [] =
    "#\n"
    "#@ 	3610569600\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	32	# 1 Jan 2009\n"
    "3550089600	33	# 1 Jul 2012\n"
    "#\n";

// short table with good hash
static const char leap_ghash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h 4b304e10 95642b3f c10b91f9 90791725 25f280d0\n"
    "#\n";

// short table with bad hash
static const char leap_bhash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h	dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n"
    "#\n";

// short table with malformed hash
static const char leap_mhash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h f2349a02 788b9534 a8f2e141 f2029Q6d 4064a7ee\n"
    "#\n";

// short table with only 4 hash groups
static const char leap_shash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h f2349a02 788b9534 a8f2e141 f2029Q6d\n"
    "#\n";

// table with good hash and truncated/missing leading zeros
static const char leap_gthash [] = {
    "#\n"
    "#$	 3535228800\n"
    "#\n"
    "#	Updated through IERS Bulletin C46\n"
    "#	File expires on:  28 June 2014\n"
    "#\n"
    "#@	3612902400\n"
    "#\n"
    "2272060800	10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	34	# 1 Jan 2009\n"
    "3550089600	35	# 1 Jul 2012\n"
    "#\n"
    "#h	1151a8f e85a5069 9000fcdb 3d5e5365 1d505b37"
};

static const uint32_t lsec2006 = 3345062400u; // +33, 1 Jan 2006, 00:00:00 utc
static const uint32_t lsec2009 = 3439756800u; // +34, 1 Jan 2009, 00:00:00 utc
static const uint32_t lsec2012 = 3550089600u; // +35, 1 Jul 2012, 00:00:00 utc
static const uint32_t lsec2015 = 3644697600u; // +36, 1 Jul 2015, 00:00:00 utc

int stringreader(void* farg)
{
	const char ** cpp = (const char**)farg;
	if (**cpp)
		return *(*cpp)++;
	else
		return EOF;
}

static int/*BOOL*/
setup_load_table(
	const char * cp,
	int          blim)
{
	int            rc;
	leap_table_t * pt = leapsec_get_table(0);
	rc = (pt != NULL) && leapsec_load(pt, stringreader, &cp, blim);
	rc = rc && leapsec_set_table(pt);
	return rc;
}

static int/*BOOL*/
setup_clear_table(void)
{
	int            rc;
	leap_table_t * pt = leapsec_get_table(0);
	if (pt)
		leapsec_clear(pt);
	rc = leapsec_set_table(pt);
	return rc;
}


char * CalendarToString(const struct calendar cal) {
	char * ss = malloc (sizeof (char) * 100);
	
	char buffer[100] ="";
	sprintf(buffer, "%u", cal.year);
	strcat(ss,buffer);
	strcat(ss,"-");
	sprintf(buffer, "%u", (u_int)cal.month);
	strcat(ss,buffer);
	strcat(ss,"-");
	sprintf(buffer, "%u", (u_int)cal.monthday);
	strcat(ss,buffer);
	strcat(ss," (");
	sprintf(buffer, "%u", (u_int) cal.yearday);
	strcat(ss,buffer);
	strcat(ss,") ");
	sprintf(buffer, "%u", (u_int)cal.hour);
	strcat(ss,buffer);
	strcat(ss,":");
	sprintf(buffer, "%u", (u_int)cal.minute);
	strcat(ss,buffer);
	strcat(ss,":");
	sprintf(buffer, "%u", (u_int)cal.second);
	strcat(ss,buffer);
	//ss << cal.year << "-" << (u_int)cal.month << "-" << (u_int)cal.monthday << " (" << cal.yearday << ") " << (u_int)cal.hour << ":" << (u_int)cal.minute << ":" << (u_int)cal.second;
	return ss;
}


int IsEqual(const struct calendar expected, const struct calendar actual) {
	if (expected.year == actual.year &&
		(expected.yearday == actual.yearday ||
		 (expected.month == actual.month &&
		  expected.monthday == actual.monthday)) &&
		expected.hour == actual.hour &&
		expected.minute == actual.minute &&
		expected.second == actual.second) {
		return TRUE;
	} else {
		printf("expected: %s but was %s", CalendarToString(expected) ,CalendarToString(actual));
		return FALSE;
			
	}
}

//-------------------------

void setUp(void)
{
    ntpcal_set_timefunc(timefunc);
    settime(1970, 1, 1, 0, 0, 0);
    leapsec_ut_pristine();
}

void tearDown(void)
{
    ntpcal_set_timefunc(NULL);
}

// =====================================================================
// VALIDATION TESTS
// =====================================================================

// ----------------------------------------------------------------------
void test_ValidateGood(void) {
	const char *cp = leap_ghash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateNoHash(void) {
	const char *cp = leap2;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_NOHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateBad(void) {
	const char *cp = leap_bhash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateMalformed(void) {
	const char *cp = leap_mhash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADFORMAT, rc);
}

// ----------------------------------------------------------------------
void test_ValidateMalformedShort(void) {
	const char *cp = leap_shash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADFORMAT, rc);
}

// ----------------------------------------------------------------------
void test_ValidateNoLeadZero(void) {
	const char *cp = leap_gthash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc);
}

// =====================================================================
// BASIC FUNCTIONS
// =====================================================================

// ----------------------------------------------------------------------
// test table selection
void test_tableSelect(void) {
	leap_table_t *pt1, *pt2, *pt3, *pt4;

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_EQUAL_MESSAGE(pt1, pt2,"first");

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL_MESSAGE(pt1, pt2,"second");

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);

	pt1 = pt3;
	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);
}

// ----------------------------------------------------------------------
// load file & check expiration

void test_loadFileExpire(void) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);

	rc =   leapsec_load(pt, stringreader, &cp, FALSE)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL_MESSAGE(1, rc,"first");
	rc = leapsec_expired(3439756800u, NULL);
	TEST_ASSERT_EQUAL(0, rc);
	rc = leapsec_expired(3610569601u, NULL);
	TEST_ASSERT_EQUAL(1, rc);
}

// ----------------------------------------------------------------------
// load file & check time-to-live

void test_loadFileTTL(void) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);
	time_t         pivot = 0x70000000u;

	const uint32_t limit = 3610569600u;

	rc =   leapsec_load(pt, stringreader, &cp, FALSE)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL(1, rc); //

	// exactly 1 day to live
	rc = leapsec_daystolive(limit - 86400, &pivot);
	TEST_ASSERT_EQUAL( 1, rc);	
	// less than 1 day to live
	rc = leapsec_daystolive(limit - 86399, &pivot);
	TEST_ASSERT_EQUAL( 0, rc);	
	// hit expiration exactly
	rc = leapsec_daystolive(limit, &pivot);
	TEST_ASSERT_EQUAL( 0, rc);	
	// expired since 1 sec
	rc = leapsec_daystolive(limit + 1, &pivot);
	TEST_ASSERT_EQUAL(-1, rc);	
}

// =====================================================================
// RANDOM QUERY TESTS
// =====================================================================

// ----------------------------------------------------------------------
// test query in pristine state (bug#2745 misbehaviour)
void test_lsQueryPristineState(void) {
	int            rc;
	leap_result_t  qr;
	
	rc = leapsec_query(&qr, lsec2012, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(0,             qr.warped   );
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -60days
void test_ls2009faraway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 60 days before leap. Nothing scheduled or indicated.
	rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1week
void test_ls2009weekaway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 7 days before leap. Leap scheduled, but not yet indicated.
	rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1hr
void test_ls2009houraway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 1 hour before leap. 61 true seconds to go.
	rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1sec
void test_ls2009secaway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 1 second before leap (last boundary...) 2 true seconds to go.
	rc = leapsec_query(&qr, lsec2009 - 1, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump to leap second at 2009.01.01
void test_ls2009onspot(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot: treat leap second as already gone.
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(34, qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// test handling of the leap second at 2009.01.01 without table
void test_ls2009nodata(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_clear_table();
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot with empty table
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(0,  qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// test handling of the leap second at 2009.01.01 with culled data
void test_ls2009limdata(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1, TRUE);
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot with limited table - this is tricky.
	// The table used ends 2012; depending on the build date, the 2009 entry
	// might be included or culled. The resulting TAI offset must be either
	// 34 or 35 seconds, depending on the build date of the test. 
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_TRUE(34 <= qr.tai_offs);
	TEST_ASSERT_TRUE(35 >= qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// Far-distance forward jump into a transiton window.
void test_qryJumpFarAhead(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2006, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2012, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
}

// ----------------------------------------------------------------------
// Forward jump into the next transition window
void test_qryJumpAheadToTransition(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2009-SECSPERDAY, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2009+1, NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
}

// ----------------------------------------------------------------------
// Forward jump over the next transition window
void test_qryJumpAheadOverTransition(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2009-SECSPERDAY, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2009+5, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
}

// =====================================================================
// TABLE MODIFICATION AT RUNTIME
// =====================================================================

// ----------------------------------------------------------------------
// add dynamic leap second (like from peer/clock)
void test_addDynamic(void) {
	int            rc;
	leap_result_t  qr;

	static const uint32_t insns[] = {
		2982009600u,	//	29	# 1 Jul 1994
		3029443200u,	//	30	# 1 Jan 1996
		3076704000u,	//	31	# 1 Jul 1997
		3124137600u,	//	32	# 1 Jan 1999
		3345062400u,	//	33	# 1 Jan 2006
		3439756800u,	//	34	# 1 Jan 2009
		3550089600u,	//	35	# 1 Jul 2012
		0 // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	leap_table_t * pt = leapsec_get_table(0);
	int 		idx;

	for (idx=1; insns[idx]; ++idx) {
		rc = leapsec_add_dyn(TRUE, insns[idx] - 20*SECSPERDAY - 100, NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	// try to slip in a previous entry
	rc = leapsec_add_dyn(TRUE, insns[0] - 20*SECSPERDAY - 100, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}

// ----------------------------------------------------------------------
// add fixed leap seconds (like from network packet)
#if 0 /* currently unused -- possibly revived later */
void FAILtest_addFixed(void) {
	int            rc;
	leap_result_t  qr;

	static const struct { uint32_t tt; int of; } insns[] = {
		{2982009600u, 29},//	# 1 Jul 1994
		{3029443200u, 30},//	# 1 Jan 1996
		{3076704000u, 31},//	# 1 Jul 1997
		{3124137600u, 32},//	# 1 Jan 1999
		{3345062400u, 33},//	# 1 Jan 2006
		{3439756800u, 34},//	# 1 Jan 2009
		{3550089600u, 35},//	# 1 Jul 2012
		{0,0} // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);
	int idx;
	leap_table_t * pt = leapsec_get_table(0);
	// try to get in BAD time stamps...
	for (idx=0; insns[idx].tt; ++idx) {
	    rc = leapsec_add_fix(
		insns[idx].of,
		insns[idx].tt - 20*SECSPERDAY - 100,
		insns[idx].tt + SECSPERDAY,
		NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
	// now do it right
	for (idx=0; insns[idx].tt; ++idx) {
		rc = leapsec_add_fix(
		    insns[idx].of,
		    insns[idx].tt,
		    insns[idx].tt + SECSPERDAY,
		    NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	// try to slip in a previous entry
	rc = leapsec_add_fix(
	    insns[0].of,
	    insns[0].tt,
	    insns[0].tt + SECSPERDAY,
	    NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
Example #20
0
/* ------------------------------------------------------------------ */
int/*BOOL*/
leapsec_frame(
        leap_result_t *qr)
{
	const leap_table_t * pt;

        memset(qr, 0, sizeof(leap_result_t));
	pt = leapsec_get_table(FALSE);

	qr->tai_offs = pt->head.this_tai;
	qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
	qr->ebase    = pt->head.ebase;
	qr->ttime    = pt->head.ttime;
	qr->dynamic  = pt->head.dynls;

	return ucmpv64(&pt->head.ttime, &pt->head.stime) >= 0;
}
Example #21
0
/* ------------------------------------------------------------------ */
bool
leapsec_frame(
        leap_result_t *qr)
{
	const leap_table_t * pt;

        memset(qr, 0, sizeof(leap_result_t));
	pt = leapsec_get_table(false);
	if (pt->head.ttime <= pt->head.stime)
                return false;

	qr->tai_offs = pt->head.this_tai;
	qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
	qr->ttime    = pt->head.ttime;
	qr->dynamic  = pt->head.dynls;

        return true;
}
Example #22
0
//#include "ntpdtest.h"
#include "config.h"


#include "ntp.h"
#include "ntp_calendar.h"
#include "ntp_stdlib.h"
#include "ntp_leapsec.h"

#include "unity.h"

#include <string.h>

#include "test-libntp.h"

static const char leap1 [] =
    "#\n"
    "#@ 	3610569600\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "   \t  \n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	34	# 1 Jan 2009\n"
    "3550089600	35	# 1 Jul 2012\n"
    "#\n"
    "#h	dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n"
    "#\n";

static const char leap2 [] =
    "#\n"
    "#@ 	2950473700\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "#\n";

// Faked table with a leap second removal at 2009 
static const char leap3 [] =
    "#\n"
    "#@ 	3610569600\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	32	# 1 Jan 2009\n"
    "3550089600	33	# 1 Jul 2012\n"
    "#\n";

// short table with good hash
static const char leap_ghash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h 4b304e10 95642b3f c10b91f9 90791725 25f280d0\n"
    "#\n";

// short table with bad hash
static const char leap_bhash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h	dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n"
    "#\n";

// short table with malformed hash
static const char leap_mhash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h f2349a02 788b9534 a8f2e141 f2029Q6d 4064a7ee\n"
    "#\n";

// short table with only 4 hash groups
static const char leap_shash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h f2349a02 788b9534 a8f2e141 f2029Q6d\n"
    "#\n";

// table with good hash and truncated/missing leading zeros
static const char leap_gthash [] = {
    "#\n"
    "#$	 3535228800\n"
    "#\n"
    "#	Updated through IERS Bulletin C46\n"
    "#	File expires on:  28 June 2014\n"
    "#\n"
    "#@	3612902400\n"
    "#\n"
    "2272060800	10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	34	# 1 Jan 2009\n"
    "3550089600	35	# 1 Jul 2012\n"
    "#\n"
    "#h	1151a8f e85a5069 9000fcdb 3d5e5365 1d505b37"
};

static const uint32_t lsec2006 = 3345062400u; // +33, 1 Jan 2006, 00:00:00 utc
static const uint32_t lsec2009 = 3439756800u; // +34, 1 Jan 2009, 00:00:00 utc
static const uint32_t lsec2012 = 3550089600u; // +35, 1 Jul 2012, 00:00:00 utc
static const uint32_t lsec2015 = 3644697600u; // +36, 1 Jul 2015, 00:00:00 utc

int stringreader(void* farg)
{
	const char ** cpp = (const char**)farg;
	if (**cpp)
		return *(*cpp)++;
	else
		return EOF;
}

static int/*BOOL*/
setup_load_table(
	const char * cp,
	int          blim)
{
	int            rc;
	leap_table_t * pt = leapsec_get_table(0);
	rc = (pt != NULL) && leapsec_load(pt, stringreader, &cp, blim);
	rc = rc && leapsec_set_table(pt);
	return rc;
}

static int/*BOOL*/
setup_clear_table(void)
{
	int            rc;
	leap_table_t * pt = leapsec_get_table(0);
	if (pt)
		leapsec_clear(pt);
	rc = leapsec_set_table(pt);
	return rc;
}


char * CalendarToString(const struct calendar cal) {
	char * ss = malloc (sizeof (char) * 100);
	
	char buffer[100] ="";
	sprintf(buffer, "%u", cal.year);
	strcat(ss,buffer);
	strcat(ss,"-");
	sprintf(buffer, "%u", (u_int)cal.month);
	strcat(ss,buffer);
	strcat(ss,"-");
	sprintf(buffer, "%u", (u_int)cal.monthday);
	strcat(ss,buffer);
	strcat(ss," (");
	sprintf(buffer, "%u", (u_int) cal.yearday);
	strcat(ss,buffer);
	strcat(ss,") ");
	sprintf(buffer, "%u", (u_int)cal.hour);
	strcat(ss,buffer);
	strcat(ss,":");
	sprintf(buffer, "%u", (u_int)cal.minute);
	strcat(ss,buffer);
	strcat(ss,":");
	sprintf(buffer, "%u", (u_int)cal.second);
	strcat(ss,buffer);
	//ss << cal.year << "-" << (u_int)cal.month << "-" << (u_int)cal.monthday << " (" << cal.yearday << ") " << (u_int)cal.hour << ":" << (u_int)cal.minute << ":" << (u_int)cal.second;
	return ss;
}


int IsEqual(const struct calendar expected, const struct calendar actual) {
	if (expected.year == actual.year &&
		(expected.yearday == actual.yearday ||
		 (expected.month == actual.month &&
		  expected.monthday == actual.monthday)) &&
		expected.hour == actual.hour &&
		expected.minute == actual.minute &&
		expected.second == actual.second) {
		return TRUE;
	} else {
		printf("expected: %s but was %s", CalendarToString(expected) ,CalendarToString(actual));
		return FALSE;
			
	}
}

//-------------------------

void setUp(void)
{
    ntpcal_set_timefunc(timefunc);
    settime(1970, 1, 1, 0, 0, 0);
    leapsec_ut_pristine();
}

void tearDown(void)
{
    ntpcal_set_timefunc(NULL);
}

// =====================================================================
// VALIDATION TESTS
// =====================================================================

// ----------------------------------------------------------------------
void test_ValidateGood(void) {
	const char *cp = leap_ghash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateNoHash(void) {
	const char *cp = leap2;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_NOHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateBad(void) {
	const char *cp = leap_bhash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateMalformed(void) {
	const char *cp = leap_mhash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADFORMAT, rc);
}

// ----------------------------------------------------------------------
void test_ValidateMalformedShort(void) {
	const char *cp = leap_shash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADFORMAT, rc);
}

// ----------------------------------------------------------------------
void test_ValidateNoLeadZero(void) {
	const char *cp = leap_gthash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc);
}

// =====================================================================
// BASIC FUNCTIONS
// =====================================================================

// ----------------------------------------------------------------------
// test table selection
void test_tableSelect(void) {
	leap_table_t *pt1, *pt2, *pt3, *pt4;

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_EQUAL_MESSAGE(pt1, pt2,"first");

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL_MESSAGE(pt1, pt2,"second");

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);

	pt1 = pt3;
	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);
}

// ----------------------------------------------------------------------
// load file & check expiration

void test_loadFileExpire(void) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);

	rc =   leapsec_load(pt, stringreader, &cp, FALSE)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL_MESSAGE(1, rc,"first");
	rc = leapsec_expired(3439756800u, NULL);
	TEST_ASSERT_EQUAL(0, rc);
	rc = leapsec_expired(3610569601u, NULL);
	TEST_ASSERT_EQUAL(1, rc);
}

// ----------------------------------------------------------------------
// load file & check time-to-live

void test_loadFileTTL(void) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);
	time_t         pivot = 0x70000000u;

	const uint32_t limit = 3610569600u;

	rc =   leapsec_load(pt, stringreader, &cp, FALSE)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL(1, rc); //

	// exactly 1 day to live
	rc = leapsec_daystolive(limit - 86400, &pivot);
	TEST_ASSERT_EQUAL( 1, rc);	
	// less than 1 day to live
	rc = leapsec_daystolive(limit - 86399, &pivot);
	TEST_ASSERT_EQUAL( 0, rc);	
	// hit expiration exactly
	rc = leapsec_daystolive(limit, &pivot);
	TEST_ASSERT_EQUAL( 0, rc);	
	// expired since 1 sec
	rc = leapsec_daystolive(limit + 1, &pivot);
	TEST_ASSERT_EQUAL(-1, rc);	
}

// =====================================================================
// RANDOM QUERY TESTS
// =====================================================================

// ----------------------------------------------------------------------
// test query in pristine state (bug#2745 misbehaviour)
void test_lsQueryPristineState(void) {
	int            rc;
	leap_result_t  qr;
	
	rc = leapsec_query(&qr, lsec2012, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(0,             qr.warped   );
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -60days
void test_ls2009faraway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 60 days before leap. Nothing scheduled or indicated.
	rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1week
void test_ls2009weekaway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 7 days before leap. Leap scheduled, but not yet indicated.
	rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1hr
void test_ls2009houraway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 1 hour before leap. 61 true seconds to go.
	rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1sec
void test_ls2009secaway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 1 second before leap (last boundary...) 2 true seconds to go.
	rc = leapsec_query(&qr, lsec2009 - 1, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump to leap second at 2009.01.01
void test_ls2009onspot(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot: treat leap second as already gone.
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(34, qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// test handling of the leap second at 2009.01.01 without table
void test_ls2009nodata(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_clear_table();
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot with empty table
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(0,  qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// test handling of the leap second at 2009.01.01 with culled data
void test_ls2009limdata(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1, TRUE);
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot with limited table - this is tricky.
	// The table used ends 2012; depending on the build date, the 2009 entry
	// might be included or culled. The resulting TAI offset must be either
	// 34 or 35 seconds, depending on the build date of the test. 
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_TRUE(34 <= qr.tai_offs);
	TEST_ASSERT_TRUE(35 >= qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// Far-distance forward jump into a transiton window.
void test_qryJumpFarAhead(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2006, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2012, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
}

// ----------------------------------------------------------------------
// Forward jump into the next transition window
void test_qryJumpAheadToTransition(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2009-SECSPERDAY, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2009+1, NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
}

// ----------------------------------------------------------------------
// Forward jump over the next transition window
void test_qryJumpAheadOverTransition(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2009-SECSPERDAY, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2009+5, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
}

// =====================================================================
// TABLE MODIFICATION AT RUNTIME
// =====================================================================

// ----------------------------------------------------------------------
// add dynamic leap second (like from peer/clock)
void test_addDynamic(void) {
	int            rc;
	leap_result_t  qr;

	static const uint32_t insns[] = {
		2982009600u,	//	29	# 1 Jul 1994
		3029443200u,	//	30	# 1 Jan 1996
		3076704000u,	//	31	# 1 Jul 1997
		3124137600u,	//	32	# 1 Jan 1999
		3345062400u,	//	33	# 1 Jan 2006
		3439756800u,	//	34	# 1 Jan 2009
		3550089600u,	//	35	# 1 Jul 2012
		0 // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	leap_table_t * pt = leapsec_get_table(0);
	int 		idx;

	for (idx=1; insns[idx]; ++idx) {
		rc = leapsec_add_dyn(TRUE, insns[idx] - 20*SECSPERDAY - 100, NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	// try to slip in a previous entry
	rc = leapsec_add_dyn(TRUE, insns[0] - 20*SECSPERDAY - 100, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}

// ----------------------------------------------------------------------
// add fixed leap seconds (like from network packet)
#if 0 /* currently unused -- possibly revived later */
void FAILtest_addFixed(void) {
	int            rc;
	leap_result_t  qr;

	static const struct { uint32_t tt; int of; } insns[] = {
		{2982009600u, 29},//	# 1 Jul 1994
		{3029443200u, 30},//	# 1 Jan 1996
		{3076704000u, 31},//	# 1 Jul 1997
		{3124137600u, 32},//	# 1 Jan 1999
		{3345062400u, 33},//	# 1 Jan 2006
		{3439756800u, 34},//	# 1 Jan 2009
		{3550089600u, 35},//	# 1 Jul 2012
		{0,0} // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);
	int idx;
	leap_table_t * pt = leapsec_get_table(0);
	// try to get in BAD time stamps...
	for (idx=0; insns[idx].tt; ++idx) {
	    rc = leapsec_add_fix(
		insns[idx].of,
		insns[idx].tt - 20*SECSPERDAY - 100,
		insns[idx].tt + SECSPERDAY,
		NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
	// now do it right
	for (idx=0; insns[idx].tt; ++idx) {
		rc = leapsec_add_fix(
		    insns[idx].of,
		    insns[idx].tt,
		    insns[idx].tt + SECSPERDAY,
		    NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	// try to slip in a previous entry
	rc = leapsec_add_fix(
	    insns[0].of,
	    insns[0].tt,
	    insns[0].tt + SECSPERDAY,
	    NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
#endif

// ----------------------------------------------------------------------
// add fixed leap seconds (like from network packet)
#if 0 /* currently unused -- possibly revived later */
void FAILtest_addFixedExtend(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;

	static const struct { uint32_t tt; int of; } insns[] = {
		{2982009600u, 29},//	# 1 Jul 1994
		{3029443200u, 30},//	# 1 Jan 1996
		{0,0} // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	leap_table_t * pt = leapsec_get_table(FALSE);
	for (last=idx=0; insns[idx].tt; ++idx) {
		last = idx;
		rc = leapsec_add_fix(
		    insns[idx].of,
		    insns[idx].tt,
		    insns[idx].tt + SECSPERDAY,
		    NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	
	// try to extend the expiration of the last entry
	rc = leapsec_add_fix(
	    insns[last].of,
	    insns[last].tt,
	    insns[last].tt + 128*SECSPERDAY,
	    NULL);
	TEST_ASSERT_EQUAL(TRUE, rc);
	
	// try to extend the expiration of the last entry with wrong offset
	rc = leapsec_add_fix(
	    insns[last].of+1,
	    insns[last].tt,
	    insns[last].tt + 129*SECSPERDAY,
	    NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
Example #23
0
//#include "ntpdtest.h"
#include "config.h"


#include "ntp.h"
#include "ntp_calendar.h"
#include "ntp_stdlib.h"
#include "ntp_leapsec.h"

#include "unity.h"

#include <string.h>

#include "test-libntp.h"

static const char leap1 [] =
    "#\n"
    "#@ 	3610569600\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "   \t  \n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	34	# 1 Jan 2009\n"
    "3550089600	35	# 1 Jul 2012\n"
    "#\n"
    "#h	dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n"
    "#\n";

static const char leap2 [] =
    "#\n"
    "#@ 	2950473700\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "#\n";

// Faked table with a leap second removal at 2009 
static const char leap3 [] =
    "#\n"
    "#@ 	3610569600\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	32	# 1 Jan 2009\n"
    "3550089600	33	# 1 Jul 2012\n"
    "#\n";

// short table with good hash
static const char leap_ghash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h 4b304e10 95642b3f c10b91f9 90791725 25f280d0\n"
    "#\n";

// short table with bad hash
static const char leap_bhash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h	dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n"
    "#\n";

// short table with malformed hash
static const char leap_mhash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h f2349a02 788b9534 a8f2e141 f2029Q6d 4064a7ee\n"
    "#\n";

// short table with only 4 hash groups
static const char leap_shash [] =
    "#\n"
    "#@ 	3610569600\n"
    "#$ 	3610566000\n"
    "#\n"
    "2272060800 10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "#\n"
    "#h f2349a02 788b9534 a8f2e141 f2029Q6d\n"
    "#\n";

// table with good hash and truncated/missing leading zeros
static const char leap_gthash [] = {
    "#\n"
    "#$	 3535228800\n"
    "#\n"
    "#	Updated through IERS Bulletin C46\n"
    "#	File expires on:  28 June 2014\n"
    "#\n"
    "#@	3612902400\n"
    "#\n"
    "2272060800	10	# 1 Jan 1972\n"
    "2287785600	11	# 1 Jul 1972\n"
    "2303683200	12	# 1 Jan 1973\n"
    "2335219200	13	# 1 Jan 1974\n"
    "2366755200	14	# 1 Jan 1975\n"
    "2398291200	15	# 1 Jan 1976\n"
    "2429913600	16	# 1 Jan 1977\n"
    "2461449600	17	# 1 Jan 1978\n"
    "2492985600	18	# 1 Jan 1979\n"
    "2524521600	19	# 1 Jan 1980\n"
    "2571782400	20	# 1 Jul 1981\n"
    "2603318400	21	# 1 Jul 1982\n"
    "2634854400	22	# 1 Jul 1983\n"
    "2698012800	23	# 1 Jul 1985\n"
    "2776982400	24	# 1 Jan 1988\n"
    "2840140800	25	# 1 Jan 1990\n"
    "2871676800	26	# 1 Jan 1991\n"
    "2918937600	27	# 1 Jul 1992\n"
    "2950473600	28	# 1 Jul 1993\n"
    "2982009600	29	# 1 Jul 1994\n"
    "3029443200	30	# 1 Jan 1996\n"
    "3076704000	31	# 1 Jul 1997\n"
    "3124137600	32	# 1 Jan 1999\n"
    "3345062400	33	# 1 Jan 2006\n"
    "3439756800	34	# 1 Jan 2009\n"
    "3550089600	35	# 1 Jul 2012\n"
    "#\n"
    "#h	1151a8f e85a5069 9000fcdb 3d5e5365 1d505b37"
};

static const uint32_t lsec2006 = 3345062400u; // +33, 1 Jan 2006, 00:00:00 utc
static const uint32_t lsec2009 = 3439756800u; // +34, 1 Jan 2009, 00:00:00 utc
static const uint32_t lsec2012 = 3550089600u; // +35, 1 Jul 2012, 00:00:00 utc
static const uint32_t lsec2015 = 3644697600u; // +36, 1 Jul 2015, 00:00:00 utc

int stringreader(void* farg)
{
	const char ** cpp = (const char**)farg;
	if (**cpp)
		return *(*cpp)++;
	else
		return EOF;
}

static int/*BOOL*/
setup_load_table(
	const char * cp,
	int          blim)
{
	int            rc;
	leap_table_t * pt = leapsec_get_table(0);
	rc = (pt != NULL) && leapsec_load(pt, stringreader, &cp, blim);
	rc = rc && leapsec_set_table(pt);
	return rc;
}

static int/*BOOL*/
setup_clear_table(void)
{
	int            rc;
	leap_table_t * pt = leapsec_get_table(0);
	if (pt)
		leapsec_clear(pt);
	rc = leapsec_set_table(pt);
	return rc;
}


char * CalendarToString(const struct calendar cal) {
	char * ss = malloc (sizeof (char) * 100);
	
	char buffer[100] ="";
	sprintf(buffer, "%u", cal.year);
	strcat(ss,buffer);
	strcat(ss,"-");
	sprintf(buffer, "%u", (u_int)cal.month);
	strcat(ss,buffer);
	strcat(ss,"-");
	sprintf(buffer, "%u", (u_int)cal.monthday);
	strcat(ss,buffer);
	strcat(ss," (");
	sprintf(buffer, "%u", (u_int) cal.yearday);
	strcat(ss,buffer);
	strcat(ss,") ");
	sprintf(buffer, "%u", (u_int)cal.hour);
	strcat(ss,buffer);
	strcat(ss,":");
	sprintf(buffer, "%u", (u_int)cal.minute);
	strcat(ss,buffer);
	strcat(ss,":");
	sprintf(buffer, "%u", (u_int)cal.second);
	strcat(ss,buffer);
	//ss << cal.year << "-" << (u_int)cal.month << "-" << (u_int)cal.monthday << " (" << cal.yearday << ") " << (u_int)cal.hour << ":" << (u_int)cal.minute << ":" << (u_int)cal.second;
	return ss;
}


int IsEqual(const struct calendar expected, const struct calendar actual) {
	if (expected.year == actual.year &&
		(expected.yearday == actual.yearday ||
		 (expected.month == actual.month &&
		  expected.monthday == actual.monthday)) &&
		expected.hour == actual.hour &&
		expected.minute == actual.minute &&
		expected.second == actual.second) {
		return TRUE;
	} else {
		printf("expected: %s but was %s", CalendarToString(expected) ,CalendarToString(actual));
		return FALSE;
			
	}
}

//-------------------------

void setUp(void)
{
    ntpcal_set_timefunc(timefunc);
    settime(1970, 1, 1, 0, 0, 0);
    leapsec_ut_pristine();
}

void tearDown(void)
{
    ntpcal_set_timefunc(NULL);
}

// =====================================================================
// VALIDATION TESTS
// =====================================================================

// ----------------------------------------------------------------------
void test_ValidateGood(void) {
	const char *cp = leap_ghash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateNoHash(void) {
	const char *cp = leap2;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_NOHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateBad(void) {
	const char *cp = leap_bhash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADHASH, rc);
}

// ----------------------------------------------------------------------
void test_ValidateMalformed(void) {
	const char *cp = leap_mhash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADFORMAT, rc);
}

// ----------------------------------------------------------------------
void test_ValidateMalformedShort(void) {
	const char *cp = leap_shash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_BADFORMAT, rc);
}

// ----------------------------------------------------------------------
void test_ValidateNoLeadZero(void) {
	const char *cp = leap_gthash;
	int         rc = leapsec_validate(stringreader, &cp);
	TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc);
}

// =====================================================================
// BASIC FUNCTIONS
// =====================================================================

// ----------------------------------------------------------------------
// test table selection
void test_tableSelect(void) {
	leap_table_t *pt1, *pt2, *pt3, *pt4;

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_EQUAL_MESSAGE(pt1, pt2,"first");

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL_MESSAGE(pt1, pt2,"second");

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);

	pt1 = pt3;
	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);
}

// ----------------------------------------------------------------------
// load file & check expiration

void test_loadFileExpire(void) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);

	rc =   leapsec_load(pt, stringreader, &cp, FALSE)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL_MESSAGE(1, rc,"first");
	rc = leapsec_expired(3439756800u, NULL);
	TEST_ASSERT_EQUAL(0, rc);
	rc = leapsec_expired(3610569601u, NULL);
	TEST_ASSERT_EQUAL(1, rc);
}

// ----------------------------------------------------------------------
// load file & check time-to-live

void test_loadFileTTL(void) {
	const char *cp = leap1;
	int rc;
	leap_table_t * pt = leapsec_get_table(0);
	time_t         pivot = 0x70000000u;

	const uint32_t limit = 3610569600u;

	rc =   leapsec_load(pt, stringreader, &cp, FALSE)
	    && leapsec_set_table(pt);
	TEST_ASSERT_EQUAL(1, rc); //

	// exactly 1 day to live
	rc = leapsec_daystolive(limit - 86400, &pivot);
	TEST_ASSERT_EQUAL( 1, rc);	
	// less than 1 day to live
	rc = leapsec_daystolive(limit - 86399, &pivot);
	TEST_ASSERT_EQUAL( 0, rc);	
	// hit expiration exactly
	rc = leapsec_daystolive(limit, &pivot);
	TEST_ASSERT_EQUAL( 0, rc);	
	// expired since 1 sec
	rc = leapsec_daystolive(limit + 1, &pivot);
	TEST_ASSERT_EQUAL(-1, rc);	
}

// =====================================================================
// RANDOM QUERY TESTS
// =====================================================================

// ----------------------------------------------------------------------
// test query in pristine state (bug#2745 misbehaviour)
void test_lsQueryPristineState(void) {
	int            rc;
	leap_result_t  qr;
	
	rc = leapsec_query(&qr, lsec2012, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(0,             qr.warped   );
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -60days
void test_ls2009faraway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 60 days before leap. Nothing scheduled or indicated.
	rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1week
void test_ls2009weekaway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 7 days before leap. Leap scheduled, but not yet indicated.
	rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1hr
void test_ls2009houraway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 1 hour before leap. 61 true seconds to go.
	rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1sec
void test_ls2009secaway(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test 1 second before leap (last boundary...) 2 true seconds to go.
	rc = leapsec_query(&qr, lsec2009 - 1, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(33, qr.tai_offs);
	TEST_ASSERT_EQUAL(1,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity);
}

// ----------------------------------------------------------------------
// ad-hoc jump to leap second at 2009.01.01
void test_ls2009onspot(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1,FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot: treat leap second as already gone.
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(34, qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// test handling of the leap second at 2009.01.01 without table
void test_ls2009nodata(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_clear_table();
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot with empty table
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_EQUAL(0,  qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// test handling of the leap second at 2009.01.01 with culled data
void test_ls2009limdata(void) {
	int            rc;
	leap_result_t  qr;

	rc = setup_load_table(leap1, TRUE);
	TEST_ASSERT_EQUAL(1, rc);

	// test on-spot with limited table - this is tricky.
	// The table used ends 2012; depending on the build date, the 2009 entry
	// might be included or culled. The resulting TAI offset must be either
	// 34 or 35 seconds, depending on the build date of the test. 
	rc = leapsec_query(&qr, lsec2009, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	TEST_ASSERT_TRUE(34 <= qr.tai_offs);
	TEST_ASSERT_TRUE(35 >= qr.tai_offs);
	TEST_ASSERT_EQUAL(0,  qr.tai_diff);
	TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity);
}

// ----------------------------------------------------------------------
// Far-distance forward jump into a transiton window.
void test_qryJumpFarAhead(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2006, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2012, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
}

// ----------------------------------------------------------------------
// Forward jump into the next transition window
void test_qryJumpAheadToTransition(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2009-SECSPERDAY, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2009+1, NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
}

// ----------------------------------------------------------------------
// Forward jump over the next transition window
void test_qryJumpAheadOverTransition(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;
	int 		mode;

	for (mode=0; mode < 2; ++mode) {
		leapsec_ut_pristine();
		rc = setup_load_table(leap1, FALSE);
		TEST_ASSERT_EQUAL(1, rc);
		leapsec_electric(mode);

		rc = leapsec_query(&qr, lsec2009-SECSPERDAY, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);

		rc = leapsec_query(&qr, lsec2009+5, NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
}

// =====================================================================
// TABLE MODIFICATION AT RUNTIME
// =====================================================================

// ----------------------------------------------------------------------
// add dynamic leap second (like from peer/clock)
void test_addDynamic(void) {
	int            rc;
	leap_result_t  qr;

	static const uint32_t insns[] = {
		2982009600u,	//	29	# 1 Jul 1994
		3029443200u,	//	30	# 1 Jan 1996
		3076704000u,	//	31	# 1 Jul 1997
		3124137600u,	//	32	# 1 Jan 1999
		3345062400u,	//	33	# 1 Jan 2006
		3439756800u,	//	34	# 1 Jan 2009
		3550089600u,	//	35	# 1 Jul 2012
		0 // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	leap_table_t * pt = leapsec_get_table(0);
	int 		idx;

	for (idx=1; insns[idx]; ++idx) {
		rc = leapsec_add_dyn(TRUE, insns[idx] - 20*SECSPERDAY - 100, NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	// try to slip in a previous entry
	rc = leapsec_add_dyn(TRUE, insns[0] - 20*SECSPERDAY - 100, NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}

// ----------------------------------------------------------------------
// add fixed leap seconds (like from network packet)
#if 0 /* currently unused -- possibly revived later */
void FAILtest_addFixed(void) {
	int            rc;
	leap_result_t  qr;

	static const struct { uint32_t tt; int of; } insns[] = {
		{2982009600u, 29},//	# 1 Jul 1994
		{3029443200u, 30},//	# 1 Jan 1996
		{3076704000u, 31},//	# 1 Jul 1997
		{3124137600u, 32},//	# 1 Jan 1999
		{3345062400u, 33},//	# 1 Jan 2006
		{3439756800u, 34},//	# 1 Jan 2009
		{3550089600u, 35},//	# 1 Jul 2012
		{0,0} // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);
	int idx;
	leap_table_t * pt = leapsec_get_table(0);
	// try to get in BAD time stamps...
	for (idx=0; insns[idx].tt; ++idx) {
	    rc = leapsec_add_fix(
		insns[idx].of,
		insns[idx].tt - 20*SECSPERDAY - 100,
		insns[idx].tt + SECSPERDAY,
		NULL);
		TEST_ASSERT_EQUAL(FALSE, rc);
	}
	// now do it right
	for (idx=0; insns[idx].tt; ++idx) {
		rc = leapsec_add_fix(
		    insns[idx].of,
		    insns[idx].tt,
		    insns[idx].tt + SECSPERDAY,
		    NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	// try to slip in a previous entry
	rc = leapsec_add_fix(
	    insns[0].of,
	    insns[0].tt,
	    insns[0].tt + SECSPERDAY,
	    NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
#endif

// ----------------------------------------------------------------------
// add fixed leap seconds (like from network packet)
#if 0 /* currently unused -- possibly revived later */
void FAILtest_addFixedExtend(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;

	static const struct { uint32_t tt; int of; } insns[] = {
		{2982009600u, 29},//	# 1 Jul 1994
		{3029443200u, 30},//	# 1 Jan 1996
		{0,0} // sentinel
	};

	rc = setup_load_table(leap2, FALSE);
	TEST_ASSERT_EQUAL(1, rc);

	leap_table_t * pt = leapsec_get_table(FALSE);
	for (last=idx=0; insns[idx].tt; ++idx) {
		last = idx;
		rc = leapsec_add_fix(
		    insns[idx].of,
		    insns[idx].tt,
		    insns[idx].tt + SECSPERDAY,
		    NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	
	// try to extend the expiration of the last entry
	rc = leapsec_add_fix(
	    insns[last].of,
	    insns[last].tt,
	    insns[last].tt + 128*SECSPERDAY,
	    NULL);
	TEST_ASSERT_EQUAL(TRUE, rc);
	
	// try to extend the expiration of the last entry with wrong offset
	rc = leapsec_add_fix(
	    insns[last].of+1,
	    insns[last].tt,
	    insns[last].tt + 129*SECSPERDAY,
	    NULL);
	TEST_ASSERT_EQUAL(FALSE, rc);
	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
#endif

// ----------------------------------------------------------------------
// add fixed leap seconds (like from network packet) in an otherwise
// empty table and test queries before / between /after the tabulated
// values.
#if 0 /* currently unused -- possibly revived later */
void FAILtest_setFixedExtend(void) {
	int            rc;
	leap_result_t  qr;
	int            last, idx;

	static const struct { uint32_t tt; int of; } insns[] = {
		{2982009600u, 29},//	# 1 Jul 1994
		{3029443200u, 30},//	# 1 Jan 1996
		{0,0} // sentinel
	};

	leap_table_t * pt = leapsec_get_table(0);
	for (last=idx=0; insns[idx].tt; ++idx) {
		last = idx;
		rc = leapsec_add_fix(
		    insns[idx].of,
		    insns[idx].tt,
		    insns[idx].tt + 128*SECSPERDAY,
		    NULL);
		TEST_ASSERT_EQUAL(TRUE, rc);
	}
	
	rc = leapsec_query(&qr, insns[0].tt - 86400, NULL);
	TEST_ASSERT_EQUAL(28, qr.tai_offs);

	rc = leapsec_query(&qr, insns[0].tt + 86400, NULL);
	TEST_ASSERT_EQUAL(29, qr.tai_offs);

	rc = leapsec_query(&qr, insns[1].tt - 86400, NULL);
	TEST_ASSERT_EQUAL(29, qr.tai_offs);

	rc = leapsec_query(&qr, insns[1].tt + 86400, NULL);
	TEST_ASSERT_EQUAL(30, qr.tai_offs);

	//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
Example #24
0
/* load a file from a FILE pointer. Note: If hcheck is true, load
 * only after successful signature check. The stream must be seekable
 * or this will fail.
 */
int/*BOOL*/
leapsec_load_stream(
	FILE       * ifp  ,
	const char * fname,
	int/*BOOL*/  logall)
{
	leap_table_t *pt;
	int           rcheck;

	if (NULL == fname)
		fname = "<unknown>";

	rcheck = leapsec_validate((leapsec_reader)getc, ifp);
	if (logall)
		switch (rcheck)
		{
		case LSVALID_GOODHASH:
			msyslog(LOG_NOTICE, "%s ('%s'): good hash signature",
				logPrefix, fname);
			break;
			
		case LSVALID_NOHASH:
			msyslog(LOG_ERR, "%s ('%s'): no hash signature",
				logPrefix, fname);
			break;
		case LSVALID_BADHASH:
			msyslog(LOG_ERR, "%s ('%s'): signature mismatch",
				logPrefix, fname);
			break;
		case LSVALID_BADFORMAT:
			msyslog(LOG_ERR, "%s ('%s'): malformed hash signature",
				logPrefix, fname);
			break;
		default:
			msyslog(LOG_ERR, "%s ('%s'): unknown error code %d",
				logPrefix, fname, rcheck);
			break;
		}
	if (rcheck < 0)
		return FALSE;

	rewind(ifp);
	pt = leapsec_get_table(TRUE);
	if (!leapsec_load(pt, (leapsec_reader)getc, ifp, TRUE)) {
		switch (errno) {
		case EINVAL:
			msyslog(LOG_ERR, "%s ('%s'): bad transition time",
				logPrefix, fname);
			break;
		case ERANGE:
			msyslog(LOG_ERR, "%s ('%s'): times not ascending",
				logPrefix, fname);
			break;
		default:
			msyslog(LOG_ERR, "%s ('%s'): parsing error",
				logPrefix, fname);
			break;
		}
		return FALSE;
	}

	if (pt->head.size)
		msyslog(LOG_NOTICE, "%s ('%s'): loaded, expire=%s last=%s ofs=%d",
			logPrefix, fname, lstostr(&pt->head.expire),
			lstostr(&pt->info[0].ttime), pt->info[0].taiof);
	else
		msyslog(LOG_NOTICE,
			"%s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)",
			logPrefix, fname, lstostr(&pt->head.expire),
			pt->head.base_tai);
	
	return leapsec_set_table(pt);
}
Example #25
0
// ----------------------------------------------------------------------
// test table selection
TEST(leapsec, tableSelect) {
	leap_table_t *pt1, *pt2, *pt3;

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_EQUAL(pt1, pt2);

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);

	pt1 = pt3;
	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);
}
Example #26
0
// ----------------------------------------------------------------------
// test table selection
void test_tableSelect(void) {
	leap_table_t *pt1, *pt2, *pt3, *pt4;

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_EQUAL_MESSAGE(pt1, pt2,"first");

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL_MESSAGE(pt1, pt2,"second");

	pt1 = leapsec_get_table(1);
	pt2 = leapsec_get_table(0);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	pt1 = leapsec_get_table(0);
	pt2 = leapsec_get_table(1);
	TEST_ASSERT_NOT_EQUAL(pt1, pt2);

	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);

	pt1 = pt3;
	leapsec_set_table(pt1);
	pt2 = leapsec_get_table(0);
	pt3 = leapsec_get_table(1);
	TEST_ASSERT_EQUAL(pt1, pt2);
	TEST_ASSERT_NOT_EQUAL(pt2, pt3);
}
Example #27
0
/* ------------------------------------------------------------------ */
int/*BOOL*/
leapsec_autokey_tai(
	int            tai_offset,
	uint32_t       ntpnow    ,
	const time_t * pivot     )
{
	leap_table_t * pt;
	leap_era_t     era;
	vint64         now64;
	int            idx;

	(void)tai_offset;
	pt = leapsec_get_table(FALSE);

	/* Bail out if the basic offset is not zero and the putative
	 * offset is bigger than 10s. That was in 1972 -- we don't want
	 * to go back that far!
	 */
	if (pt->head.base_tai != 0 || tai_offset < 10)
		return FALSE;

	/* If there's already data in the table, check if an update is
	 * possible. Update is impossible if there are static entries
	 * (since this indicates a valid leapsecond file) or if we're
	 * too close to a leapsecond transition: We do not know on what
	 * side the transition the sender might have been, so we use a
	 * dead zone around the transition.
	 */

	/* Check for static entries */
	for (idx = 0; idx != pt->head.size; idx++)
		if ( ! pt->info[idx].dynls)
			return FALSE;

	/* get the fulll time stamp and leap era for it */
	now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
	fetch_leap_era(&era, pt, &now64);

	/* check the limits with 20s dead band */
	era.ebase = addv64i32(&era.ebase,  20);
	if (ucmpv64(&now64, &era.ebase) < 0)
		return FALSE;

	era.ttime = addv64i32(&era.ttime, -20);
	if (ucmpv64(&now64, &era.ttime) > 0)
		return FALSE;

	/* Here we can proceed. Calculate the delta update. */
	tai_offset -= era.taiof;

	/* Shift the header info offsets. */
	pt->head.base_tai += tai_offset;
	pt->head.this_tai += tai_offset;
	pt->head.next_tai += tai_offset;

	/* Shift table entry offsets (if any) */
	for (idx = 0; idx != pt->head.size; idx++)
		pt->info[idx].taiof += tai_offset;

	/* claim success... */
	return TRUE;
}
Example #28
0
int/*BOOL*/
leapsec_query(
	leap_result_t * qr   ,
	uint32_t        ts32 ,
	const time_t *  pivot)
{
	leap_table_t *   pt;
	vint64           ts64, last, next;
	uint32_t         due32;
	int              fired;

	/* preset things we use later on... */
	fired = FALSE;
	ts64  = ntpcal_ntp_to_ntp(ts32, pivot);
	pt    = leapsec_get_table(FALSE);
	memset(qr, 0, sizeof(leap_result_t));

	if (ucmpv64(&ts64, &pt->head.ebase) < 0) {
		/* Most likely after leap frame reset. Could also be a
		 * backstep of the system clock. Anyway, get the new
		 * leap era frame.
		 */
		reload_limits(pt, &ts64);
	} else if (ucmpv64(&ts64, &pt->head.dtime) >= 0)	{
		/* Boundary crossed in forward direction. This might
		 * indicate a leap transition, so we prepare for that
		 * case.
		 *
		 * Some operations below are actually NOPs in electric
		 * mode, but having only one code path that works for
		 * both modes is easier to maintain.
		 */
		last = pt->head.ttime;
		qr->warped = (int16_t)(last.D_s.lo -
				       pt->head.dtime.D_s.lo);
		next = addv64i32(&ts64, qr->warped);
		reload_limits(pt, &next);
		fired = ucmpv64(&pt->head.ebase, &last) == 0;
		if (fired) {
			ts64 = next;
			ts32 = next.D_s.lo;
		} else {
			qr->warped = 0;
		}
	}

	qr->tai_offs = pt->head.this_tai;

	/* If before the next scheduling alert, we're done. */
	if (ucmpv64(&ts64, &pt->head.stime) < 0)
		return fired;

	/* now start to collect the remaing data */
	due32 = pt->head.dtime.D_s.lo;

	qr->tai_diff  = pt->head.next_tai - pt->head.this_tai;
	qr->ttime     = pt->head.ttime;
	qr->ddist     = due32 - ts32;
	qr->dynamic   = pt->head.dynls;
	qr->proximity = LSPROX_SCHEDULE;

	/* if not in the last day before transition, we're done. */
	if (!betweenu32(due32 - SECSPERDAY, ts32, due32))
		return fired;
	
	qr->proximity = LSPROX_ANNOUNCE;
	if (!betweenu32(due32 - 10, ts32, due32))
		return fired;

	/* The last 10s before the transition. Prepare for action! */
	qr->proximity = LSPROX_ALERT;
	return fired;
}
Example #29
0
/* Reset the current leap frame */
void
leapsec_reset_frame(void)
{
	reset_times(leapsec_get_table(FALSE));
}
Example #30
0
/* Reset the current leap frame */
void
leapsec_reset_frame(void)
{
	reset_times(leapsec_get_table(false));
}