1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
|
#! /usr/bin/env python
import os
import re
import shutil
import signal
import socket
import tempfile
import time
from nose.tools import assert_not_in, assert_true
SERVER_PORT = 16667
class ServerFixture(object):
def setUp(self, persistent=False):
if persistent:
self.state_dir = tempfile.mkdtemp()
else:
self.state_dir = None
pid = os.fork()
if pid == 0:
# Child.
arguments = [
"miniircd",
# "--debug",
"--ports=%d" % SERVER_PORT,
]
if persistent:
arguments.append("--state-dir=%s" % self.state_dir)
os.execv("./miniircd", arguments)
# Parent.
self.child_pid = pid
self.connections = {} # nick -> fp
def connect(self, nick):
assert_not_in(nick, self.connections)
s = socket.socket()
tries_left = 100
while tries_left > 0:
try:
s.connect(("localhost", SERVER_PORT))
break
except socket.error:
tries_left -= 1
time.sleep(0.01)
self.connections[nick] = s.makefile(mode="rw")
self.send(nick, "NICK %s" % nick)
self.send(nick, "USER %s * * %s" % (nick, nick))
self.expect(nick, r":local\S+ 001 %s :.*" % nick)
self.expect(nick, r":local\S+ 002 %s :.*" % nick)
self.expect(nick, r":local\S+ 003 %s :.*" % nick)
self.expect(nick, r":local\S+ 004 %s :.*" % nick)
self.expect(nick, r":local\S+ 251 %s :.*" % nick)
self.expect(nick, r":local\S+ 422 %s :.*" % nick)
def shutDown(self):
os.kill(self.child_pid, signal.SIGTERM)
os.waitpid(self.child_pid, 0)
if self.state_dir:
try:
shutil.rmtree(self.state_dir)
except IOError:
pass
def tearDown(self):
self.shutDown()
for x in self.connections.values():
x.close()
def send(self, nick, message):
self.connections[nick].write(message + "\r\n")
self.connections[nick].flush()
def expect(self, nick, regexp):
def timeout_handler(signum, frame):
raise AssertionError("timeout while waiting for %r" % regexp)
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(1) # Give the server 1 second to respond
line = self.connections[nick].readline().rstrip()
signal.alarm(0) # Cancel the alarm
regexp = ("^%s$" % regexp).replace(r"local\S+", socket.getfqdn())
m = re.match(regexp, line)
if m:
return m
else:
assert_true(False, "Regexp %r didn't match %r" % (regexp, line))
class TwoClientsTwoChannelsFixture(ServerFixture):
def setUp(self):
ServerFixture.setUp(self)
try:
self.connect("apa")
self.send("apa", "JOIN #fisk,#brugd")
self.expect("apa", r":apa!apa@127.0.0.1 JOIN #fisk")
self.expect("apa", r":local\S+ 331 apa #fisk :.*")
self.expect("apa", r":local\S+ 353 apa = #fisk :apa")
self.expect("apa", r":local\S+ 366 apa #fisk :.*")
self.expect("apa", r":apa!apa@127.0.0.1 JOIN #brugd")
self.expect("apa", r":local\S+ 331 apa #brugd :.*")
self.expect("apa", r":local\S+ 353 apa = #brugd :apa")
self.expect("apa", r":local\S+ 366 apa #brugd :.*")
self.connect("lemur")
self.send("lemur", "JOIN #fisk,#brugd unused1,unused2")
self.expect("lemur", r":lemur!lemur@127.0.0.1 JOIN #fisk")
self.expect("lemur", r":local\S+ 331 lemur #fisk :.*")
self.expect("lemur", r":local\S+ 353 lemur = #fisk :apa lemur")
self.expect("lemur", r":local\S+ 366 lemur #fisk :.*")
self.expect("lemur", r":lemur!lemur@127.0.0.1 JOIN #brugd")
self.expect("lemur", r":local\S+ 331 lemur #brugd :.*")
self.expect("lemur", r":local\S+ 353 lemur = #brugd :apa lemur")
self.expect("lemur", r":local\S+ 366 lemur #brugd :.*")
self.expect("apa", r":lemur!lemur@127.0.0.1 JOIN #fisk")
self.expect("apa", r":lemur!lemur@127.0.0.1 JOIN #brugd")
except:
self.shutDown()
raise
class TestBasicStuff(ServerFixture):
def test_registration(self):
self.connect("apa")
def test_bad_ping(self):
self.connect("apa")
self.send("apa", "PING")
self.expect("apa", r"\S+ 409 apa :.*")
def test_good_ping(self):
self.connect("apa")
self.send("apa", "PING :fisk")
self.expect("apa", r":local\S+ PONG \S+ :fisk")
def test_unknown_command(self):
self.connect("apa")
self.send("apa", "FISK fisk")
self.expect("apa", r":local\S+ 421 apa FISK :.*")
def test_away(self):
self.connect("apa")
self.send("apa", "AWAY :gone fishing")
# Currently no reply.
def test_argumentless_away(self):
self.connect("apa")
self.send("apa", "AWAY")
# Currently no reply.
def test_argumentless_join(self):
self.connect("apa")
self.send("apa", "JOIN")
self.expect("apa", r":local\S+ 461 apa JOIN :Not enough parameters")
def test_argumentless_list(self):
self.connect("apa")
self.send("apa", "LIST")
self.expect("apa", r":local\S+ 323 apa :End of LIST")
def test_argumentless_mode(self):
self.connect("apa")
self.send("apa", "MODE")
self.expect("apa", r":local\S+ 461 apa MODE :Not enough parameters")
def test_argumentless_motd(self):
self.connect("apa")
self.send("apa", "MOTD")
self.expect("apa", r":local\S+ 422 apa :MOTD File is missing")
def test_argumentless_nick(self):
self.connect("apa")
self.send("apa", "NICK")
self.expect("apa", r":local\S+ 431 :No nickname given")
def test_argumentless_notice(self):
self.connect("apa")
self.send("apa", "NOTICE")
self.expect("apa", r":local\S+ 411 apa :No recipient given \(NOTICE\)")
def test_privmsg_to_user(self):
self.connect("apa")
self.connect("lemur")
self.send("apa", "PRIVMSG lemur :fisk")
self.expect("lemur", r":apa!apa@127.0.0.1 PRIVMSG lemur :fisk")
def test_privmsg_to_nobody(self):
self.connect("apa")
self.send("apa", "PRIVMSG lemur :fisk")
self.expect("apa", r":local\S+ 401 apa lemur :.*")
def test_notice_to_user(self):
self.connect("apa")
self.connect("lemur")
self.send("apa", "NOTICE lemur :fisk")
self.expect("lemur", r":apa!apa@127.0.0.1 NOTICE lemur :fisk")
def test_notice_to_nobody(self):
self.connect("apa")
self.send("apa", "NOTICE lemur :fisk")
self.expect("apa", r":local\S+ 401 apa lemur :.*")
def test_join_and_part_one_user(self):
self.connect("apa")
self.send("apa", "LIST")
self.expect("apa", r":local\S+ 323 apa :.*")
self.send("apa", "JOIN #fisk")
self.expect("apa", r":apa!apa@127.0.0.1 JOIN #fisk")
self.expect("apa", r":local\S+ 331 apa #fisk :.*")
self.expect("apa", r":local\S+ 353 apa = #fisk :apa")
self.expect("apa", r":local\S+ 366 apa #fisk :.*")
self.send("apa", "LIST")
self.expect("apa", r":local\S+ 322 apa #fisk 1 :")
self.expect("apa", r":\S+ 323 apa :.*")
self.send("apa", "PART #fisk")
self.expect("apa", r":apa!apa@127.0.0.1 PART #fisk :apa")
self.send("apa", "LIST")
self.expect("apa", r":\S+ 323 apa :.*")
def test_join_and_part_two_users(self):
self.connect("apa")
self.send("apa", "JOIN #fisk")
self.expect("apa", r":apa!apa@127.0.0.1 JOIN #fisk")
self.expect("apa", r":local\S+ 331 apa #fisk :.*")
self.expect("apa", r":local\S+ 353 apa = #fisk :apa")
self.expect("apa", r":local\S+ 366 apa #fisk :.*")
self.connect("lemur")
self.send("lemur", "JOIN #fisk")
self.expect("lemur", r":lemur!lemur@127.0.0.1 JOIN #fisk")
self.expect("lemur", r":local\S+ 331 lemur #fisk :.*")
self.expect("lemur", r":local\S+ 353 lemur = #fisk :apa lemur")
self.expect("lemur", r":local\S+ 366 lemur #fisk :.*")
self.expect("apa", r":lemur!lemur@127.0.0.1 JOIN #fisk")
self.send("lemur", "PART #fisk :boa")
self.expect("lemur", r":lemur!lemur@127.0.0.1 PART #fisk :boa")
self.expect("apa", r":lemur!lemur@127.0.0.1 PART #fisk :boa")
def test_ison(self):
self.connect("apa")
self.send("apa", "ISON apa lemur")
self.expect("apa", r":local\S+ 303 apa :apa")
self.connect("lemur")
self.send("apa", "ISON apa lemur")
self.expect("apa", r":local\S+ 303 apa :apa lemur")
def test_lusers(self):
self.connect("apa")
self.send("apa", "lusers")
self.expect("apa",
r":local\S+ 251 apa :There are \d+ users and \d+ services"
" on \d+ servers*")
class TestTwoChannelsStuff(TwoClientsTwoChannelsFixture):
def test_privmsg_to_channel(self):
self.send("apa", "PRIVMSG #fisk :lax")
self.expect("lemur", r":apa!apa@127.0.0.1 PRIVMSG #fisk :lax")
def test_notice_to_channel(self):
self.send("apa", "NOTICE #fisk :lax")
self.expect("lemur", r":apa!apa@127.0.0.1 NOTICE #fisk :lax")
def test_get_empty_topic(self):
self.send("apa", "TOPIC #fisk")
self.expect("apa", r":local\S+ 331 apa #fisk :.*")
def test_set_topic(self):
self.send("apa", "TOPIC #fisk :sill")
self.expect("apa", r":apa!apa@127.0.0.1 TOPIC #fisk :sill")
self.expect("lemur", r":apa!apa@127.0.0.1 TOPIC #fisk :sill")
self.send("apa", "LIST")
self.expect("apa", r":local\S+ 322 apa #brugd 2 :")
self.expect("apa", r":local\S+ 322 apa #fisk 2 :sill")
self.expect("apa", r":\S+ 323 apa :.*")
def test_get_topic(self):
self.send("apa", "TOPIC #fisk :sill")
self.expect("apa", r":apa!apa@127.0.0.1 TOPIC #fisk :sill")
self.expect("lemur", r":apa!apa@127.0.0.1 TOPIC #fisk :sill")
self.send("lemur", "TOPIC #fisk")
self.expect("lemur", r":local\S+ 332 lemur #fisk :sill")
def test_channel_key(self):
self.send("apa", "MODE #fisk +k nors")
self.expect("apa", r":apa!apa@127.0.0.1 MODE #fisk \+k nors")
self.expect("lemur", r":apa!apa@127.0.0.1 MODE #fisk \+k nors")
self.send("apa", "PART #fisk")
self.expect("apa", r":apa!apa@127.0.0.1 PART #fisk :apa")
self.expect("lemur", r":apa!apa@127.0.0.1 PART #fisk :apa")
self.send("apa", "MODE #fisk -k")
self.expect("apa", r":local\S+ 442 #fisk :.*")
self.send("apa", "MODE #fisk +k boa")
self.expect("apa", r":local\S+ 442 #fisk :.*")
self.send("apa", "JOIN #fisk")
self.expect("apa", r":local\S+ 475 apa #fisk :.*")
self.send("apa", "JOIN #fisk nors")
self.expect("apa", r":apa!apa@127.0.0.1 JOIN #fisk")
self.expect("apa", r":local\S+ 331 apa #fisk :.*")
self.expect("apa", r":local\S+ 353 apa = #fisk :apa lemur")
self.expect("apa", r":local\S+ 366 apa #fisk :.*")
self.expect("lemur", r":apa!apa@127.0.0.1 JOIN #fisk")
self.send("apa", "MODE #fisk")
self.expect("apa", r":local\S+ 324 apa #fisk \+k nors")
class TestPersistentState(ServerFixture):
def setUp(self):
ServerFixture.setUp(self, True)
def test_persistent_channel_state(self):
self.connect("apa")
self.send("apa", "JOIN #fisk")
self.expect("apa", r":apa!apa@127.0.0.1 JOIN #fisk")
self.expect("apa", r":local\S+ 331 apa #fisk :.*")
self.expect("apa", r":local\S+ 353 apa = #fisk :apa")
self.expect("apa", r":local\S+ 366 apa #fisk :.*")
self.send("apa", "TOPIC #fisk :molusk")
self.expect("apa", r":apa!apa@127.0.0.1 TOPIC #fisk :molusk")
self.send("apa", "MODE #fisk +k skunk")
self.expect("apa", r":apa!apa@127.0.0.1 MODE #fisk \+k skunk")
self.send("apa", "PART #fisk")
self.expect("apa", r":apa!apa@127.0.0.1 PART #fisk :apa")
self.send("apa", "MODE #fisk")
self.expect("apa", r":local\S+ 403 apa #fisk :.*")
self.send("apa", "JOIN #fisk")
self.expect("apa", r":local\S+ 475 apa #fisk :.*")
self.send("apa", "JOIN #fisk skunk")
self.expect("apa", r":apa!apa@127.0.0.1 JOIN #fisk")
self.expect("apa", r":local\S+ 332 apa #fisk :molusk")
self.expect("apa", r":local\S+ 353 apa = #fisk :apa")
self.expect("apa", r":local\S+ 366 apa #fisk :.*")
self.send("apa", "MODE #fisk")
self.expect("apa", r":local\S+ 324 apa #fisk \+k skunk")
|