summary refs log tree commit diff
path: root/src/core/devices/bluetooth/tests/nm-bt-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/devices/bluetooth/tests/nm-bt-test.c')
-rw-r--r--src/core/devices/bluetooth/tests/nm-bt-test.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/core/devices/bluetooth/tests/nm-bt-test.c b/src/core/devices/bluetooth/tests/nm-bt-test.c
new file mode 100644
index 00000000..0fc8aa87
--- /dev/null
+++ b/src/core/devices/bluetooth/tests/nm-bt-test.c
@@ -0,0 +1,223 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "src/core/nm-default-daemon.h"
+
+#include <glib-unix.h>
+
+#include "devices/bluetooth/nm-bluez5-dun.h"
+
+#include "nm-test-utils-core.h"
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN LOGD_BT
+#define _NMLOG(level, ...)                                             \
+    nm_log((level),                                                    \
+           _NMLOG_DOMAIN,                                              \
+           NULL,                                                       \
+           NULL,                                                       \
+           "bt%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__),            \
+           NM_PRINT_FMT_QUOTED(gl.argv_cmd, "[", gl.argv_cmd, "]", "") \
+               _NM_UTILS_MACRO_REST(__VA_ARGS__))
+
+/*****************************************************************************/
+
+struct {
+    int                argc;
+    const char *const *argv;
+    const char *       argv_cmd;
+    GMainLoop *        loop;
+} gl;
+
+typedef struct _MainCmdInfo {
+    const char *name;
+    int (*main_func)(const struct _MainCmdInfo *main_cmd_info);
+} MainCmdInfo;
+
+/*****************************************************************************/
+
+#if WITH_BLUEZ5_DUN
+
+typedef struct {
+    NMBluez5DunContext *dun_context;
+    GCancellable *      cancellable;
+    guint               timeout_id;
+    guint               sig_term_id;
+    guint               sig_int_id;
+} DunConnectData;
+
+static void
+_dun_connect_cb(NMBluez5DunContext *context,
+                const char *        rfcomm_dev,
+                GError *            error,
+                gpointer            user_data)
+{
+    DunConnectData *dun_connect_data = user_data;
+
+    g_assert(dun_connect_data);
+    g_assert(!dun_connect_data->dun_context);
+    g_assert((!!error) != (!!rfcomm_dev));
+
+    if (rfcomm_dev && !context) {
+        _LOGI("dun-connect notifies path \"%s\". Wait longer...", rfcomm_dev);
+        return;
+    }
+
+    if (rfcomm_dev) {
+        g_assert(context);
+        _LOGI("dun-connect completed with path \"%s\"", rfcomm_dev);
+    } else {
+        g_assert(!context);
+        _LOGI("dun-connect failed with error: %s", error->message);
+    }
+
+    dun_connect_data->dun_context = context;
+
+    g_main_loop_quit(gl.loop);
+}
+
+static void
+_dun_notify_tty_hangup_cb(NMBluez5DunContext *context, gpointer user_data)
+{
+    _LOGI("dun-connect: notified TTY hangup");
+}
+
+static gboolean
+_timeout_cb(gpointer user_data)
+{
+    DunConnectData *dun_connect_data = user_data;
+
+    _LOGI("timeout");
+    dun_connect_data->timeout_id = 0;
+    if (dun_connect_data->cancellable)
+        g_cancellable_cancel(dun_connect_data->cancellable);
+    return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_sig_xxx_cb(DunConnectData *dun_connect_data, int sigid)
+{
+    _LOGI("signal %s received", sigid == SIGTERM ? "SIGTERM" : "SIGINT");
+    g_main_loop_quit(gl.loop);
+    return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+_sig_term_cb(gpointer user_data)
+{
+    return _sig_xxx_cb(user_data, SIGTERM);
+}
+
+static gboolean
+_sig_int_cb(gpointer user_data)
+{
+    return _sig_xxx_cb(user_data, SIGINT);
+}
+#endif
+
+static int
+do_dun_connect(const MainCmdInfo *main_cmd_info)
+{
+#if WITH_BLUEZ5_DUN
+    gs_unref_object GCancellable *cancellable = NULL;
+    gs_free_error GError *error               = NULL;
+    const char *          adapter;
+    const char *          remote;
+    DunConnectData        dun_connect_data = {};
+
+    if (gl.argc < 4) {
+        _LOGE("missing arguments \"adapter\" and \"remote\"");
+        return -1;
+    }
+
+    adapter = gl.argv[2];
+    remote  = gl.argv[3];
+
+    cancellable                  = g_cancellable_new();
+    dun_connect_data.cancellable = cancellable;
+
+    if (!nm_bluez5_dun_connect(adapter,
+                               remote,
+                               cancellable,
+                               _dun_connect_cb,
+                               &dun_connect_data,
+                               _dun_notify_tty_hangup_cb,
+                               &dun_connect_data,
+                               &error)) {
+        _LOGE("connect failed to start: %s", error->message);
+        return -1;
+    }
+
+    dun_connect_data.timeout_id = g_timeout_add(60000, _timeout_cb, &dun_connect_data);
+
+    g_main_loop_run(gl.loop);
+
+    nm_clear_g_source(&dun_connect_data.timeout_id);
+
+    if (dun_connect_data.dun_context) {
+        dun_connect_data.sig_term_id = g_unix_signal_add(SIGTERM, _sig_term_cb, &dun_connect_data);
+        dun_connect_data.sig_int_id  = g_unix_signal_add(SIGINT, _sig_int_cb, &dun_connect_data);
+
+        g_main_loop_run(gl.loop);
+
+        nm_clear_g_source(&dun_connect_data.sig_term_id);
+        nm_clear_g_source(&dun_connect_data.sig_int_id);
+
+        nm_bluez5_dun_disconnect(g_steal_pointer(&dun_connect_data.dun_context));
+    }
+
+    return 0;
+#else
+    _LOGE("compiled without bluetooth DUN support");
+    return 1;
+#endif
+}
+
+/*****************************************************************************/
+
+NMTST_DEFINE();
+
+int
+main(int argc, char **argv)
+{
+    static const MainCmdInfo main_cmd_infos[] = {
+        {
+            .name      = "dun-connect",
+            .main_func = do_dun_connect,
+        },
+    };
+    int   exit_code = 0;
+    guint i;
+
+    if (!g_getenv("G_MESSAGES_DEBUG"))
+        g_setenv("G_MESSAGES_DEBUG", "all", TRUE);
+
+    nmtst_init_with_logging(&argc, &argv, "DEBUG", "ALL");
+
+    nm_logging_init(NULL, TRUE);
+
+    gl.argv = (const char *const *) argv;
+    gl.argc = argc;
+    gl.loop = g_main_loop_new(NULL, FALSE);
+
+    _LOGI("bluetooth test util start");
+
+    gl.argv_cmd = argc >= 2 ? argv[1] : NULL;
+
+    for (i = 0; i < G_N_ELEMENTS(main_cmd_infos); i++) {
+        if (nm_streq0(main_cmd_infos[i].name, gl.argv_cmd)) {
+            _LOGD("start \"%s\"", gl.argv_cmd);
+            exit_code = main_cmd_infos[i].main_func(&main_cmd_infos[i]);
+            _LOGD("completed with %d", exit_code);
+            break;
+        }
+    }
+    if (gl.argv_cmd && i >= G_N_ELEMENTS(main_cmd_infos)) {
+        nm_log_err(LOGD_BT, "invalid command \"%s\"", gl.argv_cmd);
+        exit_code = -1;
+    }
+
+    nm_clear_pointer(&gl.loop, g_main_loop_unref);
+
+    return exit_code;
+}