Passed
Pull Request — master (#1709)
by dgw
01:44
created

test_loader.test_clean_callable_command()   A

Complexity

Conditions 1

Size

Total Lines 21
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 21
rs 9.45
c 0
b 0
f 0
cc 1
nop 2
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, 'thread')
178
    assert func.thread is True
179
180
    # Not added by default
181
    assert not hasattr(func, 'unblockable')
182
    assert not hasattr(func, 'priority')
183
    assert not hasattr(func, 'rate')
184
    assert not hasattr(func, 'channel_rate')
185
    assert not hasattr(func, 'global_rate')
186
    assert not hasattr(func, 'event')
187
    assert not hasattr(func, 'rule')
188
    assert not hasattr(func, 'commands')
189
    assert not hasattr(func, 'nickname_commands')
190
    assert not hasattr(func, 'action_commands')
191
    assert not hasattr(func, 'intents')
192
193
194
def test_clean_callable_command(tmpconfig, func):
195
    setattr(func, 'commands', ['test'])
196
    loader.clean_callable(func, tmpconfig)
197
198
    # Default values
199
    assert hasattr(func, 'unblockable')
200
    assert func.unblockable is False
201
    assert hasattr(func, 'priority')
202
    assert func.priority == 'medium'
203
    assert hasattr(func, 'thread')
204
    assert func.thread is True
205
    assert hasattr(func, 'rate')
206
    assert func.rate == 0
207
    assert hasattr(func, 'channel_rate')
208
    assert func.channel_rate == 0
209
    assert hasattr(func, 'global_rate')
210
    assert func.global_rate == 0
211
    assert hasattr(func, 'event')
212
    assert func.event == ['PRIVMSG']
213
    assert hasattr(func, 'rule')
214
    assert len(func.rule) == 1
215
216
217
def test_clean_callable_event(tmpconfig, func):
218
    setattr(func, 'event', ['low', 'UP', 'MiXeD'])
219
    loader.clean_callable(func, tmpconfig)
220
221
    assert hasattr(func, 'event')
222
    assert func.event == ['LOW', 'UP', 'MIXED']
223
224
    # Default values
225
    assert hasattr(func, 'unblockable')
226
    assert func.unblockable is False
227
    assert hasattr(func, 'priority')
228
    assert func.priority == 'medium'
229
    assert hasattr(func, 'thread')
230
    assert func.thread is True
231
    assert hasattr(func, 'rate')
232
    assert func.rate == 0
233
    assert hasattr(func, 'channel_rate')
234
    assert func.channel_rate == 0
235
    assert hasattr(func, 'global_rate')
236
    assert func.global_rate == 0
237
238
    # idempotency
239
    loader.clean_callable(func, tmpconfig)
240
    assert func.event == ['LOW', 'UP', 'MIXED']
241
242
    assert func.unblockable is False
243
    assert func.priority == 'medium'
244
    assert func.thread is True
245
    assert func.rate == 0
246
    assert func.channel_rate == 0
247
    assert func.global_rate == 0
248
249
250
def test_clean_callable_event_string(tmpconfig, func):
251
    setattr(func, 'event', 'some')
252
    loader.clean_callable(func, tmpconfig)
253
254
    assert hasattr(func, 'event')
255
    assert func.event == ['SOME']
256
257
    # idempotency
258
    loader.clean_callable(func, tmpconfig)
259
    assert func.event == ['SOME']
260
261
262 View Code Duplication
def test_clean_callable_rule(tmpconfig, func):
263
    setattr(func, 'rule', [r'abc'])
264
    loader.clean_callable(func, tmpconfig)
265
266
    assert hasattr(func, 'rule')
267
    assert len(func.rule) == 1
268
269
    # Test the regex is compiled properly
270
    regex = func.rule[0]
271
    assert regex.match('abc')
272
    assert regex.match('abcd')
273
    assert not regex.match('efg')
274
275
    # Default values
276
    assert hasattr(func, 'unblockable')
277
    assert func.unblockable is False
278
    assert hasattr(func, 'priority')
279
    assert func.priority == 'medium'
280
    assert hasattr(func, 'thread')
281
    assert func.thread is True
282
    assert hasattr(func, 'rate')
283
    assert func.rate == 0
284
    assert hasattr(func, 'channel_rate')
285
    assert func.channel_rate == 0
286
    assert hasattr(func, 'global_rate')
287
    assert func.global_rate == 0
288
289
    # idempotency
290
    loader.clean_callable(func, tmpconfig)
291
    assert len(func.rule) == 1
292
    assert regex in func.rule
293
294
    assert func.unblockable is False
295
    assert func.priority == 'medium'
296
    assert func.thread is True
297
    assert func.rate == 0
298
    assert func.channel_rate == 0
299
    assert func.global_rate == 0
300
301
302 View Code Duplication
def test_clean_callable_rule_string(tmpconfig, func):
303
    setattr(func, 'rule', r'abc')
304
    loader.clean_callable(func, tmpconfig)
305
306
    assert hasattr(func, 'rule')
307
    assert len(func.rule) == 1
308
309
    # Test the regex is compiled properly
310
    regex = func.rule[0]
311
    assert regex.match('abc')
312
    assert regex.match('abcd')
313
    assert not regex.match('efg')
314
315
    # idempotency
316
    loader.clean_callable(func, tmpconfig)
317
    assert len(func.rule) == 1
318
    assert regex in func.rule
319
320
321 View Code Duplication
def test_clean_callable_rule_nick(tmpconfig, func):
322
    """Assert ``$nick`` in a rule will match ``TestBot: `` or ``TestBot, ``."""
323
    setattr(func, 'rule', [r'$nickhello'])
324
    loader.clean_callable(func, tmpconfig)
325
326
    assert hasattr(func, 'rule')
327
    assert len(func.rule) == 1
328
329
    # Test the regex is compiled properly
330
    regex = func.rule[0]
331
    assert regex.match('TestBot: hello')
332
    assert regex.match('TestBot, hello')
333
    assert not regex.match('TestBot not hello')
334
335
    # idempotency
336
    loader.clean_callable(func, tmpconfig)
337
    assert len(func.rule) == 1
338
    assert regex in func.rule
339
340
341 View Code Duplication
def test_clean_callable_rule_nickname(tmpconfig, func):
342
    """Assert ``$nick`` in a rule will match ``TestBot``."""
343
    setattr(func, 'rule', [r'$nickname\s+hello'])
344
    loader.clean_callable(func, tmpconfig)
345
346
    assert hasattr(func, 'rule')
347
    assert len(func.rule) == 1
348
349
    # Test the regex is compiled properly
350
    regex = func.rule[0]
351
    assert regex.match('TestBot hello')
352
    assert not regex.match('TestBot not hello')
353
354
    # idempotency
355
    loader.clean_callable(func, tmpconfig)
356
    assert len(func.rule) == 1
357
    assert regex in func.rule
358
359
360
def test_clean_callable_nickname_command(tmpconfig, func):
361
    setattr(func, 'nickname_commands', ['hello!'])
362
    loader.clean_callable(func, tmpconfig)
363
364
    assert hasattr(func, 'nickname_commands')
365
    assert len(func.nickname_commands) == 1
366
    assert func.nickname_commands == ['hello!']
367
    assert hasattr(func, 'rule')
368
    assert len(func.rule) == 1
369
370
    regex = func.rule[0]
371
    assert regex.match('TestBot hello!')
372
    assert regex.match('TestBot, hello!')
373
    assert regex.match('TestBot: hello!')
374
    assert not regex.match('TestBot not hello')
375
376
    # Default values
377
    assert hasattr(func, 'unblockable')
378
    assert func.unblockable is False
379
    assert hasattr(func, 'priority')
380
    assert func.priority == 'medium'
381
    assert hasattr(func, 'thread')
382
    assert func.thread is True
383
    assert hasattr(func, 'rate')
384
    assert func.rate == 0
385
    assert hasattr(func, 'channel_rate')
386
    assert func.channel_rate == 0
387
    assert hasattr(func, 'global_rate')
388
    assert func.global_rate == 0
389
390
    # idempotency
391
    loader.clean_callable(func, tmpconfig)
392
    assert len(func.rule) == 1
393
    assert regex in func.rule
394
395
    assert func.unblockable is False
396
    assert func.priority == 'medium'
397
    assert func.thread is True
398
    assert func.rate == 0
399
    assert func.channel_rate == 0
400
    assert func.global_rate == 0
401
402
403
def test_clean_callable_action_command(tmpconfig, func):
404
    setattr(func, 'action_commands', ['bots'])
405
    loader.clean_callable(func, tmpconfig)
406
407
    assert hasattr(func, 'action_commands')
408
    assert len(func.action_commands) == 1
409
    assert func.action_commands == ['bots']
410
    assert hasattr(func, 'rule')
411
    assert len(func.rule) == 1
412
413
    regex = func.rule[0]
414
    assert regex.match('bots bottingly')
415
    assert not regex.match('spams spammingly')
416
417
    # idempotency
418
    loader.clean_callable(func, tmpconfig)
419
    assert len(func.rule) == 1
420
    assert regex in func.rule
421
422
423
def test_clean_callable_events(tmpconfig, func):
424
    setattr(func, 'event', ['TOPIC'])
425
    loader.clean_callable(func, tmpconfig)
426
427
    assert hasattr(func, 'event')
428
    assert func.event == ['TOPIC']
429
430
    setattr(func, 'event', ['TOPIC', 'JOIN'])
431
    loader.clean_callable(func, tmpconfig)
432
433
    assert hasattr(func, 'event')
434
    assert func.event == ['TOPIC', 'JOIN']
435
436
    setattr(func, 'event', ['TOPIC', 'join', 'Nick'])
437
    loader.clean_callable(func, tmpconfig)
438
439
    assert hasattr(func, 'event')
440
    assert func.event == ['TOPIC', 'JOIN', 'NICK']
441
442
443
def test_clean_callable_events_basestring(tmpconfig, func):
444
    setattr(func, 'event', 'topic')
445
    loader.clean_callable(func, tmpconfig)
446
447
    assert hasattr(func, 'event')
448
    assert func.event == ['TOPIC']
449
450
    setattr(func, 'event', 'JOIN')
451
    loader.clean_callable(func, tmpconfig)
452
453
    assert hasattr(func, 'event')
454
    assert func.event == ['JOIN']
455
456
457 View Code Duplication
def test_clean_callable_example(tmpconfig, func):
458
    module.commands('test')(func)
459
    module.example('.test hello')(func)
460
461
    loader.clean_callable(func, tmpconfig)
462
463
    assert hasattr(func, '_docs')
464
    assert len(func._docs) == 1
465
    assert 'test' in func._docs
466
467
    docs = func._docs['test']
468
    assert len(docs) == 2
469
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
470
    assert docs[1] == ['.test hello']
471
472
473 View Code Duplication
def test_clean_callable_example_not_set(tmpconfig, func):
474
    module.commands('test')(func)
475
476
    loader.clean_callable(func, tmpconfig)
477
478
    assert hasattr(func, '_docs')
479
    assert len(func._docs) == 1
480
    assert 'test' in func._docs
481
482
    docs = func._docs['test']
483
    assert len(docs) == 2
484
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
485
    assert docs[1] == []
486
487
488 View Code Duplication
def test_clean_callable_example_multi_commands(tmpconfig, func):
489
    module.commands('test')(func)
490
    module.commands('unit')(func)
491
    module.example('.test hello')(func)
492
493
    loader.clean_callable(func, tmpconfig)
494
495
    assert hasattr(func, '_docs')
496
    assert len(func._docs) == 2
497
    assert 'test' in func._docs
498
    assert 'unit' in func._docs
499
500
    test_docs = func._docs['test']
501
    unit_docs = func._docs['unit']
502
    assert len(test_docs) == 2
503
    assert test_docs == unit_docs
504
505
    assert test_docs[0] == inspect.cleandoc(func.__doc__).splitlines()
506
    assert test_docs[1] == ['.test hello']
507
508
509 View Code Duplication
def test_clean_callable_example_first_only(tmpconfig, func):
510
    module.commands('test')(func)
511
    module.example('.test hello')(func)
512
    module.example('.test bonjour')(func)
513
514
    loader.clean_callable(func, tmpconfig)
515
516
    assert len(func._docs) == 1
517
    assert 'test' in func._docs
518
519
    docs = func._docs['test']
520
    assert len(docs) == 2
521
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
522
    assert docs[1] == ['.test hello']
523
524
525 View Code Duplication
def test_clean_callable_example_first_only_multi_commands(tmpconfig, func):
526
    module.commands('test')(func)
527
    module.commands('unit')(func)
528
    module.example('.test hello')(func)
529
    module.example('.test bonjour')(func)
530
531
    loader.clean_callable(func, tmpconfig)
532
533
    assert hasattr(func, '_docs')
534
    assert len(func._docs) == 2
535
    assert 'test' in func._docs
536
    assert 'unit' in func._docs
537
538
    test_docs = func._docs['test']
539
    unit_docs = func._docs['unit']
540
    assert len(test_docs) == 2
541
    assert test_docs == unit_docs
542
543
    assert test_docs[0] == inspect.cleandoc(func.__doc__).splitlines()
544
    assert test_docs[1] == ['.test hello']
545
546
547 View Code Duplication
def test_clean_callable_example_user_help(tmpconfig, func):
548
    module.commands('test')(func)
549
    module.example('.test hello', user_help=True)(func)
550
551
    loader.clean_callable(func, tmpconfig)
552
553
    assert len(func._docs) == 1
554
    assert 'test' in func._docs
555
556
    docs = func._docs['test']
557
    assert len(docs) == 2
558
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
559
    assert docs[1] == ['.test hello']
560
561
562 View Code Duplication
def test_clean_callable_example_user_help_multi(tmpconfig, func):
563
    module.commands('test')(func)
564
    module.example('.test hello', user_help=True)(func)
565
    module.example('.test bonjour', user_help=True)(func)
566
567
    loader.clean_callable(func, tmpconfig)
568
569
    assert len(func._docs) == 1
570
    assert 'test' in func._docs
571
572
    docs = func._docs['test']
573
    assert len(docs) == 2
574
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
575
    assert docs[1] == ['.test hello', '.test bonjour']
576
577
578 View Code Duplication
def test_clean_callable_example_user_help_mixed(tmpconfig, func):
579
    module.commands('test')(func)
580
    module.example('.test hello')(func)
581
    module.example('.test bonjour', user_help=True)(func)
582
583
    loader.clean_callable(func, tmpconfig)
584
585
    assert len(func._docs) == 1
586
    assert 'test' in func._docs
587
588
    docs = func._docs['test']
589
    assert len(docs) == 2
590
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
591
    assert docs[1] == ['.test bonjour']
592
593
594 View Code Duplication
def test_clean_callable_example_default_prefix(tmpconfig, func):
595
    module.commands('test')(func)
596
    module.example('.test hello')(func)
597
598
    tmpconfig.core.help_prefix = '!'
599
    loader.clean_callable(func, tmpconfig)
600
601
    assert len(func._docs) == 1
602
    assert 'test' in func._docs
603
604
    docs = func._docs['test']
605
    assert len(docs) == 2
606
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
607
    assert docs[1] == ['!test hello']
608
609
610 View Code Duplication
def test_clean_callable_example_nickname(tmpconfig, func):
611
    module.commands('test')(func)
612
    module.example('$nickname: hello')(func)
613
614
    loader.clean_callable(func, tmpconfig)
615
616
    assert len(func._docs) == 1
617
    assert 'test' in func._docs
618
619
    docs = func._docs['test']
620
    assert len(docs) == 2
621
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
622
    assert docs[1] == ['TestBot: hello']
623
624
625 View Code Duplication
def test_clean_callable_example_nickname_custom_prefix(tmpconfig, func):
626
    module.commands('test')(func)
627
    module.example('$nickname: hello')(func)
628
629
    tmpconfig.core.help_prefix = '!'
630
    loader.clean_callable(func, tmpconfig)
631
632
    assert len(func._docs) == 1
633
    assert 'test' in func._docs
634
635
    docs = func._docs['test']
636
    assert len(docs) == 2
637
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
638
    assert docs[1] == ['TestBot: hello']
639
640
641 View Code Duplication
def test_clean_callable_intents(tmpconfig, func):
642
    setattr(func, 'intents', [r'abc'])
643
    loader.clean_callable(func, tmpconfig)
644
645
    assert hasattr(func, 'intents')
646
    assert len(func.intents) == 1
647
648
    # Test the regex is compiled properly
649
    regex = func.intents[0]
650
    assert regex.match('abc')
651
    assert regex.match('abcd')
652
    assert regex.match('ABC')
653
    assert regex.match('AbCdE')
654
    assert not regex.match('efg')
655
656
    # Default values
657
    assert hasattr(func, 'unblockable')
658
    assert func.unblockable is False
659
    assert hasattr(func, 'priority')
660
    assert func.priority == 'medium'
661
    assert hasattr(func, 'thread')
662
    assert func.thread is True
663
    assert hasattr(func, 'rate')
664
    assert func.rate == 0
665
    assert hasattr(func, 'channel_rate')
666
    assert func.channel_rate == 0
667
    assert hasattr(func, 'global_rate')
668
    assert func.global_rate == 0
669
670
    # idempotency
671
    loader.clean_callable(func, tmpconfig)
672
    assert len(func.intents) == 1
673
    assert regex in func.intents
674
675
    assert func.unblockable is False
676
    assert func.priority == 'medium'
677
    assert func.thread is True
678
    assert func.rate == 0
679
    assert func.channel_rate == 0
680
    assert func.global_rate == 0
681