Completed
Push — master ( 9183d8...5b047d )
by dgw
21s queued 14s
created

test_loader.test_clean_callable_action_command()   A

Complexity

Conditions 1

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 18
rs 9.7
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, '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, 'nickname_commands')
196
    assert not hasattr(func, 'action_commands')
197
    assert not hasattr(func, 'intents')
198
199
200
def test_clean_callable_event(tmpconfig, func):
201
    setattr(func, 'event', ['low', 'UP', 'MiXeD'])
202
    loader.clean_callable(func, tmpconfig)
203
204
    assert hasattr(func, 'event')
205
    assert func.event == ['LOW', 'UP', 'MIXED']
206
207
    # idempotency
208
    loader.clean_callable(func, tmpconfig)
209
    assert func.event == ['LOW', 'UP', 'MIXED']
210
211
212
def test_clean_callable_event_string(tmpconfig, func):
213
    setattr(func, 'event', 'some')
214
    loader.clean_callable(func, tmpconfig)
215
216
    assert hasattr(func, 'event')
217
    assert func.event == ['SOME']
218
219
    # idempotency
220
    loader.clean_callable(func, tmpconfig)
221
    assert func.event == ['SOME']
222
223
224 View Code Duplication
def test_clean_callable_rule(tmpconfig, func):
225
    setattr(func, 'rule', [r'abc'])
226
    loader.clean_callable(func, tmpconfig)
227
228
    assert hasattr(func, 'rule')
229
    assert len(func.rule) == 1
230
231
    # Test the regex is compiled properly
232
    regex = func.rule[0]
233
    assert regex.match('abc')
234
    assert regex.match('abcd')
235
    assert not regex.match('efg')
236
237
    # idempotency
238
    loader.clean_callable(func, tmpconfig)
239
    assert len(func.rule) == 1
240
    assert regex in func.rule
241
242
243 View Code Duplication
def test_clean_callable_rule_string(tmpconfig, func):
244
    setattr(func, 'rule', r'abc')
245
    loader.clean_callable(func, tmpconfig)
246
247
    assert hasattr(func, 'rule')
248
    assert len(func.rule) == 1
249
250
    # Test the regex is compiled properly
251
    regex = func.rule[0]
252
    assert regex.match('abc')
253
    assert regex.match('abcd')
254
    assert not regex.match('efg')
255
256
    # idempotency
257
    loader.clean_callable(func, tmpconfig)
258
    assert len(func.rule) == 1
259
    assert regex in func.rule
260
261
262 View Code Duplication
def test_clean_callable_rule_nick(tmpconfig, func):
263
    """Assert ``$nick`` in a rule will match ``TestBot: `` or ``TestBot, ``."""
264
    setattr(func, 'rule', [r'$nickhello'])
265
    loader.clean_callable(func, tmpconfig)
266
267
    assert hasattr(func, 'rule')
268
    assert len(func.rule) == 1
269
270
    # Test the regex is compiled properly
271
    regex = func.rule[0]
272
    assert regex.match('TestBot: hello')
273
    assert regex.match('TestBot, hello')
274
    assert not regex.match('TestBot not hello')
275
276
    # idempotency
277
    loader.clean_callable(func, tmpconfig)
278
    assert len(func.rule) == 1
279
    assert regex in func.rule
280
281
282 View Code Duplication
def test_clean_callable_rule_nickname(tmpconfig, func):
283
    """Assert ``$nick`` in a rule will match ``TestBot``."""
284
    setattr(func, 'rule', [r'$nickname\s+hello'])
285
    loader.clean_callable(func, tmpconfig)
286
287
    assert hasattr(func, 'rule')
288
    assert len(func.rule) == 1
289
290
    # Test the regex is compiled properly
291
    regex = func.rule[0]
292
    assert regex.match('TestBot hello')
293
    assert not regex.match('TestBot not hello')
294
295
    # idempotency
296
    loader.clean_callable(func, tmpconfig)
297
    assert len(func.rule) == 1
298
    assert regex in func.rule
299
300
301
def test_clean_callable_nickname_command(tmpconfig, func):
302
    setattr(func, 'nickname_commands', ['hello!'])
303
    loader.clean_callable(func, tmpconfig)
304
305
    assert hasattr(func, 'nickname_commands')
306
    assert len(func.nickname_commands) == 1
307
    assert func.nickname_commands == ['hello!']
308
    assert hasattr(func, 'rule')
309
    assert len(func.rule) == 1
310
311
    regex = func.rule[0]
312
    assert regex.match('TestBot hello!')
313
    assert regex.match('TestBot, hello!')
314
    assert regex.match('TestBot: hello!')
315
    assert not regex.match('TestBot not hello')
316
317
    # idempotency
318
    loader.clean_callable(func, tmpconfig)
319
    assert len(func.rule) == 1
320
    assert regex in func.rule
321
322
323
def test_clean_callable_action_command(tmpconfig, func):
324
    setattr(func, 'action_commands', ['bots'])
325
    loader.clean_callable(func, tmpconfig)
326
327
    assert hasattr(func, 'action_commands')
328
    assert len(func.action_commands) == 1
329
    assert func.action_commands == ['bots']
330
    assert hasattr(func, 'rule')
331
    assert len(func.rule) == 1
332
333
    regex = func.rule[0]
334
    assert regex.match('bots bottingly')
335
    assert not regex.match('spams spammingly')
336
337
    # idempotency
338
    loader.clean_callable(func, tmpconfig)
339
    assert len(func.rule) == 1
340
    assert regex in func.rule
341
342
343
def test_clean_callable_events(tmpconfig, func):
344
    setattr(func, 'event', ['TOPIC'])
345
    loader.clean_callable(func, tmpconfig)
346
347
    assert hasattr(func, 'event')
348
    assert func.event == ['TOPIC']
349
350
    setattr(func, 'event', ['TOPIC', 'JOIN'])
351
    loader.clean_callable(func, tmpconfig)
352
353
    assert hasattr(func, 'event')
354
    assert func.event == ['TOPIC', 'JOIN']
355
356
    setattr(func, 'event', ['TOPIC', 'join', 'Nick'])
357
    loader.clean_callable(func, tmpconfig)
358
359
    assert hasattr(func, 'event')
360
    assert func.event == ['TOPIC', 'JOIN', 'NICK']
361
362
363
def test_clean_callable_events_basestring(tmpconfig, func):
364
    setattr(func, 'event', 'topic')
365
    loader.clean_callable(func, tmpconfig)
366
367
    assert hasattr(func, 'event')
368
    assert func.event == ['TOPIC']
369
370
    setattr(func, 'event', 'JOIN')
371
    loader.clean_callable(func, tmpconfig)
372
373
    assert hasattr(func, 'event')
374
    assert func.event == ['JOIN']
375
376
377 View Code Duplication
def test_clean_callable_example(tmpconfig, func):
378
    module.commands('test')(func)
379
    module.example('.test hello')(func)
380
381
    loader.clean_callable(func, tmpconfig)
382
383
    assert hasattr(func, '_docs')
384
    assert len(func._docs) == 1
385
    assert 'test' in func._docs
386
387
    docs = func._docs['test']
388
    assert len(docs) == 2
389
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
390
    assert docs[1] == ['.test hello']
391
392
393 View Code Duplication
def test_clean_callable_example_not_set(tmpconfig, func):
394
    module.commands('test')(func)
395
396
    loader.clean_callable(func, tmpconfig)
397
398
    assert hasattr(func, '_docs')
399
    assert len(func._docs) == 1
400
    assert 'test' in func._docs
401
402
    docs = func._docs['test']
403
    assert len(docs) == 2
404
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
405
    assert docs[1] == []
406
407
408 View Code Duplication
def test_clean_callable_example_multi_commands(tmpconfig, func):
409
    module.commands('test')(func)
410
    module.commands('unit')(func)
411
    module.example('.test hello')(func)
412
413
    loader.clean_callable(func, tmpconfig)
414
415
    assert hasattr(func, '_docs')
416
    assert len(func._docs) == 2
417
    assert 'test' in func._docs
418
    assert 'unit' in func._docs
419
420
    test_docs = func._docs['test']
421
    unit_docs = func._docs['unit']
422
    assert len(test_docs) == 2
423
    assert test_docs == unit_docs
424
425
    assert test_docs[0] == inspect.cleandoc(func.__doc__).splitlines()
426
    assert test_docs[1] == ['.test hello']
427
428
429 View Code Duplication
def test_clean_callable_example_first_only(tmpconfig, func):
430
    module.commands('test')(func)
431
    module.example('.test hello')(func)
432
    module.example('.test bonjour')(func)
433
434
    loader.clean_callable(func, tmpconfig)
435
436
    assert len(func._docs) == 1
437
    assert 'test' in func._docs
438
439
    docs = func._docs['test']
440
    assert len(docs) == 2
441
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
442
    assert docs[1] == ['.test hello']
443
444
445 View Code Duplication
def test_clean_callable_example_first_only_multi_commands(tmpconfig, func):
446
    module.commands('test')(func)
447
    module.commands('unit')(func)
448
    module.example('.test hello')(func)
449
    module.example('.test bonjour')(func)
450
451
    loader.clean_callable(func, tmpconfig)
452
453
    assert hasattr(func, '_docs')
454
    assert len(func._docs) == 2
455
    assert 'test' in func._docs
456
    assert 'unit' in func._docs
457
458
    test_docs = func._docs['test']
459
    unit_docs = func._docs['unit']
460
    assert len(test_docs) == 2
461
    assert test_docs == unit_docs
462
463
    assert test_docs[0] == inspect.cleandoc(func.__doc__).splitlines()
464
    assert test_docs[1] == ['.test hello']
465
466
467 View Code Duplication
def test_clean_callable_example_user_help(tmpconfig, func):
468
    module.commands('test')(func)
469
    module.example('.test hello', user_help=True)(func)
470
471
    loader.clean_callable(func, tmpconfig)
472
473
    assert len(func._docs) == 1
474
    assert 'test' in func._docs
475
476
    docs = func._docs['test']
477
    assert len(docs) == 2
478
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
479
    assert docs[1] == ['.test hello']
480
481
482 View Code Duplication
def test_clean_callable_example_user_help_multi(tmpconfig, func):
483
    module.commands('test')(func)
484
    module.example('.test hello', user_help=True)(func)
485
    module.example('.test bonjour', user_help=True)(func)
486
487
    loader.clean_callable(func, tmpconfig)
488
489
    assert len(func._docs) == 1
490
    assert 'test' in func._docs
491
492
    docs = func._docs['test']
493
    assert len(docs) == 2
494
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
495
    assert docs[1] == ['.test hello', '.test bonjour']
496
497
498 View Code Duplication
def test_clean_callable_example_user_help_mixed(tmpconfig, func):
499
    module.commands('test')(func)
500
    module.example('.test hello')(func)
501
    module.example('.test bonjour', user_help=True)(func)
502
503
    loader.clean_callable(func, tmpconfig)
504
505
    assert len(func._docs) == 1
506
    assert 'test' in func._docs
507
508
    docs = func._docs['test']
509
    assert len(docs) == 2
510
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
511
    assert docs[1] == ['.test bonjour']
512
513
514 View Code Duplication
def test_clean_callable_example_default_prefix(tmpconfig, func):
515
    module.commands('test')(func)
516
    module.example('.test hello')(func)
517
518
    tmpconfig.core.help_prefix = '!'
519
    loader.clean_callable(func, tmpconfig)
520
521
    assert len(func._docs) == 1
522
    assert 'test' in func._docs
523
524
    docs = func._docs['test']
525
    assert len(docs) == 2
526
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
527
    assert docs[1] == ['!test hello']
528
529
530 View Code Duplication
def test_clean_callable_example_nickname(tmpconfig, func):
531
    module.commands('test')(func)
532
    module.example('$nickname: hello')(func)
533
534
    loader.clean_callable(func, tmpconfig)
535
536
    assert len(func._docs) == 1
537
    assert 'test' in func._docs
538
539
    docs = func._docs['test']
540
    assert len(docs) == 2
541
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
542
    assert docs[1] == ['TestBot: hello']
543
544
545 View Code Duplication
def test_clean_callable_example_nickname_custom_prefix(tmpconfig, func):
546
    module.commands('test')(func)
547
    module.example('$nickname: hello')(func)
548
549
    tmpconfig.core.help_prefix = '!'
550
    loader.clean_callable(func, tmpconfig)
551
552
    assert len(func._docs) == 1
553
    assert 'test' in func._docs
554
555
    docs = func._docs['test']
556
    assert len(docs) == 2
557
    assert docs[0] == inspect.cleandoc(func.__doc__).splitlines()
558
    assert docs[1] == ['TestBot: hello']
559
560
561
def test_clean_callable_intents(tmpconfig, func):
562
    setattr(func, 'intents', [r'abc'])
563
    loader.clean_callable(func, tmpconfig)
564
565
    assert hasattr(func, 'intents')
566
    assert len(func.intents) == 1
567
568
    # Test the regex is compiled properly
569
    regex = func.intents[0]
570
    assert regex.match('abc')
571
    assert regex.match('abcd')
572
    assert regex.match('ABC')
573
    assert regex.match('AbCdE')
574
    assert not regex.match('efg')
575
576
    # idempotency
577
    loader.clean_callable(func, tmpconfig)
578
    assert len(func.intents) == 1
579
    assert regex in func.intents
580