diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_bits.c | 233 | ||||
-rw-r--r-- | tests/test_float.c | 305 | ||||
-rw-r--r-- | tests/test_geoid.c | 39 | ||||
-rw-r--r-- | tests/test_gpsmm.cpp | 173 | ||||
-rw-r--r-- | tests/test_json.c | 623 | ||||
-rw-r--r-- | tests/test_libgps.c | 153 | ||||
-rw-r--r-- | tests/test_matrix.c | 88 | ||||
-rw-r--r-- | tests/test_mktime.c | 177 | ||||
-rwxr-xr-x | tests/test_nmea2000 | 117 | ||||
-rw-r--r-- | tests/test_packet.c | 409 | ||||
-rw-r--r-- | tests/test_timespec.c | 557 | ||||
-rw-r--r-- | tests/test_trig.c | 55 |
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; +} |