[gnome-control-center/wip/benzea/ci-network: 16/17] tests/network: Add basic testing of network panel



commit f655e46ce792311730fbed6ac8482dbfa6101bba
Author: Benjamin Berg <bberg redhat com>
Date:   Fri Apr 6 17:19:58 2018 +0200

    tests/network: Add basic testing of network panel
    
    This adds tests for the network panel based on the test service found in
    NetworkManager. Another possible solution may be to use the one from
    dbusmock, however NetworkManager already has readily available code to
    write tests in C which makes checking the widget hierarchy easier.

 .gitignore                          |   1 +
 meson.build                         |   5 +
 panels/network/meson.build          |   3 +-
 tests/meson.build                   |   1 +
 tests/network/cc-test-window.c      | 203 +++++++++++++++++++
 tests/network/cc-test-window.h      |  35 ++++
 tests/network/meson.build           |  30 +++
 tests/network/nmtst-helpers.h       | 297 ++++++++++++++++++++++++++++
 tests/network/test-network-panel.c  | 382 ++++++++++++++++++++++++++++++++++++
 tests/network/test-network-panel.py |  44 +++++
 10 files changed, 1000 insertions(+), 1 deletion(-)
---
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..bee8a64b7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+__pycache__
diff --git a/meson.build b/meson.build
index a2d484442..16911d990 100644
--- a/meson.build
+++ b/meson.build
@@ -30,6 +30,11 @@ enable_tracing = get_option('tracing')
 
 config_h = configuration_data()
 
+py3 = import('python3')
+python = py3.find_python()
+
+config_h.set_quoted('TEST_NM_PYTHON', python.path())
+
 # defines
 set_defines = [
   # package
diff --git a/panels/network/meson.build b/panels/network/meson.build
index 0325c5afd..2ec4fbf71 100644
--- a/panels/network/meson.build
+++ b/panels/network/meson.build
@@ -69,7 +69,7 @@ sources += gnome.compile_resources(
 
 cflags += '-DGNOMELOCALEDIR="@0@"'.format(control_center_localedir)
 
-panels_libs += static_library(
+network_panel_lib = static_library(
   cappletname,
   sources: sources,
   include_directories: [top_inc, common_inc],
@@ -77,3 +77,4 @@ panels_libs += static_library(
   c_args: cflags,
   link_with: libconnection_editor
 )
+panels_libs += network_panel_lib
diff --git a/tests/meson.build b/tests/meson.build
index 49229a305..41adee9e1 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,4 +1,5 @@
 subdir('common')
 subdir('datetime')
+subdir('network')
 subdir('printers')
 subdir('info')
diff --git a/tests/network/cc-test-window.c b/tests/network/cc-test-window.c
new file mode 100644
index 000000000..dcda579b3
--- /dev/null
+++ b/tests/network/cc-test-window.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2009, 2010 Intel, Inc.
+ * Copyright (c) 2010, 2018 Red Hat, Inc.
+ * Copyright (c) 2016 Endless, Inc.
+ *
+ * The Control Center 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.
+ *
+ * The Control Center 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Benjamin Berg <bberg redhat com>
+ */
+
+#define G_LOG_DOMAIN "cc-test-window"
+
+#include <config.h>
+
+#include "cc-test-window.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <string.h>
+
+#include "shell/cc-panel.h"
+#include "shell/cc-shell.h"
+#include "cc-util.h"
+
+
+struct _CcTestWindow
+{
+  GtkWindow    parent;
+
+  GtkWidget   *main_box;
+
+  GtkWidget  *header;
+  CcPanel    *active_panel;
+};
+
+static void     cc_shell_iface_init         (CcShellInterface      *iface);
+
+G_DEFINE_TYPE_WITH_CODE (CcTestWindow, cc_test_window, GTK_TYPE_WINDOW,
+                         G_IMPLEMENT_INTERFACE (CC_TYPE_SHELL, cc_shell_iface_init))
+
+enum
+{
+  PROP_0,
+  PROP_ACTIVE_PANEL
+};
+
+
+
+static void
+set_active_panel (CcTestWindow *shell,
+                  CcPanel      *panel)
+{
+  g_assert (CC_IS_SHELL (shell));
+  g_assert (CC_IS_PANEL (panel));
+
+  /* Only allow setting to a non NULL value once. */
+  g_assert (shell->active_panel == NULL);
+
+  if (panel)
+    {
+      shell->active_panel = g_object_ref (panel);
+      gtk_container_add_with_properties (GTK_CONTAINER (shell->main_box), GTK_WIDGET (panel),
+                                         "pack-type", GTK_PACK_END,
+                                         "expand", TRUE,
+                                         "fill", TRUE,
+                                         NULL);
+    }
+}
+
+/* CcShell implementation */
+static gboolean
+cc_test_window_set_active_panel_from_id (CcShell      *shell,
+                                         const gchar  *start_id,
+                                         GVariant     *parameters,
+                                         GError      **error)
+{
+  /* Not implemented */
+  g_assert_not_reached ();
+}
+
+static void
+cc_test_window_embed_widget_in_header (CcShell   *shell,
+                                       GtkWidget *widget)
+{
+  CcTestWindow *self = CC_TEST_WINDOW (shell);
+
+  /* add to main box */
+  gtk_container_add_with_properties (GTK_CONTAINER (self->main_box), GTK_WIDGET (widget),
+                                     "pack-type", GTK_PACK_START,
+                                     "expand", FALSE,
+                                     "fill", TRUE,
+                                     NULL);
+  gtk_widget_show (widget);
+}
+
+static GtkWidget *
+cc_test_window_get_toplevel (CcShell *shell)
+{
+  return GTK_WIDGET (shell);
+}
+
+static void
+cc_shell_iface_init (CcShellInterface *iface)
+{
+  iface->set_active_panel_from_id = cc_test_window_set_active_panel_from_id;
+  iface->embed_widget_in_header = cc_test_window_embed_widget_in_header;
+  iface->get_toplevel = cc_test_window_get_toplevel;
+}
+
+/* GObject Implementation */
+static void
+cc_test_window_get_property (GObject    *object,
+                             guint       property_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  CcTestWindow *self = CC_TEST_WINDOW (object);
+
+  switch (property_id)
+    {
+    case PROP_ACTIVE_PANEL:
+      g_value_set_object (value, self->active_panel);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+cc_test_window_set_property (GObject      *object,
+                             guint         property_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  CcTestWindow *shell = CC_TEST_WINDOW (object);
+
+  switch (property_id)
+    {
+    case PROP_ACTIVE_PANEL:
+      set_active_panel (shell, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+cc_test_window_dispose (GObject *object)
+{
+  CcTestWindow *self = CC_TEST_WINDOW (object);
+
+  g_clear_object (&self->active_panel);
+
+  G_OBJECT_CLASS (cc_test_window_parent_class)->dispose (object);
+}
+
+static void
+cc_test_window_class_init (CcTestWindowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = cc_test_window_get_property;
+  object_class->set_property = cc_test_window_set_property;
+  object_class->dispose = cc_test_window_dispose;
+
+  g_object_class_override_property (object_class, PROP_ACTIVE_PANEL, "active-panel");
+}
+
+static void
+cc_test_window_init (CcTestWindow *self)
+{
+  gtk_widget_set_size_request (GTK_WIDGET (self), 500, 800);
+
+  self->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
+
+  gtk_container_add (GTK_CONTAINER (self), self->main_box);
+}
+
+CcTestWindow *
+cc_test_window_new (void)
+{
+  return g_object_new (CC_TYPE_TEST_WINDOW,
+                       "resizable", TRUE,
+                       "title", "Test Settings",
+                       "window-position", GTK_WIN_POS_CENTER,
+                       NULL);
+}
diff --git a/tests/network/cc-test-window.h b/tests/network/cc-test-window.h
new file mode 100644
index 000000000..abbdb61a0
--- /dev/null
+++ b/tests/network/cc-test-window.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010 Intel, Inc.
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * The Control Center 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.
+ *
+ * The Control Center 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Benjamin Berg <bberg redhat com>
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include "shell/cc-shell.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_TEST_WINDOW (cc_test_window_get_type ())
+
+G_DECLARE_FINAL_TYPE (CcTestWindow, cc_test_window, CC, TEST_WINDOW, GtkWindow)
+
+CcTestWindow *cc_test_window_new (void);
+
+G_END_DECLS
diff --git a/tests/network/meson.build b/tests/network/meson.build
new file mode 100644
index 000000000..4378ea72a
--- /dev/null
+++ b/tests/network/meson.build
@@ -0,0 +1,30 @@
+
+
+includes = [top_inc, include_directories('../../panels/network', 'nm-utils')]
+cflags = [
+  '-DTEST_SRCDIR="@0@"'.format(meson.current_source_dir()),
+  '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_WITH_GLIB',
+  '-DNETWORKMANAGER_COMPILATION_TEST',
+  '-DTEST_NM_SERVICE="@0@"'.format(join_paths(meson.source_root(), 'tests', 'network', 'nm-utils', 
'test-networkmanager-service.py')),
+]
+
+exe = executable(
+  'test-network-panel',
+  ['test-network-panel.c', 'cc-test-window.c', 'nm-utils/nm-test-utils-impl.c'],
+  include_directories: includes + [common_inc],
+  dependencies: common_deps + network_manager_deps + [libtestshell_dep],
+  link_with: [network_panel_lib],
+  c_args: cflags
+)
+
+envs = [
+  'G_MESSAGES_DEBUG=all',
+  'BUILDDIR=' + meson.current_build_dir(),
+  'TOP_BUILDDIR=' + meson.build_root()
+]
+
+test('test-network-panel',
+  find_program('test-network-panel.py'),
+  env: envs,
+  timeout: 10
+)
diff --git a/tests/network/nmtst-helpers.h b/tests/network/nmtst-helpers.h
new file mode 100644
index 000000000..868da36e3
--- /dev/null
+++ b/tests/network/nmtst-helpers.h
@@ -0,0 +1,297 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * 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, 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2010 - 2014, 2018 Red Hat, Inc.
+ *
+ */
+
+/* Static functions to help with testing */
+
+
+/* nmtst_create_minimal_connection is copied from nm-test-utils.h. */
+
+static inline NMConnection *
+nmtst_create_minimal_connection (const char *id, const char *uuid, const char *type, NMSettingConnection 
**out_s_con)
+{
+       NMConnection *con;
+       NMSetting *s_base = NULL;
+       NMSettingConnection *s_con;
+       gs_free char *uuid_free = NULL;
+
+       g_assert (id);
+
+       if (uuid)
+               g_assert (nm_utils_is_uuid (uuid));
+       else
+               uuid = uuid_free = nm_utils_uuid_generate ();
+
+       if (type) {
+               GType type_g;
+
+               type_g = nm_setting_lookup_type (type);
+
+               g_assert (type_g != G_TYPE_INVALID);
+
+               s_base = g_object_new (type_g, NULL);
+               g_assert (NM_IS_SETTING (s_base));
+       }
+
+       con = nm_simple_connection_new ();
+
+       s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+
+       g_object_set (s_con,
+                     NM_SETTING_CONNECTION_ID, id,
+                     NM_SETTING_CONNECTION_UUID, uuid,
+                     NM_SETTING_CONNECTION_TYPE, type,
+                     NULL);
+       nm_connection_add_setting (con, NM_SETTING (s_con));
+
+       if (s_base)
+               nm_connection_add_setting (con, s_base);
+
+       if (out_s_con)
+               *out_s_con = s_con;
+       return con;
+}
+
+
+typedef struct {
+       GMainLoop *loop;
+       NMDevice *device;
+       NMClient *client;
+
+       NMActiveConnection *ac;
+
+       const gchar * const *client_props;
+       const gchar * const *device_props;
+
+       int client_remaining;
+       int device_remaining;
+       int other_remaining;
+} EventWaitInfo;
+
+
+#define WAIT_CHECK_REMAINING() \
+       if (info->client_remaining == 0 && info->device_remaining == 0 && info->other_remaining == 0) { \
+               g_debug ("Got expected events, quitting mainloop"); \
+               g_main_loop_quit (info->loop); \
+       } \
+       if (info->client_remaining < 0 || info->device_remaining < 0 || info->other_remaining < 0) { \
+               g_error ("Pending events are negative: client: %d, device: %d, other: %d", 
info->client_remaining, info->device_remaining, info->other_remaining); \
+               g_assert_not_reached (); \
+       }
+
+#define WAIT_DECL() \
+       EventWaitInfo info = {0}; \
+       gint _timeout_id;
+#define WAIT_DEVICE(_device, count, ...) \
+       info.device = (_device); \
+       info.device_remaining = (count); \
+       { const gchar * const *props = (const char const *[]){ __VA_ARGS__, NULL }; \
+       info.device_props = props; } \
+       g_signal_connect ((_device), "notify", G_CALLBACK (device_notify_cb), &info);
+#define WAIT_CLIENT(_client, count, ...) \
+       info.client = (_client); \
+       info.client_remaining = (count); \
+       info.client_props = (const char *[]) {__VA_ARGS__, NULL}; \
+       g_signal_connect ((_client), "notify", G_CALLBACK (client_notify_cb), &info);
+
+#define WAIT_DESTROY() \
+       g_source_remove (_timeout_id); \
+       if (info.device) \
+               g_signal_handlers_disconnect_by_func (info.device, G_CALLBACK (device_notify_cb), &info); \
+       if (info.client) \
+               g_signal_handlers_disconnect_by_func (info.client, G_CALLBACK (client_notify_cb), &info); \
+       g_main_loop_unref (info.loop);
+
+#define WAIT_FINISHED(timeout) \
+       info.loop = g_main_loop_new (NULL, FALSE); \
+       _timeout_id = g_timeout_add_seconds ((timeout), timeout_cb, &info); \
+       g_main_loop_run (info.loop); \
+       WAIT_DESTROY()
+
+static gboolean
+timeout_cb (gpointer user_data)
+{
+       EventWaitInfo *info = user_data;
+
+       if (info)
+               g_error ("Pending events are: client: %d, device: %d, other: %d", info->client_remaining, 
info->device_remaining, info->other_remaining); \
+       g_assert_not_reached ();
+       return G_SOURCE_REMOVE;
+}
+
+static void
+device_notify_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data)
+{
+       EventWaitInfo *info = user_data;
+
+       g_assert (device == info->device);
+
+       if (!g_strv_contains (info->device_props, g_param_spec_get_name (pspec))) {
+               g_debug ("Ignoring notification for device property %s", g_param_spec_get_name (pspec));
+               return;
+       }
+
+       g_debug ("Counting notification for device property %s", g_param_spec_get_name (pspec));
+
+       info->device_remaining--;
+       WAIT_CHECK_REMAINING()
+}
+
+static void
+client_notify_cb (NMClient *client, GParamSpec *pspec, gpointer user_data)
+{
+       EventWaitInfo *info = user_data;
+
+       g_assert (client == info->client);
+
+       if (!g_strv_contains (info->client_props, g_param_spec_get_name (pspec))) {
+               g_debug ("Ignoring notification for client property %s", g_param_spec_get_name (pspec));
+               return;
+       }
+
+       g_debug ("Counting notification for client property %s", g_param_spec_get_name (pspec));
+
+       info->client_remaining--;
+       WAIT_CHECK_REMAINING()
+}
+
+static void
+nmtst_set_device_state (NMTstcServiceInfo *sinfo, NMDevice *device, NMDeviceState state, NMDeviceStateReason 
reason)
+{
+       GError *error = NULL;
+       WAIT_DECL()
+
+       g_debug ("Setting device %s state to %d with reason %d", nm_device_get_iface (device), state, reason);
+
+       g_dbus_proxy_call_sync (sinfo->proxy,
+                               "SetDeviceState",
+                               g_variant_new ("(suu)", nm_object_get_path (NM_OBJECT (device)), state, 
reason),
+                               G_DBUS_CALL_FLAGS_NO_AUTO_START,
+                               3000,
+                               NULL,
+                               &error);
+       g_assert_no_error (error);
+
+       WAIT_DEVICE(device, 1, "state-reason")
+       WAIT_FINISHED(5)
+}
+
+static void
+nmtst_set_wired_speed (NMTstcServiceInfo *sinfo, NMDevice *device, guint32 speed)
+{
+       GError *error = NULL;
+       WAIT_DECL()
+
+       g_debug ("Setting device %s speed to %d", nm_device_get_iface (device), speed);
+
+       g_dbus_proxy_call_sync (sinfo->proxy,
+                               "SetWiredSpeed",
+                               g_variant_new ("(su)", nm_object_get_path (NM_OBJECT (device)), speed),
+                               G_DBUS_CALL_FLAGS_NO_AUTO_START,
+                               3000,
+                               NULL,
+                               &error);
+
+       g_assert_no_error (error);
+
+       WAIT_DEVICE(device, 2, "speed", "carrier")
+       WAIT_FINISHED(5)
+}
+
+
+static void
+device_removed_cb (NMClient *client,
+                   NMDevice *device,
+                   gpointer user_data)
+{
+       EventWaitInfo *info = user_data;
+
+       g_assert (device);
+       g_assert (device == info->device);
+
+       info->other_remaining--;
+       WAIT_CHECK_REMAINING()
+}
+
+static void
+nmtst_remove_device (NMTstcServiceInfo *sinfo, NMClient *client, NMDevice *device)
+{
+       GError *error = NULL;
+       WAIT_DECL()
+
+       g_object_ref (device);
+
+       g_dbus_proxy_call_sync (sinfo->proxy,
+                               "RemoveDevice",
+                               g_variant_new ("(s)", nm_object_get_path (NM_OBJECT (device))),
+                               G_DBUS_CALL_FLAGS_NO_AUTO_START,
+                               3000,
+                               NULL,
+                               &error);
+       g_assert_no_error (error);
+
+       info.device = device;
+       info.client = client;
+       info.other_remaining = 1;
+       g_signal_connect (client, "device-removed",
+                         G_CALLBACK (device_removed_cb), &info);
+
+       WAIT_FINISHED(5)
+
+       g_object_unref(device);
+       g_signal_handlers_disconnect_by_func (client, device_removed_cb, &info);
+}
+
+static void
+add_and_activate_cb (GObject *object,
+                     GAsyncResult *result,
+                     gpointer user_data)
+{
+       NMClient *client = NM_CLIENT (object);
+       EventWaitInfo *info = user_data;
+       GError *error = NULL;
+
+       info->ac = nm_client_add_and_activate_connection_finish (client, result, &error);
+       g_assert_no_error (error);
+       g_assert (info->ac != NULL);
+
+       info->other_remaining--;
+       WAIT_CHECK_REMAINING()
+}
+
+static NMActiveConnection*
+nmtst_add_and_activate_connection (NMTstcServiceInfo *sinfo, NMClient *client, NMDevice *device, 
NMConnection *conn)
+{
+       WAIT_DECL()
+
+       nm_client_add_and_activate_connection_async (client, conn, device, NULL,
+                                                    NULL, add_and_activate_cb, &info);
+
+       info.other_remaining = 1;
+       WAIT_CLIENT(client, 1, NM_CLIENT_ACTIVE_CONNECTIONS);
+       WAIT_DEVICE(device, 1, NM_DEVICE_ACTIVE_CONNECTION);
+
+       g_object_unref (conn);
+
+       WAIT_FINISHED(5)
+
+       g_assert (info.ac != NULL);
+
+       return info.ac;
+}
diff --git a/tests/network/test-network-panel.c b/tests/network/test-network-panel.c
new file mode 100644
index 000000000..9b3c0cab4
--- /dev/null
+++ b/tests/network/test-network-panel.c
@@ -0,0 +1,382 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (c) 2010-2014, 2018 Red Hat, Inc.
+ *
+ * The Control Center 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.
+ *
+ * The Control Center 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Benjamin Berg <bberg redhat com>
+ */
+
+#define G_LOG_DOMAIN "test-network-panel"
+
+#include "nm-macros-internal.h"
+
+#include <NetworkManager.h>
+#include <nm-client.h>
+#include <nm-utils.h>
+
+#include "nm-test-libnm-utils.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <gtk/gtk.h>
+
+#include "cc-test-window.h"
+#include "shell/cc-object-storage.h"
+
+#include "nmtst-helpers.h"
+
+typedef struct {
+  NMTstcServiceInfo *sinfo;
+  NMClient *client;
+
+  NMDevice *main_ether;
+
+  GtkWidget *shell;
+  CcPanel *panel;
+} NetworkPanelFixture;
+
+
+extern GType cc_network_panel_get_type (void);
+
+static void
+fixture_set_up_empty (NetworkPanelFixture  *fixture,
+                      gconstpointer         user_data)
+{
+  g_autoptr(GError) error = NULL;
+
+  cc_object_storage_initialize ();
+
+  /* Bring up the libnm service. */
+  fixture->sinfo = nmtstc_service_init ();
+
+  fixture->client = nm_client_new (NULL, &error);
+  g_assert_no_error (error);
+
+  fixture->shell = GTK_WIDGET (cc_test_window_new ());
+
+  fixture->panel = g_object_new (cc_network_panel_get_type (),
+                                 "shell", CC_SHELL (fixture->shell),
+                                 NULL);
+
+  g_object_ref (fixture->panel);
+  cc_shell_set_active_panel (CC_SHELL (fixture->shell), fixture->panel);
+
+  gtk_widget_show_all (fixture->shell);
+}
+
+static void
+fixture_tear_down (NetworkPanelFixture  *fixture,
+                   gconstpointer         user_data)
+{
+  g_clear_object (&fixture->panel);
+  g_clear_pointer (&fixture->shell, gtk_widget_destroy);
+
+  cc_object_storage_destroy ();
+
+  g_clear_pointer (&fixture->sinfo, nmtstc_service_cleanup);
+}
+
+static void
+fixture_set_up_wired (NetworkPanelFixture  *fixture,
+                      gconstpointer         user_data)
+{
+  NMDevice *second;
+
+  fixture_set_up_empty (fixture, user_data);
+
+  fixture->main_ether = nmtstc_service_add_wired_device (fixture->sinfo,
+                                                         fixture->client,
+                                                         "eth1000",
+                                                         "52:54:00:ab:db:23",
+                                                         NULL);
+
+  /* Add/remove one to catch issues with signal disconnects. */
+  second = nmtstc_service_add_wired_device (fixture->sinfo,
+                                            fixture->client,
+                                            "eth1001",
+                                            "52:54:00:ab:db:24",
+                                            NULL);
+  nmtst_remove_device (fixture->sinfo, fixture->client, second);
+}
+
+/*****************************************************************************/
+
+static void
+test_device_add (NetworkPanelFixture  *fixture,
+                 gconstpointer         user_data)
+{
+  const gchar *device_path;
+
+  /* Tell the test service to add a new device.
+   * We use some weird numbers so that the devices will not exist on the
+   * host system. */
+  fixture->main_ether = nmtstc_service_add_wired_device (fixture->sinfo,
+                                                         fixture->client,
+                                                         "eth1000",
+                                                         "52:54:00:ab:db:23",
+                                                         NULL);
+  device_path = nm_object_get_path (NM_OBJECT (fixture->main_ether));
+  g_debug("Device added: %s\n", device_path);
+
+  g_assert (gtk_test_find_label(fixture->shell, "Wired") != NULL);
+}
+
+static void
+test_second_device_add (NetworkPanelFixture  *fixture,
+                        gconstpointer         user_data)
+{
+  NMDevice *device;
+  const gchar *device_path;
+
+  test_device_add (fixture, user_data);
+
+  device = nmtstc_service_add_wired_device (fixture->sinfo,
+                                            fixture->client,
+                                            "eth1001",
+                                            "52:54:00:ab:db:24",
+                                            NULL);
+  device_path = nm_object_get_path (NM_OBJECT (device));
+  g_debug("Second device added: %s\n", device_path);
+
+  g_assert_null (gtk_test_find_label (fixture->shell, "Wired"));
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "Ethernet (eth1000)"));
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "Ethernet (eth1001)"));
+}
+
+static void
+test_second_device_add_remove (NetworkPanelFixture  *fixture,
+                               gconstpointer         user_data)
+{
+  NMDevice *device;
+  const gchar *device_path;
+
+  test_device_add (fixture, user_data);
+
+  device = nmtstc_service_add_wired_device (fixture->sinfo,
+                                            fixture->client,
+                                            "eth1001",
+                                            "52:54:00:ab:db:24",
+                                            NULL);
+  device_path = nm_object_get_path (NM_OBJECT (device));
+  g_debug("Second device added: %s\n", device_path);
+
+  nmtst_remove_device (fixture->sinfo, fixture->client, device);
+  g_debug("Second device removed again\n");
+
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "Wired"));
+  g_assert_null (gtk_test_find_label (fixture->shell, "Ethernet (eth1000)"));
+  g_assert_null (gtk_test_find_label (fixture->shell, "Ethernet (eth1001)"));
+}
+
+/*****************************************************************************/
+
+static void
+add_cb (GObject       *object,
+        GAsyncResult  *result,
+        gpointer       user_data)
+{
+  NMClient *client = NM_CLIENT (object);
+  EventWaitInfo *info = user_data;
+  NMRemoteConnection *remote_conn;
+  g_autoptr(GError) error = NULL;
+
+  remote_conn = nm_client_add_connection_finish (client, result, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (remote_conn);
+  g_object_unref (remote_conn);
+
+  info->other_remaining--;
+  WAIT_CHECK_REMAINING()
+}
+
+static void
+test_connection_add (NetworkPanelFixture  *fixture,
+                     gconstpointer         user_data)
+{
+  NMConnection *conn;
+  g_autoptr(GError) error = NULL;
+  WAIT_DECL()
+
+  conn = nmtst_create_minimal_connection ("test-inactive", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+  nm_device_connection_compatible (fixture->main_ether, conn, &error);
+  g_assert_no_error (error);
+
+  nm_client_add_connection_async (fixture->client, conn, TRUE, NULL, add_cb, &info);
+
+  info.other_remaining = 1;
+  WAIT_CLIENT(fixture->client, 1, NM_CLIENT_CONNECTIONS);
+
+  g_object_unref (conn);
+
+  WAIT_FINISHED(5)
+
+  /* We have one (non-active) connection only, so we get a special case */
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "Cable unplugged"));
+}
+
+/*****************************************************************************/
+
+static void
+test_unconnected_carrier_plug (NetworkPanelFixture  *fixture,
+                               gconstpointer         user_data)
+{
+  nmtst_set_wired_speed (fixture->sinfo, fixture->main_ether, 1234);
+  nmtst_set_device_state (fixture->sinfo, fixture->main_ether, NM_DEVICE_STATE_DISCONNECTED, 
NM_DEVICE_STATE_REASON_CARRIER);
+
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "1234 Mb/s"));
+}
+
+
+/*****************************************************************************/
+
+
+static void
+test_connection_add_activate (NetworkPanelFixture  *fixture,
+                              gconstpointer         user_data)
+{
+  NMConnection *conn;
+  NMActiveConnection *active_conn = NULL;
+  g_autoptr(GError) error = NULL;
+  GtkWidget *label, *sw;
+
+  /* First set us into disconnected state with a carrier. */
+  nmtst_set_wired_speed (fixture->sinfo, fixture->main_ether, 1234);
+  nmtst_set_device_state (fixture->sinfo, fixture->main_ether, NM_DEVICE_STATE_DISCONNECTED, 
NM_DEVICE_STATE_REASON_CARRIER);
+
+  conn = nmtst_create_minimal_connection ("test-active", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+
+  nm_device_connection_compatible (fixture->main_ether, conn, &error);
+  g_assert_no_error (error);
+
+  active_conn = nmtst_add_and_activate_connection (fixture->sinfo, fixture->client, fixture->main_ether, 
conn);
+  g_object_unref (active_conn);
+
+  label = gtk_test_find_label (fixture->shell, "1234 Mb/s");
+  sw = gtk_test_find_sibling (label, GTK_TYPE_SWITCH);
+  g_assert_nonnull (sw);
+  g_assert_false (gtk_switch_get_state (GTK_SWITCH (sw)));
+
+  /* Now set the state to connected and check the switch state */
+  nmtst_set_device_state (fixture->sinfo, fixture->main_ether, NM_DEVICE_STATE_ACTIVATED, 
NM_DEVICE_STATE_REASON_NONE);
+  g_assert_true (gtk_switch_get_state (GTK_SWITCH (sw)));
+
+  /* Let's toggle the switch back and check we get events */
+  gtk_switch_set_active (GTK_SWITCH (sw), FALSE);
+
+  /* Only one connection, so a generic label. */
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "Connected - 1234 Mb/s"));
+}
+
+static void
+test_connection_multi_add_activate (NetworkPanelFixture  *fixture,
+                                    gconstpointer         user_data)
+{
+  NMConnection *conn;
+  GtkWidget *sw;
+  g_autoptr(GError) error = NULL;
+
+  /* Add a single connection (just chainging up to other test). */
+  test_connection_add (fixture, user_data);
+
+  /* Basically same as test_connection_add_activate but with different assertions. */
+  nmtst_set_wired_speed (fixture->sinfo, fixture->main_ether, 1234);
+  nmtst_set_device_state (fixture->sinfo, fixture->main_ether, NM_DEVICE_STATE_DISCONNECTED, 
NM_DEVICE_STATE_REASON_CARRIER);
+
+  conn = nmtst_create_minimal_connection ("test-active", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+
+  nm_device_connection_compatible (fixture->main_ether, conn, &error);
+  g_assert_no_error (error);
+
+  g_object_unref (nmtst_add_and_activate_connection (fixture->sinfo, fixture->client, fixture->main_ether, 
conn));
+
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "test-inactive"));
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "test-active"));
+  g_assert_null (gtk_test_find_label (fixture->shell, "52:54:00:ab:db:23"));
+
+  /* We have no switch if there are multiple connections */
+  sw = gtk_test_find_sibling (gtk_test_find_label (fixture->shell, "test-active"), GTK_TYPE_SWITCH);
+  if (sw)
+    g_assert_false (gtk_widget_is_visible (sw));
+
+  /* Now set the state to connected and check the switch state */
+  nmtst_set_device_state (fixture->sinfo, fixture->main_ether, NM_DEVICE_STATE_ACTIVATED, 
NM_DEVICE_STATE_REASON_NONE);
+
+  /* Hardware address is shown at this point */
+  g_assert_nonnull (gtk_test_find_label (fixture->shell, "52:54:00:ab:db:23"));
+}
+
+int
+main (int argc, char **argv)
+{
+  g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
+  g_setenv ("LIBNM_USE_SESSION_BUS", "1", TRUE);
+  g_setenv ("LC_ALL", "C", TRUE);
+
+  gtk_test_init (&argc, &argv, NULL);
+
+  g_test_add ("/network-panel-wired/device-add",
+              NetworkPanelFixture,
+              NULL,
+              fixture_set_up_empty,
+              test_device_add,
+              fixture_tear_down);
+
+  g_test_add ("/network-panel-wired/second-device-add",
+              NetworkPanelFixture,
+              NULL,
+              fixture_set_up_empty,
+              test_second_device_add,
+              fixture_tear_down);
+
+  g_test_add ("/network-panel-wired/second-device-add-remove",
+              NetworkPanelFixture,
+              NULL,
+              fixture_set_up_empty,
+              test_second_device_add_remove,
+              fixture_tear_down);
+
+  g_test_add ("/network-panel-wired/unconnected-carrier-plug",
+              NetworkPanelFixture,
+              NULL,
+              fixture_set_up_wired,
+              test_unconnected_carrier_plug,
+              fixture_tear_down);
+
+  g_test_add ("/network-panel-wired/connection-add",
+              NetworkPanelFixture,
+              NULL,
+              fixture_set_up_wired,
+              test_connection_add,
+              fixture_tear_down);
+
+  g_test_add ("/network-panel-wired/connection-add-activate",
+              NetworkPanelFixture,
+              NULL,
+              fixture_set_up_wired,
+              test_connection_add_activate,
+              fixture_tear_down);
+
+  g_test_add ("/network-panel-wired/connection-multi-add-activate",
+              NetworkPanelFixture,
+              NULL,
+              fixture_set_up_wired,
+              test_connection_multi_add_activate,
+              fixture_tear_down);
+
+  return g_test_run ();
+}
+
diff --git a/tests/network/test-network-panel.py b/tests/network/test-network-panel.py
new file mode 100644
index 000000000..f91958a7a
--- /dev/null
+++ b/tests/network/test-network-panel.py
@@ -0,0 +1,44 @@
+#!/usr/bin/python3
+# Copyright © 2018 Red Hat, Inc
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# Authors: Benjamin Berg <bberg redhat com>
+
+import os
+import sys
+import unittest
+
+try:
+    import dbusmock
+except ImportError:
+    sys.stderr.write('You need python-dbusmock (http://pypi.python.org/pypi/python-dbusmock) for this test 
suite.\n')
+    sys.exit(1)
+
+# Add the shared directory to the search path
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'shared'))
+
+from gtest import GTest
+from x11session import X11SessionTestCase
+
+BUILDDIR = os.environ.get('BUILDDIR', os.path.join(os.path.dirname(__file__)))
+
+
+class PanelTestCase(X11SessionTestCase, GTest):
+    g_test_exe = os.path.join(BUILDDIR, 'test-network-panel')
+
+
+if __name__ == '__main__':
+    # avoid writing to stderr
+    unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]