Hide keyboard shortcuts

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 

12 

13 

14PLUGIN_OUTPUT = { 

15 "instance": "0", 

16 "short_text": "", 

17 "name": "Plugin", 

18 "full_text": "test" 

19} 

20 

21 

22@mock.patch('pi3bar.app.logging') 

23class Pi3BarTestCase(unittest.TestCase): 

24 def setUp(self): 

25 self._original_modules = sys.modules.copy() 

26 

27 def tearDown(self): 

28 sys.modules = self._original_modules 

29 

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) 

34 

35 def test_init_empty(self, mock_logging): 

36 pi3bar = Pi3Bar() 

37 self.assertEqual([], pi3bar.plugins) 

38 self.assertIsInstance(pi3bar.event_thread, I3BarEventThread) 

39 

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) 

44 

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') 

48 

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' 

52 

53 Pi3Bar() 

54 

55 mock_logging.FileHandler.assert_called_once_with('/random-test/path/status.log') 

56 

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() 

60 

61 Pi3Bar() 

62 

63 mock_logging.FileHandler.assert_called_once_with(os.path.join(os.path.expanduser('~'), '.pi3bar.log')) 

64 

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) 

71 

72 call = mock.call('Registered') 

73 mock_info.assert_has_calls([call, call]) 

74 

75 self.assertEqual(2, len(pi3bar.plugins)) 

76 

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) 

86 

87 def test_init_existing_plugin_is_ignored(self, mock_logging): 

88 p = Plugin() 

89 

90 with mock.patch('pi3bar.app.logging.getLogger') as m: 

91 m.return_value = mock.MagicMock() 

92 pi3bar = Pi3Bar(p, p) 

93 

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) 

99 

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') 

103 

104 with mock.patch('pi3bar.app.logging.getLogger') as m: 

105 m.return_value = mock.MagicMock() 

106 pi3bar = Pi3Bar(p1, p2) 

107 

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) 

113 

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) 

118 

119 p1 = Plugin() 

120 p2 = Plugin() 

121 pi3bar = Pi3Bar(p1, p2) 

122 output = pi3bar.collect_output() 

123 

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 ) 

135 

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 

144 

145 plugin = Plugin() 

146 pi3bar = Pi3Bar(plugin) 

147 output = pi3bar.collect_output() 

148 

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) 

158 

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) 

162 

163 pi3bar = Pi3Bar() 

164 output = pi3bar.collect_output() 

165 

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) 

173 

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(',[{}]') 

180 

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) 

187 

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) 

195 

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) 

200 

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) 

204 

205 mock_p2_click.assert_called_once_with(1, 1916, 1071) 

206 self.assertEqual(0, mock_p_click.call_count) 

207 

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 ) 

217 

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) 

226 

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 

232 

233 pi3bar.run() 

234 

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()