From 38548fedd3e2e6509acf699845a91edb9d0d5935 Mon Sep 17 00:00:00 2001 From: Andrey Skvortsov Date: Sat, 10 Jan 2026 23:44:43 +0300 Subject: [PATCH 1/4] location-gps-nmea: use g_autofree in location_gps_nmea_take_trace to track ownership It allows simplify error handling. Signed-off-by: Andrey Skvortsov --- libmm-glib/mm-location-gps-nmea.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/libmm-glib/mm-location-gps-nmea.c b/libmm-glib/mm-location-gps-nmea.c index b2ef6951..ab693fa9 100644 --- a/libmm-glib/mm-location-gps-nmea.c +++ b/libmm-glib/mm-location-gps-nmea.c @@ -76,16 +76,15 @@ check_append_or_replace (MMLocationGpsNmea *self, static gboolean location_gps_nmea_take_trace (MMLocationGpsNmea *self, - gchar *trace) + gchar *_trace) { - gchar *i; - gchar *trace_type; + gchar *i; + g_autofree gchar *trace_type = NULL; + g_autofree gchar *trace = _trace; i = strchr (trace, ','); - if (!i || i == trace) { - g_free (trace); + if (!i || i == trace) return FALSE; - } trace_type = g_malloc (i - trace + 1); memcpy (trace_type, trace, i - trace); @@ -100,27 +99,24 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self, previous = g_hash_table_lookup (self->priv->traces, trace_type); if (previous) { - gchar *sequence; + g_autofree gchar *sequence = NULL; /* Skip the trace if we already have it there */ - if (strstr (previous, trace)) { - g_free (trace_type); - g_free (trace); + if (strstr (previous, trace)) return TRUE; - } sequence = g_strdup_printf ("%s%s%s", previous, g_str_has_suffix (previous, "\r\n") ? "" : "\r\n", trace); g_free (trace); - trace = sequence; + trace = g_steal_pointer (&sequence); } } g_hash_table_replace (self->priv->traces, - trace_type, - trace); + g_steal_pointer (&trace_type), + g_steal_pointer (&trace)); return TRUE; } From f7da97989bcd85ec2c00d3621c47671deed17272 Mon Sep 17 00:00:00 2001 From: Andrey Skvortsov Date: Sun, 11 Jan 2026 00:17:53 +0300 Subject: [PATCH 2/4] location-gps-nmea: add complete sequence to traces at once Currently sequence messages are added to traces as they are received. Therefore content of traces depends at the time, when request is issued and data is reported. Sometimes not all sequence traces are reported, because only first record was received yet and it overwrote previous full sequence trace. For example, ``` $GPGSV,3,1,12,01,35,282,,02,68,264,,03,02,243,,08,35,206,,1*68 $GPGSV,3,2,12,10,46,074,,14,16,312,,17,,,,22,16,327,,1*53 $GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60 $GLGSV,2,1,08,82,25,135,,76,11,029,,75,21,334,,69,11,201,,1*7A ``` one GSV sentence is missing and second later full sequence is reported ``` $GPGSV,3,1,12,01,35,282,,02,68,264,,03,02,243,,08,35,206,,1*68 $GPGSV,3,2,12,10,46,074,,14,16,312,,17,,,,22,16,327,,1*53 $GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60 $GLGSV,2,1,08,82,25,135,,76,11,029,,75,21,334,,69,11,201,,1*7A $GLGSV,2,2,08,84,40,306,,83,79,161,,67,56,056,,68,58,177,,1*7D ``` Therefore client software reports different number of visible satellites. Usually sequence sentences are reported consequently in NMEA stream. Let's cache and join sequence sentences until new trace type is received. In that case full sequence always overrides previous full sequence. Signed-off-by: Andrey Skvortsov --- libmm-glib/mm-location-gps-nmea.c | 74 +++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/libmm-glib/mm-location-gps-nmea.c b/libmm-glib/mm-location-gps-nmea.c index ab693fa9..2cad8dae 100644 --- a/libmm-glib/mm-location-gps-nmea.c +++ b/libmm-glib/mm-location-gps-nmea.c @@ -45,16 +45,20 @@ G_DEFINE_TYPE (MMLocationGpsNmea, mm_location_gps_nmea, G_TYPE_OBJECT) struct _MMLocationGpsNmeaPrivate { GHashTable *traces; - GRegex *sequence_regex; + gchar *sequence_last_trace; + gchar *sequence_last_trace_type; + GRegex *sequence_regex; }; /*****************************************************************************/ static gboolean -check_append_or_replace (MMLocationGpsNmea *self, - const gchar *trace) +is_sequence_trace_type (MMLocationGpsNmea *self, + const gchar *trace, + gboolean *first) { g_autoptr(GMatchInfo) match_info = NULL; + guint index; if (G_UNLIKELY (!self->priv->sequence_regex)) self->priv->sequence_regex = g_regex_new ("\\$..(?:ALM|GSV|RTE|SFI),(\\d),(\\d).*", @@ -62,15 +66,13 @@ check_append_or_replace (MMLocationGpsNmea *self, 0, NULL); + *first = FALSE; if (g_regex_match (self->priv->sequence_regex, trace, 0, &match_info)) { - guint index; - - /* If we don't have the first element of a sequence, append */ - if (mm_get_uint_from_match_info (match_info, 2, &index) && index != 1) + if (mm_get_uint_from_match_info (match_info, 2, &index)) { + *first = (index == 1); return TRUE; + } } - - /* By default, replace */ return FALSE; } @@ -81,6 +83,10 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self, gchar *i; g_autofree gchar *trace_type = NULL; g_autofree gchar *trace = _trace; + g_autofree gchar *sequence = NULL; + gboolean first; + gboolean sequence_trace; + i = strchr (trace, ','); if (!i || i == trace) @@ -93,14 +99,38 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self, /* Some traces are part of a SEQUENCE; so we need to decide whether we * completely replace the previous trace, or we append the new one to * the already existing list */ - if (check_append_or_replace (self, trace)) { + sequence_trace = is_sequence_trace_type (self, trace, &first); + + /* Save previous sequence trace, if + * new trace type is different or this is the first sequence trace */ + if (self->priv->sequence_last_trace_type && + self->priv->sequence_last_trace && + (g_strcmp0 (self->priv->sequence_last_trace_type, trace_type) || + (sequence_trace && first))) { + g_hash_table_replace (self->priv->traces, + g_steal_pointer (&self->priv->sequence_last_trace_type), + g_steal_pointer (&self->priv->sequence_last_trace)); + } + + if (!sequence_trace) { + g_hash_table_replace (self->priv->traces, + g_steal_pointer (&trace_type), + g_steal_pointer (&trace)); + return TRUE; + } + + /* Process the trace as part of a sequence. */ + if (first) { + /* Record first record in the sequence */ + sequence = g_steal_pointer (&trace); + } else { /* Append */ - const gchar *previous; + const gchar *previous = self->priv->sequence_last_trace; + + if (!previous) + previous = g_hash_table_lookup (self->priv->traces, trace_type); - previous = g_hash_table_lookup (self->priv->traces, trace_type); if (previous) { - g_autofree gchar *sequence = NULL; - /* Skip the trace if we already have it there */ if (strstr (previous, trace)) return TRUE; @@ -109,14 +139,18 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self, previous, g_str_has_suffix (previous, "\r\n") ? "" : "\r\n", trace); - g_free (trace); - trace = g_steal_pointer (&sequence); + } else { + /* No previous records, record first record in the sequence */ + sequence = g_steal_pointer (&trace); } } - g_hash_table_replace (self->priv->traces, - g_steal_pointer (&trace_type), - g_steal_pointer (&trace)); + g_clear_pointer (&self->priv->sequence_last_trace, g_free); + self->priv->sequence_last_trace = g_steal_pointer (&sequence); + + g_clear_pointer (&self->priv->sequence_last_trace_type, g_free); + self->priv->sequence_last_trace_type = g_steal_pointer (&trace_type); + return TRUE; } @@ -287,6 +321,8 @@ finalize (GObject *object) g_hash_table_destroy (self->priv->traces); if (self->priv->sequence_regex) g_regex_unref (self->priv->sequence_regex); + g_free (self->priv->sequence_last_trace); + g_free (self->priv->sequence_last_trace_type); G_OBJECT_CLASS (mm_location_gps_nmea_parent_class)->finalize (object); } From c0d94970669f3855438b7db11551b563034f197a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 24 May 2026 10:35:01 -0500 Subject: [PATCH 3/4] libmm-glib: add tests for MMLocationGpsNmea Signed-off-by: Dan Williams --- libmm-glib/tests/meson.build | 1 + libmm-glib/tests/test-location-gps-nmea.c | 188 ++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 libmm-glib/tests/test-location-gps-nmea.c diff --git a/libmm-glib/tests/meson.build b/libmm-glib/tests/meson.build index 83dd80d1..afde8448 100644 --- a/libmm-glib/tests/meson.build +++ b/libmm-glib/tests/meson.build @@ -4,6 +4,7 @@ test_units = [ 'common-helpers', 'pco', + 'location-gps-nmea', ] foreach test_unit: test_units diff --git a/libmm-glib/tests/test-location-gps-nmea.c b/libmm-glib/tests/test-location-gps-nmea.c new file mode 100644 index 00000000..c1590721 --- /dev/null +++ b/libmm-glib/tests/test-location-gps-nmea.c @@ -0,0 +1,188 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2026 Dan Williams + */ + +#include +#include +#include + +static gchar * +combine_traces (const gchar *traces[], + const guint num_traces, + gboolean trailing_crlf) +{ + guint i; + GString *all = g_string_new (""); + + for (i = 0; i < num_traces; i++) { + g_string_append (all, traces[i]); + if (i != num_traces - 1) { + g_string_append_c (all, '\r'); + g_string_append_c (all, '\n'); + } + } + + return g_string_free (all, FALSE); +} + +static gchar * +get_trace_type (const gchar *trace) +{ + gchar *trace_type = g_strdup (trace); + gchar *p; + + p = strchr (trace_type, ','); + g_assert (p); + *p = '\0'; + return trace_type; +} + +/**************************************************************/ + +static void +test_round_trip (void) +{ + MMLocationGpsNmea *nmea; + g_autofree gchar *combined_traces; + g_autofree gchar **all_traces; + GError *error = NULL; + guint traces_len; + guint i, j; + + #define TRACE0 "$GNGGA,001626.000,,,,,0,00,25.5,,,,,,*79" + #define TRACE1 "$GPGSV,2,1,08,04,41,189,,07,49,311,28,08,67,149,25,09,55,237,18,0*68" + #define TRACE2 "$GPGSV,2,2,08,16,31,057,,21,05,153,,27,56,070,,30,19,303,,0*61" + #define TRACE3 "$BDGSV,1,1,00,0*74" + #define TRACE4 "$GNGGA,001626.000,,,,,0,00,25.5,,,,,,*79" + + static const char *traces[] = { TRACE0, TRACE1, TRACE2, TRACE3, TRACE4 }; + + static const char *expected_traces[] = { + TRACE1 "\r\n" TRACE2, + TRACE3, + TRACE4, + }; + + combined_traces = combine_traces (traces, G_N_ELEMENTS (traces), TRUE); + nmea = mm_location_gps_nmea_new_from_string_variant (g_variant_new_string (combined_traces), &error); + g_assert_no_error (error); + + for (i = 0; i < G_N_ELEMENTS (expected_traces); i++) { + g_autofree gchar *trace_type = get_trace_type (expected_traces[i]); + const gchar * trace; + + trace = mm_location_gps_nmea_get_trace (nmea, trace_type); + g_assert_cmpstr (trace, ==, expected_traces[i]); + } + + all_traces = mm_location_gps_nmea_get_traces (nmea); + traces_len = g_strv_length (all_traces); + g_assert_cmpint (traces_len, ==, G_N_ELEMENTS (expected_traces)); + + /* For each expected trace type, assert that trace type exists in the + * found traces, and matches the expected trace. + */ + for (i = 0; i < G_N_ELEMENTS (expected_traces); i++) { + g_autofree gchar *expected_type = get_trace_type (expected_traces[i]); + gboolean found = FALSE; + + for (j = 0; j < traces_len; j++) { + g_autofree gchar *found_type = get_trace_type (all_traces[j]); + + if (!g_strcmp0 (found_type, expected_type)) { + /* Found the trace type; compare */ + g_assert_cmpstr (all_traces[j], ==, expected_traces[i]); + found = TRUE; + } + } + g_assert_true (found); + } +} + +#undef TRACE0 +#undef TRACE1 +#undef TRACE2 +#undef TRACE3 +#undef TRACE4 + +/**************************************************************/ + +static void +test_append_sequence (void) +{ + MMLocationGpsNmea *nmea; + GError *error = NULL; + g_autofree gchar *combined = NULL; + + #define TRACE0 "$GNGGA,001626.000,,,,,0,00,25.5,,,,,,*79" + #define TRACE1 "$GPGSV,3,1,12,01,35,282,,02,68,264,,03,02,243,,08,35,206,,1*68" + #define TRACE2 "$GPGSV,3,2,12,10,46,074,,14,16,312,,17,,,,22,16,327,,1*53" + #define TRACE3 "$GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60" + #define TRACE4 "$BDGSV,1,1,00,0*74" + #define TRACE5 "$GNGGA,001626.000,,,,,0,00,25.6,,,,,,*79" + + #define GPGSV_TYPE "$GPGSV" + #define BDGSV_TYPE "$BDGSV" + #define GNGGA_TYPE "$GNGGA" + + static const gchar *gpgsvs[] = { TRACE1, TRACE2, TRACE3 }; + + nmea = mm_location_gps_nmea_new_from_string_variant (g_variant_new_string (TRACE0), &error); + g_assert_no_error (error); + + /* No part of the GPGSV sequence should be found in traces until a new + * trace type is added. + */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE1)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE2)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE3)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + + /* Add a new type, but it's also a sequence so it won't be found yet */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE4)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, BDGSV_TYPE), ==, NULL); + + /* New type was just added, now we should find the $GPGSV too */ + combined = combine_traces (gpgsvs, G_N_ELEMENTS (gpgsvs), FALSE); + g_assert (combined); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined); + + /* Add a non-sequence trace */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE5)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GNGGA_TYPE), ==, TRACE5); + + /* And now we should find the $BDGSV */ + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, BDGSV_TYPE), ==, TRACE4); +} + +#undef TRACE0 +#undef TRACE1 +#undef TRACE2 +#undef TRACE3 +#undef TRACE4 +#undef TRACE5 + +/**************************************************************/ + +int main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/MM/Location/GPS/NMEA/round-trip", test_round_trip); + g_test_add_func ("/MM/Location/GPS/NMEA/append-sequence", test_append_sequence); + + return g_test_run (); +} From 13a3ee81a3f0deb1d2a46845cfa278c9e4bdf67a Mon Sep 17 00:00:00 2001 From: Andrey Skvortsov Date: Tue, 26 May 2026 23:53:24 +0300 Subject: [PATCH 4/4] libmm-glib: add more tests for MMLocationGpsNmea Extra tests cover various corner cases. Signed-off-by: Andrey Skvortsov --- libmm-glib/tests/test-location-gps-nmea.c | 188 ++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/libmm-glib/tests/test-location-gps-nmea.c b/libmm-glib/tests/test-location-gps-nmea.c index c1590721..3cbad06d 100644 --- a/libmm-glib/tests/test-location-gps-nmea.c +++ b/libmm-glib/tests/test-location-gps-nmea.c @@ -11,6 +11,7 @@ * GNU General Public License for more details: * * Copyright (C) 2026 Dan Williams + * Copyright (C) 2026 Andrey Skvortsov */ #include @@ -177,12 +178,199 @@ test_append_sequence (void) /**************************************************************/ +static void +test_append_sequence_interleaved (void) +{ + MMLocationGpsNmea *nmea; + GError *error = NULL; + g_autofree gchar *combined_start = NULL; + g_autofree gchar *combined_full = NULL; + + #define TRACE0 "$GNGGA,001626.000,,,,,0,00,25.5,,,,,,*79" + #define TRACE1 "$GPGSV,3,1,12,01,35,282,,02,68,264,,03,02,243,,08,35,206,,1*68" + #define TRACE2 "$GPGSV,3,2,12,10,46,074,,14,16,312,,17,,,,22,16,327,,1*53" + #define TRACE3 "$BDGSV,1,1,00,0*74" + #define TRACE4 "$GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60" + #define TRACE5 "$GNGGA,001626.000,,,,,0,00,25.6,,,,,,*79" + + #define GPGSV_TYPE "$GPGSV" + #define BDGSV_TYPE "$BDGSV" + #define GNGGA_TYPE "$GNGGA" + + static const gchar *gpgsvs_start[] = { TRACE1, TRACE2 }; + static const gchar *gpgsvs_full[] = { TRACE1, TRACE2, TRACE4 }; + + nmea = mm_location_gps_nmea_new_from_string_variant (g_variant_new_string (TRACE0), &error); + g_assert_no_error (error); + + /* No part of the GPGSV sequence should be found in traces until a new + * trace type is added. + */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE1)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE2)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + + /* Add a new type, but it's also a sequence so it won't be found yet */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE3)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, BDGSV_TYPE), ==, NULL); + + /* New type was just added, now first part of $GPGSV should be available */ + combined_start = combine_traces (gpgsvs_start, G_N_ELEMENTS (gpgsvs_start), FALSE); + g_assert (combined_start); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined_start); + + /* Add third GPGSV, but it won't be found yet, only previous GPGSV records + * But $BDGSV should be found now. + */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE4)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined_start); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, BDGSV_TYPE), ==, TRACE3); + + /* Add a non-sequence trace */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE5)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GNGGA_TYPE), ==, TRACE5); + + /* New type was just added, now we should find full $GPGSV sequence */ + combined_full = combine_traces (gpgsvs_full, G_N_ELEMENTS (gpgsvs_full), FALSE); + g_assert (combined_full); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined_full); + +} + +#undef TRACE0 +#undef TRACE1 +#undef TRACE2 +#undef TRACE3 +#undef TRACE4 +#undef TRACE5 + +/**************************************************************/ + +static void +test_append_multiple_sequences (void) +{ + MMLocationGpsNmea *nmea; + GError *error = NULL; + g_autofree gchar *combined1 = NULL; + g_autofree gchar *combined2 = NULL; + + #define TRACE0 "$GPGSV,3,1,12,01,35,282,,02,68,264,,03,02,243,,08,35,206,,1*68" + #define TRACE1 "$GPGSV,3,2,12,10,46,074,,14,16,312,,17,,,,22,16,327,,1*53" + #define TRACE2 "$GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60" + #define TRACE3 "$GPGSV,3,1,12,01,35,282,23,02,68,264,,03,02,243,,08,35,206,,1*69" + #define TRACE4 "$GPGSV,3,2,12,10,46,074,28,14,16,312,,17,,,,22,16,327,,1*59" + #define TRACE5 "$GPGSV,3,3,12,23,07,068,18,24,15,030,,27,14,181,,32,51,120,,1*69" + #define TRACE6 "$GPGSV,3,1,12,01,35,282,25,02,68,264,,03,02,243,,08,35,206,,1*6F" + + #define GPGSV_TYPE "$GPGSV" + + static const gchar *gpgsvs1[] = { TRACE0, TRACE1, TRACE2 }; + static const gchar *gpgsvs2[] = { TRACE3, TRACE4, TRACE5 }; + + /* No part of the GPGSV sequence should be found in traces until a new + * trace sequence is added. + */ + nmea = mm_location_gps_nmea_new_from_string_variant (g_variant_new_string (TRACE0), &error); + g_assert_no_error (error); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE1)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE2)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + + /* Add first record for next sequence, old sequence should be available */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE3)); + combined1 = combine_traces (gpgsvs1, G_N_ELEMENTS (gpgsvs1), FALSE); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined1); + + /* No part of new the GPGSV sequence should be found in traces until a new + * trace sequence is added. + */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE4)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined1); + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE5)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined1); + + /* Add first record for next sequence, updated sequence should be available */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE6)); + combined2 = combine_traces (gpgsvs2, G_N_ELEMENTS (gpgsvs2), FALSE); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined2); +} + +#undef TRACE0 +#undef TRACE1 +#undef TRACE2 +#undef TRACE3 +#undef TRACE4 +#undef TRACE5 +#undef TRACE6 + +/**************************************************************/ + +static void +test_start_from_incomplete_sequence (void) +{ + MMLocationGpsNmea *nmea; + GError *error = NULL; + g_autofree gchar *combined = NULL; + + #define TRACE0 "$GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60" + #define TRACE1 "$GNGGA,001626.000,,,,,0,00,25.5,,,,,,*79" + #define TRACE2 "$GPGSV,3,1,12,01,35,282,,02,68,264,,03,02,243,,08,35,206,,1*68" + #define TRACE3 "$GPGSV,3,2,12,10,46,074,,14,16,312,,17,,,,22,16,327,,1*53" + #define TRACE4 "$GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60" + #define TRACE5 "$GNGGA,001626.000,,,,,0,00,25.6,,,,,,*7A" + + #define GPGSV_TYPE "$GPGSV" + #define GNGGA_TYPE "$GNGGA" + + static const gchar *gpgsvs[] = { TRACE2, TRACE3, TRACE4 }; + + /* Start sequence not from the first record */ + nmea = mm_location_gps_nmea_new_from_string_variant (g_variant_new_string (TRACE0), &error); + g_assert_no_error (error); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, NULL); + + /* New type was just added, now we should find $GPGSV sequence */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE1)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GNGGA_TYPE), ==, TRACE1); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, TRACE0); + + /* Add first record for next sequence, old sequence should be available */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE2)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, TRACE0); + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE3)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, TRACE0); + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE4)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, TRACE0); + + /* New type was just added, now we should find new full $GPGSV sequence */ + g_assert (mm_location_gps_nmea_add_trace (nmea, TRACE5)); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GNGGA_TYPE), ==, TRACE5); + + combined = combine_traces (gpgsvs, G_N_ELEMENTS (gpgsvs), FALSE); + g_assert_cmpstr (mm_location_gps_nmea_get_trace (nmea, GPGSV_TYPE), ==, combined); +} + +#undef TRACE0 +#undef TRACE1 +#undef TRACE2 +#undef TRACE3 +#undef TRACE4 +#undef TRACE5 + +/**************************************************************/ + int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/Location/GPS/NMEA/round-trip", test_round_trip); g_test_add_func ("/MM/Location/GPS/NMEA/append-sequence", test_append_sequence); + g_test_add_func ("/MM/Location/GPS/NMEA/append-sequence-interleaved", test_append_sequence_interleaved); + g_test_add_func ("/MM/Location/GPS/NMEA/append-multiple-sequences", test_append_multiple_sequences); + g_test_add_func ("/MM/Location/GPS/NMEA/start-from-incomplete-sequence", test_start_from_incomplete_sequence); return g_test_run (); }