1##
2# Copyright (c) 2002-2005, Jeremiah Fincher
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are met:
7#
8#   * Redistributions of source code must retain the above copyright notice,
9#     this list of conditions, and the following disclaimer.
10#   * Redistributions in binary form must reproduce the above copyright notice,
11#     this list of conditions, and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#   * Neither the name of the author of this software nor the name of
14#     contributors to this software may be used to endorse or promote products
15#     derived from this software without specific prior written consent.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28###
29
30from supybot.test import *
31
32import copy
33import pickle
34
35import supybot.conf as conf
36import supybot.irclib as irclib
37import supybot.ircmsgs as ircmsgs
38import supybot.ircutils as ircutils
39
40# The test framework used to provide these, but not it doesn't.  We'll add
41# messages to as we find bugs (if indeed we find bugs).
42msgs = []
43rawmsgs = []
44
45class IrcMsgQueueTestCase(SupyTestCase):
46    mode = ircmsgs.op('#foo', 'jemfinch')
47    msg = ircmsgs.privmsg('#foo', 'hey, you')
48    msgs = [ircmsgs.privmsg('#foo', str(i)) for i in range(10)]
49    kick = ircmsgs.kick('#foo', 'PeterB')
50    pong = ircmsgs.pong('123')
51    ping = ircmsgs.ping('123')
52    topic = ircmsgs.topic('#foo')
53    notice = ircmsgs.notice('jemfinch', 'supybot here')
54    join = ircmsgs.join('#foo')
55    who = ircmsgs.who('#foo')
56
57    def testInit(self):
58        q = irclib.IrcMsgQueue([self.msg, self.topic, self.ping])
59        self.assertEqual(len(q), 3)
60
61    def testLen(self):
62        q = irclib.IrcMsgQueue()
63        q.enqueue(self.msg)
64        self.assertEqual(len(q), 1)
65        q.enqueue(self.mode)
66        self.assertEqual(len(q), 2)
67        q.enqueue(self.kick)
68        self.assertEqual(len(q), 3)
69        q.enqueue(self.topic)
70        self.assertEqual(len(q), 4)
71        q.dequeue()
72        self.assertEqual(len(q), 3)
73        q.dequeue()
74        self.assertEqual(len(q), 2)
75        q.dequeue()
76        self.assertEqual(len(q), 1)
77        q.dequeue()
78        self.assertEqual(len(q), 0)
79
80    def testContains(self):
81        q = irclib.IrcMsgQueue()
82        q.enqueue(self.msg)
83        q.enqueue(self.msg)
84        q.enqueue(self.msg)
85        self.failUnless(self.msg in q)
86        q.dequeue()
87        self.failUnless(self.msg in q)
88        q.dequeue()
89        self.failUnless(self.msg in q)
90        q.dequeue()
91        self.failIf(self.msg in q)
92
93    def testRepr(self):
94        q = irclib.IrcMsgQueue()
95        self.assertEqual(repr(q), 'IrcMsgQueue([])')
96        q.enqueue(self.msg)
97        try:
98            repr(q)
99        except Exception as e:
100            self.fail('repr(q) raised an exception: %s' %
101                      utils.exnToString(e))
102
103    def testEmpty(self):
104        q = irclib.IrcMsgQueue()
105        self.failIf(q)
106
107    def testEnqueueDequeue(self):
108        q = irclib.IrcMsgQueue()
109        q.enqueue(self.msg)
110        self.failUnless(q)
111        self.assertEqual(self.msg, q.dequeue())
112        self.failIf(q)
113        q.enqueue(self.msg)
114        q.enqueue(self.notice)
115        self.assertEqual(self.msg, q.dequeue())
116        self.assertEqual(self.notice, q.dequeue())
117        for msg in self.msgs:
118            q.enqueue(msg)
119        for msg in self.msgs:
120            self.assertEqual(msg, q.dequeue())
121
122    def testPrioritizing(self):
123        q = irclib.IrcMsgQueue()
124        q.enqueue(self.msg)
125        q.enqueue(self.mode)
126        self.assertEqual(self.mode, q.dequeue())
127        self.assertEqual(self.msg, q.dequeue())
128        q.enqueue(self.msg)
129        q.enqueue(self.kick)
130        self.assertEqual(self.kick, q.dequeue())
131        self.assertEqual(self.msg, q.dequeue())
132        q.enqueue(self.ping)
133        q.enqueue(self.msgs[0])
134        q.enqueue(self.kick)
135        q.enqueue(self.msgs[1])
136        q.enqueue(self.mode)
137        self.assertEqual(self.kick, q.dequeue())
138        self.assertEqual(self.mode, q.dequeue())
139        self.assertEqual(self.ping, q.dequeue())
140        self.assertEqual(self.msgs[0], q.dequeue())
141        self.assertEqual(self.msgs[1], q.dequeue())
142
143    def testNoIdenticals(self):
144        configVar = conf.supybot.protocols.irc.queuing.duplicates
145        original = configVar()
146        try:
147            configVar.setValue(True)
148            q = irclib.IrcMsgQueue()
149            q.enqueue(self.msg)
150            q.enqueue(self.msg)
151            self.assertEqual(self.msg, q.dequeue())
152            self.failIf(q)
153        finally:
154            configVar.setValue(original)
155
156    def testJoinBeforeWho(self):
157        q = irclib.IrcMsgQueue()
158        q.enqueue(self.join)
159        q.enqueue(self.who)
160        self.assertEqual(self.join, q.dequeue())
161        self.assertEqual(self.who, q.dequeue())
162##         q.enqueue(self.who)
163##         q.enqueue(self.join)
164##         self.assertEqual(self.join, q.dequeue())
165##         self.assertEqual(self.who, q.dequeue())
166
167    def testTopicBeforePrivmsg(self):
168        q = irclib.IrcMsgQueue()
169        q.enqueue(self.msg)
170        q.enqueue(self.topic)
171        self.assertEqual(self.topic, q.dequeue())
172        self.assertEqual(self.msg, q.dequeue())
173
174    def testModeBeforePrivmsg(self):
175        q = irclib.IrcMsgQueue()
176        q.enqueue(self.msg)
177        q.enqueue(self.mode)
178        self.assertEqual(self.mode, q.dequeue())
179        self.assertEqual(self.msg, q.dequeue())
180        q.enqueue(self.mode)
181        q.enqueue(self.msg)
182        self.assertEqual(self.mode, q.dequeue())
183        self.assertEqual(self.msg, q.dequeue())
184
185
186class ChannelStateTestCase(SupyTestCase):
187    def testPickleCopy(self):
188        c = irclib.ChannelState()
189        self.assertEqual(pickle.loads(pickle.dumps(c)), c)
190        c.addUser('jemfinch')
191        c1 = pickle.loads(pickle.dumps(c))
192        self.assertEqual(c, c1)
193        c.removeUser('jemfinch')
194        self.failIf('jemfinch' in c.users)
195        self.failUnless('jemfinch' in c1.users)
196
197    def testCopy(self):
198        c = irclib.ChannelState()
199        c.addUser('jemfinch')
200        c1 = copy.deepcopy(c)
201        c.removeUser('jemfinch')
202        self.failIf('jemfinch' in c.users)
203        self.failUnless('jemfinch' in c1.users)
204
205    def testAddUser(self):
206        c = irclib.ChannelState()
207        c.addUser('foo')
208        self.failUnless('foo' in c.users)
209        self.failIf('foo' in c.ops)
210        self.failIf('foo' in c.voices)
211        self.failIf('foo' in c.halfops)
212        c.addUser('+bar')
213        self.failUnless('bar' in c.users)
214        self.failUnless('bar' in c.voices)
215        self.failIf('bar' in c.ops)
216        self.failIf('bar' in c.halfops)
217        c.addUser('%baz')
218        self.failUnless('baz' in c.users)
219        self.failUnless('baz' in c.halfops)
220        self.failIf('baz' in c.voices)
221        self.failIf('baz' in c.ops)
222        c.addUser('@quuz')
223        self.failUnless('quuz' in c.users)
224        self.failUnless('quuz' in c.ops)
225        self.failIf('quuz' in c.halfops)
226        self.failIf('quuz' in c.voices)
227
228
229class IrcStateTestCase(SupyTestCase):
230    class FakeIrc:
231        nick = 'nick'
232        prefix = 'nick!user@host'
233
234        def isChannel(self, s):
235            return ircutils.isChannel(s)
236
237    irc = FakeIrc()
238    def testKickRemovesChannel(self):
239        st = irclib.IrcState()
240        st.channels['#foo'] = irclib.ChannelState()
241        m = ircmsgs.kick('#foo', self.irc.nick, prefix=self.irc.prefix)
242        st.addMsg(self.irc, m)
243        self.failIf('#foo' in st.channels)
244
245    def testAddMsgRemovesOpsProperly(self):
246        st = irclib.IrcState()
247        st.channels['#foo'] = irclib.ChannelState()
248        st.channels['#foo'].ops.add('bar')
249        m = ircmsgs.mode('#foo', ('-o', 'bar'))
250        st.addMsg(self.irc, m)
251        self.failIf('bar' in st.channels['#foo'].ops)
252
253    def testNickChangesChangeChannelUsers(self):
254        st = irclib.IrcState()
255        st.channels['#foo'] = irclib.ChannelState()
256        st.channels['#foo'].addUser('@bar')
257        self.failUnless('bar' in st.channels['#foo'].users)
258        self.failUnless(st.channels['#foo'].isOp('bar'))
259        st.addMsg(self.irc, ircmsgs.IrcMsg(':bar!asfd@asdf.com NICK baz'))
260        self.failIf('bar' in st.channels['#foo'].users)
261        self.failIf(st.channels['#foo'].isOp('bar'))
262        self.failUnless('baz' in st.channels['#foo'].users)
263        self.failUnless(st.channels['#foo'].isOp('baz'))
264
265    def testHistory(self):
266        if len(msgs) < 10:
267            return
268        maxHistoryLength = conf.supybot.protocols.irc.maxHistoryLength
269        with maxHistoryLength.context(10):
270            state = irclib.IrcState()
271            for msg in msgs:
272                try:
273                    state.addMsg(self.irc, msg)
274                except Exception:
275                    pass
276                self.failIf(len(state.history) > maxHistoryLength())
277            self.assertEqual(len(state.history), maxHistoryLength())
278            self.assertEqual(list(state.history),
279                             msgs[len(msgs) - maxHistoryLength():])
280
281    def testWasteland005(self):
282        state = irclib.IrcState()
283        # Here we're testing if PREFIX works without the (ov) there.
284        state.addMsg(self.irc, ircmsgs.IrcMsg(':desolate.wasteland.org 005 jemfinch NOQUIT WATCH=128 SAFELIST MODES=6 MAXCHANNELS=10 MAXBANS=100 NICKLEN=30 TOPICLEN=307 KICKLEN=307 CHANTYPES=&# PREFIX=@+ NETWORK=DALnet SILENCE=10 :are available on this server'))
285        self.assertEqual(state.supported['prefix']['o'], '@')
286        self.assertEqual(state.supported['prefix']['v'], '+')
287
288    def testIRCNet005(self):
289        state = irclib.IrcState()
290        # Testing IRCNet's misuse of MAXBANS
291        state.addMsg(self.irc, ircmsgs.IrcMsg(':irc.inet.tele.dk 005 adkwbot WALLCHOPS KNOCK EXCEPTS INVEX MODES=4 MAXCHANNELS=20 MAXBANS=beI:100 MAXTARGETS=4 NICKLEN=9 TOPICLEN=120 KICKLEN=90 :are supported by this server'))
292        self.assertEqual(state.supported['maxbans'], 100)
293
294    def testSupportedUmodes(self):
295        state = irclib.IrcState()
296        state.addMsg(self.irc, ircmsgs.IrcMsg(':coulomb.oftc.net 004 testnick coulomb.oftc.net hybrid-7.2.2+oftc1.6.8 CDGPRSabcdfgiklnorsuwxyz biklmnopstveI bkloveI'))
297        self.assertEqual(state.supported['umodes'],
298                frozenset('CDGPRSabcdfgiklnorsuwxyz'))
299        self.assertEqual(state.supported['chanmodes'],
300                         frozenset('biklmnopstveI'))
301
302    def testEmptyTopic(self):
303        state = irclib.IrcState()
304        state.addMsg(self.irc, ircmsgs.topic('#foo'))
305
306    def testPickleCopy(self):
307        state = irclib.IrcState()
308        self.assertEqual(state, pickle.loads(pickle.dumps(state)))
309        for msg in msgs:
310            try:
311                state.addMsg(self.irc, msg)
312            except Exception:
313                pass
314        self.assertEqual(state, pickle.loads(pickle.dumps(state)))
315
316    def testCopy(self):
317        state = irclib.IrcState()
318        self.assertEqual(state, state.copy())
319        for msg in msgs:
320            try:
321                state.addMsg(self.irc, msg)
322            except Exception:
323                pass
324        self.assertEqual(state, state.copy())
325
326    def testCopyCopiesChannels(self):
327        state = irclib.IrcState()
328        stateCopy = state.copy()
329        state.channels['#foo'] = None
330        self.failIf('#foo' in stateCopy.channels)
331
332    def testJoin(self):
333        st = irclib.IrcState()
334        st.addMsg(self.irc, ircmsgs.join('#foo', prefix=self.irc.prefix))
335        self.failUnless('#foo' in st.channels)
336        self.failUnless(self.irc.nick in st.channels['#foo'].users)
337        st.addMsg(self.irc, ircmsgs.join('#foo', prefix='foo!bar@baz'))
338        self.failUnless('foo' in st.channels['#foo'].users)
339        st2 = st.copy()
340        st.addMsg(self.irc, ircmsgs.quit(prefix='foo!bar@baz'))
341        self.failIf('foo' in st.channels['#foo'].users)
342        self.failUnless('foo' in st2.channels['#foo'].users)
343
344
345    def testEq(self):
346        state1 = irclib.IrcState()
347        state2 = irclib.IrcState()
348        self.assertEqual(state1, state2)
349        for msg in msgs:
350            try:
351                state1.addMsg(self.irc, msg)
352                state2.addMsg(self.irc, msg)
353                self.assertEqual(state1, state2)
354            except Exception:
355                pass
356
357    def testHandlesModes(self):
358        st = irclib.IrcState()
359        st.addMsg(self.irc, ircmsgs.join('#foo', prefix=self.irc.prefix))
360        self.failIf('bar' in st.channels['#foo'].ops)
361        st.addMsg(self.irc, ircmsgs.op('#foo', 'bar'))
362        self.failUnless('bar' in st.channels['#foo'].ops)
363        st.addMsg(self.irc, ircmsgs.deop('#foo', 'bar'))
364        self.failIf('bar' in st.channels['#foo'].ops)
365
366        self.failIf('bar' in st.channels['#foo'].voices)
367        st.addMsg(self.irc, ircmsgs.voice('#foo', 'bar'))
368        self.failUnless('bar' in st.channels['#foo'].voices)
369        st.addMsg(self.irc, ircmsgs.devoice('#foo', 'bar'))
370        self.failIf('bar' in st.channels['#foo'].voices)
371
372        self.failIf('bar' in st.channels['#foo'].halfops)
373        st.addMsg(self.irc, ircmsgs.halfop('#foo', 'bar'))
374        self.failUnless('bar' in st.channels['#foo'].halfops)
375        st.addMsg(self.irc, ircmsgs.dehalfop('#foo', 'bar'))
376        self.failIf('bar' in st.channels['#foo'].halfops)
377
378    def testDoModeOnlyChannels(self):
379        st = irclib.IrcState()
380        self.assert_(st.addMsg(self.irc, ircmsgs.IrcMsg('MODE foo +i')) or 1)
381
382class IrcTestCase(SupyTestCase):
383    def setUp(self):
384        self.irc = irclib.Irc('test')
385
386        #m = self.irc.takeMsg()
387        #self.failUnless(m.command == 'PASS', 'Expected PASS, got %r.' % m)
388
389        m = self.irc.takeMsg()
390        self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
391        self.failUnless(m.args == ('LS', '302'), 'Expected CAP LS 302, got %r.' % m)
392
393        m = self.irc.takeMsg()
394        self.failUnless(m.command == 'NICK', 'Expected NICK, got %r.' % m)
395
396        m = self.irc.takeMsg()
397        self.failUnless(m.command == 'USER', 'Expected USER, got %r.' % m)
398
399        # TODO
400        self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
401            args=('*', 'LS', '*', 'account-tag multi-prefix')))
402        self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
403            args=('*', 'LS', 'extended-join')))
404
405        m = self.irc.takeMsg()
406        self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
407        self.assertEqual(m.args[0], 'REQ', m)
408        # NOTE: Capabilities are requested in alphabetic order, because
409        # sets are unordered, and their "order" is nondeterministic.
410        self.assertEqual(m.args[1], 'account-tag extended-join multi-prefix')
411
412        self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
413            args=('*', 'ACK', 'account-tag multi-prefix extended-join')))
414
415        m = self.irc.takeMsg()
416        self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
417        self.assertEqual(m.args, ('END',), m)
418
419        m = self.irc.takeMsg()
420        self.failUnless(m is None, m)
421
422    def testPingResponse(self):
423        self.irc.feedMsg(ircmsgs.ping('123'))
424        self.assertEqual(ircmsgs.pong('123'), self.irc.takeMsg())
425
426    def test433Response(self):
427        # This is necessary; it won't change nick if irc.originalName==irc.nick
428        self.irc.nick = 'somethingElse'
429        self.irc.feedMsg(ircmsgs.IrcMsg('433 * %s :Nickname already in use.' %\
430                                        self.irc.nick))
431        msg = self.irc.takeMsg()
432        self.failUnless(msg.command == 'NICK' and msg.args[0] != self.irc.nick)
433        self.irc.feedMsg(ircmsgs.IrcMsg('433 * %s :Nickname already in use.' %\
434                                        self.irc.nick))
435        msg = self.irc.takeMsg()
436        self.failUnless(msg.command == 'NICK' and msg.args[0] != self.irc.nick)
437
438    def testSendBeforeQueue(self):
439        while self.irc.takeMsg() is not None:
440            self.irc.takeMsg()
441        self.irc.queueMsg(ircmsgs.IrcMsg('NOTICE #foo bar'))
442        self.irc.sendMsg(ircmsgs.IrcMsg('PRIVMSG #foo yeah!'))
443        msg = self.irc.takeMsg()
444        self.failUnless(msg.command == 'PRIVMSG')
445        msg = self.irc.takeMsg()
446        self.failUnless(msg.command == 'NOTICE')
447
448    def testNoMsgLongerThan512(self):
449        self.irc.queueMsg(ircmsgs.privmsg('whocares', 'x'*1000))
450        msg = self.irc.takeMsg()
451        self.failUnless(len(msg) <= 512, 'len(msg) was %s' % len(msg))
452
453    def testReset(self):
454        for msg in msgs:
455            try:
456                self.irc.feedMsg(msg)
457            except:
458                pass
459        self.irc.reset()
460        self.failIf(self.irc.state.history)
461        self.failIf(self.irc.state.channels)
462        self.failIf(self.irc.outstandingPing)
463
464    def testHistory(self):
465        self.irc.reset()
466        msg1 = ircmsgs.IrcMsg('PRIVMSG #linux :foo bar baz!')
467        self.irc.feedMsg(msg1)
468        self.assertEqual(self.irc.state.history[0], msg1)
469        msg2 = ircmsgs.IrcMsg('JOIN #sourcereview')
470        self.irc.feedMsg(msg2)
471        self.assertEqual(list(self.irc.state.history), [msg1, msg2])
472
473    def testQuit(self):
474        self.irc.reset()
475        self.irc.feedMsg(ircmsgs.IrcMsg(':someuser JOIN #foo'))
476        self.irc.feedMsg(ircmsgs.IrcMsg(':someuser JOIN #bar'))
477        self.irc.feedMsg(ircmsgs.IrcMsg(':someuser2 JOIN #bar2'))
478        class Callback(irclib.IrcCallback):
479            channels_set = None
480            def name(self):
481                return 'testcallback'
482            def doQuit(self2, irc, msg):
483                self2.channels_set = msg.tagged('channels')
484        c = Callback()
485        self.irc.addCallback(c)
486        try:
487            self.irc.feedMsg(ircmsgs.IrcMsg(':someuser QUIT'))
488        finally:
489            self.irc.removeCallback(c.name())
490        self.assertEqual(c.channels_set, ircutils.IrcSet(['#foo', '#bar']))
491
492    def testNick(self):
493        self.irc.reset()
494        self.irc.feedMsg(ircmsgs.IrcMsg(':someuser JOIN #foo'))
495        self.irc.feedMsg(ircmsgs.IrcMsg(':someuser JOIN #bar'))
496        self.irc.feedMsg(ircmsgs.IrcMsg(':someuser2 JOIN #bar2'))
497        class Callback(irclib.IrcCallback):
498            channels_set = None
499            def name(self):
500                return 'testcallback'
501            def doNick(self2, irc, msg):
502                self2.channels_set = msg.tagged('channels')
503        c = Callback()
504        self.irc.addCallback(c)
505        try:
506            self.irc.feedMsg(ircmsgs.IrcMsg(':someuser NICK newuser'))
507        finally:
508            self.irc.removeCallback(c.name())
509        self.assertEqual(c.channels_set, ircutils.IrcSet(['#foo', '#bar']))
510
511    def testBatch(self):
512        self.irc.reset()
513        self.irc.feedMsg(ircmsgs.IrcMsg(':someuser1 JOIN #foo'))
514        self.irc.feedMsg(ircmsgs.IrcMsg(':host BATCH +name netjoin'))
515        m1 = ircmsgs.IrcMsg('@batch=name :someuser2 JOIN #foo')
516        self.irc.feedMsg(m1)
517        self.irc.feedMsg(ircmsgs.IrcMsg(':someuser3 JOIN #foo'))
518        m2 = ircmsgs.IrcMsg('@batch=name :someuser4 JOIN #foo')
519        self.irc.feedMsg(m2)
520        class Callback(irclib.IrcCallback):
521            batch = None
522            def name(self):
523                return 'testcallback'
524            def doBatch(self2, irc, msg):
525                self2.batch = msg.tagged('batch')
526        c = Callback()
527        self.irc.addCallback(c)
528        try:
529            self.irc.feedMsg(ircmsgs.IrcMsg(':host BATCH -name'))
530        finally:
531            self.irc.removeCallback(c.name())
532        self.assertEqual(c.batch, irclib.Batch('netjoin', (), [m1, m2]))
533
534class SaslTestCase(SupyTestCase):
535    def setUp(self):
536        pass
537
538    def startCapNegociation(self, caps='sasl'):
539        m = self.irc.takeMsg()
540        self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
541        self.failUnless(m.args == ('LS', '302'), 'Expected CAP LS 302, got %r.' % m)
542
543        m = self.irc.takeMsg()
544        self.failUnless(m.command == 'NICK', 'Expected NICK, got %r.' % m)
545
546        m = self.irc.takeMsg()
547        self.failUnless(m.command == 'USER', 'Expected USER, got %r.' % m)
548
549        self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
550            args=('*', 'LS', caps)))
551
552        if caps:
553            m = self.irc.takeMsg()
554            self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
555            self.assertEqual(m.args[0], 'REQ', m)
556            self.assertEqual(m.args[1], 'sasl')
557
558            self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
559                args=('*', 'ACK', 'sasl')))
560
561    def endCapNegociation(self):
562        m = self.irc.takeMsg()
563        self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
564        self.assertEqual(m.args, ('END',), m)
565
566    def testPlain(self):
567        try:
568            conf.supybot.networks.test.sasl.username.setValue('jilles')
569            conf.supybot.networks.test.sasl.password.setValue('sesame')
570            self.irc = irclib.Irc('test')
571        finally:
572            conf.supybot.networks.test.sasl.username.setValue('')
573            conf.supybot.networks.test.sasl.password.setValue('')
574        self.assertEqual(self.irc.sasl_current_mechanism, None)
575        self.assertEqual(self.irc.sasl_next_mechanisms, ['plain'])
576
577        self.startCapNegociation()
578
579        m = self.irc.takeMsg()
580        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
581            args=('PLAIN',)))
582
583        self.irc.feedMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=('+',)))
584
585        m = self.irc.takeMsg()
586        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
587            args=('amlsbGVzAGppbGxlcwBzZXNhbWU=',)))
588
589        self.irc.feedMsg(ircmsgs.IrcMsg(command='900', args=('jilles',)))
590        self.irc.feedMsg(ircmsgs.IrcMsg(command='903', args=('jilles',)))
591
592        self.endCapNegociation()
593
594    def testExternalFallbackToPlain(self):
595        try:
596            conf.supybot.networks.test.sasl.username.setValue('jilles')
597            conf.supybot.networks.test.sasl.password.setValue('sesame')
598            conf.supybot.networks.test.certfile.setValue('foo')
599            self.irc = irclib.Irc('test')
600        finally:
601            conf.supybot.networks.test.sasl.username.setValue('')
602            conf.supybot.networks.test.sasl.password.setValue('')
603            conf.supybot.networks.test.certfile.setValue('')
604        self.assertEqual(self.irc.sasl_current_mechanism, None)
605        self.assertEqual(self.irc.sasl_next_mechanisms,
606                ['external', 'plain'])
607
608        self.startCapNegociation()
609
610        m = self.irc.takeMsg()
611        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
612            args=('EXTERNAL',)))
613
614        self.irc.feedMsg(ircmsgs.IrcMsg(command='904',
615            args=('mechanism not available',)))
616
617        m = self.irc.takeMsg()
618        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
619            args=('PLAIN',)))
620
621        self.irc.feedMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=('+',)))
622
623        m = self.irc.takeMsg()
624        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
625            args=('amlsbGVzAGppbGxlcwBzZXNhbWU=',)))
626
627        self.irc.feedMsg(ircmsgs.IrcMsg(command='900', args=('jilles',)))
628        self.irc.feedMsg(ircmsgs.IrcMsg(command='903', args=('jilles',)))
629
630        self.endCapNegociation()
631
632    def testFilter(self):
633        try:
634            conf.supybot.networks.test.sasl.username.setValue('jilles')
635            conf.supybot.networks.test.sasl.password.setValue('sesame')
636            conf.supybot.networks.test.certfile.setValue('foo')
637            self.irc = irclib.Irc('test')
638        finally:
639            conf.supybot.networks.test.sasl.username.setValue('')
640            conf.supybot.networks.test.sasl.password.setValue('')
641            conf.supybot.networks.test.certfile.setValue('')
642        self.assertEqual(self.irc.sasl_current_mechanism, None)
643        self.assertEqual(self.irc.sasl_next_mechanisms,
644                ['external', 'plain'])
645
646        self.startCapNegociation(caps='sasl=foo,plain,bar')
647
648        m = self.irc.takeMsg()
649        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
650            args=('PLAIN',)))
651
652        self.irc.feedMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=('+',)))
653
654        m = self.irc.takeMsg()
655        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
656            args=('amlsbGVzAGppbGxlcwBzZXNhbWU=',)))
657
658        self.irc.feedMsg(ircmsgs.IrcMsg(command='900', args=('jilles',)))
659        self.irc.feedMsg(ircmsgs.IrcMsg(command='903', args=('jilles',)))
660
661        self.endCapNegociation()
662
663    def testReauthenticate(self):
664        try:
665            conf.supybot.networks.test.sasl.username.setValue('jilles')
666            conf.supybot.networks.test.sasl.password.setValue('sesame')
667            self.irc = irclib.Irc('test')
668        finally:
669            conf.supybot.networks.test.sasl.username.setValue('')
670            conf.supybot.networks.test.sasl.password.setValue('')
671        self.assertEqual(self.irc.sasl_current_mechanism, None)
672        self.assertEqual(self.irc.sasl_next_mechanisms, ['plain'])
673
674        self.startCapNegociation(caps='')
675
676        self.endCapNegociation()
677
678        while self.irc.takeMsg():
679            pass
680
681        self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
682                args=('*', 'NEW', 'sasl=EXTERNAL')))
683
684        self.irc.takeMsg() # None. But even if it was CAP REQ sasl, it would be ok
685        self.assertEqual(self.irc.takeMsg(), None)
686
687        try:
688            conf.supybot.networks.test.sasl.username.setValue('jilles')
689            conf.supybot.networks.test.sasl.password.setValue('sesame')
690            self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
691                    args=('*', 'DEL', 'sasl')))
692            self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
693                    args=('*', 'NEW', 'sasl=PLAIN')))
694        finally:
695            conf.supybot.networks.test.sasl.username.setValue('')
696            conf.supybot.networks.test.sasl.password.setValue('')
697
698        m = self.irc.takeMsg()
699        self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m)
700        self.assertEqual(m.args[0], 'REQ', m)
701        self.assertEqual(m.args[1], 'sasl')
702        self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP',
703            args=('*', 'ACK', 'sasl')))
704
705        m = self.irc.takeMsg()
706        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
707            args=('PLAIN',)))
708
709        self.irc.feedMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=('+',)))
710
711        m = self.irc.takeMsg()
712        self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE',
713            args=('amlsbGVzAGppbGxlcwBzZXNhbWU=',)))
714
715        self.irc.feedMsg(ircmsgs.IrcMsg(command='900', args=('jilles',)))
716        self.irc.feedMsg(ircmsgs.IrcMsg(command='903', args=('jilles',)))
717
718
719
720class IrcCallbackTestCase(SupyTestCase):
721    class FakeIrc:
722        pass
723    irc = FakeIrc()
724    def testName(self):
725        class UnnamedIrcCallback(irclib.IrcCallback):
726            pass
727        unnamed = UnnamedIrcCallback()
728
729        class NamedIrcCallback(irclib.IrcCallback):
730            myName = 'foobar'
731            def name(self):
732                return self.myName
733        named = NamedIrcCallback()
734        self.assertEqual(unnamed.name(), unnamed.__class__.__name__)
735        self.assertEqual(named.name(), named.myName)
736
737    def testDoCommand(self):
738        def makeCommand(msg):
739            return 'do' + msg.command.capitalize()
740        class DoCommandCatcher(irclib.IrcCallback):
741            def __init__(self):
742                self.L = []
743            def __getattr__(self, attr):
744                self.L.append(attr)
745                return lambda *args: None
746        doCommandCatcher = DoCommandCatcher()
747        for msg in msgs:
748            doCommandCatcher(self.irc, msg)
749        commands = list(map(makeCommand, msgs))
750        self.assertEqual(doCommandCatcher.L, commands)
751
752    def testFirstCommands(self):
753        try:
754            originalNick = conf.supybot.nick()
755            originalUser = conf.supybot.user()
756            originalPassword = conf.supybot.networks.test.password()
757            nick = 'nick'
758            conf.supybot.nick.setValue(nick)
759            user = 'user any user'
760            conf.supybot.user.setValue(user)
761            expected = [
762                ircmsgs.IrcMsg(command='CAP', args=('LS', '302')),
763                ircmsgs.nick(nick),
764                ircmsgs.user('limnoria', user),
765            ]
766            irc = irclib.Irc('test')
767            msgs = [irc.takeMsg()]
768            while msgs[-1] is not None:
769                msgs.append(irc.takeMsg())
770            msgs.pop()
771            self.assertEqual(msgs, expected)
772            password = 'password'
773            conf.supybot.networks.test.password.setValue(password)
774            irc = irclib.Irc('test')
775            msgs = [irc.takeMsg()]
776            while msgs[-1] is not None:
777                msgs.append(irc.takeMsg())
778            msgs.pop()
779            expected.insert(1, ircmsgs.password(password))
780            self.assertEqual(msgs, expected)
781        finally:
782            conf.supybot.nick.setValue(originalNick)
783            conf.supybot.user.setValue(originalUser)
784            conf.supybot.networks.test.password.setValue(originalPassword)
785
786# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
787
788