aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Martinsons <frederic.martinsons@sigfox.com>2020-11-12 10:48:11 +0100
committerFrédéric Martinsons <frederic.martinsons@sigfox.com>2021-01-15 16:44:19 +0000
commit89cd1696c3a105f314b1a7e275c68377ed6df5e4 (patch)
tree0e9e2f127a117ff24d09d17cccaff6f7fa74729d
parent17ea1dfa130001599b7dff421a1b7d71c25faa34 (diff)
tools: add unit test for test-modemmanager-service.py
The tests spawn the service via GTestDBus framework and make some test API call while checking libmm interface to verify that informations are well propagated. To be able to use the fresh built libmm typelib, I used a wrapper script to set GI_TYPELIB_PATH (because DBus activation process clean the environment so it is not possible to set it directly in the file). This requires also the install of libgirepository-dev and python3-gi in the CI docker. Signed-off-by: Frederic Martinsons <frederic.martinsons@sigfox.com>
-rw-r--r--.gitignore4
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--configure.ac2
-rw-r--r--tools/Makefile.am2
-rw-r--r--tools/tests/Makefile.am46
-rw-r--r--tools/tests/services/org.freedesktop.ModemManager1.service.in3
-rw-r--r--tools/tests/test-stub.c459
-rw-r--r--tools/tests/test-wrapper.sh.in5
8 files changed, 523 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index ea7705af..4a0e5fd7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -175,5 +175,9 @@ Makefile.in
/test/mmsmspdu
/test/mmsmsmonitor
+/tools/tests/services/org.freedesktop.ModemManager1.service
+/tools/tests/test-stub
+/tools/tests/test-wrapper.sh
+
/ModemManager-*-coverage.info
/ModemManager-*-coverage/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e429af6c..a8371f75 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,7 +5,7 @@ stages:
before_script:
- apt update || true
- - apt -y install autoconf automake libtool libglib2.0-dev libgudev-1.0-dev libgettextpo-dev autopoint xsltproc dbus autoconf-archive gettext
+ - apt -y install autoconf automake libtool libgettextpo-dev libgirepository1.0-dev libglib2.0-dev libgudev-1.0-dev python3-dbus python3-gi autopoint xsltproc dbus autoconf-archive gettext
build-no-qmi:
stage: build
@@ -125,7 +125,7 @@ build-default:
- schedules
- pushes
script:
- - apt -y install gtk-doc-tools libglib2.0-doc gobject-introspection libgirepository1.0-dev libsystemd-dev libpolkit-gobject-1-dev valac
+ - apt -y install gtk-doc-tools libglib2.0-doc gobject-introspection libsystemd-dev libpolkit-gobject-1-dev valac
- git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libmbim.git
- pushd libmbim
- NOCONFIGURE=1 ./autogen.sh
diff --git a/configure.ac b/configure.ac
index c96e4b76..8c2711f2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -542,6 +542,8 @@ src/tests/Makefile
plugins/Makefile
test/Makefile
tools/Makefile
+tools/tests/Makefile
+tools/tests/services/org.freedesktop.ModemManager1.service
introspection/Makefile
introspection/tests/Makefile
po/Makefile.in
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 8b1edd5f..af87c73b 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1 +1,3 @@
+SUBDIRS = . tests
+
EXTRA_DIST = test-modemmanager-service.py
diff --git a/tools/tests/Makefile.am b/tools/tests/Makefile.am
new file mode 100644
index 00000000..c91675bf
--- /dev/null
+++ b/tools/tests/Makefile.am
@@ -0,0 +1,46 @@
+include $(top_srcdir)/gtester.make
+
+################################################################################
+# common
+################################################################################
+
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(MM_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ -I$(top_srcdir)/libmm-glib \
+ -I${top_srcdir}/libmm-glib/generated \
+ -I${top_builddir}/libmm-glib/generated \
+ $(NULL)
+
+LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(MM_LIBS) \
+ $(MM_LDFLAGS) \
+ $(NULL)
+
+noinst_PROGRAMS = test-stub
+test_stub_CPPFLAGS = \
+ -DTEST_SERVICES=\""$(abs_top_builddir)/tools/tests/services"\" \
+ $(NULL)
+
+TEST_PROGS += $(noinst_PROGRAMS)
+
+test-wrapper.sh: test-wrapper.sh.in
+ @sed \
+ -e 's|@abs_top_builddir[@]|$(abs_top_builddir)|g' \
+ -e 's|@abs_top_srcdir[@]|$(abs_top_srcdir)|g' \
+ $< >$@
+ @chmod +x $@
+
+BUILT_SOURCES = test-wrapper.sh
+CLEANFILES = test-wrapper.sh
+
+EXTRA_DIST += test-wrapper.sh.in services/org.freedesktop.ModemManager1.service.in
diff --git a/tools/tests/services/org.freedesktop.ModemManager1.service.in b/tools/tests/services/org.freedesktop.ModemManager1.service.in
new file mode 100644
index 00000000..f6113d1f
--- /dev/null
+++ b/tools/tests/services/org.freedesktop.ModemManager1.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.ModemManager1
+Exec=@abs_top_builddir@/tools/tests/test-wrapper.sh
diff --git a/tools/tests/test-stub.c b/tools/tests/test-stub.c
new file mode 100644
index 00000000..5687feca
--- /dev/null
+++ b/tools/tests/test-stub.c
@@ -0,0 +1,459 @@
+/* -*- 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) 2020 Frederic Martinsons <frederic.martinsons@sigfox.com>
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <libmm-glib.h>
+#include <ModemManager-names.h>
+#define MM_LOG_NO_OBJECT
+#include <mm-log-test.h>
+
+#define MM_TEST_IFACE_NAME "org.freedesktop.ModemManager1.LibmmGlibTest"
+
+typedef struct {
+ GMainLoop *loop;
+ MMManager *mm_manager;
+ GDBusConnection *gdbus_connection;
+ GDBusProxy *mm_proxy;
+ GDBusProxy *mm_modem_prop_proxy;
+ GTestDBus *test_bus;
+ gchar *modem_object_path;
+ guint timeout_id;
+ MMSim *sim;
+ gboolean pin_error;
+} TestData;
+
+static void
+setup (TestData **ptdata,
+ gconstpointer data)
+{
+ GError *error = NULL;
+ TestData *tdata = NULL;
+
+ tdata = (TestData *)g_malloc0 (sizeof(TestData));
+ *ptdata = tdata;
+
+ tdata->loop = g_main_loop_new (NULL, FALSE);
+ g_assert_nonnull (tdata->loop);
+
+ tdata->test_bus = g_test_dbus_new (G_TEST_DBUS_NONE);
+ g_assert_nonnull (tdata->test_bus);
+
+ g_test_dbus_add_service_dir (tdata->test_bus, TEST_SERVICES);
+ g_test_dbus_up (tdata->test_bus);
+
+ /* Grab a proxy to the fake NM service to trigger tests */
+ tdata->mm_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ MM_DBUS_SERVICE,
+ MM_DBUS_PATH,
+ MM_TEST_IFACE_NAME,
+ NULL, &error);
+ g_assert_no_error (error);
+
+ tdata->gdbus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+
+ tdata->mm_manager = mm_manager_new_sync (tdata->gdbus_connection,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (tdata->mm_manager);
+}
+
+static void
+teardown (TestData **ptdata,
+ gconstpointer data)
+{
+ TestData *tdata = NULL;
+
+ tdata = *ptdata;
+ g_clear_object (&tdata->mm_modem_prop_proxy);
+ g_clear_object (&tdata->mm_proxy);
+ g_clear_object (&tdata->mm_manager);
+ g_clear_object (&tdata->gdbus_connection);
+ g_test_dbus_down (tdata->test_bus);
+ g_clear_object (&tdata->test_bus);
+ g_main_loop_unref (tdata->loop);
+ g_free (tdata);
+}
+
+static gboolean
+loop_timeout_cb (gpointer user_data)
+{
+ mm_err ("Timeout has elapsed");
+ g_assert_not_reached ();
+ return G_SOURCE_REMOVE;
+}
+
+static void
+run_loop_for_ms (TestData *tdata,
+ guint32 timeout)
+{
+ mm_info ("Run loop for %u ms", timeout);
+ tdata->timeout_id = g_timeout_add (timeout, loop_timeout_cb, tdata);
+ g_main_loop_run (tdata->loop);
+}
+
+static void
+stop_loop (TestData *tdata)
+{
+ if (tdata->timeout_id) {
+ g_source_remove (tdata->timeout_id);
+ tdata->timeout_id = 0;
+ }
+ mm_info ("Stop the loop");
+ g_main_loop_quit (tdata->loop);
+}
+
+static void
+add_modem_completion_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GVariant *dbus_return = NULL;
+ GVariant *obj_path_variant = NULL;
+ TestData *tdata = NULL;
+
+ tdata = (TestData*)user_data;
+
+ mm_info ("AddModem DBus call completed");
+ dbus_return = g_dbus_proxy_call_finish (tdata->mm_proxy, res, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (dbus_return);
+ g_assert_cmpstr (g_variant_get_type_string (dbus_return), == , "(o)");
+
+ obj_path_variant = g_variant_get_child_value (dbus_return, 0);
+ tdata->modem_object_path = g_variant_dup_string (obj_path_variant, NULL);
+
+ g_assert_null (tdata->mm_modem_prop_proxy);
+ tdata->mm_modem_prop_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ MM_DBUS_SERVICE,
+ tdata->modem_object_path,
+ "org.freedesktop.DBus.Properties",
+ NULL, &error);
+
+ g_assert_no_error (error);
+ g_assert_nonnull (tdata->mm_modem_prop_proxy);
+
+ g_variant_unref (dbus_return);
+ g_variant_unref (obj_path_variant);
+
+ stop_loop (tdata);
+}
+
+static gchar*
+add_modem (TestData *tdata,
+ gboolean add_sim,
+ const gchar *iccid)
+{
+ g_dbus_proxy_call (tdata->mm_proxy,
+ "AddModem",
+ g_variant_new ("(bs)", add_sim, iccid),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 1000,
+ NULL,
+ add_modem_completion_cb,
+ tdata);
+
+ run_loop_for_ms (tdata, 1000);
+ return tdata->modem_object_path;
+}
+
+static void
+emit_state_changed_completion_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GVariant *dbus_return = NULL;
+ TestData *tdata = NULL;
+
+ tdata = (TestData*)user_data;
+
+ mm_info ("EmitStateChanged DBus call completed");
+ dbus_return = g_dbus_proxy_call_finish (tdata->mm_proxy, res, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (dbus_return);
+ g_variant_unref (dbus_return);
+
+ stop_loop (tdata);
+}
+
+static void
+set_modem_state (TestData *tdata,
+ MMModemState state,
+ MMModemStateFailedReason reason)
+{
+ GError *error = NULL;
+ GVariant *ret = NULL;
+ GVariant *old_state_variant = NULL;
+ guint old_state = 0;
+
+ g_assert_nonnull (tdata->mm_modem_prop_proxy);
+
+ /* Get current state */
+ ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy,
+ "org.freedesktop.DBus.Properties.Get",
+ g_variant_new ("(ss)",
+ MM_DBUS_INTERFACE_MODEM,
+ MM_MODEM_PROPERTY_STATE),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_variant_get (ret, "(v)", &old_state_variant);
+ old_state = (guint)g_variant_get_int32 (old_state_variant);
+ g_variant_unref (ret);
+ g_variant_unref (old_state_variant);
+
+ ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy,
+ "org.freedesktop.DBus.Properties.Set",
+ g_variant_new ("(ssv)",
+ MM_DBUS_INTERFACE_MODEM,
+ MM_MODEM_PROPERTY_STATE,
+ g_variant_new_int32 (state)),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_variant_unref (ret);
+
+ ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy,
+ "org.freedesktop.DBus.Properties.Set",
+ g_variant_new ("(ssv)",
+ MM_DBUS_INTERFACE_MODEM,
+ MM_MODEM_PROPERTY_STATEFAILEDREASON,
+ g_variant_new_uint32 (reason)),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+
+ g_assert_no_error (error);
+ g_variant_unref (ret);
+
+ /* Emit state change signal */
+ g_dbus_proxy_call (tdata->mm_proxy,
+ "EmitStateChanged",
+ g_variant_new ("(uuu)", old_state, state, reason),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 1000,
+ NULL,
+ emit_state_changed_completion_cb,
+ tdata);
+
+ run_loop_for_ms (tdata, 1000);
+}
+
+static void
+set_modem_unlock_completion_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GVariant *dbus_return = NULL;
+ TestData *tdata = NULL;
+
+ tdata = (TestData*)user_data;
+
+ mm_info ("org.freedesktop.DBus.Properties.Set DBus call completed");
+ dbus_return = g_dbus_proxy_call_finish (tdata->mm_modem_prop_proxy, res, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (dbus_return);
+ g_variant_unref (dbus_return);
+
+ stop_loop (tdata);
+}
+
+static void
+set_modem_unlock (TestData *tdata,
+ MMModemLock lock_state)
+{
+ g_assert_nonnull (tdata->mm_modem_prop_proxy);
+
+ g_dbus_proxy_call (tdata->mm_modem_prop_proxy,
+ "org.freedesktop.DBus.Properties.Set",
+ g_variant_new ("(ssv)",
+ MM_DBUS_INTERFACE_MODEM,
+ MM_MODEM_PROPERTY_UNLOCKREQUIRED,
+ g_variant_new_uint32 (lock_state)),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 1000,
+ NULL,
+ set_modem_unlock_completion_cb,
+ tdata);
+
+ run_loop_for_ms (tdata, 1000);
+}
+
+static void
+set_modem_equipment_error (TestData *tdata,
+ MMMobileEquipmentError equipmentError,
+ gboolean clear)
+{
+ GError *error = NULL;
+ GVariant *ret = NULL;
+
+ ret = g_dbus_proxy_call_sync (tdata->mm_proxy,
+ "SetMobileEquipmentError",
+ g_variant_new ("(ub)", equipmentError, clear),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 3000,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_variant_unref (ret);
+}
+
+static void
+test_modem_interface (TestData **ptdata,
+ gconstpointer data)
+{
+ TestData *tdata = NULL;
+ GDBusObject *modem_object = NULL;
+ MMModem *mm_modem = NULL;
+ g_autofree gchar *modem_path = NULL;
+
+ tdata = *ptdata;
+ /* Add a modem object (with no sim attached) */
+ modem_path = add_modem (tdata, FALSE, "");
+ modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (tdata->mm_manager), modem_path);
+ g_assert_nonnull (modem_object);
+
+ mm_modem = mm_object_get_modem (MM_OBJECT (modem_object));
+ g_clear_object (&modem_object);
+
+ /* Check the modem states */
+ g_assert_cmpuint (mm_modem_get_state (mm_modem), ==, MM_MODEM_STATE_UNKNOWN);
+ g_assert_cmpuint (mm_modem_get_state_failed_reason (mm_modem), ==, MM_MODEM_STATE_FAILED_REASON_UNKNOWN);
+
+ /* Set new state and check that it is propagated */
+ set_modem_state (tdata, MM_MODEM_STATE_REGISTERED, MM_MODEM_STATE_FAILED_REASON_NONE);
+
+ g_assert_cmpuint (mm_modem_get_state (mm_modem), ==, MM_MODEM_STATE_REGISTERED);
+ g_assert_cmpuint (mm_modem_get_state_failed_reason (mm_modem), ==, MM_MODEM_STATE_FAILED_REASON_NONE);
+
+ g_clear_object (&mm_modem);
+}
+
+static void
+mm_sim_send_pin_completion_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gboolean ret = FALSE;
+ TestData *tdata = NULL;
+
+ tdata = (TestData*)user_data;
+
+ mm_info("SendPin DBus method call completed");
+ ret = mm_sim_send_pin_finish (tdata->sim, res, &error);
+ if (tdata->pin_error) {
+ g_assert_nonnull (error);
+ g_assert_false (ret);
+ g_clear_error (&error);
+ } else {
+ g_assert_no_error (error);
+ g_assert_true (ret);
+ }
+ stop_loop (tdata);
+}
+
+static void
+test_sim_interface (TestData **ptdata,
+ gconstpointer data)
+{
+ TestData *tdata = NULL;
+ GDBusObject *modem_object = NULL;
+ MMModem *mm_modem = NULL;
+ GError *error = NULL;
+ g_autofree gchar *modem_path = NULL;
+
+ tdata = *ptdata;
+ /* Add a modem with a sim object */
+ modem_path = add_modem (tdata, TRUE, "89330122503000800750");
+ modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (tdata->mm_manager), modem_path);
+ g_assert_nonnull (modem_object);
+ mm_modem = mm_object_get_modem (MM_OBJECT (modem_object));
+ g_clear_object (&modem_object);
+
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE);
+ /* Lock the modem */
+ set_modem_unlock (tdata, MM_MODEM_LOCK_SIM_PIN);
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN);
+
+ tdata->sim = mm_modem_get_sim_sync (mm_modem, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (tdata->sim);
+ g_assert_cmpstr (mm_sim_get_identifier(tdata->sim), ==, "89330122503000800750");
+
+ /* Send a pin code */
+ tdata->pin_error = FALSE;
+ mm_sim_send_pin (tdata->sim, "1234", NULL, mm_sim_send_pin_completion_cb, tdata);
+ run_loop_for_ms (tdata, 1000);
+
+ /* Check that the modem has been unlocked */
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE);
+
+ /* Re lock it */
+ set_modem_unlock (tdata, MM_MODEM_LOCK_SIM_PIN);
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN);
+
+ /* Set an error that will simulate wrong pin code */
+ set_modem_equipment_error (tdata, MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, FALSE);
+
+ tdata->pin_error = TRUE;
+ mm_sim_send_pin (tdata->sim, "0000", NULL, mm_sim_send_pin_completion_cb, tdata);
+ run_loop_for_ms (tdata, 1000);
+
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN);
+
+ /* Clear the error and retry the pin code */
+ set_modem_equipment_error (tdata, 0, TRUE);
+ tdata->pin_error = FALSE;
+ mm_sim_send_pin (tdata->sim, "1234", NULL, mm_sim_send_pin_completion_cb, tdata);
+ run_loop_for_ms (tdata, 1000);
+
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE);
+
+ g_clear_object (&tdata->sim);
+ g_clear_object (&mm_modem);
+}
+
+int main (int argc,
+ char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/MM/stub/modem/interface",
+ TestData *, NULL, setup,
+ test_modem_interface,
+ teardown);
+ g_test_add ("/MM/stub/sim/interface",
+ TestData *, NULL, setup,
+ test_sim_interface,
+ teardown);
+ return g_test_run ();
+}
diff --git a/tools/tests/test-wrapper.sh.in b/tools/tests/test-wrapper.sh.in
new file mode 100644
index 00000000..d64ea4cb
--- /dev/null
+++ b/tools/tests/test-wrapper.sh.in
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+# For debugging behavior of test-modemmanager-service.py, you can modify
+# this line to add --log-file option
+GI_TYPELIB_PATH=@abs_top_builddir@/libmm-glib @abs_top_srcdir@/tools/test-modemmanager-service.py