timespecops.c revision 290001
1#include "config.h"
2
3#include "ntp_types.h"
4#include "ntp_fp.h"
5#include "timespecops.h"
6
7#include "unity.h"
8
9#include <math.h>
10#include <string.h>
11
12
13#define TEST_ASSERT_EQUAL_timespec(a, b) { \
14    TEST_ASSERT_EQUAL_MESSAGE(a.tv_sec, b.tv_sec, "Field tv_sec"); \
15    TEST_ASSERT_EQUAL_MESSAGE(a.tv_nsec, b.tv_nsec, "Field tv_nsec");	\
16}
17
18
19#define TEST_ASSERT_EQUAL_l_fp(a, b) { \
20    TEST_ASSERT_EQUAL_MESSAGE(a.l_i, b.l_i, "Field l_i"); \
21    TEST_ASSERT_EQUAL_UINT_MESSAGE(a.l_uf, b.l_uf, "Field l_uf");	\
22}
23
24
25static u_int32 my_tick_to_tsf(u_int32 ticks);
26static u_int32 my_tsf_to_tick(u_int32 tsf);
27
28
29// that's it...
30struct lfpfracdata {
31	long	nsec;
32	u_int32 frac;
33};
34
35
36void test_Helpers1(void);
37void test_Normalise(void);
38void test_SignNoFrac(void);
39void test_SignWithFrac(void);
40void test_CmpFracEQ(void);
41void test_CmpFracGT(void);
42void test_CmpFracLT(void);
43void test_AddFullNorm(void);
44void test_AddFullOflow1(void);
45void test_AddNsecNorm(void);
46void test_AddNsecOflow1(void);
47void test_SubFullNorm(void);
48void test_SubFullOflow(void);
49void test_SubNsecNorm(void);
50void test_SubNsecOflow(void);
51void test_Neg(void);
52void test_AbsNoFrac(void);
53void test_AbsWithFrac(void);
54void test_Helpers2(void);
55void test_ToLFPbittest(void);
56void test_ToLFPrelPos(void);
57void test_ToLFPrelNeg(void);
58void test_ToLFPabs(void);
59void test_FromLFPbittest(void);
60void test_FromLFPrelPos(void);
61void test_FromLFPrelNeg(void);
62void test_LFProundtrip(void);
63void test_ToString(void);
64
65typedef int bool;
66
67const bool timespec_isValid(struct timespec V);
68struct timespec timespec_init(time_t hi, long lo);
69l_fp l_fp_init(int32 i, u_int32 f);
70bool AssertFpClose(const l_fp m, const l_fp n, const l_fp limit);
71bool AssertTimespecClose(const struct timespec m, const struct timespec n, const struct timespec limit);
72
73
74//******************************************MY CUSTOM FUNCTIONS*******************************
75
76
77
78const bool
79timespec_isValid(struct timespec V) {
80	return V.tv_nsec >= 0 && V.tv_nsec < 1000000000;
81}
82
83
84struct timespec
85timespec_init(time_t hi, long lo) {
86	struct timespec V;
87	V.tv_sec = hi;
88	V.tv_nsec = lo;
89	return V;
90}
91
92
93l_fp
94l_fp_init(int32 i, u_int32 f) {
95	l_fp temp;
96	temp.l_i  = i;
97	temp.l_uf = f;
98
99	return temp;
100}
101
102
103bool
104AssertFpClose(const l_fp m, const l_fp n, const l_fp limit) {
105	l_fp diff;
106
107	if (L_ISGEQ(&m, &n)) {
108		diff = m;
109		L_SUB(&diff, &n);
110	} else {
111		diff = n;
112		L_SUB(&diff, &m);
113	}
114	if (L_ISGEQ(&limit, &diff)){
115		return TRUE;
116	}
117	else {
118		printf("m_expr which is %s \nand\nn_expr which is %s\nare not close; diff=%susec\n", lfptoa(&m, 10), lfptoa(&n, 10), lfptoa(&diff, 10));
119		return FALSE;
120	}
121}
122
123
124bool
125AssertTimespecClose(const struct timespec m, const struct timespec n, const struct timespec limit) {
126	struct timespec diff;
127
128	diff = abs_tspec(sub_tspec(m, n));
129	if (cmp_tspec(limit, diff) >= 0)
130		return TRUE;
131	else
132	{
133		printf("m_expr which is %ld.%lu \nand\nn_expr which is %ld.%lu\nare not close; diff=%ld.%lunsec\n", m.tv_sec, m.tv_nsec, n.tv_sec, n.tv_nsec, diff.tv_sec, diff.tv_nsec);
134		return FALSE;
135	}
136}
137
138//-----------------------------------------------
139
140static const struct lfpfracdata fdata[] = {
141	{	  0, 0x00000000 }, {   2218896, 0x00916ae6 },
142	{  16408100, 0x0433523d }, { 125000000, 0x20000000 },
143	{ 250000000, 0x40000000 }, { 287455871, 0x4996b53d },
144	{ 375000000, 0x60000000 }, { 500000000, 0x80000000 },
145	{ 518978897, 0x84dbcd0e }, { 563730222, 0x90509fb3 },
146	{ 563788007, 0x9054692c }, { 583289882, 0x95527c57 },
147	{ 607074509, 0x9b693c2a }, { 625000000, 0xa0000000 },
148	{ 645184059, 0xa52ac851 }, { 676497788, 0xad2ef583 },
149	{ 678910895, 0xadcd1abb }, { 679569625, 0xadf84663 },
150	{ 690926741, 0xb0e0932d }, { 705656483, 0xb4a5e73d },
151	{ 723553854, 0xb93ad34c }, { 750000000, 0xc0000000 },
152	{ 763550253, 0xc3780785 }, { 775284917, 0xc6791284 },
153	{ 826190764, 0xd3813ce8 }, { 875000000, 0xe0000000 },
154	{ 956805507, 0xf4f134a9 }, { 982570733, 0xfb89c16c }
155	};
156
157
158u_int32
159my_tick_to_tsf(u_int32 ticks) {
160	// convert nanoseconds to l_fp fractional units, using double
161	// precision float calculations or, if available, 64bit integer
162	// arithmetic. This should give the precise fraction, rounded to
163	// the nearest representation.
164#ifdef HAVE_U_INT64
165	return (u_int32)((( ((u_int64)(ticks)) << 32) + 500000000) / 1000000000);
166#else
167	return (u_int32)((double(ticks)) * 4.294967296 + 0.5);
168#endif
169	// And before you ask: if ticks >= 1000000000, the result is
170	// truncated nonsense, so don't use it out-of-bounds.
171}
172
173
174u_int32
175my_tsf_to_tick(u_int32 tsf) {
176	// Inverse operation: converts fraction to microseconds.
177#ifdef HAVE_U_INT64
178	return (u_int32)(( ((u_int64)(tsf)) * 1000000000 + 0x80000000) >> 32);
179#else
180	return (u_int32)(double(tsf) / 4.294967296 + 0.5);
181#endif
182	// Beware: The result might be 10^9 due to rounding!
183}
184
185
186
187// ---------------------------------------------------------------------
188// test support stuff -- part 1
189// ---------------------------------------------------------------------
190
191void
192test_Helpers1(void) {
193	struct timespec x;
194
195	for (x.tv_sec = -2; x.tv_sec < 3; x.tv_sec++) {
196		x.tv_nsec = -1;
197		TEST_ASSERT_FALSE(timespec_isValid(x));
198		x.tv_nsec = 0;
199		TEST_ASSERT_TRUE(timespec_isValid(x));
200		x.tv_nsec = 999999999;
201		TEST_ASSERT_TRUE(timespec_isValid(x));
202		x.tv_nsec = 1000000000;
203		TEST_ASSERT_FALSE(timespec_isValid(x));
204	}
205}
206
207
208//----------------------------------------------------------------------
209// test normalisation
210//----------------------------------------------------------------------
211
212void
213test_Normalise(void) {
214	long ns;
215	for ( ns = -2000000000; ns <= 2000000000; ns += 10000000) {
216		struct timespec x = timespec_init(0, ns);
217
218		x = normalize_tspec(x);
219		TEST_ASSERT_TRUE(timespec_isValid(x));
220	}
221}
222
223//----------------------------------------------------------------------
224// test classification
225//----------------------------------------------------------------------
226
227void
228test_SignNoFrac(void) {
229	// sign test, no fraction
230	int i;
231	for (i = -4; i <= 4; ++i) {
232		struct timespec a = timespec_init(i, 0);
233		int E = (i > 0) - (i < 0);
234		int r = test_tspec(a);
235
236		TEST_ASSERT_EQUAL(E, r);
237	}
238}
239
240
241void
242test_SignWithFrac(void) {
243	// sign test, with fraction
244	int i;
245	for (i = -4; i <= 4; ++i) {
246		struct timespec a = timespec_init(i, 10);
247		int E = (i >= 0) - (i < 0);
248		int r = test_tspec(a);
249		TEST_ASSERT_EQUAL(E, r);
250	}
251}
252
253//----------------------------------------------------------------------
254// test compare
255//----------------------------------------------------------------------
256void
257test_CmpFracEQ(void) {
258	// fractions are equal
259	int i, j;
260	for (i = -4; i <= 4; ++i)
261		for (j = -4; j <= 4; ++j) {
262			struct timespec a = timespec_init( i , 200);
263			struct timespec b = timespec_init( j , 200);
264			int   E = (i > j) - (i < j);
265			int   r = cmp_tspec_denorm(a, b);
266			TEST_ASSERT_EQUAL(E, r);
267		}
268}
269
270
271void
272test_CmpFracGT(void) {
273	// fraction a bigger fraction b
274	int i, j;
275	for (i = -4; i <= 4; ++i)
276		for (j = -4; j <= 4; ++j) {
277			struct timespec a = timespec_init(i, 999999800);
278			struct timespec b = timespec_init(j, 200);
279			int   E = (i >= j) - (i < j);
280			int   r = cmp_tspec_denorm(a, b);
281			TEST_ASSERT_EQUAL(E, r);
282		}
283}
284
285
286void
287test_CmpFracLT(void) {
288	// fraction a less fraction b
289	int i, j;
290	for (i = -4; i <= 4; ++i)
291		for (j = -4; j <= 4; ++j) {
292			struct timespec a = timespec_init(i, 200);
293			struct timespec b = timespec_init(j, 999999800);
294			int   E = (i > j) - (i <= j);
295			int   r = cmp_tspec_denorm(a, b);
296			TEST_ASSERT_EQUAL(E, r);
297		}
298}
299
300//----------------------------------------------------------------------
301// Test addition (sum)
302//----------------------------------------------------------------------
303
304void
305test_AddFullNorm(void) {
306	int i, j;
307	for (i = -4; i <= 4; ++i)
308		for (j = -4; j <= 4; ++j) {
309			struct timespec a = timespec_init(i, 200);
310			struct timespec b = timespec_init(j, 400);
311			struct timespec E = timespec_init(i + j, 200 + 400);
312			struct timespec c;
313
314			c = add_tspec(a, b);
315			TEST_ASSERT_EQUAL_timespec(E, c);
316		}
317}
318
319
320void
321test_AddFullOflow1(void) {
322	int i, j;
323	for (i = -4; i <= 4; ++i)
324		for (j = -4; j <= 4; ++j) {
325			struct timespec a = timespec_init(i, 200);
326			struct timespec b = timespec_init(j, 999999900);
327			struct timespec E = timespec_init(i + j + 1, 100);
328			struct timespec c;
329
330			c = add_tspec(a, b);
331			TEST_ASSERT_EQUAL_timespec(E, c);
332		}
333}
334
335
336void
337test_AddNsecNorm(void) {
338	int i;
339	for (i = -4; i <= 4; ++i) {
340		struct timespec a = timespec_init(i, 200);
341		struct timespec E = timespec_init(i, 600);
342		struct timespec c;
343
344		c = add_tspec_ns(a, 600 - 200);
345		TEST_ASSERT_EQUAL_timespec(E, c);
346	}
347}
348
349
350void
351test_AddNsecOflow1(void) {
352	int i;
353	for (i = -4; i <= 4; ++i) {
354		struct timespec a = timespec_init(i, 200);
355		struct timespec E = timespec_init(i + 1, 100);
356		struct timespec c;
357
358		c = add_tspec_ns(a, NANOSECONDS - 100);
359		TEST_ASSERT_EQUAL_timespec(E, c);
360	}
361}
362
363//----------------------------------------------------------------------
364// test subtraction (difference)
365//----------------------------------------------------------------------
366
367void
368test_SubFullNorm(void) {
369	int i, j;
370	for (i = -4; i <= 4; ++i)
371		for (j = -4; j <= 4; ++j) {
372			struct timespec a = timespec_init( i , 600);
373			struct timespec b = timespec_init( j , 400);
374			struct timespec E = timespec_init(i-j, 200);
375			struct timespec c;
376
377			c = sub_tspec(a, b);
378			TEST_ASSERT_EQUAL_timespec(E, c);
379		}
380}
381
382
383void
384test_SubFullOflow(void) {
385	int i, j;
386	for (i = -4; i <= 4; ++i)
387		for (j = -4; j <= 4; ++j) {
388			struct timespec a = timespec_init(i, 100);
389			struct timespec b = timespec_init(j, 999999900);
390			struct timespec E = timespec_init(i - j - 1, 200);
391			struct timespec c;
392
393			c = sub_tspec(a, b);
394			TEST_ASSERT_EQUAL_timespec(E, c);
395		}
396}
397
398
399void
400test_SubNsecNorm(void) {
401	int i;
402	for (i = -4; i <= 4; ++i) {
403		struct timespec a = timespec_init(i, 600);
404		struct timespec E = timespec_init(i, 200);
405		struct timespec c;
406
407		c = sub_tspec_ns(a, 600 - 200);
408		TEST_ASSERT_EQUAL_timespec(E, c);
409	}
410}
411
412
413void
414test_SubNsecOflow(void) {
415	int i;
416	for (i = -4; i <= 4; ++i) {
417		struct timespec a = timespec_init( i , 100);
418		struct timespec E = timespec_init(i-1, 200);
419		struct timespec c;
420
421		c = sub_tspec_ns(a, NANOSECONDS - 100);
422		TEST_ASSERT_EQUAL_timespec(E, c);
423	}
424}
425
426//----------------------------------------------------------------------
427// test negation
428//----------------------------------------------------------------------
429
430
431void
432test_Neg(void) {
433	int i;
434	for (i = -4; i <= 4; ++i) {
435		struct timespec a = timespec_init(i, 100);
436		struct timespec b;
437		struct timespec c;
438
439		b = neg_tspec(a);
440		c = add_tspec(a, b);
441		TEST_ASSERT_EQUAL(0, test_tspec(c));
442	}
443}
444
445//----------------------------------------------------------------------
446// test abs value
447//----------------------------------------------------------------------
448
449void
450test_AbsNoFrac(void) {
451	int i;
452	for (i = -4; i <= 4; ++i) {
453		struct timespec a = timespec_init(i , 0);
454		struct timespec b;
455
456		b = abs_tspec(a);
457		TEST_ASSERT_EQUAL((i != 0), test_tspec(b));
458	}
459}
460
461
462void
463test_AbsWithFrac(void) {
464	int i;
465	for (i = -4; i <= 4; ++i) {
466		struct timespec a = timespec_init(i, 100);
467		struct timespec b;
468
469		b = abs_tspec(a);
470		TEST_ASSERT_EQUAL(1, test_tspec(b));
471	}
472}
473
474// ---------------------------------------------------------------------
475// test support stuff -- part 2
476// ---------------------------------------------------------------------
477
478void
479test_Helpers2(void) {
480	struct timespec limit = timespec_init(0, 2);
481
482	struct timespec x, y;
483	long i;
484
485	for (x.tv_sec = -2; x.tv_sec < 3; x.tv_sec++)
486		for (x.tv_nsec = 1;
487		     x.tv_nsec < 1000000000;
488		     x.tv_nsec += 499999999) {
489			for (i = -4; i < 5; ++i) {
490				y = x;
491				y.tv_nsec += i;
492				if (i >= -2 && i <= 2){
493					TEST_ASSERT_TRUE(AssertTimespecClose(x, y, limit));
494				}
495				else
496				{
497					TEST_ASSERT_FALSE(AssertTimespecClose(x, y, limit));
498				}
499			}
500		}
501}
502
503//----------------------------------------------------------------------
504// conversion to l_fp
505//----------------------------------------------------------------------
506
507void
508test_ToLFPbittest(void) {
509	l_fp lfpClose =  l_fp_init(0, 1);
510	u_int32 i;
511	for (i = 0; i < 1000000000; i+=1000) {
512		struct timespec a = timespec_init(1, i);
513		l_fp E= l_fp_init(1, my_tick_to_tsf(i));
514		l_fp r;
515
516		r = tspec_intv_to_lfp(a);
517		TEST_ASSERT_TRUE(AssertFpClose(E, r, lfpClose));
518	}
519}
520
521
522void
523test_ToLFPrelPos(void) {
524	int i;
525	for (i = 0; i < COUNTOF(fdata); ++i) {
526		struct timespec a = timespec_init(1, fdata[i].nsec);
527		l_fp E = l_fp_init(1, fdata[i].frac);
528		l_fp r;
529
530		r = tspec_intv_to_lfp(a);
531		TEST_ASSERT_EQUAL_l_fp(E, r);
532	}
533}
534
535
536void
537test_ToLFPrelNeg(void) {
538	int i;
539	for (i = 0; i < COUNTOF(fdata); ++i) {
540		struct timespec a = timespec_init(-1, fdata[i].nsec);
541		l_fp E = l_fp_init(~0, fdata[i].frac);
542		l_fp r;
543
544		r = tspec_intv_to_lfp(a);
545		TEST_ASSERT_EQUAL_l_fp(E, r);
546	}
547}
548
549
550void
551test_ToLFPabs(void) {
552	int i;
553	for (i = 0; i < COUNTOF(fdata); ++i) {
554		struct timespec a = timespec_init(1, fdata[i].nsec);
555		l_fp E = l_fp_init(1 + JAN_1970, fdata[i].frac);
556		l_fp r;
557
558		r = tspec_stamp_to_lfp(a);
559		TEST_ASSERT_EQUAL_l_fp(E, r);
560	}
561}
562
563//----------------------------------------------------------------------
564// conversion from l_fp
565//----------------------------------------------------------------------
566
567void
568test_FromLFPbittest(void) {
569	struct timespec limit = timespec_init(0, 2);
570
571	// Not *exactly* a bittest, because 2**32 tests would take a
572	// really long time even on very fast machines! So we do test
573	// every 1000 fractional units.
574	u_int32 tsf;
575	for (tsf = 0; tsf < ~((u_int32)(1000)); tsf += 1000) {
576		struct timespec E = timespec_init(1, my_tsf_to_tick(tsf));
577		l_fp a = l_fp_init(1, tsf);
578		struct timespec r;
579
580		r = lfp_intv_to_tspec(a);
581		// The conversion might be off by one nanosecond when
582		// comparing to calculated value.
583		TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit));
584	}
585}
586
587
588void
589test_FromLFPrelPos(void) {
590	struct timespec limit = timespec_init(0, 2);
591	int i;
592	for (i = 0; i < COUNTOF(fdata); ++i) {
593		l_fp a = l_fp_init(1, fdata[i].frac);
594		struct timespec E = timespec_init(1, fdata[i].nsec);
595		struct timespec r;
596
597		r = lfp_intv_to_tspec(a);
598		TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit));
599	}
600}
601
602
603void
604test_FromLFPrelNeg(void) {
605	struct timespec limit = timespec_init(0, 2);
606	int i;
607	for (i = 0; i < COUNTOF(fdata); ++i) {
608		l_fp a = l_fp_init(~0, fdata[i].frac);
609		struct timespec E = timespec_init(-1, fdata[i].nsec);
610		struct timespec r;
611
612		r = lfp_intv_to_tspec(a);
613		TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit));
614	}
615}
616
617
618// nsec -> frac -> nsec roundtrip, using a prime start and increment
619void
620test_LFProundtrip(void) {
621	int32_t t;
622	u_int32 i;
623	for (t = -1; t < 2; ++t)
624		for (i = 4999; i < 1000000000; i += 10007) {
625			struct timespec E = timespec_init(t, i);
626			l_fp a;
627			struct timespec r;
628
629			a = tspec_intv_to_lfp(E);
630			r = lfp_intv_to_tspec(a);
631			TEST_ASSERT_EQUAL_timespec(E, r);
632		}
633}
634
635//----------------------------------------------------------------------
636// string formatting
637//----------------------------------------------------------------------
638
639void
640test_ToString(void) {
641	static const struct {
642		time_t		sec;
643		long		nsec;
644		const char *	repr;
645	} data [] = {
646		{ 0, 0,	 "0.000000000" },
647		{ 2, 0,	 "2.000000000" },
648		{-2, 0, "-2.000000000" },
649		{ 0, 1,	 "0.000000001" },
650		{ 0,-1,	"-0.000000001" },
651		{ 1,-1,	 "0.999999999" },
652		{-1, 1, "-0.999999999" },
653		{-1,-1, "-1.000000001" },
654	};
655	int i;
656	for (i = 0; i < COUNTOF(data); ++i) {
657		struct timespec a = timespec_init(data[i].sec, data[i].nsec);
658		const char * E = data[i].repr;
659		const char * r = tspectoa(a);
660		TEST_ASSERT_EQUAL_STRING(E, r);
661	}
662}
663
664// -*- EOF -*-
665