1# -*- coding: utf-8 -*-
2# pylint: disable=missing-docstring
3# pylint: disable=no-method-argument
4# pylint: disable=no-self-use
5# pylint: disable=too-many-public-methods
6# pylint: disable=unused-argument
7# pylint: disable=too-many-instance-attributes
8# pylint: disable=invalid-name
9# pylint: disable=too-many-arguments
10
11from __future__ import unicode_literals
12
13import collections
14import copy
15import io
16import os
17import shutil
18import tarfile
19import types
20
21class MockWeechat(types.ModuleType):
22
23    WEECHAT_RC_ERROR = None
24    WEECHAT_RC_OK = None
25
26    def __init__(self, weechat_dir):
27        self.weechat_dir = weechat_dir
28
29        self.config_options = {}
30        self.script_name = None
31        self.printed = {}
32        self.saved_state = None
33        self.weechat_dir_tar = None
34        self.infos = {
35            ('',) : {
36                'weechat_dir': self.weechat_dir,
37                'version_number': 0x01000100,
38                },
39            ('server',) : {
40                'irc_nick': 'nick',
41                },
42            ('server,nick',) : {
43                'irc_buffer' : 'server_nick_buffer',
44                },
45            ('server,nick2',) : {
46                'irc_buffer' : 'server_nick2_buffer',
47                },
48            ('server,no_window_nick',) : {
49                'irc_buffer' : 'non_private_buffer',
50                },
51            ('server,STATUSMSG',) : {
52                'irc_server_isupport_value' : '@+',
53                },
54            ('server,CHANTYPES',) : {
55                'irc_server_isupport_value' : '#',
56                },
57        }
58        self.buffers = {
59            None : {
60                'localvar_type' : 'private',
61                'localvar_channel' : 'nick',
62                'localvar_server' : 'server',
63                'localvar_nick' : 'me',
64                },
65            'server_nick_buffer' : {
66                'localvar_type' : 'private',
67                'localvar_channel' : 'nick',
68                'localvar_server' : 'server',
69                'name' : 'server_nick_buffer_name',
70                'plugin' : 'irc',
71                },
72            'server_nick2_buffer': {
73                'localvar_type' : 'private',
74                'localvar_channel' : 'nick2',
75                'localvar_server' : 'server',
76                'name' : 'server_nick2_buffer_name',
77                'plugin' : 'irc',
78                },
79            'non_private_buffer' : {
80                'localvar_type' : 'non_private',
81                'name' : 'non_private_buffer_name',
82                'plugin' : 'irc',
83                }
84            }
85        self.config_written = []
86        self.bar_items_removed = []
87        self.config_section_free_options_calls = []
88        self.config_section_free_calls = []
89        self.config_free_calls = []
90        self.buffer_new_calls = []
91        self.buffer_sets = {}
92        self.buffer_search_calls = []
93        self.buffer_search_returns = []
94        self.info_hashtables = {}
95        self.info_get_hashtable_raise = None
96        self.infolists = {}
97        self.config_integer_defaults = {}
98        self.commands = []
99        self.hook_signals = []
100        self.info_gets = []
101        self.window_get_pointers = {}
102
103    def save(self):
104        self.snapshot_weechat_dir()
105        self.saved_state = copy.deepcopy(self.__dict__)
106
107    def restore(self):
108        prev_state = copy.deepcopy(self.saved_state)
109        self.__dict__.clear()
110        self.__dict__.update(prev_state)
111
112        self.restore_weechat_dir()
113
114    def snapshot_weechat_dir(self):
115        tar_io = io.BytesIO()
116        # Note that we cannot use "with tarfile.open" due to lack of support
117        # for context manager in Python 2.6
118        tar = tarfile.open(fileobj=tar_io, mode='w')
119        tar.add(self.weechat_dir, '.')
120        tar.close()
121        self.weechat_dir_tar = tar_io.getvalue()
122
123    def restore_weechat_dir(self):
124        shutil.rmtree(self.weechat_dir)
125        tar_io = io.BytesIO(self.weechat_dir_tar)
126        # Note that we cannot use "with tarfile.open" due to lack of support
127        # for context manager in Python 2.6
128        tar = tarfile.open(fileobj=tar_io)
129        tar.extractall(self.weechat_dir)
130        tar.close()
131
132    def bar_item_new(*args):
133        return 'bar item'
134
135    def bar_item_update(*args):
136        pass
137
138    def buffer_get_string(self, buf, string):
139        return self.buffers[buf].get(string)
140
141    def buffer_search(self, plugin, name):
142        self.buffer_search_calls.append((plugin, name))
143        return self.buffer_search_returns.pop(0)
144
145    def command(self, *args):
146        self.commands.append(args)
147
148    def config_boolean(self, val):
149        if val == 'on':
150            return 1
151        return 0
152
153    def config_get(self, key):
154        return self.config_options.get(key, '')
155
156    def config_new(*args):
157        return 'config file'
158
159    def config_new_option(self, config_file, section, name, *args):
160        parts = [self.script_name]
161        if section is not None:
162            parts.append(section)
163        parts.append(name)
164        default = args[5]
165        full_option_name = '.'.join(parts)
166
167        self.config_options[full_option_name] = default
168
169    def config_new_section(self, config_file, name, *args):
170        return name
171
172    def config_read(*args):
173        pass
174
175    def config_string(self, key):
176        return key
177
178    def current_buffer(*args):
179        pass
180
181    def hook_command(*args):
182        pass
183
184    def hook_completion(*args):
185        pass
186
187    def hook_config(*args):
188        pass
189
190    def hook_modifier(*args):
191        pass
192
193    def hook_signal(self, *args):
194        self.hook_signals.append(args)
195
196    def info_get(self, name, *args):
197        self.info_gets.append((name, args))
198
199        return self.infos[args].get(name)
200
201    def info_get_hashtable(self, name, args):
202        # pylint: disable=too-many-branches
203        # pylint: disable=too-many-statements
204        # pylint: disable=too-many-nested-blocks
205        if self.info_get_hashtable_raise:
206            # pylint: disable=raising-bad-type
207            raise self.info_get_hashtable_raise
208        if name in self.info_hashtables:
209            return self.info_hashtables[name].pop()
210        if name == 'irc_message_parse':
211            # Python translation of WeeChat's message parsing code.
212            #
213            # https://github.com/weechat/weechat/blob/bd06f0f60f8c3f5ab883df9c
214            # b876fe29715055b3/src/plugins/irc/irc-message.c#L43-210
215            result = {
216                'arguments' : '',
217                'channel' : '',
218                'host' : '',
219                'nick' : ''
220                }
221
222            nick_set = False
223
224            ptr_message = args['message']
225
226            if ptr_message[0] == ':':
227                pos3 = ptr_message.find('@')
228                pos2 = ptr_message.find('!')
229                pos = ptr_message.find(' ')
230                if pos2 == -1 or (pos != -1 and pos2 > pos):
231                    pos2 = pos3
232                if pos2 != -1 and (pos == -1 or pos > pos2):
233                    result['nick'] = ptr_message[1:pos2]
234                    nick_set = True
235                elif pos != -1:
236                    result['nick'] = ptr_message[1:pos]
237                    nick_set = True
238                if pos != -1:
239                    result['host'] = ptr_message[1:pos]
240                    ptr_message = ptr_message[pos:].lstrip()
241                else:
242                    result['host'] = ptr_message[1:]
243                    ptr_message = ''
244
245            if ptr_message:
246                pos = ptr_message.find(' ')
247                if pos != -1:
248                    result['command'] = ptr_message[:pos]
249                    pos += 1
250                    while ptr_message[pos] == ' ':
251                        pos += 1
252                    result['arguments'] = ptr_message[pos:]
253                    if ptr_message[pos] != ':':
254                        if ptr_message[pos] in ('#', '&', '+', '!'):
255                            pos2 = ptr_message[pos:].find(' ')
256                            if pos2 != -1:
257                                result['channel'] = ptr_message[pos:][:pos2]
258                            else:
259                                result['channel'] = ptr_message[pos:]
260                        else:
261                            pos2 = ptr_message[pos:].find(' ')
262                            if not nick_set:
263                                if pos2 != -1:
264                                    result['nick'] = ptr_message[pos:][:pos2]
265                                else:
266                                    result['nick'] = ptr_message[pos:]
267                            if pos2 != -1:
268                                pos3 = pos2
269                                pos2 += 1
270                                while ptr_message[pos2] == ' ':
271                                    pos2 += 1
272                                if ptr_message[pos2] in ('#', '&', '+', '!'):
273                                    pos4 = ptr_message[pos2:].find(' ')
274                                    if pos4 != -1:
275                                        result['channel'] = \
276                                            ptr_message[pos2:][:pos4]
277                                    else:
278                                        result['channel'] = ptr_message[pos2:]
279                                elif not result['channel']:
280                                    result['channel'] = ptr_message[pos:][:pos3]
281                else:
282                    result['command'] = ptr_message
283
284            return result
285
286    def infolist_free(*args):
287        pass
288
289    def infolist_get(self, *args):
290        return collections.deque(self.infolists.get(args, []) + [False])
291
292    def infolist_next(self, infolist):
293        # The current item in the infolist is the last item.
294        infolist.rotate(-1)
295        return infolist[-1]
296
297    def infolist_integer(self, infolist, key):
298        return infolist[-1]['integer'][key]
299
300    def infolist_pointer(self, infolist, key):
301        return infolist[-1]['pointer'][key]
302
303    def mkdir_home(self, name, mode):
304        os.mkdir(os.path.join(self.weechat_dir, name), mode)
305
306    def string_eval_expression(self, expr, pointers, extra_vars, options):
307        return 'eval({0})'.format(expr)
308
309    def config_color(self, key):
310        return key
311
312    def color(self, name):
313        return '(color {0})'.format(name)
314
315    def prnt(self, buf, message):
316        self.printed.setdefault(buf, []).append(message)
317
318    def register(self, script_name, *args):
319        self.script_name = script_name
320
321        return True
322
323    def set_server_current_nick(self, server, nick):
324        self.infos[(server, )]['irc_nick'] = nick
325
326    def config_integer_default(self, key):
327        return self.config_integer_defaults.get(key)
328
329    def config_write(self, *args):
330        self.config_written.append(args)
331
332    def config_section_free_options(self, *args):
333        self.config_section_free_options_calls.append(args)
334
335    def config_section_free(self, *args):
336        self.config_section_free_calls.append(args)
337
338    def config_free(self, *args):
339        self.config_free_calls.append(args)
340
341    def bar_item_remove(self, *args):
342        self.bar_items_removed.append(args)
343
344    def buffer_new(self, *args):
345        self.buffer_new_calls.append(args)
346
347        return args[0]
348
349    def buffer_set(self, name, key, value):
350        self.buffer_sets.setdefault(name, {}).update({key: value})
351
352    def window_get_pointer(self, *args):
353        return self.window_get_pointers[args]
354
355    def add_channel_prefix(self, prefix):
356        self.infos[('server,CHANTYPES',)]['irc_server_isupport_value'] += \
357                prefix
358
359    def server_no_chantypes(self):
360        self.infos[('server,CHANTYPES',)]['irc_server_isupport_value'] = ''
361        self.infos[('server,STATUSMSG',)]['irc_server_isupport_value'] = ''
362