Completed
Push — master ( 5cf337...9183d8 )
by dgw
16s queued 11s
created

test_loader.testplugin()   A

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
# coding=utf-8
2
"""Tests for the ``sopel.loader`` module."""
3
from __future__ import unicode_literals, absolute_import, print_function, division
4
5
import inspect
6
7
import pytest
8
9
from sopel import loader, config, module, plugins
10
11
12
MOCK_MODULE_CONTENT = """# coding=utf-8
13
import sopel.module
14
15
16
@sopel.module.commands("first")
17
def first_command(bot, trigger):
18
    pass
19
20
21
@sopel.module.commands("second")
22
def second_command(bot, trigger):
23
    pass
24
25
26
@sopel.module.interval(5)
27
def interval5s(bot):
28
    pass
29
30
31
@sopel.module.interval(10)
32
def interval10s(bot):
33
    pass
34
35
36
@sopel.module.url(r'.\\.example\\.com')
37
def example_url(bot):
38
    pass
39
40
41
@sopel.module.event('TOPIC')
42
def on_topic_command(bot):
43
    pass
44
45
46
def shutdown():
47
    pass
48
49
50
def ignored():
51
    pass
52
53
"""
54
55
56
@pytest.fixture
57
def func():
58
    """Pytest fixture to get a function that will return True all the time"""
59
    def bot_command():
60
        """Test callable defined as a pytest fixture."""
61
        return True
62
    return bot_command
63
64
65
@pytest.fixture
66
def tmpconfig(tmpdir):
67
    conf_file = tmpdir.join('conf.ini')
68
    conf_file.write("\n".join([
69
        "[core]",
70
        "owner=testnick",
71
        "nick = TestBot",
72
        ""
73
    ]))
74
    return config.Config(conf_file.strpath)
75
76
77
@pytest.fixture
78
def testplugin(tmpdir):
79
    root = tmpdir.mkdir('loader_mods')
80
    mod_file = root.join('file_mod.py')
81
    mod_file.write(MOCK_MODULE_CONTENT)
82
83
    return plugins.handlers.PyFilePlugin(mod_file.strpath)
84
85
86
def test_is_triggerable(testplugin):
87
    """Test is_triggerable behavior before clean_module is called."""
88
    testplugin.load()
89
    test_mod = testplugin._module
90
91
    assert loader.is_triggerable(test_mod.first_command)
92
    assert loader.is_triggerable(test_mod.second_command)
93
    assert loader.is_triggerable(test_mod.on_topic_command)
94
95
    assert not loader.is_triggerable(test_mod.interval5s)
96
    assert not loader.is_triggerable(test_mod.interval10s)
97
98
    assert not loader.is_triggerable(test_mod.shutdown)
99
    assert not loader.is_triggerable(test_mod.example_url)
100
101
102
def test_clean_module(testplugin, tmpconfig):
103
    testplugin.load()
104
    test_mod = testplugin._module
105
106
    callables, jobs, shutdowns, urls = loader.clean_module(
107
        test_mod, tmpconfig)
108
109
    assert len(callables) == 3
110
    assert test_mod.first_command in callables
111
    assert test_mod.second_command in callables
112
    assert test_mod.on_topic_command in callables
113
    assert len(jobs) == 2
114
    assert test_mod.interval5s in jobs
115
    assert test_mod.interval10s in jobs
116
    assert len(shutdowns)
117
    assert test_mod.shutdown in shutdowns
118
    assert len(urls) == 1
119
    assert test_mod.example_url in urls
120
121
    # assert is_triggerable behavior *after* clean_module has been called
122
    assert loader.is_triggerable(test_mod.first_command)
123
    assert loader.is_triggerable(test_mod.second_command)
124
    assert loader.is_triggerable(test_mod.on_topic_command)
125
126
    assert not loader.is_triggerable(test_mod.interval5s)
127
    assert not loader.is_triggerable(test_mod.interval10s)
128
129
    assert not loader.is_triggerable(test_mod.shutdown)
130
    assert not loader.is_triggerable(test_mod.example_url)
131
132
    # ignored function is ignored
133
    assert test_mod.ignored not in callables
134
    assert test_mod.ignored not in jobs
135
    assert test_mod.ignored not in shutdowns
136
    assert test_mod.ignored not in urls
137
138
139
def test_clean_module_idempotency(testplugin, tmpconfig):
140
    testplugin.load()
141
    test_mod = testplugin._module
142
143
    callables, jobs, shutdowns, urls = loader.clean_module(
144
        test_mod, tmpconfig)
145
146
    # sanity assertions: check test_clean_module if any of these fails
147
    assert len(callables) == 3
148
    assert len(jobs) == 2
149
    assert len(shutdowns) == 1
150
    assert len(urls) == 1
151
152
    # recall clean_module, we should have the same result
153
    new_callables, new_jobs, new_shutdowns, new_urls = loader.clean_module(
154
        test_mod, tmpconfig)
155
156
    assert new_callables == callables
157
    assert new_jobs == jobs
158
    assert new_shutdowns == shutdowns
159
    assert new_urls == new_urls
160
161
    # assert is_triggerable behavior
162
    assert loader.is_triggerable(test_mod.first_command)
163
    assert loader.is_triggerable(test_mod.second_command)
164
    assert loader.is_triggerable(test_mod.on_topic_command)
165
166
    assert not loader.is_triggerable(test_mod.interval5s)
167
    assert not loader.is_triggerable(test_mod.interval10s)
168
169
    assert not loader.is_triggerable(test_mod.shutdown)
170
    assert not loader.is_triggerable(test_mod.example_url)
171
172
173
def test_clean_callable_default(tmpconfig, func):
174
    loader.clean_callable(func, tmpconfig)
175
176
    # Default values
177
    assert hasattr(func, 'unblockable')
178
    assert func.unblockable is False
179
    assert hasattr(func, 'priority')
180
    assert func.priority == 'medium'
181
    assert hasattr(func, 'thread')
182
    assert func.thread is True
183
    assert hasattr(func, 'rate')
184
    assert func.rate == 0
185
    assert hasattr(func, 'channel_rate')
186
    assert func.rate == 0
187
    assert hasattr(func, 'global_rate')
188
    assert func.global_rate == 0
189
    assert hasattr(func, 'event')
190
    assert func.event == ['PRIVMSG']
191
192
    # Not added by default
193
    assert not hasattr(func, 'rule')
194
    assert not hasattr(func, 'commands')
195
    assert not hasattr(func, 'intents')
196
197
198
def test_clean_callable_event(tmpconfig, func):
199
    setattr(func, 'event', ['low', 'UP', 'MiXeD'])
200
    loader.clean_callable(func, tmpconfig)
201
202
    assert hasattr(func, 'event')
203
    assert func.event == ['LOW', 'UP', 'MIXED']
204
205
    # idempotency
206
    loader.clean_callable(func, tmpconfig)
207
    assert func.event == ['LOW', 'UP', 'MIXED']
208
209
210
def test_clean_callable_event_string(tmpconfig, func):
211
    setattr(func, 'event', 'some')
212
    loader.clean_callable(func, tmpconfig)
213
214
    assert hasattr(func, 'event')
215
    assert func.event == ['SOME']
216
217
    # idempotency
218
    loader.clean_callable(func, tmpconfig)
219
    assert func.event == ['SOME']
220
221
222 View Code Duplication
def test_clean_callable_rule(tmpconfig, func):
223
    setattr(func, 'rule', [r'abc'])
224
    loader.clean_callable(func, tmpconfig)
225
226
    assert hasattr(func, 'rule')
227
    assert len(func.rule) == 1
228
229
    # Test the regex is compiled properly
230
    regex = func.rule[0]
231
    assert regex.match('abc')
232
    assert regex.match('abcd')
233
    assert not regex.match('efg')
234
235
    # idempotency
236
    loader.clean_callable(func, tmpconfig)
237
    assert len(func.rule) == 1
238
    assert regex in func.rule
239
240
241 View Code Duplication
def test_clean_callable_rule_string(tmpconfig, func):
242
    setattr(func, 'rule', r'abc')
243
    loader.clean_callable(func, tmpconfig)
244
245
    assert hasattr(func, 'rule')
246
    assert len(func.rule) == 1
247
248
    # Test the regex is compiled properly
249
    regex = func.rule[0]
250
    assert regex.match('abc')
251
    assert regex.match('abcd')
252
    assert not regex.match('efg')
253
254
    # idempotency
255
    loader.clean_callable(func, tmpconfig)
256
    assert len(func.rule) == 1
257
    assert regex in func.rule
258
259
260 View Code Duplication
def test_clean_callable_rule_nick(tmpconfig, func):
261
    """Assert ``$nick`` in a rule will match ``TestBot: `` or ``TestBot, ``."""
262
    setattr(func, 'rule', [r'$nickhello'])
263
    loader.clean_callable(func, tmpconfig)
264
265
    assert hasattr(func, 'rule')
266
    assert len(func.rule) == 1
267
268
    # Test the regex is compiled properly
269
    regex = func.rule[0]
270
    assert regex.match('TestBot: hello')
271
    assert regex.match('TestBot, hello')
272
    assert not regex.match('TestBot not hello')
273
274
    # idempotency
275
    loader.clean_callable(func, tmpconfig)
276
    assert len(func.rule) == 1
277
    assert regex in func.rule
278
279
280 View Code Duplication
def test_clean_callable_rule_nickname(tmpconfig, func):
281
    """Assert ``$nick`` in a rule will match ``TestBot``."""
282
    setattr(func, 'rule', [r'$nickname\s+hello'])
283
    loader.clean_callable(func, tmpconfig)
284
285
    assert hasattr(func, 'rule')
286
    assert len(func.rule) == 1
287
288
    # Test the regex is compiled properly
289
    regex = func.rule[0]
290
    assert regex.match('TestBot hello')
291
    assert not regex.match('TestBot not hello')
292
293
    # idempotency
294
    loader.clean_callable(func, tmpconfig)
295
    assert len(func.rule) == 1
296
    assert regex in func.rule
297
298
299
def test_clean_callable_nickname_command(tmpconfig, func):
300
    setattr(func, 'nickname_commands', ['hello!'])
301
    loader.clean_callable(func, tmpconfig)
302
303
    assert hasattr(func, 'nickname_commands')
304
    assert len(func.nickname_commands) == 1
305
    assert func.nickname_commands == ['hello!']
306
    assert hasattr(func, 'rule')
307
    assert len(func.rule) == 1
308
309
    regex = func.rule[0]
310
    assert regex.match('TestBot hello!')
311
    assert regex.match('TestBot, hello!')
312
    assert regex.match('TestBot: hello!')
313
    assert not regex.match('TestBot not hello')
314
315
    # idempotency
316
    loader.clean_callable(func, tmpconfig)
317
    assert len(func.rule) == 1
318
    assert regex in func.rule
319
320
321
def test_clean_callable_events(tmpconfig, func):
322
    setattr(func, 'event', ['TOPIC'])
323
    loader.clean_callable(func, tmpconfig)
324
325
    assert hasattr(func, 'event')
326
    assert func.event == ['TOPIC']
327
328
    setattr(func, 'event', ['TOPIC', 'JOIN'])
329
    loader.clean_callable(func, tmpconfig)
330
331
    assert hasattr(func, 'event')
332
    assert func.event == ['TOPIC', 'JOIN']
333
334
    setattr(func, 'event', ['TOPIC', 'join', 'Nick'])
335
    loader.clean_callable(func, tmpconfig)
336
337
    assert hasattr(func, 'event')
338
    assert func.event == ['TOPIC', 'JOIN', 'NICK']
339
340
341
def test_clean_callable_events_basestring(tmpconfig, func):
342
    setattr(func, 'event', 'topic')
343
    loader.clean_callable(func, tmpconfig)
344
345
    assert hasattr(func, 'event')
346
    assert func.event == ['TOPIC']
347
348
    setattr(func, 'event', 'JOIN')
349
    loader.clean_callable(func, tmpconfig)
350
351
    assert hasattr(func, 'event')
352
    assert func.event == ['JOIN']
353
354
355 View Code Duplication
def test_clean_callable_example(tmpconfig, func):
356
    module.commands('test')(func)
357
    module.example('.test hello')(func)
358
359
    loader.clean_callable(func, tmpconfig)
360
361
    assert hasattr(func, '_docs')
362
    assert len(func._docs) == 1
363
    assert 'test' in func._docs
364
365
    docs = func._docs['test']
366
    assert len(docs) == 2
367
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
368
    assert docs[1] == ['.test hello']
369
370
371 View Code Duplication
def test_clean_callable_example_not_set(tmpconfig, func):
372
    module.commands('test')(func)
373
374
    loader.clean_callable(func, tmpconfig)
375
376
    assert hasattr(func, '_docs')
377
    assert len(func._docs) == 1
378
    assert 'test' in func._docs
379
380
    docs = func._docs['test']
381
    assert len(docs) == 2
382
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
383
    assert docs[1] == []
384
385
386 View Code Duplication
def test_clean_callable_example_multi_commands(tmpconfig, func):
387
    module.commands('test')(func)
388
    module.commands('unit')(func)
389
    module.example('.test hello')(func)
390
391
    loader.clean_callable(func, tmpconfig)
392
393
    assert hasattr(func, '_docs')
394
    assert len(func._docs) == 2
395
    assert 'test' in func._docs
396
    assert 'unit' in func._docs
397
398
    test_docs = func._docs['test']
399
    unit_docs = func._docs['unit']
400
    assert len(test_docs) == 2
401
    assert test_docs == unit_docs
402
403
    assert test_docs[0] == inspect.cleandoc(func.__doc__).splitlines()
404
    assert test_docs[1] == ['.test hello']
405
406
407 View Code Duplication
def test_clean_callable_example_first_only(tmpconfig, func):
408
    module.commands('test')(func)
409
    module.example('.test hello')(func)
410
    module.example('.test bonjour')(func)
411
412
    loader.clean_callable(func, tmpconfig)
413
414
    assert len(func._docs) == 1
415
    assert 'test' in func._docs
416
417
    docs = func._docs['test']
418
    assert len(docs) == 2
419
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
420
    assert docs[1] == ['.test hello']
421
422
423 View Code Duplication
def test_clean_callable_example_first_only_multi_commands(tmpconfig, func):
424
    module.commands('test')(func)
425
    module.commands('unit')(func)
426
    module.example('.test hello')(func)
427
    module.example('.test bonjour')(func)
428
429
    loader.clean_callable(func, tmpconfig)
430
431
    assert hasattr(func, '_docs')
432
    assert len(func._docs) == 2
433
    assert 'test' in func._docs
434
    assert 'unit' in func._docs
435
436
    test_docs = func._docs['test']
437
    unit_docs = func._docs['unit']
438
    assert len(test_docs) == 2
439
    assert test_docs == unit_docs
440
441
    assert test_docs[0] == inspect.cleandoc(func.__doc__).splitlines()
442
    assert test_docs[1] == ['.test hello']
443
444
445 View Code Duplication
def test_clean_callable_example_user_help(tmpconfig, func):
446
    module.commands('test')(func)
447
    module.example('.test hello', user_help=True)(func)
448
449
    loader.clean_callable(func, tmpconfig)
450
451
    assert len(func._docs) == 1
452
    assert 'test' in func._docs
453
454
    docs = func._docs['test']
455
    assert len(docs) == 2
456
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
457
    assert docs[1] == ['.test hello']
458
459
460 View Code Duplication
def test_clean_callable_example_user_help_multi(tmpconfig, func):
461
    module.commands('test')(func)
462
    module.example('.test hello', user_help=True)(func)
463
    module.example('.test bonjour', user_help=True)(func)
464
465
    loader.clean_callable(func, tmpconfig)
466
467
    assert len(func._docs) == 1
468
    assert 'test' in func._docs
469
470
    docs = func._docs['test']
471
    assert len(docs) == 2
472
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
473
    assert docs[1] == ['.test hello', '.test bonjour']
474
475
476 View Code Duplication
def test_clean_callable_example_user_help_mixed(tmpconfig, func):
477
    module.commands('test')(func)
478
    module.example('.test hello')(func)
479
    module.example('.test bonjour', user_help=True)(func)
480
481
    loader.clean_callable(func, tmpconfig)
482
483
    assert len(func._docs) == 1
484
    assert 'test' in func._docs
485
486
    docs = func._docs['test']
487
    assert len(docs) == 2
488
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
489
    assert docs[1] == ['.test bonjour']
490
491
492 View Code Duplication
def test_clean_callable_example_default_prefix(tmpconfig, func):
493
    module.commands('test')(func)
494
    module.example('.test hello')(func)
495
496
    tmpconfig.core.help_prefix = '!'
497
    loader.clean_callable(func, tmpconfig)
498
499
    assert len(func._docs) == 1
500
    assert 'test' in func._docs
501
502
    docs = func._docs['test']
503
    assert len(docs) == 2
504
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
505
    assert docs[1] == ['!test hello']
506
507
508 View Code Duplication
def test_clean_callable_example_nickname(tmpconfig, func):
509
    module.commands('test')(func)
510
    module.example('$nickname: hello')(func)
511
512
    loader.clean_callable(func, tmpconfig)
513
514
    assert len(func._docs) == 1
515
    assert 'test' in func._docs
516
517
    docs = func._docs['test']
518
    assert len(docs) == 2
519
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
520
    assert docs[1] == ['TestBot: hello']
521
522
523 View Code Duplication
def test_clean_callable_example_nickname_custom_prefix(tmpconfig, func):
524
    module.commands('test')(func)
525
    module.example('$nickname: hello')(func)
526
527
    tmpconfig.core.help_prefix = '!'
528
    loader.clean_callable(func, tmpconfig)
529
530
    assert len(func._docs) == 1
531
    assert 'test' in func._docs
532
533
    docs = func._docs['test']
534
    assert len(docs) == 2
535
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
536
    assert docs[1] == ['TestBot: hello']
537
538
539
def test_clean_callable_intents(tmpconfig, func):
540
    setattr(func, 'intents', [r'abc'])
541
    loader.clean_callable(func, tmpconfig)
542
543
    assert hasattr(func, 'intents')
544
    assert len(func.intents) == 1
545
546
    # Test the regex is compiled properly
547
    regex = func.intents[0]
548
    assert regex.match('abc')
549
    assert regex.match('abcd')
550
    assert regex.match('ABC')
551
    assert regex.match('AbCdE')
552
    assert not regex.match('efg')
553
554
    # idempotency
555
    loader.clean_callable(func, tmpconfig)
556
    assert len(func.intents) == 1
557
    assert regex in func.intents
558