Пример #1
0
async def test_can_send_binary_data_over_connection(
        tcp_connection_class, integration_tcp_server_and_pipe):
    from moler.connection import ObservableConnection
    (tcp_server, tcp_server_pipe) = integration_tcp_server_and_pipe

    moler_conn = ObservableConnection()  # no decoder, just pass bytes 1:1
    connection = tcp_connection_class(moler_connection=moler_conn,
                                      port=tcp_server.port,
                                      host=tcp_server.host)
    async with connection:
        moler_conn.send(
            data=b'data to be send'
        )  # TODO: await moler_conn.send(data=b'data to be send') ???
        time.sleep(
            0.1
        )  # otherwise we have race between server's pipe and from-client-connection
        tcp_server_pipe.send(("get history", {}))
        dialog_with_server = tcp_server_pipe.recv()
        assert ['Received data:', b'data to be send'] == dialog_with_server[-1]
 def tcp_thd_conn(port, host='localhost', name=None):
     moler_conn = ObservableConnection(
         decoder=lambda data: data.decode("utf-8"))
     conn_logger_name = 'threaded.tcp-connection({}:{})'.format(host, port)
     conn_logger = logging.getLogger(conn_logger_name)
     io_conn = tcp.ThreadedTcp(moler_connection=moler_conn,
                               port=port,
                               host=host,
                               logger=conn_logger)
     return io_conn
Пример #3
0
def test_event_string_is_required_to_start_command(lineevent_class):
    from moler.exceptions import NoDetectPatternProvided
    moler_conn = ObservableConnection()

    event_class = do_nothing_command_class(base_class=lineevent_class)
    event = event_class(connection=moler_conn, detect_patterns=[])
    assert not event.detect_patterns  # ensure it is empty before starting command

    with pytest.raises(NoDetectPatternProvided) as error:
        event.start()  # start the command-future (background run)
Пример #4
0
def test_connections_use_same_loop(tcp_connection_class,
                                   integration_tcp_server_and_pipe,
                                   integration_second_tcp_server_and_pipe):
    # same loop means also same thread since asyncio has one loop in thread
    from moler.connection import ObservableConnection
    (tcp_server0, tcp_server0_pipe) = integration_tcp_server_and_pipe
    (tcp_server1, tcp_server1_pipe) = integration_second_tcp_server_and_pipe

    connection0 = tcp_connection_class(moler_connection=ObservableConnection(),
                                       port=tcp_server0.port, host=tcp_server0.host)
    connection1 = tcp_connection_class(moler_connection=ObservableConnection(),
                                       port=tcp_server1.port, host=tcp_server1.host)
    with connection0.open():
        with connection1.open():
            # loop and thread appear after open()
            async_loop_of_connection0 = connection0._async_tcp._stream_reader._loop
            async_loop_of_connection1 = connection0._async_tcp._stream_reader._loop

            assert async_loop_of_connection0 == async_loop_of_connection1
Пример #5
0
def test_can_notify_its_observer_about_data_comming_from_external_io(
        buffer_transport_class):
    from moler.connection import ObservableConnection

    moler_received_data = []

    def buffer_observer(data):
        moler_received_data.append(data)

    moler_conn = ObservableConnection()
    moler_conn.subscribe(buffer_observer)

    used_io = buffer_transport_class(
        moler_connection=moler_conn)  # external-IO internally sets .how2send
    used_io.write(
        input_bytes=b"incoming data")  # inject to buffer for next line read
    used_io.read()

    assert b"incoming data" in moler_received_data
Пример #6
0
def test_connection_has_not_stopped_loop_after_close(tcp_connection_class,
                                                     integration_tcp_server_and_pipe):
    from moler.connection import ObservableConnection
    (tcp_server, tcp_server_pipe) = integration_tcp_server_and_pipe

    moler_conn = ObservableConnection()
    connection = tcp_connection_class(moler_connection=moler_conn, port=tcp_server.port, host=tcp_server.host)
    connection.open()
    async_loop_of_connection = connection._async_tcp._stream_reader._loop
    connection.close()
    assert async_loop_of_connection.is_running()
Пример #7
0
def test_can_receive_binary_data_from_connection(tcp_connection_class,
                                                 integration_tcp_server_and_pipe):
    from moler.connection import ObservableConnection
    (tcp_server, tcp_server_pipe) = integration_tcp_server_and_pipe
    received_data = bytearray()
    receiver_called = threading.Event()

    def receiver(data):
        received_data.extend(data)
        receiver_called.set()

    moler_conn = ObservableConnection()  # no decoder, just pass bytes 1:1
    moler_conn.subscribe(receiver)       # build forwarding path
    connection = tcp_connection_class(moler_connection=moler_conn, port=tcp_server.port, host=tcp_server.host)
    with connection.open():  # TODO: async with connection.open():
        time.sleep(0.1)  # otherwise we have race between server's pipe and from-client-connection
        tcp_server_pipe.send(("send async msg", {'msg': b'data to read'}))
        receiver_called.wait(timeout=0.5)

    assert b'data to read' == received_data
Пример #8
0
def test_notified_observer_may_stop_subscription_of_data_comming_from_external_io(buffer_transport_class):
    from moler.connection import ObservableConnection

    moler_conn = ObservableConnection()
    moler_received_data = []

    def one_time_observer(data):
        moler_received_data.append(data)
        moler_conn.unsubscribe(one_time_observer)

    moler_conn.subscribe(one_time_observer)

    used_io = buffer_transport_class(moler_connection=moler_conn)  # external-IO internally sets .how2send
    used_io.write(input_bytes=b"data 1")  # inject to buffer for next line read
    used_io.read()
    used_io.write(input_bytes=b"data 2")  # inject to buffer for next line read
    used_io.read()

    assert b"data 1" in moler_received_data
    assert b"data 2" not in moler_received_data  # because of unsubscription during notification
def ping_observing_task(address, ping_ip):
    logger = logging.getLogger('moler.user.app-code')
    net_addr = 'tcp://{}:{}'.format(*address)

    # Lowest layer of Moler's usage (you manually glue all elements):
    # 1. create observers
    net_down_detector = NetworkDownDetector(ping_ip)
    net_drop_found = False
    net_up_detector = NetworkUpDetector(ping_ip)
    moler_conn = ObservableConnection(
        decoder=lambda data: data.decode("utf-8"))
    # 2. virtually "start" observer by making it data-listener
    moler_conn.subscribe(net_down_detector.data_received)

    info = '{} on {} using {}'.format(ping_ip, net_addr, net_down_detector)
    logger.debug('observe ' + info)
    for _ in tcp_connection(address, moler_conn):
        # anytime new data comes it may change status of observer
        if not net_drop_found and net_down_detector.done():
            net_drop_found = True
            net_down_time = net_down_detector.result()
            timestamp = time.strftime("%H:%M:%S",
                                      time.localtime(net_down_time))
            logger.debug('Network {} is down from {}'.format(
                ping_ip, timestamp))
            # 3. virtually "stop" that observer
            moler_conn.unsubscribe(net_down_detector.data_received)
            # 4. and start subsequent one (to know when net is back "up")
            info = '{} on {} using {}'.format(ping_ip, net_addr,
                                              net_up_detector)
            logger.debug('observe ' + info)
            moler_conn.subscribe(net_up_detector.data_received)
        if net_up_detector.done():
            net_up_time = net_up_detector.result()
            timestamp = time.strftime("%H:%M:%S", time.localtime(net_up_time))
            logger.debug('Network {} is back "up" from {}'.format(
                ping_ip, timestamp))
            # 5. virtually "stop" that observer
            moler_conn.unsubscribe(net_up_detector.data_received)
            break
Пример #10
0
async def ping_observing_task(address):
    logger = logging.getLogger('moler.user.app-code')

    # Lowest layer of Moler's usage (you manually glue all elements):
    # 1. create observer
    net_down_detector = NetworkDownDetector()
    # 2. ObservableConnection is a proxy-glue between observer (speaks str)
    #                                   and curio-connection (speaks bytes)
    moler_conn = ObservableConnection(
        decoder=lambda data: data.decode("utf-8"))
    # 3a. glue from proxy to observer
    moler_conn.subscribe(net_down_detector.data_received)

    logger.debug('waiting for data to observe')
    async with curio.meta.finalize(tcp_connection(address)) as tcp_conn:
        async for connection_data in tcp_conn:
            # 3b. glue to proxy from external-IO (curio tcp client connection)
            #    (client has to pass it's received data into Moler's connection)
            moler_conn.data_received(connection_data)
            # 4. Moler's client code must manually check status of observer ...
            if net_down_detector.done():
                # 5. ... to know when it can ask for result
                net_down_time = net_down_detector.result()
                timestamp = time.strftime("%H:%M:%S",
                                          time.localtime(net_down_time))
                logger.debug('Network is down from {}'.format(timestamp))
                break
Пример #11
0
def test_repeated_unsubscription_does_nothing_but_logs_warning(
        buffer_transport_class):
    """
    Because of possible different concurrency models (and their races)
    we don't want to raise exception when there is already
    "no such subscription" - just put warning to logs
    """
    from moler.connection import ObservableConnection

    moler_conn = ObservableConnection()
    moler_received_data = []

    def one_time_observer(data):
        moler_received_data.append(data)
        moler_conn.unsubscribe(one_time_observer)

    moler_conn.subscribe(one_time_observer)

    used_io = buffer_transport_class(
        moler_connection=moler_conn)  # external-IO internally sets .how2send
    used_io.write(input_bytes=b"data 1")  # inject to buffer for next line read
    used_io.read()
    moler_conn.unsubscribe(
        one_time_observer
    )  # TODO: check warning in logs (when we set logging system)
    used_io.write(input_bytes=b"data 2")  # inject to buffer for next line read
    used_io.read()

    assert b"data 1" in moler_received_data
    assert b"data 2" not in moler_received_data  # because of unsubscription during notification
Пример #12
0
def test_events_false_any():
    connection = ObservableConnection()
    events = list()
    patterns = ("aaa", "bbb")
    for pattern in patterns:
        event = Wait4prompt(connection=connection, till_occurs_times=1, prompt=pattern)
        event.start()
        events.append(event)
    assert EventAwaiter.wait_for_any(timeout=0.1, events=events) is False
    done, not_done = EventAwaiter.separate_done_events(events)
    assert 0 == len(done)
    assert 2 == len(not_done)
    EventAwaiter.cancel_all_events(events)
Пример #13
0
def test_can_notify_multiple_observers_about_data_comming_from_external_io(buffer_transport_class):
    from moler.connection import ObservableConnection

    class BufferObserver(object):
        def __init__(self):
            self.received_data = []

        def on_new_data(self, data):
            self.received_data.append(data)

    buffer_observer1 = BufferObserver()
    buffer_observer2 = BufferObserver()

    moler_conn = ObservableConnection()
    moler_conn.subscribe(buffer_observer1.on_new_data)
    moler_conn.subscribe(buffer_observer2.on_new_data)

    used_io = buffer_transport_class(moler_connection=moler_conn)  # external-IO internally sets .how2send
    used_io.write(input_bytes=b"incoming data")  # inject to buffer for next line read
    used_io.read()

    assert b"incoming data" in buffer_observer1.received_data
    assert b"incoming data" in buffer_observer2.received_data
Пример #14
0
def test_can_open_and_close_connection_as_context_manager(tcp_connection_class,
                                                          integration_tcp_server_and_pipe):
    from moler.connection import ObservableConnection
    (tcp_server, tcp_server_pipe) = integration_tcp_server_and_pipe

    moler_conn = ObservableConnection()
    connection = tcp_connection_class(moler_connection=moler_conn, port=tcp_server.port, host=tcp_server.host)
    with connection.open():
        pass
    time.sleep(0.1)  # otherwise we have race between server's pipe and from-client-connection
    tcp_server_pipe.send(("get history", {}))
    dialog_with_server = tcp_server_pipe.recv()
    assert 'Client connected' in dialog_with_server
    assert 'Client disconnected' in dialog_with_server
Пример #15
0
def connection_to_remote():
    """
    Any external-IO connection that embeds Moler-connection
    Alows to check if data send from command has reached remote side via:
    `data in conn.remote_endpoint()`
    """
    class RemoteConnection(FifoBuffer):
        def remote_endpoint(self):
            """Simulate remote endpoint that gets data"""
            return self.buffer

    ext_io = RemoteConnection(moler_connection=ObservableConnection(encoder=lambda data: data.encode("utf-8"),
                                                                    decoder=lambda data: data.decode("utf-8")))
    return ext_io
Пример #16
0
def test_command_string_is_required_to_start_command(command_major_base_class):
    from moler.exceptions import NoCommandStringProvided
    moler_conn = ObservableConnection()

    command_class = do_nothing_command_class(base_class=command_major_base_class)
    command = command_class(connection=moler_conn)
    assert not command.command_string  # ensure it is empty before starting command

    with pytest.raises(NoCommandStringProvided) as error:
        command.start()  # start the command-future (background run)

    assert error.value.command == command
    assert 'for {}'.format(str(command)) in str(error.value)
    assert 'You should fill .command_string member before starting command' in str(error.value)
Пример #17
0
def failing_net_down_detector(fail_on_data, fail_by_raising, runner):
    from moler.connection import ObservableConnection

    class FailingNetworkDownDetector(NetworkDownDetector):
        def data_received(self, data):
            if data == fail_on_data:
                raise fail_by_raising
            return super(FailingNetworkDownDetector, self).data_received(data)

    moler_conn = ObservableConnection()
    failing_detector = FailingNetworkDownDetector(connection=moler_conn,
                                                  runner=runner)
    yield failing_detector
    # remove exceptions collected inside ConnectionObserver
    ConnectionObserver.get_unraised_exceptions(remove=True)
Пример #18
0
def test_runner_shutdown_cancels_remaining_inactive_feeders_inside_main_thread(
        observer_runner):
    from moler.connection import ObservableConnection

    connection_observer = NetworkDownDetector(
        connection=ObservableConnection(), runner=observer_runner)

    connection_observer.start_time = time.time(
    )  # must start observer lifetime before runner.submit()
    future = observer_runner.submit(connection_observer)

    time.sleep(
        0.2
    )  # won't enter event loop of future - feeder won't start processing
    observer_runner.shutdown()
    assert connection_observer.cancelled()
Пример #19
0
def test_runner_shutdown_cancels_remaining_active_feeders_inside_main_thread(
        async_runner):
    from moler.connection import ObservableConnection

    connection_observer = NetworkDownDetector(
        connection=ObservableConnection(), runner=async_runner)

    connection_observer.start_time = time.time(
    )  # must start observer lifetime before runner.submit()
    future = async_runner.submit(connection_observer)

    future._loop.run_until_complete(
        asyncio.sleep(1.0))  # feeder will start processing inside loop
    # time.sleep(0.5)
    async_runner.shutdown()
    assert connection_observer.cancelled()
Пример #20
0
def test_closing_closed_connection_does_nothing(tcp_connection_class,
                                                integration_tcp_server_and_pipe):
    from moler.connection import ObservableConnection
    (tcp_server, tcp_server_pipe) = integration_tcp_server_and_pipe

    moler_conn = ObservableConnection()
    connection = tcp_connection_class(moler_connection=moler_conn, port=tcp_server.port, host=tcp_server.host)
    connection.open()
    connection.close()
    connection.close()
    time.sleep(0.1)  # otherwise we have race between server's pipe and from-client-connection
    tcp_server_pipe.send(("get history", {}))
    dialog_with_server = tcp_server_pipe.recv()
    assert 'Client connected' in dialog_with_server
    assert 'Client disconnected' in dialog_with_server
    assert dialog_with_server[-2] != 'Client disconnected'  # not closed twice
Пример #21
0
def test_gets_all_data_of_connection_after_it_is_started(observer_runner):
    from moler.connection import ObservableConnection

    for n in range(20):  # need to test multiple times because of thread races
        moler_conn = ObservableConnection()
        net_down_detector = NetworkDownDetector(connection=moler_conn,
                                                runner=observer_runner)
        connection = net_down_detector.connection
        net_down_detector.start()

        connection.data_received("61 bytes")
        connection.data_received("62 bytes")
        connection.data_received("ping: Network is unreachable")

        assert net_down_detector.all_data_received == [
            "61 bytes", "62 bytes", "ping: Network is unreachable"
        ]
Пример #22
0
async def test_wait_for__prohibited_inside_async_def_speaks_in_observer_API(
        async_runner):
    from moler.exceptions import WrongUsage
    from moler.connection import ObservableConnection

    connection_observer = NetworkDownDetector(
        connection=ObservableConnection(), runner=async_runner)
    connection_observer.start()  # internally calls async_runner.submit()
    future = async_runner.submit(connection_observer)
    with pytest.raises(WrongUsage) as err:
        connection_observer.await_done(
        )  # internally calls async_runner.wait_for() + connection_observer.result()

    assert "Can't call await_done() from 'async def' - it is blocking call" in str(
        err.value)
    # check "fix-hint" inside exception
    assert re.findall(
        r'consider using:\s+await observer\s+instead of:\s+observer.await_done()',
        str(err.value))
Пример #23
0
def test_can_open_and_close_connection(tcp_connection_class,
                                       integration_tcp_server_and_pipe):
    """
    Not so atomic test (checks 2 things) but:
    - it is integration tests
    - anyway open needs close as cleanup to not have resources leaking in tests
    """
    from moler.connection import ObservableConnection
    (tcp_server, tcp_server_pipe) = integration_tcp_server_and_pipe

    moler_conn = ObservableConnection()
    connection = tcp_connection_class(moler_connection=moler_conn, port=tcp_server.port, host=tcp_server.host)
    connection.open()
    connection.close()
    time.sleep(0.1)  # otherwise we have race between server's pipe and from-client-connection
    tcp_server_pipe.send(("get history", {}))
    dialog_with_server = tcp_server_pipe.recv()
    assert 'Client connected' in dialog_with_server
    assert 'Client disconnected' in dialog_with_server
Пример #24
0
def buffer_connection():
    """External-io based on memory FIFO-buffer"""
    from moler.io.raw.memory import ThreadedFifoBuffer
    from moler.connection import ObservableConnection

    class RemoteConnection(ThreadedFifoBuffer):
        def remote_inject_response(self, input_strings, delay=0.0):
            """
            Simulate remote endpoint that sends response.
            Response is given as strings.
            """
            in_bytes = [data.encode("utf-8") for data in input_strings]
            self.inject_response(in_bytes, delay)

    moler_conn = ObservableConnection(
        encoder=lambda data: data.encode("utf-8"),
        decoder=lambda data: data.decode("utf-8"))
    ext_io_in_memory = RemoteConnection(moler_connection=moler_conn,
                                        echo=False)  # we don't want echo on it
    return ext_io_in_memory
Пример #25
0
def buffer_connection():
    from moler.io.raw.memory import ThreadedFifoBuffer
    from moler.connection import ObservableConnection

    class RemoteConnection(ThreadedFifoBuffer):
        def remote_inject_response(self, input_strings, delay=0.0):
            """
            Simulate remote endpoint that sends response.
            Response is given as strings.
            """
            in_bytes = [data.encode("utf-8") for data in input_strings]
            self.inject_response(in_bytes, delay)

    moler_conn = ObservableConnection(
        encoder=lambda data: data.encode("utf-8"),
        decoder=lambda data: data.decode("utf-8"))
    ext_io_in_memory = RemoteConnection(
        moler_connection=moler_conn,
        echo=False)  # we don't want echo on connection
    # all tests assume working with already open connection
    with ext_io_in_memory:  # open it (autoclose by context-mngr)
        yield ext_io_in_memory
Пример #26
0
def main(connections2observe4ip):
    # Starting the clients
    connections = []
    for address, ping_ip in connections2observe4ip:
        host, port = address
        # 1. create Moler's connection that knows encoding
        decoder = lambda data: data.decode("utf-8")
        moler_conn = ObservableConnection(decoder=decoder)
        # 2. create external-IO connection gluing to Moler's connection
        conn_logger_name = 'threaded.tcp-connection({}:{})'.format(*address)
        conn_logger = logging.getLogger(conn_logger_name)
        tcp_connection = tcp.ThreadedTcp(moler_connection=moler_conn,
                                         port=port,
                                         host=host,
                                         logger=conn_logger)
        client_thread = threading.Thread(target=ping_observing_task,
                                         args=(tcp_connection, ping_ip))
        client_thread.start()
        connections.append(client_thread)
    # await observers job to be done
    for client_thread in connections:
        client_thread.join()
Пример #27
0
def test_exception_in_observer_doesnt_break_connection_nor_other_observers(
        buffer_transport_class):
    from moler.connection import ObservableConnection

    moler_conn = ObservableConnection()
    moler_received_data = []

    def failing_observer(data):
        raise Exception("Fail inside observer")

    def one_time_observer(data):
        moler_received_data.append(data)
        moler_conn.unsubscribe(one_time_observer)

    moler_conn.subscribe(failing_observer)
    moler_conn.subscribe(one_time_observer)

    used_io = buffer_transport_class(
        moler_connection=moler_conn)  # external-IO internally sets .how2send
    used_io.write(input_bytes=b"data 1")  # inject to buffer for next line read
    used_io.read()
    moler_conn.unsubscribe(failing_observer)

    assert b"data 1" in moler_received_data
Пример #28
0
async def test_runner_unsubscribes_from_connection_after_runner_shutdown(
        observer_runner):
    # see - Raw 'def' usage note
    from moler.connection import ObservableConnection

    moler_conn = ObservableConnection()
    # check if shutdown unsubscribes all observers running inside given runner
    net_down_detector1 = NetworkDownDetector(connection=moler_conn,
                                             runner=observer_runner)
    net_down_detector2 = NetworkDownDetector(connection=moler_conn,
                                             runner=observer_runner)
    net_down_detector1.start_time = time.time(
    )  # must start observer lifetime before runner.submit()
    net_down_detector2.start_time = time.time(
    )  # must start observer lifetime before runner.submit()
    assert len(moler_conn._observers) == 0
    observer_runner.submit(net_down_detector1)
    observer_runner.submit(net_down_detector2)
    assert len(moler_conn._observers) == 2

    observer_runner.shutdown()
    await asyncio.sleep(0.1)
    assert len(moler_conn._observers) == 0
Пример #29
0
def test_garbage_collected_subscriber_is_not_notified():
    from moler.connection import ObservableConnection

    moler_conn = ObservableConnection()
    received_data = []

    class Subscriber(object):
        def __call__(self, data):
            received_data.append(data)

    subscr1 = Subscriber()
    subscr2 = Subscriber()
    moler_conn.subscribe(subscr1)
    moler_conn.subscribe(subscr2)

    del subscr1
    gc.collect()

    moler_conn.data_received("data")
    assert len(received_data) == 1
Пример #30
0
def connection_observer():
    from moler.connection import ObservableConnection
    moler_conn = ObservableConnection()
    observer = NetworkDownDetector(connection=moler_conn)
    return observer