From c0f0c02ac14dc565867163e92a16d44a40b7ea3f Mon Sep 17 00:00:00 2001 From: Andrey Skvortsov Date: Tue, 6 Jan 2026 00:32:59 +0300 Subject: [PATCH] tests: add tests for location-cache only GGA NMEA sentences are supported by current implementation of MMLocationGpsRaw. RMC and GLL messages are added, but currently test are negative. Signed-off-by: Andrey Skvortsov --- src/tests/meson.build | 1 + src/tests/test-location-cache.c | 453 ++++++++++++++++++++++++++++++++ 2 files changed, 454 insertions(+) create mode 100644 src/tests/test-location-cache.c diff --git a/src/tests/meson.build b/src/tests/meson.build index 18ece913..3804fe63 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -10,6 +10,7 @@ test_units = { 'charsets': libhelpers_dep, 'error-helpers': libhelpers_dep, 'kernel-device-helpers': libkerneldevice_dep, + 'location-cache': libhelpers_dep, 'modem-helpers': libhelpers_dep, 'port-scheduler': libport_dep, 'sms-part-3gpp': libhelpers_dep, diff --git a/src/tests/test-location-cache.c b/src/tests/test-location-cache.c new file mode 100644 index 00000000..fcffca0f --- /dev/null +++ b/src/tests/test-location-cache.c @@ -0,0 +1,453 @@ +/*****************************************************************************/ +/* -*- 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 Andrey Skvortsov + */ + +#include + +#include +#include +#include + +#define _LIBMM_INSIDE_MM +#include +#include "mm-log-test.h" +#include "mm-location-cache.h" + +/*****************************************************************************/ + +static const gdouble reset_lat = 0.1; +static const gdouble reset_lon = 0.2; + +typedef struct { + const gchar *file_content; + const gdouble expect_lat; + const gdouble expect_lon; + const gboolean expect_success; +} LoadTestcase; + +static const LoadTestcase load_testcases[] = { + { + .file_content = "[last_position]\n" + "latitude=48.137\n" + "longitude=11.575\n", + .expect_lat = 48.137, + .expect_lon = 11.575, + .expect_success = TRUE, + }, + { + .file_content = "[last_position]\n" + "longitude=11.575\n" + "latitude=48.137\n", + .expect_lat = 48.137, + .expect_lon = 11.575, + .expect_success = TRUE, + }, + { + .file_content = "[last_position]\n" + "latitude=-27.142\n" + "longitude=-109.314\n", + .expect_lat = -27.142, + .expect_lon = -109.314, + .expect_success = TRUE, + }, + { + .file_content = "[last_position]\n" + "latitude=48.137\n", + .expect_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .expect_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_success = FALSE, + }, + { + .file_content = "[last_position]\n" + "longitude=11.575\n", + .expect_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .expect_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_success = FALSE, + }, + { + .file_content = "[last_position]\n", + .expect_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .expect_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_success = FALSE, + }, + { + .file_content = "To be, or not to be, that is the question...\n", + .expect_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .expect_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_success = FALSE, + }, +}; + +static void +test_location_cache_load (void) +{ + g_autoptr(MMLocationCache) cache = NULL; + gdouble get_lat; + gdouble get_lon; + g_autofree gchar *filename = NULL; + g_autoptr(GFile) loc = NULL; + g_autoptr(GIOStream) stream = NULL; + g_autoptr(GError) err = NULL; + gulong i; + gboolean ret; + + cache = mm_location_cache_new (); + g_assert_nonnull (cache); + + loc = g_file_new_tmp (NULL, (GFileIOStream **)&stream, &err); + filename = g_file_get_path (loc); + g_assert_no_error (err); + g_assert_nonnull (filename); + + mm_location_cache_set_filename (cache, filename); + + for (i = 0; i < G_N_ELEMENTS (load_testcases); i++) { + g_autoptr(GError) error = NULL; + LoadTestcase test = load_testcases[i]; + + g_debug ("load test %ld", i); + g_file_replace_contents (loc, + test.file_content, + strlen (test.file_content), + NULL, /* etag */ + FALSE, /* make backup */ + G_FILE_CREATE_NONE, /* flags */ + NULL, /* new etag */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + + /* reset coordinates */ + mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon); + ret = mm_location_cache_load_from_file (cache, filename, &error); + g_assert (ret == test.expect_success); + g_assert (test.expect_success ? error == NULL : error != NULL); + + mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon); + g_assert_cmpfloat_with_epsilon (test.expect_lat, get_lat, 0.01); + g_assert_cmpfloat_with_epsilon (test.expect_lon, get_lon, 0.01); + } + + /* try to load missing file */ + g_file_delete (loc, NULL, NULL); + mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon); + ret = mm_location_cache_load_from_file (cache, filename, &err); + g_assert (err != NULL); + + mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon); + g_assert_cmpfloat_with_epsilon (MM_LOCATION_LATITUDE_UNKNOWN, get_lat, 0.01); + g_assert_cmpfloat_with_epsilon (MM_LOCATION_LONGITUDE_UNKNOWN, get_lon, 0.01); +} + +/*****************************************************************************/ + +typedef struct { + const gchar *nmea; + const gdouble set_lat; + const gdouble set_lon; + const gdouble expect_lat; + const gdouble expect_lon; +} UpdateTestcase; + +static const UpdateTestcase update_testcases[] = { + { + .nmea = NULL, + .set_lat = 48.137, + .set_lon = 11.575, + .expect_lat = 48.137, + .expect_lon = 11.575, + }, + { + .nmea = NULL, + .set_lat = 48.137, + .set_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_lat = reset_lat, + .expect_lon = reset_lon, + }, + { + .nmea = NULL, + .set_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .set_lon = 11.575, + .expect_lat = reset_lat, + .expect_lon = reset_lon, + }, + /* only GGA NMEA sentences are supported by current implementation */ + { + .nmea = "$GNGLL,4808.2200,N,01134.5000,E,012345.000,A,A*40", + .set_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .set_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_lat = reset_lat, + .expect_lon = reset_lon, + }, + { + .nmea = "$GNRMC,012345.000,A,4808.2200,N,01134.5000,E,1.0,300.5,100817,3.6,E,A,V*6B", + .set_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .set_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_lat = reset_lat, + .expect_lon = reset_lon, + }, + { + .nmea = "$GNGGA,012345.000,4808.2200,N,01134.5000,E,1,12,1.1,-14.1,M,32.0,M,,*5E", + .set_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .set_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_lat = 48.137, + .expect_lon = 11.575, + }, + { + .nmea = NULL, + .set_lat = -27.142, + .set_lon = -109.314, + .expect_lat = -27.142, + .expect_lon = -109.314, + }, + { + .nmea = "$GNGLL,2708.5200,S,10918.8400,W,012345.000,A,A*4E", + .set_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .set_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_lat = reset_lat, + .expect_lon = reset_lon, + }, + { + .nmea = "$GNRMC,012345.000,A,2708.5200,S,10918.8400,W,1.0,300.5,100817,3.6,E,A,V*65", + .set_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .set_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_lat = reset_lat, + .expect_lon = reset_lon, + }, + { + .nmea = "$GNGGA,012345.000,2708.5200,S,10918.8400,W,1,12,1.1,-14.1,M,32.0,M,,*50", + .set_lat = MM_LOCATION_LATITUDE_UNKNOWN, + .set_lon = MM_LOCATION_LONGITUDE_UNKNOWN, + .expect_lat = -27.142, + .expect_lon = -109.314, + }, +}; + +static void +test_location_cache_update (void) +{ + g_autoptr(MMLocationCache) cache = NULL; + gulong i; + + cache = mm_location_cache_new (); + g_assert_nonnull (cache); + + for (i = 0; i < G_N_ELEMENTS (update_testcases); i++) { + gdouble get_lat = 0; + gdouble get_lon = 0; + UpdateTestcase test = update_testcases[i]; + + g_debug ("update test %ld", i); + + /* reset coordinates */ + mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon); + + if (test.nmea) + mm_location_cache_update_from_nmea (cache, test.nmea); + else + mm_location_cache_update_from_lat_lon (cache, test.set_lat, test.set_lon); + mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon); + g_assert_cmpfloat_with_epsilon (test.expect_lat, get_lat, 0.01); + g_assert_cmpfloat_with_epsilon (test.expect_lon, get_lon, 0.01); + } +} + +/*****************************************************************************/ + +static gchar * +get_temp_filename (void) +{ + g_autoptr(GError) error = NULL; + gchar *filename; + gint fd; + + fd = g_file_open_tmp (NULL, &filename, &error); + g_assert_no_error (error); + g_assert_nonnull (filename); + + g_close (fd, &error); + g_assert_no_error (error); + + g_unlink (filename); + + return filename; +} + +static void +test_location_cache_save (void) +{ + g_autoptr(MMLocationCache) cache = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *filename = NULL; + g_autofree gchar *path = NULL; + gboolean ret; + + cache = mm_location_cache_new (); + g_assert_nonnull (cache); + + filename = get_temp_filename (); + + // write empty cache + g_unlink (filename); + ret = mm_location_cache_save_to_file (cache, filename, &error); + g_assert_true (ret); + g_assert_no_error (error); + + ret = g_file_test (filename, G_FILE_TEST_EXISTS); + g_assert_false (ret); + + // write non-empty cache + g_unlink (filename); + mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon); + ret = mm_location_cache_save_to_file (cache, filename, &error); + g_assert_true (ret); + g_assert_no_error (error); + + ret = g_file_test (filename, G_FILE_TEST_EXISTS); + g_assert_true (ret); + g_unlink (filename); + + // write to non-existing path + path = g_build_path (G_DIR_SEPARATOR_S, "missing", "X", "Y", "Z", "location.ini", NULL); + ret = mm_location_cache_save_to_file (cache, path, &error); + g_assert_false (ret); + g_assert (error != NULL); +} + +/*****************************************************************************/ + +typedef struct { + const gdouble set_lat; + const gdouble set_lon; +} SaveLoadTestcase; + +static const SaveLoadTestcase save_load_testcases[] = { + { + .set_lat = 48.137, + .set_lon = 11.575, + }, + { + .set_lat = -27.142, + .set_lon = -109.314, + }, +}; + +static void +test_location_cache_save_load (void) +{ + g_autoptr(MMLocationCache) cache = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *filename = NULL; + gboolean ret; + gulong i; + + cache = mm_location_cache_new (); + g_assert_nonnull (cache); + + filename = get_temp_filename (); + mm_location_cache_set_filename (cache, filename); + + for (i = 0; i < G_N_ELEMENTS (save_load_testcases); i++) { + gdouble get_lat = 0; + gdouble get_lon = 0; + SaveLoadTestcase test = save_load_testcases[i]; + + g_debug ("save/load test %ld", i); + + mm_location_cache_update_from_lat_lon (cache, test.set_lat, test.set_lon); + g_unlink (filename); + ret = mm_location_cache_save (cache, &error); + g_assert_true (ret); + g_assert_no_error (error); + + ret = g_file_test (filename, G_FILE_TEST_EXISTS); + g_assert_true (ret); + + mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon); + ret = mm_location_cache_load (cache, &error); + g_assert_true (ret); + g_assert_no_error (error); + + mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon); + g_assert_cmpfloat_with_epsilon (test.set_lat, get_lat, 0.01); + g_assert_cmpfloat_with_epsilon (test.set_lon, get_lon, 0.01); + } + + g_unlink (filename); +} + +/*****************************************************************************/ + +static void +test_location_cache_create_dispose (void) +{ + MMLocationCache *cache = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *filename = NULL; + gboolean ret; + gdouble get_lat = 0; + gdouble get_lon = 0; + + cache = mm_location_cache_new (); + g_assert_nonnull (cache); + + // to check later, that MMLocationCache object is finalized + g_object_add_weak_pointer (G_OBJECT (cache), (gpointer *) &cache); + + filename = get_temp_filename (); + mm_location_cache_set_filename (cache, filename); + mm_location_cache_update_from_lat_lon (cache, reset_lat, reset_lon); + + // drop the last reference and save cached location + g_object_unref (cache); + g_assert_null (cache); + + // check whether file is created + ret = g_file_test (filename, G_FILE_TEST_EXISTS); + g_assert_true (ret); + + // check file content + cache = mm_location_cache_new (); + g_assert_nonnull (cache); + + mm_location_cache_set_filename (cache, filename); + ret = mm_location_cache_load (cache, &error); + g_assert_true (ret); + g_assert_no_error (error); + + mm_location_cache_get_lat_lon (cache, &get_lat, &get_lon); + g_assert_cmpfloat_with_epsilon (reset_lat, get_lat, 0.01); + g_assert_cmpfloat_with_epsilon (reset_lon, get_lon, 0.01); + + g_object_unref (cache); + g_unlink (filename); +} + +/*****************************************************************************/ + +int main (int argc, char **argv) +{ + setlocale (LC_ALL, ""); + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/MM/location-cache/update", test_location_cache_update); + g_test_add_func ("/MM/location-cache/load", test_location_cache_load); + g_test_add_func ("/MM/location-cache/save", test_location_cache_save); + g_test_add_func ("/MM/location-cache/save_load", test_location_cache_save_load); + g_test_add_func ("/MM/location-cache/create_dispose", test_location_cache_create_dispose); + + return g_test_run (); +}