From 6dbe440cb50c6c94b48ef936d1106cd95e7a4212 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sat, 4 Mar 2017 11:25:52 +0100 Subject: [PATCH 01/33] [tests] Purge tests and start with a clean implementation of subprocess Seems like subprocess and friends (Popen, communicate) are not so easy to mock cleanly. Therefore, start from scratch and carefully write test by test, until (at least) the old test coverage has been restored. --- bumblebee/util.py | 2 + tests/__init__.py | 0 tests/mocks.py | 27 ++++++ tests/modules/__init__.py | 0 tests/modules/test_battery.py | 68 ------------- tests/modules/test_brightness.py | 56 ----------- tests/modules/test_caffeine.py | 21 ---- tests/modules/test_cmus.py | 58 ----------- tests/modules/test_cpu.py | 48 --------- tests/modules/test_disk.py | 56 ----------- tests/modules/test_load.py | 47 --------- tests/modules/test_memory.py | 47 --------- tests/modules/test_modules.py | 57 ----------- tests/modules/test_pulseaudio.py | 56 ----------- tests/test_config.py | 52 ---------- tests/test_engine.py | 83 ---------------- tests/test_i3barinput.py | 129 ------------------------ tests/test_i3baroutput.py | 104 -------------------- tests/test_module.py | 50 ---------- tests/test_store.py | 24 ----- tests/test_theme.py | 115 ---------------------- tests/test_util.py | 39 ++++++-- tests/util.py | 162 ------------------------------- 23 files changed, 61 insertions(+), 1240 deletions(-) delete mode 100644 tests/__init__.py create mode 100644 tests/mocks.py delete mode 100644 tests/modules/__init__.py delete mode 100644 tests/modules/test_battery.py delete mode 100644 tests/modules/test_brightness.py delete mode 100644 tests/modules/test_caffeine.py delete mode 100644 tests/modules/test_cmus.py delete mode 100644 tests/modules/test_cpu.py delete mode 100644 tests/modules/test_disk.py delete mode 100644 tests/modules/test_load.py delete mode 100644 tests/modules/test_memory.py delete mode 100644 tests/modules/test_modules.py delete mode 100644 tests/modules/test_pulseaudio.py delete mode 100644 tests/test_config.py delete mode 100644 tests/test_engine.py delete mode 100644 tests/test_i3barinput.py delete mode 100644 tests/test_i3baroutput.py delete mode 100644 tests/test_module.py delete mode 100644 tests/test_store.py delete mode 100644 tests/test_theme.py delete mode 100644 tests/util.py diff --git a/bumblebee/util.py b/bumblebee/util.py index 730ed87..07f8091 100644 --- a/bumblebee/util.py +++ b/bumblebee/util.py @@ -15,6 +15,8 @@ def execute(cmd, wait=True): out, _ = proc.communicate() if proc.returncode != 0: raise RuntimeError("{} exited with {}".format(cmd, proc.returncode)) + if type(out) == str: + return out return out.decode("utf-8") return None diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/mocks.py b/tests/mocks.py new file mode 100644 index 0000000..f60a9ee --- /dev/null +++ b/tests/mocks.py @@ -0,0 +1,27 @@ +# pylint: disable=C0103,C0111 + +import mock +import shlex +import subprocess + +class MockPopen(object): + def __init__(self, module): + self._patch = mock.patch("{}.subprocess.Popen".format(module)) + self._popen = self._patch.start() + self.mock = mock.Mock() + # for a nicer, more uniform interface + self.mock.popen = self._popen + # for easier command execution checks + self.mock.popen.assert_call = self.assert_call + self._popen.return_value = self.mock + + self.mock.communicate.return_value = [ "", None ] + self.mock.returncode = 0 + + def assert_call(self, cmd): + self.mock.popen.assert_called_with(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + def cleanup(self): + self._patch.stop() + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/__init__.py b/tests/modules/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/modules/test_battery.py b/tests/modules/test_battery.py deleted file mode 100644 index 499a0fa..0000000 --- a/tests/modules/test_battery.py +++ /dev/null @@ -1,68 +0,0 @@ -# pylint: disable=C0103,C0111 - -import sys -import json -import unittest -import mock - -from contextlib import contextmanager - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.battery import Module -from tests.util import MockEngine, MockConfig, assertPopen - -class MockOpen(object): - def __init__(self): - self._value = "" - - def returns(self, value): - self._value = value - - def __enter__(self): - return self - - def __exit__(self, a, b, c): - pass - - def read(self): - return self._value - -class TestBatteryModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.config = MockConfig() - self.module = Module(engine=self.engine, config={ "config": self.config }) - for widget in self.module.widgets(): - widget.link_module(self.module) - - @mock.patch("sys.stdout") - def test_format(self, mock_output): - for widget in self.module.widgets(): - self.assertEquals(len(widget.full_text()), len("100%")) - - @mock.patch("os.path.exists") - @mock.patch("{}.open".format("__builtin__" if sys.version_info[0] < 3 else "builtins")) - @mock.patch("subprocess.Popen") - def test_critical(self, mock_output, mock_open, mock_exists): - mock_open.return_value = MockOpen() - mock_open.return_value.returns("19") - mock_exists.return_value = True - self.config.set("battery.critical", "20") - self.config.set("battery.warning", "25") - self.module.update(self.module.widgets()) - self.assertTrue("critical" in self.module.widgets()[0].state()) - - @mock.patch("os.path.exists") - @mock.patch("{}.open".format("__builtin__" if sys.version_info[0] < 3 else "builtins")) - @mock.patch("subprocess.Popen") - def test_warning(self, mock_output, mock_open, mock_exists): - mock_open.return_value = MockOpen() - mock_exists.return_value = True - mock_open.return_value.returns("22") - self.config.set("battery.critical", "20") - self.config.set("battery.warning", "25") - self.module.update(self.module.widgets()) - self.assertTrue("warning" in self.module.widgets()[0].state()) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_brightness.py b/tests/modules/test_brightness.py deleted file mode 100644 index b14af4b..0000000 --- a/tests/modules/test_brightness.py +++ /dev/null @@ -1,56 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.brightness import Module -from tests.util import MockEngine, MockConfig, assertPopen, assertMouseEvent - -class TestBrightnessModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.engine.input = I3BarInput() - self.engine.input.need_event = True - self.config = MockConfig() - self.module = Module(engine=self.engine, config={ "config": self.config }) - for widget in self.module.widgets(): - widget.link_module(self.module) - - @mock.patch("sys.stdout") - def test_format(self, mock_output): - for widget in self.module.widgets(): - self.assertEquals(len(widget.full_text()), len("100%")) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_wheel_up(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.WHEEL_UP, - "xbacklight +2%" - ) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_wheel_down(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.WHEEL_DOWN, - "xbacklight -2%" - ) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_custom_step(self, mock_input, mock_output, mock_select): - self.config.set("brightness.step", "10") - module = Module(engine=self.engine, config={ "config": self.config }) - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - module, bumblebee.input.WHEEL_DOWN, - "xbacklight -10%" - ) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_caffeine.py b/tests/modules/test_caffeine.py deleted file mode 100644 index 995eece..0000000 --- a/tests/modules/test_caffeine.py +++ /dev/null @@ -1,21 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.caffeine import Module -from tests.util import MockEngine, MockConfig, assertPopen - -class TestCaffeineModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.engine.input = I3BarInput() - self.engine.input.need_event = True - self.engine.input.need_valid_event = True - self.config = MockConfig() - self.module = Module(engine=self.engine, config={ "config": self.config }) - for widget in self.module.widgets(): - widget.link_module(self.module) diff --git a/tests/modules/test_cmus.py b/tests/modules/test_cmus.py deleted file mode 100644 index 58be4bc..0000000 --- a/tests/modules/test_cmus.py +++ /dev/null @@ -1,58 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.cmus import Module -from tests.util import MockEngine, MockConfig, assertPopen, MockEpoll - -class TestCmusModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.engine.input = I3BarInput() - self.engine.input.need_event = True - self.module = Module(engine=self.engine, config={"config": MockConfig()}) - - @mock.patch("subprocess.Popen") - def test_read_song(self, mock_output): - rv = mock.Mock() - rv.configure_mock(**{ - "communicate.return_value": ("out", None) - }) - mock_output.return_value = rv - self.module.update(self.module.widgets()) - assertPopen(mock_output, "cmus-remote -Q") - - def test_widgets(self): - self.assertTrue(len(self.module.widgets()), 5) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_interaction(self, mock_input, mock_output, mock_select): - events = [ - {"widget": "cmus.shuffle", "action": "cmus-remote -S"}, - {"widget": "cmus.repeat", "action": "cmus-remote -R"}, - {"widget": "cmus.next", "action": "cmus-remote -n"}, - {"widget": "cmus.prev", "action": "cmus-remote -r"}, - {"widget": "cmus.main", "action": "cmus-remote -u"}, - ] - - mock_input.fileno.return_value = 1 - mock_select.return_value = MockEpoll() - - for event in events: - mock_input.readline.return_value = json.dumps({ - "name": self.module.id, - "button": bumblebee.input.LEFT_MOUSE, - "instance": self.module.widget(event["widget"]).id - }) - self.engine.input.start() - self.engine.input.stop() - mock_input.readline.assert_any_call() - assertPopen(mock_output, event["action"]) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_cpu.py b/tests/modules/test_cpu.py deleted file mode 100644 index fea056e..0000000 --- a/tests/modules/test_cpu.py +++ /dev/null @@ -1,48 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.cpu import Module -from tests.util import MockEngine, MockConfig, assertPopen, assertMouseEvent, assertStateContains - -class TestCPUModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.engine.input = I3BarInput() - self.engine.input.need_event = True - self.config = MockConfig() - self.module = Module(engine=self.engine, config={ "config": self.config }) - - @mock.patch("sys.stdout") - def test_format(self, mock_output): - for widget in self.module.widgets(): - self.assertEquals(len(widget.full_text()), len("100.00%")) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_leftclick(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.LEFT_MOUSE, - "gnome-system-monitor" - ) - - @mock.patch("psutil.cpu_percent") - def test_warning(self, mock_psutil): - self.config.set("cpu.critical", "20") - self.config.set("cpu.warning", "18") - mock_psutil.return_value = 19.0 - assertStateContains(self, self.module, "warning") - - @mock.patch("psutil.cpu_percent") - def test_critical(self, mock_psutil): - self.config.set("cpu.critical", "20") - self.config.set("cpu.warning", "19") - mock_psutil.return_value = 21.0 - assertStateContains(self, self.module, "critical") - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_disk.py b/tests/modules/test_disk.py deleted file mode 100644 index 423612e..0000000 --- a/tests/modules/test_disk.py +++ /dev/null @@ -1,56 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.disk import Module -from tests.util import MockEngine, MockConfig, assertPopen, assertStateContains, MockEpoll - -class MockVFS(object): - def __init__(self, perc): - self.f_blocks = 1024*1024 - self.f_frsize = 1 - self.f_bavail = self.f_blocks - self.f_blocks*(perc/100.0) - -class TestDiskModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.engine.input = I3BarInput() - self.engine.input.need_event = True - self.config = MockConfig() - self.config.set("disk.path", "somepath") - self.module = Module(engine=self.engine, config={"config": self.config}) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_leftclick(self, mock_input, mock_output, mock_select): - mock_input.readline.return_value = json.dumps({ - "name": self.module.id, - "button": bumblebee.input.LEFT_MOUSE, - "instance": None - }) - mock_select.return_value = MockEpoll() - self.engine.input.start() - self.engine.input.stop() - mock_input.readline.assert_any_call() - assertPopen(mock_output, "nautilus {}".format(self.module.parameter("path"))) - - @mock.patch("os.statvfs") - def test_warning(self, mock_stat): - self.config.set("disk.critical", "80") - self.config.set("disk.warning", "70") - mock_stat.return_value = MockVFS(75.0) - assertStateContains(self, self.module, "warning") - - @mock.patch("os.statvfs") - def test_critical(self, mock_stat): - self.config.set("disk.critical", "80") - self.config.set("disk.warning", "70") - mock_stat.return_value = MockVFS(85.0) - assertStateContains(self, self.module, "critical") - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_load.py b/tests/modules/test_load.py deleted file mode 100644 index e4ed353..0000000 --- a/tests/modules/test_load.py +++ /dev/null @@ -1,47 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.load import Module -from tests.util import MockEngine, MockConfig, assertStateContains, assertMouseEvent - -class TestLoadModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.engine.input = I3BarInput() - self.engine.input.need_event = True - self.config = MockConfig() - self.module = Module(engine=self.engine, config={ "config": self.config }) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_leftclick(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.LEFT_MOUSE, - "gnome-system-monitor" - ) - - @mock.patch("multiprocessing.cpu_count") - @mock.patch("os.getloadavg") - def test_warning(self, mock_loadavg, mock_cpucount): - self.config.set("load.critical", "1") - self.config.set("load.warning", "0.8") - mock_cpucount.return_value = 1 - mock_loadavg.return_value = [ 0.9, 0, 0 ] - assertStateContains(self, self.module, "warning") - - @mock.patch("multiprocessing.cpu_count") - @mock.patch("os.getloadavg") - def test_critical(self, mock_loadavg, mock_cpucount): - self.config.set("load.critical", "1") - self.config.set("load.warning", "0.8") - mock_cpucount.return_value = 1 - mock_loadavg.return_value = [ 1.1, 0, 0 ] - assertStateContains(self, self.module, "critical") - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_memory.py b/tests/modules/test_memory.py deleted file mode 100644 index b32205d..0000000 --- a/tests/modules/test_memory.py +++ /dev/null @@ -1,47 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.memory import Module -from tests.util import MockEngine, MockConfig, assertPopen, assertMouseEvent, assertStateContains - -class VirtualMemory(object): - def __init__(self, percent): - self.percent = percent - -class TestMemoryModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.engine.input = I3BarInput() - self.engine.input.need_event = True - self.config = MockConfig() - self.module = Module(engine=self.engine, config={ "config": self.config }) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_leftclick(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.LEFT_MOUSE, - "gnome-system-monitor" - ) - - @mock.patch("psutil.virtual_memory") - def test_warning(self, mock_vmem): - self.config.set("memory.critical", "80") - self.config.set("memory.warning", "70") - mock_vmem.return_value = VirtualMemory(75) - assertStateContains(self, self.module, "warning") - - @mock.patch("psutil.virtual_memory") - def test_critical(self, mock_vmem): - self.config.set("memory.critical", "80") - self.config.set("memory.warning", "70") - mock_vmem.return_value = VirtualMemory(85) - assertStateContains(self, self.module, "critical") - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_modules.py b/tests/modules/test_modules.py deleted file mode 100644 index 59db4fa..0000000 --- a/tests/modules/test_modules.py +++ /dev/null @@ -1,57 +0,0 @@ -# pylint: disable=C0103,C0111 - -import unittest -import importlib -import mock - -from bumblebee.engine import all_modules -from bumblebee.config import Config -from tests.util import assertWidgetAttributes, MockEngine - -class MockCommunicate(object): - def __init__(self): - self.returncode = 0 - - def communicate(self): - return (str.encode("1"), "error") - -class TestGenericModules(unittest.TestCase): - @mock.patch("subprocess.Popen") - def setUp(self, mock_output): - mock_output.return_value = MockCommunicate() - engine = MockEngine() - config = Config() - self.objects = {} - for mod in all_modules(): - cls = importlib.import_module("bumblebee.modules.{}".format(mod["name"])) - self.objects[mod["name"]] = getattr(cls, "Module")(engine, {"config": config}) - for widget in self.objects[mod["name"]].widgets(): - self.assertEquals(widget.get("variable", None), None) - - @mock.patch("subprocess.Popen") - def test_widgets(self, mock_output): - mock_output.return_value = MockCommunicate() - for mod in self.objects: - widgets = self.objects[mod].widgets() - for widget in widgets: - widget.link_module(self.objects[mod]) - self.assertEquals(widget.module, mod) - assertWidgetAttributes(self, widget) - widget.set("variable", "value") - self.assertEquals(widget.get("variable", None), "value") - self.assertTrue(isinstance(widget.full_text(), str) or isinstance(widget.full_text(), unicode)) - - @mock.patch("subprocess.Popen") - def test_update(self, mock_output): - mock_output.return_value = MockCommunicate() - rv = mock.Mock() - rv.configure_mock(**{ - "communicate.return_value": ("out", None) - }) - for mod in self.objects: - widgets = self.objects[mod].widgets() - self.objects[mod].update(widgets) - self.test_widgets() - self.assertEquals(widgets, self.objects[mod].widgets()) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_pulseaudio.py b/tests/modules/test_pulseaudio.py deleted file mode 100644 index 5aae761..0000000 --- a/tests/modules/test_pulseaudio.py +++ /dev/null @@ -1,56 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from bumblebee.modules.pulseaudio import Module -from tests.util import MockEngine, MockConfig, assertPopen, assertMouseEvent, assertStateContains - -class TestPulseAudioModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.engine.input = I3BarInput() - self.engine.input.need_event = True - self.config = MockConfig() - self.module = Module(engine=self.engine, config={ "config": self.config }) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_leftclick(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.LEFT_MOUSE, - "pactl set-source-mute @DEFAULT_SOURCE@ toggle" - ) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_rightclick(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.RIGHT_MOUSE, - "pavucontrol" - ) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_wheelup(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.WHEEL_UP, - "pactl set-source-volume @DEFAULT_SOURCE@ +2%" - ) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_wheeldown(self, mock_input, mock_output, mock_select): - assertMouseEvent(mock_input, mock_output, mock_select, self.engine, - self.module, bumblebee.input.WHEEL_DOWN, - "pactl set-source-volume @DEFAULT_SOURCE@ -2%" - ) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_config.py b/tests/test_config.py deleted file mode 100644 index e7a05f9..0000000 --- a/tests/test_config.py +++ /dev/null @@ -1,52 +0,0 @@ -# pylint: disable=C0103,C0111 - -import unittest -import mock -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - -from bumblebee.config import Config -from bumblebee.theme import themes -from bumblebee.engine import all_modules - -class TestConfig(unittest.TestCase): - def setUp(self): - self.defaultConfig = Config() - self.someSimpleModules = ["foo", "bar", "baz"] - self.someAliasModules = ["foo:a", "bar:b", "baz:c"] - - def test_no_modules_by_default(self): - self.assertEquals(self.defaultConfig.modules(), []) - - def test_simple_modules(self): - cfg = Config(["-m"] + self.someSimpleModules) - self.assertEquals(cfg.modules(), [{ - "name": x, "module": x - } for x in self.someSimpleModules]) - - def test_alias_modules(self): - cfg = Config(["-m"] + self.someAliasModules) - self.assertEquals(cfg.modules(), [{ - "module": x.split(":")[0], - "name": x.split(":")[1], - } for x in self.someAliasModules]) - - @mock.patch("sys.stdout", new_callable=StringIO) - @mock.patch("sys.exit") - def test_list_themes(self, exit, stdout): - cfg = Config(["-l", "themes"]) - result = stdout.getvalue() - for theme in themes(): - self.assertTrue(theme in result) - - @mock.patch("sys.stdout", new_callable=StringIO) - @mock.patch("sys.exit") - def test_list_modules(self, exit, stdout): - cfg = Config(["-l", "modules"]) - result = stdout.getvalue() - for module in all_modules(): - self.assertTrue(module["name"] in result) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_engine.py b/tests/test_engine.py deleted file mode 100644 index 0b71fd4..0000000 --- a/tests/test_engine.py +++ /dev/null @@ -1,83 +0,0 @@ -# pylint: disable=C0103,C0111,W0703,W0212 - -import shlex -import unittest - -from bumblebee.error import ModuleLoadError -from bumblebee.engine import Engine -from bumblebee.config import Config -import bumblebee.input - -from tests.util import MockOutput, MockInput - -class TestEngine(unittest.TestCase): - def setUp(self): - self.engine = Engine(config=Config(), output=MockOutput(), inp=MockInput()) - self.singleWidgetModule = [{"module": "test", "name": "a"}] - self.testModule = "test" - self.invalidModule = "no-such-module" - self.testModuleSpec = "bumblebee.modules.{}".format(self.testModule) - self.testModules = [ - {"module": "test", "name": "a"}, - {"module": "test", "name": "b"}, - ] - - def test_stop(self): - self.assertTrue(self.engine.running()) - self.engine.stop() - self.assertFalse(self.engine.running()) - - def test_load_module(self): - module = self.engine._load_module(self.testModule) - self.assertEquals(module.__module__, self.testModuleSpec) - - def test_load_invalid_module(self): - with self.assertRaises(ModuleLoadError): - self.engine._load_module(self.invalidModule) - - def test_load_none(self): - with self.assertRaises(ModuleLoadError): - self.engine._load_module(None) - - def test_load_modules(self): - modules = self.engine.load_modules(self.testModules) - self.assertEquals(len(modules), len(self.testModules)) - self.assertEquals( - [module.__module__ for module in modules], - [self.testModuleSpec for module in modules] - ) - - def test_run(self): - self.engine.load_modules(self.singleWidgetModule) - try: - self.engine.run() - except Exception as e: - self.fail(e) - - def test_custom_cmd(self): - testmodules = [ - { "name": "test", "button": "test.left-click", "action": "echo" }, - { "name": "test:alias", "button": "alias.right-click", "action": "echo2" }, - ] - cmd = "-m" - for test in testmodules: - cmd += " " + test["name"] - cmd += " -p" - for test in testmodules: - cmd += " " + test["button"] + "=" + test["action"] - cfg = Config(shlex.split(cmd)) - inp = MockInput() - engine = Engine(config=cfg, output=MockOutput(), inp=inp) - - i = 0 - for module in engine.modules(): - callback = inp.get_callback(module.id) - self.assertTrue(callback is not None) - self.assertEquals(callback["command"], testmodules[i]["action"]) - if "left" in testmodules[i]["button"]: - self.assertTrue(callback["button"], bumblebee.input.LEFT_MOUSE) - if "right" in testmodules[i]["button"]: - self.assertTrue(callback["button"], bumblebee.input.RIGHT_MOUSE) - i += 1 - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_i3barinput.py b/tests/test_i3barinput.py deleted file mode 100644 index ca9892f..0000000 --- a/tests/test_i3barinput.py +++ /dev/null @@ -1,129 +0,0 @@ -# pylint: disable=C0103,C0111 - -import unittest -import json -import subprocess -import mock - -import bumblebee.input -from bumblebee.input import I3BarInput -from tests.util import MockWidget, MockModule, assertPopen, assertMouseEvent, MockEpoll - -class TestI3BarInput(unittest.TestCase): - def setUp(self): - self.input = I3BarInput() - self.input.need_event = True - self.anyModule = MockModule() - self.anyWidget = MockWidget("test") - self.anyModule.id = "test-module" - self._called = 0 - - def callback(self, event): - self._called += 1 - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_basic_read_event(self, mock_input, mock_select): - mock_input.readline.return_value = "somedata" - mock_input.fileno.return_value = 1 - mock_select.return_value = MockEpoll() - self.input.start() - self.input.stop() - mock_input.readline.assert_any_call() - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_ignore_invalid_data(self, mock_input, mock_select): - mock_select.return_value = MockEpoll() - mock_input.readline.return_value = "garbage" - self.input.start() - self.assertEquals(self.input.alive(), True) - self.assertEquals(self.input.stop(), True) - mock_input.readline.assert_any_call() - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_ignore_invalid_event(self, mock_input, mock_select): - mock_select.return_value = MockEpoll() - mock_input.readline.return_value = json.dumps({ - "name": None, - "instance": None, - "button": 1, - }) - self.input.start() - self.assertEquals(self.input.alive(), True) - self.assertEquals(self.input.stop(), True) - mock_input.readline.assert_any_call() - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_ignore_partial_event(self, mock_input, mock_select): - mock_select.return_value = MockEpoll() - self.input.register_callback(None, button=1, cmd=self.callback) - mock_input.readline.return_value = json.dumps({ - "button": 1, - }) - self.input.start() - self.assertEquals(self.input.alive(), True) - self.assertEquals(self.input.stop(), True) - mock_input.readline.assert_any_call() - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_global_callback(self, mock_input, mock_select): - self.input.register_callback(None, button=1, cmd=self.callback) - assertMouseEvent(mock_input, None, mock_select, self, None, - bumblebee.input.LEFT_MOUSE, None, "someinstance") - self.assertTrue(self._called > 0) - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_remove_global_callback(self, mock_input, mock_select): - self.input.register_callback(None, button=1, cmd=self.callback) - self.input.deregister_callbacks(None) - assertMouseEvent(mock_input, None, mock_select, self, None, - bumblebee.input.LEFT_MOUSE, None, "someinstance") - self.assertTrue(self._called == 0) - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_global_callback_button_missmatch(self, mock_input, mock_select): - self.input.register_callback(self.anyModule, button=1, cmd=self.callback) - assertMouseEvent(mock_input, None, mock_select, self, None, - bumblebee.input.RIGHT_MOUSE, None, "someinstance") - self.assertTrue(self._called == 0) - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_module_callback(self, mock_input, mock_select): - self.input.register_callback(self.anyModule, button=1, cmd=self.callback) - assertMouseEvent(mock_input, None, mock_select, self, self.anyModule, - bumblebee.input.LEFT_MOUSE, None) - self.assertTrue(self._called > 0) - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_remove_module_callback(self, mock_input, mock_select): - self.input.register_callback(self.anyModule, button=1, cmd=self.callback) - self.input.deregister_callbacks(self.anyModule) - assertMouseEvent(mock_input, None, mock_select, self, None, - bumblebee.input.LEFT_MOUSE, None, self.anyWidget.id) - self.assertTrue(self._called == 0) - - @mock.patch("select.epoll") - @mock.patch("sys.stdin") - def test_widget_callback(self, mock_input, mock_select): - self.input.register_callback(self.anyWidget, button=1, cmd=self.callback) - assertMouseEvent(mock_input, None, mock_select, self, None, - bumblebee.input.LEFT_MOUSE, None, self.anyWidget.id) - self.assertTrue(self._called > 0) - - @mock.patch("select.epoll") - @mock.patch("subprocess.Popen") - @mock.patch("sys.stdin") - def test_widget_cmd_callback(self, mock_input, mock_output, mock_select): - self.input.register_callback(self.anyWidget, button=1, cmd="echo") - assertMouseEvent(mock_input, mock_output, mock_select, self, None, - bumblebee.input.LEFT_MOUSE, "echo", self.anyWidget.id) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_i3baroutput.py b/tests/test_i3baroutput.py deleted file mode 100644 index f4c4cca..0000000 --- a/tests/test_i3baroutput.py +++ /dev/null @@ -1,104 +0,0 @@ -# pylint: disable=C0103,C0111 - -import json -import unittest -import mock -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - -from bumblebee.output import I3BarOutput -from tests.util import MockWidget, MockTheme, MockModule - -class TestI3BarOutput(unittest.TestCase): - def setUp(self): - self.theme = MockTheme() - self.output = I3BarOutput(self.theme) - self.expectedStart = json.dumps({"version": 1, "click_events": True}) + "[\n" - self.expectedStop = "]\n" - self.someWidget = MockWidget("foo bar baz") - self.anyModule = MockModule(None, None) - self.anyColor = "#ababab" - self.anotherColor = "#cccccc" - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_start(self, stdout): - self.output.start() - self.assertEquals(self.expectedStart, stdout.getvalue()) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_stop(self, stdout): - self.output.stop() - self.assertEquals(self.expectedStop, stdout.getvalue()) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_draw_single_widget(self, stdout): - self.output.draw(self.someWidget, self.anyModule) - self.output.flush() - result = json.loads(stdout.getvalue())[0] - self.assertEquals(result["full_text"], self.someWidget.full_text()) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_draw_multiple_widgets(self, stdout): - for widget in [self.someWidget, self.someWidget]: - self.output.draw(widget, self.anyModule) - self.output.flush() - result = json.loads(stdout.getvalue()) - for res in result: - self.assertEquals(res["full_text"], self.someWidget.full_text()) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_begin(self, stdout): - self.output.begin() - self.assertEquals("", stdout.getvalue()) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_end(self, stdout): - self.output.end() - self.assertEquals(",\n", stdout.getvalue()) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_prefix(self, stdout): - self.theme.attr_prefix = " - " - self.output.draw(self.someWidget, self.anyModule) - self.output.flush() - result = json.loads(stdout.getvalue())[0] - self.assertEquals(result["full_text"], "{}{}".format( - self.theme.prefix(self.someWidget), self.someWidget.full_text()) - ) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_suffix(self, stdout): - self.theme.attr_suffix = " - " - self.output.draw(self.someWidget, self.anyModule) - self.output.flush() - result = json.loads(stdout.getvalue())[0] - self.assertEquals(result["full_text"], "{}{}".format( - self.someWidget.full_text(), self.theme.suffix(self.someWidget)) - ) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_bothfix(self, stdout): - self.theme.attr_suffix = " - " - self.theme.attr_prefix = " * " - self.output.draw(self.someWidget, self.anyModule) - self.output.flush() - result = json.loads(stdout.getvalue())[0] - self.assertEquals(result["full_text"], "{}{}{}".format( - self.theme.prefix(self.someWidget), - self.someWidget.full_text(), - self.theme.suffix(self.someWidget) - )) - - @mock.patch("sys.stdout", new_callable=StringIO) - def test_colors(self, stdout): - self.theme.attr_fg = self.anyColor - self.theme.attr_bg = self.anotherColor - self.output.draw(self.someWidget, self.anyModule) - self.output.flush() - result = json.loads(stdout.getvalue())[0] - self.assertEquals(result["color"], self.anyColor) - self.assertEquals(result["background"], self.anotherColor) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_module.py b/tests/test_module.py deleted file mode 100644 index 0cc95ad..0000000 --- a/tests/test_module.py +++ /dev/null @@ -1,50 +0,0 @@ -# pylint: disable=C0103,C0111,W0703 - -import unittest - -from bumblebee.engine import Module -from bumblebee.config import Config -from tests.util import MockWidget - -class TestModule(unittest.TestCase): - def setUp(self): - self.widget = MockWidget("foo") - self.config = Config() - self.moduleWithoutWidgets = Module(engine=None, widgets=None) - self.moduleWithOneWidget = Module(engine=None, widgets=self.widget) - self.moduleWithMultipleWidgets = Module(engine=None, - widgets=[self.widget, self.widget, self.widget] - ) - - self.anyConfigName = "cfg" - self.anotherConfigName = "cfg2" - self.anyModule = Module(engine=None, widgets=self.widget, config={ - "name": self.anyConfigName, "config": self.config - }) - self.anotherModule = Module(engine=None, widgets=self.widget, config={ - "name": self.anotherConfigName, "config": self.config - }) - self.anyKey = "some-parameter" - self.anyValue = "value" - self.anotherValue = "another-value" - self.emptyKey = "i-do-not-exist" - self.config.set("{}.{}".format(self.anyConfigName, self.anyKey), self.anyValue) - self.config.set("{}.{}".format(self.anotherConfigName, self.anyKey), self.anotherValue) - - def test_empty_widgets(self): - self.assertEquals(self.moduleWithoutWidgets.widgets(), []) - - def test_single_widget(self): - self.assertEquals(self.moduleWithOneWidget.widgets(), [self.widget]) - - def test_multiple_widgets(self): - for widget in self.moduleWithMultipleWidgets.widgets(): - self.assertEquals(widget, self.widget) - - def test_parameters(self): - self.assertEquals(self.anyModule.parameter(self.anyKey), self.anyValue) - self.assertEquals(self.anotherModule.parameter(self.anyKey), self.anotherValue) - - def test_default_parameters(self): - self.assertEquals(self.anyModule.parameter(self.emptyKey), None) - self.assertEquals(self.anyModule.parameter(self.emptyKey, self.anyValue), self.anyValue) diff --git a/tests/test_store.py b/tests/test_store.py deleted file mode 100644 index 0b712f0..0000000 --- a/tests/test_store.py +++ /dev/null @@ -1,24 +0,0 @@ -# pylint: disable=C0103,C0111,W0703 - -import unittest - -from bumblebee.store import Store - -class TestStore(unittest.TestCase): - def setUp(self): - self.store = Store() - self.anyKey = "some-key" - self.anyValue = "some-value" - self.unsetKey = "invalid-key" - - def test_set_value(self): - self.store.set(self.anyKey, self.anyValue) - self.assertEquals(self.store.get(self.anyKey), self.anyValue) - - def test_get_invalid_value(self): - result = self.store.get(self.unsetKey) - self.assertEquals(result, None) - - def test_get_invalid_with_default_value(self): - result = self.store.get(self.unsetKey, self.anyValue) - self.assertEquals(result, self.anyValue) diff --git a/tests/test_theme.py b/tests/test_theme.py deleted file mode 100644 index ef356ca..0000000 --- a/tests/test_theme.py +++ /dev/null @@ -1,115 +0,0 @@ -# pylint: disable=C0103,C0111,W0703 - -import unittest -from bumblebee.theme import Theme -from bumblebee.error import ThemeLoadError -from tests.util import MockWidget - -class TestTheme(unittest.TestCase): - def setUp(self): - self.nonexistentThemeName = "no-such-theme" - self.invalidThemeName = "test_invalid" - self.validThemeName = "test" - self.themedWidget = MockWidget("bla") - self.theme = Theme(self.validThemeName) - self.cycleTheme = Theme("test_cycle") - self.anyWidget = MockWidget("bla") - self.anotherWidget = MockWidget("blub") - - data = self.theme.data() - self.widgetTheme = "test-widget" - self.themedWidget.module = self.widgetTheme - self.defaultColor = data["defaults"]["fg"] - self.defaultBgColor = data["defaults"]["bg"] - self.widgetColor = data[self.widgetTheme]["fg"] - self.widgetBgColor = data[self.widgetTheme]["bg"] - self.defaultPrefix = data["defaults"]["prefix"] - self.defaultSuffix = data["defaults"]["suffix"] - self.widgetPrefix = data[self.widgetTheme]["prefix"] - self.widgetSuffix = data[self.widgetTheme]["suffix"] - - def test_load_valid_theme(self): - try: - Theme(self.validThemeName) - except Exception as e: - self.fail(e) - - def test_load_nonexistent_theme(self): - with self.assertRaises(ThemeLoadError): - Theme(self.nonexistentThemeName) - - def test_load_invalid_theme(self): - with self.assertRaises(ThemeLoadError): - Theme(self.invalidThemeName) - - def test_default_prefix(self): - self.assertEquals(self.theme.prefix(self.anyWidget), self.defaultPrefix) - - def test_default_suffix(self): - self.assertEquals(self.theme.suffix(self.anyWidget), self.defaultSuffix) - - def test_widget_prefix(self): - self.assertEquals(self.theme.prefix(self.themedWidget), self.widgetPrefix) - - def test_widget_fg(self): - self.assertEquals(self.theme.fg(self.anyWidget), self.defaultColor) - self.anyWidget.module = self.widgetTheme - self.assertEquals(self.theme.fg(self.anyWidget), self.widgetColor) - - def test_widget_bg(self): - self.assertEquals(self.theme.bg(self.anyWidget), self.defaultBgColor) - self.anyWidget.module = self.widgetTheme - self.assertEquals(self.theme.bg(self.anyWidget), self.widgetBgColor) - - def test_absent_cycle(self): - theme = self.theme - try: - theme.fg(self.anyWidget) - theme.fg(self.anotherWidget) - except Exception as e: - self.fail(e) - - def test_reset(self): - theme = self.cycleTheme - data = theme.data() - theme.reset() - self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"]) - self.assertEquals(theme.fg(self.anotherWidget), data["cycle"][1]["fg"]) - theme.reset() - self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"]) - - def test_separator_block_width(self): - theme = self.theme - data = theme.data() - - self.assertEquals(theme.separator_block_width(self.anyWidget), - data["defaults"]["separator-block-width"] - ) - - def test_separator(self): - for theme in [self.theme, self.cycleTheme]: - theme.reset() - prev_bg = theme.bg(self.anyWidget) - theme.bg(self.anotherWidget) - - self.assertEquals(theme.separator_fg(self.anotherWidget), theme.bg(self.anotherWidget)) - self.assertEquals(theme.separator_bg(self.anotherWidget), prev_bg) - - def test_state(self): - theme = self.theme - data = theme.data() - - self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["fg"]) - self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["bg"]) - - self.anyWidget.attr_state = ["critical"] - self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["critical"]["fg"]) - self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["critical"]["bg"]) - - self.themedWidget.attr_state = ["critical"] - self.assertEquals(theme.fg(self.themedWidget), data[self.widgetTheme]["critical"]["fg"]) - # if elements are missing in the state theme, they are taken from the - # widget theme instead (i.e. no fallback to a more general state theme) - self.assertEquals(theme.bg(self.themedWidget), data[self.widgetTheme]["bg"]) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_util.py b/tests/test_util.py index b9b04c4..0720e34 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,18 +1,27 @@ # pylint: disable=C0103,C0111 +import mock import unittest +import mocks + from bumblebee.util import * class TestUtil(unittest.TestCase): + def setUp(self): + self.popen = mocks.MockPopen("bumblebee.util") + self.some_command_with_args = "sample-command -a -b -c" + self.some_utf8 = "some string".encode("utf-8") + + def tearDown(self): + self.popen.cleanup() + def test_bytefmt(self): - value = 10 - display = 10 - units = [ "B", "KiB", "MiB", "GiB" ] - for unit in units: - self.assertEquals(bytefmt(value), "{:.2f}{}".format(display, unit)) - value *= 1024 - self.assertEquals(bytefmt(value), "{:.2f}GiB".format(display*1024)) + self.assertEquals(bytefmt(10), "10.00B") + self.assertEquals(bytefmt(15*1024), "15.00KiB") + self.assertEquals(bytefmt(20*1024*1024), "20.00MiB") + self.assertEquals(bytefmt(22*1024*1024*1024), "22.00GiB") + self.assertEquals(bytefmt(35*1024*1024*1024*1024), "35840.00GiB") def test_durationfmt(self): self.assertEquals(durationfmt(00), "00:00") @@ -22,4 +31,20 @@ class TestUtil(unittest.TestCase): self.assertEquals(durationfmt(3600), "01:00:00") self.assertEquals(durationfmt(7265), "02:01:05") + def test_execute(self): + execute(self.some_command_with_args) + self.assertTrue(self.popen.mock.popen.called) + self.popen.mock.popen.assert_call(self.some_command_with_args) + self.assertTrue(self.popen.mock.communicate.called) + + def test_execute_utf8(self): + self.popen.mock.communicate.return_value = [ self.some_utf8, None ] + self.test_execute() + + def test_execute_error(self): + self.popen.mock.returncode = 1 + + with self.assertRaises(RuntimeError): + execute(self.some_command_with_args) + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/util.py b/tests/util.py deleted file mode 100644 index c0f244d..0000000 --- a/tests/util.py +++ /dev/null @@ -1,162 +0,0 @@ -# pylint: disable=C0103,C0111,W0613 - -import json -import shlex -import subprocess - -from bumblebee.output import Widget - -def assertWidgetAttributes(test, widget): - test.assertTrue(isinstance(widget, Widget)) - test.assertTrue(hasattr(widget, "full_text")) - -def assertPopen(output, cmd): - res = shlex.split(cmd) - output.assert_any_call(res, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT - ) - -def assertStateContains(test, module, state): - for widget in module.widgets(): - widget.link_module(module) - module.update(module.widgets()) - test.assertTrue(state in module.widgets()[0].state()) - -class MockEpoll(object): - def register(self, fileno, event): - pass - - def poll(self, timeout): - return [(1,2)] - - def unregister(self, fileno): - pass - - def close(self): - pass - -def assertMouseEvent(mock_input, mock_output, mock_select, engine, module, button, cmd, instance_id=None): - mock_input.readline.return_value = json.dumps({ - "name": module.id if module else "test", - "button": button, - "instance": instance_id - }) - mock_input.fileno.return_value = 1 - mock_select.return_value = MockEpoll() - engine.input.start() - engine.input.stop() - mock_input.readline.assert_any_call() - if cmd: - assertPopen(mock_output, cmd) - -class MockInput(object): - def __init__(self): - self._callbacks = {} - def start(self): - pass - - def stop(self): - pass - - def get_callback(self, uid): - return self._callbacks.get(uid, None) - - def register_callback(self, obj, button, cmd): - if not obj: - return - self._callbacks[obj.id] = { - "button": button, - "command": cmd, - } - -class MockEngine(object): - def __init__(self): - self.input = MockInput() - -class MockConfig(object): - def __init__(self): - self._data = {} - - def get(self, name, default): - if name in self._data: - return self._data[name] - return default - - def set(self, name, value): - self._data[name] = value - -class MockOutput(object): - def start(self): - pass - - def stop(self): - pass - - def draw(self, widget, engine, module): - engine.stop() - - def begin(self): - pass - - def flush(self): - pass - - def end(self): - pass - -class MockModule(object): - def __init__(self, engine=None, config=None): - self.id = None - -class MockWidget(Widget): - def __init__(self, text): - super(MockWidget, self).__init__(text) - self._text = text - self.module = None - self.attr_state = ["state-default"] - self.id = "none" - - def state(self): - return self.attr_state - - def update(self, widgets): - pass - - def full_text(self): - return self._text - -class MockTheme(object): - def __init__(self): - self.attr_prefix = None - self.attr_suffix = None - self.attr_fg = None - self.attr_bg = None - self.attr_separator = None - self.attr_separator_block_width = 0 - - def padding(self, widget): - return "" - - def reset(self): - pass - - def separator_block_width(self, widget): - return self.attr_separator_block_width - - def separator(self, widget): - return self.attr_separator - - def prefix(self, widget, default=None): - return self.attr_prefix - - def suffix(self, widget, default=None): - return self.attr_suffix - - def fg(self, widget): - return self.attr_fg - - def bg(self, widget): - return self.attr_bg - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 7be8df222debae9eaa8e357571e75f8ee226ff40 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sat, 4 Mar 2017 11:35:25 +0100 Subject: [PATCH 02/33] [runtests] Clean up coverage report + add it to runtests.sh runtests.sh now also reports coverage. --- .coveragerc | 9 +++++++++ runtests.sh | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.coveragerc b/.coveragerc index f593bd0..d98e676 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,12 @@ [run] omit = tests/* + *mock* + *funcsigs* + *pbr* + *six* + +[report] + exclude_lines = + except ImportError: + pass diff --git a/runtests.sh b/runtests.sh index a267458..60f058e 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,11 +1,11 @@ #!/bin/sh echo "testing with $(python2 -V 2>&1)" -python2 $(which nosetests) --rednose -v tests/ +python2 $(which nosetests) --rednose -v --with-coverage --cover-erase tests/ if [ $? == 0 ]; then echo echo "testing with $(python3 -V 2>&1)" - python3 $(which nosetests-3) --rednose -v tests/ + python3 $(which nosetests-3) --rednose -v --with-coverage --cover-erase tests/ fi From f0013c75811cc481d920b59459470bfaf7971bc4 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sat, 4 Mar 2017 11:35:46 +0100 Subject: [PATCH 03/33] [tests/util] Add test for execute with wait=False --- tests/test_util.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_util.py b/tests/test_util.py index 0720e34..823605b 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -37,6 +37,12 @@ class TestUtil(unittest.TestCase): self.popen.mock.popen.assert_call(self.some_command_with_args) self.assertTrue(self.popen.mock.communicate.called) + def test_execute_nowait(self): + execute(self.some_command_with_args, False) + self.assertTrue(self.popen.mock.popen.called) + self.popen.mock.popen.assert_call(self.some_command_with_args) + self.assertFalse(self.popen.mock.communicate.called) + def test_execute_utf8(self): self.popen.mock.communicate.return_value = [ self.some_utf8, None ] self.test_execute() From f65ab6bcaee8f204ba76de903050a259e86b1dfa Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sat, 4 Mar 2017 12:11:43 +0100 Subject: [PATCH 04/33] [tests/config] Add unit tests for config.py --- .coveragerc | 1 + bumblebee/config.py | 2 -- tests/test_config.py | 77 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tests/test_config.py diff --git a/.coveragerc b/.coveragerc index d98e676..e5b6155 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,6 +5,7 @@ omit = *funcsigs* *pbr* *six* + /usr/lib* [report] exclude_lines = diff --git a/bumblebee/config.py b/bumblebee/config.py index e3b24aa..3086cff 100644 --- a/bumblebee/config.py +++ b/bumblebee/config.py @@ -25,8 +25,6 @@ class print_usage(argparse.Action): self.print_modules() elif value == "themes": self.print_themes() - else: - parser.print_help() sys.exit(0) def print_themes(self): diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..395dcb7 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,77 @@ +# pylint: disable=C0103,C0111 + +import unittest +import mock +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +from bumblebee.config import Config +from bumblebee.theme import themes +from bumblebee.engine import all_modules + +class TestConfig(unittest.TestCase): + def setUp(self): + self._stdout = mock.patch("bumblebee.config.sys.stdout", new_callable=StringIO) + self._stderr = mock.patch("bumblebee.config.sys.stderr", new_callable=StringIO) + + self.stdout = self._stdout.start() + self.stderr = self._stderr.start() + + self.defaultConfig = Config() + self.someSimpleModules = ["foo", "bar", "baz"] + self.someAliasModules = ["foo:a", "bar:b", "baz:c"] + self.someTheme = "some-theme" + + def tearDown(self): + self._stdout.stop() + self._stderr.stop() + + def test_no_modules_by_default(self): + self.assertEquals(self.defaultConfig.modules(), []) + + def test_simple_modules(self): + cfg = Config(["-m"] + self.someSimpleModules) + self.assertEquals(cfg.modules(), [{ + "name": x, "module": x + } for x in self.someSimpleModules]) + + def test_alias_modules(self): + cfg = Config(["-m"] + self.someAliasModules) + self.assertEquals(cfg.modules(), [{ + "module": x.split(":")[0], + "name": x.split(":")[1], + } for x in self.someAliasModules]) + + def test_parameters(self): + cfg = Config(["-m", "module", "-p", "module.key=value"]) + self.assertEquals(cfg.get("module.key"), "value") + + def test_theme(self): + cfg = Config(["-t", self.someTheme]) + self.assertEquals(cfg.theme(), self.someTheme) + + def test_notheme(self): + self.assertEquals(self.defaultConfig.theme(), "default") + + def test_list_themes(self): + with self.assertRaises(SystemExit): + cfg = Config(["-l", "themes"]) + result = self.stdout.getvalue() + for theme in themes(): + self.assertTrue(theme in result) + + def test_list_modules(self): + with self.assertRaises(SystemExit): + cfg = Config(["-l", "modules"]) + result = self.stdout.getvalue() + for module in all_modules(): + self.assertTrue(module["name"] in result) + + def test_invalid_list(self): + with self.assertRaises(SystemExit): + cfg = Config(["-l", "invalid"]) + self.assertTrue("invalid choice" in "".join(self.stderr.getvalue())) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 64523119afac97270be3e76ad8d21d8de389bcc7 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sat, 4 Mar 2017 13:44:51 +0100 Subject: [PATCH 05/33] [tests] Added engine and module tests Added those two unit tests together, as they are tightly linked together anyhow. --- tests/mocks.py | 62 ++++++++++++++++++++++++++++++ tests/test_engine.py | 83 +++++++++++++++++++++++++++++++++++++++++ tests/test_module.py | 89 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 tests/test_engine.py create mode 100644 tests/test_module.py diff --git a/tests/mocks.py b/tests/mocks.py index f60a9ee..3a5aac3 100644 --- a/tests/mocks.py +++ b/tests/mocks.py @@ -4,6 +4,8 @@ import mock import shlex import subprocess +from bumblebee.output import Widget + class MockPopen(object): def __init__(self, module): self._patch = mock.patch("{}.subprocess.Popen".format(module)) @@ -24,4 +26,64 @@ class MockPopen(object): def cleanup(self): self._patch.stop() +class MockInput(object): + def __init__(self): + self._callbacks = {} + def start(self): + pass + + def stop(self): + pass + + def get_callback(self, uid): + return self._callbacks.get(uid, None) + + def register_callback(self, obj, button, cmd): + if not obj: + return + self._callbacks[obj.id] = { + "button": button, + "command": cmd, + } + +class MockOutput(object): + def start(self): + pass + + def stop(self): + pass + + def draw(self, widget, engine, module): + engine.stop() + + def begin(self): + pass + + def flush(self): + pass + + def end(self): + pass + +class MockEngine(object): + def __init__(self): + self.input = MockInput() + +class MockWidget(Widget): + def __init__(self, text): + super(MockWidget, self).__init__(text) + self._text = text + self.module = None + self.attr_state = ["state-default"] + self.id = "none" + + def state(self): + return self.attr_state + + def update(self, widgets): + pass + + def full_text(self): + return self._text + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_engine.py b/tests/test_engine.py new file mode 100644 index 0000000..5623bbf --- /dev/null +++ b/tests/test_engine.py @@ -0,0 +1,83 @@ +# pylint: disable=C0103,C0111,W0703,W0212 + +import shlex +import unittest + +from bumblebee.error import ModuleLoadError +from bumblebee.engine import Engine +from bumblebee.config import Config +import bumblebee.input + +from mocks import MockOutput, MockInput + +class TestEngine(unittest.TestCase): + def setUp(self): + self.engine = Engine(config=Config(), output=MockOutput(), inp=MockInput()) + self.singleWidgetModule = [{"module": "test", "name": "a"}] + self.testModule = "test" + self.invalidModule = "no-such-module" + self.testModuleSpec = "bumblebee.modules.{}".format(self.testModule) + self.testModules = [ + {"module": "test", "name": "a"}, + {"module": "test", "name": "b"}, + ] + + def test_stop(self): + self.assertTrue(self.engine.running()) + self.engine.stop() + self.assertFalse(self.engine.running()) + + def test_load_module(self): + module = self.engine._load_module(self.testModule) + self.assertEquals(module.__module__, self.testModuleSpec) + + def test_load_invalid_module(self): + with self.assertRaises(ModuleLoadError): + self.engine._load_module(self.invalidModule) + + def test_load_none(self): + with self.assertRaises(ModuleLoadError): + self.engine._load_module(None) + + def test_load_modules(self): + modules = self.engine.load_modules(self.testModules) + self.assertEquals(len(modules), len(self.testModules)) + self.assertEquals( + [module.__module__ for module in modules], + [self.testModuleSpec for module in modules] + ) + + def test_run(self): + self.engine.load_modules(self.singleWidgetModule) + try: + self.engine.run() + except Exception as e: + self.fail(e) + + def test_custom_cmd(self): + testmodules = [ + { "name": "test", "button": "test.left-click", "action": "echo" }, + { "name": "test:alias", "button": "alias.right-click", "action": "echo2" }, + ] + cmd = "-m" + for test in testmodules: + cmd += " " + test["name"] + cmd += " -p" + for test in testmodules: + cmd += " " + test["button"] + "=" + test["action"] + cfg = Config(shlex.split(cmd)) + inp = MockInput() + engine = Engine(config=cfg, output=MockOutput(), inp=inp) + + i = 0 + for module in engine.modules(): + callback = inp.get_callback(module.id) + self.assertTrue(callback is not None) + self.assertEquals(callback["command"], testmodules[i]["action"]) + if "left" in testmodules[i]["button"]: + self.assertTrue(callback["button"], bumblebee.input.LEFT_MOUSE) + if "right" in testmodules[i]["button"]: + self.assertTrue(callback["button"], bumblebee.input.RIGHT_MOUSE) + i += 1 + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_module.py b/tests/test_module.py new file mode 100644 index 0000000..6e1c9b2 --- /dev/null +++ b/tests/test_module.py @@ -0,0 +1,89 @@ +# pylint: disable=C0103,C0111,W0703 + +import unittest + +from bumblebee.engine import Module +from bumblebee.config import Config +from mocks import MockWidget + +class TestModule(unittest.TestCase): + def setUp(self): + self.widgetName = "foo" + self.widget = MockWidget(self.widgetName) + self.config = Config() + self.anyWidgetName = "random-widget-name" + self.noSuchModule = "this-module-does-not-exist" + self.moduleWithoutWidgets = Module(engine=None, widgets=None) + self.moduleWithOneWidget = Module(engine=None, widgets=self.widget, config={"config": self.config}) + self.moduleWithMultipleWidgets = Module(engine=None, + widgets=[self.widget, self.widget, self.widget] + ) + + self.anyConfigName = "cfg" + self.anotherConfigName = "cfg2" + self.anyModule = Module(engine=None, widgets=self.widget, config={ + "name": self.anyConfigName, "config": self.config + }) + self.anotherModule = Module(engine=None, widgets=self.widget, config={ + "name": self.anotherConfigName, "config": self.config + }) + self.anyKey = "some-parameter" + self.anyValue = "value" + self.anotherValue = "another-value" + self.emptyKey = "i-do-not-exist" + self.config.set("{}.{}".format(self.anyConfigName, self.anyKey), self.anyValue) + self.config.set("{}.{}".format(self.anotherConfigName, self.anyKey), self.anotherValue) + + def test_empty_widgets(self): + self.assertEquals(self.moduleWithoutWidgets.widgets(), []) + + def test_single_widget(self): + self.assertEquals(self.moduleWithOneWidget.widgets(), [self.widget]) + + def test_multiple_widgets(self): + for widget in self.moduleWithMultipleWidgets.widgets(): + self.assertEquals(widget, self.widget) + + def test_retrieve_widget_by_name(self): + widget = MockWidget(self.anyWidgetName) + widget.name = self.anyWidgetName + module = Module(engine=None, widgets=[self.widget, widget, self.widget]) + retrievedWidget = module.widget(self.anyWidgetName) + self.assertEquals(retrievedWidget, widget) + + def test_retrieve_widget_by_id(self): + widget = MockWidget(self.anyWidgetName) + widget.id = self.anyWidgetName + module = Module(engine=None, widgets=[self.widget, widget, self.widget]) + retrievedWidget = module.widget_by_id(self.anyWidgetName) + self.assertEquals(retrievedWidget, widget) + + def test_retrieve_missing_widget(self): + module = self.moduleWithMultipleWidgets + + widget = module.widget(self.noSuchModule) + self.assertEquals(widget, None) + + widget = module.widget_by_id(self.noSuchModule) + self.assertEquals(widget, None) + + def test_threshold(self): + module = self.moduleWithOneWidget + module.name = self.widgetName + + self.config.set("{}.critical".format(self.widgetName), 10.0) + self.config.set("{}.warning".format(self.widgetName), 8.0) + self.assertEquals("critical", module.threshold_state(10.1, 0, 0)) + self.assertEquals("warning", module.threshold_state(10.0, 0, 0)) + self.assertEquals(None, module.threshold_state(8.0, 0, 0)) + self.assertEquals(None, module.threshold_state(7.9, 0, 0)) + + def test_parameters(self): + self.assertEquals(self.anyModule.parameter(self.anyKey), self.anyValue) + self.assertEquals(self.anotherModule.parameter(self.anyKey), self.anotherValue) + + def test_default_parameters(self): + self.assertEquals(self.anyModule.parameter(self.emptyKey), None) + self.assertEquals(self.anyModule.parameter(self.emptyKey, self.anyValue), self.anyValue) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 6a14681a657823a82e3461268db45c78e7cd156e Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sat, 4 Mar 2017 14:17:27 +0100 Subject: [PATCH 06/33] [tests/theme] Add tests for theme.py --- bumblebee/theme.py | 5 -- tests/test_theme.py | 129 ++++++++++++++++++++++++++++++++++++++++++++ themes/test.json | 3 ++ 3 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 tests/test_theme.py diff --git a/bumblebee/theme.py b/bumblebee/theme.py index 779155b..08584c7 100644 --- a/bumblebee/theme.py +++ b/bumblebee/theme.py @@ -90,11 +90,6 @@ class Theme(object): """Return the SBW""" return self._get(widget, "separator-block-width", None) - def loads(self, data): - """Initialize the theme from a JSON string""" - theme = json.loads(data) - self._init(theme) - def _load_icons(self, name): """Load icons for a theme""" path = "{}/icons/".format(theme_path()) diff --git a/tests/test_theme.py b/tests/test_theme.py new file mode 100644 index 0000000..708fa08 --- /dev/null +++ b/tests/test_theme.py @@ -0,0 +1,129 @@ +# pylint: disable=C0103,C0111,W0703 + +import unittest +from bumblebee.theme import Theme +from bumblebee.error import ThemeLoadError +from mocks import MockWidget + +class TestTheme(unittest.TestCase): + def setUp(self): + self.nonexistentThemeName = "no-such-theme" + self.invalidThemeName = "test_invalid" + self.validThemeName = "test" + self.validThemeSeparator = " * " + self.themedWidget = MockWidget("bla") + self.theme = Theme(self.validThemeName) + self.cycleTheme = Theme("test_cycle") + self.anyWidget = MockWidget("bla") + self.anotherWidget = MockWidget("blub") + + data = self.theme.data() + self.widgetTheme = "test-widget" + self.themedWidget.module = self.widgetTheme + self.defaultColor = data["defaults"]["fg"] + self.defaultBgColor = data["defaults"]["bg"] + self.widgetColor = data[self.widgetTheme]["fg"] + self.widgetBgColor = data[self.widgetTheme]["bg"] + self.defaultPrefix = data["defaults"]["prefix"] + self.defaultSuffix = data["defaults"]["suffix"] + self.widgetPrefix = data[self.widgetTheme]["prefix"] + self.widgetSuffix = data[self.widgetTheme]["suffix"] + + def test_load_valid_theme(self): + try: + Theme(self.validThemeName) + except Exception as e: + self.fail(e) + + def test_load_nonexistent_theme(self): + with self.assertRaises(ThemeLoadError): + Theme(self.nonexistentThemeName) + + def test_load_invalid_theme(self): + with self.assertRaises(ThemeLoadError): + Theme(self.invalidThemeName) + + def test_default_prefix(self): + self.assertEquals(self.theme.prefix(self.anyWidget), self.defaultPrefix) + + def test_default_suffix(self): + self.assertEquals(self.theme.suffix(self.anyWidget), self.defaultSuffix) + + def test_widget_prefix(self): + self.assertEquals(self.theme.prefix(self.themedWidget), self.widgetPrefix) + + def test_widget_fg(self): + self.assertEquals(self.theme.fg(self.anyWidget), self.defaultColor) + self.anyWidget.module = self.widgetTheme + self.assertEquals(self.theme.fg(self.anyWidget), self.widgetColor) + + def test_widget_bg(self): + self.assertEquals(self.theme.bg(self.anyWidget), self.defaultBgColor) + self.anyWidget.module = self.widgetTheme + self.assertEquals(self.theme.bg(self.anyWidget), self.widgetBgColor) + + def test_absent_cycle(self): + theme = self.theme + try: + theme.fg(self.anyWidget) + theme.fg(self.anotherWidget) + except Exception as e: + self.fail(e) + + def test_reset(self): + theme = self.cycleTheme + data = theme.data() + theme.reset() + self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"]) + self.assertEquals(theme.fg(self.anotherWidget), data["cycle"][1]["fg"]) + theme.reset() + self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"]) + + def test_separator_block_width(self): + theme = self.theme + data = theme.data() + + self.assertEquals(theme.separator_block_width(self.anyWidget), + data["defaults"]["separator-block-width"] + ) + + def test_separator(self): + for theme in [self.theme, self.cycleTheme]: + theme.reset() + prev_bg = theme.bg(self.anyWidget) + theme.bg(self.anotherWidget) + + self.assertEquals(theme.separator_fg(self.anotherWidget), theme.bg(self.anotherWidget)) + self.assertEquals(theme.separator_bg(self.anotherWidget), prev_bg) + + def test_state(self): + theme = self.theme + data = theme.data() + + self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["fg"]) + self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["bg"]) + + self.anyWidget.attr_state = ["critical"] + self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["critical"]["fg"]) + self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["critical"]["bg"]) + + self.themedWidget.attr_state = ["critical"] + self.assertEquals(theme.fg(self.themedWidget), data[self.widgetTheme]["critical"]["fg"]) + # if elements are missing in the state theme, they are taken from the + # widget theme instead (i.e. no fallback to a more general state theme) + self.assertEquals(theme.bg(self.themedWidget), data[self.widgetTheme]["bg"]) + + def test_separator(self): + self.assertEquals(self.validThemeSeparator, self.theme.separator(self.anyWidget)) + + def test_list(self): + theme = self.theme + data = theme.data()[self.widgetTheme]["cycle-test"]["fg"] + self.themedWidget.attr_state = ["cycle-test"] + self.assertTrue(len(data) > 1) + + for idx in range(0, len(data)): + self.assertEquals(theme.fg(self.themedWidget), data[idx]) + self.assertEquals(theme.fg(self.themedWidget), data[0]) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/themes/test.json b/themes/test.json index 401a234..1d457a7 100644 --- a/themes/test.json +++ b/themes/test.json @@ -17,6 +17,9 @@ "bg": "#222222", "critical": { "fg": "#bababa" + }, + "cycle-test": { + "fg": [ "#000000", "#111111" ] } } } From 90dd636b431435cbced354e0051a54784af8c63a Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sat, 4 Mar 2017 18:53:31 +0100 Subject: [PATCH 07/33] [tests/engine] Add test for module aliasing --- bumblebee/modules/test.py | 2 ++ tests/test_engine.py | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bumblebee/modules/test.py b/bumblebee/modules/test.py index 5e92e0e..e4099c6 100644 --- a/bumblebee/modules/test.py +++ b/bumblebee/modules/test.py @@ -5,6 +5,8 @@ import bumblebee.engine +ALIASES = [ "test-alias" ] + class Module(bumblebee.engine.Module): def __init__(self, engine, config): super(Module, self).__init__(engine, config, diff --git a/tests/test_engine.py b/tests/test_engine.py index 5623bbf..fe3f213 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -13,8 +13,10 @@ from mocks import MockOutput, MockInput class TestEngine(unittest.TestCase): def setUp(self): self.engine = Engine(config=Config(), output=MockOutput(), inp=MockInput()) - self.singleWidgetModule = [{"module": "test", "name": "a"}] self.testModule = "test" + self.testAlias = "test-alias" + self.singleWidgetModule = [{"module": self.testModule, "name": "a"}] + self.singleWidgetAlias = [{"module": self.testAlias, "name": "a" }] self.invalidModule = "no-such-module" self.testModuleSpec = "bumblebee.modules.{}".format(self.testModule) self.testModules = [ @@ -54,6 +56,11 @@ class TestEngine(unittest.TestCase): except Exception as e: self.fail(e) + def test_aliases(self): + modules = self.engine.load_modules(self.singleWidgetAlias) + self.assertEquals(len(modules), 1) + self.assertEquals(modules[0].__module__, self.testModuleSpec) + def test_custom_cmd(self): testmodules = [ { "name": "test", "button": "test.left-click", "action": "echo" }, From ce2bc58919ead19838b8e34cc594a876862189df Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sat, 4 Mar 2017 18:54:42 +0100 Subject: [PATCH 08/33] [coveragerc] excludelines somehow broke the build for Python 2.7 --- .coveragerc | 3 --- 1 file changed, 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index e5b6155..e809714 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,6 +8,3 @@ omit = /usr/lib* [report] - exclude_lines = - except ImportError: - pass From cdbddcfff74cffe7ca32de64068a5404ae9eae3f Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 07:56:10 +0100 Subject: [PATCH 09/33] [tests/i3bar-input] Add tests for i3bar input processing --- tests/test_i3barinput.py | 108 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/test_i3barinput.py diff --git a/tests/test_i3barinput.py b/tests/test_i3barinput.py new file mode 100644 index 0000000..fe41753 --- /dev/null +++ b/tests/test_i3barinput.py @@ -0,0 +1,108 @@ +# pylint: disable=C0103,C0111 + +import json +import mock +import unittest + +import mocks + +from bumblebee.input import I3BarInput, LEFT_MOUSE, RIGHT_MOUSE + +class TestI3BarInput(unittest.TestCase): + def setUp(self): + self.input = I3BarInput() + self.input.need_event = True + + self._stdin = mock.patch("bumblebee.input.sys.stdin") + self.stdin = self._stdin.start() + self._select = mock.patch("bumblebee.input.select") + self.select = self._select.start() + self.popen = mocks.MockPopen() + + self.stdin.fileno.return_value = 1 + epoll = mock.Mock() + self.select.epoll.return_value = epoll + + epoll.poll.return_value = [(self.stdin.fileno.return_value, 2)] + + self.anyModule = mocks.MockModule() + self.anotherModule = mocks.MockModule() + self.anyWidget = mocks.MockWidget("some-widget") + self.anotherWidget = mocks.MockWidget("another-widget") + self.anyData = self.invalidData = "any data" + self.invalidEvent = json.dumps({"name": None, "instance": None, "button": 1}) + self.incompleteEvent = json.dumps({"button": 1}) + self.anyCommand = "this is a command with arguments" + + self._called = 0 + + def tearDown(self): + self._stdin.stop() + self._select.stop() + self.popen.cleanup() + + def callback(self, event): + self._called += 1 + + def calls(self): + rv = self._called + self._called = 0 + return rv + + def test_read_event(self): + self.stdin.readline.return_value = self.anyData + self.input.start() + self.input.stop() + self.stdin.readline.assert_any_call() + + def test_ignore_invalid_input(self): + for data in [ self.invalidData, self.incompleteEvent, self.invalidEvent ]: + self.stdin.readline.return_value = data + self.input.start() + self.assertEquals(self.input.alive(), True) + self.assertEquals(self.input.stop(), True) + self.stdin.readline.assert_any_call() + + def test_global_callback(self): + self.input.register_callback(None, button=LEFT_MOUSE, cmd=self.callback) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin) + self.assertTrue(self.calls() > 0) + + def test_remove_global_callback(self): + self.test_global_callback() + self.input.deregister_callbacks(None) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin) + self.assertTrue(self.calls() == 0) + + def test_global_callback_wrong_button(self): + self.input.register_callback(None, button=LEFT_MOUSE, cmd=self.callback) + mocks.mouseEvent(button=RIGHT_MOUSE, inp=self.input, stdin=self.stdin) + self.assertTrue(self.calls() == 0) + + def test_module_callback(self): + self.input.register_callback(self.anyModule, button=LEFT_MOUSE, cmd=self.callback) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyModule) + self.assertTrue(self.calls() > 0) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anotherModule) + self.assertTrue(self.calls() == 0) + + def test_remove_module_callback(self): + self.test_module_callback() + self.input.deregister_callbacks(self.anyModule) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyModule) + self.assertTrue(self.calls() == 0) + + def test_widget_callback(self): + self.input.register_callback(self.anyWidget, button=LEFT_MOUSE, cmd=self.callback) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyWidget) + self.assertTrue(self.calls() > 0) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anotherWidget) + self.assertTrue(self.calls() == 0) + + def test_widget_cmd_callback(self): + self.input.register_callback(self.anyWidget, button=LEFT_MOUSE, cmd=self.anyCommand) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyWidget) + self.popen.assert_call(self.anyCommand) + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 70f138b97bb10380b6b6f041fbb36fe43020d13a Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 08:35:15 +0100 Subject: [PATCH 10/33] [tests/i3bar-input] Add input tests for i3bar protocol Also, replaced the MockModule with a generic mock object. --- tests/mocks.py | 33 ++++++++++++++++++++++++--------- tests/test_i3barinput.py | 6 ++++-- tests/test_theme.py | 13 +++++++++---- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/tests/mocks.py b/tests/mocks.py index 3a5aac3..62a5700 100644 --- a/tests/mocks.py +++ b/tests/mocks.py @@ -1,14 +1,31 @@ # pylint: disable=C0103,C0111 import mock +import json import shlex import subprocess from bumblebee.output import Widget +import random, string + +def rand(cnt): + return "".join(random.choice(string.lowercase) for i in range(cnt)) + +def mouseEvent(stdin, button, inp, module=None, instance=None): + stdin.readline.return_value = json.dumps({ + "name": module.id if module else rand(10), + "button": button, + "instance": instance + }) + inp.start() + inp.stop() + stdin.readline.assert_any_call() + class MockPopen(object): - def __init__(self, module): - self._patch = mock.patch("{}.subprocess.Popen".format(module)) + def __init__(self, module=""): + if len(module) > 0: module = "{}.".format(module) + self._patch = mock.patch("{}subprocess.Popen".format(module)) self._popen = self._patch.start() self.mock = mock.Mock() # for a nicer, more uniform interface @@ -72,18 +89,16 @@ class MockEngine(object): class MockWidget(Widget): def __init__(self, text): super(MockWidget, self).__init__(text) - self._text = text self.module = None self.attr_state = ["state-default"] - self.id = "none" + self.id = rand(10) - def state(self): - return self.attr_state + self.full_text(text) + +# def state(self): +# return self.attr_state def update(self, widgets): pass - def full_text(self): - return self._text - # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/test_i3barinput.py b/tests/test_i3barinput.py index fe41753..5b4bfb1 100644 --- a/tests/test_i3barinput.py +++ b/tests/test_i3barinput.py @@ -25,8 +25,10 @@ class TestI3BarInput(unittest.TestCase): epoll.poll.return_value = [(self.stdin.fileno.return_value, 2)] - self.anyModule = mocks.MockModule() - self.anotherModule = mocks.MockModule() + self.anyModule = mock.Mock() + self.anyModule.id = mocks.rand(10) + self.anotherModule = mock.Mock() + self.anotherModule.id = mocks.rand(10) self.anyWidget = mocks.MockWidget("some-widget") self.anotherWidget = mocks.MockWidget("another-widget") self.anyData = self.invalidData = "any data" diff --git a/tests/test_theme.py b/tests/test_theme.py index 708fa08..474b66a 100644 --- a/tests/test_theme.py +++ b/tests/test_theme.py @@ -1,5 +1,6 @@ # pylint: disable=C0103,C0111,W0703 +import mock import unittest from bumblebee.theme import Theme from bumblebee.error import ThemeLoadError @@ -14,9 +15,15 @@ class TestTheme(unittest.TestCase): self.themedWidget = MockWidget("bla") self.theme = Theme(self.validThemeName) self.cycleTheme = Theme("test_cycle") + self.anyModule = mock.Mock() self.anyWidget = MockWidget("bla") self.anotherWidget = MockWidget("blub") + self.anyModule.state.return_value = "state-default" + + self.anyWidget.link_module(self.anyModule) + self.themedWidget.link_module(self.anyModule) + data = self.theme.data() self.widgetTheme = "test-widget" self.themedWidget.module = self.widgetTheme @@ -103,11 +110,9 @@ class TestTheme(unittest.TestCase): self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["fg"]) self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["bg"]) - self.anyWidget.attr_state = ["critical"] + self.anyModule.state.return_value = "critical" self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["critical"]["fg"]) self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["critical"]["bg"]) - - self.themedWidget.attr_state = ["critical"] self.assertEquals(theme.fg(self.themedWidget), data[self.widgetTheme]["critical"]["fg"]) # if elements are missing in the state theme, they are taken from the # widget theme instead (i.e. no fallback to a more general state theme) @@ -119,7 +124,7 @@ class TestTheme(unittest.TestCase): def test_list(self): theme = self.theme data = theme.data()[self.widgetTheme]["cycle-test"]["fg"] - self.themedWidget.attr_state = ["cycle-test"] + self.anyModule.state.return_value = "cycle-test" self.assertTrue(len(data) > 1) for idx in range(0, len(data)): From 728cb73189bc441a77f388860f720721a36a0977 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 09:34:47 +0100 Subject: [PATCH 11/33] [test_i3bar-output] Added i3bar output test modules --- tests/test_i3baroutput.py | 131 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tests/test_i3baroutput.py diff --git a/tests/test_i3baroutput.py b/tests/test_i3baroutput.py new file mode 100644 index 0000000..7c09eba --- /dev/null +++ b/tests/test_i3baroutput.py @@ -0,0 +1,131 @@ +# pylint: disable=C0103,C0111 + +import json +import mock +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import mocks + +from bumblebee.output import I3BarOutput + +class TestI3BarOutput(unittest.TestCase): + def setUp(self): + self.theme = mock.Mock() + self.theme.separator_fg.return_value = "#123456" + self.theme.separator_bg.return_value = "#000000" + self.theme.separator.return_value = "" + self.theme.prefix.return_value = "" + self.theme.suffix.return_value = "" + self.theme.separator_block_width.return_value = 1 + self.theme.fg.return_value = "#ababab" + self.theme.bg.return_value = "#ababab" + self.output = I3BarOutput(self.theme) + + self._stdout = mock.patch("bumblebee.output.sys.stdout", new_callable=StringIO) + self.stdout = self._stdout.start() + + self.anyWidget = mocks.MockWidget("some text") + self.anyModule = mock.Mock() + self.anyModule.id = mocks.rand(10) + self.anyModule.name = mocks.rand(10) + + self.expectedStart = json.dumps({"version": 1, "click_events": True}) + "[\n" + self.expectedStop = "]\n" + + self.anyColor = "#ffffff" + self.anotherColor = "#cdcdcd" + + def tearDown(self): + self._stdout.stop() + + def test_start(self): + self.output.start() + self.assertEquals(self.expectedStart, self.stdout.getvalue()) + + def test_stop(self): + self.output.stop() + self.assertEquals(self.expectedStop, self.stdout.getvalue()) + + def test_draw_single_widget(self): + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["full_text"], self.anyWidget.full_text()) + + def test_draw_multiple_widgets(self): + for i in range(4): + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue()) + for res in result: + self.assertEquals(res["full_text"], self.anyWidget.full_text()) + + def test_begin(self): + self.output.begin() + self.assertEquals("", self.stdout.getvalue()) + + def test_end(self): + self.output.end() + self.assertEquals(",\n", self.stdout.getvalue()) + + def test_prefix(self): + self.theme.prefix.return_value = " - " + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["full_text"], " - {}".format(self.anyWidget.full_text())) + + def test_suffix(self): + self.theme.suffix.return_value = " - " + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["full_text"], "{} - ".format(self.anyWidget.full_text())) + + def test_bothfix(self): + self.theme.prefix.return_value = "*" + self.theme.suffix.return_value = " - " + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["full_text"], "*{} - ".format(self.anyWidget.full_text())) + + def test_colors(self): + self.theme.fg.return_value = self.anyColor + self.theme.bg.return_value = self.anotherColor + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["color"], self.anyColor) + self.assertEquals(result["background"], self.anotherColor) + + def test_widget_link(self): + self.anyWidget.link_module(self.anyModule) + self.assertEquals(self.anyWidget._module, self.anyModule) + self.assertEquals(self.anyWidget.module, self.anyModule.name) + + def test_unlinked_widget_state(self): + state = self.anyWidget.state() + self.assertTrue(type(state) == list) + + def test_linked_widget_state(self): + self.anyWidget.link_module(self.anyModule) + for lst in [ "samplestate", ["a", "b", "c"], [] ]: + self.anyModule.state.return_value = lst + state = self.anyWidget.state() + self.assertEquals(type(state), list) + if type(lst) is not list: lst = [lst] + self.assertEquals(state, lst) + + def test_widget_fulltext(self): + self.anyWidget.full_text("some text") + self.assertEquals(self.anyWidget.full_text(), "some text") + self.anyWidget.full_text(lambda x: "callable fulltext") + self.assertEquals(self.anyWidget.full_text(), "callable fulltext") + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 8724af2906f73ab9236603b0297ded7d0a387328 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 09:35:56 +0100 Subject: [PATCH 12/33] [tests/battery] Add tests for battery module --- bumblebee/engine.py | 3 + tests/__init__.py | 0 tests/modules/__init__.py | 0 tests/modules/backup.py | 66 ++++++++++++++++++++ tests/modules/test_battery.py | 110 ++++++++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/modules/__init__.py create mode 100644 tests/modules/backup.py create mode 100644 tests/modules/test_battery.py diff --git a/bumblebee/engine.py b/bumblebee/engine.py index 9a9c35f..67234fe 100644 --- a/bumblebee/engine.py +++ b/bumblebee/engine.py @@ -52,6 +52,9 @@ class Module(object): """By default, update() is a NOP""" pass + def update_all(self): + self.update(self._widgets) + def parameter(self, name, default=None): """Return the config parameter 'name' for this module""" name = "{}.{}".format(self.name, name) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/modules/__init__.py b/tests/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/modules/backup.py b/tests/modules/backup.py new file mode 100644 index 0000000..45b76c4 --- /dev/null +++ b/tests/modules/backup.py @@ -0,0 +1,66 @@ +# pylint: disable=C0103,C0111 + +import sys +import json +import unittest +import mock + +from contextlib import contextmanager + +from bumblebee.input import I3BarInput +from bumblebee.modules.battery import Module + +class MockOpen(object): + def __init__(self): + self._value = "" + + def returns(self, value): + self._value = value + + def __enter__(self): + return self + + def __exit__(self, a, b, c): + pass + + def read(self): + return self._value + +class TestBatteryModule(unittest.TestCase): + def setUp(self): + self.engine = MockEngine() + self.config = MockConfig() + self.module = Module(engine=self.engine, config={ "config": self.config }) + for widget in self.module.widgets(): + widget.link_module(self.module) + + @mock.patch("sys.stdout") + def test_format(self, mock_output): + for widget in self.module.widgets(): + self.assertEquals(len(widget.full_text()), len("100%")) + + @mock.patch("os.path.exists") + @mock.patch("{}.open".format("__builtin__" if sys.version_info[0] < 3 else "builtins")) + @mock.patch("subprocess.Popen") + def test_critical(self, mock_output, mock_open, mock_exists): + mock_open.return_value = MockOpen() + mock_open.return_value.returns("19") + mock_exists.return_value = True + self.config.set("battery.critical", "20") + self.config.set("battery.warning", "25") + self.module.update(self.module.widgets()) + self.assertTrue("critical" in self.module.widgets()[0].state()) + + @mock.patch("os.path.exists") + @mock.patch("{}.open".format("__builtin__" if sys.version_info[0] < 3 else "builtins")) + @mock.patch("subprocess.Popen") + def test_warning(self, mock_output, mock_open, mock_exists): + mock_open.return_value = MockOpen() + mock_exists.return_value = True + mock_open.return_value.returns("22") + self.config.set("battery.critical", "20") + self.config.set("battery.warning", "25") + self.module.update(self.module.widgets()) + self.assertTrue("warning" in self.module.widgets()[0].state()) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/modules/test_battery.py b/tests/modules/test_battery.py new file mode 100644 index 0000000..6f31b16 --- /dev/null +++ b/tests/modules/test_battery.py @@ -0,0 +1,110 @@ +# pylint: disable=C0103,C0111 + +import sys +import mock +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import tests.mocks as mocks + +from bumblebee.modules.battery import Module +from bumblebee.config import Config + +class TestBatteryModule(unittest.TestCase): + def setUp(self): + self._stdout = mock.patch("sys.stdout", new_callable=StringIO) + self._exists = mock.patch("bumblebee.modules.battery.os.path.exists") + self._open = mock.patch("bumblebee.modules.battery.open") + + self.stdout = self._stdout.start() + self.exists = self._exists.start() + self.open = self._open.start() + self.file = mock.Mock() + self.file.__enter__ = lambda x: self.file + self.file.__exit__ = lambda x, a, b, c: "" + self.open.return_value = self.file + + self.exists.return_value = True + self.engine = mock.Mock() + self.config = Config() + self.module = Module(engine=self.engine, config={"config":self.config}) + self.popen = mocks.MockPopen() + + self.config.set("battery.critical", "20") + self.config.set("battery.warning", "25") + self.criticalValue = "19" + self.warningValue = "21" + self.normalValue = "26" + self.chargedValue = "96" + + for widget in self.module.widgets(): + widget.link_module(self.module) + self.anyWidget = widget + + def tearDown(self): + self._stdout.stop() + self._exists.stop() + self._open.stop() + self.popen.cleanup() + + def test_format(self): + for widget in self.module.widgets(): + self.assertEquals(len(widget.full_text()), len("100%")) + + def test_critical(self): + self.file.read.return_value = self.criticalValue + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + + def test_warning(self): + self.file.read.return_value = self.warningValue + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_normal(self): + self.file.read.return_value = self.normalValue + self.module.update_all() + self.assertTrue(not "warning" in self.module.state(self.anyWidget)) + self.assertTrue(not "critical" in self.module.state(self.anyWidget)) + + def test_overload(self): + self.file.read.return_value = "120" + self.module.update_all() + self.assertTrue(not "warning" in self.module.state(self.anyWidget)) + self.assertTrue(not "critical" in self.module.state(self.anyWidget)) + self.assertEquals(self.module.capacity(self.anyWidget), "100%") + + def test_ac(self): + self.exists.return_value = False + self.module.update_all() + self.assertEquals(self.module.capacity(self.anyWidget), "ac") + self.assertTrue("AC" in self.module.state(self.anyWidget)) + + def test_error(self): + self.file.read.side_effect = IOError("failed to read") + self.module.update_all() + self.assertEquals(self.module.capacity(self.anyWidget), "n/a") + self.assertTrue("critical" in self.module.state(self.anyWidget)) + self.assertTrue("unknown" in self.module.state(self.anyWidget)) + + def test_charging(self): + self.file.read.return_value = self.chargedValue + self.module.update_all() + self.assertTrue("charged" in self.module.state(self.anyWidget)) + self.file.read.return_value = self.normalValue + self.module.update_all() + self.assertTrue("charging" in self.module.state(self.anyWidget)) + + def test_discharging(self): + for limit in [ 10, 25, 50, 80, 100 ]: + value = limit - 1 + self.file.read.return_value = str(value) + self.module.update_all() + self.file.read.return_value = "Discharging" + self.assertTrue("discharging-{}".format(limit) in self.module.state(self.anyWidget)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 30f3c898edff84aeebe70181dd79703e3ee740bf Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 09:37:59 +0100 Subject: [PATCH 13/33] [tests] Do not use string.lowercase, as Python3 complains about it --- tests/mocks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mocks.py b/tests/mocks.py index 62a5700..ee190f6 100644 --- a/tests/mocks.py +++ b/tests/mocks.py @@ -7,10 +7,10 @@ import subprocess from bumblebee.output import Widget -import random, string +import random def rand(cnt): - return "".join(random.choice(string.lowercase) for i in range(cnt)) + return "".join(random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for i in range(cnt)) def mouseEvent(stdin, button, inp, module=None, instance=None): stdin.readline.return_value = json.dumps({ From e4f728327e25ebbe60e6e71c08c097082c36fedf Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 09:40:15 +0100 Subject: [PATCH 14/33] [tests] Fix imports for Python3 compatibility --- tests/test_engine.py | 2 +- tests/test_i3barinput.py | 2 +- tests/test_i3baroutput.py | 2 +- tests/test_module.py | 2 +- tests/test_theme.py | 2 +- tests/test_util.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_engine.py b/tests/test_engine.py index fe3f213..946f604 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -8,7 +8,7 @@ from bumblebee.engine import Engine from bumblebee.config import Config import bumblebee.input -from mocks import MockOutput, MockInput +from tests.mocks import MockOutput, MockInput class TestEngine(unittest.TestCase): def setUp(self): diff --git a/tests/test_i3barinput.py b/tests/test_i3barinput.py index 5b4bfb1..6852f97 100644 --- a/tests/test_i3barinput.py +++ b/tests/test_i3barinput.py @@ -4,7 +4,7 @@ import json import mock import unittest -import mocks +import tests.mocks as mocks from bumblebee.input import I3BarInput, LEFT_MOUSE, RIGHT_MOUSE diff --git a/tests/test_i3baroutput.py b/tests/test_i3baroutput.py index 7c09eba..9b81c42 100644 --- a/tests/test_i3baroutput.py +++ b/tests/test_i3baroutput.py @@ -9,7 +9,7 @@ try: except ImportError: from io import StringIO -import mocks +import tests.mocks as mocks from bumblebee.output import I3BarOutput diff --git a/tests/test_module.py b/tests/test_module.py index 6e1c9b2..cf663dc 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -4,7 +4,7 @@ import unittest from bumblebee.engine import Module from bumblebee.config import Config -from mocks import MockWidget +from tests.mocks import MockWidget class TestModule(unittest.TestCase): def setUp(self): diff --git a/tests/test_theme.py b/tests/test_theme.py index 474b66a..747d749 100644 --- a/tests/test_theme.py +++ b/tests/test_theme.py @@ -4,7 +4,7 @@ import mock import unittest from bumblebee.theme import Theme from bumblebee.error import ThemeLoadError -from mocks import MockWidget +from tests.mocks import MockWidget class TestTheme(unittest.TestCase): def setUp(self): diff --git a/tests/test_util.py b/tests/test_util.py index 823605b..1988756 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,7 +3,7 @@ import mock import unittest -import mocks +import tests.mocks as mocks from bumblebee.util import * From 1217aa1f60d125f19797837063a418eea4bdfd1a Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 10:39:08 +0100 Subject: [PATCH 15/33] [tests/battery] Try to fix failing CI tests --- tests/modules/test_battery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/modules/test_battery.py b/tests/modules/test_battery.py index 6f31b16..2963318 100644 --- a/tests/modules/test_battery.py +++ b/tests/modules/test_battery.py @@ -18,7 +18,7 @@ class TestBatteryModule(unittest.TestCase): def setUp(self): self._stdout = mock.patch("sys.stdout", new_callable=StringIO) self._exists = mock.patch("bumblebee.modules.battery.os.path.exists") - self._open = mock.patch("bumblebee.modules.battery.open") + self._open = mock.patch("bumblebee.modules.battery.open", create=True) self.stdout = self._stdout.start() self.exists = self._exists.start() From ce22e9dc75234f185a9d4942be360a65b0b02369 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 11:23:52 +0100 Subject: [PATCH 16/33] [tests] remove accidentially committed backup module --- tests/modules/backup.py | 66 ----------------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 tests/modules/backup.py diff --git a/tests/modules/backup.py b/tests/modules/backup.py deleted file mode 100644 index 45b76c4..0000000 --- a/tests/modules/backup.py +++ /dev/null @@ -1,66 +0,0 @@ -# pylint: disable=C0103,C0111 - -import sys -import json -import unittest -import mock - -from contextlib import contextmanager - -from bumblebee.input import I3BarInput -from bumblebee.modules.battery import Module - -class MockOpen(object): - def __init__(self): - self._value = "" - - def returns(self, value): - self._value = value - - def __enter__(self): - return self - - def __exit__(self, a, b, c): - pass - - def read(self): - return self._value - -class TestBatteryModule(unittest.TestCase): - def setUp(self): - self.engine = MockEngine() - self.config = MockConfig() - self.module = Module(engine=self.engine, config={ "config": self.config }) - for widget in self.module.widgets(): - widget.link_module(self.module) - - @mock.patch("sys.stdout") - def test_format(self, mock_output): - for widget in self.module.widgets(): - self.assertEquals(len(widget.full_text()), len("100%")) - - @mock.patch("os.path.exists") - @mock.patch("{}.open".format("__builtin__" if sys.version_info[0] < 3 else "builtins")) - @mock.patch("subprocess.Popen") - def test_critical(self, mock_output, mock_open, mock_exists): - mock_open.return_value = MockOpen() - mock_open.return_value.returns("19") - mock_exists.return_value = True - self.config.set("battery.critical", "20") - self.config.set("battery.warning", "25") - self.module.update(self.module.widgets()) - self.assertTrue("critical" in self.module.widgets()[0].state()) - - @mock.patch("os.path.exists") - @mock.patch("{}.open".format("__builtin__" if sys.version_info[0] < 3 else "builtins")) - @mock.patch("subprocess.Popen") - def test_warning(self, mock_output, mock_open, mock_exists): - mock_open.return_value = MockOpen() - mock_exists.return_value = True - mock_open.return_value.returns("22") - self.config.set("battery.critical", "20") - self.config.set("battery.warning", "25") - self.module.update(self.module.widgets()) - self.assertTrue("warning" in self.module.widgets()[0].state()) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 463850eddca496aa8dbd7911293d9d6c78561bee Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 11:24:11 +0100 Subject: [PATCH 17/33] [tests/battery] Add unused variables --- tests/modules/test_battery.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/modules/test_battery.py b/tests/modules/test_battery.py index 2963318..0c86cc9 100644 --- a/tests/modules/test_battery.py +++ b/tests/modules/test_battery.py @@ -32,7 +32,6 @@ class TestBatteryModule(unittest.TestCase): self.engine = mock.Mock() self.config = Config() self.module = Module(engine=self.engine, config={"config":self.config}) - self.popen = mocks.MockPopen() self.config.set("battery.critical", "20") self.config.set("battery.warning", "25") @@ -49,7 +48,6 @@ class TestBatteryModule(unittest.TestCase): self._stdout.stop() self._exists.stop() self._open.stop() - self.popen.cleanup() def test_format(self): for widget in self.module.widgets(): From 36848770a57d896a00641e7661b734995fdb775a Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 11:24:30 +0100 Subject: [PATCH 18/33] [tests] Add unit tests for brightness module --- tests/mocks.py | 16 ++++++++ tests/modules/test_brightness.py | 69 ++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 tests/modules/test_brightness.py diff --git a/tests/mocks.py b/tests/mocks.py index ee190f6..4d7eab9 100644 --- a/tests/mocks.py +++ b/tests/mocks.py @@ -12,6 +12,22 @@ import random def rand(cnt): return "".join(random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for i in range(cnt)) +def epoll_mock(module=""): + if len(module) > 0: module = "{}.".format(module) + + stdin = mock.patch("{}sys.stdin".format(module)) + select = mock.patch("{}select".format(module)) + epoll = mock.Mock() + + stdin_mock = stdin.start() + select_mock = select.start() + + stdin_mock.fileno.return_value = 1 + select_mock.epoll.return_value = epoll + epoll.poll.return_value = [(stdin_mock.fileno.return_value, 100)] + + return stdin, select, stdin_mock, select_mock + def mouseEvent(stdin, button, inp, module=None, instance=None): stdin.readline.return_value = json.dumps({ "name": module.id if module else rand(10), diff --git a/tests/modules/test_brightness.py b/tests/modules/test_brightness.py new file mode 100644 index 0000000..5112e41 --- /dev/null +++ b/tests/modules/test_brightness.py @@ -0,0 +1,69 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import tests.mocks as mocks + +import bumblebee.input +from bumblebee.config import Config +from bumblebee.engine import Engine +from bumblebee.input import I3BarInput, WHEEL_UP, WHEEL_DOWN +from bumblebee.modules.brightness import Module + +class TestBrightnessModule(unittest.TestCase): + def setUp(self): + self._stdout = mock.patch("sys.stdout", new_callable=StringIO) + self.stdout = self._stdout.start() + + self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") + + self.popen = mocks.MockPopen() + + self.config = Config() + self.input = I3BarInput() + self.engine = mock.Mock() + self.engine.input = self.input + self.input.need_event = True + + self.module = Module(engine=self.engine, config={ "config": self.config }) + for widget in self.module.widgets(): + widget.link_module(self.module) + self.anyWidget = widget + + def tearDown(self): + self._stdout.stop() + self._stdin.stop() + self._select.stop() + self.popen.cleanup() + + def test_format(self): + for widget in self.module.widgets(): + self.assertEquals(len(widget.full_text()), len("100%")) + + def test_wheel_up(self): + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_UP, inp=self.input, module=self.module) + self.popen.assert_call("xbacklight +2%") + + def test_wheel_down(self): + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=self.module) + self.popen.assert_call("xbacklight -2%") + + def test_custom_step(self): + self.config.set("brightness.step", "10") + module = Module(engine=self.engine, config={"config": self.config}) + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=module) + self.popen.assert_call("xbacklight -10%") + + def test_update(self): + self.popen.mock.communicate.return_value = ("20.0", None) + self.module.update_all() + self.assertEquals(self.module.brightness(self.anyWidget), "020%") + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From aaaf62673d1079e547f4e94899a70b83fa322d73 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 11:48:00 +0100 Subject: [PATCH 19/33] [tests/brightness] Remove unneeded mock for stdout --- tests/modules/test_brightness.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/modules/test_brightness.py b/tests/modules/test_brightness.py index 5112e41..ce63342 100644 --- a/tests/modules/test_brightness.py +++ b/tests/modules/test_brightness.py @@ -12,15 +12,11 @@ import tests.mocks as mocks import bumblebee.input from bumblebee.config import Config -from bumblebee.engine import Engine from bumblebee.input import I3BarInput, WHEEL_UP, WHEEL_DOWN from bumblebee.modules.brightness import Module class TestBrightnessModule(unittest.TestCase): def setUp(self): - self._stdout = mock.patch("sys.stdout", new_callable=StringIO) - self.stdout = self._stdout.start() - self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") self.popen = mocks.MockPopen() @@ -37,7 +33,6 @@ class TestBrightnessModule(unittest.TestCase): self.anyWidget = widget def tearDown(self): - self._stdout.stop() self._stdin.stop() self._select.stop() self.popen.cleanup() From e6d44e789866c94be43f40b7a49d3c0282f671d9 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 11:48:33 +0100 Subject: [PATCH 20/33] [tests/caffeine] Add unit tests for caffeine --- tests/mocks.py | 2 +- tests/modules/test_caffeine.py | 69 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/modules/test_caffeine.py diff --git a/tests/mocks.py b/tests/mocks.py index 4d7eab9..5097e65 100644 --- a/tests/mocks.py +++ b/tests/mocks.py @@ -54,7 +54,7 @@ class MockPopen(object): self.mock.returncode = 0 def assert_call(self, cmd): - self.mock.popen.assert_called_with(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.mock.popen.assert_any_call(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) def cleanup(self): self._patch.stop() diff --git a/tests/modules/test_caffeine.py b/tests/modules/test_caffeine.py new file mode 100644 index 0000000..6eaf228 --- /dev/null +++ b/tests/modules/test_caffeine.py @@ -0,0 +1,69 @@ +# pylint: disable=C0103,C0111 + +import json +import unittest +import mock + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import tests.mocks as mocks + +import bumblebee.input +from bumblebee.config import Config +from bumblebee.input import I3BarInput, LEFT_MOUSE +from bumblebee.modules.caffeine import Module + +class TestCaffeineModule(unittest.TestCase): + def setUp(self): + self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") + + self.popen = mocks.MockPopen() + + self.input = I3BarInput() + self.engine = mock.Mock() + self.config = Config() + self.engine.input = self.input + self.engine.input.need_event = True + + self.module = Module(engine=self.engine, config={ "config": self.config }) + for widget in self.module.widgets(): + widget.link_module(self.module) + self.anyWidget = widget + + self.xset_active = " timeout: 0 cycle: 123" + self.xset_inactive = " timeout: 600 cycle: 123" + + def tearDown(self): + self._stdin.stop() + self._select.stop() + self.popen.cleanup() + + def test_text(self): + self.assertEquals(self.module.caffeine(self.anyWidget), "") + + def test_active(self): + self.popen.mock.communicate.return_value = (self.xset_active, None) + self.assertTrue(not "deactivated" in self.module.state(self.anyWidget)) + self.assertTrue("activated" in self.module.state(self.anyWidget)) + + def test_inactive(self): + self.popen.mock.communicate.return_value = (self.xset_inactive, None) + self.assertTrue("deactivated" in self.module.state(self.anyWidget)) + self.popen.mock.communicate.return_value = ("no text", None) + self.assertTrue("deactivated" in self.module.state(self.anyWidget)) + + def test_toggle(self): + self.popen.mock.communicate.return_value = (self.xset_active, None) + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("xset s default") + self.popen.assert_call("notify-send \"Out of coffee\"") + + self.popen.mock.communicate.return_value = (self.xset_inactive, None) + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("xset s off") + self.popen.assert_call("notify-send \"Consuming caffeine\"") + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 939d9851bdf3656ffca0fc5a5b68ac0d5e412a75 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 12:15:13 +0100 Subject: [PATCH 21/33] [tests/brightness] Remove unused import --- tests/modules/test_brightness.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/modules/test_brightness.py b/tests/modules/test_brightness.py index ce63342..81cc49a 100644 --- a/tests/modules/test_brightness.py +++ b/tests/modules/test_brightness.py @@ -10,7 +10,6 @@ except ImportError: import tests.mocks as mocks -import bumblebee.input from bumblebee.config import Config from bumblebee.input import I3BarInput, WHEEL_UP, WHEEL_DOWN from bumblebee.modules.brightness import Module From 46977f4d36e09cccd5485352b27d1bac4d5b702a Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 12:15:29 +0100 Subject: [PATCH 22/33] [tests/cmus] Add unit tests for cmus module --- tests/modules/test_cmus.py | 118 +++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 tests/modules/test_cmus.py diff --git a/tests/modules/test_cmus.py b/tests/modules/test_cmus.py new file mode 100644 index 0000000..01afdb4 --- /dev/null +++ b/tests/modules/test_cmus.py @@ -0,0 +1,118 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.config import Config +from bumblebee.input import I3BarInput, LEFT_MOUSE +from bumblebee.modules.cmus import Module + +class TestCmusModule(unittest.TestCase): + def setUp(self): + self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") + + self.popen = mocks.MockPopen() + + self.config = Config() + self.input = I3BarInput() + self.engine = mock.Mock() + self.engine.input = self.input + self.input.need_event = True + + self.module = Module(engine=self.engine, config={ "config": self.config }) + for widget in self.module.widgets(): + widget.link_module(self.module) + self.anyWidget = widget + + self.songTemplate = """ +status {status} +file /path/to/file +duration {duration} +position {position} +tag title {title} +tag artist {artist} +tag album {album} +tag tracknumber 1 +tag date 1984 +tag comment comment + """ + + def tearDown(self): + self._stdin.stop() + self._select.stop() + self.popen.cleanup() + + def test_read_song(self): + self.popen.mock.communicate.return_value = ("song", None) + self.module.update_all() + self.popen.assert_call("cmus-remote -Q") + + def test_handle_runtimeerror(self): + self.popen.mock.communicate.side_effect = RuntimeError("error loading song") + self.module.update_all() + self.assertEquals(self.module.description(self.anyWidget), " - /") + + def test_format(self): + self.popen.mock.communicate.return_value = (self.songTemplate.format( + artist="an artist", title="a title", duration="100", position="20", + album="an album", status="irrelevant" + ), None) + self.module.update_all() + self.assertEquals(self.module.description(self.anyWidget), + "an artist - a title 00:20/01:40" + ) + + def test_repeat(self): + self.popen.mock.communicate.return_value = ("set repeat false", None) + self.module.update_all() + self.assertTrue("repeat-off" in self.module.state(self.module.widget("cmus.repeat"))) + self.popen.mock.communicate.return_value = ("set repeat true", None) + self.module.update_all() + self.assertTrue("repeat-on" in self.module.state(self.module.widget("cmus.repeat"))) + + def test_shuffle(self): + self.popen.mock.communicate.return_value = ("set shuffle false", None) + self.module.update_all() + self.assertTrue("shuffle-off" in self.module.state(self.module.widget("cmus.shuffle"))) + self.popen.mock.communicate.return_value = ("set shuffle true", None) + self.module.update_all() + self.assertTrue("shuffle-on" in self.module.state(self.module.widget("cmus.shuffle"))) + + def test_prevnext(self): + self.assertTrue("prev" in self.module.state(self.module.widget("cmus.prev"))) + self.assertTrue("next" in self.module.state(self.module.widget("cmus.next"))) + + def test_main(self): + self.popen.mock.communicate.return_value = ("status paused", None) + self.module.update_all() + self.assertTrue("paused" in self.module.state(self.module.widget("cmus.main"))) + + self.popen.mock.communicate.return_value = ("status playing", None) + self.module.update_all() + self.assertTrue("playing" in self.module.state(self.module.widget("cmus.main"))) + + self.popen.mock.communicate.return_value = ("status stopped", None) + self.module.update_all() + self.assertTrue("stopped" in self.module.state(self.module.widget("cmus.main"))) + + def test_widget(self): + self.assertEquals(len(self.module.widgets()), 5) + + for idx, val in enumerate(["prev", "main", "next", "shuffle", "repeat"]): + self.assertEquals(self.module.widgets()[idx].name, "cmus.{}".format(val)) + + def test_interaction(self): + events = [ + {"widget": "cmus.shuffle", "action": "cmus-remote -S"}, + {"widget": "cmus.repeat", "action": "cmus-remote -R"}, + {"widget": "cmus.next", "action": "cmus-remote -n"}, + {"widget": "cmus.prev", "action": "cmus-remote -r"}, + {"widget": "cmus.main", "action": "cmus-remote -u"}, + ] + for event in events: + mocks.mouseEvent(stdin=self.stdin, inp=self.input, module=self.module, instance=self.module.widget(event["widget"]).id, button=LEFT_MOUSE) + self.popen.assert_call(event["action"]) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 69dceca7d089b356a6a259592640aebf6cdeaece Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 12:30:37 +0100 Subject: [PATCH 23/33] [tests/cpu] Add unit tests for module cpu --- tests/modules/test_cpu.py | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/modules/test_cpu.py diff --git a/tests/modules/test_cpu.py b/tests/modules/test_cpu.py new file mode 100644 index 0000000..b53d2ad --- /dev/null +++ b/tests/modules/test_cpu.py @@ -0,0 +1,61 @@ +# pylint: disable=C0103,C0111 + +import json +import unittest +import mock + +import tests.mocks as mocks + +from bumblebee.config import Config +from bumblebee.input import I3BarInput, LEFT_MOUSE +from bumblebee.modules.cpu import Module + +class TestCPUModule(unittest.TestCase): + def setUp(self): + self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") + + self.popen = mocks.MockPopen() + self._psutil = mock.patch("bumblebee.modules.cpu.psutil") + self.psutil = self._psutil.start() + + self.config = Config() + self.input = I3BarInput() + self.engine = mock.Mock() + self.engine.input = self.input + self.input.need_event = True + self.module = Module(engine=self.engine, config={ "config": self.config }) + for widget in self.module.widgets(): + widget.link_module(self.module) + self.anyWidget = widget + + def tearDown(self): + self._stdin.stop() + self._select.stop() + self._psutil.stop() + self.popen.cleanup() + + def test_format(self): + self.psutil.cpu_percent.return_value = 21.0 + self.module.update_all() + for widget in self.module.widgets(): + self.assertEquals(len(widget.full_text()), len("100.00%")) + + def test_leftclick(self): + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("gnome-system-monitor") + + def test_warning(self): + self.config.set("cpu.critical", "20") + self.config.set("cpu.warning", "18") + self.psutil.cpu_percent.return_value = 19.0 + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_critical(self): + self.config.set("cpu.critical", "20") + self.config.set("cpu.warning", "19") + self.psutil.cpu_percent.return_value = 21.0 + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 00849aa4fa89bde6e2e26ba023622178178fd348 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 13:01:28 +0100 Subject: [PATCH 24/33] [tests] Refactor setting up complex I/O mocking for modules Modules now only have to have a single setup/teardown line in their code to fully set up the I/O part of a test. --- tests/mocks.py | 25 +++++++++++++++++++++++-- tests/modules/test_brightness.py | 22 +++------------------- tests/modules/test_caffeine.py | 22 +++------------------- tests/modules/test_cmus.py | 22 +++------------------- tests/modules/test_cpu.py | 21 +++------------------ 5 files changed, 35 insertions(+), 77 deletions(-) diff --git a/tests/mocks.py b/tests/mocks.py index 5097e65..f5be753 100644 --- a/tests/mocks.py +++ b/tests/mocks.py @@ -3,15 +3,36 @@ import mock import json import shlex +import random import subprocess +from bumblebee.input import I3BarInput from bumblebee.output import Widget - -import random +from bumblebee.config import Config def rand(cnt): return "".join(random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for i in range(cnt)) +def setup_test(test, Module): + test._stdin, test._select, test.stdin, test.select = epoll_mock("bumblebee.input") + + test.popen = MockPopen() + + test.config = Config() + test.input = I3BarInput() + test.engine = mock.Mock() + test.engine.input = test.input + test.input.need_event = True + test.module = Module(engine=test.engine, config={ "config": test.config }) + for widget in test.module.widgets(): + widget.link_module(test.module) + test.anyWidget = widget + +def teardown_test(test): + test._stdin.stop() + test._select.stop() + test.popen.cleanup() + def epoll_mock(module=""): if len(module) > 0: module = "{}.".format(module) diff --git a/tests/modules/test_brightness.py b/tests/modules/test_brightness.py index 81cc49a..b636d44 100644 --- a/tests/modules/test_brightness.py +++ b/tests/modules/test_brightness.py @@ -10,31 +10,15 @@ except ImportError: import tests.mocks as mocks -from bumblebee.config import Config -from bumblebee.input import I3BarInput, WHEEL_UP, WHEEL_DOWN +from bumblebee.input import WHEEL_UP, WHEEL_DOWN from bumblebee.modules.brightness import Module class TestBrightnessModule(unittest.TestCase): def setUp(self): - self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") - - self.popen = mocks.MockPopen() - - self.config = Config() - self.input = I3BarInput() - self.engine = mock.Mock() - self.engine.input = self.input - self.input.need_event = True - - self.module = Module(engine=self.engine, config={ "config": self.config }) - for widget in self.module.widgets(): - widget.link_module(self.module) - self.anyWidget = widget + mocks.setup_test(self, Module) def tearDown(self): - self._stdin.stop() - self._select.stop() - self.popen.cleanup() + mocks.teardown_test(self) def test_format(self): for widget in self.module.widgets(): diff --git a/tests/modules/test_caffeine.py b/tests/modules/test_caffeine.py index 6eaf228..e95e2ac 100644 --- a/tests/modules/test_caffeine.py +++ b/tests/modules/test_caffeine.py @@ -11,35 +11,19 @@ except ImportError: import tests.mocks as mocks -import bumblebee.input from bumblebee.config import Config -from bumblebee.input import I3BarInput, LEFT_MOUSE +from bumblebee.input import LEFT_MOUSE from bumblebee.modules.caffeine import Module class TestCaffeineModule(unittest.TestCase): def setUp(self): - self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") - - self.popen = mocks.MockPopen() - - self.input = I3BarInput() - self.engine = mock.Mock() - self.config = Config() - self.engine.input = self.input - self.engine.input.need_event = True - - self.module = Module(engine=self.engine, config={ "config": self.config }) - for widget in self.module.widgets(): - widget.link_module(self.module) - self.anyWidget = widget + mocks.setup_test(self, Module) self.xset_active = " timeout: 0 cycle: 123" self.xset_inactive = " timeout: 600 cycle: 123" def tearDown(self): - self._stdin.stop() - self._select.stop() - self.popen.cleanup() + mocks.teardown_test(self) def test_text(self): self.assertEquals(self.module.caffeine(self.anyWidget), "") diff --git a/tests/modules/test_cmus.py b/tests/modules/test_cmus.py index 01afdb4..e7118d1 100644 --- a/tests/modules/test_cmus.py +++ b/tests/modules/test_cmus.py @@ -5,26 +5,12 @@ import unittest import tests.mocks as mocks -from bumblebee.config import Config -from bumblebee.input import I3BarInput, LEFT_MOUSE +from bumblebee.input import LEFT_MOUSE from bumblebee.modules.cmus import Module class TestCmusModule(unittest.TestCase): def setUp(self): - self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") - - self.popen = mocks.MockPopen() - - self.config = Config() - self.input = I3BarInput() - self.engine = mock.Mock() - self.engine.input = self.input - self.input.need_event = True - - self.module = Module(engine=self.engine, config={ "config": self.config }) - for widget in self.module.widgets(): - widget.link_module(self.module) - self.anyWidget = widget + mocks.setup_test(self, Module) self.songTemplate = """ status {status} @@ -40,9 +26,7 @@ tag comment comment """ def tearDown(self): - self._stdin.stop() - self._select.stop() - self.popen.cleanup() + mocks.teardown_test(self) def test_read_song(self): self.popen.mock.communicate.return_value = ("song", None) diff --git a/tests/modules/test_cpu.py b/tests/modules/test_cpu.py index b53d2ad..da1d069 100644 --- a/tests/modules/test_cpu.py +++ b/tests/modules/test_cpu.py @@ -6,33 +6,18 @@ import mock import tests.mocks as mocks -from bumblebee.config import Config -from bumblebee.input import I3BarInput, LEFT_MOUSE +from bumblebee.input import LEFT_MOUSE from bumblebee.modules.cpu import Module class TestCPUModule(unittest.TestCase): def setUp(self): - self._stdin, self._select, self.stdin, self.select = mocks.epoll_mock("bumblebee.input") - - self.popen = mocks.MockPopen() + mocks.setup_test(self, Module) self._psutil = mock.patch("bumblebee.modules.cpu.psutil") self.psutil = self._psutil.start() - self.config = Config() - self.input = I3BarInput() - self.engine = mock.Mock() - self.engine.input = self.input - self.input.need_event = True - self.module = Module(engine=self.engine, config={ "config": self.config }) - for widget in self.module.widgets(): - widget.link_module(self.module) - self.anyWidget = widget - def tearDown(self): - self._stdin.stop() - self._select.stop() self._psutil.stop() - self.popen.cleanup() + mocks.teardown_test(self) def test_format(self): self.psutil.cpu_percent.return_value = 21.0 From c7621caf782e44c76d2477813726ea35de00d49c Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 13:04:20 +0100 Subject: [PATCH 25/33] [tests] Add unit tests for the store class --- tests/test_store.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/test_store.py diff --git a/tests/test_store.py b/tests/test_store.py new file mode 100644 index 0000000..faa1efb --- /dev/null +++ b/tests/test_store.py @@ -0,0 +1,26 @@ +# pylint: disable=C0103,C0111,W0703 + +import unittest + +from bumblebee.store import Store + +class TestStore(unittest.TestCase): + def setUp(self): + self.store = Store() + self.anyKey = "some-key" + self.anyValue = "some-value" + self.unsetKey = "invalid-key" + + def test_set_value(self): + self.store.set(self.anyKey, self.anyValue) + self.assertEquals(self.store.get(self.anyKey), self.anyValue) + + def test_get_invalid_value(self): + result = self.store.get(self.unsetKey) + self.assertEquals(result, None) + + def test_get_invalid_with_default_value(self): + result = self.store.get(self.unsetKey, self.anyValue) + self.assertEquals(result, self.anyValue) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From f9984ba386754889e3d4c5e2aa8482434d8a75e1 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 13:16:13 +0100 Subject: [PATCH 26/33] [tests] Add unit tests for load module --- bumblebee/modules/load.py | 2 +- tests/modules/test_load.py | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/modules/test_load.py diff --git a/bumblebee/modules/load.py b/bumblebee/modules/load.py index c44ae5a..4d94ee1 100644 --- a/bumblebee/modules/load.py +++ b/bumblebee/modules/load.py @@ -22,7 +22,7 @@ class Module(bumblebee.engine.Module): self._load = [0, 0, 0] try: self._cpus = multiprocessing.cpu_count() - except multiprocessing.NotImplementedError as e: + except NotImplementedError as e: self._cpus = 1 engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd="gnome-system-monitor") diff --git a/tests/modules/test_load.py b/tests/modules/test_load.py new file mode 100644 index 0000000..e57bc2b --- /dev/null +++ b/tests/modules/test_load.py @@ -0,0 +1,57 @@ +# pylint: disable=C0103,C0111 + +import json +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.load import Module + +class TestLoadModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + + self._mp = mock.patch("bumblebee.modules.load.multiprocessing") + self._os = mock.patch("bumblebee.modules.load.os") + + self.mp = self._mp.start() + self.os = self._os.start() + + self.mp.cpu_count.return_value = 1 + + def tearDown(self): + self._mp.stop() + self._os.stop() + mocks.teardown_test(self) + + def test_leftclick(self): + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("gnome-system-monitor") + + def test_load_format(self): + self.os.getloadavg.return_value = [ 5.9, 1.2, 0.8 ] + self.module.update_all() + self.assertEquals(self.module.load(self.anyWidget), "5.90/1.20/0.80") + + def test_warning(self): + self.config.set("load.critical", "1") + self.config.set("load.warning", "0.8") + self.os.getloadavg.return_value = [ 0.9, 0, 0 ] + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_critical(self): + self.config.set("load.critical", "1") + self.config.set("load.warning", "0.8") + self.os.getloadavg.return_value = [ 1.1, 0, 0 ] + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + + def test_assume_single_core(self): + self.mp.cpu_count.side_effect = NotImplementedError + module = Module(engine=self.engine, config={"config": mock.Mock() }) + self.assertEquals(1, module._cpus) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 826c5686256756f507627de3c22031302b752d65 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 13:30:10 +0100 Subject: [PATCH 27/33] [tests] Add generic module tests These tests check that all widgets of all modules conform to some basic principles, at least. --- bumblebee/modules/dnf.py | 2 -- tests/test_modules.py | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/test_modules.py diff --git a/bumblebee/modules/dnf.py b/bumblebee/modules/dnf.py index 8dc000f..f36dfa9 100644 --- a/bumblebee/modules/dnf.py +++ b/bumblebee/modules/dnf.py @@ -53,9 +53,7 @@ class Module(bumblebee.engine.Module): def __init__(self, engine, config): widget = bumblebee.output.Widget(full_text=self.updates) super(Module, self).__init__(engine, config, widget) - self._next_check = 0 - widget def updates(self, widget): result = [] diff --git a/tests/test_modules.py b/tests/test_modules.py new file mode 100644 index 0000000..aa135e1 --- /dev/null +++ b/tests/test_modules.py @@ -0,0 +1,51 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest +import importlib + +import tests.mocks as mocks + +from bumblebee.engine import all_modules +from bumblebee.output import Widget +from bumblebee.config import Config + +class TestGenericModules(unittest.TestCase): + def setUp(self): + self.popen = mocks.MockPopen() + self.popen.mock.communicate.return_value = (str.encode("1"), "error") + self.popen.mock.returncode = 0 + + engine = mock.Mock() + engine.input = mock.Mock() + config = Config() + self.objects = {} + for mod in all_modules(): + cls = importlib.import_module("bumblebee.modules.{}".format(mod["name"])) + self.objects[mod["name"]] = getattr(cls, "Module")(engine, {"config": config}) + for widget in self.objects[mod["name"]].widgets(): + self.assertEquals(widget.get("variable", None), None) + + def tearDown(self): + self.popen.cleanup() + + def test_widgets(self): + for mod in self.objects: + widgets = self.objects[mod].widgets() + for widget in widgets: + widget.link_module(self.objects[mod]) + self.assertEquals(widget.module, mod) + self.assertTrue(isinstance(widget, Widget)) + self.assertTrue(hasattr(widget, "full_text")) + widget.set("variable", "value") + self.assertEquals(widget.get("variable", None), "value") + self.assertTrue(isinstance(widget.full_text(), str) or isinstance(widget.full_text(), unicode)) + + def test_update(self): + for mod in self.objects: + widgets = self.objects[mod].widgets() + self.objects[mod].update(widgets) + self.test_widgets() + self.assertEquals(widgets, self.objects[mod].widgets()) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From be01980afe4b1dbd5a1d5b07651cd7a54c771d01 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 13:49:45 +0100 Subject: [PATCH 28/33] [tests] Add unit tests for disk module --- tests/modules/test_disk.py | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/modules/test_disk.py diff --git a/tests/modules/test_disk.py b/tests/modules/test_disk.py new file mode 100644 index 0000000..d7e3370 --- /dev/null +++ b/tests/modules/test_disk.py @@ -0,0 +1,47 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.disk import Module + +class MockVFS(object): + def __init__(self, perc): + self.f_blocks = 1024*1024 + self.f_frsize = 1 + self.f_bavail = self.f_blocks - self.f_blocks*(perc/100.0) + +class TestDiskModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + self._os = mock.patch("bumblebee.modules.disk.os") + self.os = self._os.start() + self.config.set("disk.path", "somepath") + + def tearDown(self): + self._os.stop() + mocks.teardown_test(self) + + def test_leftclick(self): + module = Module(engine=self.engine, config={"config":self.config}) + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=module) + self.popen.assert_call("nautilus {}".format(self.module.parameter("path"))) + + def test_warning(self): + self.config.set("disk.critical", "80") + self.config.set("disk.warning", "70") + self.os.statvfs.return_value = MockVFS(75.0) + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_critical(self): + self.config.set("disk.critical", "80") + self.config.set("disk.warning", "70") + self.os.statvfs.return_value = MockVFS(85.0) + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From e07a889393ba673de18912d86409519984c6c466 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 13:49:52 +0100 Subject: [PATCH 29/33] [tests/cpu] Cleaned up unnecessary import --- tests/modules/test_cpu.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/modules/test_cpu.py b/tests/modules/test_cpu.py index da1d069..cd5ce89 100644 --- a/tests/modules/test_cpu.py +++ b/tests/modules/test_cpu.py @@ -1,8 +1,7 @@ # pylint: disable=C0103,C0111 -import json -import unittest import mock +import unittest import tests.mocks as mocks From e59a870a1e039e12da2097401f925146ecc1a5fb Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 13:58:15 +0100 Subject: [PATCH 30/33] [tests] Add unit tests for memory module --- tests/modules/test_memory.py | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/modules/test_memory.py diff --git a/tests/modules/test_memory.py b/tests/modules/test_memory.py new file mode 100644 index 0000000..94c61f9 --- /dev/null +++ b/tests/modules/test_memory.py @@ -0,0 +1,52 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.memory import Module + +class VirtualMemory(object): + def __init__(self, percent): + self.percent = percent + +class TestMemoryModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + self._psutil = mock.patch("bumblebee.modules.memory.psutil") + self.psutil = self._psutil.start() + + def tearDown(self): + self._psutil.stop() + mocks.teardown_test(self) + + def test_leftclick(self): + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("gnome-system-monitor") + + def test_warning(self): + self.config.set("memory.critical", "80") + self.config.set("memory.warning", "70") + self.psutil.virtual_memory.return_value = VirtualMemory(75) + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_critical(self): + self.config.set("memory.critical", "80") + self.config.set("memory.warning", "70") + self.psutil.virtual_memory.return_value = VirtualMemory(81) + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + + def test_usage(self): + rv = VirtualMemory(50) + rv.total = 1000 + rv.available = 500 + self.psutil.virtual_memory.return_value = rv + self.module.update_all() + self.assertEquals("500.00B/1000.00B (50.00%)", self.module.memory_usage(self.anyWidget)) + self.assertEquals(None, self.module.state(self.anyWidget)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From a47860e73c68ffbba97f1a70355223a2052a5f3c Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 14:02:11 +0100 Subject: [PATCH 31/33] [tests] Add unit tests for pulseaudio module --- tests/modules/test_pulseaudio.py | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/modules/test_pulseaudio.py diff --git a/tests/modules/test_pulseaudio.py b/tests/modules/test_pulseaudio.py new file mode 100644 index 0000000..f8754c6 --- /dev/null +++ b/tests/modules/test_pulseaudio.py @@ -0,0 +1,34 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE, RIGHT_MOUSE, WHEEL_UP, WHEEL_DOWN +from bumblebee.modules.pulseaudio import Module + +class TestPulseAudioModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + + def tearDown(self): + mocks.teardown_test(self) + + def test_leftclick(self): + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("pactl set-source-mute @DEFAULT_SOURCE@ toggle") + + def test_rightclick(self): + mocks.mouseEvent(stdin=self.stdin, button=RIGHT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("pavucontrol") + + def test_wheelup(self): + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_UP, inp=self.input, module=self.module) + self.popen.assert_call("pactl set-source-volume @DEFAULT_SOURCE@ +2%") + + def test_wheeldown(self): + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=self.module) + self.popen.assert_call("pactl set-source-volume @DEFAULT_SOURCE@ -2%") + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 11c68310bba5e4867945ee2a23c86fea12efd4e5 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 14:08:26 +0100 Subject: [PATCH 32/33] [tests] generic modules tests don't work on Travis Maybe this fixes it... --- tests/test_modules.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_modules.py b/tests/test_modules.py index aa135e1..6a67353 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -12,10 +12,6 @@ from bumblebee.config import Config class TestGenericModules(unittest.TestCase): def setUp(self): - self.popen = mocks.MockPopen() - self.popen.mock.communicate.return_value = (str.encode("1"), "error") - self.popen.mock.returncode = 0 - engine = mock.Mock() engine.input = mock.Mock() config = Config() @@ -26,10 +22,11 @@ class TestGenericModules(unittest.TestCase): for widget in self.objects[mod["name"]].widgets(): self.assertEquals(widget.get("variable", None), None) - def tearDown(self): - self.popen.cleanup() - def test_widgets(self): + popen = mocks.MockPopen() + popen.mock.communicate.return_value = (str.encode("1"), "error") + popen.mock.returncode = 0 + for mod in self.objects: widgets = self.objects[mod].widgets() for widget in widgets: @@ -40,12 +37,17 @@ class TestGenericModules(unittest.TestCase): widget.set("variable", "value") self.assertEquals(widget.get("variable", None), "value") self.assertTrue(isinstance(widget.full_text(), str) or isinstance(widget.full_text(), unicode)) + popen.cleanup() def test_update(self): + popen = mocks.MockPopen() + popen.mock.communicate.return_value = (str.encode("1"), "error") + popen.mock.returncode = 0 for mod in self.objects: widgets = self.objects[mod].widgets() self.objects[mod].update(widgets) self.test_widgets() self.assertEquals(widgets, self.objects[mod].widgets()) + popen.cleanup() # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 935b50a23b66620e01d068c17782b83366620b05 Mon Sep 17 00:00:00 2001 From: Tobi-wan Kenobi Date: Sun, 5 Mar 2017 14:18:34 +0100 Subject: [PATCH 33/33] [tests] Maybe patching the platform call fixes the issue with Travis --- tests/test_modules.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/test_modules.py b/tests/test_modules.py index 6a67353..4b280a3 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -16,17 +16,27 @@ class TestGenericModules(unittest.TestCase): engine.input = mock.Mock() config = Config() self.objects = {} + + self.popen = mocks.MockPopen() + self.popen.mock.communicate.return_value = (str.encode("1"), "error") + self.popen.mock.returncode = 0 + + self._platform = mock.patch("bumblebee.modules.kernel.platform") + self.platform = self._platform.start() + self.platform.release.return_value = "unknown linux v1" + for mod in all_modules(): - cls = importlib.import_module("bumblebee.modules.{}".format(mod["name"])) + name = "bumblebee.modules.{}".format(mod["name"]) + cls = importlib.import_module(name) self.objects[mod["name"]] = getattr(cls, "Module")(engine, {"config": config}) for widget in self.objects[mod["name"]].widgets(): self.assertEquals(widget.get("variable", None), None) - def test_widgets(self): - popen = mocks.MockPopen() - popen.mock.communicate.return_value = (str.encode("1"), "error") - popen.mock.returncode = 0 + def tearDown(self): + self._platform.stop() + self.popen.cleanup() + def test_widgets(self): for mod in self.objects: widgets = self.objects[mod].widgets() for widget in widgets: @@ -37,17 +47,12 @@ class TestGenericModules(unittest.TestCase): widget.set("variable", "value") self.assertEquals(widget.get("variable", None), "value") self.assertTrue(isinstance(widget.full_text(), str) or isinstance(widget.full_text(), unicode)) - popen.cleanup() def test_update(self): - popen = mocks.MockPopen() - popen.mock.communicate.return_value = (str.encode("1"), "error") - popen.mock.returncode = 0 for mod in self.objects: widgets = self.objects[mod].widgets() self.objects[mod].update(widgets) self.test_widgets() self.assertEquals(widgets, self.objects[mod].widgets()) - popen.cleanup() # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4