Beispiel #1
0
    def test_emit_update_indeterminate(self, add_handler):
        ws = WebSocket('pb')
        module_pb = ProgressBar(indeterminate=True)

        ws.bind(module_pb)
        ws.emit = Mock()

        # 1st try, without label
        module_pb.tick()

        call1 = call('module_progressbar_before_update', None)
        call2 = call('module_progressbar_update', {})
        call3 = call('module_progressbar_after_update', None)

        ws.emit.assert_has_calls([call1, call2, call3])
        ws.emit.reset_mock()

        # 2nd and last try, with label
        module_pb.tick('My label')

        call1 = call('module_progressbar_before_update', None)
        call2 = call('module_progressbar_update', {'label': 'My label'})
        call3 = call('module_progressbar_after_update', None)

        ws.emit.assert_has_calls([call1, call2, call3])
class TestModuleProgressBarCommunication(WebSocketBaseTestCase):
    @patch('tornado_websockets.tornadowrapper.TornadoWrapper.add_handler')
    def get_app(self, add_handler):
        self.ws = WebSocket('pb')
        self.close_future = Future()

        return tornado.web.Application([
            ('/ws/module/pb', WebSocketHandlerForTests, {'websocket': self.ws, 'close_future': self.close_future}),
        ])

    @gen_test
    def test_open_event(self):
        @self.ws.on
        def open():
            self.ws.emit('opened')

        module_pb = ProgressBar()
        module_pb.emit_init = Mock()
        self.ws.bind(module_pb)

        ws_connection = yield self.ws_connect('/ws/module/pb')

        # 1st: clasical websocket on open event
        response = yield ws_connection.read_message()
        response = json_decode(response)

        self.assertDictEqual(response, {
            'event': 'opened',
            'data': {}
        })

        # 2nd: ProgressBar module on open event
        module_pb.emit_init.assert_called_with()

        self.close(ws_connection)
    def get_app(self, add_handler):
        self.ws = WebSocket('pb')
        self.close_future = Future()

        return tornado.web.Application([
            ('/ws/module/pb', WebSocketHandlerForTests, {'websocket': self.ws, 'close_future': self.close_future}),
        ])
    def test_emit(self, add_handler):
        ws = WebSocket('foo')
        ws.emit = Mock()
        moduleBar = MyModule('bar')
        moduleBar.initialize = Mock()

        # Module is not binded to WebSocket instance
        self.assertEqual(ws.modules, [])
        self.assertEqual(ws.events, {})
        self.assertIsNone(moduleBar._websocket)
        with self.assertRaisesRegexp(
                AttributeError, "'NoneType' object has no attribute 'emit'"):
            moduleBar.emit('my_event', {'my': 'data'})
        ws.emit.assert_not_called()

        # Module is now binded to WebSocket instance
        ws.bind(moduleBar)

        moduleBar.initialize.assert_called_with()

        self.assertEqual(ws.modules, [moduleBar])
        self.assertEqual(ws.events, {})
        self.assertEqual(moduleBar._websocket, ws)

        moduleBar.emit('my_event', {'my': 'data'})

        ws.emit.assert_called_with('module_mymodule_bar_my_event',
                                   {'my': 'data'})
    def test_on(self, add_handler):
        ws = WebSocket('foo')
        moduleBar = MyModule('bar')
        moduleBar.initialize = Mock()

        # Module is not binded to WebSocket instance
        self.assertEqual(ws.modules, [])
        self.assertEqual(ws.events, {})
        self.assertIsNone(moduleBar._websocket)
        with self.assertRaisesRegexp(
                AttributeError, "'NoneType' object has no attribute 'on'"):

            @moduleBar.on
            def func():
                pass

        # Module is now binded to WebSocket instance
        ws.bind(moduleBar)
        moduleBar.initialize.assert_called_with()

        self.assertEqual(ws.modules, [moduleBar])
        self.assertEqual(ws.events, {})
        self.assertEqual(moduleBar._websocket, ws)

        @moduleBar.on
        def func():
            pass

        self.assertDictEqual(ws.events, {'module_mymodule_bar_func': func})
    def test_construct(self, add_handler):
        add_handler.assert_called_with(('/ws/path1', WebSocketHandler, {'websocket': WebSocket('path1')}))
        add_handler.assert_called_with(('/ws/path2', WebSocketHandler, {'websocket': WebSocket('/path2')}))
        add_handler.assert_called_with(('/ws/path3', WebSocketHandler, {'websocket': WebSocket('  path3  ')}))
        add_handler.assert_called_with(('/ws/path4', WebSocketHandler, {'websocket': WebSocket('   /path4 ')}))

        with self.assertRaisesRegexp(TypeError, '« Path » parameter should be a string.'):
            WebSocket(path=1234)
    def test_emit_with_bad_parameter_data(self):
        ws = WebSocket('/abc')
        ws.handlers = ['handler']

        time.sleep(SLEEPING_TIME)

        with self.assertRaises(TypeError) as e:
            ws.emit('my_event', 123)

        self.assertEqual(str(e.exception), 'Data should be a string or a dictionary.')
    def test_emit_with_good_parameter_event(self):
        ws = WebSocket('/abc')
        ws.handlers = ['not_an_handler']

        time.sleep(SLEEPING_TIME)

        # It raises an InvalidInstanceError because we override ws's handlers to dodge EmitHandlerError exception,
        # and we can't get a real WebSocketHandler to use with this ws. But it works
        with self.assertRaises(InvalidInstanceError) as e:
            ws.emit('my_event')
    def test_emit_with_bad_parameter_event(self):
        ws = WebSocket('/abc')
        ws.handlers = ['not_an_handler']

        time.sleep(SLEEPING_TIME)

        with self.assertRaises(TypeError) as e:
            ws.emit(12345)

        self.assertEqual(str(e.exception), 'Event should be a string.')
Beispiel #10
0
    def test_initialize(self, add_handler):
        ws = WebSocket('pb')
        module_pb = ProgressBar()
        module_pb.initialize = Mock()

        self.assertEqual(ws.modules, [])

        ws.bind(module_pb)

        self.assertEqual(ws.modules, [module_pb])
        module_pb.initialize.assert_called_with()
Beispiel #11
0
    def test_context(self, add_handler):
        module = MyModule()
        ws = WebSocket('pb')

        self.assertIsNone(module._websocket)
        with self.assertRaisesRegexp(AttributeError, "'NoneType' object has no attribute 'context'"):
            print(module.context)

        ws.bind(module)

        module.context = 'foo'
        self.assertEqual(module._websocket.context, 'foo')
        self.assertEqual(module.context, 'foo')
    def test_on(self, add_handler):
        ws = WebSocket('path')

        self.assertDictEqual(ws.events, {})

        with self.assertRaises(NotCallableError):
            ws.on('string')

        @ws.on
        def func():
            pass

        self.assertDictEqual(ws.events, {'func': func})
Beispiel #13
0
    def test_emit_init_indeterminate(self, add_handler):
        ws = WebSocket('pb')
        module_pb = ProgressBar(indeterminate=True)

        ws.bind(module_pb)
        ws.emit = Mock()

        module_pb.emit_init()

        call1 = call('module_progressbar_before_init', None)
        call2 = call('module_progressbar_init', {'indeterminate': True})
        call3 = call('module_progressbar_after_init', None)

        ws.emit.assert_has_calls([call1, call2, call3])
    def test_bind_module(self, add_handler):
        ws = WebSocket('path')
        module = ProgressBar('progress')

        module.initialize = Mock()

        self.assertListEqual(ws.modules, [])
        self.assertIsNone(module._websocket)
        module.initialize.assert_not_called()

        ws.bind(module)

        self.assertListEqual(ws.modules, [module])
        self.assertEqual(module._websocket, ws)
        module.initialize.assert_called_with()
    def test_emit_outside_on_decorator(self):
        ws = WebSocket('/abc')

        time.sleep(SLEEPING_TIME)

        with self.assertRaises(EmitHandlerError) as e:
            ws.emit('my_event', 'my_message')

        self.assertEqual(e.exception.event, 'my_event')
        self.assertEqual(e.exception.path, '/abc')
        self.assertEqual(
            str(e.exception),
            'Can not emit "%s" event in "%s" path, emit() should be used in a function or class method'
            ' decorated by @WebSocket.on decorator.' % ('my_event', '/abc')
        )
    def test_emit_with_bad_handlers(self):
        ws = WebSocket('/abc')
        ws.handlers = ['not_an_handler']

        time.sleep(SLEEPING_TIME)

        with self.assertRaises(InvalidInstanceError) as e:
            ws.emit('my_event')

        self.assertEqual(e.exception.actual_instance, 'not_an_handler')
        self.assertEqual(e.exception.expected_instance_name, 'tornado_websockets.websockethandler.WebSocketHandler')
        self.assertEqual(
            str(e.exception),
            'Expected instance of "%s", got "%s" instead.' % (
                'tornado_websockets.websockethandler.WebSocketHandler', repr('not_an_handler')
            )
        )
Beispiel #17
0
    def test_emit_done(self, add_handler):
        ws = WebSocket('pb')
        module_pb = ProgressBar(min=0, max=2)

        ws.bind(module_pb)
        ws.emit = Mock()
        module_pb.emit_init = Mock()
        module_pb.emit_update = Mock()

        # 1st try
        module_pb.tick()
        self.assertEqual(module_pb.current, 1)
        ws.emit.assert_not_called()

        # 2nd and last try
        module_pb.tick()
        self.assertEqual(module_pb.current, 2)
        self.assertEqual(module_pb.current, module_pb.max)
        ws.emit.assert_called_with('module_progressbar_done', None)
Beispiel #18
0
    def test_emit_init_determinate(self, add_handler):
        ws = WebSocket('pb')
        module_pb = ProgressBar()

        ws.bind(module_pb)
        ws.emit = Mock()

        module_pb.emit_init()

        call1 = call('module_progressbar_before_init', None)
        call2 = call('module_progressbar_init', {
            'indeterminate': False,
            'min': 0,
            'max': 100,
            'current': 0
        })
        call3 = call('module_progressbar_after_init', None)

        ws.emit.assert_has_calls([call1, call2, call3])
Beispiel #19
0
def get_ws():
    ws = WebSocket('/test')

    @ws.on
    def hello(socket, data):
        ws.emit(
            'hello', {
                'socket': str(socket),
                'data_sent': data,
                'message': 'Hello from hello callback!'
            })

    return ws
    def __init__(self, path, min=0, max=100, add_to_handlers=True):

        if max < min:
            raise ValueError('`max` value (%d) can not be lower than `min` value (%d).' % (max, min))

        self.min = min
        self.max = max
        self._value = min
        self.indeterminate = min is max

        self.path = path.strip()
        self.path = self.path if self.path.startswith('/') else '/' + self.path
        self.websocket = WebSocket('/module/progress_bar' + self.path, add_to_handlers)
        self.bind_default_events()
Beispiel #21
0
class TestModuleProgressBarCommunication(WebSocketBaseTestCase):
    @patch('tornado_websockets.tornadowrapper.TornadoWrapper.add_handler')
    def get_app(self, add_handler):
        self.ws = WebSocket('pb')
        self.close_future = Future()

        return tornado.web.Application([
            ('/ws/module/pb', WebSocketHandlerForTests, {
                'websocket': self.ws,
                'close_future': self.close_future
            }),
        ])

    @gen_test
    def test_open_event(self):
        module_pb = ProgressBar()
        module_pb.emit_init = Mock()
        self.ws.bind(module_pb)

        ws_connection = yield self.ws_connect('/ws/module/pb')

        module_pb.emit_init.assert_called_with()

        self.close(ws_connection)
Beispiel #22
0
# coding: utf-8
"""
    Example of module « Progress Bar » by using `tornado_websocket.modules.ProgressBar` to handle communications,
    and Django's TemplateView for rendering.
"""

from django.views.generic import TemplateView
from tornado import gen

from tornado_websockets.modules import ProgressBar
from tornado_websockets.websocket import WebSocket

ws = WebSocket('module_progressbar')
progressbar = ProgressBar('foo', min=0, max=100)

ws.bind(progressbar)


@progressbar.on
def reset():
    progressbar.reset()


@progressbar.on
@gen.engine  # Make this function asynchronous for Tornado's IOLoop
def start():
    for value in range(0, progressbar.max):
        yield gen.sleep(.1)  # like time.sleep(), but asynchronous
        progressbar.tick(label="[%d/%d] Tâche %d terminée" %
                         (progress_bar.current + 1, progress_bar.max, value))
# coding: utf-8

"""
    Example of a « echo » websocket server by using `tornado_websocket.WebSocket`.
"""

from tornado_websockets.websocket import WebSocket

tws = WebSocket('/echo')


# Listen the « message » event
@tws.on
def message(socket, data):
    socket.emit('new_message', {
        'message': data.get('message')
    })

    # Shorter version
    # socket.emit('new_message', data)
# coding: utf-8
"""
    Example of a « chat application » by using `tornado_websocket.WebSocket` to handle communications,
    and Django's TemplateView for rendering.
"""

from django.views.generic import TemplateView

from tornado_websockets.websocket import WebSocket

tws = WebSocket('/my_chat')


class MyChat(TemplateView):
    """
        Proof of concept about a really simple web chat using websockets and supporting messages history
    """

    template_name = 'testapp/index.html'
    messages = []

    def __init__(self, **kwargs):
        super(MyChat, self).__init__(**kwargs)

        # Otherwise, 'self' parameter for method decorated by @ws_chat.on will not be defined
        tws.context = self

    @tws.on
    def connection(self, socket, data):
        # Send an history of the chat
        [socket.emit('new_message', __) for __ in self.messages]
class ProgressBar(object):
    """
        Initialize a new ProgressBar module instance.

        If ``min`` and ``max`` values are equal, this progress bar has its indeterminate state
        set to ``True``.

        :param path: WebSocket path, see ``tornado_websockets.websocket.WebSocket``
        :param min: Minimum _value
        :param max: Maximum _value
        :type path: str
        :type min: int
        :type max: int
    """

    def __init__(self, path, min=0, max=100, add_to_handlers=True):

        if max < min:
            raise ValueError('`max` value (%d) can not be lower than `min` value (%d).' % (max, min))

        self.min = min
        self.max = max
        self._value = min
        self.indeterminate = min is max

        self.path = path.strip()
        self.path = self.path if self.path.startswith('/') else '/' + self.path
        self.websocket = WebSocket('/module/progress_bar' + self.path, add_to_handlers)
        self.bind_default_events()

    def reset(self):
        """
            Reset progress bar's progression to its minimum value.
        """
        self._value = self.min

    def tick(self, label=None):
        """
            Increments progress bar's _value by ``1`` and emit ``update`` event. Can also emit ``done`` event if
            progression is done.

            Call :meth:`~tornado_websockets.modules.progress_bar.ProgressBar.emit_update` method each time this
            method is called.
            Call :meth:`~tornado_websockets.modules.progress_bar.ProgressBar.emit_done` method if progression is
            done.

            :param label: A label which can be displayed on the client screen
            :type label: str
        """

        if not self.indeterminate and self._value < self.max:
            self._value += 1

        self.emit_update(label)

        if self.is_done():
            self.emit_done()

    def is_done(self):
        """
            Return ``True`` if progress bar's progression is done, otherwise ``False``.

            Returns ``False`` if progress bar is indeterminate, returns ``True`` if progress bar is
            determinate and current value is equals to ``max`` value.
            Returns ``False`` by default.

            :rtype: bool
        """

        if self.indeterminate:
            return False

        if self.value is self.max:
            return True

        return False

    def bind_default_events(self):
        """
            Bind default events for WebSocket instance.

            Actually, it only binds ``open`` event.
        """

        @self.websocket.on
        def open():
            self.emit_init()

    def on(self, callback):
        """
            Shortcut for :meth:`tornado_websockets.websocket.WebSocket.on` decorator.

            :param callback: Function or a class method.
            :type callback: Callable
            :return: ``callback`` parameter.
        """

        return self.websocket.on(callback)

    def emit_init(self):
        """
            Emit ``before_init``, ``init`` and ``after_init`` events to initialize a client-side progress bar.

            If progress bar is not indeterminate, ``min``, ``max`` and ``value`` values are sent with ``init`` event.
        """

        data = {'indeterminate': self.indeterminate}

        if not self.indeterminate:
            data.update({
                'min': int(self.min),
                'max': int(self.max),
                'value': int(self._value),
            })

        self.websocket.emit('before_init')
        self.websocket.emit('init', data)
        self.websocket.emit('after_init')

    def emit_update(self, label=None):
        """
            Emit ``before_update``, ``update`` and ``after_update`` events to update a client-side progress bar.

            :param label: A label which can be displayed on the client screen
            :type label: str
        """

        data = {}

        if not self.indeterminate:
            data.update({'value': int(self._value)})

        if label:
            data.update({'label': label})

        self.websocket.emit('before_update')
        self.websocket.emit('update', data)
        self.websocket.emit('after_update')

    def emit_done(self):
        """
            Emit ``done`` event when progress bar's progression :meth:`~tornado_websockets.modules.progress_bar.ProgressBar.is_done`.
        """

        self.websocket.emit('done')

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
        if not self.indeterminate and not self.min <= value <= self.max:
            raise ValueError('Value is not in [%d; %d] range.' % (self.min, self.max))

        self._value = value

    @property
    def context(self):
        return self.websocket.context

    @context.setter
    def context(self, value):
        self.websocket.context = value
    def test_emit(self, add_handler):
        ws = WebSocket('path')
        handler = Mock()

        # Emulate WebSocketHandler class with Mock, because only Tornado can instantiate it properly
        def side_effect(websocket):
            ws.handlers.append(handler)
            handler.websocket = websocket

        handler.return_value = None
        handler.websocket = None
        handler.initialize.side_effect = side_effect
        handler.emit = Mock()

        self.assertListEqual(ws.handlers, [])
        self.assertIsNone(handler.websocket)

        handler.initialize(ws)

        self.assertListEqual(ws.handlers, [handler])
        self.assertEqual(handler.websocket, ws)

        with self.assertRaisesRegexp(TypeError, 'Param « event » should be a string.'):
            ws.emit(123)
        handler.emit.assert_not_called()

        ws.emit('event')
        handler.emit.assert_called_with('event', {})
        handler.emit.reset_mock()

        ws.emit('event', {})
        handler.emit.assert_called_with('event', {})
        handler.emit.reset_mock()

        ws.emit('event', 'my message')
        handler.emit.assert_called_with('event', {'message': 'my message'})
        handler.emit.reset_mock()

        with self.assertRaisesRegexp(TypeError, 'Param « data » should be a string or a dictionary.'):
            ws.emit('event', 123)
        handler.emit.assert_not_called()