Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import print_function
2from pi3bar.app import Pi3Bar
3from pi3bar.io import I3BarEventThread
4from pi3bar.plugins.utils import NoPlugins, Version
5from pi3bar.plugins.base import Plugin
6import json
7import logging
8import os
9import sys
10import unittest
11from unittest import mock
14PLUGIN_OUTPUT = {
15 "instance": "0",
16 "short_text": "",
17 "name": "Plugin",
18 "full_text": "test"
19}
22@mock.patch('pi3bar.app.logging')
23class Pi3BarTestCase(unittest.TestCase):
24 def setUp(self):
25 self._original_modules = sys.modules.copy()
27 def tearDown(self):
28 sys.modules = self._original_modules
30 @mock.patch.object(I3BarEventThread, '__init__', return_value=None)
31 def test_init_initializes_i3bar_event_thread(self, mock_event_thread_init, mock_logging):
32 pi3bar = Pi3Bar()
33 mock_event_thread_init.assert_called_with(pi3bar.on_plugin_click, pi3bar.logger)
35 def test_init_empty(self, mock_logging):
36 pi3bar = Pi3Bar()
37 self.assertEqual([], pi3bar.plugins)
38 self.assertIsInstance(pi3bar.event_thread, I3BarEventThread)
40 def test_alternative_logging(self, mock_logging):
41 mock_logging.getLogger.return_value = mock.MagicMock()
42 pi3bar = Pi3Bar(log_level=logging.WARNING)
43 pi3bar.logger.setLevel.assert_called_once_with(logging.WARNING)
45 def test_kwargs_log_path(self, mock_logging):
46 Pi3Bar(log_path='/home/test/status.log')
47 mock_logging.FileHandler.assert_called_once_with('/home/test/status.log')
49 def test_default_log_path(self, mock_logging):
50 sys.modules['__main__'] = mock.Mock()
51 sys.modules['__main__'].__file__ = '/random-test/path/file.py'
53 Pi3Bar()
55 mock_logging.FileHandler.assert_called_once_with('/random-test/path/status.log')
57 def test_default_log_path_alternative(self, mock_logging):
58 """check default log path if __main__ has no __file__ attribute"""
59 sys.modules['__main__'] = object()
61 Pi3Bar()
63 mock_logging.FileHandler.assert_called_once_with(os.path.join(os.path.expanduser('~'), '.pi3bar.log'))
65 @mock.patch.object(Plugin, 'info')
66 def test_init_plugins(self, mock_info, mock_logging):
67 p1 = Plugin()
68 p2 = Plugin()
69 plugins = [p1, p2]
70 pi3bar = Pi3Bar(*plugins)
72 call = mock.call('Registered')
73 mock_info.assert_has_calls([call, call])
75 self.assertEqual(2, len(pi3bar.plugins))
77 def test_init_plugins_instance_renaming(self, mock_logging):
78 plugins = [
79 Plugin(),
80 Plugin(),
81 ]
82 pi3bar = Pi3Bar(*plugins)
83 self.assertEqual(plugins, pi3bar.plugins)
84 self.assertEqual('0', pi3bar.plugins[0].instance)
85 self.assertEqual('1', pi3bar.plugins[1].instance)
87 def test_init_existing_plugin_is_ignored(self, mock_logging):
88 p = Plugin()
90 with mock.patch('pi3bar.app.logging.getLogger') as m:
91 m.return_value = mock.MagicMock()
92 pi3bar = Pi3Bar(p, p)
94 calls = [
95 mock.call('Plugin %s already registered!' % p)
96 ]
97 pi3bar.logger.error.assert_has_calls(calls)
98 self.assertEqual([p], pi3bar.plugins)
100 def test_init_plugin_with_existing_name_and_instance_is_ignored(self, mock_logging):
101 p1 = Plugin(instance='plugin')
102 p2 = Plugin(instance='plugin')
104 with mock.patch('pi3bar.app.logging.getLogger') as m:
105 m.return_value = mock.MagicMock()
106 pi3bar = Pi3Bar(p1, p2)
108 calls = [
109 mock.call('Plugin %s already registered!' % p2)
110 ]
111 pi3bar.logger.error.assert_has_calls(calls)
112 self.assertEqual([p1], pi3bar.plugins)
114 @mock.patch.object(Plugin, 'get_output', return_value=PLUGIN_OUTPUT)
115 @mock.patch('pi3bar.app.json')
116 def test_collect_output(self, mock_json, mock_get_output, mock_logging):
117 mock_json.dumps.return_value = json.dumps(PLUGIN_OUTPUT)
119 p1 = Plugin()
120 p2 = Plugin()
121 pi3bar = Pi3Bar(p1, p2)
122 output = pi3bar.collect_output()
124 self.assertTrue(output.startswith(',[{'))
125 self.assertTrue(output.endswith('}]'))
126 self.assertIn('},{', output)
127 mock_get_output.assert_has_calls([mock.call(), mock.call()])
128 mock_json.dumps.assert_has_calls([mock.call(PLUGIN_OUTPUT), mock.call(PLUGIN_OUTPUT)])
129 pi3bar.logger.debug.assert_has_calls(
130 [
131 mock.call('collect_output() [%s] %s' % (plugin, plugin.get_output()))
132 for plugin in [p1, p2]
133 ]
134 )
136 @mock.patch.object(Plugin, 'get_output', return_value=PLUGIN_OUTPUT)
137 @mock.patch('pi3bar.app.json')
138 def test_collect_output_logs_TypeError_in_plugin_output(self, mock_json, mock_get_output, mock_logging):
139 def dumps_effect(d):
140 if mock_json.dumps.call_count == 1:
141 raise TypeError
142 return json.dumps(d)
143 mock_json.dumps.side_effect = dumps_effect
145 plugin = Plugin()
146 pi3bar = Pi3Bar(plugin)
147 output = pi3bar.collect_output()
149 error_dict = {
150 'name': plugin.name,
151 'full_text': 'ERROR',
152 'short_text': 'E',
153 'background': '#ff0000',
154 }
155 mock_json.dumps.assert_has_calls([mock.call(PLUGIN_OUTPUT), mock.call(error_dict)])
156 pi3bar.logger.error.assert_called_once_with('TypeError in plugin {}:'.format(plugin), exc_info=1)
157 self.assertIn(json.dumps(error_dict), output)
159 @mock.patch('pi3bar.app.json')
160 def test_collect_output_without_plugins_appends_info_and_version(self, mock_json, mock_logging):
161 mock_json.dumps.side_effect = lambda d: json.dumps(d)
163 pi3bar = Pi3Bar()
164 output = pi3bar.collect_output()
166 mock_json.dumps.assert_has_calls(
167 [
168 mock.call(Version().get_output()),
169 mock.call(NoPlugins().get_output())
170 ]
171 )
172 self.assertIn('version', output)
174 @mock.patch.object(Pi3Bar, 'collect_output', return_value=',[{}]')
175 @mock.patch('pi3bar.io.write_sys')
176 def test_tick_collect_write(self, mock_write, mock_collect_output, mock_logging):
177 pi3bar = Pi3Bar()
178 pi3bar.collect_write()
179 mock_write.assert_called_once_with(',[{}]')
181 @mock.patch.object(Plugin, 'on_click')
182 def test_on_plugin_click(self, mock_on_click, mock_logging):
183 p = Plugin()
184 pi3bar = Pi3Bar(p)
185 pi3bar.on_plugin_click(p.name, p.instance, 1, 1916, 1071)
186 mock_on_click.assert_called_once_with(1, 1916, 1071)
188 @mock.patch.object(Plugin, 'on_click', side_effect=ValueError)
189 def test_on_plugin_click_exception_is_logged(self, mock_on_click, mock_logging):
190 p = Plugin()
191 pi3bar = Pi3Bar(p)
192 pi3bar.on_plugin_click(p.name, p.instance, 1, 1916, 1071)
193 mock_on_click.assert_called_once_with(1, 1916, 1071)
194 pi3bar.logger.error.assert_called_once_with('Exception in %s.on_click:' % p, exc_info=1)
196 def test_on_plugin_click_on_sibling_is_ignored(self, mock_logging):
197 p = Plugin()
198 p2 = Plugin(instance='2')
199 pi3bar = Pi3Bar(p, p2)
201 with mock.patch.object(p, 'on_click') as mock_p_click:
202 with mock.patch.object(p2, 'on_click') as mock_p2_click:
203 pi3bar.on_plugin_click(p2.name, p2.instance, 1, 1916, 1071)
205 mock_p2_click.assert_called_once_with(1, 1916, 1071)
206 self.assertEqual(0, mock_p_click.call_count)
208 def test_on_plugin_click_by_unknown_is_logged(self, mock_logging):
209 p = Plugin()
210 with mock.patch('pi3bar.app.logging.getLogger') as m:
211 m.return_value = mock.MagicMock()
212 pi3bar = Pi3Bar()
213 pi3bar.on_plugin_click(p.name, p.instance, 1, 1916, 1071)
214 pi3bar.logger.error.assert_called_once_with(
215 'Click by unregistered plugin "%s" instance "%s"' % (p.name, p.instance)
216 )
218 @mock.patch.object(I3BarEventThread, 'start')
219 @mock.patch('pi3bar.io.write_sys')
220 @mock.patch('time.sleep')
221 @mock.patch.object(Plugin, 'run')
222 def test_run(self, mock_tick, mock_sleep, mock_write, mock_event_init, mock_logging):
223 mock_logging.setLevel(logging.ERROR)
224 p = Plugin()
225 pi3bar = Pi3Bar(p)
227 def write_side_effect(msg):
228 if mock_write.call_count == 4:
229 # exit after the 4th call which is the first run in loop
230 pi3bar.event_thread.CONTINUE = False
231 mock_write.side_effect = write_side_effect
233 pi3bar.run()
235 mock_write.assert_has_calls([
236 mock.call('{ "version": 1, "click_events": true }'), # header
237 mock.call('['), # start array
238 mock.call('[%s]' % json.dumps(Version().get_output())), # version
239 mock.call(',[%s]' % json.dumps(p.get_output()))
240 ])
241 mock_sleep.assert_has_calls([
242 mock.call(1), # after version
243 mock.call(1) # after first run
244 ])
245 mock_tick.assert_called_once_with()