async def test_merger_active_connection_prevents_ending( outside_source_stream, mock_merge_strategy, mock_stream_builder, mock_connection_streams, mock_connections, event_loop): connection = mock_connections() connection_stream = mock_connection_streams() mock_stream_builder.side_effect = [connection_stream] canonical_stream = outside_source_stream async def long_read(): await asyncio.sleep(0.05) return b"" connection_stream.read.side_effect = long_read connection_stream.ended.side_effect = lambda: connection_stream.read.called merger = Merger(mock_stream_builder, 0.01, mock_merge_strategy, canonical_stream) f = asyncio.ensure_future(merger.wait_for_ended()) h = asyncio.ensure_future(merger.handle_connection(connection)) await asyncio.sleep(0.02) exhaust_callbacks(event_loop) assert not f.done() await h await asyncio.sleep(0.02) exhaust_callbacks(event_loop) assert f.done()
async def test_merger_ends_when_refusing_conns_and_no_connections( outside_source_stream, mock_merge_strategy, mock_reader_builder, mock_delayed_stream_builder): canonical_stream = outside_source_stream merger = Merger(mock_reader_builder, mock_delayed_stream_builder, mock_merge_strategy, canonical_stream) merger.stop_accepting_connections() await merger.wait_for_ended() mock_merge_strategy.finalize.assert_called() assert canonical_stream.ended()
async def test_merger_rejects_writers_when_asked(outside_source_stream, mock_merge_strategy, mock_reader_builder, mock_delayed_stream_builder, mock_connections): connection = mock_connections() canonical_stream = outside_source_stream merger = Merger(mock_reader_builder, mock_delayed_stream_builder, mock_merge_strategy, canonical_stream) merger.stop_accepting_connections() with pytest.raises(CannotAcceptConnectionError): await merger.handle_connection(connection) mock_reader_builder.assert_not_called() await merger.wait_for_ended()
async def test_merger_times_out_after_creation(outside_source_stream, mock_merge_strategy, mock_stream_builder): canonical_stream = outside_source_stream merger = Merger(mock_stream_builder, 0.01, mock_merge_strategy, canonical_stream) await merger.wait_for_ended() canonical_stream.finish.assert_called()
async def test_merger_no_connections_wait_empty(outside_source_stream, mock_merge_strategy, mock_reader_builder, mock_delayed_stream_builder, event_loop): canonical_stream = outside_source_stream merger = Merger(mock_reader_builder, mock_delayed_stream_builder, mock_merge_strategy, canonical_stream) f = asyncio.ensure_future(merger.no_connections_for(0.01)) exhaust_callbacks(event_loop) assert not f.done() await asyncio.sleep(0.02) exhaust_callbacks(event_loop) assert f.done() await f merger.stop_accepting_connections() await merger.wait_for_ended()
async def test_merger_read_exception(outside_source_stream, mock_merge_strategy, mock_reader_builder, mock_delayed_stream_builder, mock_readers, mock_connections): connection = mock_connections() canonical_stream = outside_source_stream reader = mock_readers() mock_reader_builder.side_effect = [reader] reader.read.side_effect = BadConnectionError merger = Merger(mock_reader_builder, mock_delayed_stream_builder, mock_merge_strategy, canonical_stream) with pytest.raises(BadConnectionError): await merger.handle_connection(connection) mock_merge_strategy.track_stream.assert_awaited_with(reader.stream) merger.stop_accepting_connections() await merger.wait_for_ended()
async def test_merger_successful_connection(event_loop, mock_connections, data_send_mixin): conn = mock_connections() replay_data = example_replay.data data_send_mixin(conn, replay_data, 0.25, 200) merger = Merger.build(**config) await merger.handle_connection(conn) await verify_merger_ending_with_data(merger, replay_data)
async def test_merger_successful_connection(event_loop, mock_connections, mock_conn_read_data_mixin): conn = mock_connections() mock_conn_read_data_mixin(conn, example_replay.data, 0.25, 200) merger = Merger.build(merger_config(merger_dict), delay_config(delay_dict)) await merger.handle_connection(conn) await verify_merger_ending_with_data(merger, example_replay.data)
async def test_merger_one_connection_lifetime(outside_source_stream, mock_merge_strategy, mock_reader_builder, mock_delayed_stream_builder, mock_readers, mock_connections): connection = mock_connections() reader = mock_readers() mock_reader_builder.side_effect = [reader] canonical_stream = outside_source_stream merger = Merger(mock_reader_builder, mock_delayed_stream_builder, mock_merge_strategy, canonical_stream) await merger.handle_connection(connection) reader.read.assert_awaited() mock_merge_strategy.track_stream.assert_awaited_with(reader.stream) merger.stop_accepting_connections() await merger.wait_for_ended()
async def test_merger_incomplete_header(event_loop, mock_connections, data_send_mixin): conn = mock_connections() replay_data = example_replay.header_data[:-100] data_send_mixin(conn, replay_data, 0.25, 200) merger = Merger.build(**config) with pytest.raises(MalformedDataError): await merger.handle_connection(conn) await verify_merger_ending_with_data(merger, None)
async def test_merger_connection_extends_grace_period( outside_source_stream, mock_merge_strategy, mock_stream_builder, mock_connection_streams, mock_connections, event_loop): connection = mock_connections() connection_stream = mock_connection_streams() mock_stream_builder.side_effect = [connection_stream] canonical_stream = outside_source_stream connection_stream.ended.return_value = True merger = Merger(mock_stream_builder, 0.03, mock_merge_strategy, canonical_stream) f = asyncio.ensure_future(merger.wait_for_ended()) await asyncio.sleep(0.02) await merger.handle_connection(connection) await asyncio.sleep(0.02) exhaust_callbacks(event_loop) assert not f.done() await f
async def test_merger_no_connection_wait_active_connection( outside_source_stream, mock_merge_strategy, mock_reader_builder, mock_delayed_stream_builder, mock_readers, mock_connections, event_loop): connection = mock_connections() reader = mock_readers() mock_reader_builder.side_effect = [reader] canonical_stream = outside_source_stream async def long_read(): await asyncio.sleep(0.05) return reader.read.side_effect = long_read merger = Merger(mock_reader_builder, mock_delayed_stream_builder, mock_merge_strategy, canonical_stream) f = asyncio.ensure_future(merger.no_connections_for(0.01)) h = asyncio.ensure_future(merger.handle_connection(connection)) await asyncio.sleep(0.02) exhaust_callbacks(event_loop) assert not f.done() await h exhaust_callbacks(event_loop) assert not f.done() await asyncio.sleep(0.02) exhaust_callbacks(event_loop) assert f.done() await f merger.stop_accepting_connections() await merger.wait_for_ended()
async def test_merger_closes_fast(event_loop, mock_connections, data_send_mixin): conn = mock_connections() replay_data = example_replay.data data_send_mixin(conn, replay_data, 0.6, 160) merger = Merger.build(**config) f = asyncio.ensure_future(merger.handle_connection(conn)) await asyncio.sleep(45) conn.read.side_effect = lambda _: b"" # Simulate connection.close() merger.close() await asyncio.wait_for(merger.wait_for_ended(), 1) await asyncio.wait_for(f, 1)
async def test_merger_ends(event_loop, mock_connections, data_send_mixin): conn_1 = mock_connections() conn_2 = mock_connections() replay_data = example_replay.data data_send_mixin(conn_1, replay_data, 0.6, 160) data_send_mixin(conn_2, replay_data, 0.6, 160) merger = Merger.build(**config) await merger.handle_connection(conn_1) await asyncio.sleep(45) with pytest.raises(CannotAcceptConnectionError): await merger.handle_connection(conn_2) await verify_merger_ending_with_data(merger, replay_data)
async def test_merger_sequential_connections(event_loop, mock_connections, data_send_mixin): conn_1 = mock_connections() conn_2 = mock_connections() replay_data = example_replay.data # First has incomplete data, but sends faster data_send_mixin(conn_1, replay_data[:-100], 0.4, 160) data_send_mixin(conn_2, replay_data, 0.6, 160) merger = Merger.build(**config) await merger.handle_connection(conn_1) await asyncio.sleep(15) await merger.handle_connection(conn_2) await verify_merger_ending_with_data(merger, replay_data)
async def test_merger_two_connections(event_loop, mock_connections, data_send_mixin): conn_1 = mock_connections() conn_2 = mock_connections() replay_data = example_replay.data # First has incomplete data, but sends faster data_send_mixin(conn_1, replay_data[:-100], 0.4, 160) data_send_mixin(conn_2, replay_data, 0.6, 160) merger = Merger.build(**config) f_1 = asyncio.ensure_future(merger.handle_connection(conn_1)) f_2 = asyncio.ensure_future(merger.handle_connection(conn_2)) await f_1 await f_2 await verify_merger_ending_with_data(merger, replay_data)
async def test_merger_refuses_conns(event_loop, mock_connections, mock_conn_read_data_mixin): conn_1 = mock_connections() conn_2 = mock_connections() replay_data = example_replay.data mock_conn_read_data_mixin(conn_1, replay_data, 0.6, 160) mock_conn_read_data_mixin(conn_2, replay_data, 0.6, 160) merger = Merger.build(merger_config(merger_dict), delay_config(delay_dict)) await merger.handle_connection(conn_1) merger.stop_accepting_connections() with pytest.raises(CannotAcceptConnectionError): await merger.handle_connection(conn_2) await verify_merger_ending_with_data(merger, example_replay.data)
async def test_merger_incomplete_header_then_data(event_loop, mock_connections, mock_conn_read_data_mixin): conn_1 = mock_connections() conn_2 = mock_connections() replay_data = example_replay.header_data[:-100] mock_conn_read_data_mixin(conn_1, replay_data, 0.25, 200) mock_conn_read_data_mixin(conn_2, example_replay.data, 0.25, 200) merger = Merger.build(merger_config(merger_dict), delay_config(delay_dict)) with pytest.raises(MalformedDataError): await merger.handle_connection(conn_1) await merger.handle_connection(conn_2) await verify_merger_ending_with_data(merger, example_replay.data)
async def test_merger_read_header_exception(outside_source_stream, mock_merge_strategy, mock_stream_builder, mock_connection_streams, mock_connections): connection = mock_connections() connection_stream = mock_connection_streams() mock_stream_builder.side_effect = [connection_stream] canonical_stream = outside_source_stream connection_stream.read_header.side_effect = BadConnectionError merger = Merger(mock_stream_builder, 0.01, mock_merge_strategy, canonical_stream) with pytest.raises(BadConnectionError): await merger.handle_connection(connection) mock_merge_strategy.stream_added.assert_called_with(connection_stream) mock_merge_strategy.stream_removed.assert_called_with(connection_stream) mock_merge_strategy.new_header.assert_not_called() await merger.wait_for_ended()
async def test_merger_one_connection_lifetime(outside_source_stream, mock_merge_strategy, mock_stream_builder, mock_connection_streams, mock_connections): connection = mock_connections() connection_stream = mock_connection_streams() mock_stream_builder.side_effect = [connection_stream] canonical_stream = outside_source_stream conn_data = b"" async def mock_read(): nonlocal conn_data conn_data = b"foo" def mock_ended(): return conn_data == b"foo" connection_stream.read_header.return_value = "Header" connection_stream.read.side_effect = mock_read connection_stream.ended.side_effect = mock_ended merger = Merger(mock_stream_builder, 0.01, mock_merge_strategy, canonical_stream) await merger.handle_connection(connection) connection_stream.read_header.assert_called() connection_stream.read.assert_called() mock_merge_strategy.stream_added.assert_called_with(connection_stream) mock_merge_strategy.stream_removed.assert_called_with(connection_stream) mock_merge_strategy.new_header.assert_called_with(connection_stream) mock_merge_strategy.new_data.assert_called_with(connection_stream) await asyncio.sleep(0.02) mock_merge_strategy.finalize.assert_called()
def build(cls, game_id, bookkeeper, config): merger = Merger.build(config.merge, config.delay) sender = Sender.build(merger.canonical_stream) return cls(merger, sender, bookkeeper, config, game_id)
def test_merger_init(): Merger.build(**config)
def test_merger_init(): Merger.build(merger_config(merger_dict), delay_config(delay_dict))
def build(cls, game_id, bookkeeper, *, config_replay_forced_end_time, **kwargs): merger = Merger.build(**kwargs) sender = Sender.build(merger.canonical_stream, **kwargs) return cls(merger, sender, bookkeeper, config_replay_forced_end_time, game_id)