<commit_before>import json

import pytest

from netdumplings import Dumpling, DumplingChef, DumplingDriver

class TestChef(DumplingChef):
    pass

@pytest.fixture
def mock_kitchen(mocker):
    kitchen = mocker.Mock()
    kitchen.name = 'TestKitchen'
    kitchen.interface = 'en0'
    kitchen.filter = 'tcp'
    kitchen.chef_poke_interval = 5

    return kitchen

@pytest.fixture
def mock_packet(mocker):
    packet = mocker.Mock()
    packet.summary.return_value = 'packet summary string'

    return packet

class TestDumplingChef:
    """
    Test the DumplingChef class.
    """
    def test_init_default(self):
        """
        Test default DumplingChef initialization.
        """
        chef = DumplingChef()

        assert chef.kitchen is None
        assert chef.dumpling_queue is None
        assert chef.receive_pokes is False
        assert chef.name == 'DumplingChef'
        assert chef.dumplings_sent_count == 0

    def test_subclass_chef_name(self):
        """
        Test the setting of the name for a subclass of DumplingChef. The name
        should match the class name.
        """
        chef = TestChef()
        assert chef.name == 'TestChef'

    def test_kitchen_registration_with_pokes(self, mock_kitchen):
        """
        Test registering a chef with a kitchen with interval pokes enabled.
        """
        chef = TestChef(kitchen=mock_kitchen, receive_pokes=True)

        mock_kitchen.register_handler.assert_called_once_with(
            chef_name='TestChef',
            packet_handler=chef.packet_handler,
            interval_handler=chef.interval_handler,
        )

    def test_kitchen_registration_without_pokes(self, mock_kitchen):
        """
        Test registering a chef with a kitchen with interval pokes disabled.
        """
        chef = TestChef(kitchen=mock_kitchen, receive_pokes=False)

        mock_kitchen.register_handler.assert_called_once_with(
            chef_name='TestChef',
            packet_handler=chef.packet_handler,
            interval_handler=False,
        )

    def test_default_packet_handler(self, mocker, mock_kitchen, mock_packet):
        """
        Test the default packet handler. It should send a dumpling with a
        string payload matching "<ChefName>: <packet summary string>".
        """
        chef = TestChef(kitchen=mock_kitchen, receive_pokes=False)
        mocker.patch.object(chef, 'send_packet_dumpling')

        expected_payload = 'TestChef: packet summary string'
        chef.packet_handler(mock_packet)
        chef.send_packet_dumpling.assert_called_with(expected_payload)

    def test_send_interval_dumpling(self, mocker, mock_kitchen):
        """
        Test sending an interval dumpling. It should invoke _send_dumpling
        with the interval dumpling driver.
        """
        chef = TestChef(kitchen=mock_kitchen, receive_pokes=True)
        mocker.patch.object(chef, '_send_dumpling')

        test_payload = {'one': 1, 'two': 2}
        chef.send_interval_dumpling(test_payload)

        chef._send_dumpling.assert_called_with(
            test_payload, driver=DumplingDriver.interval
        )

    def test_send_packet_dumpling(self, mocker, mock_kitchen):
        """
        Test sending an interval dumpling. It should invoke _send_dumpling
        with the packet dumpling driver.
        """
        chef = TestChef(kitchen=mock_kitchen, receive_pokes=True)
        mocker.patch.object(chef, '_send_dumpling')

        test_payload = {'one': 1, 'two': 2}
        chef.send_packet_dumpling(test_payload)

        chef._send_dumpling.assert_called_once_with(
            test_payload, driver=DumplingDriver.packet
        )

    def test_dumpling_send(self, mocker, mock_kitchen):
        """
        Test the _send_dumpling method to ensure that it puts the dumpling
        contents onto the queue.
        """
        test_payload = {
            'one': 1,
            'two': 2,
        }
        test_payload_json = json.dumps(test_payload)

        mock_queue = mocker.Mock()
        chef = TestChef(
            kitchen=mock_kitchen,
            dumpling_queue=mock_queue,
            receive_pokes=True,
        )

        patch_dumpling_callable = mocker.patch.object(Dumpling, '__call__')
        patch_dumpling_callable.return_value = test_payload_json

        # Ensure that _send_dumpling() puts the return value of the
        # dumpling call (which is the same as Dumpling.make()) onto the queue.
        chef._send_dumpling(test_payload, driver=DumplingDriver.packet)
        mock_queue.put.assert_called_once_with(test_payload_json)
<commit_msg>Rename TestChef to prevent it from being treated as a test runner class<commit_after>import json

import pytest

from netdumplings import Dumpling, DumplingChef, DumplingDriver

class ChefForTests(DumplingChef):
    pass

@pytest.fixture
def mock_kitchen(mocker):
    kitchen = mocker.Mock()
    kitchen.name = 'TestKitchen'
    kitchen.interface = 'en0'
    kitchen.filter = 'tcp'
    kitchen.chef_poke_interval = 5

    return kitchen

@pytest.fixture
def mock_packet(mocker):
    packet = mocker.Mock()
    packet.summary.return_value = 'packet summary string'

    return packet

class TestDumplingChef:
    """
    Test the DumplingChef class.
    """
    def test_init_default(self):
        """
        Test default DumplingChef initialization.
        """
        chef = DumplingChef()

        assert chef.kitchen is None
        assert chef.dumpling_queue is None
        assert chef.receive_pokes is False
        assert chef.name == 'DumplingChef'
        assert chef.dumplings_sent_count == 0

    def test_subclass_chef_name(self):
        """
        Test the setting of the name for a subclass of DumplingChef. The name
        should match the class name.
        """
        chef = ChefForTests()
        assert chef.name == 'ChefForTests'

    def test_kitchen_registration_with_pokes(self, mock_kitchen):
        """
        Test registering a chef with a kitchen with interval pokes enabled.
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=True)

        mock_kitchen.register_handler.assert_called_once_with(
            chef_name='ChefForTests',
            packet_handler=chef.packet_handler,
            interval_handler=chef.interval_handler,
        )

    def test_kitchen_registration_without_pokes(self, mock_kitchen):
        """
        Test registering a chef with a kitchen with interval pokes disabled.
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=False)

        mock_kitchen.register_handler.assert_called_once_with(
            chef_name='ChefForTests',
            packet_handler=chef.packet_handler,
            interval_handler=False,
        )

    def test_default_packet_handler(self, mocker, mock_kitchen, mock_packet):
        """
        Test the default packet handler. It should send a dumpling with a
        string payload matching "<ChefName>: <packet summary string>".
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=False)
        mocker.patch.object(chef, 'send_packet_dumpling')

        expected_payload = 'ChefForTests: packet summary string'
        chef.packet_handler(mock_packet)
        chef.send_packet_dumpling.assert_called_with(expected_payload)

    def test_send_interval_dumpling(self, mocker, mock_kitchen):
        """
        Test sending an interval dumpling. It should invoke _send_dumpling
        with the interval dumpling driver.
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=True)
        mocker.patch.object(chef, '_send_dumpling')

        test_payload = {'one': 1, 'two': 2}
        chef.send_interval_dumpling(test_payload)

        chef._send_dumpling.assert_called_with(
            test_payload, driver=DumplingDriver.interval
        )

    def test_send_packet_dumpling(self, mocker, mock_kitchen):
        """
        Test sending an interval dumpling. It should invoke _send_dumpling
        with the packet dumpling driver.
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=True)
        mocker.patch.object(chef, '_send_dumpling')

        test_payload = {'one': 1, 'two': 2}
        chef.send_packet_dumpling(test_payload)

        chef._send_dumpling.assert_called_once_with(
            test_payload, driver=DumplingDriver.packet
        )

    def test_dumpling_send(self, mocker, mock_kitchen):
        """
        Test the _send_dumpling method to ensure that it puts the dumpling
        contents onto the queue.
        """
        test_payload = {
            'one': 1,
            'two': 2,
        }
        test_payload_json = json.dumps(test_payload)

        mock_queue = mocker.Mock()
        chef = ChefForTests(
            kitchen=mock_kitchen,
            dumpling_queue=mock_queue,
            receive_pokes=True,
        )

        patch_dumpling_callable = mocker.patch.object(Dumpling, '__call__')
        patch_dumpling_callable.return_value = test_payload_json

        # Ensure that _send_dumpling() puts the return value of the
        # dumpling call (which is the same as Dumpling.make()) onto the queue.
        chef._send_dumpling(test_payload, driver=DumplingDriver.packet)
        mock_queue.put.assert_called_once_with(test_payload_json)
<|endoftext|>


<commit_before>"Smarkets API package"
# Copyright (C) 2011 Smarkets Limited <support@smarkets.com>
#
# This module is released under the MIT License:
# http://www.opensource.org/licenses/mit-license.php

import os

if 'READTHEDOCS' in os.environ:
    from mock import Mock
    import sys

    sys.modules['smarkets.streaming_api.eto'] = sys.modules['smarkets.streaming_api.seto'] = Mock()

__version__ = '0.6.0c2'

def private(something):
    something.__private__ = True
    return something

__all__ = ()
<commit_msg>Bump version to 0.6.0<commit_after>"Smarkets API package"
# Copyright (C) 2011 Smarkets Limited <support@smarkets.com>
#
# This module is released under the MIT License:
# http://www.opensource.org/licenses/mit-license.php

import os

if 'READTHEDOCS' in os.environ:
    from mock import Mock
    import sys

    sys.modules['smarkets.streaming_api.eto'] = sys.modules['smarkets.streaming_api.seto'] = Mock()

__version__ = '0.6.0'

def private(something):
    something.__private__ = True
    return something

__all__ = ()
<|endoftext|>


<commit_before><commit_msg>Test redis<commit_after><|endoftext|>


<commit_before><commit_msg>You must return the class from a decorator function<commit_after><|endoftext|>


<commit_before># Module:	utils
# Date:		13th September 2007
# Author:	James Mills, prologic at shortcircuit dot net dot au

"""Utilities

This module implements utility functions.
"""

import re
import cgi
from gzip import GzipFile
from cStringIO import StringIO

quoted_slash = re.compile("(?i)%2F")

def quoteHTML(html):
	return html.replace("&", "&").replace("<", "<").replace(">", ">")

image_map_pattern = re.compile(r"[0-9]+,[0-9]+")

def parseQueryString(query_string, keep_blank_values=True):
	"""parseQueryString(query_string) -> dict

	Build a params dictionary from a query_string.
	If keep_blank_values is True (the default), keep
	values that are blank.
	"""

	if image_map_pattern.match(query_string):
		# Server-side image map. Map the coords to "x" and "y"
		# (like CGI::Request does).
		pm = query_string.split(",")
		pm = {"x": int(pm[0]), "y": int(pm[1])}
	else:
		pm = cgi.parse_qs(query_string, keep_blank_values)
		for key, val in pm.items():
			if len(val) == 1:
				pm[key] = val[0]
	return pm

def dictform(form):
	d = {}
	for key in form.keys():
		values = form[key]
		if isinstance(values, list):
			d[key] = []
			for item in values:
				if item.filename is not None:
					value = item # It's a file upload
				else:
					value = item.value # It's a regular field
				d[key].append(value)
		else:
			if values.filename is not None:
				value = values # It's a file upload
			else:
				value = values.value # It's a regular field
			d[key] = value
	return d

def compressBuf(buf):
	zbuf = StringIO()
	zfile = GzipFile(mode="wb",  fileobj=zbuf, compresslevel=1)
	zfile.write(buf)
	zfile.close()
	zbuf.flush()
	zbuf.seek(0)
	return zbuf
<commit_msg>lib.web.utils: Added url() function to calculate an absolute url given a request object and optional parameters<commit_after># Module:	utils
# Date:		13th September 2007
# Author:	James Mills, prologic at shortcircuit dot net dot au

"""Utilities

This module implements utility functions.
"""

import re
import cgi
from gzip import GzipFile
from cStringIO import StringIO

quoted_slash = re.compile("(?i)%2F")

def quoteHTML(html):
	return html.replace("&", "&").replace("<", "<").replace(">", ">")

image_map_pattern = re.compile(r"[0-9]+,[0-9]+")

def parseQueryString(query_string, keep_blank_values=True):
	"""parseQueryString(query_string) -> dict

	Build a params dictionary from a query_string.
	If keep_blank_values is True (the default), keep
	values that are blank.
	"""

	if image_map_pattern.match(query_string):
		# Server-side image map. Map the coords to "x" and "y"
		# (like CGI::Request does).
		pm = query_string.split(",")
		pm = {"x": int(pm[0]), "y": int(pm[1])}
	else:
		pm = cgi.parse_qs(query_string, keep_blank_values)
		for key, val in pm.items():
			if len(val) == 1:
				pm[key] = val[0]
	return pm

def dictform(form):
	d = {}
	for key in form.keys():
		values = form[key]
		if isinstance(values, list):
			d[key] = []
			for item in values:
				if item.filename is not None:
					value = item # It's a file upload
				else:
					value = item.value # It's a regular field
				d[key].append(value)
		else:
			if values.filename is not None:
				value = values # It's a file upload
			else:
				value = values.value # It's a regular field
			d[key] = value
	return d

def compressBuf(buf):
	zbuf = StringIO()
	zfile = GzipFile(mode="wb",  fileobj=zbuf, compresslevel=1)
	zfile.write(buf)
	zfile.close()
	zbuf.flush()
	zbuf.seek(0)
	return zbuf

def url(request, path="", qs="", script_name=None, base=None, relative=None):
    """Create an absolute URL for the given path.

    If 'path' starts with a slash ('/'), this will return
        (base + script_name + path + qs).
    If it does not start with a slash, this returns
        (base + script_name [+ request.path_info] + path + qs).

    If script_name is None, request will be used
    to find a script_name, if available.

    If base is None, request.base will be used (if available).

    Finally, note that this function can be used to obtain an absolute URL
    for the current request path (minus the querystring) by passing no args.
    If you call url(qs=request.query_string), you should get the
    original browser URL (assuming no internal redirections).

    If relative is None or not provided, request.app.relative_urls will
    be used (if available, else False). If False, the output will be an
    absolute URL (including the scheme, host, vhost, and script_name).
    If True, the output will instead be a URL that is relative to the
    current request path, perhaps including '..' atoms. If relative is
    the string 'server', the output will instead be a URL that is
    relative to the server root; i.e., it will start with a slash.
    """
    if qs:
        qs = '?' + qs

    if request.app:
        if not path.startswith("/"):
            # Append/remove trailing slash from path_info as needed
            # (this is to support mistyped URL's without redirecting;
            # if you want to redirect, use tools.trailing_slash).
            pi = request.path_info
            if request.is_index is True:
                if not pi.endswith('/'):
                    pi = pi + '/'
            elif request.is_index is False:
                if pi.endswith('/') and pi != '/':
                    pi = pi[:-1]

            if path == "":
                path = pi
            else:
                path = _urljoin(pi, path)

        if script_name is None:
            script_name = request.script_name
        if base is None:
            base = request.base

        newurl = base + script_name + path + qs
    else:
        # No request.app (we're being called outside a request).
        # We'll have to guess the base from server.* attributes.
        # This will produce very different results from the above
        # if you're using vhosts or tools.proxy.
        if base is None:
            base = request.server.base()

        path = (script_name or "") + path
        newurl = base + path + qs

    if './' in newurl:
        # Normalize the URL by removing ./ and ../
        atoms = []
        for atom in newurl.split('/'):
            if atom == '.':
                pass
            elif atom == '..':
                atoms.pop()
            else:
                atoms.append(atom)
        newurl = '/'.join(atoms)

    # At this point, we should have a fully-qualified absolute URL.

    if relative is None:
        relative = getattr(request.app, "relative_urls", False)

    # See http://www.ietf.org/rfc/rfc2396.txt
    if relative == 'server':
        # "A relative reference beginning with a single slash character is
        # termed an absolute-path reference, as defined by <abs_path>..."
        # This is also sometimes called "server-relative".
        newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
    elif relative:
        # "A relative reference that does not begin with a scheme name
        # or a slash character is termed a relative-path reference."
        old = url().split('/')[:-1]
        new = newurl.split('/')
        while old and new:
            a, b = old[0], new[0]
            if a != b:
                break
            old.pop(0)
            new.pop(0)
        new = (['..'] * len(old)) + new
        newurl = '/'.join(new)

    return newurl
<|endoftext|>


<commit_before><commit_msg>add traceroute feature :bus: :rocket:<commit_after><|endoftext|>


<commit_before>import os
import commonware.log
import codecs

from django.views.static import serve
from django.http import HttpResponse, HttpResponseForbidden, HttpResponseServerError
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.cache import never_cache
from django.views.decorators.http import require_POST
from django.conf import settings

from base.shortcuts import get_object_with_related_or_404
from utils import validator
from utils.helpers import get_random_string
from xpi import xpi_utils

from jetpack import tasks
from jetpack.models import PackageRevision, SDK

log = commonware.log.getLogger('f.xpi')

@csrf_exempt
@require_POST
def prepare_test(r, id_number, revision_number=None):
    """
    Test XPI from data saved in the database
    """
    revision = get_object_with_related_or_404(PackageRevision,
                        package__id_number=id_number, package__type='a',
                        revision_number=revision_number)
    hashtag = r.POST.get('hashtag')
    if not hashtag:
        log.warning('[security] No hashtag provided')
        return HttpResponseForbidden('{"error": "No hashtag"}')
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    # prepare codes to be sent to the task
    mod_codes = {}
    att_codes = {}
    if r.POST.get('live_data_testing', False):
        for mod in revision.modules.all():
            if r.POST.get(mod.filename, False):
                code = r.POST.get(mod.filename, '')
                if mod.code != code:
                    mod_codes[str(mod.pk)] = code
        for att in revision.attachments.all():
            if r.POST.get(str(att.pk), False):
                code = r.POST.get(str(att.pk))
                att_codes[str(att.pk)] = code
    tasks.xpi_build_from_model(revision.pk,
            mod_codes=mod_codes, att_codes=att_codes, hashtag=hashtag)
    return HttpResponse('{"delayed": true}')

@never_cache
def get_test(r, hashtag):
    """
    return XPI file for testing
    """
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    path = os.path.join(settings.XPI_TARGETDIR, '%s.xpi' % hashtag)
    mimetype = 'text/plain; charset=x-user-defined'
    try:
        xpi = codecs.open(path, mode='rb').read()
    except Exception, err:
        log.debug('Add-on not yet created: %s' % str(err))
        return HttpResponse('')
    log.info('Downloading Add-on: %s' % hashtag)
    return HttpResponse(xpi, mimetype=mimetype)

@csrf_exempt
@require_POST
def prepare_download(r, id_number, revision_number=None):
    """
    Prepare download XPI.  This package is built asynchronously and we assume
    it works. It will be downloaded in ``get_download``
    """
    revision = get_object_with_related_or_404(PackageRevision,
                        package__id_number=id_number, package__type='a',
                        revision_number=revision_number)
    hashtag = r.POST.get('hashtag')
    if not hashtag:
        return HttpResponseForbidden('Add-on Builder has been updated!'
                'We have updated this part of the pplication, please '
                'empty your cache and reload to get changes.')
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    revision.build_xpi(hashtag=hashtag)
    return HttpResponse('{"delayed": true}')

@never_cache
def check_download(r, hashtag):
    """Check if XPI file is prepared."""
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    path = os.path.join(settings.XPI_TARGETDIR, '%s.xpi' % hashtag)
    # Check file if it exists
    if os.path.isfile(path):
        return HttpResponse('{"ready": true}')
    return HttpResponse('{"ready": false}')

@never_cache
def get_download(r, hashtag, filename):
    """
    Download XPI (it has to be ready)
    """
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    path = os.path.join(settings.XPI_TARGETDIR, '%s.xpi' % hashtag)
    log.info('Downloading %s.xpi from %s' % (filename, path))
    response = serve(r, path, '/', show_indexes=False)
    response['Content-Disposition'] = ('attachment; '
            'filename="%s.xpi"' % filename)
    return response

@never_cache
def clean(r, path):
    " remove whole temporary SDK on request "
    # Validate sdk_name
    if not validator.is_valid('alphanum', path):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    xpi_utils.remove(os.path.join(settings.XPI_TARGETDIR, '%s.xpi' % path))
    return HttpResponse('{"success": true}', mimetype='application/json')

@never_cache
def repackage(r, amo_id, amo_file, sdk_dir=None):
    """Pull amo_id/amo_file.xpi, schedule xpi creation, return hashtag
    """
    # validate entries
    # prepare data
    sdk = SDK.objects.get(dir=sdk_dir) if sdk_dir else SDK.objects.all()[0]
    hashtag = get_random_string(10)
    rep = xpi_utils.Repackage(amo_id, amo_file, sdk, hashtag)
    response = rep.build_xpi()
    rep.destroy()
    if response:
        return HttpResponseForbidden('{"error": "%s"}' % response)
    # extract packages
    # call build xpi task
    # respond with a hashtag which will identify downloadable xpi
    xpi_targetpath = "%s.xpi" % os.path.join(settings.XPI_TARGETDIR, hashtag)
    return HttpResponse('{"hashtag": "%s", "file": "%s"}' % (
        hashtag, xpi_targetpath))#, mimetype='application/json')
<commit_msg>download is using celery as well<commit_after>import os
import commonware.log
import codecs

from django.views.static import serve
from django.http import HttpResponse, HttpResponseForbidden, HttpResponseServerError
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.cache import never_cache
from django.views.decorators.http import require_POST
from django.conf import settings

from base.shortcuts import get_object_with_related_or_404
from utils import validator
from utils.helpers import get_random_string
from xpi import xpi_utils

from jetpack import tasks
from jetpack.models import PackageRevision, SDK

log = commonware.log.getLogger('f.xpi')

@csrf_exempt
@require_POST
def prepare_test(r, id_number, revision_number=None):
    """
    Test XPI from data saved in the database
    """
    revision = get_object_with_related_or_404(PackageRevision,
                        package__id_number=id_number, package__type='a',
                        revision_number=revision_number)
    hashtag = r.POST.get('hashtag')
    if not hashtag:
        log.warning('[security] No hashtag provided')
        return HttpResponseForbidden('{"error": "No hashtag"}')
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    # prepare codes to be sent to the task
    mod_codes = {}
    att_codes = {}
    if r.POST.get('live_data_testing', False):
        for mod in revision.modules.all():
            if r.POST.get(mod.filename, False):
                code = r.POST.get(mod.filename, '')
                if mod.code != code:
                    mod_codes[str(mod.pk)] = code
        for att in revision.attachments.all():
            if r.POST.get(str(att.pk), False):
                code = r.POST.get(str(att.pk))
                att_codes[str(att.pk)] = code
    tasks.xpi_build_from_model.delay(revision.pk,
            mod_codes=mod_codes, att_codes=att_codes, hashtag=hashtag)
    return HttpResponse('{"delayed": true}')

@never_cache
def get_test(r, hashtag):
    """
    return XPI file for testing
    """
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    path = os.path.join(settings.XPI_TARGETDIR, '%s.xpi' % hashtag)
    mimetype = 'text/plain; charset=x-user-defined'
    try:
        xpi = codecs.open(path, mode='rb').read()
    except Exception, err:
        log.debug('Add-on not yet created: %s' % str(err))
        return HttpResponse('')
    log.info('Downloading Add-on: %s' % hashtag)
    return HttpResponse(xpi, mimetype=mimetype)

@csrf_exempt
@require_POST
def prepare_download(r, id_number, revision_number=None):
    """
    Prepare download XPI.  This package is built asynchronously and we assume
    it works. It will be downloaded in ``get_download``
    """
    revision = get_object_with_related_or_404(PackageRevision,
                        package__id_number=id_number, package__type='a',
                        revision_number=revision_number)
    hashtag = r.POST.get('hashtag')
    if not hashtag:
        return HttpResponseForbidden('Add-on Builder has been updated!'
                'We have updated this part of the pplication, please '
                'empty your cache and reload to get changes.')
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    tasks.xpi_build_from_model.delay(revision.pk, hashtag=hashtag)
    return HttpResponse('{"delayed": true}')

@never_cache
def check_download(r, hashtag):
    """Check if XPI file is prepared."""
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    path = os.path.join(settings.XPI_TARGETDIR, '%s.xpi' % hashtag)
    # Check file if it exists
    if os.path.isfile(path):
        return HttpResponse('{"ready": true}')
    return HttpResponse('{"ready": false}')

@never_cache
def get_download(r, hashtag, filename):
    """
    Download XPI (it has to be ready)
    """
    if not validator.is_valid('alphanum', hashtag):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    path = os.path.join(settings.XPI_TARGETDIR, '%s.xpi' % hashtag)
    log.info('Downloading %s.xpi from %s' % (filename, path))
    response = serve(r, path, '/', show_indexes=False)
    response['Content-Disposition'] = ('attachment; '
            'filename="%s.xpi"' % filename)
    return response

@never_cache
def clean(r, path):
    " remove whole temporary SDK on request "
    # Validate sdk_name
    if not validator.is_valid('alphanum', path):
        log.warning('[security] Wrong hashtag provided')
        return HttpResponseForbidden("{'error': 'Wrong hashtag'}")
    xpi_utils.remove(os.path.join(settings.XPI_TARGETDIR, '%s.xpi' % path))
    return HttpResponse('{"success": true}', mimetype='application/json')

@never_cache
def repackage(r, amo_id, amo_file, sdk_dir=None):
    """Pull amo_id/amo_file.xpi, schedule xpi creation, return hashtag
    """
    # validate entries
    # prepare data
    sdk = SDK.objects.get(dir=sdk_dir) if sdk_dir else SDK.objects.all()[0]
    hashtag = get_random_string(10)
    rep = xpi_utils.Repackage(amo_id, amo_file, sdk, hashtag)
    response = rep.build_xpi()
    rep.destroy()
    if response:
        return HttpResponseForbidden('{"error": "%s"}' % response)
    # extract packages
    # call build xpi task
    # respond with a hashtag which will identify downloadable xpi
    xpi_targetpath = "%s.xpi" % os.path.join(settings.XPI_TARGETDIR, hashtag)
    return HttpResponse('{"hashtag": "%s", "file": "%s"}' % (
        hashtag, xpi_targetpath))#, mimetype='application/json')
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import discord
from discord import Forbidden
from discord.ext import commands
import random
from dict import DictionaryReader
from botkey import Key
from subprocess import call
import sys
from priestLogger import PriestLogger
import logging
import time
from discord import HTTPException
from discord import utils

logging.basicConfig(level=logging.INFO)

client = discord.Client()

prefix = Key().prefix()

logger = PriestLogger()

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

@client.event
async def on_message(message):
    r = DictionaryReader()
    # we do not want the bot to reply to itself
    if message.author == client.user:
        return

    if message.content.startswith(prefix):
        await messageHandler(message)

    if message.channel.name != None and message.channel.name in r.logChannels():
        logger.log(message)

@client.event
async def on_member_join(member):
    await sendWelcomeMessage(member)
    await logAction(member, 'joined')

@client.event
async def on_member_remove(member):
    print('member left')
    await logAction(member, 'left')

@client.event
async def on_member_ban(member):
    await logAction(member, 'banned')

@client.event
async def on_member_unban(member):
    await logAction(member, 'unbanned')

@client.event
async def on_member_update(before, after):
    # Checks if the Game state changed
    if before.game != after.game:
        p = DictionaryReader()
        if after.game is None:
            # Removes role if assigned
            role = utils.find(lambda r: r.name == p.streamingRole(), after.roles)
            if role is not None:
                await client.remove_roles(after, role)

        else:
            role = utils.find(lambda r: r.name == p.streamingRole(), after.server.roles)
            if role not in after.roles and after.game.type = 1:
            # Adds the role if started streaming
                await client.add_roles(after, role)

async def logAction(member, action):
    r = DictionaryReader()
    if member.server:
        await client.send_message(client.get_channel(r.actionLogChannel()), '['+time.strftime("%Y-%m-%d %H:%M:%S")+'] {0.server.name} - {0.name} ({0.id}) {1}'.format(member, action))
    else:
        await client.send_message(client.get_channel(r.actionLogChannel()), 'No Server - {0.name} ({0.id}) {1}'.format(member, action))
    #print('error while writing {0} log'.format(action))

async def messageHandler(message):
    if message.server:
        await client.send_message(client.get_channel('220534135947526154'), '{0.server.name} - {0.channel.name} - {0.author} invoked {0.content}'.format(message))
    else:
        await client.send_message(client.get_channel('220534135947526154'), 'PM - PM - {0.author} invoked {0.content}'.format(message))

    if message.content.startswith(prefix+'fullupdate') or message.content.startswith(prefix+'update') or message.content.startswith(prefix+'channel'):
        await maintenanceMessages(message)

    elif message.content.startswith(prefix+'send'):
        await forwardMessage(message)

    elif message.content.startswith(prefix+'item'):
        await itemMessage(message)

    elif message.content.startswith(prefix+'pin') or message.content.startswith(prefix+'pins'):
        await sendPinMessages(message)

    elif message.content.startswith(prefix+'channel'):
        await client.send_message(message.channel, message.channel.id)

    elif message.content.startswith(prefix+'ban'):
        await adminControl(message)

    else:
        await generalMessage(message)

async def maintenanceMessages(message):
    if message.content.startswith(prefix+'update'):
        call(["git","pull"])
    p = DictionaryReader()
    if message.content.startswith(prefix+'fullupdate'):
        if message.author.id not in p.admins():
            await client.send_message(message.channel, 'You\'re not my dad, {0.mention}!'.format(message.author))
            return
        call(["git","pull"])
        call(["start_bot.sh"])
        sys.exit()

async def forwardMessage(message):
    p = DictionaryReader()
    roles = message.author.roles
    canSend = False
    for role in roles:
        canSend = canSend or (role.name in p.roles())
    if not canSend:
        print('{0.author.name} can\'t send whispers'.format(message))
        return
    entries = message.content.split(' ')
    target = message.mentions[0]
    if target != None:
        entry = ' '.join(entries[2::])
        msg = p.commandReader(entry)
        if msg != None:
            await client.send_message(target, msg)
            await client.delete_message(message)
            await client.send_message(message.author, 'Message sent to {0.mention}'.format(target))
        else:
            await client.send_message(message.channel, 'Invalid Message, {0.mention}'.format(message.author))

async def itemMessage(message):
    p = DictionaryReader()
    msg = p.itemReader(message.content[1::])
    await client.send_message(message.channel, msg)

async def sendWelcomeMessage(member):
    p = DictionaryReader()
    msg = p.commandReader('help')
    await client.send_message(member, msg)

async def sendPinMessages(message):
    pins = await client.pins_from(message.channel)
    size = 10
    count = 0
    command = message.content.split(' ')
    try:
        await client.delete_message(message)
    except (HTTPException, Forbidden):
        print('Error deleting message, probably from whisper')
    if len(command) > 1:
        size = int(command[1]) if isinstance(command[1], int) else 10

    for msg in pins:
        if count >= size:
            return
        if msg.content:
            await client.send_message(message.author, '``` Pin '+ str(count+1) + ' ```')
            await client.send_message(message.author, msg.content)
        count += 1

async def generalMessage(message):
    p = DictionaryReader()
    try:
        roles = len(message.author.roles)
    except Exception:
        roles = 10
    command = message.content[1::].split(' ')[0].lower()
    msg = p.commandReader(message.content[1::],message.channel.name)
    if msg != None:
        if command in p.whisperCommands():
            if command == 'pub' and roles > 1 and 'help' not in message.content:
                await client.send_message(message.channel, msg)
            else:
                await client.send_message(message.author, msg)
                try:
                    await client.delete_message(message)
                except (HTTPException, Forbidden):
                    print('Error deleting message, probably from whisper')
        else:
            await client.send_message(message.channel, msg)
    else:
        msg = p.commandReader('invalid',message.channel.name)
        await client.send_message(message.author, msg)
        try:
            await client.delete_message(message)
        except (HTTPException, Forbidden):
            print('Error deleting message, probably from whisper')

async def adminControl(message):
    p = DictionaryReader()
    roles = message.author.roles
    canSend = False
    for role in roles:
        canSend = canSend or (role.name in p.roles())
    if not canSend:
        print('{0.author.name} can\'t ban members!'.format(message))
        return
    else:
        if message.content.startswith(prefix+'ban'):
            if not message.server.me.server_permissions.ban_members:
                await client.send_message(message.author, 'The bot does not have permissions to ban members.')
                return
            id = message.content.split(' ')[1]
            try:
                await client.http.ban(id, message.server.id)
                user = await client.get_user_info(id)
                if user != None:
                    await client.send_message(message.author, 'User '+user.name+' banned successfully')
                else:
                    await client.send_message(message.author, 'Invalid user ID')
            except discord.HTTPException:
                pass
            finally:
                await client.delete_message(message)

client.run(Key().value())
<commit_msg>Temporary crash fix<commit_after># -*- coding: utf-8 -*-

import discord
from discord import Forbidden
from discord.ext import commands
import random
from dict import DictionaryReader
from botkey import Key
from subprocess import call
import sys
from priestLogger import PriestLogger
import logging
import time
from discord import HTTPException
from discord import utils

logging.basicConfig(level=logging.INFO)

client = discord.Client()

prefix = Key().prefix()

logger = PriestLogger()

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

@client.event
async def on_message(message):
    r = DictionaryReader()
    # we do not want the bot to reply to itself
    if message.author == client.user:
        return

    if message.content.startswith(prefix):
        await messageHandler(message)

    if message.channel.name != None and message.channel.name in r.logChannels():
        logger.log(message)

@client.event
async def on_member_join(member):
    await sendWelcomeMessage(member)
    await logAction(member, 'joined')

@client.event
async def on_member_remove(member):
    print('member left')
    await logAction(member, 'left')

@client.event
async def on_member_ban(member):
    await logAction(member, 'banned')

@client.event
async def on_member_unban(member):
    await logAction(member, 'unbanned')

@client.event
async def on_member_update(before, after):
    return
    # Checks if the Game state changed
    if before.game != after.game:
        p = DictionaryReader()
        if after.game is None:
            # Removes role if assigned
            role = utils.find(lambda r: r.name == p.streamingRole(), after.roles)
            if role is not None:
                await client.remove_roles(after, role)

        else:
            role = utils.find(lambda r: r.name == p.streamingRole(), after.server.roles)
            if role not in after.roles and after.game.type == 1:
            # Adds the role if started streaming
                await client.add_roles(after, role)

async def logAction(member, action):
    r = DictionaryReader()
    if member.server:
        await client.send_message(client.get_channel(r.actionLogChannel()), '['+time.strftime("%Y-%m-%d %H:%M:%S")+'] {0.server.name} - {0.name} ({0.id}) {1}'.format(member, action))
    else:
        await client.send_message(client.get_channel(r.actionLogChannel()), 'No Server - {0.name} ({0.id}) {1}'.format(member, action))
    #print('error while writing {0} log'.format(action))

async def messageHandler(message):
    if message.server:
        await client.send_message(client.get_channel('220534135947526154'), '{0.server.name} - {0.channel.name} - {0.author} invoked {0.content}'.format(message))
    else:
        await client.send_message(client.get_channel('220534135947526154'), 'PM - PM - {0.author} invoked {0.content}'.format(message))

    if message.content.startswith(prefix+'fullupdate') or message.content.startswith(prefix+'update') or message.content.startswith(prefix+'channel'):
        await maintenanceMessages(message)

    elif message.content.startswith(prefix+'send'):
        await forwardMessage(message)

    elif message.content.startswith(prefix+'item'):
        await itemMessage(message)

    elif message.content.startswith(prefix+'pin') or message.content.startswith(prefix+'pins'):
        await sendPinMessages(message)

    elif message.content.startswith(prefix+'channel'):
        await client.send_message(message.channel, message.channel.id)

    elif message.content.startswith(prefix+'ban'):
        await adminControl(message)

    else:
        await generalMessage(message)

async def maintenanceMessages(message):
    if message.content.startswith(prefix+'update'):
        call(["git","pull"])
    p = DictionaryReader()
    if message.content.startswith(prefix+'fullupdate'):
        if message.author.id not in p.admins():
            await client.send_message(message.channel, 'You\'re not my dad, {0.mention}!'.format(message.author))
            return
        call(["git","pull"])
        call(["start_bot.sh"])
        sys.exit()

async def forwardMessage(message):
    p = DictionaryReader()
    roles = message.author.roles
    canSend = False
    for role in roles:
        canSend = canSend or (role.name in p.roles())
    if not canSend:
        print('{0.author.name} can\'t send whispers'.format(message))
        return
    entries = message.content.split(' ')
    target = message.mentions[0]
    if target != None:
        entry = ' '.join(entries[2::])
        msg = p.commandReader(entry)
        if msg != None:
            await client.send_message(target, msg)
            await client.delete_message(message)
            await client.send_message(message.author, 'Message sent to {0.mention}'.format(target))
        else:
            await client.send_message(message.channel, 'Invalid Message, {0.mention}'.format(message.author))

async def itemMessage(message):
    p = DictionaryReader()
    msg = p.itemReader(message.content[1::])
    await client.send_message(message.channel, msg)

async def sendWelcomeMessage(member):
    p = DictionaryReader()
    msg = p.commandReader('help')
    await client.send_message(member, msg)

async def sendPinMessages(message):
    pins = await client.pins_from(message.channel)
    size = 10
    count = 0
    command = message.content.split(' ')
    try:
        await client.delete_message(message)
    except (HTTPException, Forbidden):
        print('Error deleting message, probably from whisper')
    if len(command) > 1:
        size = int(command[1]) if isinstance(command[1], int) else 10

    for msg in pins:
        if count >= size:
            return
        if msg.content:
            await client.send_message(message.author, '``` Pin '+ str(count+1) + ' ```')
            await client.send_message(message.author, msg.content)
        count += 1

async def generalMessage(message):
    p = DictionaryReader()
    try:
        roles = len(message.author.roles)
    except Exception:
        roles = 10
    command = message.content[1::].split(' ')[0].lower()
    msg = p.commandReader(message.content[1::],message.channel.name)
    if msg != None:
        if command in p.whisperCommands():
            if command == 'pub' and roles > 1 and 'help' not in message.content:
                await client.send_message(message.channel, msg)
            else:
                await client.send_message(message.author, msg)
                try:
                    await client.delete_message(message)
                except (HTTPException, Forbidden):
                    print('Error deleting message, probably from whisper')
        else:
            await client.send_message(message.channel, msg)
    else:
        msg = p.commandReader('invalid',message.channel.name)
        await client.send_message(message.author, msg)
        try:
            await client.delete_message(message)
        except (HTTPException, Forbidden):
            print('Error deleting message, probably from whisper')

async def adminControl(message):
    p = DictionaryReader()
    roles = message.author.roles
    canSend = False
    for role in roles:
        canSend = canSend or (role.name in p.roles())
    if not canSend:
        print('{0.author.name} can\'t ban members!'.format(message))
        return
    else:
        if message.content.startswith(prefix+'ban'):
            if not message.server.me.server_permissions.ban_members:
                await client.send_message(message.author, 'The bot does not have permissions to ban members.')
                return
            id = message.content.split(' ')[1]
            try:
                await client.http.ban(id, message.server.id)
                user = await client.get_user_info(id)
                if user != None:
                    await client.send_message(message.author, 'User '+user.name+' banned successfully')
                else:
                    await client.send_message(message.author, 'Invalid user ID')
            except discord.HTTPException:
                pass
            finally:
                await client.delete_message(message)

client.run(Key().value())
<|endoftext|>


<commit_before>#!/usr/bin/env python
"""
Functions to launch and manage KyotoTycoon servers.
"""

import os
import random
import socket
import shutil
import signal
from zipfile import ZipFile
from time import sleep
from multiprocessing import Process

from toil.lib.bioio import logger
from cactus.shared.common import cactus_call

# For some reason ktserver believes there are only 32768 TCP ports.
MAX_KTSERVER_PORT = 32767

def runKtserver(dbElem, fileStore, existingSnapshotID=None, snapshotExportID=None):
    """
    Run a KTServer. This function launches a separate python process that manages the server.

    Writing to the special key "TERMINATE" signals this thread to safely shut
    down the DB and save the results. After finishing, the data will
    eventually be written to snapshotFile.

    Returns a tuple containing an updated version of the database config dbElem and the
    path to the log file.
    """
    logPath = fileStore.getLocalTempFile()
    dbElem.setDbHost(getHostName())

    # Find a suitable port to run on.
    try:
        occupiedPorts = findOccupiedPorts()
        unoccupiedPorts = set(xrange(1025,MAX_KTSERVER_PORT)) - occupiedPorts
        port = random.choice(list(unoccupiedPorts))
    except:
        logger.warning("Can't find which ports are occupied--likely netstat is not installed."
                       " Choosing a random port to start the DB on, good luck!")
        port = random.randint(1025,MAX_KTSERVER_PORT)
    dbElem.setDbPort(port)

    process = Process(target=serverProcess,
                      args=(dbElem, logPath, fileStore, existingSnapshotID, snapshotExportID))
    process.daemon = True
    process.start()

    if not blockUntilKtserverIsRunning(logPath):
        raise RuntimeError("Unable to launch ktserver in time.")

    return dbElem, logPath

def serverProcess(dbElem, logPath, fileStore, existingSnapshotID=None, snapshotExportID=None):
    """Independent process that babysits the ktserver process.

    Waits for the TERMINATE flag to be set, then kills the DB and
    copies the final snapshot to snapshotExportID.
    """
    snapshotDir = os.path.join(fileStore.getLocalTempDir(), 'snapshot')
    os.mkdir(snapshotDir)
    if existingSnapshotID is not None:
        # Extract the existing snapshot (a zip file) to the snapshot
        # directory so it will be automatically loaded
        snapshot = fileStore.readGlobalFile(existingSnapshotID)
        zip = ZipFile(snapshot)
        zip.extractall(snapshotDir)
        zip.close()
    process = cactus_call(server=True, shell=False,
                          parameters=__getKtserverCommand(dbElem, logPath, snapshotDir))

    blockUntilKtserverIsRunning(logPath)
    if existingSnapshotID is not None:
        # Clear the termination flag from the snapshot
        cactus_call(parameters=["ktremotemgr", "remove"] + getRemoteParams(dbElem) + ["TERMINATE"])

    while True:
        try:
            cactus_call(parameters=["ktremotemgr", "get"] + getRemoteParams(dbElem) + ["TERMINATE"])
        except:
            # No terminate signal sent yet
            pass
        else:
            # Terminate signal received
            break
        sleep(60)
    process.send_signal(signal.SIGINT)
    process.wait()
    blockUntilKtserverIsFinished(logPath)
    if snapshotExportID is not None:
        # Export the snapshot file(s) to the file store
        zipBasePath = fileStore.getLocalTempFile()
        zipPath = shutil.make_archive(zipBasePath, 'zip', root_dir=snapshotDir)
        fileStore.jobStore.updateFile(snapshotExportID, zipPath)

def blockUntilKtserverIsRunning(logPath, createTimeout=1800):
    """Check status until it's successful, an error is found, or we timeout.

    Returns True if the ktserver is now running, False if something went wrong."""
    success = False
    for i in xrange(createTimeout):
        if __isKtServerFailed(logPath):
            logger.critical('Error starting ktserver.')
            success = False
            break
        if __isKtServerRunning(logPath):
            logger.info('Ktserver running.')
            success = True
            break
        sleep(1)
    return success

def blockUntilKtserverIsFinished(logPath, timeout=1800,
                                 timeStep=10):
    """Wait for the ktserver log to indicate that it shut down properly.

    Returns True if the server shut down, False if the timeout expired."""
    for i in xrange(0, timeout, timeStep):
        with open(logPath) as f:
            log = f.read()
            if '[FINISH]' in log:
                return True
        sleep(timeStep)
    raise RuntimeError("Timeout reached while waiting for ktserver" %
                       logPath)

def __isKtServerRunning(logPath):
    """Check if the server started running."""
    success = False
    with open(logPath) as f:
        for line in f:
            if line.lower().find("listening") >= 0:
                success = True
    return success

def __isKtServerFailed(logPath):
    """Does the server log contain an error?"""
    isFailed = False
    with open(logPath) as f:
        for line in f:
            if line.lower().find("error") >= 0:
                isFailed = True
                break
    return isFailed

def __getKtTuningOptions(dbElem):
    """Get the appropriate KTServer tuning parameters (bucket size, etc.)"""
    # these are some hardcoded defaults.  should think about moving to config
    tuningOptions = "#opts=ls#bnum=30m#msiz=50g#ktopts=p"
    # override default ktserver settings if they are present in the
    # experiment xml file.
    if dbElem.getDbTuningOptions() is not None:
        tuningOptions = dbElem.getDbTuningOptions()
    if dbElem.getDbCreateTuningOptions() is not None:
        tuningOptions = dbElem.getDbCreateTuningOptions()
    return tuningOptions

def __getKtServerOptions(dbElem):
    # these are some hardcoded defaults.  should think about moving to config
    serverOptions = "-ls -tout 200000 -th 64"
    if dbElem.getDbServerOptions() is not None:
        serverOptions = dbElem.getDbServerOptions()
    return serverOptions

def __getKtserverCommand(dbElem, logPath, snapshotDir):
    """Get a ktserver command line with the proper options (in popen-type list format)."""
    serverOptions = __getKtServerOptions(dbElem)
    tuning = __getKtTuningOptions(dbElem)
    cmd = ["ktserver", "-host", dbElem.getDbHost(), "-port", dbElem.getDbPort()]
    cmd += serverOptions.split()
    cmd += ["-bgs", snapshotDir, "-bgsc", "lzo"]
    cmd += ["-log", logPath]
    cmd += [":" + tuning]
    return cmd

def getRemoteParams(dbElem):
    """Get parameters to supply to ktremotemgr to connect to the right DB."""
    return ['-port', str(dbElem.getDbPort()),
            '-host', dbElem.getDbHost() or 'localhost']

def stopKtserver(dbElem):
    """Attempt to send the terminate signal to a ktserver."""
    try:
        cactus_call(parameters=['ktremotemgr', 'set'] + getRemoteParams(dbElem) + ['TERMINATE', '1'])
    except:
        # The server is likely already down.
        pass

###############################################################################
# Hostnames of swarm nodes (ex kkr18u57.local) are not visible from
# other swarm nodes (dropping the .local does work).  So we try to
# work with ip addresses whenever possible. Current recipe (to review):
# if IP address does not start with 127. return IP address
# otherwise return primary hostname
###############################################################################
def getHostName():
    hostName = socket.gethostname()
    hostIp = socket.gethostbyname(hostName)
    if hostIp.find("127.") != 0:
        return hostIp
    return hostName

def findOccupiedPorts():
    """Attempt to find all currently taken TCP ports.

    Returns a set of ints, representing taken ports."""
    netstatOutput = cactus_call(parameters=["netstat", "-tuplen"], check_output=True)
    ports = set()
    for line in netstatOutput.split("\n"):
        fields = line.split()
        if len(fields) != 9:
            # Header or other garbage line
            continue
        port = int(fields[3].split(':')[-1])
        ports.add(port)
    logger.debug('Detected ports in use: %s' % repr(ports))
    return ports
<commit_msg>Use tgz instead of zip for DB snapshots<commit_after>#!/usr/bin/env python
"""
Functions to launch and manage KyotoTycoon servers.
"""

import os
import random
import socket
import shutil
import signal
import tarfile
from time import sleep
from multiprocessing import Process

from toil.lib.bioio import logger
from cactus.shared.common import cactus_call

# For some reason ktserver believes there are only 32768 TCP ports.
MAX_KTSERVER_PORT = 32767

def runKtserver(dbElem, fileStore, existingSnapshotID=None, snapshotExportID=None):
    """
    Run a KTServer. This function launches a separate python process that manages the server.

    Writing to the special key "TERMINATE" signals this thread to safely shut
    down the DB and save the results. After finishing, the data will
    eventually be written to snapshotFile.

    Returns a tuple containing an updated version of the database config dbElem and the
    path to the log file.
    """
    logPath = fileStore.getLocalTempFile()
    dbElem.setDbHost(getHostName())

    # Find a suitable port to run on.
    try:
        occupiedPorts = findOccupiedPorts()
        unoccupiedPorts = set(xrange(1025,MAX_KTSERVER_PORT)) - occupiedPorts
        port = random.choice(list(unoccupiedPorts))
    except:
        logger.warning("Can't find which ports are occupied--likely netstat is not installed."
                       " Choosing a random port to start the DB on, good luck!")
        port = random.randint(1025,MAX_KTSERVER_PORT)
    dbElem.setDbPort(port)

    process = Process(target=serverProcess,
                      args=(dbElem, logPath, fileStore, existingSnapshotID, snapshotExportID))
    process.daemon = True
    process.start()

    if not blockUntilKtserverIsRunning(logPath):
        raise RuntimeError("Unable to launch ktserver in time.")

    return dbElem, logPath

def serverProcess(dbElem, logPath, fileStore, existingSnapshotID=None, snapshotExportID=None):
    """Independent process that babysits the ktserver process.

    Waits for the TERMINATE flag to be set, then kills the DB and
    copies the final snapshot to snapshotExportID.
    """
    snapshotDir = os.path.join(fileStore.getLocalTempDir(), 'snapshot')
    os.mkdir(snapshotDir)
    if existingSnapshotID is not None:
        # Extract the existing snapshot (a zip file) to the snapshot
        # directory so it will be automatically loaded
        snapshot = fileStore.readGlobalFile(existingSnapshotID)
        with tarfile.open(snapshot) as tar:
            tar.extractall(snapshotDir)
    process = cactus_call(server=True, shell=False,
                          parameters=__getKtserverCommand(dbElem, logPath, snapshotDir))

    blockUntilKtserverIsRunning(logPath)
    if existingSnapshotID is not None:
        # Clear the termination flag from the snapshot
        cactus_call(parameters=["ktremotemgr", "remove"] + getRemoteParams(dbElem) + ["TERMINATE"])

    while True:
        try:
            cactus_call(parameters=["ktremotemgr", "get"] + getRemoteParams(dbElem) + ["TERMINATE"])
        except:
            # No terminate signal sent yet
            pass
        else:
            # Terminate signal received
            break
        sleep(60)
    process.send_signal(signal.SIGINT)
    process.wait()
    blockUntilKtserverIsFinished(logPath)
    if snapshotExportID is not None:
        # Export the snapshot file(s) to the file store
        tarBasePath = fileStore.getLocalTempFile()
        tarPath = shutil.make_archive(tarBasePath, 'gztar', root_dir=snapshotDir)
        fileStore.jobStore.updateFile(snapshotExportID, tarPath)

def blockUntilKtserverIsRunning(logPath, createTimeout=1800):
    """Check status until it's successful, an error is found, or we timeout.

    Returns True if the ktserver is now running, False if something went wrong."""
    success = False
    for i in xrange(createTimeout):
        if __isKtServerFailed(logPath):
            logger.critical('Error starting ktserver.')
            success = False
            break
        if __isKtServerRunning(logPath):
            logger.info('Ktserver running.')
            success = True
            break
        sleep(1)
    return success

def blockUntilKtserverIsFinished(logPath, timeout=1800,
                                 timeStep=10):
    """Wait for the ktserver log to indicate that it shut down properly.

    Returns True if the server shut down, False if the timeout expired."""
    for i in xrange(0, timeout, timeStep):
        with open(logPath) as f:
            log = f.read()
            if '[FINISH]' in log:
                return True
        sleep(timeStep)
    raise RuntimeError("Timeout reached while waiting for ktserver" %
                       logPath)

def __isKtServerRunning(logPath):
    """Check if the server started running."""
    success = False
    with open(logPath) as f:
        for line in f:
            if line.lower().find("listening") >= 0:
                success = True
    return success

def __isKtServerFailed(logPath):
    """Does the server log contain an error?"""
    isFailed = False
    with open(logPath) as f:
        for line in f:
            if line.lower().find("error") >= 0:
                isFailed = True
                break
    return isFailed

def __getKtTuningOptions(dbElem):
    """Get the appropriate KTServer tuning parameters (bucket size, etc.)"""
    # these are some hardcoded defaults.  should think about moving to config
    tuningOptions = "#opts=ls#bnum=30m#msiz=50g#ktopts=p"
    # override default ktserver settings if they are present in the
    # experiment xml file.
    if dbElem.getDbTuningOptions() is not None:
        tuningOptions = dbElem.getDbTuningOptions()
    if dbElem.getDbCreateTuningOptions() is not None:
        tuningOptions = dbElem.getDbCreateTuningOptions()
    return tuningOptions

def __getKtServerOptions(dbElem):
    # these are some hardcoded defaults.  should think about moving to config
    serverOptions = "-ls -tout 200000 -th 64"
    if dbElem.getDbServerOptions() is not None:
        serverOptions = dbElem.getDbServerOptions()
    return serverOptions

def __getKtserverCommand(dbElem, logPath, snapshotDir):
    """Get a ktserver command line with the proper options (in popen-type list format)."""
    serverOptions = __getKtServerOptions(dbElem)
    tuning = __getKtTuningOptions(dbElem)
    cmd = ["ktserver", "-host", dbElem.getDbHost(), "-port", dbElem.getDbPort()]
    cmd += serverOptions.split()
    cmd += ["-bgs", snapshotDir, "-bgsc", "lzo"]
    cmd += ["-log", logPath]
    cmd += [":" + tuning]
    return cmd

def getRemoteParams(dbElem):
    """Get parameters to supply to ktremotemgr to connect to the right DB."""
    return ['-port', str(dbElem.getDbPort()),
            '-host', dbElem.getDbHost() or 'localhost']

def stopKtserver(dbElem):
    """Attempt to send the terminate signal to a ktserver."""
    try:
        cactus_call(parameters=['ktremotemgr', 'set'] + getRemoteParams(dbElem) + ['TERMINATE', '1'])
    except:
        # The server is likely already down.
        pass

###############################################################################
# Hostnames of swarm nodes (ex kkr18u57.local) are not visible from
# other swarm nodes (dropping the .local does work).  So we try to
# work with ip addresses whenever possible. Current recipe (to review):
# if IP address does not start with 127. return IP address
# otherwise return primary hostname
###############################################################################
def getHostName():
    hostName = socket.gethostname()
    hostIp = socket.gethostbyname(hostName)
    if hostIp.find("127.") != 0:
        return hostIp
    return hostName

def findOccupiedPorts():
    """Attempt to find all currently taken TCP ports.

    Returns a set of ints, representing taken ports."""
    netstatOutput = cactus_call(parameters=["netstat", "-tuplen"], check_output=True)
    ports = set()
    for line in netstatOutput.split("\n"):
        fields = line.split()
        if len(fields) != 9:
            # Header or other garbage line
            continue
        port = int(fields[3].split(':')[-1])
        ports.add(port)
    logger.debug('Detected ports in use: %s' % repr(ports))
    return ports
<|endoftext|>


<commit_before>import json
from multiprocessing import Queue

import pytest

from netdumplings import DumplingChef, DumplingDriver, DumplingKitchen

class ChefForTests(DumplingChef):
    pass

@pytest.fixture
def mock_kitchen(mocker):
    kitchen = mocker.Mock()
    kitchen.name = 'TestKitchen'
    kitchen.interface = 'en0'
    kitchen.filter = 'tcp'
    kitchen.chef_poke_interval = 5

    return kitchen

@pytest.fixture
def mock_packet(mocker):
    packet = mocker.Mock()
    packet.summary.return_value = 'packet summary string'

    return packet

class TestDumplingChef:
    """
    Test the DumplingChef class.
    """
    def test_init_default(self):
        """
        Test default DumplingChef initialization.
        """
        chef = DumplingChef()

        assert chef.kitchen is None
        assert chef.dumpling_queue is None
        assert chef.receive_pokes is False
        assert chef.name == 'DumplingChef'
        assert chef.dumplings_sent_count == 0

    def test_subclass_chef_name(self):
        """
        Test the setting of the name for a subclass of DumplingChef. The name
        should match the class name.
        """
        chef = ChefForTests()
        assert chef.name == 'ChefForTests'

    def test_kitchen_registration_with_pokes(self, mock_kitchen):
        """
        Test registering a chef with a kitchen with interval pokes enabled.
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=True)

        mock_kitchen.register_handler.assert_called_once_with(
            chef_name='ChefForTests',
            packet_handler=chef.packet_handler,
            interval_handler=chef.interval_handler,
        )

    def test_kitchen_registration_without_pokes(self, mock_kitchen):
        """
        Test registering a chef with a kitchen with interval pokes disabled.
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=False)

        mock_kitchen.register_handler.assert_called_once_with(
            chef_name='ChefForTests',
            packet_handler=chef.packet_handler,
            interval_handler=False,
        )

    def test_default_packet_handler(self, mocker, mock_kitchen, mock_packet):
        """
        Test the default packet handler. It should send a dumpling with a
        string payload matching "<ChefName>: <packet summary string>".
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=False)
        mocker.patch.object(chef, 'send_packet_dumpling')

        expected_payload = 'ChefForTests: packet summary string'
        chef.packet_handler(mock_packet)
        chef.send_packet_dumpling.assert_called_with(expected_payload)

    def test_send_interval_dumpling(self, mocker, mock_kitchen):
        """
        Test sending an interval dumpling. It should invoke _send_dumpling
        with the interval dumpling driver.
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=True)
        mocker.patch.object(chef, '_send_dumpling')

        test_payload = {'one': 1, 'two': 2}
        chef.send_interval_dumpling(test_payload)

        chef._send_dumpling.assert_called_with(
            test_payload, driver=DumplingDriver.interval
        )

    def test_send_packet_dumpling(self, mocker, mock_kitchen):
        """
        Test sending an interval dumpling. It should invoke _send_dumpling
        with the packet dumpling driver.
        """
        chef = ChefForTests(kitchen=mock_kitchen, receive_pokes=True)
        mocker.patch.object(chef, '_send_dumpling')

        test_payload = {'one': 1, 'two': 2}
        chef.send_packet_dumpling(test_payload)

        chef._send_dumpling.assert_called_once_with(
            test_payload, driver=DumplingDriver.packet
        )

    def test_dumpling_send(self, mocker, mock_kitchen):
        """
        Test the _send_dumpling method to ensure that it puts the dumpling
        contents onto the queue.
        """
        test_payload = {
            'one': 1,
            'two': 2,
        }
        test_payload_json = json.dumps(test_payload)

        mock_queue = mocker.Mock()
        chef = ChefForTests(
            kitchen=mock_kitchen,
            dumpling_queue=mock_queue,
            receive_pokes=True,
        )

        mocker.patch(
            'netdumplings.dumplingchef.Dumpling.__call__',
            return_value=test_payload_json,
        )

        # Ensure that _send_dumpling() puts the return value of the
        # dumpling call (which is the same as Dumpling.make()) onto the queue.
        chef._send_dumpling(test_payload, driver=DumplingDriver.packet)
        mock_queue.put.assert_called_once_with(test_payload_json)

    def test_repr(self):
        """
        Test the string representation.
        """
        chef = DumplingChef()
        assert repr(chef) == (
            'DumplingChef('
            'kitchen=None, '
            'dumpling_queue=None, '
            'receive_pokes=False)'
        )

        kitchen = DumplingKitchen()
        queue = Queue()

        chef = DumplingChef(
            kitchen=kitchen,
            dumpling_queue=queue,
            receive_pokes=True,
        )

        assert repr(chef) == (
            'DumplingChef('
            'kitchen={}, '
            'dumpling_queue={}, '
            'receive_pokes=True)'.format(repr(kitchen), repr(queue))
        )
<commit_msg>Update DumplingChef tests with new dumpling-return approach<commit_after>import pytest

from netdumplings import DumplingChef, DumplingKitchen

class ChefForTests(DumplingChef):
    pass

@pytest.fixture
def mock_kitchen(mocker):
    kitchen = mocker.Mock()
    kitchen.name = 'TestKitchen'
    kitchen.interface = 'en0'
    kitchen.filter = 'tcp'
    kitchen.chef_poke_interval = 5

    return kitchen

@pytest.fixture
def mock_packet(mocker):
    packet = mocker.Mock()
    packet.summary.return_value = 'packet summary string'

    return packet

class TestDumplingChef:
    """
    Test the DumplingChef class.
    """
    def test_init_default(self):
        """
        Test default DumplingChef initialization.
        """
        chef = DumplingChef()

        assert chef.kitchen is None
        assert chef.name == 'DumplingChef'
        assert chef.dumplings_sent_count == 0

    def test_subclass_chef_name(self):
        """
        Test the setting of the name for a subclass of DumplingChef. The name
        should match the class name.
        """
        chef = ChefForTests()
        assert chef.name == 'ChefForTests'

    def test_kitchen_registration(self, mock_kitchen):
        """
        Test registering the chef with the given kitchen.
        """
        chef = ChefForTests(kitchen=mock_kitchen)

        mock_kitchen.register_chef.assert_called_once_with(chef)

    def test_default_packet_handler(self, mock_kitchen, mock_packet, mocker):
        """
        Test the default packet handler. It should return a dumpling with a
        string payload matching "<ChefName>: <packet summary string>".
        """
        chef = ChefForTests(kitchen=mock_kitchen)
        mock_logger = mocker.patch.object(chef, '_logger')

        dumpling = chef.packet_handler(mock_packet)
        assert dumpling == 'ChefForTests: packet summary string'
        assert mock_logger.debug.call_count >= 1

    def test_default_interval_handler(self, mock_kitchen, mocker):
        """
        Test the default interval handler. It should return None (no dumpling).
        """
        chef = ChefForTests(kitchen=mock_kitchen)
        mock_logger = mocker.patch.object(chef, '_logger')

        dumpling = chef.interval_handler(interval=5)
        assert dumpling is None
        assert mock_logger.debug.call_count >= 1

    def test_repr(self):
        """
        Test the string representation.
        """
        chef = DumplingChef()
        assert repr(chef) == 'DumplingChef(kitchen=None)'

        kitchen = DumplingKitchen()
        chef = DumplingChef(kitchen=kitchen)

        assert repr(chef) == 'DumplingChef(kitchen={})'.format(repr(kitchen))
<|endoftext|>


<commit_before>"Smarkets API package"
# Copyright (C) 2011 Smarkets Limited <support@smarkets.com>
#
# This module is released under the MIT License:
# http://www.opensource.org/licenses/mit-license.php
import inspect
import os

if 'READTHEDOCS' in os.environ:
    from mock import Mock
    import sys

    eto = seto = \
        sys.modules['smarkets.eto'] = sys.modules['smarkets.eto.piqi_pb2'] = \
        sys.modules['smarkets.seto'] = sys.modules['smarkets.seto.piqi_pb2'] = Mock()

from smarkets.clients import Smarkets
from smarkets.events import (
    EventsRequest,
    Politics,
    CurrentAffairs,
    TvAndEntertainment,
    SportByDate,
    FootballByDate,
    HorseRacingByDate,
    TennisByDate,
    BasketballByDate,
    AmericanFootballByDate,
    BaseballByDate,
    CricketByDate,
    HandballByDate,
    RugbyByDate,
    RugbyLeagueByDate,
    VolleyballByDate,
    SportOther,
)
from smarkets.exceptions import (
    Error,
    ConnectionError,
    DecodeError,
    ParseError,
    SocketDisconnected,
    InvalidCallbackError,
)
from smarkets.orders import OrderCreate
from smarkets.sessions import Session, SessionSettings

__version__ = '0.4.2'

__all__ = sorted(name for name, obj in locals().items()
                 if not (name.startswith('_') or inspect.ismodule(obj)))

VERSION = tuple((int(x) for x in __version__.split('.')))
<commit_msg>Increase version number.<commit_after>"Smarkets API package"
# Copyright (C) 2011 Smarkets Limited <support@smarkets.com>
#
# This module is released under the MIT License:
# http://www.opensource.org/licenses/mit-license.php
import inspect
import os

if 'READTHEDOCS' in os.environ:
    from mock import Mock
    import sys

    eto = seto = \
        sys.modules['smarkets.eto'] = sys.modules['smarkets.eto.piqi_pb2'] = \
        sys.modules['smarkets.seto'] = sys.modules['smarkets.seto.piqi_pb2'] = Mock()

from smarkets.clients import Smarkets
from smarkets.events import (
    EventsRequest,
    Politics,
    CurrentAffairs,
    TvAndEntertainment,
    SportByDate,
    FootballByDate,
    HorseRacingByDate,
    TennisByDate,
    BasketballByDate,
    AmericanFootballByDate,
    BaseballByDate,
    CricketByDate,
    HandballByDate,
    RugbyByDate,
    RugbyLeagueByDate,
    VolleyballByDate,
    SportOther,
)
from smarkets.exceptions import (
    Error,
    ConnectionError,
    DecodeError,
    ParseError,
    SocketDisconnected,
    InvalidCallbackError,
)
from smarkets.orders import OrderCreate
from smarkets.sessions import Session, SessionSettings

__version__ = '0.4.3'

__all__ = sorted(name for name, obj in locals().items()
                 if not (name.startswith('_') or inspect.ismodule(obj)))

VERSION = tuple((int(x) for x in __version__.split('.')))
<|endoftext|>


<commit_before>"""Aqara Client"""

import json
import logging
import socket
import struct

from .const import (MCAST_ADDR, MCAST_PORT, GATEWAY_PORT)

_LOGGER = logging.getLogger(__name__)

class AqaraProtocol(object):
    """Base aqara client protocol."""

    def __init__(self):
        self.transport = None

    def connection_made(self, transport):
        """Implementation when connection is made."""
        self.transport = transport
        self._add_membership()

    def datagram_received(self, data, addr):
        """Implementation when datagram is received."""
        self.handle_message(data, addr)

    def error_received(self, exc):
        """Implentation when error is received."""
        _LOGGER.error('error_received: %s', exc)

    def handle_message(self, data, addr):
        """Callback to handle new messages, override to add implementation."""
        _LOGGER.debug('handle_message from %s: %s', addr, json.dumps(data))

    def broadcast(self, msg):
        """Send a message to the Aqara multicast channel."""
        self._send(msg, (MCAST_ADDR, MCAST_PORT))

    def unicast(self, addr, msg):
        """Send a message to a specific gateway at <ip>"""
        self._send(msg, (addr, GATEWAY_PORT))

    def _send(self, msg, dest):
        """private: send a message as UDP packet."""
        data = json.dumps(msg).encode('utf-8')
        self.transport.sendto(data, dest)

    def _add_membership(self):
        """private: add multicast membership"""
        _LOGGER.debug("Joining multicast group...")
        sock = self.transport.get_extra_info("socket")
        group = socket.inet_aton(MCAST_ADDR)
        mreq = struct.pack("4sL", group, socket.INADDR_ANY)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
        _LOGGER.debug("Multicast membership added")
<commit_msg>updated module name<commit_after>"""Aqara Protocol"""

import json
import logging
import socket
import struct

from .const import (MCAST_ADDR, MCAST_PORT, GATEWAY_PORT)

_LOGGER = logging.getLogger(__name__)

class AqaraProtocol(object):
    """Base aqara client protocol."""

    def __init__(self):
        self.transport = None

    def connection_made(self, transport):
        """Implementation when connection is made."""
        self.transport = transport
        self._add_membership()

    def datagram_received(self, data, addr):
        """Implementation when datagram is received."""
        self.handle_message(data, addr)

    def error_received(self, exc):
        """Implentation when error is received."""
        _LOGGER.error('error_received: %s', exc)

    def handle_message(self, data, addr):
        """Callback to handle new messages, override to add implementation."""
        _LOGGER.debug('handle_message from %s: %s', addr, json.dumps(data))

    def broadcast(self, msg):
        """Send a message to the Aqara multicast channel."""
        self._send(msg, (MCAST_ADDR, MCAST_PORT))

    def unicast(self, addr, msg):
        """Send a message to a specific gateway at <ip>"""
        self._send(msg, (addr, GATEWAY_PORT))

    def _send(self, msg, dest):
        """private: send a message as UDP packet."""
        data = json.dumps(msg).encode('utf-8')
        self.transport.sendto(data, dest)

    def _add_membership(self):
        """private: add multicast membership"""
        _LOGGER.debug("Joining multicast group...")
        sock = self.transport.get_extra_info("socket")
        group = socket.inet_aton(MCAST_ADDR)
        mreq = struct.pack("4sL", group, socket.INADDR_ANY)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
        _LOGGER.debug("Multicast membership added")
<|endoftext|>


<commit_before><commit_msg>some more error checking<commit_after><|endoftext|>


<commit_before># -*- coding: utf-8 -*-

# Copyright 2012 splinter authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

try:
    import unittest2 as unittest
except ImportError:
    import unittest

from splinter.element_list import ElementList
from splinter.exceptions import ElementDoesNotExist

class Person(object):
    """Very simple class, just for tests"""

    def __init__(self):
        self.current_action = None

    def walk(self):
        self.current_action = "walking"

class ElementListTest(unittest.TestCase):

    def test_method_that_verifies_if_the_list_is_empty(self):
        "should verify if the list is empty"
        the_list = ElementList([1, 2, 3])
        self.assertFalse(the_list.is_empty())
        self.assertTrue(ElementList([]).is_empty())

    def test_property_first_and_last(self):
        "should provide a \"first\" and a \"last\" properties which returns the first and last element"
        the_list = ElementList([1, 2, 3])
        self.assertEqual(the_list[0], the_list.first)
        self.assertEqual(the_list[2], the_list.last)

    def test_call_method_on_first_element(self):
        "when some method is missing on ElementList and is present in element, it should be passed"
        the_list = ElementList([Person(), Person(), Person()])
        the_list.walk()
        the_person = the_list.first
        self.assertEqual("walking", the_person.current_action)

    def test_raise_exception_on_indexerror(self):
        "should raise ElementDoesNotExist exception on IndexError"
        with self.assertRaises(ElementDoesNotExist):
            ElementList([]).first

    def test_raise_attribute_error(self):
        "should raise AttributeError when trying to access a non-existent method on list and element"
        with self.assertRaises(AttributeError):
            the_list = ElementList([Person(), Person()])
            the_list.talk()

    def test_attribute_error_for_empty(self):
        "should raise AttributeError when the list is empty and someone tries to access a method or property on it"
        with self.assertRaises(AttributeError):
            the_list = ElementList([])
            the_list.unknown_method()

    def test_attribute_error_content(self):
        "should raise AttributeError with right content"
        with self.assertRaises(AttributeError) as cm:
            the_list = ElementList([Person(), Person()])
            the_list.talk()

        expected_message = "'ElementList' object has no attribute 'talk'"
        e = cm.exception
        self.assertEqual(expected_message, e.args[0])

    def test_not_found_exception_with_query_and_method(self):
        "should receive the find method and the query and use them in exception"
        with self.assertRaises(ElementDoesNotExist) as cm:
            the_list = ElementList([], find_by="id", query="menu")
            the_list.first

        expected_message = 'no elements could be found with id "menu"'
        e = cm.exception
        self.assertEqual(expected_message, e.args[0])
<commit_msg>tests/test_element_list.py is now pep8 certified<commit_after># -*- coding: utf-8 -*-

# Copyright 2012 splinter authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

try:
    import unittest2 as unittest
except ImportError:
    import unittest

from splinter.element_list import ElementList
from splinter.exceptions import ElementDoesNotExist

class Person(object):
    """Very simple class, just for tests"""

    def __init__(self):
        self.current_action = None

    def walk(self):
        self.current_action = "walking"

class ElementListTest(unittest.TestCase):

    def test_method_that_verifies_if_the_list_is_empty(self):
        "should verify if the list is empty"
        the_list = ElementList([1, 2, 3])
        self.assertFalse(the_list.is_empty())
        self.assertTrue(ElementList([]).is_empty())

    def test_property_first_and_last(self):
        """
        should provide a \"first\" and a \"last\" properties
        which returns the first and last element
        """
        the_list = ElementList([1, 2, 3])
        self.assertEqual(the_list[0], the_list.first)
        self.assertEqual(the_list[2], the_list.last)

    def test_call_method_on_first_element(self):
        """
        when some method is missing on ElementList and
        is present in element, it should be passed
        """
        the_list = ElementList([Person(), Person(), Person()])
        the_list.walk()
        the_person = the_list.first
        self.assertEqual("walking", the_person.current_action)

    def test_raise_exception_on_indexerror(self):
        "should raise ElementDoesNotExist exception on IndexError"
        with self.assertRaises(ElementDoesNotExist):
            ElementList([]).first

    def test_raise_attribute_error(self):
        """
        should raise AttributeError when trying to access
        a non-existent method on list and element
        """
        with self.assertRaises(AttributeError):
            the_list = ElementList([Person(), Person()])
            the_list.talk()

    def test_attribute_error_for_empty(self):
        """
        should raise AttributeError when the list is empty
        and someone tries to access a method or property on it
        """
        with self.assertRaises(AttributeError):
            the_list = ElementList([])
            the_list.unknown_method()

    def test_attribute_error_content(self):
        "should raise AttributeError with right content"
        with self.assertRaises(AttributeError) as cm:
            the_list = ElementList([Person(), Person()])
            the_list.talk()

        expected_message = "'ElementList' object has no attribute 'talk'"
        e = cm.exception
        self.assertEqual(expected_message, e.args[0])

    def test_not_found_exception_with_query_and_method(self):
        """
        should receive the find method
        and the query and use them in exception
        """
        with self.assertRaises(ElementDoesNotExist) as cm:
            the_list = ElementList([], find_by="id", query="menu")
            the_list.first

        expected_message = 'no elements could be found with id "menu"'
        e = cm.exception
        self.assertEqual(expected_message, e.args[0])
<|endoftext|>