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