diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2014-05-31 11:49:21 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2014-05-31 11:49:21 +0200 |
commit | 70d2b0ad8eb7854932d24c007a05b8627f751205 (patch) | |
tree | 986f3012cb14ec5f45048307db34de621c021b4d | |
parent | bb779271ae052726551f8d59b014ed609386c06e (diff) | |
download | weechat-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-x | test/weercd.py | 486 |
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() |