summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2014-05-31 11:49:21 +0200
committerSébastien Helleu <flashcode@flashtux.org>2014-05-31 11:49:21 +0200
commit70d2b0ad8eb7854932d24c007a05b8627f751205 (patch)
tree986f3012cb14ec5f45048307db34de621c021b4d
parentbb779271ae052726551f8d59b014ed609386c06e (diff)
downloadweechat-70d2b0ad8eb7854932d24c007a05b8627f751205.zip
Remove test/weercd.py (move script to its own repository)
The script has been moved to: https://github.com/weechat/weercd
-rwxr-xr-xtest/weercd.py486
1 files changed, 0 insertions, 486 deletions
diff --git a/test/weercd.py b/test/weercd.py
deleted file mode 100755
index 612075aca..000000000
--- a/test/weercd.py
+++ /dev/null
@@ -1,486 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2011-2014 Sébastien Helleu <flashcode@flashtux.org>
-#
-# This file is part of WeeChat, the extensible chat client.
-#
-# WeeChat is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# WeeChat is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
-#
-
-"""
-weercd - the WeeChat IRC testing server
-
-It can be used with any IRC client (not only WeeChat).
-
-In the "flood" mode, various IRC commands are sent in a short time (privmsg,
-notice, join/quit, ..) to test client resistance and memory usage (to quickly
-detect memory leaks, for example with client scripts).
-
-This script works with Python 2.x (>= 2.7) and 3.x.
-
-It is *STRONGLY RECOMMENDED* to connect this server with a client in a test
-environment:
-- for WeeChat, another home with: `weechat --dir /tmp/weechat`
-- on a test machine, because CPU will be used a lot by client to display
- messages from weercd
-- if possible locally (ie server and client on same machine), to speed up
- data exchange between server and client.
-
-Instructions to use this server with WeeChat:
- 1. open a terminal and run server:
- python weercd.py
- 2. open another terminal and run WeeChat with home in /tmp:
- weechat --dir /tmp/weechat
- 3. optional: install script(s) (/script install ...)
- 4. add server and connect to it:
- /server add weercd 127.0.0.1/7777
- /connect weercd
- 5. wait some months.....
- WeeChat still not crashed and does not use 200 TB of RAM ?
- Yeah, it's stable!
-"""
-
-from __future__ import division, print_function
-
-import argparse
-import os
-import random
-import re
-import select
-import shlex
-import socket
-import string
-import sys
-import time
-import traceback
-
-NAME = 'weercd'
-VERSION = '0.8'
-
-
-def fuzzy_string(minlength=1, maxlength=50, spaces=False):
- """Return a fuzzy string (random length and content)."""
- length = random.randint(minlength, maxlength)
- strspace = ''
- if spaces:
- strspace = ' '
- return ''.join(random.choice(string.ascii_uppercase +
- string.ascii_lowercase +
- string.digits + strspace)
- for x in range(length))
-
-
-def fuzzy_host():
- """Return a fuzzy host name."""
- return '{0}@{1}'.format(fuzzy_string(1, 10), fuzzy_string(1, 10))
-
-
-def fuzzy_channel():
- """Return a fuzzy channel name."""
- return '#{0}'.format(fuzzy_string(1, 25))
-
-
-class Client(object):
- """A client of weercd server."""
-
- def __init__(self, sock, addr, args):
- self.sock, self.addr = sock, addr
- self.args = args
- self.name = NAME
- self.version = VERSION
- self.nick = ''
- self.nicknumber = 0
- self.channels = {}
- self.lastbuf = ''
- self.incount, self.outcount, self.inbytes, self.outbytes = 0, 0, 0, 0
- self.quit, self.endmsg, self.endexcept = False, '', None
- self.starttime = time.time()
- self.connect()
-
- def run(self):
- """Execute the action asked for the client."""
- if self.quit:
- return
-
- # send commands from file (which can be stdin)
- if self.args.file:
- self.send_file()
- return
-
- # flood the client
- if self.args.wait > 0:
- print('Waiting', self.args.wait, 'seconds')
- time.sleep(self.args.wait)
- sys.stdout.write('Flooding client..')
- sys.stdout.flush()
- try:
- while not self.quit:
- self.flood()
- except Exception as exc:
- if self.quit:
- self.endmsg = 'quit received'
- else:
- self.endmsg = 'connection lost'
- self.endexcept = exc
- except KeyboardInterrupt:
- self.endmsg = 'interrupted'
- else:
- self.endmsg = 'quit received'
-
- def fuzzy_nick(self, with_number=False):
- """Return a fuzzy nick name."""
- if with_number:
- self.nicknumber += 1
- return '{0}{1}'.format(fuzzy_string(1, 5), self.nicknumber)
- else:
- return fuzzy_string(1, 10)
-
- def send(self, data):
- """Send one message to client."""
- if self.args.debug:
- print('<--', data)
- msg = data + '\r\n'
- self.outbytes += len(msg)
- self.sock.send(msg.encode('UTF-8'))
- self.outcount += 1
-
- def send_cmd(self, cmd, data, nick='{self.name}', host='',
- target='{self.nick}'):
- """Send an IRC command to the client."""
- self.send(':{0}{1}{2} {3}{4}{5}{6}{7}'
- ''.format(nick,
- '!' if host else '',
- host,
- cmd,
- ' ' if target else '',
- target,
- ' :' if data else '',
- data).format(self=self))
-
- def recv(self, data):
- """Read one IRC message from client."""
- if self.args.debug:
- print('-->', data)
- if data.startswith('PING '):
- args = data[5:]
- if args[0] == ':':
- args = args[1:]
- self.send('PONG :{0}'.format(args))
- elif data.startswith('NICK '):
- self.nick = data[5:]
- elif data.startswith('PART '):
- match = re.search('^PART :?(#[^ ]+)', data)
- if match:
- channel = match.group(1)
- if channel in self.channels:
- del self.channels[channel]
- elif data.startswith('QUIT '):
- self.quit = True
- self.incount += 1
-
- def read(self, timeout):
- """Read raw data received from client."""
- inr = select.select([self.sock], [], [], timeout)[0]
- if inr:
- data = self.sock.recv(4096)
- if data:
- data = data.decode('UTF-8')
- self.inbytes += len(data)
- data = self.lastbuf + data
- while True:
- pos = data.find('\r\n')
- if pos < 0:
- break
- self.recv(data[0:pos])
- data = data[pos + 2:]
- self.lastbuf = data
-
- def connect(self):
- """Inform the client that the connection is OK."""
- try:
- count = self.args.nickused
- while self.nick == '':
- self.read(0.1)
- if self.nick and count > 0:
- self.send_cmd('433', 'Nickname is already in use.',
- target='* {self.nick}')
- self.nick = ''
- count -= 1
- self.send_cmd('001', 'Welcome to the WeeChat IRC server')
- self.send_cmd('002', 'Your host is {self.name}, running version '
- '{self.version}')
- self.send_cmd('003', 'Are you solid like a rock?')
- self.send_cmd('004', 'Let\'s see!')
- except KeyboardInterrupt:
- self.quit = True
- self.endmsg = 'interrupted'
- return
-
- def channel_random_nick(self, channel):
- """Return a random nick of a channel."""
- if len(self.channels[channel]) < 2:
- return None
- rnick = self.nick
- while rnick == self.nick:
- rnick = self.channels[channel][
- random.randint(0, len(self.channels[channel]) - 1)]
- return rnick
-
- def flood_self_join(self):
- """Self join on a new channel."""
- channel = fuzzy_channel()
- if channel in self.channels:
- return
- self.send_cmd('JOIN', channel,
- nick=self.nick, host=self.addr[0], target='')
- self.send_cmd('353', '@{self.nick}',
- target='{0} = {1}'.format(self.nick, channel))
- self.send_cmd('366', 'End of /NAMES list.',
- target='{0} {1}'.format(self.nick, channel))
- self.channels[channel] = [self.nick]
-
- def flood_user_notice(self):
- """Notice for the user."""
- self.send_cmd('NOTICE', fuzzy_string(1, 400, spaces=True),
- nick=self.fuzzy_nick(), host=fuzzy_host())
-
- def flood_channel_join(self, channel):
- """Join of a user in a channel."""
- if len(self.channels[channel]) >= self.args.maxnicks:
- return
- newnick = self.fuzzy_nick(with_number=True)
- self.send_cmd('JOIN', channel,
- nick=newnick, host=fuzzy_host(), target='')
- self.channels[channel].append(newnick)
-
- def flood_channel_part(self, channel):
- """Part or quit of a user in a channel."""
- if len(self.channels[channel]) == 0:
- return
- rnick = self.channel_random_nick(channel)
- if not rnick:
- return
- if random.randint(1, 2) == 1:
- self.send_cmd('PART', channel,
- nick=rnick, host=fuzzy_host(), target='')
- else:
- self.send_cmd('QUIT', fuzzy_string(1, 30),
- nick=rnick, host=fuzzy_host(), target='')
- self.channels[channel].remove(rnick)
-
- def flood_channel_kick(self, channel):
- """Kick of a user in a channel."""
- if len(self.channels[channel]) == 0:
- return
- rnick1 = self.channel_random_nick(channel)
- rnick2 = self.channel_random_nick(channel)
- if rnick1 and rnick2 and rnick1 != rnick2:
- self.send_cmd('KICK', fuzzy_string(1, 50),
- nick=rnick1, host=fuzzy_host(),
- target='{0} {1}'.format(channel, rnick2))
- self.channels[channel].remove(rnick2)
-
- def flood_channel_message(self, channel):
- """Message from a user in a channel."""
- if len(self.channels[channel]) == 0:
- return
- rnick = self.channel_random_nick(channel)
- if not rnick:
- return
- msg = fuzzy_string(1, 400, spaces=True)
- if 'channel' in self.args.notice and random.randint(1, 100) == 100:
- # notice for channel
- self.send_cmd('NOTICE', msg,
- nick=rnick, host=fuzzy_host(), target=channel)
- else:
- # add random highlight
- if random.randint(1, 100) == 100:
- msg = '{0}: {1}'.format(self.nick, msg)
- action2 = random.randint(1, 50)
- if action2 == 1:
- # CTCP action (/me)
- msg = '\x01ACTION {0}\x01'.format(msg)
- elif action2 == 2:
- # CTCP version
- msg = '\x01VERSION\x01'
- self.send_cmd('PRIVMSG', msg,
- nick=rnick, host=fuzzy_host(), target=channel)
-
- def flood(self):
- """Yay, funny stuff here! Flood the client!"""
- self.read(self.args.sleep)
- # global actions
- action = random.randint(1, 2)
- if action == 1 and len(self.channels) < self.args.maxchans:
- self.flood_self_join()
- elif action == 2 and 'user' in self.args.notice:
- self.flood_user_notice()
- # actions for each channel
- for channel in self.channels:
- action = random.randint(1, 50)
- if 1 <= action <= 10:
- self.flood_channel_join(channel)
- elif action == 11:
- self.flood_channel_part(channel)
- elif action == 12:
- self.flood_channel_kick(channel)
- else:
- self.flood_channel_message(channel)
- # display progress
- if self.outcount % 1000 == 0:
- sys.stdout.write('.')
- sys.stdout.flush()
-
- def send_file(self):
- """Send messages from a file to client."""
- stdin = self.args.file == sys.stdin
- count = 0
- self.read(0.2)
- try:
- while True:
- # display the prompt if we are reading in stdin
- if stdin:
- sys.stdout.write('Message to send to client: ')
- sys.stdout.flush()
- message = self.args.file.readline()
- if not message:
- break
- if sys.version_info < (3,):
- message = message.decode('UTF-8')
- message = message.rstrip('\n')
- if message and not message.startswith('//'):
- self.send(message.format(self=self))
- count += 1
- self.read(0.1 if stdin else self.args.sleep)
- except IOError as exc:
- self.endmsg = 'unable to read file {0}'.format(self.args.file)
- self.endexcept = exc
- return
- except Exception as exc:
- traceback.print_exc()
- self.endmsg = 'connection lost'
- return
- except KeyboardInterrupt:
- self.endmsg = 'interrupted'
- return
- finally:
- sys.stdout.write('\n')
- sys.stdout.write('{0} messages sent from {1}, press Enter to exit'
- .format(count, 'stdin' if stdin else 'file'))
- sys.stdout.flush()
- try:
- sys.stdin.readline()
- except Exception:
- pass
-
- def stats(self):
- """Display some statistics about data exchanged with the client."""
- msgexcept = ''
- if self.endexcept:
- msgexcept = '({0})'.format(self.endexcept)
- print(self.endmsg, msgexcept)
- elapsed = time.time() - self.starttime
- countrate = self.outcount / elapsed
- bytesrate = self.outbytes / elapsed
- print('Elapsed: {elapsed:.1f}s - '
- 'packets: in:{self.incount}, out:{self.outcount} '
- '({countrate:.0f}/s) - '
- 'bytes: in:{self.inbytes}, out: {self.outbytes} '
- '({bytesrate:.0f}/s)'
- ''.format(self=self,
- elapsed=elapsed,
- countrate=countrate,
- bytesrate=bytesrate))
- if self.endmsg == 'connection lost':
- print('Uh-oh! No quit received, client has crashed? Ahah \\o/')
-
- def __del__(self):
- self.stats()
- print('Closing connection with', self.addr)
- self.sock.close()
-
-
-def main():
- """Main function."""
- # parse command line arguments
- parser = argparse.ArgumentParser(
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- fromfile_prefix_chars='@',
- description='The WeeChat IRC testing server.',
- epilog='Note: the environment variable "WEERCD_OPTIONS" can be '
- 'set with default options. Argument "@file.txt" can be used to read '
- 'default options in a file.')
- parser.add_argument('-H', '--host', help='host for socket bind')
- parser.add_argument('-p', '--port', type=int, default=7777,
- help='port for socket bind')
- parser.add_argument('-f', '--file', type=argparse.FileType('r'),
- help='send messages from file, instead of flooding '
- 'the client (use "-" for stdin)')
- parser.add_argument('-c', '--maxchans', type=int, default=5,
- help='max number of channels to join')
- parser.add_argument('-n', '--maxnicks', type=int, default=100,
- help='max number of nicks per channel')
- parser.add_argument('-u', '--nickused', type=int, default=0,
- help='send 433 (nickname already in use) this number '
- 'of times before accepting nick')
- parser.add_argument('-N', '--notice', metavar='NOTICE_TYPE',
- choices=['user', 'channel'],
- default=['user', 'channel'], nargs='*',
- help='notices to send: "user" (to user), "channel" '
- '(to channel)')
- parser.add_argument('-s', '--sleep', type=float, default=0,
- help='sleep for select: delay between 2 messages sent '
- 'to client (float, in seconds)')
- parser.add_argument('-w', '--wait', type=float, default=0,
- help='time to wait before flooding client (float, '
- 'in seconds)')
- parser.add_argument('-d', '--debug', action='store_true',
- help='debug output')
- parser.add_argument('-v', '--version', action='version', version=VERSION)
- args = parser.parse_args(shlex.split(os.getenv('WEERCD_OPTIONS') or '') +
- sys.argv[1:])
-
- # welcome message, with options
- print(NAME, VERSION, '- WeeChat IRC testing server')
- print('Options:', vars(args))
-
- # main loop
- while True:
- servsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- try:
- servsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- servsock.bind((args.host or '', args.port))
- servsock.listen(1)
- except Exception as exc:
- print('Socket error: {0}'.format(exc))
- sys.exit(1)
- print('Listening on port', args.port, '(ctrl-C to exit)')
- clientsock = None
- addr = None
- try:
- clientsock, addr = servsock.accept()
- except KeyboardInterrupt:
- servsock.close()
- sys.exit(0)
- print('Connection from', addr)
- client = Client(clientsock, addr, args)
- client.run()
- del client
- # no loop if message were sent from a file
- if args.file:
- break
-
-if __name__ == "__main__":
- main()