tools: Remove unused dev tools
This commit is contained in:
parent
413d539a7b
commit
4758a0ac12
@ -27,48 +27,6 @@ code. So, if you're out of work, the code coverage and flake8 data at the CI
|
||||
server should give you a place to start.
|
||||
|
||||
|
||||
Protocol debugger
|
||||
=================
|
||||
|
||||
Since the main interface provided to Mopidy is through the MPD protocol, it is
|
||||
crucial that we try and stay in sync with protocol developments. In an attempt
|
||||
to make it easier to debug differences Mopidy and MPD protocol handling we have
|
||||
created ``tools/debug-proxy.py``.
|
||||
|
||||
This tool is proxy that sits in front of two MPD protocol aware servers and
|
||||
sends all requests to both, returning the primary response to the client and
|
||||
then printing any diff in the two responses.
|
||||
|
||||
Note that this tool depends on ``gevent`` unlike the rest of Mopidy at the time
|
||||
of writing. See :option:`tools/debug-proxy.py --help` for available options.
|
||||
Sample session::
|
||||
|
||||
[127.0.0.1]:59714
|
||||
listallinfo
|
||||
--- Reference response
|
||||
+++ Actual response
|
||||
@@ -1,16 +1,1 @@
|
||||
-file: uri1
|
||||
-Time: 4
|
||||
-Artist: artist1
|
||||
-Title: track1
|
||||
-Album: album1
|
||||
-file: uri2
|
||||
-Time: 4
|
||||
-Artist: artist2
|
||||
-Title: track2
|
||||
-Album: album2
|
||||
-file: uri3
|
||||
-Time: 4
|
||||
-Artist: artist3
|
||||
-Title: track3
|
||||
-Album: album3
|
||||
-OK
|
||||
+ACK [2@0] {listallinfo} incorrect arguments
|
||||
|
||||
To ensure that Mopidy and MPD have comparable state it is suggested you scan
|
||||
the same media directory with both servers.
|
||||
|
||||
Documentation writing
|
||||
=====================
|
||||
|
||||
|
||||
@ -1,195 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import sys
|
||||
|
||||
from gevent import select, server, socket
|
||||
|
||||
COLORS = ['\033[1;%dm' % (30 + i) for i in range(8)]
|
||||
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = COLORS
|
||||
RESET = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
|
||||
|
||||
def proxy(client, address, reference_address, actual_address):
|
||||
"""Main handler code that gets called for each connection."""
|
||||
client.setblocking(False)
|
||||
|
||||
reference = connect(reference_address)
|
||||
actual = connect(actual_address)
|
||||
|
||||
if reference and actual:
|
||||
loop(client, address, reference, actual)
|
||||
else:
|
||||
print 'Could not connect to one of the backends.'
|
||||
|
||||
for sock in (client, reference, actual):
|
||||
close(sock)
|
||||
|
||||
|
||||
def connect(address):
|
||||
"""Connect to given address and set socket non blocking."""
|
||||
try:
|
||||
sock = socket.socket()
|
||||
sock.connect(address)
|
||||
sock.setblocking(False)
|
||||
except socket.error:
|
||||
return None
|
||||
return sock
|
||||
|
||||
|
||||
def close(sock):
|
||||
"""Shutdown and close our sockets."""
|
||||
try:
|
||||
sock.shutdown(socket.SHUT_WR)
|
||||
sock.close()
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
|
||||
def loop(client, address, reference, actual):
|
||||
"""Loop that handles one MPD reqeust/response pair per iteration."""
|
||||
|
||||
# Consume banners from backends
|
||||
responses = dict()
|
||||
disconnected = read(
|
||||
[reference, actual], responses, find_response_end_token)
|
||||
diff(address, '', responses[reference], responses[actual])
|
||||
|
||||
# We lost a backend, might as well give up.
|
||||
if disconnected:
|
||||
return
|
||||
|
||||
client.sendall(responses[reference])
|
||||
|
||||
while True:
|
||||
responses = dict()
|
||||
|
||||
# Get the command from the client. Not sure how an if this will handle
|
||||
# client sending multiple commands currently :/
|
||||
disconnected = read([client], responses, find_request_end_token)
|
||||
|
||||
# We lost the client, might as well give up.
|
||||
if disconnected:
|
||||
return
|
||||
|
||||
# Send the entire command to both backends.
|
||||
reference.sendall(responses[client])
|
||||
actual.sendall(responses[client])
|
||||
|
||||
# Get the entire resonse from both backends.
|
||||
disconnected = read(
|
||||
[reference, actual], responses, find_response_end_token)
|
||||
|
||||
# Send the client the complete reference response
|
||||
client.sendall(responses[reference])
|
||||
|
||||
# Compare our responses
|
||||
diff(address,
|
||||
responses[client], responses[reference], responses[actual])
|
||||
|
||||
# Give up if we lost a backend.
|
||||
if disconnected:
|
||||
return
|
||||
|
||||
|
||||
def read(sockets, responses, find_end_token):
|
||||
"""Keep reading from sockets until they disconnet or we find our token."""
|
||||
|
||||
# This function doesn't go to well with idle when backends are out of sync.
|
||||
disconnected = False
|
||||
|
||||
for sock in sockets:
|
||||
responses.setdefault(sock, '')
|
||||
|
||||
while sockets:
|
||||
for sock in select.select(sockets, [], [])[0]:
|
||||
data = sock.recv(4096)
|
||||
responses[sock] += data
|
||||
|
||||
if find_end_token(responses[sock]):
|
||||
sockets.remove(sock)
|
||||
|
||||
if not data:
|
||||
sockets.remove(sock)
|
||||
disconnected = True
|
||||
|
||||
return disconnected
|
||||
|
||||
|
||||
def find_response_end_token(data):
|
||||
"""Find token that indicates the response is over."""
|
||||
for line in data.splitlines(True):
|
||||
if line.startswith(('OK', 'ACK')) and line.endswith('\n'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def find_request_end_token(data):
|
||||
"""Find token that indicates that request is over."""
|
||||
lines = data.splitlines(True)
|
||||
if not lines:
|
||||
return False
|
||||
elif 'command_list_ok_begin' == lines[0].strip():
|
||||
return 'command_list_end' == lines[-1].strip()
|
||||
else:
|
||||
return lines[0].endswith('\n')
|
||||
|
||||
|
||||
def diff(address, command, reference_response, actual_response):
|
||||
"""Print command from client and a unified diff of the responses."""
|
||||
sys.stdout.write('[%s]:%s\n%s' % (address[0], address[1], command))
|
||||
for line in difflib.unified_diff(reference_response.splitlines(True),
|
||||
actual_response.splitlines(True),
|
||||
fromfile='Reference response',
|
||||
tofile='Actual response'):
|
||||
|
||||
if line.startswith('+') and not line.startswith('+++'):
|
||||
sys.stdout.write(GREEN)
|
||||
elif line.startswith('-') and not line.startswith('---'):
|
||||
sys.stdout.write(RED)
|
||||
elif line.startswith('@@'):
|
||||
sys.stdout.write(CYAN)
|
||||
|
||||
sys.stdout.write(line)
|
||||
sys.stdout.write(RESET)
|
||||
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Handle flag parsing."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Proxy and compare MPD protocol interactions.')
|
||||
parser.add_argument('--listen', default=':6600', type=parse_address,
|
||||
help='address:port to listen on.')
|
||||
parser.add_argument('--reference', default=':6601', type=parse_address,
|
||||
help='address:port for the reference backend.')
|
||||
parser.add_argument('--actual', default=':6602', type=parse_address,
|
||||
help='address:port for the actual backend.')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def parse_address(address):
|
||||
"""Convert host:port or port to address to pass to connect."""
|
||||
if ':' not in address:
|
||||
return ('', int(address))
|
||||
host, port = address.rsplit(':', 1)
|
||||
return (host, int(port))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parse_args()
|
||||
|
||||
def handle(client, address):
|
||||
"""Wrapper that adds reference and actual backends to proxy calls."""
|
||||
return proxy(client, address, args.reference, args.actual)
|
||||
|
||||
try:
|
||||
server.StreamServer(args.listen, handle).serve_forever()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
pass
|
||||
203
tools/idle.py
203
tools/idle.py
@ -1,203 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# This script is helper to systematicly test the behaviour of MPD's idle
|
||||
# command. It is simply provided as a quick hack, expect nothing more.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import pprint
|
||||
import socket
|
||||
|
||||
host = ''
|
||||
port = 6601
|
||||
|
||||
url = "13 - a-ha - White Canvas.mp3"
|
||||
artist = "a-ha"
|
||||
|
||||
data = {'id': None, 'id2': None, 'url': url, 'artist': artist}
|
||||
|
||||
# Commands to run before test requests to coerce MPD into right state
|
||||
setup_requests = [
|
||||
'clear',
|
||||
'add "%(url)s"',
|
||||
'add "%(url)s"',
|
||||
'add "%(url)s"',
|
||||
'play',
|
||||
#'pause', # Uncomment to test paused idle behaviour
|
||||
#'stop', # Uncomment to test stopped idle behaviour
|
||||
]
|
||||
|
||||
# List of commands to test for idle behaviour. Ordering of list is important in
|
||||
# order to keep MPD state as intended. Commands that are obviously
|
||||
# informational only or "harmfull" have been excluded.
|
||||
test_requests = [
|
||||
'add "%(url)s"',
|
||||
'addid "%(url)s" "1"',
|
||||
'clear',
|
||||
#'clearerror',
|
||||
#'close',
|
||||
#'commands',
|
||||
'consume "1"',
|
||||
'consume "0"',
|
||||
# 'count',
|
||||
'crossfade "1"',
|
||||
'crossfade "0"',
|
||||
#'currentsong',
|
||||
#'delete "1:2"',
|
||||
'delete "0"',
|
||||
'deleteid "%(id)s"',
|
||||
'disableoutput "0"',
|
||||
'enableoutput "0"',
|
||||
#'find',
|
||||
#'findadd "artist" "%(artist)s"',
|
||||
#'idle',
|
||||
#'kill',
|
||||
#'list',
|
||||
#'listall',
|
||||
#'listallinfo',
|
||||
#'listplaylist',
|
||||
#'listplaylistinfo',
|
||||
#'listplaylists',
|
||||
#'lsinfo',
|
||||
'move "0:1" "2"',
|
||||
'move "0" "1"',
|
||||
'moveid "%(id)s" "1"',
|
||||
'next',
|
||||
#'notcommands',
|
||||
#'outputs',
|
||||
#'password',
|
||||
'pause',
|
||||
#'ping',
|
||||
'play',
|
||||
'playid "%(id)s"',
|
||||
#'playlist',
|
||||
'playlistadd "foo" "%(url)s"',
|
||||
'playlistclear "foo"',
|
||||
'playlistadd "foo" "%(url)s"',
|
||||
'playlistdelete "foo" "0"',
|
||||
#'playlistfind',
|
||||
#'playlistid',
|
||||
#'playlistinfo',
|
||||
'playlistadd "foo" "%(url)s"',
|
||||
'playlistadd "foo" "%(url)s"',
|
||||
'playlistmove "foo" "0" "1"',
|
||||
#'playlistsearch',
|
||||
#'plchanges',
|
||||
#'plchangesposid',
|
||||
'previous',
|
||||
'random "1"',
|
||||
'random "0"',
|
||||
'rm "bar"',
|
||||
'rename "foo" "bar"',
|
||||
'repeat "0"',
|
||||
'rm "bar"',
|
||||
'save "bar"',
|
||||
'load "bar"',
|
||||
#'search',
|
||||
'seek "1" "10"',
|
||||
'seekid "%(id)s" "10"',
|
||||
#'setvol "10"',
|
||||
'shuffle',
|
||||
'shuffle "0:1"',
|
||||
'single "1"',
|
||||
'single "0"',
|
||||
#'stats',
|
||||
#'status',
|
||||
'stop',
|
||||
'swap "1" "2"',
|
||||
'swapid "%(id)s" "%(id2)s"',
|
||||
#'tagtypes',
|
||||
#'update',
|
||||
#'urlhandlers',
|
||||
#'volume',
|
||||
]
|
||||
|
||||
|
||||
def create_socketfile():
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((host, port))
|
||||
sock.settimeout(0.5)
|
||||
fd = sock.makefile('rw', 1) # 1 = line buffered
|
||||
fd.readline() # Read banner
|
||||
return fd
|
||||
|
||||
|
||||
def wait(fd, prefix=None, collect=None):
|
||||
while True:
|
||||
line = fd.readline().rstrip()
|
||||
if prefix:
|
||||
logging.debug('%s: %s', prefix, repr(line))
|
||||
if line.split()[0] in ('OK', 'ACK'):
|
||||
break
|
||||
|
||||
|
||||
def collect_ids(fd):
|
||||
fd.write('playlistinfo\n')
|
||||
|
||||
ids = []
|
||||
while True:
|
||||
line = fd.readline()
|
||||
if line.split()[0] == 'OK':
|
||||
break
|
||||
if line.split()[0] == 'Id:':
|
||||
ids.append(line.split()[1])
|
||||
return ids
|
||||
|
||||
|
||||
def main():
|
||||
subsystems = {}
|
||||
|
||||
command = create_socketfile()
|
||||
|
||||
for test in test_requests:
|
||||
# Remove any old ids
|
||||
del data['id']
|
||||
del data['id2']
|
||||
|
||||
# Run setup code to force MPD into known state
|
||||
for setup in setup_requests:
|
||||
command.write(setup % data + '\n')
|
||||
wait(command)
|
||||
|
||||
data['id'], data['id2'] = collect_ids(command)[:2]
|
||||
|
||||
# This connection needs to be make after setup commands are done or
|
||||
# else they will cause idle events.
|
||||
idle = create_socketfile()
|
||||
|
||||
# Wait for new idle events
|
||||
idle.write('idle\n')
|
||||
|
||||
test = test % data
|
||||
|
||||
logging.debug('idle: %s', repr('idle'))
|
||||
logging.debug('command: %s', repr(test))
|
||||
|
||||
command.write(test + '\n')
|
||||
wait(command, prefix='command')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = idle.readline().rstrip()
|
||||
except socket.timeout:
|
||||
# Abort try if we time out.
|
||||
idle.write('noidle\n')
|
||||
break
|
||||
|
||||
logging.debug('idle: %s', repr(line))
|
||||
|
||||
if line == 'OK':
|
||||
break
|
||||
|
||||
request_type = test.split()[0]
|
||||
subsystem = line.split()[1]
|
||||
subsystems.setdefault(request_type, set()).add(subsystem)
|
||||
|
||||
logging.debug('---')
|
||||
|
||||
pprint.pprint(subsystems)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user