diff --git a/bumblebee_status/modules/contrib/network.py b/bumblebee_status/modules/contrib/network.py index 26c7889..6c35ee4 100644 --- a/bumblebee_status/modules/contrib/network.py +++ b/bumblebee_status/modules/contrib/network.py @@ -1,8 +1,6 @@ """ A module to show currently active network connection (ethernet or wifi) and connection strength if the connection is wireless. -Dependencies: nm-connection-editor if users would like a graphical -network manager when left-clicking the module """ @@ -18,7 +16,7 @@ import socket class Module(core.module.Module): - @core.decorators.every(seconds=10) + @core.decorators.every(seconds=5) def __init__(self, config, theme): super().__init__(config, theme, core.widget.Widget(self.network)) self.__is_wireless = False @@ -33,20 +31,26 @@ class Module(core.module.Module): try: socket.create_connection(("1.1.1.1", 53)) self.__is_connected = True - except: + except Exception: self.__is_connected = False # Attempt to extract a valid network interface device - self.__interface = netifaces.gateways()["default"][netifaces.AF_INET][1] + try: + self.__interface = netifaces.gateways()["default"][netifaces.AF_INET][1] + except Exception: + self.__interface = None # Check to see if the interface (if connected to the internet) is wireless - if self.__is_connected: - with open("/proc/net/wireless", "r") as f: - self.__is_wireless = self.__interface in f.read() - f.close() + if self.__is_connected and self.__interface: + try: + with open("/proc/net/wireless", "r") as f: + self.__is_wireless = self.__interface in f.read() + f.close() + except Exception: + self.__is_wireless = False # setup message to send to the user - if not self.__is_connected: + if not self.__is_connected or not self.__interface: self.__message = "No connection" elif not self.__is_wireless: # Assuming that if user is connected via non-wireless means that it will be ethernet @@ -57,7 +61,11 @@ class Module(core.module.Module): iw_dat = util.cli.execute("iwgetid") has_ssid = "ESSID" in iw_dat signal = self.__compute_signal(self.__interface) - self.__signal = util.format.asint(signal, minimum=-110, maximum=-30) + + # If signal is None, that means that we can't compute the default interface's signal strength + self.__signal = ( + util.format.asint(signal, minimum=-110, maximum=-30) if signal else None + ) ssid = ( iw_dat[iw_dat.index(":") + 1 :].replace('"', "").strip() @@ -80,10 +88,12 @@ class Module(core.module.Module): # manually done for better granularity / ease of parsing strength data def __generate_wireles_message(self, ssid, signal): computed_strength = self.__compute_strength(signal) - return "{} {}%".format(ssid, int(computed_strength)) + strength_str = str(computed_strength) if computed_strength else "?" + + return "{} {}%".format(ssid, strength_str) def __compute_strength(self, signal): - return 100 * ((signal + 100) / 70.0) + return int(100 * ((signal + 100) / 70.0)) if signal else None # get signal strength in decibels/milliwat def __compute_signal(self, interface): @@ -91,6 +101,11 @@ class Module(core.module.Module): cmd = "iwconfig {}".format(interface) config_dat = " ".join(util.cli.execute(cmd).split()) config_tokens = config_dat.replace("=", " ").split() - signal = config_tokens[config_tokens.index("level") + 1] + + # handle weird output + try: + signal = config_tokens[config_tokens.index("level") + 1] + except Exception: + signal = None return signal diff --git a/tests/modules/contrib/test_network.py b/tests/modules/contrib/test_network.py index e171d69..9b270bf 100644 --- a/tests/modules/contrib/test_network.py +++ b/tests/modules/contrib/test_network.py @@ -1,65 +1,107 @@ -import pytest from unittest import TestCase, mock +import pytest import core.config import core.widget import modules.contrib.network +import socket + +pytest.importorskip("netifaces") + + def build_module(): config = core.config.Config([]) return modules.contrib.network.Module(config=config, theme=None) + def wireless_default(): - return { - "default": { - 1: ('10.0.1.12', 'wlan0') - } - } + return {"default": {1: ("10.0.1.12", "wlan3")}} + def wired_default(): - return { - 'default': { - 18: ('10.0.1.12', 'eth0') - } - } + return {"default": {18: ("10.0.1.12", "eth3")}} -def exec_side_effect(*args, **kwargs): + +def exec_side_effect_valid(*args, **kwargs): if args[0] == "iwgetid": return "ESSID: bumblefoo" - elif "iwconfig" in args[0]: + if "iwconfig" in args[0]: return "level=-30" + return mock.DEFAULT - return "default" + +def exec_side_effect_invalid(*args, **kwargs): + return "invalid gibberish, can't parse for info" class TestNetworkUnit(TestCase): def test_load_module(self): __import__("modules.contrib.network") - @pytest.mark.allow_hosts(['127.0.0.1']) + @pytest.mark.allow_hosts(["127.0.0.1"]) def test_no_internet(self): module = build_module() assert module.widgets()[0].full_text() == "No connection" - @mock.patch('util.cli.execute') - @mock.patch('netifaces.gateways') - @mock.patch('netifaces.AF_INET', 1) - def test_wireless_connection(self, gateways_mock, execute_mock): + @mock.patch("util.cli.execute") + @mock.patch("netifaces.gateways") + @mock.patch("socket.create_connection") + @mock.patch("netifaces.AF_INET", 1) + @mock.patch("builtins.open", mock.mock_open(read_data="wlan3")) + def test_valid_wireless_connection(self, socket_mock, gateways_mock, execute_mock): + socket_mock.return_value = mock.MagicMock() fake_ssid = "bumblefoo" gateways_mock.return_value = wireless_default() - execute_mock.side_effect = exec_side_effect + execute_mock.side_effect = exec_side_effect_valid module = build_module() assert fake_ssid in module.widgets()[0].full_text() - @mock.patch('util.cli.execute') - @mock.patch('netifaces.gateways') - @mock.patch('netifaces.AF_INET', 18) - def test_wired_connection(self, gateways_mock, execute_mock): + @mock.patch("netifaces.gateways") + @mock.patch("socket.create_connection") + @mock.patch("netifaces.AF_INET", 18) + @mock.patch("builtins.open", mock.mock_open(read_data="wlan3")) + def test_valid_wired_connection(self, socket_mock, gateways_mock): gateways_mock.return_value = wired_default() - execute_mock.side_effect = exec_side_effect + socket_mock.return_value = mock.MagicMock() module = build_module() assert module.widgets()[0].full_text() == "Ethernet" + + @mock.patch("netifaces.gateways") + @mock.patch("socket.create_connection") + def test_invalid_gateways(self, socket_mock, gateways_mock): + socket_mock.return_value = mock.Mock() + gateways_mock.return_value = {"xyz": "abc"} + + module = build_module() + assert module.widgets()[0].full_text() == "No connection" + + @mock.patch("util.cli.execute") + @mock.patch("socket.create_connection") + @mock.patch("netifaces.gateways") + @mock.patch("netifaces.AF_INET", 1) + @mock.patch("builtins.open", mock.mock_open(read_data="wlan3")) + def test_invalid_execs(self, gateways_mock, socket_mock, execute_mock): + execute_mock.side_effect = exec_side_effect_invalid + socket_mock.return_value = mock.MagicMock() + gateways_mock.return_value = wireless_default() + + module = build_module() + + assert module.widgets()[0].full_text() == "Unknown ?%" + + @mock.patch("builtins.open", **{"return_value.raiseError.side_effect": Exception()}) + @mock.patch("socket.create_connection") + @mock.patch("netifaces.gateways") + @mock.patch("netifaces.AF_INET", 18) + @mock.patch("builtins.open", mock.mock_open(read_data="wlan3")) + def test_no_wireless_file(self, gateways_mock, socket_mock, mock_open): + gateways_mock.return_value = wired_default() + socket_mock.return_value = mock.MagicMock() + module = build_module() + + assert module.widgets()[0].full_text() == "Ethernet"