From 5be4d4019df15e3a940d6e066d113dae70ff208a Mon Sep 17 00:00:00 2001 From: Michael Ziegler Date: Mon, 21 Dec 2009 15:56:04 +0100 Subject: [PATCH] automate starting of Murmur and process handling --- pyweb/mumble/murmurenvutils.py | 45 ++++++++++++++++++ pyweb/mumble/testrunner.py | 87 ++++++++++++++++++++++++++++++++++ pyweb/mumble/tests.py | 49 ------------------- pyweb/settings.py | 2 +- 4 files changed, 133 insertions(+), 50 deletions(-) create mode 100644 pyweb/mumble/testrunner.py diff --git a/pyweb/mumble/murmurenvutils.py b/pyweb/mumble/murmurenvutils.py index 2df8571..6eb46b9 100644 --- a/pyweb/mumble/murmurenvutils.py +++ b/pyweb/mumble/murmurenvutils.py @@ -15,11 +15,13 @@ """ import os, subprocess, signal +from select import select from os.path import join, exists from shutil import copyfile from django.conf import settings +from utils import ObjectInfo def get_available_versions(): """ Return murmur versions installed inside the LAB_DIR. """ @@ -116,11 +118,54 @@ def run_murmur( version ): stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=murmur_root ); + + # Check capabilities by waiting for certain lines to show up. + capa = ObjectInfo( has_dbus=False, has_ice=False, has_instance=False, has_users=False ); + + def canRead( self, timeout=1 ): + rdy_read, rdy_write, rdy_other = select( [self.stdout], [], [], timeout ); + return self.stdout in rdy_read; + + setattr(subprocess.Popen, 'canRead', canRead) + + while process.canRead(0.5): + line = process.stdout.readline(); + if line == 'DBus registration succeeded\n': + capa.has_dbus = True; + elif line == 'MurmurIce: Endpoint "tcp -h 127.0.0.1 -p 6502" running\n': + capa.has_ice = True; + elif line == '1 => Server listening on 0.0.0.0:64738\n': + capa.has_instance = True; + elif "> Authenticated\n" in line: + capa.has_users = True; + + process.capabilities = capa; + return process; raise EnvironmentError( "Murmur binary not found. (Tried %s)" % unicode(binary_candidates) ); +def wait_for_user( process, timeout=1 ): + """ Wait for a user to connect. This call will consume any output from murmur + until a line indicating a user's attempt to connect has been found. + + The timeout parameter specifies how long (in seconds) to wait for input. + It defaults to 1 second. If you set this to 0 it will return at the end + of input (and thereby tell you if a player has already connected). If + you set this to None, the call will block until a player has connected. + + Returns True if a user has connected before the timeout has been hit, + False otherwise. + """ + while process.canRead( timeout ): + line = process.stdout.readline(); + if "> Authenticated\n" in line: + process.capabilities.has_users = True; + return True; + return False; + + def kill_murmur( process ): """ Send a sigterm to the given process. """ return os.kill( process.pid, signal.SIGTERM ); diff --git a/pyweb/mumble/testrunner.py b/pyweb/mumble/testrunner.py new file mode 100644 index 0000000..998c9c8 --- /dev/null +++ b/pyweb/mumble/testrunner.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +""" + * Copyright (C) 2009, Michael "Svedrin" Ziegler + * + * Mumble-Django 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 2 of the License, or + * (at your option) any later version. + * + * This package 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. +""" + +import os + +from django.test.simple import run_tests as django_run_tests +from django.conf import settings + +from murmurenvutils import get_available_versions, run_callback, wait_for_user + + +def run_tests( test_labels, verbosity=1, interactive=True, extra_tests=[] ): + """ Run the Django built in testing framework, but before testing the mumble + app, allow Murmur to be set up correctly. + """ + + if not test_labels: + test_labels = [ appname.split('.')[-1] for appname in settings.INSTALLED_APPS ]; + + # No need to sync any murmur servers for the other apps + os.environ['MURMUR_CONNSTR'] = ''; + + # The easy way: mumble is not being tested. + if "mumble" not in test_labels: + return django_run_tests( test_labels, verbosity, interactive, extra_tests ); + + # First run everything apart from mumble. mumble will be tested separately, so Murmur + # can be set up properly first. + failed_tests = 0; + + if len(test_labels) > 1: + # only run others if mumble is not the only app to be tested + test_labels = list(test_labels); + test_labels.remove( "mumble" ); + failed_tests += django_run_tests( test_labels, verbosity, interactive, extra_tests ); + + failed_tests += run_mumble_tests( verbosity, interactive ); + + return failed_tests; + + +def run_mumble_tests( verbosity=1, interactive=True ): + + connstrings = { + 'DBus': 'net.sourceforge.mumble.murmur', + 'Ice': 'Meta:tcp -h 127.0.0.1 -p 6502', + }; + + def django_run_tests_wrapper( process, version ): + wr_failed_tests = 0; + + for method in connstrings: + # Check if this server is ready to be used with the current method + if getattr( process.capabilities, ("has_%s" % method.lower()), False ): + print "Testing mumble %s via %s" % ( version, method ); + + os.environ['MURMUR_CONNSTR'] = connstrings[method]; + settings.DEFAULT_CONN = connstrings[method]; + + print "Waiting for user to connect (60 seconds)." + wait_for_user( process, timeout=60 ); + + wr_failed_tests += django_run_tests( ('mumble',), verbosity, interactive, [] ); + else: + print "Mumble %s does not support Method %s" % ( version, method ); + + return wr_failed_tests; + + failed_tests = 0; + + for version in get_available_versions(): + failed_tests += run_callback( version, django_run_tests_wrapper, version ); + + return failed_tests; diff --git a/pyweb/mumble/tests.py b/pyweb/mumble/tests.py index 0037a8b..5cc289e 100644 --- a/pyweb/mumble/tests.py +++ b/pyweb/mumble/tests.py @@ -17,12 +17,9 @@ import os from django.test import TestCase -from django.test.simple import run_tests as django_run_tests -from django.conf import settings from models import * from utils import ObjectInfo -from murmurenvutils import get_available_versions, run_callback class InstancesHandling( TestCase ): @@ -202,50 +199,4 @@ class DataReading( TestCase ): self.assert_( hasattr( grp, "members" ) ); -def run_tests( test_labels, verbosity=1, interactive=True, extra_tests=[] ): - """ Run the Django built in testing framework, but before testing the mumble - app, allow Murmur to be set up correctly. - """ - - if not test_labels: - test_labels = [ appname.split('.')[-1] for appname in settings.INSTALLED_APPS ]; - - # No need to sync any murmur servers for the other apps - os.environ['MURMUR_CONNSTR'] = ''; - - # The easy way: mumble is not being tested. - if "mumble" not in test_labels: - return django_run_tests( test_labels, verbosity, interactive, extra_tests ); - - # First run everything apart from mumble. mumble will be tested separately, so Murmur - # can be set up properly first. - - test_labels.remove( "mumble" ); - failed_tests = django_run_tests( test_labels, verbosity, interactive, extra_tests ); - - failed_tests += run_mumble_tests( verbosity, interactive ); - - return failed_tests; - - -def run_mumble_tests( verbosity=1, interactive=True ): - - connstrings = { - 'DBus': 'net.sourceforge.mumble.murmur', - 'Ice': 'Meta:tcp -h 127.0.0.1 -p 6502', - }; - - failed_tests = 0; - - def django_run_tests_wrapper( process ): - return django_run_tests( ('mumble',), verbosity, interactive, [] ), False; - - for version in get_available_versions(): - for method in connstrings: - print "Testing mumble %s via %s" % ( version, method ); - os.environ['MURMUR_CONNSTR'] = connstrings[method]; - settings.DEFAULT_CONN = connstrings[method]; - failed_tests += run_callback( version, django_run_tests_wrapper ); - - return failed_tests; diff --git a/pyweb/settings.py b/pyweb/settings.py index 76c0ffa..5695d0e 100644 --- a/pyweb/settings.py +++ b/pyweb/settings.py @@ -180,7 +180,7 @@ TEMPLATE_DIRS = ( join( MUMBLE_DJANGO_ROOT, 'template' ), ) -TEST_RUNNER = 'mumble.tests.run_tests' +TEST_RUNNER = 'mumble.testrunner.run_tests' TEST_MURMUR_LAB_DIR = join( dirname(MUMBLE_DJANGO_ROOT), 'murmur' ); TEST_MURMUR_FILES_DIR = join( MUMBLE_DJANGO_ROOT, 'testdata' );