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