summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorGary E. Miller <gem@rellim.com>2018-11-08 18:43:32 -0800
committerGary E. Miller <gem@rellim.com>2018-11-08 18:43:32 -0800
commite85dfb65934efb47c42ee7adacbd3215764cf4e6 (patch)
tree4b052c40dc4ce74b9d5387dc4c30f8e2d18030e1 /tests
parent7983561de0effae9e09b03c0b1385f13a7871b20 (diff)
downloadgpsd-e85dfb65934efb47c42ee7adacbd3215764cf4e6.tar.gz
tests: move most tests into tests/
Cuts the clutter a bit in the top level directory.
Diffstat (limited to 'tests')
-rw-r--r--tests/test_bits.c233
-rw-r--r--tests/test_float.c305
-rw-r--r--tests/test_geoid.c39
-rw-r--r--tests/test_gpsmm.cpp173
-rw-r--r--tests/test_json.c623
-rw-r--r--tests/test_libgps.c153
-rw-r--r--tests/test_matrix.c88
-rw-r--r--tests/test_mktime.c177
-rwxr-xr-xtests/test_nmea2000117
-rw-r--r--tests/test_packet.c409
-rw-r--r--tests/test_timespec.c557
-rw-r--r--tests/test_trig.c55
12 files changed, 2929 insertions, 0 deletions
diff --git a/tests/test_bits.c b/tests/test_bits.c
new file mode 100644
index 00000000..a0af7470
--- /dev/null
+++ b/tests/test_bits.c
@@ -0,0 +1,233 @@
+/* test harness for bits.h
+ *
+ * This file is Copyright (c) 2010 by the GPSD project
+ * SPDX-License-Identifier: BSD-2-clause
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "../bits.h"
+
+static unsigned char buf[80];
+static signed char sb1, sb2;
+static unsigned char ub1, ub2;
+static short sw1, sw2;
+static unsigned short uw1, uw2;
+static int sl1, sl2;
+static unsigned int ul1, ul2;
+static int64_t sL1, sL2;
+static uint64_t uL1, uL2;
+static float f1;
+static double d1;
+
+static char *hexdump(const void *binbuf, size_t len)
+{
+ static char hexbuf[BUFSIZ];
+ size_t i, j = 0;
+ const char *ibuf = (const char *)binbuf;
+ const char *hexchar = "0123456789abcdef";
+
+ for (i = 0; i < len; i++) {
+ hexbuf[j++] = hexchar[(ibuf[i] & 0xf0) >> 4];
+ hexbuf[j++] = hexchar[ibuf[i] & 0x0f];
+ }
+ hexbuf[j] = '\0';
+ return hexbuf;
+}
+
+static void bedumpall(void)
+{
+ (void)printf("getsb: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) sb1, (uint64_t) sb2,
+ (uint64_t) getsb(buf, 0), (uint64_t) getsb(buf, 8));
+ (void)printf("getub: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) ub1, (uint64_t) ub2,
+ (uint64_t) getub(buf, 0), (uint64_t) getub(buf, 8));
+ (void)printf("getbes16: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) sw1, (uint64_t) sw2,
+ (uint64_t) getbes16(buf, 0), (uint64_t) getbes16(buf, 8));
+ (void)printf("getbeu16: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) uw1, (uint64_t) uw2,
+ (uint64_t) getbeu16(buf, 0), (uint64_t) getbeu16(buf, 8));
+ (void)printf("getbes32: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) sl1, (uint64_t) sl2,
+ (uint64_t) getbes32(buf, 0), (uint64_t) getbes32(buf, 8));
+ (void)printf("getbeu32: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) ul1, (uint64_t) ul2,
+ (uint64_t) getbeu32(buf, 0), (uint64_t) getbeu32(buf, 8));
+ (void)printf("getbes64: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) sL1, (uint64_t) sL2,
+ (uint64_t) getbes64(buf, 0), (uint64_t) getbes64(buf, 8));
+ (void)printf("getbeu64: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) uL1, (uint64_t) uL2,
+ (uint64_t) getbeu64(buf, 0), (uint64_t) getbeu64(buf, 8));
+ (void)printf("getbef32: %f %f\n", f1, getbef32((const char *)buf, 24));
+ (void)printf("getbed64: %.16f %.16f\n", d1, getbed64((const char *)buf, 16));
+}
+
+static void ledumpall(void)
+{
+ (void)printf("getsb: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) sb1, (uint64_t) sb2,
+ (uint64_t) getsb(buf, 0), (uint64_t) getsb(buf, 8));
+ (void)printf("getub: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) ub1, (uint64_t) ub2,
+ (uint64_t) getub(buf, 0), (uint64_t) getub(buf, 8));
+ (void)printf("getles16: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) sw1, (uint64_t) sw2,
+ (uint64_t) getles16(buf, 0), (uint64_t) getles16(buf, 8));
+ (void)printf("getleu16: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) uw1, (uint64_t) uw2,
+ (uint64_t) getleu16(buf, 0), (uint64_t) getleu16(buf, 8));
+ (void)printf("getles32: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) sl1, (uint64_t) sl2,
+ (uint64_t) getles32(buf, 0), (uint64_t) getles32(buf, 8));
+ (void)printf("getleu32: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) ul1, (uint64_t) ul2,
+ (uint64_t) getleu32(buf, 0), (uint64_t) getleu32(buf, 8));
+ (void)printf("getles64: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) sL1, (uint64_t) sL2,
+ (uint64_t) getles64(buf, 0), (uint64_t) getles64(buf, 8));
+ (void)printf("getleu64: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n",
+ (uint64_t) uL1, (uint64_t) uL2,
+ (uint64_t) getleu64(buf, 0), (uint64_t) getleu64(buf, 8));
+ (void)printf("getlef32: %f %f\n", f1, getlef32((const char *)buf, 24));
+ (void)printf("getled64: %.16f %.16f\n", d1, getled64((const char *)buf, 16));
+}
+
+struct unsigned_test
+{
+ unsigned char *buf;
+ unsigned int start, width;
+ uint64_t expected;
+ bool le;
+ char *description;
+};
+
+int main(int argc, char *argv[])
+{
+ bool failures = false;
+ bool quiet = (argc > 1) && (strcmp(argv[1], "--quiet") == 0);
+
+ struct unsigned_test *up, unsigned_tests[] = {
+ /* tests using the big buffer */
+ {buf, 0, 1, 0, false, "first bit of first byte"},
+ {buf, 0, 8, 0x01, false, "first 8 bits"},
+ {buf, 32, 7, 0x02, false, "first seven bits of fifth byte (0x05)"},
+ {buf, 56, 12, 0x8f, false, "12 bits crossing 7th to 8th bytes (0x08ff)"},
+ {buf, 78, 4, 0xb, false, "4 bits crossing 8th to 9th byte (0xfefd)"},
+ {buf, 0, 1, 0, true, "first bit of first byte"},
+ {buf, 0, 8, 0x80, true, "first 8 bits"},
+ {buf, 32, 7, 0x20, true, "first seven bits of fifth byte (0x05)"},
+ {buf, 56, 12, 0xf10,true, "12 bits crossing 7th to 8th bytes (0x08ff)"},
+ {buf, 78, 4, 0xd, true, "4 bits crossing 8th to 9th byte (0xfefd)"},
+ /* sporadic tests based on found bugs */
+ {(unsigned char *)"\x19\x23\f6",
+ 7, 2, 2, false, "2 bits crossing 1st to 2nd byte (0x1923)"},
+ };
+
+ memcpy(buf, "\x01\x02\x03\x04\x05\x06\x07\x08", 8);
+ memcpy(buf + 8, "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8", 8);
+ memcpy(buf + 16, "\x40\x09\x21\xfb\x54\x44\x2d\x18", 8);
+ memcpy(buf + 24, "\x40\x49\x0f\xdb", 4);
+
+ if (!quiet)
+ (void)printf("Testing bitfield extraction\n");
+
+ sb1 = getsb(buf, 0);
+ sb2 = getsb(buf, 8);
+ ub1 = getub(buf, 0);
+ ub2 = getub(buf, 8);
+
+ if (!quiet) {
+ unsigned char *sp;
+
+ (void)fputs("Test data:", stdout);
+ for (sp = buf; sp < buf + 28; sp++)
+ (void)printf(" %02x", *sp);
+ (void)putc('\n', stdout);
+
+ /* big-endian test */
+ printf("Big-endian:\n");
+ sw1 = getbes16(buf, 0);
+ sw2 = getbes16(buf, 8);
+ uw1 = getbeu16(buf, 0);
+ uw2 = getbeu16(buf, 8);
+ sl1 = getbes32(buf, 0);
+ sl2 = getbes32(buf, 8);
+ ul1 = getbeu32(buf, 0);
+ ul2 = getbeu32(buf, 8);
+ sL1 = getbes64(buf, 0);
+ sL2 = getbes64(buf, 8);
+ uL1 = getbeu64(buf, 0);
+ uL2 = getbeu64(buf, 8);
+ f1 = getbef32((const char *)buf, 24);
+ d1 = getbed64((const char *)buf, 16);
+ bedumpall();
+
+ /* little-endian test */
+ printf("Little-endian:\n");
+ sw1 = getles16(buf, 0);
+ sw2 = getles16(buf, 8);
+ uw1 = getleu16(buf, 0);
+ uw2 = getleu16(buf, 8);
+ sl1 = getles32(buf, 0);
+ sl2 = getles32(buf, 8);
+ ul1 = getleu32(buf, 0);
+ ul2 = getleu32(buf, 8);
+ sL1 = getles64(buf, 0);
+ sL2 = getles64(buf, 8);
+ uL1 = getleu64(buf, 0);
+ uL2 = getleu64(buf, 8);
+ f1 = getlef32((const char *)buf, 24);
+ d1 = getled64((const char *)buf, 16);
+ ledumpall();
+ }
+
+ if (sb1 != 1) printf("getsb(buf, 0) FAILED\n");
+ if (sb2 != -1) printf("getsb(buf, 8) FAILED\n");
+ if (ub1 != 1) printf("getub(buf, 0) FAILED\n");
+ if (ub2 != 0xff) printf("getub(buf, 8) FAILED\n");
+
+ for (up = unsigned_tests;
+ up <
+ unsigned_tests + sizeof(unsigned_tests) / sizeof(unsigned_tests[0]);
+ up++) {
+ uint64_t res = ubits((unsigned char *)buf, up->start, up->width, up->le);
+ bool success = (res == up->expected);
+ if (!success)
+ failures = true;
+ if (!success || !quiet)
+ (void)printf("ubits(%s, %d, %d, %s) %s should be %" PRIx64 ", is %" PRIx64 ": %s\n",
+ hexdump(buf, strlen((char *)buf)),
+ up->start, up->width, up->le ? "true" : "false",
+ up->description, up->expected, res,
+ success ? "succeeded" : "FAILED");
+ }
+
+
+ shiftleft(buf, 28, 30);
+ if (!quiet)
+ printf("Left-shifted 30 bits: %s\n", hexdump(buf, 28));
+ /*
+ * After the 24-bit shift, the bit array loses its first three bytes:
+ * 0x0405060708 = 00000100 00000101 00000110 00000111 00001000
+ * By inspection, the results of the 6-bit shift are
+ * 00000001 01000001 10000001 11000010 00
+ */
+#define LASSERT(n, v) if (buf[n] != v) printf("Expected buf[%d] to be %02x, was %02x\n", n, v, buf[n])
+ LASSERT(0, 0x01);
+ LASSERT(1, 0x41);
+ LASSERT(2, 0x81);
+ LASSERT(3, 0xc1);
+#undef LASSERT
+
+
+ exit(failures ? EXIT_FAILURE : EXIT_SUCCESS);
+
+}
+
diff --git a/tests/test_float.c b/tests/test_float.c
new file mode 100644
index 00000000..8d17f218
--- /dev/null
+++ b/tests/test_float.c
@@ -0,0 +1,305 @@
+/*
+ * This file is Copyright (c) 2010 by the GPSD project
+ * SPDX-License-Identifier: BSD-2-clause
+ */
+#include <stdio.h>
+
+/*
+ * Copyright (c) 2006 Chris Kuethe <chris.kuethe@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * this simple program tests to see whether your system can do proper
+ * single and double precision floating point. This is apparently Very
+ * Hard To Do(tm) on embedded systems, judging by the number of broken
+ * ARM toolchains I've seen... :(
+ *
+ * Added in 2015 by ESR: Test for C99 behavior on negative operand(s)
+ * of %, that is the result should have the sign of the left operand.
+ *
+ * compile with: gcc -O -o test_float test_float.c
+ * (use whatever -O level you like)
+ */
+
+int main(void);
+int test_single(void);
+int test_double(void);
+int test_modulo(void);
+
+int main(void) {
+ int i, j, k;
+
+ if ((i = test_single()))
+ printf("WARNING: Single-precision "
+ "floating point math might be broken\n");
+
+ if ((j = test_double()))
+ printf("WARNING: Double-precision "
+ "floating point math might be broken\n");
+
+ if ((k = test_modulo()))
+ printf("WARNING: Modular arithmetic is broken\n");
+
+ i += j;
+ i += k;
+ if (i == 0)
+ printf("floating point and modular math appears to work\n");
+ return i;
+}
+
+int test_single(void) {
+ static float f;
+ static int i;
+ static int e = 0;
+
+ /* addition test */
+ f = 1.0;
+ for(i = 0; i < 10; i++)
+ f += (1<<i);
+ if (f != 1024.0) {
+ printf("s1 ");
+ e++;
+ }
+
+ /* subtraction test */
+ f = 1024.0;
+ for(i = 0; i < 10; i++)
+ f -= (1<<i);
+ if (f != 1.0) {
+ printf("s2 ");
+ e++;
+ }
+
+ /* multiplication test */
+ f = 1.0;
+ for(i = 1; i < 10; i++)
+ f *= i;
+ if (f != 362880.0) {
+ printf("s3 ");
+ e++;
+ }
+
+ /* division test */
+ f = 362880.0;
+ for(i = 1; i < 10; i++)
+ f /= i;
+ if (f != 1.0) {
+ printf("s4 ");
+ e++;
+ }
+
+ /* multiply-accumulate test */
+ f = 0.5;
+ for(i = 1; i < 1000000; i++) {
+ f += 2.0;
+ f *= 0.5;
+ }
+ if (f != 2.0) {
+ printf("s5 ");
+ e++;
+ }
+
+ /* divide-subtract test */
+ f = 2.0;
+ for(i = 1; i < 1000000; i++) {
+ f /= 0.5;
+ f -= 2.0;
+ }
+ if (f != 2.0) {
+ printf("s6 ");
+ e++;
+ }
+
+ /* add-multiply-subtract-divide test */
+ f = 1000000.0;
+ for(i = 1; i < 1000000; i++)
+ f = ((((f + 1.5) * 0.5) - 1.25) / 0.5);
+ if (f != 1.0) {
+ printf("s7 ");
+ e++;
+ }
+
+ /* multiply-add-divide-subtract test */
+ f = 1.0;
+ for(i = 1; i < 1000000; i++)
+ f = ((((f * 5.0) + 3.0) / 2.0) - 3.0);
+ if (f != 1.0)
+ printf("s8 ");
+
+ /* subtract-divide-add-multiply test */
+ f = 8.0;
+ for(i = 1; i < 1000000; i++)
+ f = ((((f - 5.0) / 2.0) + 2.5) * 2.0);
+ if (f != 8.0) {
+ printf("s9 ");
+ e++;
+ }
+
+ /* divide-subtract-multiply-add test */
+ f = 42.0;
+ for(i = 1; i < 1000000; i++)
+ f = ((((f / 6.0) - 5.0) * 19.75 ) + 2.5);
+ if (f != 42.0) {
+ printf("s10 ");
+ e++;
+ }
+ if (e) {
+ printf("\n");
+ return 1;
+ }
+ return 0;
+}
+
+
+int test_double(void) {
+ static double f;
+ static int i;
+ static int e = 0;
+
+ /* addition test */
+ f = 1.0;
+ for(i = 0; i < 10; i++)
+ f += (1<<i);
+ if (f != 1024.0) {
+ printf("d1 ");
+ e++;
+ }
+
+ /* subtraction test */
+ f = 1024.0;
+ for(i = 0; i < 10; i++)
+ f -= (1<<i);
+ if (f != 1.0) {
+ printf("d2 ");
+ e++;
+ }
+
+ /* multiplication test */
+ f = 1.0;
+ for(i = 1; i < 10; i++)
+ f *= i;
+ if (f != 362880.0) {
+ printf("d3 ");
+ e++;
+ }
+
+ /* division test */
+ f = 362880.0;
+ for(i = 1; i < 10; i++)
+ f /= i;
+ if (f != 1.0) {
+ printf("d4 ");
+ e++;
+ }
+
+ /* multiply-accumulate test */
+ f = 0.5;
+ for(i = 1; i < 1000000; i++) {
+ f += 2.0;
+ f *= 0.5;
+ }
+ if (f != 2.0) {
+ printf("d5 ");
+ e++;
+ }
+
+ /* divide-subtract test */
+ f = 2.0;
+ for(i = 1; i < 1000000; i++) {
+ f /= 0.5;
+ f -= 2.0;
+ }
+ if (f != 2.0) {
+ printf("d6 ");
+ e++;
+ }
+
+ /* add-multiply-subtract-divide test */
+ f = 1000000.0;
+ for(i = 1; i < 1000000; i++)
+ f = ((((f + 1.5) * 0.5) - 1.25) / 0.5);
+ if (f != 1.0) {
+ printf("d7 ");
+ e++;
+ }
+
+ /* multiply-add-divide-subtract test */
+ f = 1.0;
+ for(i = 1; i < 1000000; i++)
+ f = ((((f * 5.0) + 3.0) / 2.0) - 3.0);
+ if (f != 1.0)
+ printf("d8 ");
+
+ /* subtract-divide-add-multiply test */
+ f = 8.0;
+ for(i = 1; i < 1000000; i++)
+ f = ((((f - 5.0) / 2.0) + 2.5) * 2.0);
+ if (f != 8.0) {
+ printf("d9 ");
+ e++;
+ }
+
+ /* divide-subtract-multiply-add test */
+ f = 42.0;
+ for(i = 1; i < 1000000; i++)
+ f = ((((f / 6.0) - 5.0) * 19.75 ) + 2.5);
+ if (f != 42.0) {
+ printf("d10 ");
+ e++;
+ }
+ if (e) {
+ printf("\n");
+ return 1;
+ }
+ return 0;
+}
+
+int test_modulo(void) {
+ static int e = 0;
+
+ /* make sure that gcc does not optimize these away */
+ volatile int a;
+ volatile int b;
+
+ a = -5;
+ b = 2;
+ //cppcheck-suppress knownConditionTrueFalse
+ if (a % b != -1) {
+ printf("m1 ");
+ e++;
+ }
+
+ a = -5;
+ b = -2;
+ //cppcheck-suppress knownConditionTrueFalse
+ if (a % b != -1) {
+ printf("m2 ");
+ e++;
+ }
+
+ a = 5;
+ b = -2;
+ //cppcheck-suppress knownConditionTrueFalse
+ if (a % b != 1) {
+ printf("m3 ");
+ e++;
+ }
+
+ if (e) {
+ printf("\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/tests/test_geoid.c b/tests/test_geoid.c
new file mode 100644
index 00000000..cd2213c8
--- /dev/null
+++ b/tests/test_geoid.c
@@ -0,0 +1,39 @@
+/* test driver for the ECEF to WGS84 conversions in geoid.c
+ *
+ * This file is Copyright (c) 2010 by the GPSD project
+ * SPDX-License-Identifier: BSD-2-clause
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "../gpsd.h"
+
+int main(int argc, char **argv)
+{
+ double lat, lon;
+
+ if (argc != 3) {
+ (void)fprintf(stderr, "Usage: %s lat lon\n", argv[0]);
+ return 1;
+ }
+
+ lat = atof(argv[1]);
+ lon = atof(argv[2]);
+
+ if (lon > 180.0 || lat < -180.0) {
+ (void)fprintf(stderr, " -180 <= lon=%s(%.f) <= 180 ?\n", argv[2], lon);
+ return 1;
+ }
+
+ if (lat > 90.0 || lat < -90.0) {
+ (void)fprintf(stderr, " -90 <= lat=%s(%.f) <= 90 ?\n", argv[1], lat);
+ return 1;
+ }
+
+ (void)printf(" lat= %f lon= %f geoid correction= %f\n",
+ lat, lon, wgs84_separation(lat, lon));
+
+ return 0;
+}
diff --git a/tests/test_gpsmm.cpp b/tests/test_gpsmm.cpp
new file mode 100644
index 00000000..4106b476
--- /dev/null
+++ b/tests/test_gpsmm.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 Eric S. Raymond.
+ *
+ * This software is distributed under a BSD-style license. See the
+ * file "COPYING" in the top-level directory of the distribution for details.
+ *
+ */
+
+/* This simple program shows the basic functionality of the C++ wrapper class */
+#include <iostream>
+
+#include <getopt.h>
+
+#include "libgpsmm.h"
+#include "gpsdclient.c"
+/* YES ---> ^^^^
+ Using .c rather than the .h to embed gpsd_source_spec() source here
+ so that it is compiled in C++ rather than C of the gps library
+ (otherwise fails to link as the signatures are unavailable/different)
+*/
+using namespace std;
+
+/*
+ * We should get libgps_dump_state() from the client library, but
+ * scons has a bug; we can't get it to add -lgps to the link line,
+ * apparently because it doesn't honor parse_flags on a Program()
+ * build of a C++ file.
+ */
+static void libgps_dump_state(struct gps_data_t *collect)
+{
+ /* no need to dump the entire state, this is a sanity check */
+#ifndef USE_QT
+ (void)fprintf(stdout, "flags: (0x%04x) %s\n",
+ (unsigned int)collect->set, gps_maskdump(collect->set));
+#endif
+ if (collect->set & ONLINE_SET)
+ (void)fprintf(stdout, "ONLINE: %lf\n", collect->online);
+ if (collect->set & TIME_SET)
+ (void)fprintf(stdout, "TIME: %lf\n", collect->fix.time);
+ if (collect->set & LATLON_SET)
+ (void)fprintf(stdout, "LATLON: lat/lon: %lf %lf\n",
+ collect->fix.latitude, collect->fix.longitude);
+ if (collect->set & ALTITUDE_SET)
+ (void)fprintf(stdout, "ALTITUDE: altitude: %lf U: climb: %lf\n",
+ collect->fix.altitude, collect->fix.climb);
+ if (collect->set & SPEED_SET)
+ (void)fprintf(stdout, "SPEED: %lf\n", collect->fix.speed);
+ if (collect->set & TRACK_SET)
+ (void)fprintf(stdout, "TRACK: track: %lf\n", collect->fix.track);
+ if (collect->set & CLIMB_SET)
+ (void)fprintf(stdout, "CLIMB: climb: %lf\n", collect->fix.climb);
+ if (collect->set & STATUS_SET)
+ (void)fprintf(stdout, "STATUS: status: %d\n", collect->status);
+ if (collect->set & MODE_SET)
+ (void)fprintf(stdout, "MODE: mode: %d\n", collect->fix.mode);
+ if (collect->set & DOP_SET)
+ (void)fprintf(stdout,
+ "DOP: satellites %d, pdop=%lf, hdop=%lf, vdop=%lf\n",
+ collect->satellites_used, collect->dop.pdop,
+ collect->dop.hdop, collect->dop.vdop);
+ if (collect->set & VERSION_SET)
+ (void)fprintf(stdout, "VERSION: release=%s rev=%s proto=%d.%d\n",
+ collect->version.release,
+ collect->version.rev,
+ collect->version.proto_major,
+ collect->version.proto_minor);
+ if (collect->set & POLICY_SET)
+ (void)fprintf(stdout,
+ "POLICY: watcher=%s nmea=%s raw=%d scaled=%s timing=%s, devpath=%s\n",
+ collect->policy.watcher ? "true" : "false",
+ collect->policy.nmea ? "true" : "false",
+ collect->policy.raw,
+ collect->policy.scaled ? "true" : "false",
+ collect->policy.timing ? "true" : "false",
+ collect->policy.devpath);
+ if (collect->set & SATELLITE_SET) {
+ int i;
+
+ (void)fprintf(stdout, "SKY: satellites in view: %d\n",
+ collect->satellites_visible);
+ for (i = 0; i < collect->satellites_visible; i++) {
+ (void)fprintf(stdout, " %2.2d: %2.2d %3.3d %3.0f %c\n",
+ collect->skyview[i].PRN,
+ collect->skyview[i].elevation,
+ collect->skyview[i].azimuth,
+ collect->skyview[i].ss,
+ collect->skyview[i].used ? 'Y' : 'N');
+ }
+ }
+ if (collect->set & DEVICE_SET)
+ (void)fprintf(stdout, "DEVICE: Device is '%s', driver is '%s'\n",
+ collect->dev.path, collect->dev.driver);
+#ifdef OLDSTYLE_ENABLE
+ if (collect->set & DEVICEID_SET)
+ (void)fprintf(stdout, "GPSD ID is %s\n", collect->dev.subtype);
+#endif /* OLDSTYLE_ENABLE */
+ if (collect->set & DEVICELIST_SET) {
+ int i;
+ (void)fprintf(stdout, "DEVICELIST:%d devices:\n",
+ collect->devices.ndevices);
+ for (i = 0; i < collect->devices.ndevices; i++) {
+ (void)fprintf(stdout, "%d: path='%s' driver='%s'\n",
+ collect->devices.ndevices,
+ collect->devices.list[i].path,
+ collect->devices.list[i].driver);
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ uint looper = UINT_MAX;
+
+ // A typical C++ program may look to use a more native option parsing method
+ // such as boost::program_options
+ // But for this test program we don't want extra dependencies
+ // Hence use C style getopt for (build) simplicity
+ int option;
+ while ((option = getopt(argc, argv, "l:h?")) != -1) {
+ switch (option) {
+ case 'l':
+ looper = atoi(optarg);
+ break;
+ case '?':
+ case 'h':
+ default:
+ cout << "usage: " << argv[0] << " [-l n]\n";
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ struct fixsource_t source;
+ /* Grok the server, port, and device. */
+ if (optind < argc) {
+ gpsd_source_spec(argv[optind], &source);
+ } else
+ gpsd_source_spec(NULL, &source);
+
+ //gpsmm gps_rec("localhost", DEFAULT_GPSD_PORT);
+ gpsmm gps_rec(source.server, source.port);
+
+ if ( !((std::string)source.server == (std::string)GPSD_SHARED_MEMORY ||
+ (std::string)source.server == (std::string)GPSD_DBUS_EXPORT) ) {
+ if (gps_rec.stream(WATCH_ENABLE|WATCH_JSON) == NULL) {
+ cerr << "No GPSD running.\n";
+ return 1;
+ }
+ }
+
+ // Loop for the specified number of times
+ // If not specified then by default it loops until ll simply goes out of bounds
+ // So with the 5 second wait & a 4 byte uint - this equates to ~680 years
+ // - long enough for a test program :)
+ for (uint ll = 0; ll < looper; ll++) {
+ struct gps_data_t* newdata;
+
+ if (!gps_rec.waiting(5000000))
+ continue;
+
+ if ((newdata = gps_rec.read()) == NULL) {
+ cerr << "Read error.\n";
+ return 1;
+ } else {
+ libgps_dump_state(newdata);
+ }
+ }
+
+ cout << "Exiting\n";
+ return 0;
+}
+
diff --git a/tests/test_json.c b/tests/test_json.c
new file mode 100644
index 00000000..01f36d3a
--- /dev/null
+++ b/tests/test_json.c
@@ -0,0 +1,623 @@
+/* json.c - unit test for JSON parsing into fixed-extent structures
+ *
+ * This file is Copyright (c) 2010 by the GPSD project
+ * SPDX-License-Identifier: BSD-2-clause
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <getopt.h>
+
+#include "../gpsd.h"
+#include "../gps_json.h"
+#include "../revision.h"
+
+/* GPSD is built with JSON_MINIMAL. Any !JSON_MINIMAL tests,
+ * like 18, 19 and 20 will thus fail.
+ * So this define removes them, they never execute.
+ */
+#define JSON_MINIMAL
+
+static int debug = 0;
+static int current_test = 0;
+
+static void assert_case(int status)
+{
+ if (status != 0) {
+ (void)fprintf(stderr, "case %d FAILED\n", current_test);
+ (void)fprintf(stderr, "status %d (%s).\n",
+ status, json_error_string(status));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void assert_string(char *attr, char *fld, char *val)
+{
+ if (strcmp(fld, val)) {
+ (void)fprintf(stderr, "case %d FAILED\n", current_test);
+ (void)fprintf(stderr,
+ "'%s' string attribute eval failed, value = %s.\n",
+ attr, fld);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void assert_integer(char *attr, int fld, int val)
+{
+ if (fld != val) {
+ (void)fprintf(stderr, "case %d FAILED\n", current_test);
+ (void)fprintf(stderr,
+ "'%s' integer attribute eval failed, value = %d.\n",
+ attr, fld);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void assert_uinteger(char *attr, unsigned int fld, unsigned int val)
+{
+ if (fld != val) {
+ (void)fprintf(stderr, "case %d FAILED\n", current_test);
+ (void)fprintf(stderr,
+ "'%s' integer attribute eval failed, value = %u.\n",
+ attr, fld);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void assert_boolean(char *attr, bool fld, bool val)
+{
+ if (fld != val) {
+ (void)fprintf(stderr, "case %d FAILED\n", current_test);
+ (void)fprintf(stderr,
+ "'%s' boolean attribute eval failed, value = %s.\n",
+ attr, fld ? "true" : "false");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void assert_other(char *desc, int val, int val1)
+{
+ if (val != val1) {
+ (void)fprintf(stderr, "case %d FAILED\n", current_test);
+ (void)fprintf(stderr, "'%s' was %d, s/b %d\n", desc, val, val1);
+ exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * Floating point comparisons are iffy, but at least if any of these fail
+ * the output will make it clear whether it was a precision issue
+ */
+static void assert_real(char *attr, double fld, double val)
+{
+ if (fld != val) {
+ (void)fprintf(stderr, "case %d FAILED\n", current_test);
+ (void)fprintf(stderr,
+ "'%s' real attribute eval failed, value = %f.\n", attr,
+ fld);
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+static struct gps_data_t gpsdata;
+
+/* Case 1: TPV report */
+
+/* *INDENT-OFF* */
+static const char json_str1[] = "{\"class\":\"TPV\",\
+ \"device\":\"GPS#1\", \
+ \"time\":\"2005-06-19T08:12:41.89Z\",\"lon\":46.498203637,\"lat\":7.568074350,\
+ \"alt\":1327.780,\"epx\":21.000,\"epy\":23.000,\"epv\":124.484,\"mode\":3}";
+
+/*
+ * Case 2: SKY report
+ *
+ * The fields of the last satellite entry are arranged in the reverse order
+ * of the structure fields, in order to test for field overflow.
+ */
+
+static const char *json_str2 = "{\"class\":\"SKY\",\
+ \"time\":\"2005-06-19T12:12:42.03Z\", \
+ \"satellites\":[\
+ {\"PRN\":10,\"el\":45,\"az\":196,\"ss\":34,\"used\":true},\
+ {\"PRN\":29,\"el\":67,\"az\":310,\"ss\":40,\"used\":true},\
+ {\"PRN\":28,\"el\":59,\"az\":108,\"ss\":42,\"used\":true},\
+ {\"PRN\":26,\"el\":51,\"az\":304,\"ss\":43,\"used\":true},\
+ {\"PRN\":8,\"el\":44,\"az\":58,\"ss\":41,\"used\":true},\
+ {\"PRN\":27,\"el\":16,\"az\":66,\"ss\":39,\"used\":true},\
+ {\"az\":301,\"el\":10,\"PRN\":21,\"used\":false,\"ss\":0}]}";
+
+/* Case 3: String list syntax */
+
+static const char *json_str3 = "[\"foo\",\"bar\",\"baz\"]";
+
+static char *stringptrs[3];
+static char stringstore[256];
+static int stringcount;
+
+static const struct json_array_t json_array_3 = {
+ .element_type = t_string,
+ .arr.strings.ptrs = stringptrs,
+ .arr.strings.store = stringstore,
+ .arr.strings.storelen = sizeof(stringstore),
+ .count = &stringcount,
+ .maxlen = sizeof(stringptrs)/sizeof(stringptrs[0]),
+};
+
+/* Case 4: test defaulting of unspecified attributes */
+
+static const char *json_str4 = "{\"flag1\":true,\"flag2\":false}";
+
+static bool flag1, flag2;
+static double dftreal;
+static int dftinteger;
+static unsigned int dftuinteger;
+
+static const struct json_attr_t json_attrs_4[] = {
+ {"dftint", t_integer, .addr.integer = &dftinteger, .dflt.integer = -5},
+ {"dftuint", t_integer, .addr.uinteger = &dftuinteger, .dflt.uinteger = 10},
+ {"dftreal", t_real, .addr.real = &dftreal, .dflt.real = 23.17},
+ {"flag1", t_boolean, .addr.boolean = &flag1,},
+ {"flag2", t_boolean, .addr.boolean = &flag2,},
+ {NULL},
+};
+
+/* Case 5: test DEVICE parsing */
+
+static const char *json_str5 = "{\"class\":\"DEVICE\",\
+ \"path\":\"/dev/ttyUSB0\",\
+ \"flags\":5,\
+ \"driver\":\"Foonly\",\"subtype\":\"Foonly Frob\"\
+ }";
+
+/* Case 6: test parsing of subobject list into array of structures */
+
+static const char *json_str6 = "{\"parts\":[\
+ {\"name\":\"Urgle\", \"flag\":true, \"count\":3},\
+ {\"name\":\"Burgle\",\"flag\":false,\"count\":1},\
+ {\"name\":\"Witter\",\"flag\":true, \"count\":4},\
+ {\"name\":\"Thud\", \"flag\":false,\"count\":1}]}";
+
+struct dumbstruct_t {
+ char name[64];
+ bool flag;
+ int count;
+};
+static struct dumbstruct_t dumbstruck[5];
+static int dumbcount;
+
+static const struct json_attr_t json_attrs_6_subtype[] = {
+ {"name", t_string, .addr.offset = offsetof(struct dumbstruct_t, name),
+ .len = 64},
+ {"flag", t_boolean, .addr.offset = offsetof(struct dumbstruct_t, flag),},
+ {"count", t_integer, .addr.offset = offsetof(struct dumbstruct_t, count),},
+ {NULL},
+};
+
+static const struct json_attr_t json_attrs_6[] = {
+ {"parts", t_array, .addr.array.element_type = t_structobject,
+ .addr.array.arr.objects.base = (char*)&dumbstruck,
+ .addr.array.arr.objects.stride = sizeof(struct dumbstruct_t),
+ .addr.array.arr.objects.subtype = json_attrs_6_subtype,
+ .addr.array.count = &dumbcount,
+ .addr.array.maxlen = sizeof(dumbstruck)/sizeof(dumbstruck[0])},
+ {NULL},
+};
+
+/* Case 7: test parsing of version response */
+
+static const char *json_str7 = "{\"class\":\"VERSION\",\
+ \"release\":\"2.40dev\",\"rev\":\"dummy-revision\",\
+ \"proto_major\":3,\"proto_minor\":1}";
+
+/* Case 8: test parsing arrays of enumerated types */
+
+static const char *json_str8 = "{\"fee\":\"FOO\",\"fie\":\"BAR\",\"foe\":\"BAZ\"}";
+static const struct json_enum_t enum_table[] = {
+ {"BAR", 6}, {"FOO", 3}, {"BAZ", 14}, {NULL}
+};
+
+static int fee, fie, foe;
+static const struct json_attr_t json_attrs_8[] = {
+ {"fee", t_integer, .addr.integer = &fee, .map=enum_table},
+ {"fie", t_integer, .addr.integer = &fie, .map=enum_table},
+ {"foe", t_integer, .addr.integer = &foe, .map=enum_table},
+ {NULL},
+};
+
+/* Case 9: Like case 6 but w/ an empty array */
+
+static const char *json_str9 = "{\"parts\":[]}";
+
+/* Case 10: test parsing of PPS message */
+
+static const char *json_strPPS = "{\"class\":\"PPS\",\"device\":\"GPS#1\"," \
+ "\"real_sec\":1428001514, \"real_nsec\":1000000," \
+ "\"clock_sec\":1428001513,\"clock_nsec\":999999999," \
+ "\"precision\":-20}";
+
+/* Case 11: test parsing of TOFF message */
+
+static const char *json_strTOFF = "{\"class\":\"TOFF\",\"device\":\"GPS#1\"," \
+ "\"real_sec\":1428001514, \"real_nsec\":1000000," \
+ "\"clock_sec\":1428001513,\"clock_nsec\":999999999}";
+
+/* Case 12: test parsing of OSC message */
+
+static const char *json_strOSC = "{\"class\":\"OSC\",\"device\":\"GPS#1\"," \
+ "\"running\":true,\"reference\":true,\"disciplined\":false," \
+ "\"delta\":67}";
+
+/* Case 13: test parsing of ERROR message, and some escape sequences */
+
+static char *json_strErr = "{\"class\":\"ERROR\",\"message\":" \
+ "\"Hello\b\f\n\r\t\"}";
+
+/* Case 14: test parsing of ERROR message and \u escape */
+/* per ECMA-404, \u must be followed by 4 hex digits */
+
+static char *json_strErr1 = "{\"class\":\"ERROR\",\"message\":\"0\\u00334\"}";
+
+/* Case 15: test buffer overflow of short string destination */
+
+static char *json_strOver = "{\"name\":\"\\u0033\\u0034\\u0035\\u0036\"}";
+
+char json_short_string_dst[2];
+int json_short_string_cnt = 5;
+static const struct json_attr_t json_short_string[] = {
+ {"name", t_string,
+ .addr.string = json_short_string_dst,
+ .len = sizeof(json_short_string_dst)},
+ {"count", t_integer, .addr.integer = &json_short_string_cnt},
+ {NULL},
+};
+
+/* Case 16: test buffer overflow of short string destination */
+
+static char json_strOver2[7 * JSON_VAL_MAX]; /* dynamically built */
+
+
+#ifndef JSON_MINIMAL
+/* Case 17: Read array of integers */
+
+static const char *json_strInt = "[23,-17,5]";
+static int intstore[4], intcount;
+
+static const struct json_array_t json_array_Int = {
+ .element_type = t_integer,
+ .arr.integers.store = intstore,
+ .count = &intcount,
+ .maxlen = sizeof(intstore)/sizeof(intstore[0]),
+};
+
+/* Case 18: Read array of booleans */
+
+static const char *json_strBool = "[true,false,true]";
+static bool boolstore[4];
+static int boolcount;
+
+static const struct json_array_t json_array_Bool = {
+ .element_type = t_boolean,
+ .arr.booleans.store = boolstore,
+ .count = &boolcount,
+ .maxlen = sizeof(boolstore)/sizeof(boolstore[0]),
+};
+
+/* Case 19: Read array of reals */
+
+static const char *json_strReal = "[23.1,-17.2,5.3]";
+static double realstore[4];
+static int realcount;
+
+static const struct json_array_t json_array_Real = {
+ .element_type = t_real,
+ .arr.reals.store = realstore,
+ .count = &realcount,
+ .maxlen = sizeof(realstore)/sizeof(realstore[0]),
+};
+#endif /* JSON_MINIMAL */
+
+/* *INDENT-ON* */
+
+static void jsontest(int i)
+{
+ int status = 0; /* libgps_json_unpack() returned status */
+ int n; /* generic index */
+
+ if (0 < debug) {
+ (void)fprintf(stderr, "Running test #%d.\n", i);
+ }
+ current_test = i;
+
+ /* do not keep old data! */
+ memset((void *)&gpsdata, 0, sizeof(gpsdata));
+
+ switch (i)
+ {
+ case 1:
+ status = libgps_json_unpack(json_str1, &gpsdata, NULL);
+ assert_case(status);
+ assert_string("device", gpsdata.dev.path, "GPS#1");
+ assert_integer("mode", gpsdata.fix.mode, 3);
+ assert_real("time", gpsdata.fix.time, 1119168761.8900001);
+ assert_real("lon", gpsdata.fix.longitude, 46.498203637);
+ assert_real("lat", gpsdata.fix.latitude, 7.568074350);
+ break;
+
+ case 2:
+ status = libgps_json_unpack(json_str2, &gpsdata, NULL);
+ assert_case(status);
+ assert_integer("used", gpsdata.satellites_used, 6);
+ assert_integer("PRN[0]", gpsdata.skyview[0].PRN, 10);
+ assert_integer("el[0]", gpsdata.skyview[0].elevation, 45);
+ assert_integer("az[0]", gpsdata.skyview[0].azimuth, 196);
+ assert_real("ss[0]", gpsdata.skyview[0].ss, 34);
+ assert_boolean("used[0]", gpsdata.skyview[0].used, true);
+ assert_integer("PRN[6]", gpsdata.skyview[6].PRN, 21);
+ assert_integer("el[6]", gpsdata.skyview[6].elevation, 10);
+ assert_integer("az[6]", gpsdata.skyview[6].azimuth, 301);
+ assert_real("ss[6]", gpsdata.skyview[6].ss, 0);
+ assert_boolean("used[6]", gpsdata.skyview[6].used, false);
+ break;
+
+ case 3:
+ status = json_read_array(json_str3, &json_array_3, NULL);
+ assert_case(status);
+ assert_other("stringcount", stringcount, 3);
+ assert_other("stringptrs[0] == foo", strcmp(stringptrs[0], "foo"), 0);
+ assert_other("stringptrs[1] == bar", strcmp(stringptrs[1], "bar"), 0);
+ assert_other("stringptrs[2] == baz", strcmp(stringptrs[2], "baz"), 0);
+ break;
+
+ case 4:
+ status = json_read_object(json_str4, json_attrs_4, NULL);
+ assert_case(status);
+ assert_integer("dftint", dftinteger, -5); /* did the default work? */
+ assert_uinteger("dftuint", dftuinteger, 10); /* did the default work? */
+ assert_real("dftreal", dftreal, 23.17); /* did the default work? */
+ assert_boolean("flag1", flag1, true);
+ assert_boolean("flag2", flag2, false);
+ break;
+
+ case 5:
+ status = libgps_json_unpack(json_str5, &gpsdata, NULL);
+ assert_case(status);
+ assert_string("path", gpsdata.dev.path, "/dev/ttyUSB0");
+ assert_integer("flags", gpsdata.dev.flags, 5);
+ assert_string("driver", gpsdata.dev.driver, "Foonly");
+ break;
+
+ case 6:
+ status = json_read_object(json_str6, json_attrs_6, NULL);
+ assert_case(status);
+ assert_integer("dumbcount", dumbcount, 4);
+ assert_string("dumbstruck[0].name", dumbstruck[0].name, "Urgle");
+ assert_string("dumbstruck[1].name", dumbstruck[1].name, "Burgle");
+ assert_string("dumbstruck[2].name", dumbstruck[2].name, "Witter");
+ assert_string("dumbstruck[3].name", dumbstruck[3].name, "Thud");
+ assert_boolean("dumbstruck[0].flag", dumbstruck[0].flag, true);
+ assert_boolean("dumbstruck[1].flag", dumbstruck[1].flag, false);
+ assert_boolean("dumbstruck[2].flag", dumbstruck[2].flag, true);
+ assert_boolean("dumbstruck[3].flag", dumbstruck[3].flag, false);
+ assert_integer("dumbstruck[0].count", dumbstruck[0].count, 3);
+ assert_integer("dumbstruck[1].count", dumbstruck[1].count, 1);
+ assert_integer("dumbstruck[2].count", dumbstruck[2].count, 4);
+ assert_integer("dumbstruck[3].count", dumbstruck[3].count, 1);
+ break;
+
+ case 7:
+ status = libgps_json_unpack(json_str7, &gpsdata, NULL);
+ assert_case(status);
+ assert_string("release", gpsdata.version.release, "2.40dev");
+ assert_string("rev", gpsdata.version.rev, "dummy-revision");
+ assert_integer("proto_major", gpsdata.version.proto_major, 3);
+ assert_integer("proto_minor", gpsdata.version.proto_minor, 1);
+ break;
+
+ case 8:
+ status = json_read_object(json_str8, json_attrs_8, NULL);
+ assert_case(status);
+ assert_integer("fee", fee, 3);
+ assert_integer("fie", fie, 6);
+ assert_integer("foe", foe, 14);
+ break;
+
+ case 9:
+ /* yes, the '6' in the next line is correct */
+ status = json_read_object(json_str9, json_attrs_6, NULL);
+ assert_case(status);
+ assert_integer("dumbcount", dumbcount, 0);
+ break;
+
+ case 10:
+ status = json_pps_read(json_strPPS, &gpsdata, NULL);
+ assert_case(status);
+ assert_string("device", gpsdata.dev.path, "GPS#1");
+ assert_integer("real_sec", gpsdata.pps.real.tv_sec, 1428001514);
+ assert_integer("real_nsec", gpsdata.pps.real.tv_nsec, 1000000);
+ assert_integer("clock_sec", gpsdata.pps.clock.tv_sec, 1428001513);
+ assert_integer("clock_nsec", gpsdata.pps.clock.tv_nsec, 999999999);
+ break;
+
+ case 11:
+ status = json_toff_read(json_strTOFF, &gpsdata, NULL);
+ assert_case(status);
+ assert_string("device", gpsdata.dev.path, "GPS#1");
+ assert_integer("real_sec", gpsdata.pps.real.tv_sec, 1428001514);
+ assert_integer("real_nsec", gpsdata.pps.real.tv_nsec, 1000000);
+ assert_integer("clock_sec", gpsdata.pps.clock.tv_sec, 1428001513);
+ assert_integer("clock_nsec", gpsdata.pps.clock.tv_nsec, 999999999);
+ break;
+
+ case 12:
+ status = json_oscillator_read(json_strOSC, &gpsdata, NULL);
+ assert_case(status);
+ assert_string("device", gpsdata.dev.path, "GPS#1");
+ assert_boolean("running", gpsdata.osc.running, true);
+ assert_boolean("reference", gpsdata.osc.reference, true);
+ assert_boolean("disciplined", gpsdata.osc.disciplined, false);
+ assert_integer("delta", gpsdata.osc.delta, 67);
+ break;
+
+ case 13:
+ if (2 < debug) {
+ (void)fprintf(stderr, "test string: %s.\n", json_strErr);
+ }
+ status = libgps_json_unpack(json_strErr, &gpsdata, NULL);
+ assert_case(status);
+ assert_string("message", gpsdata.error, "Hello\b\f\n\r\t");
+ break;
+
+ case 14:
+ if (2 < debug) {
+ (void)fprintf(stderr, "test string: %s.\n", json_strErr1);
+ }
+ status = libgps_json_unpack(json_strErr1, &gpsdata, NULL);
+ assert_case(status);
+ assert_string("message", gpsdata.error, "034");
+ break;
+
+ case 15:
+ /* check for string overrun caught */
+ if (2 < debug) {
+ (void)fprintf(stderr, "test string: %s.\n", json_strOver);
+ }
+ json_short_string_cnt = 7;
+ status = json_read_object(json_strOver, json_short_string, NULL);
+ assert_case(JSON_ERR_STRLONG != status);
+ assert_string("name", json_short_string_dst, "");
+ assert_integer("count", json_short_string_cnt, 0);
+ break;
+
+ case 16:
+ /* check for string overrun caught */
+ json_strOver2[0] = '\0';
+ /* build a LONG test string */
+ strlcat(json_strOver2, "{\"name\":\"", sizeof(json_strOver2));
+ for (n = 0; n < (2 * JSON_VAL_MAX); n++) {
+ strlcat(json_strOver2, "\\u0033", sizeof(json_strOver2));
+ }
+ strlcat(json_strOver2, "\"}", sizeof(json_strOver2));
+
+ if (2 < debug) {
+ (void)fprintf(stderr, "test string: %s.\n", json_strOver2);
+ }
+ json_short_string_cnt = 7;
+ status = json_read_object(json_strOver2, json_short_string, NULL);
+ assert_case(JSON_ERR_STRLONG != status);
+ assert_string("name", json_short_string_dst, "");
+ assert_integer("count", json_short_string_cnt, 0);
+ break;
+
+ case 17:
+ /* check for a different string overrun caught */
+ json_strOver2[0] = '\0';
+ /* build a LONG test string */
+ strlcat(json_strOver2, "{\"name\":\"", sizeof(json_strOver2));
+ for (n = 0; n < (2 * JSON_VAL_MAX); n++) {
+ strlcat(json_strOver2, "\\A", sizeof(json_strOver2));
+ }
+ strlcat(json_strOver2, "\"}", sizeof(json_strOver2));
+
+ if (2 < debug) {
+ (void)fprintf(stderr, "test string: %s.\n", json_strOver2);
+ }
+ json_short_string_cnt = 7;
+ status = json_read_object(json_strOver2, json_short_string, NULL);
+ assert_case(JSON_ERR_STRLONG != status);
+ assert_string("name", json_short_string_dst, "");
+ assert_integer("count", json_short_string_cnt, 0);
+ break;
+
+#ifdef JSON_MINIMAL
+#define MAXTEST 17
+#else
+ case 18:
+ status = json_read_array(json_strInt, &json_array_Int, NULL);
+ assert_integer("count", intcount, 3);
+ assert_integer("intstore[0]", intstore[0], 23);
+ assert_integer("intstore[1]", intstore[1], -17);
+ assert_integer("intstore[2]", intstore[2], 5);
+ assert_integer("intstore[3]", intstore[3], 0);
+ break;
+
+ case 19:
+ status = json_read_array(json_strBool, &json_array_Bool, NULL);
+ assert_integer("count", boolcount, 3);
+ assert_boolean("boolstore[0]", boolstore[0], true);
+ assert_boolean("boolstore[1]", boolstore[1], false);
+ assert_boolean("boolstore[2]", boolstore[2], true);
+ assert_boolean("boolstore[3]", boolstore[3], false);
+ break;
+
+ case 20:
+ status = json_read_array(json_strReal, &json_array_Real, NULL);
+ assert_integer("count", realcount, 3);
+ assert_real("realstore[0]", realstore[0], 23.1);
+ assert_real("realstore[1]", realstore[1], -17.2);
+ assert_real("realstore[2]", realstore[2], 5.3);
+ assert_real("realstore[3]", realstore[3], 0);
+ break;
+
+#define MAXTEST 20
+#endif /* JSON_MINIMAL */
+
+ default:
+ (void)fputs("Unknown test number\n", stderr);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main(int argc UNUSED, char *argv[]UNUSED)
+{
+ int option;
+ int individual = 0;
+
+ while ((option = getopt(argc, argv, "D:hn:V?")) != -1) {
+ switch (option) {
+#ifdef CLIENTDEBUG_ENABLE
+ case 'D':
+ debug = atoi(optarg);
+ gps_enable_debug(debug, stdout);
+ break;
+#endif
+ case 'n':
+ individual = atoi(optarg);
+ break;
+ case '?':
+ case 'h':
+ default:
+ (void)fprintf(stderr,
+ "usage: %s [-D lvl] [-n tst] [-V]\n"
+ " -D lvl set debug level\n"
+ " -n tst run only test tst\n"
+ " -V Print version and exit\n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ case 'V':
+ (void)fprintf(stderr, "%s: %s (revision %s)\n",
+ argv[0], VERSION, REVISION);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ (void)fprintf(stderr, "JSON unit tests\n");
+
+ if (individual)
+ jsontest(individual);
+ else {
+ int i;
+ for (i = 1; i <= MAXTEST; i++) {
+ jsontest(i);
+ }
+ }
+
+ (void)fprintf(stderr, "succeeded.\n");
+
+ exit(EXIT_SUCCESS);
+}
+
+/* end */
diff --git a/tests/test_libgps.c b/tests/test_libgps.c
new file mode 100644
index 00000000..050cd443
--- /dev/null
+++ b/tests/test_libgps.c
@@ -0,0 +1,153 @@
+/*
+ * A simple command-line exerciser for the library.
+ * Not really useful for anything but debugging.
+ * SPDX-License-Identifier: BSD-2-clause
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "../gps.h"
+#include "../libgps.h"
+#include "../gpsdclient.h"
+
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+
+static void onsig(int sig)
+{
+ (void)fprintf(stderr, "libgps: died with signal %d\n", sig);
+ exit(EXIT_FAILURE);
+}
+
+#ifdef SOCKET_EXPORT_ENABLE
+/* must start zeroed, otherwise the unit test will try to chase garbage pointer fields. */
+static struct gps_data_t gpsdata;
+#endif
+
+int main(int argc, char *argv[])
+{
+ struct gps_data_t collect;
+ struct fixsource_t source;
+ char buf[BUFSIZ];
+ int option;
+ bool batchmode = false;
+ bool forwardmode = false;
+ char *fmsg = NULL;
+#ifdef CLIENTDEBUG_ENABLE
+ int debug = 0;
+#endif
+
+ (void)signal(SIGSEGV, onsig);
+#ifdef SIGBUS
+ (void)signal(SIGBUS, onsig);
+#endif
+
+ while ((option = getopt(argc, argv, "bf:hsD:?")) != -1) {
+ switch (option) {
+ case 'b':
+ batchmode = true;
+ break;
+ case 'f':
+ forwardmode = true;
+ fmsg = optarg;
+ break;
+ case 's':
+ (void)
+ printf("Sizes: fix=%zd gpsdata=%zd rtcm2=%zd rtcm3=%zd "
+ "ais=%zd compass=%zd raw=%zd devices=%zd policy=%zd "
+ "version=%zd, noise=%zd\n",
+ sizeof(struct gps_fix_t),
+ sizeof(struct gps_data_t), sizeof(struct rtcm2_t),
+ sizeof(struct rtcm3_t), sizeof(struct ais_t),
+ sizeof(struct attitude_t), sizeof(struct rawdata_t),
+ sizeof(collect.devices), sizeof(struct gps_policy_t),
+ sizeof(struct version_t), sizeof(struct gst_t));
+ exit(EXIT_SUCCESS);
+#ifdef CLIENTDEBUG_ENABLE
+ case 'D':
+ debug = atoi(optarg);
+ break;
+#endif
+ case '?':
+ case 'h':
+ default:
+ (void)fputs("usage: test_libgps [-b] [-f fwdmsg] [-D lvl] [-s] [server[:port:[device]]]\n", stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Grok the server, port, and device. */
+ if (optind < argc) {
+ gpsd_source_spec(argv[optind], &source);
+ } else
+ gpsd_source_spec(NULL, &source);
+
+#ifdef CLIENTDEBUG_ENABLE
+ gps_enable_debug(debug, stdout);
+#endif
+ if (batchmode) {
+#ifdef SOCKET_EXPORT_ENABLE
+ while (fgets(buf, sizeof(buf), stdin) != NULL) {
+ if (buf[0] == '{' || isalpha( (int) buf[0])) {
+ gps_unpack(buf, &gpsdata);
+#ifdef LIBGPS_DEBUG
+ libgps_dump_state(&gpsdata);
+#endif
+ }
+ }
+#endif
+ } else if (gps_open(source.server, source.port, &collect) != 0) {
+ (void)fprintf(stderr,
+ "test_libgps: no gpsd running or network error: %d, %s\n",
+ errno, gps_errstr(errno));
+ exit(EXIT_FAILURE);
+ } else if (forwardmode) {
+ if (gps_send(&collect, fmsg) == -1) {
+ (void)fprintf(stderr,
+ "test_libgps: gps send error: %d, %s\n",
+ errno, gps_errstr(errno));
+ }
+ if (gps_read(&collect, NULL, 0) == -1) {
+ (void)fprintf(stderr,
+ "test_libgps: gps read error: %d, %s\n",
+ errno, gps_errstr(errno));
+ }
+#ifdef SOCKET_EXPORT_ENABLE
+#ifdef LIBGPS_DEBUG
+ libgps_dump_state(&collect);
+#endif
+#endif
+ (void)gps_close(&collect);
+ } else {
+ int tty = isatty(0);
+
+ if (tty)
+ (void)fputs("This is the gpsd exerciser.\n", stdout);
+ for (;;) {
+ if (tty)
+ (void)fputs("> ", stdout);
+ if (fgets(buf, sizeof(buf), stdin) == NULL) {
+ if (tty)
+ putchar('\n');
+ break;
+ }
+ collect.set = 0;
+ (void)gps_send(&collect, buf);
+ (void)gps_read(&collect, NULL, 0);
+#ifdef SOCKET_EXPORT_ENABLE
+#ifdef LIBGPS_DEBUG
+ libgps_dump_state(&collect);
+#endif
+#endif
+ }
+ (void)gps_close(&collect);
+ }
+ return 0;
+}
+
diff --git a/tests/test_matrix.c b/tests/test_matrix.c
new file mode 100644
index 00000000..a4a342aa
--- /dev/null
+++ b/tests/test_matrix.c
@@ -0,0 +1,88 @@
+/*
+ * Unit test for matrix-algebra code
+ *
+ * Check examples computed at
+ * http://www.elektro-energetika.cz/calculations/matreg.php
+ *
+ * This file is Copyright (c) 2010 by the GPSD project
+ * SPDX-License-Identifier: BSD-2-clause
+ */
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "../compiler.h"
+#include "../matrix.h"
+
+static struct {
+ double mat[4][4];
+ double inv[4][4];
+} inverses[] = {
+ /* identity matrix is self-inverse */
+ {
+ .mat = {{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1}},
+ .inv = {{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1}},
+ },
+ /* inverse of a diagonal matrix has reciprocal values */
+ {
+ .mat = {{10,0,0,0}, {0,10,0,0}, {0,0,10,0}, {0,0,0,10}},
+ .inv = {{0.1,0,0,0}, {0,0.1,0,0}, {0,0,0.1,0}, {0,0,0,0.1}},
+ },
+ /* random values with asymmetrical off-diagonal elements */
+ {
+ .mat = {{1,0,0,0}, {0,1,0,-2}, {0, 2,1,-4}, {0,0,0,1}},
+ .inv = {{1,0,0,0}, {0,1,0, 2}, {0,-2,1, 0}, {0,0,0,1}},
+ },
+ {
+ .mat = {{6,-4,1,-3},{-4,7,3,2},{1,3,6,-4},{-3,2,-4,6}},
+ .inv = {{14,34,-40,-31},{34,84,-99,-77},{-40,-99,117,91},{-31,-77,91,71}},
+ },
+};
+
+static void dump(const char *label, double m[4][4])
+{
+ printf("%s:\n", label);
+ printf("%f, %f, %f, %f\n", m[0][0], m[0][1], m[0][2], m[0][3]);
+ printf("%f, %f, %f, %f\n", m[1][0], m[1][1], m[1][2], m[1][3]);
+ printf("%f, %f, %f, %f\n", m[2][0], m[2][1], m[2][2], m[2][3]);
+ printf("%f, %f, %f, %f\n", m[3][0], m[3][1], m[3][2], m[3][3]);
+}
+
+static bool check_diag(int n, double a[4][4], double b[4][4])
+{
+#define approx(x, y) (fabs(x - y) < 0.0001)
+
+ if (approx(b[0][0], a[0][0]) && approx(b[1][1], a[1][1]) &&
+ approx(b[2][2], a[2][2]) && approx(b[3][3], a[3][3]))
+ return true;
+
+ dump("a", a);
+ dump("b", b);
+ printf("Test %d residuals: %f %f %f %f\n",
+ n,
+ b[0][0] - a[0][0],
+ b[1][1] - a[1][1],
+ b[2][2] - a[2][2],
+ b[3][3] - a[3][3]
+ );
+ return true;
+}
+
+int main(int argc UNUSED, char *argv[] UNUSED)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(inverses) / sizeof(inverses[0]); i++) {
+ double inverse[4][4];
+ if (!matrix_invert(inverses[i].mat, inverse)) {
+ printf("Vanishing determinant in test %u\n", i);
+ }
+ if (!check_diag(i, inverse, inverses[i].inv))
+ break;
+ }
+
+ printf("Matrix-algebra regression test succeeded\n");
+ exit(0);
+}
diff --git a/tests/test_mktime.c b/tests/test_mktime.c
new file mode 100644
index 00000000..46b49556
--- /dev/null
+++ b/tests/test_mktime.c
@@ -0,0 +1,177 @@
+/*
+ * tests for mktime(), mkgmtime(), unix_to_iso8601() and iso8601_to_unix().
+ * mktime() is a libc function, why test it?
+ *
+ * This file is Copyright (c) 2010 by the GPSD project
+ * SPDX-License-Identifier: BSD-2-clause
+ */
+#include <math.h> /* for fabs() */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "../gps.h"
+#include "../compiler.h"
+
+static struct
+{
+ struct tm t;
+ time_t result;
+} tests[] = {
+ /* *INDENT-OFF* */
+ /* sec, min, h, md, mon, year, wd, yd, isdst, gmtoff, zone timestamp what */
+ {{ 0, 0, 0, 1, 0, 70, 0, 0, 0, 0, 0, }, 0 },
+ {{ 0, 0, 0, 1, 0, 70, 0, 0, 0, 0, 0, }, 0 }, /* lower limit */
+ {{ 7, 14, 3, 19, 0, 138, 0, 0, 0, 0, 0, }, 0x7fffffff }, /* upper limit */
+ {{ 0, 0, 12, 1, 0, 99, 0, 0, 0, 0, 0, }, 915192000 }, /* leap year */
+ {{ 0, 0, 12, 1, 1, 99, 0, 0, 0, 0, 0, }, 917870400 }, /* leap year */
+ {{ 0, 0, 12, 1, 2, 99, 0, 0, 0, 0, 0, }, 920289600 }, /* leap year */
+ {{ 0, 0, 12, 1, 8, 99, 0, 0, 0, 0, 0, }, 936187200 }, /* leap year */
+ {{ 0, 0, 12, 1, 0, 100, 0, 0, 0, 0, 0, }, 946728000 }, /* leap year */
+ {{ 0, 0, 12, 1, 1, 100, 0, 0, 0, 0, 0, }, 949406400 }, /* leap year */
+ {{ 0, 0, 12, 1, 2, 100, 0, 0, 0, 0, 0, }, 951912000 }, /* leap year */
+ {{ 0, 0, 12, 1, 8, 100, 0, 0, 0, 0, 0, }, 967809600 }, /* leap year */
+ {{ 0, 0, 12, 1, 0, 101, 0, 0, 0, 0, 0, }, 978350400 }, /* leap year */
+ {{ 0, 0, 12, 1, 1, 101, 0, 0, 0, 0, 0, }, 981028800 }, /* leap year */
+ {{ 0, 0, 12, 1, 2, 101, 0, 0, 0, 0, 0, }, 983448000 }, /* leap year */
+ {{ 0, 0, 12, 1, 8, 101, 0, 0, 0, 0, 0, }, 999345600 }, /* leap year */
+ {{ 0, 0, 12, 1, 0, 102, 0, 0, 0, 0, 0, }, 1009886400 }, /* leap year */
+ {{ 0, 0, 12, 1, 1, 102, 0, 0, 0, 0, 0, }, 1012564800 }, /* leap year */
+ {{ 0, 0, 12, 1, 2, 102, 0, 0, 0, 0, 0, }, 1014984000 }, /* leap year */
+ {{ 0, 0, 12, 1, 8, 102, 0, 0, 0, 0, 0, }, 1030881600 }, /* leap year */
+ {{ 0, 0, 12, 1, 0, 103, 0, 0, 0, 0, 0, }, 1041422400 }, /* leap year */
+ {{ 0, 0, 12, 1, 1, 103, 0, 0, 0, 0, 0, }, 1044100800 }, /* leap year */
+ {{ 0, 0, 12, 1, 2, 103, 0, 0, 0, 0, 0, }, 1046520000 }, /* leap year */
+ {{ 0, 0, 12, 1, 8, 103, 0, 0, 0, 0, 0, }, 1062417600 }, /* leap year */
+ {{ 0, 0, 12, 1, 0, 104, 0, 0, 0, 0, 0, }, 1072958400 }, /* leap year */
+ {{ 0, 0, 12, 1, 1, 104, 0, 0, 0, 0, 0, }, 1075636800 }, /* leap year */
+ {{ 0, 0, 12, 1, 2, 104, 0, 0, 0, 0, 0, }, 1078142400 }, /* leap year */
+ {{ 0, 0, 12, 1, 8, 104, 0, 0, 0, 0, 0, }, 1094040000 }, /* leap year */
+ {{ 0, 0, 12, 1, 0, 108, 0, 0, 0, 0, 0, }, 1199188800 }, /* leap year */
+ {{ 0, 0, 12, 1, 1, 108, 0, 0, 0, 0, 0, }, 1201867200 }, /* leap year */
+ {{ 0, 0, 12, 1, 2, 108, 0, 0, 0, 0, 0, }, 1204372800 }, /* leap year */
+ {{ 0, 0, 12, 1, 8, 108, 0, 0, 0, 0, 0, }, 1220270400 }, /* leap year */
+ {{ 59, 59, 23, 31, 12, 110, 0, 0, 0, 0, 0, }, 1296518399 }, /* year wrap */
+ {{ 0, 0, 0, 1, 0, 111, 0, 0, 0, 0, 0, }, 1293840000 }, /* year wrap */
+ {{ 59, 59, 23, 31, 12, 111, 0, 0, 0, 0, 0, }, 1328054399 }, /* year wrap */
+ {{ 0, 0, 0, 1, 0, 112, 0, 0, 0, 0, 0, }, 1325376000 }, /* year wrap */
+ {{ 59, 59, 23, 31, 12, 112, 0, 0, 0, 0, 0, }, 1359676799 }, /* year wrap */
+ {{ 0, 0, 0, 1, 0, 113, 0, 0, 0, 0, 0, }, 1356998400 }, /* year wrap */
+ {{ 59, 59, 23, 31, 0, 115, 0, 0, 0, 0, 0, }, 1422748799 }, /* month wrap */
+ {{ 0, 0, 0, 1, 1, 115, 0, 0, 0, 0, 0, }, 1422748800 }, /* month wrap */
+ {{ 59, 59, 23, 28, 1, 115, 0, 0, 0, 0, 0, }, 1425167999 }, /* month wrap */
+ {{ 0, 0, 0, 1, 2, 115, 0, 0, 0, 0, 0, }, 1425168000 }, /* month wrap */
+ {{ 59, 59, 23, 31, 2, 115, 0, 0, 0, 0, 0, }, 1427846399 }, /* month wrap */
+ {{ 0, 0, 0, 1, 3, 115, 0, 0, 0, 0, 0, }, 1427846400 }, /* month wrap */
+ {{ 59, 59, 23, 30, 3, 115, 0, 0, 0, 0, 0, }, 1430438399 }, /* month wrap */
+ {{ 0, 0, 0, 1, 4, 115, 0, 0, 0, 0, 0, }, 1430438400 }, /* month wrap */
+ {{ 59, 59, 23, 31, 4, 115, 0, 0, 0, 0, 0, }, 1433116799 }, /* month wrap */
+ {{ 0, 0, 0, 1, 5, 115, 0, 0, 0, 0, 0, }, 1433116800 }, /* month wrap */
+ {{ 59, 59, 23, 30, 5, 115, 0, 0, 0, 0, 0, }, 1435708799 }, /* month wrap */
+ {{ 0, 0, 0, 1, 6, 115, 0, 0, 0, 0, 0, }, 1435708800 }, /* month wrap */
+ {{ 59, 59, 23, 31, 6, 115, 0, 0, 0, 0, 0, }, 1438387199 }, /* month wrap */
+ {{ 0, 0, 0, 1, 7, 115, 0, 0, 0, 0, 0, }, 1438387200 }, /* month wrap */
+ {{ 59, 59, 23, 31, 7, 115, 0, 0, 0, 0, 0, }, 1441065599 }, /* month wrap */
+ {{ 0, 0, 0, 1, 8, 115, 0, 0, 0, 0, 0, }, 1441065600 }, /* month wrap */
+ {{ 59, 59, 23, 30, 8, 115, 0, 0, 0, 0, 0, }, 1443657599 }, /* month wrap */
+ {{ 0, 0, 0, 1, 9, 115, 0, 0, 0, 0, 0, }, 1443657600 }, /* month wrap */
+ {{ 59, 59, 23, 31, 9, 115, 0, 0, 0, 0, 0, }, 1446335999 }, /* month wrap */
+ {{ 0, 0, 0, 1, 10, 115, 0, 0, 0, 0, 0, }, 1446336000 }, /* month wrap */
+ {{ 59, 59, 23, 30, 10, 115, 0, 0, 0, 0, 0, }, 1448927999 }, /* month wrap */
+ {{ 0, 0, 0, 1, 11, 115, 0, 0, 0, 0, 0, }, 1448928000 }, /* month wrap */
+ {{ 59, 59, 23, 31, 11, 115, 0, 0, 0, 0, 0, }, 1451606399 }, /* month wrap */
+ {{ 0, 0, 0, 1, 0, 116, 0, 0, 0, 0, 0, }, 1451606400 }, /* month wrap */
+ /* *INDENT-ON* */
+};
+
+
+/* tests for unit_to_iso8601() */
+static struct
+{
+ timestamp_t unixtime; /* unix time */
+ char *iso8601; /* iso8601 result */
+} tests1[] = {
+ /* time zero */
+ {(timestamp_t)0, "1970-01-01T00:00:00.000Z"},
+
+ /* before/after leap second end of 2008, notice no :60! */
+ {(timestamp_t)1230767999.01, "2008-12-31T23:59:59.010Z"},
+ {(timestamp_t)1230768000.02, "2009-01-01T00:00:00.020Z"},
+
+ /* test for rounding at %.3f */
+ {(timestamp_t)1541766896.999412, "2018-11-09T12:34:56.999Z"},
+ {(timestamp_t)1541766896.999499, "2018-11-09T12:34:56.999Z"},
+ {(timestamp_t)1541766896.999500, "2018-11-09T12:34:57.000Z"},
+ {(timestamp_t)1541766896.999501, "2018-11-09T12:34:57.000Z"},
+
+ /* the end of time: 2038 */
+ {(timestamp_t)2147483647.123456, "2038-01-19T03:14:07.123Z"},
+ {(timestamp_t)2147483648.123456, "2038-01-19T03:14:08.123Z"},
+};
+
+int main(int argc UNUSED, char *argv[] UNUSED)
+{
+ int i;
+ char tbuf[128];
+ bool failed = false;
+ timestamp_t ttime;
+
+ (void)setenv("TZ", "GMT", 1);
+
+ /* test mktime() */
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ time_t ts = mktime(&tests[i].t);
+ if (ts != tests[i].result) {
+ failed = true;
+ (void)strftime(tbuf, sizeof(tbuf), "%F %T", &tests[i].t);
+ (void)printf("test_mktime: mktime() test %2d failed.\n"
+ " Time returned from: %s should be %lu "
+ " (but was: %lu)\n",
+ i, tbuf, (unsigned long)tests[i].result,
+ (unsigned long)ts);
+ }
+ }
+
+ /* test mkgmtime() */
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ time_t ts = mkgmtime(&tests[i].t);
+ if (ts != tests[i].result) {
+ failed = true;
+ (void)strftime(tbuf, sizeof(tbuf), "%F %T", &tests[i].t);
+ (void)printf("test_mktime: mkgmtime() test %2d failed.\n"
+ " Time returned from: %s should be %lu "
+ " (but was: %lu)\n",
+ i, tbuf, (unsigned long)tests[i].result,
+ (unsigned long)ts);
+ }
+ }
+
+ /* test unix_to_iso8601() */
+ for (i = 0; i < (int)(sizeof(tests1) / sizeof(tests1[0])); i++) {
+ unix_to_iso8601(tests1[i].unixtime, tbuf, sizeof(tbuf));
+ if (0 != strcmp(tests1[i].iso8601, tbuf)) {
+ failed = true;
+ (void)printf("test_mktime: unix_to_iso8601() test %f failed.\n"
+ " Got %s, s/b %s\n",
+ tests1[i].unixtime, tbuf, tests1[i].iso8601);
+ }
+ }
+
+ /* test iso8601_to_unix() */
+ for (i = 0; i < (int)(sizeof(tests1) / sizeof(tests1[0])); i++) {
+ ttime = iso8601_to_unix(tests1[i].iso8601);
+ if (0.001 <= fabs(ttime - tests1[i].unixtime)) {
+ failed = true;
+ (void)printf("test_mktime: iso8601_to_unit() test %s failed.\n"
+ " Got %.3f, s/b %.3f\n",
+ tests1[i].iso8601, ttime, tests1[i].unixtime);
+ }
+ }
+
+ return (int)failed;
+}
+
+/* end */
+
diff --git a/tests/test_nmea2000 b/tests/test_nmea2000
new file mode 100755
index 00000000..d154ef22
--- /dev/null
+++ b/tests/test_nmea2000
@@ -0,0 +1,117 @@
+#!/bin/sh
+#
+# The regression-test driver script for the nmea2000 driver.
+# CAN is a bit different from normal serial interfaces.
+#
+
+GPSD_PORT=2948
+CAN_PLAYER=canplayer
+CAN_UNIT="vcan0"
+BUILD=0
+
+# Requires GNU date extensions
+# Should return an empty blank string if those are not present.
+STARTTIME=`date +"%s" 2>/dev/null`
+
+# We need to have the build directory in $GPSD_HOME to find the new gpsd
+if [ "`dirname $0`" = "." ]; then
+ GPSD_HOME=`pwd`
+else
+ GPSD_HOME=`dirname $0`
+fi
+
+version()
+{
+ echo
+ echo `basename $0`" : Version v0.20";
+ echo
+}
+
+usage()
+{
+ version;
+ echo "usage :" `basename $0` " [-S <portnumber>] [-u <can_device>] [-p <canplayer>] [-b] <testfile>";
+ echo " :" `basename $0` " -v";
+ echo " :" `basename $0` " -h";
+ echo " Options include:";
+ echo " -S <portnumber> = Port for gpsd communication. Default is 2948.";
+ echo " -u <can_device> = Used CAN device. Default is \"vcan0\"";
+ echo " -p <canplayer> = Programm to replay logfile. Default is \"canplayer\"";
+ echo " -b = Create testfile.chk instead of checking it.";
+ echo " -v = Print version and exit.";
+ echo " -h = Print this helptext and exit.";
+ echo " <testfile> = A file created by \"candump -l <can_device> ><testfile>\".";
+ echo
+}
+
+device()
+{
+ echo
+ echo "CAN device \"${CAN_UNIT}\" do not exit."
+ echo "Try: sudo ./gpsinit vcan "
+ echo
+}
+
+can_utils()
+(
+ echo
+ echo "Command to play CAN logfile \"${CAN_PLAYER}\" do not exist."
+ echo "Try to install \"can-utils\" from your distribution."
+ echo "I your distibution do not provide \"can-utils\", then"
+ echo "git clone https://gitorious.org/linux-can/can-utils.git"
+ echo "in a directory outside of gpsd, build and install it."
+ echo "On most linux systems, \"make\", and then \"sudo make install\" works."
+ echo
+)
+
+while getopts :S:u:p:vhb opt
+do
+ case ${opt} in
+ S) GPSD_PORT=${OPTARG};;
+ u) CAN_UNIT=${OPTARG};;
+ p) CAN_PLAYER=${OPTARG};;
+ b) BUILD=1;;
+ v) version; exit 0;;
+ h) usage; exit 0;;
+ \?) usage; exit 1;;
+ esac
+done
+
+shift $((${OPTIND} - 1))
+
+if [ -z $1 ]; then usage; exit 1; fi
+
+TEST_FILE=$1
+
+if [ `ifconfig -a | grep ${CAN_UNIT} | wc -c` -eq 0 ]; then device; exit 1; fi
+
+if [ `command -v ${CAN_PLAYER} | wc -c` -eq 0 ]; then can_utils; exit 1; fi
+
+TMP_PID_FILE=`mktemp`
+TMP_OUT_FILE=`mktemp`
+
+${GPSD_HOME}/gpsd -n -G -D0 -S ${GPSD_PORT} -P ${TMP_PID_FILE} nmea2000://${CAN_UNIT}
+
+sleep 1
+
+${GPSD_HOME}/gpspipe -d -r -w -S -o ${TMP_OUT_FILE} :${GPSD_PORT}
+
+sleep 1
+
+${CAN_PLAYER} -I${TEST_FILE} ${CAN_UNIT}=can0
+
+sleep 1
+
+kill `cat ${TMP_PID_FILE}`
+
+sleep 1
+
+if [ ${BUILD} -ne 0 ]
+then
+ cp ${TMP_OUT_FILE} ${TEST_FILE}.chk
+else
+ diff ${TEST_FILE}.chk ${TMP_OUT_FILE}
+fi
+
+rm -f ${TMP_PID_FILE}
+rm -f ${TMP_OUT_FILE}
diff --git a/tests/test_packet.c b/tests/test_packet.c
new file mode 100644
index 00000000..6f486b24
--- /dev/null
+++ b/tests/test_packet.c
@@ -0,0 +1,409 @@
+/*
+ * This file is Copyright (c) 2010 by the GPSD project
+ * SPDX-License-Identifier: BSD-2-clause
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "../gpsd.h"
+
+static int verbose = 0;
+
+struct map
+{
+ char *legend;
+ char test[MAX_PACKET_LENGTH + 1];
+ size_t testlen;
+ int garbage_offset;
+ int type;
+};
+
+/* *INDENT-OFF* */
+static struct map singletests[] = {
+ /* NMEA tests */
+ {
+ .legend = "NMEA packet with checksum (1)",
+ .test = "$GPVTG,308.74,T,,M,0.00,N,0.0,K*68\r\n",
+ .testlen = 36,
+ .garbage_offset = 0,
+ NMEA_PACKET,
+ },
+ {
+ .legend = "NMEA packet with checksum (2)",
+ .test = "$GPGGA,110534.994,4002.1425,N,07531.2585,W,0,00,50.0,172.7,M,-33.8,M,0.0,0000*7A\r\n",
+ .testlen = 82,
+ .garbage_offset = 0,
+ .type = NMEA_PACKET,
+ },
+ {
+ .legend = "NMEA packet with checksum and 4 chars of leading garbage",
+ .test = "\xff\xbf\x00\xbf$GPVTG,308.74,T,,M,0.00,N,0.0,K*68\r\n",
+ .testlen = 40,
+ .garbage_offset = 4,
+ .type = NMEA_PACKET,
+ },
+ {
+ .legend = "NMEA packet without checksum",
+ .test = "$PSRF105,1\r\n",
+ .testlen = 12,
+ .garbage_offset = 0,
+ .type = NMEA_PACKET,
+ },
+ {
+ .legend = "NMEA packet with wrong checksum",
+ .test = "$GPVTG,308.74,T,,M,0.00,N,0.0,K*28\r\n",
+ .testlen = 36,
+ .garbage_offset = 0,
+ .type = BAD_PACKET,
+ },
+ {
+ .legend = "NMEA interspersed packet",
+ .test = "$GPZDA,112533.00,20,01,20$PTNTA,20000102173852,1,T4,,,6,1,0*32\r\n",
+ .testlen = 64,
+ .garbage_offset = 25,
+ .type = NMEA_PACKET,
+ },
+ {
+ .legend = "NMEA interrupted packet",
+ .test = "$GPZDA,112533.00,20,01,2016,00,00*67\r\n$GPZDA,112533.00,20,01,20$PTNTA,20000102173852,1,T4,,,6,1,0*32\r\n16,00,00*67\r\n",
+ .testlen = 115,
+ .garbage_offset = 0,
+ .type = NMEA_PACKET,
+ },
+ /* SiRF tests */
+ {
+ .legend = "SiRF WAAS version ID",
+ .test = {
+ 0xA0, 0xA2, 0x00, 0x15,
+ 0x06, 0x06, 0x31, 0x2E, 0x32, 0x2E, 0x30, 0x44,
+ 0x4B, 0x49, 0x54, 0x31, 0x31, 0x39, 0x20, 0x53,
+ 0x4D, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x82, 0xB0, 0xB3},
+ .testlen = 29,
+ .garbage_offset = 0,
+ .type = SIRF_PACKET,
+ },
+ {
+ .legend = "SiRF WAAS version ID with 3 chars of leading garbage",
+ .test = {
+ 0xff, 0x00, 0xff,
+ 0xA0, 0xA2, 0x00, 0x15,
+ 0x06, 0x06, 0x31, 0x2E, 0x32, 0x2E, 0x30, 0x44,
+ 0x4B, 0x49, 0x54, 0x31, 0x31, 0x39, 0x20, 0x53,
+ 0x4D, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x82, 0xB0, 0xB3},
+ .testlen = 32,
+ .garbage_offset = 3,
+ .type = SIRF_PACKET,
+ },
+ {
+ .legend = "SiRF WAAS version ID with wrong checksum",
+ .test = {
+ 0xA0, 0xA2, 0x00, 0x15,
+ 0x06, 0x06, 0x31, 0x2E, 0x32, 0x2E, 0x30, 0x44,
+ 0x4B, 0x49, 0x54, 0x31, 0x31, 0x39, 0x20, 0x53,
+ 0x4D, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0xB0, 0xB3},
+ .testlen = 29,
+ .garbage_offset = 0,
+ .type = BAD_PACKET,
+ },
+ {
+ .legend = "SiRF WAAS version ID with bad length",
+ .test = {
+ 0xA0, 0xA2, 0xff, 0x15,
+ 0x06, 0x06, 0x31, 0x2E, 0x32, 0x2E, 0x30, 0x44,
+ 0x4B, 0x49, 0x54, 0x31, 0x31, 0x39, 0x20, 0x53,
+ 0x4D, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x82, 0xB0, 0xB3},
+ .testlen = 29,
+ .garbage_offset = 0,
+ .type = BAD_PACKET,
+ },
+ /* Zodiac tests */
+ {
+ .legend = "Zodiac binary 1000 Geodetic Status Output Message",
+ .test = {
+ 0xff, 0x81, 0xe8, 0x03, 0x31, 0x00, 0x00, 0x00, 0xe8, 0x79,
+ 0x74, 0x0e, 0x00, 0x00, 0x24, 0x00, 0x24, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x03, 0x23, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x06, 0x00,
+ 0xcd, 0x07, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x7b, 0x0d,
+ 0x00, 0x00, 0x12, 0x6b, 0xa7, 0x04, 0x41, 0x75, 0x32, 0xf8,
+ 0x03, 0x1f, 0x00, 0x00, 0xe6, 0xf2, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x11, 0xf6, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x40,
+ 0xd9, 0x12, 0x90, 0xd0, 0x03, 0x00, 0x00, 0xa3, 0xe1, 0x11,
+ 0x10, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0xe1, 0x11,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0x93, 0x04, 0x00, 0x04, 0xaa},
+ .testlen = 110,
+ .garbage_offset = 0,
+ .type = ZODIAC_PACKET,
+ },
+ /* EverMore tests */
+ {
+ .legend = "EverMore status packet 0x20",
+ .test = {
+ 0x10, 0x02, 0x0D, 0x20, 0xE1, 0x00, 0x00, 0x00,
+ 0x0A, 0x00, 0x1E, 0x00, 0x32, 0x00, 0x5b, 0x10,
+ 0x03},
+ .testlen = 17,
+ .garbage_offset = 0,
+ .type = EVERMORE_PACKET,
+ },
+ {
+ .legend = "EverMore packet 0x04 with 0x10 0x10 sequence",
+ .test = {
+ 0x10, 0x02, 0x0f, 0x04, 0x00, 0x00, 0x10, 0x10,
+ 0xa7, 0x13, 0x03, 0x2c, 0x26, 0x24, 0x0a, 0x17,
+ 0x00, 0x68, 0x10, 0x03},
+ .testlen = 20,
+ .garbage_offset = 0,
+ .type = EVERMORE_PACKET,
+ },
+ {
+ .legend = "EverMore packet 0x04 with 0x10 0x10 sequence, some noise before packet data",
+ .test = {
+ 0x10, 0x03, 0xff, 0x10, 0x02, 0x0f, 0x04, 0x00,
+ 0x00, 0x10, 0x10, 0xa7, 0x13, 0x03, 0x2c, 0x26,
+ 0x24, 0x0a, 0x17, 0x00, 0x68, 0x10, 0x03},
+ .testlen = 23,
+ .garbage_offset = 3,
+ .type = EVERMORE_PACKET,
+ },
+ {
+ .legend = "EverMore packet 0x04, 0x10 and some other data at the beginning",
+ .test = {
+ 0x10, 0x12, 0x10, 0x03, 0xff, 0x10, 0x02, 0x0f,
+ 0x04, 0x00, 0x00, 0x10, 0x10, 0xa7, 0x13, 0x03,
+ 0x2c, 0x26, 0x24, 0x0a, 0x17, 0x00, 0x68, 0x10,
+ 0x03},
+ .testlen = 25,
+ .garbage_offset = 5,
+ .type = EVERMORE_PACKET,
+ },
+ {
+ .legend = "EverMore packet 0x04, 0x10 three times at the beginning",
+ .test = {
+ 0x10, 0x10, 0x10,
+ 0x10, 0x02, 0x0f, 0x04, 0x00, 0x00, 0x10, 0x10,
+ 0xa7, 0x13, 0x03, 0x2c, 0x26, 0x24, 0x0a, 0x17,
+ 0x00, 0x68, 0x10, 0x03},
+ .testlen = 23,
+ .garbage_offset = 3,
+ .type = EVERMORE_PACKET,
+ },
+ {
+ /* from page 4-3 of RTCM 10403.1 */
+ .legend = "RTCM104V3 type 1005 packet",
+ /*
+ * Reference Station Id = 2003
+ * GPS Service supported, but not GLONASS or Galileo
+ * ARP ECEF-X = 1114104.5999 meters
+ * ARP ECEF-Y = -4850729.7108 meters
+ * ARP ECEF-Z = 3975521.4643 meters
+ */
+ .test = {
+ 0xD3, 0x00, 0x13, 0x3E, 0xD7, 0xD3, 0x02, 0x02,
+ 0x98, 0x0E, 0xDE, 0xEF, 0x34, 0xB4, 0xBD, 0x62,
+ 0xAC, 0x09, 0x41, 0x98, 0x6F, 0x33, 0x36, 0x0B,
+ 0x98,
+ },
+ .testlen = 25,
+ .garbage_offset = 0,
+ .type = RTCM3_PACKET,
+ },
+ {
+ .legend = "RTCM104V3 type 1005 packet with 4th byte garbled",
+ .test = {
+ 0xD3, 0x00, 0x13, 0x3F, 0xD7, 0xD3, 0x02, 0x02,
+ 0x98, 0x0E, 0xDE, 0xEF, 0x34, 0xB4, 0xBD, 0x62,
+ 0xAC, 0x09, 0x41, 0x98, 0x6F, 0x33, 0x36, 0x0B,
+ 0x98,
+ },
+ .testlen = 25,
+ .garbage_offset = 0,
+ .type = BAD_PACKET,
+ },
+ {
+ /* from page 3-71 of the RTCM 10403.1 */
+ .legend = "RTCM104V3 type 1029 packet",
+ .test = {
+ 0xD3, 0x00, 0x27, 0x40, 0x50, 0x17, 0x00, 0x84,
+ 0x73, 0x6E, 0x15, 0x1E, 0x55, 0x54, 0x46, 0x2D,
+ 0x38, 0x20, 0xD0, 0xBF, 0xD1, 0x80, 0xD0, 0xBE,
+ 0xD0, 0xB2, 0xD0, 0xB5, 0xD1, 0x80, 0xD0, 0xBA,
+ 0xD0, 0xB0, 0x20, 0x77, 0xC3, 0xB6, 0x72, 0x74,
+ 0x65, 0x72, 0xED, 0xA3, 0x3B
+ },
+ .testlen = 45,
+ .garbage_offset = 0,
+ .type = RTCM3_PACKET,
+ },
+};
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+static struct map runontests[] = {
+ /* NMEA tests */
+ {
+ .legend = "Double NMEA packet with checksum",
+ .test = "$GPVTG,308.74,T,,M,0.00,N,0.0,K*68\r\n$GPGGA,110534.994,4002.1425,N,07531.2585,W,0,00,50.0,172.7,M,-33.8,M,0.0,0000*7A\r\n",
+ .testlen = 118,
+ 0,
+ NMEA_PACKET,
+ },
+};
+/* *INDENT-ON* */
+
+static int packet_test(struct map *mp)
+{
+ struct gps_lexer_t lexer;
+ int failure = 0;
+
+ lexer_init(&lexer);
+ lexer.errout.debug = verbose;
+ memcpy(lexer.inbufptr = lexer.inbuffer, mp->test, mp->testlen);
+ lexer.inbuflen = mp->testlen;
+ packet_parse(&lexer);
+ if (lexer.type != mp->type)
+ printf("%2ti: %s test FAILED (packet type %d wrong).\n",
+ mp - singletests + 1, mp->legend, lexer.type);
+ else if (memcmp
+ (mp->test + mp->garbage_offset, lexer.outbuffer,
+ lexer.outbuflen)) {
+ printf("%2ti: %s test FAILED (data garbled).\n", mp - singletests + 1,
+ mp->legend);
+ ++failure;
+ } else
+ printf("%2ti: %s test succeeded.\n", mp - singletests + 1,
+ mp->legend);
+
+ return failure;
+}
+
+static void runon_test(struct map *mp)
+{
+ struct gps_lexer_t lexer;
+ int nullfd = open("/dev/null", O_RDONLY);
+ ssize_t st;
+
+ lexer_init(&lexer);
+ lexer.errout.debug = verbose;
+ memcpy(lexer.inbufptr = lexer.inbuffer, mp->test, mp->testlen);
+ lexer.inbuflen = mp->testlen;
+ (void)fputs(mp->test, stdout);
+ do {
+ st = packet_get(nullfd, &lexer);
+ //printf("packet_parse() returned %zd\n", st);
+ } while (st > 0);
+}
+
+static int property_check(void)
+{
+ const struct gps_type_t **dp;
+ int status;
+
+ for (dp = gpsd_drivers; *dp; dp++) {
+ if (*dp == NULL || (*dp)->packet_type == COMMENT_PACKET)
+ continue;
+
+#ifdef RECONFIGURE_ENABLE
+ if (CONTROLLABLE(*dp))
+ (void)fputs("control\t", stdout);
+ else
+ (void)fputs(".\t", stdout);
+ if ((*dp)->event_hook != NULL)
+ (void)fputs("hook\t", stdout);
+ else
+ (void)fputs(".\t", stdout);
+#endif /* RECONFIGURE_ENABLE */
+ if ((*dp)->trigger != NULL)
+ (void)fputs("trigger\t", stdout);
+ else if ((*dp)->probe_detect != NULL)
+ (void)fputs("probe\t", stdout);
+ else
+ (void)fputs(".\t", stdout);
+#ifdef CONTROLSEND_ENABLE
+ if ((*dp)->control_send != NULL)
+ (void)fputs("send\t", stdout);
+ else
+ (void)fputs(".\t", stdout);
+#endif /* CONTROLSEND_ENABLE */
+ if ((*dp)->packet_type > NMEA_PACKET)
+ (void)fputs("binary\t", stdout);
+ else
+ (void)fputs("NMEA\t", stdout);
+#ifdef CONTROLSEND_ENABLE
+ if (STICKY(*dp))
+ (void)fputs("sticky\t", stdout);
+ else
+ (void)fputs(".\t", stdout);
+#endif /* CONTROLSEND_ENABLE */
+ (void)puts((*dp)->type_name);
+ }
+
+ status = EXIT_SUCCESS;
+ for (dp = gpsd_drivers; *dp; dp++) {
+ if (*dp == NULL || (*dp)->packet_type == COMMENT_PACKET)
+ continue;
+#if defined(CONTROLSEND_ENABLE) && defined(RECONFIGURE_ENABLE)
+ if (CONTROLLABLE(*dp) && (*dp)->control_send == NULL) {
+ (void)fprintf(stderr, "%s has control methods but no send\n",
+ (*dp)->type_name);
+ status = EXIT_FAILURE;
+ }
+ if ((*dp)->event_hook != NULL && (*dp)->control_send == NULL) {
+ (void)fprintf(stderr, "%s has event hook but no send\n",
+ (*dp)->type_name);
+ status = EXIT_FAILURE;
+ }
+#endif /* CONTROLSEND_ENABLE && RECONFIGURE_ENABLE*/
+ }
+
+ return status;
+}
+
+int main(int argc, char *argv[])
+{
+ struct map *mp;
+ int failcount = 0;
+ int option, singletest = 0;
+
+ verbose = 0;
+ while ((option = getopt(argc, argv, "ce:t:v:")) != -1) {
+ switch (option) {
+ case 'c':
+ exit(property_check());
+ case 'e':
+ mp = singletests + atoi(optarg) - 1;
+ (void)fwrite(mp->test, mp->testlen, sizeof(char), stdout);
+ (void)fflush(stdout);
+ exit(EXIT_SUCCESS);
+ case 't':
+ singletest = atoi(optarg);
+ break;
+ case 'v':
+ verbose = atoi(optarg);
+ break;
+ }
+ }
+
+ if (singletest)
+ failcount += packet_test(singletests + singletest - 1);
+ else {
+ (void)fputs("=== Packet identification tests ===\n", stdout);
+ for (mp = singletests;
+ mp < singletests + sizeof(singletests) / sizeof(singletests[0]);
+ mp++)
+ failcount += packet_test(mp);
+ (void)fputs("=== EOF with buffer nonempty test ===\n", stdout);
+ runon_test(&runontests[0]);
+ }
+ exit(failcount > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/tests/test_timespec.c b/tests/test_timespec.c
new file mode 100644
index 00000000..826071b0
--- /dev/null
+++ b/tests/test_timespec.c
@@ -0,0 +1,557 @@
+/*
+ * Unit test for timespec's
+ *
+ * This file is Copyright (c) 2010 by the GPSD project
+ * SPDX-License-Identifier: BSD-2-clause
+ *
+ */
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h> /* required by C99, for int32_t */
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+
+#include "../compiler.h"
+#include "../revision.h"
+#include "../ppsthread.h"
+#include "../timespec.h"
+
+#define TS_ZERO {0,0}
+#define TS_ZERO_ONE {0,1}
+#define TS_ZERO_TWO {0,2}
+#define TS_ZERO_TREES {0,333333333}
+#define TS_ZERO_SIXS7 {0,666666667}
+#define TS_ZERO_NINES {0,999999999}
+#define TS_ONE {1,0}
+#define TS_ONE_ONE {1,1}
+#define TS_TWO {2,0}
+#define TS_N_ZERO_ONE {0,-1}
+#define TS_N_ZERO_TWO {0,-2}
+#define TS_N_ZERO_TREES {0,-333333333}
+#define TS_N_ZERO_NINES {0,-999999999}
+#define TS_N_ONE {-1,0}
+
+/* minutes, hours, days */
+#define TS_ONEM {60,0} /* one minute */
+#define TS_ONEM_TREES {60,333333333} /* one minute, threes */
+#define TS_ONEM_NINES {60,999999999} /* one minute, nines */
+#define TS_ONEH {3600,0} /* one hour */
+#define TS_ONEH_TREES {3600,333333333} /* one hour, threes */
+#define TS_ONEH_NINES {3600,999999999} /* one hour, nines */
+#define TS_ONED {86400,0} /* one day */
+#define TS_ONED_TREES {86400,333333333} /* one day, threes */
+#define TS_ONED_NINES {86400,999999999} /* one day, nines */
+#define TS_N_ONEM {-60,0} /* negative one minute */
+#define TS_N_ONEH {-3600,0} /* negative one hour */
+#define TS_N_ONED {-86400,0} /* negative one day */
+
+/* Dec 31, 23:59 2037 GMT */
+#define TS_2037 {2145916799, 0}
+#define TS_2037_ONE {2145916799, 1}
+#define TS_2037_TWO {2145916799, 2}
+#define TS_2037_X {2145916799, 123456789}
+#define TS_2037_TREES {2145916799, 333333333}
+#define TS_2037_SIXS7 {2145916799, 666666667}
+#define TS_2037_NINES {2145916799, 999999999}
+#define TS_N_2037_TREES {-2145916799, -333333333}
+#define TS_N_2037_NINES {-2145916799, -999999999}
+
+/* a 32 bit copy of timespec_diff_ns() to force a 32 bit int */
+/* used to demonstrate how 32 bit longs can not work */
+#define timespec_diff_ns32(x, y) \
+ (int32_t)((int32_t)(((x).tv_sec-(y).tv_sec)*NS_IN_SEC)+(x).tv_nsec-(y).tv_nsec)
+
+/* a 64 bit copy of timespec_diff_ns() to force a 64 bit int */
+/* used to demonstrate how 64 bit long longs can work */
+#define timespec_diff_ns64(x, y) \
+ (int64_t)((int64_t)(((x).tv_sec-(y).tv_sec)*NS_IN_SEC)+(x).tv_nsec-(y).tv_nsec)
+
+/* convert long long ns to a timespec */
+#define ns_to_timespec(ts, ns) \
+ (ts).tv_sec = ns / NS_IN_SEC; \
+ (ts).tv_nsec = ns % NS_IN_SEC;
+
+/* convert double to a timespec */
+static inline void d_str( const double d, char *buf, size_t buf_size)
+{
+ /* convert to string */
+ if ( 0 <= d ) {
+ (void) snprintf( buf, buf_size, " %.9f", d);
+ } else {
+ (void) snprintf( buf, buf_size, "%.9f", d);
+ }
+}
+
+/* a - b should be c */
+struct subtract_test {
+ struct timespec a;
+ struct timespec b;
+ struct timespec c;
+ bool last; /* last test marker */
+};
+
+struct subtract_test subtract_tests[] = {
+ { TS_ZERO, TS_ZERO, TS_ZERO, 0},
+ { TS_ONE, TS_ONE, TS_ZERO, 0},
+ { TS_ZERO_ONE, TS_ZERO_ONE, TS_ZERO, 0},
+ { TS_ONE_ONE, TS_ONE_ONE, TS_ZERO, 0},
+ { TS_N_ONE, TS_N_ONE, TS_ZERO, 0},
+ { TS_N_ZERO_ONE, TS_N_ZERO_ONE, TS_ZERO, 0},
+ { TS_ZERO_TREES, TS_ZERO_TREES, TS_ZERO, 0},
+ { TS_ZERO_NINES, TS_ZERO_NINES, TS_ZERO, 0},
+ { TS_ZERO_TREES, TS_ZERO, TS_ZERO_TREES, 0},
+ { TS_ZERO, TS_N_ONE, TS_ONE, 0},
+ { TS_ONE, TS_ZERO, TS_ONE, 0},
+ { TS_TWO, TS_ONE, TS_ONE, 0},
+ { TS_ONE_ONE, TS_ONE, TS_ZERO_ONE, 0},
+ { TS_ONE, TS_ZERO_TREES, TS_ZERO_SIXS7, 0},
+ { TS_ONE, TS_ZERO_NINES, TS_ZERO_ONE, 0},
+ { TS_ZERO_TWO, TS_ZERO_ONE, TS_ZERO_ONE, 0},
+ { TS_2037_ONE, TS_2037, TS_ZERO_ONE, 0},
+ { TS_ONE_ONE, TS_ZERO_NINES, TS_ZERO_TWO, 0},
+ { TS_ONEM, TS_ZERO, TS_ONEM, 0},
+ { TS_ONEM_TREES, TS_ZERO, TS_ONEM_TREES, 0},
+ { TS_ONEM_NINES, TS_ZERO, TS_ONEM_NINES, 0},
+ { TS_ZERO, TS_ONEM, TS_N_ONEM, 0},
+ { TS_ONEH, TS_ZERO, TS_ONEH, 0},
+ { TS_ONEH_TREES, TS_ZERO, TS_ONEH_TREES, 0},
+ { TS_ONEH_NINES, TS_ZERO, TS_ONEH_NINES, 0},
+ { TS_ZERO, TS_ONEH, TS_N_ONEH, 0},
+ { TS_ONED, TS_ZERO, TS_ONED, 0},
+ { TS_ONED_TREES, TS_ZERO, TS_ONED_TREES, 0},
+ { TS_ONED_NINES, TS_ZERO, TS_ONED_NINES, 0},
+ { TS_ZERO, TS_ONED, TS_N_ONED, 0},
+ { TS_2037_NINES, TS_2037, TS_ZERO_NINES, 0},
+ { TS_2037_TREES, TS_ZERO, TS_2037_TREES, 0},
+ { TS_2037_SIXS7, TS_2037, TS_ZERO_SIXS7, 0},
+ { TS_2037_TREES, TS_2037, TS_ZERO_TREES, 0},
+ { TS_2037_NINES, TS_ZERO, TS_2037_NINES, 0},
+ { TS_ZERO, TS_ONE, TS_N_ONE, 0},
+ { TS_ONE, TS_TWO, TS_N_ONE, 0},
+ { TS_ZERO, TS_ZERO_ONE, TS_N_ZERO_ONE, 0},
+ { TS_ONE, TS_ONE_ONE, TS_N_ZERO_ONE, 0},
+ { TS_ZERO_ONE, TS_ZERO_TWO, TS_N_ZERO_ONE, 0},
+ { TS_2037, TS_2037_ONE, TS_N_ZERO_ONE, 0},
+ { TS_ZERO_NINES, TS_ONE_ONE, TS_N_ZERO_TWO, 0},
+ { TS_2037, TS_2037_NINES, TS_N_ZERO_NINES, 0},
+ { TS_ZERO, TS_2037_NINES, TS_N_2037_NINES, 1},
+};
+
+typedef struct format_test {
+ struct timespec input;
+ char *expected;
+ bool last;
+} format_test_t;
+
+struct format_test format_tests[] = {
+ { TS_ZERO, " 0.000000000", 0},
+ { TS_ZERO_ONE, " 0.000000001", 0},
+ { TS_ZERO_TWO, " 0.000000002", 0},
+ { TS_ZERO_NINES, " 0.999999999", 0},
+ { TS_ONE, " 1.000000000", 0},
+ { TS_ONE_ONE, " 1.000000001", 0},
+ { TS_TWO, " 2.000000000", 0},
+ { TS_N_ZERO_ONE, "-0.000000001", 0},
+ { TS_N_ZERO_TWO, "-0.000000002", 0},
+ { TS_N_ZERO_NINES, "-0.999999999", 0},
+ { TS_N_ONE, "-1.000000000", 0},
+ { TS_ONEM, " 60.000000000", 0},
+ { TS_ONEM_TREES, " 60.333333333", 0},
+ { TS_ONEH, " 3600.000000000", 0},
+ { TS_ONEH_TREES, " 3600.333333333", 0},
+ { TS_ONED, " 86400.000000000", 0},
+ { TS_ONED_TREES, " 86400.333333333", 0},
+ { TS_N_ONEM, "-60.000000000", 0},
+ { TS_N_ONEH, "-3600.000000000", 0},
+ { TS_N_ONED, "-86400.000000000", 0},
+ { { -1, 1}, "-1.000000001", 0},
+ { { -1, -1}, "-1.000000001", 0},
+ { TS_2037, " 2145916799.000000000", 0},
+ { TS_2037_ONE, " 2145916799.000000001", 0},
+ { TS_2037_TREES, " 2145916799.333333333", 1},
+ { TS_2037_NINES, " 2145916799.999999999", 1},
+};
+
+/*
+ * test subtractions using native timespec math: TS_SUB()
+ *
+ */
+static int test_ts_subtract( int verbose )
+{
+ struct subtract_test *p = subtract_tests;
+ int fail_count = 0;
+
+ while ( 1 ) {
+ char buf_a[TIMESPEC_LEN];
+ char buf_b[TIMESPEC_LEN];
+ char buf_c[TIMESPEC_LEN];
+ char buf_r[TIMESPEC_LEN];
+ struct timespec r;
+
+ TS_SUB(&r, &p->a, &p->b);
+ timespec_str( &p->a, buf_a, sizeof(buf_a) );
+ timespec_str( &p->b, buf_b, sizeof(buf_b) );
+ timespec_str( &p->c, buf_c, sizeof(buf_c) );
+ timespec_str( &r, buf_r, sizeof(buf_r) );
+ if ( (p->c.tv_sec != r.tv_sec) || (p->c.tv_nsec != r.tv_nsec) ) {
+ printf("%21s - %21s = %21s, FAIL s/b %21s\n",
+ buf_a, buf_b, buf_r, buf_c);
+ fail_count++;
+ } else if ( verbose ) {
+ printf("%21s - %21s = %21s\n", buf_a, buf_b, buf_r);
+ }
+
+
+ if ( p->last ) {
+ break;
+ }
+ p++;
+ };
+
+ if ( fail_count ) {
+ printf("timespec subtract test failed %d tests\n", fail_count );
+ } else {
+ puts("timespec subtract test succeeded\n");
+ }
+ return fail_count;
+}
+
+/*
+ * test subtractions using timespec_diff_ns()
+ *
+ */
+static int test_ns_subtract( int verbose )
+{
+ struct subtract_test *p = subtract_tests;
+ int fail_count = 0;
+
+ while ( 1 ) {
+ char buf_a[TIMESPEC_LEN];
+ char buf_b[TIMESPEC_LEN];
+ char buf_c[TIMESPEC_LEN];
+ char buf_r[TIMESPEC_LEN];
+ struct timespec r;
+ long long r_ns;
+
+ r_ns = timespec_diff_ns(p->a, p->b);
+ timespec_str( &p->a, buf_a, sizeof(buf_a) );
+ timespec_str( &p->b, buf_b, sizeof(buf_b) );
+ timespec_str( &p->c, buf_c, sizeof(buf_c) );
+ ns_to_timespec( r, r_ns);
+ timespec_str( &r, buf_r, sizeof(buf_r) );
+ if ( (p->c.tv_sec != r.tv_sec) || (p->c.tv_nsec != r.tv_nsec) ) {
+ printf("%21s - %21s = %21s, FAIL s/b %21s\n",
+ buf_a, buf_b, buf_r, buf_c);
+ fail_count++;
+ } else if ( verbose ) {
+ printf("%21s - %21s = %21s\n", buf_a, buf_b, buf_r);
+ }
+
+
+ if ( p->last ) {
+ break;
+ }
+ p++;
+ };
+
+ if ( fail_count ) {
+ printf("ns subtract test failed %d tests\n", fail_count );
+ } else {
+ puts("ns subtract test succeeded\n");
+ }
+ return fail_count;
+}
+
+static int test_format(int verbose )
+{
+ format_test_t *p = format_tests;
+ int fail_count = 0;
+
+ while ( 1 ) {
+ char buf[TIMESPEC_LEN];
+ int fail;
+
+ timespec_str( &p->input, buf, sizeof(buf) );
+ fail = strncmp( buf, p->expected, TIMESPEC_LEN);
+ if ( fail ) {
+ printf("%21s, FAIL s/b: %21s\n", buf, p->expected);
+ fail_count++;
+ } else if ( verbose ) {
+ printf("%21s\n", buf);
+ }
+
+ if ( p->last ) {
+ break;
+ }
+ p++;
+ };
+
+ if ( fail_count ) {
+ printf("timespec_str test failed %d tests\n", fail_count );
+ } else {
+ puts("timespec_str test succeeded\n");
+ }
+ return fail_count;
+}
+
+static int ex_subtract_float( void )
+{
+ struct subtract_test *p = subtract_tests;
+ int fail_count = 0;
+
+ printf( "\n\nsubtract test examples using doubles,floats,longs:\n"
+ " ts: TS_SUB()\n"
+ " l: timespec_to_ns() math\n"
+ " l32: timespec_to_ns() math with 32 bit long\n"
+ " l64: timespec_to_ns() math with 64 bit long\n"
+ " f: float math\n"
+ " d: double float math\n"
+ "\n");
+
+ while ( 1 ) {
+ char buf_a[TIMESPEC_LEN];
+ char buf_b[TIMESPEC_LEN];
+ char buf_c[TIMESPEC_LEN];
+ char buf_r[TIMESPEC_LEN];
+ char buf_l[TIMESPEC_LEN];
+ char buf_l32[TIMESPEC_LEN];
+ char buf_l64[TIMESPEC_LEN];
+ char buf_f[TIMESPEC_LEN];
+ char buf_d[TIMESPEC_LEN];
+ struct timespec ts_r;
+ struct timespec ts_l;
+ struct timespec ts_l32;
+ struct timespec ts_l64;
+ float f_a, f_b, f_r;
+ double d_a, d_b, d_r;
+ long long l;
+ int32_t l32; /* simulate a 32 bit long */
+ int64_t l64; /* simulate a 64 bit long */
+ const char *fail_ts = "";
+ const char *fail_l = "";
+ const char *fail_l32 = "";
+ const char *fail_l64 = "";
+ const char *fail_f = "";
+ const char *fail_d = "";
+
+ /* timespec math */
+ TS_SUB(&ts_r, &p->a, &p->b);
+
+ /* float math */
+ f_a = TSTONS( &p->a );
+ f_b = TSTONS( &p->b );
+ f_r = f_a - f_b;
+
+ /* double float math */
+ d_a = TSTONS( &p->a );
+ d_b = TSTONS( &p->b );
+ d_r = d_a - d_b;
+
+ /* long math */
+ l = timespec_diff_ns( p->a, p->b);
+ l32 = timespec_diff_ns32( p->a, p->b);
+ l64 = timespec_diff_ns64( p->a, p->b);
+
+ /* now convert to strings */
+ timespec_str( &p->a, buf_a, sizeof(buf_a) );
+ timespec_str( &p->b, buf_b, sizeof(buf_b) );
+ timespec_str( &p->c, buf_c, sizeof(buf_c) );
+ timespec_str( &ts_r, buf_r, sizeof(buf_r) );
+
+ ns_to_timespec( ts_l, l );
+ timespec_str( &ts_l, buf_l, sizeof(buf_l) );
+ ns_to_timespec( ts_l32, l32 );
+ timespec_str( &ts_l32, buf_l32, sizeof(buf_l32) );
+ ns_to_timespec( ts_l64, l64);
+ timespec_str( &ts_l64, buf_l64, sizeof(buf_l64) );
+ d_str( f_r, buf_f, sizeof(buf_f) );
+ d_str( d_r, buf_d, sizeof(buf_d) );
+
+ /* test strings */
+ if ( strcmp( buf_r, buf_c) ) {
+ fail_ts = "FAIL";
+ fail_count++;
+ }
+ if ( strcmp( buf_l, buf_c) ) {
+ fail_l = "FAIL";
+ fail_count++;
+ }
+ if ( strcmp( buf_l32, buf_c) ) {
+ fail_l32 = "FAIL";
+ fail_count++;
+ }
+ if ( strcmp( buf_l64, buf_c) ) {
+ fail_l64 = "FAIL";
+ fail_count++;
+ }
+ if ( strcmp( buf_f, buf_c) ) {
+ fail_f = "FAIL";
+ fail_count++;
+ }
+ if ( strcmp( buf_d, buf_c) ) {
+ fail_d = "FAIL";
+ fail_count++;
+ }
+ printf("ts: %21s - %21s = %21s %s\n"
+ "l; %21s - %21s = %21lld %s\n"
+ "l32; %21s - %21s = %21lld %s\n"
+ "l64; %21s - %21s = %21lld %s\n"
+ "f; %21.9f - %21.9f = %21.9f %s\n"
+ "d; %21.9f - %21.9f = %21.9f %s\n"
+ "\n",
+ buf_a, buf_b, buf_r, fail_ts,
+ buf_a, buf_b, l, fail_l,
+ buf_a, buf_b, (long long)l32, fail_l32,
+ buf_a, buf_b, (long long)l64, fail_l64,
+ f_a, f_b, f_r, fail_f,
+ d_a, d_b, d_r, fail_d);
+
+
+ if ( p->last ) {
+ break;
+ }
+ p++;
+ };
+
+ if ( fail_count ) {
+ printf("subtract test failed %d tests\n", fail_count );
+ } else {
+ puts("subtract test succeeded\n");
+ }
+ return fail_count;
+}
+
+
+/*
+ * show examples of how integers and floats fail
+ *
+ */
+static void ex_precision(void)
+{
+ format_test_t *p = format_tests;
+
+ puts( "\n\n Simple conversion examples\n\n"
+ "ts: timespec\n"
+ "l32: 32 bit long\n"
+ "l64: 64 bit long\n"
+ "f: float\n"
+ "d: double\n\n");
+
+ while ( 1 ) {
+ float f;
+ double d;
+ int32_t l32;
+ int64_t l64;
+ char buf_ts[TIMESPEC_LEN];
+ char buf_l32[TIMESPEC_LEN];
+ char buf_l64[TIMESPEC_LEN];
+ char buf_f[TIMESPEC_LEN];
+ char buf_d[TIMESPEC_LEN];
+ const char *fail_ts = "";
+ const char *fail_l32 = "";
+ const char *fail_l64 = "";
+ const char *fail_f = "";
+ const char *fail_d = "";
+
+ struct timespec *v = &(p->input);
+ struct timespec ts_l32;
+ struct timespec ts_l64;
+
+
+ /* convert to test size */
+ l32 = (int32_t)(v->tv_sec * NS_IN_SEC)+(int32_t)v->tv_nsec;
+ l64 = (int64_t)(v->tv_sec * NS_IN_SEC)+(int64_t)v->tv_nsec;
+ f = (float)TSTONS( v );
+ d = TSTONS( v );
+
+ /* now convert to strings */
+ timespec_str( v, buf_ts, sizeof(buf_ts) );
+ ns_to_timespec( ts_l32, l32);
+ timespec_str( &ts_l32, buf_l32, sizeof(buf_l32) );
+ ns_to_timespec( ts_l64, l64);
+ timespec_str( &ts_l64, buf_l64, sizeof(buf_l64) );
+ d_str( f, buf_f, sizeof(buf_f) );
+ d_str( d, buf_d, sizeof(buf_d) );
+
+ /* test strings */
+ if ( strcmp( buf_ts, p->expected) ) {
+ fail_ts = "FAIL";
+ }
+ if ( strcmp( buf_l32, p->expected) ) {
+ fail_l32 = "FAIL";
+ }
+ if ( strcmp( buf_l64, p->expected) ) {
+ fail_l64 = "FAIL";
+ }
+ if ( strcmp( buf_f, p->expected) ) {
+ fail_f = "FAIL";
+ }
+ if ( strcmp( buf_d, p->expected) ) {
+ fail_d = "FAIL";
+ }
+ printf( "ts: %21s %s\n"
+ "l32: %21lld %s\n"
+ "l64: %21lld %s\n"
+ "f: %21.9f %s\n"
+ "d: %21.9f %s\n\n",
+ buf_ts, fail_ts,
+ (long long)l32, fail_l32,
+ (long long)l64, fail_l64,
+ f, fail_f,
+ d, fail_d);
+
+ if ( p->last ) {
+ break;
+ }
+ p++;
+ }
+
+ printf( "\n\nSubtraction examples:\n");
+
+ ex_subtract_float();
+}
+
+int main(int argc, char *argv[])
+{
+ int fail_count = 0;
+ int verbose = 0;
+ int option;
+
+ while ((option = getopt(argc, argv, "h?vV")) != -1) {
+ switch (option) {
+ default:
+ fail_count = 1;
+ /* FALL THROUGH! */
+ case '?':
+ case 'h':
+ (void)fputs("usage: test_timespec [-v] [-V]\n", stderr);
+ exit(fail_count);
+ case 'V':
+ (void)fprintf( stderr, "test_timespec %s\n",
+ VERSION);
+ exit(EXIT_SUCCESS);
+ case 'v':
+ verbose = 1;
+ break;
+ }
+ }
+
+
+ fail_count = test_format( verbose );
+ fail_count += test_ts_subtract( verbose );
+ fail_count += test_ns_subtract( verbose );
+
+ if ( fail_count ) {
+ printf("timespec tests failed %d tests\n", fail_count );
+ exit(1);
+ }
+ printf("timespec tests succeeded\n");
+
+ if ( verbose ) {
+ ex_precision();
+ }
+ exit(0);
+}
diff --git a/tests/test_trig.c b/tests/test_trig.c
new file mode 100644
index 00000000..760dd0e7
--- /dev/null
+++ b/tests/test_trig.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006 Chris Kuethe <chris.kuethe@gmail.com>
+ * Copyright (c) 2009 BBN Technologies (Greg Troxel)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This program provides a way to check sin/cos.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+int test_trig(void);
+
+int main(void) {
+ test_trig();
+
+ /* For now, no evaluation. */
+ return 0;
+}
+
+#define Deg2Rad(x) ((x) * (2 * M_PI / 360.0))
+
+int test_trig(void) {
+ int i;
+ double arg;
+ double res;
+
+ for (i = 0; i <= 360; i++) {
+ arg = Deg2Rad(i);
+ res = sin(arg);
+ printf("sin(%.30f) = %.30f\n", arg, res);
+ }
+
+ for (i = 0; i <= 360; i++) {
+ arg = Deg2Rad(i);
+ res = cos(arg);
+ printf("cos(%.30f) = %.30f\n", arg, res);
+ }
+
+ /* Always claim success. */
+ return 0;
+}