From 3bcc674518d70493495cf963ac2a6691a306b643 Mon Sep 17 00:00:00 2001 From: Romuald Bierbasz Date: Fri, 2 Aug 2019 12:28:13 +0200 Subject: [PATCH] Use MagicMocks with async_return_value and pytest.mark.asyncio --- src/galaxy/unittest/mock.py | 9 +- tests/__init__.py | 1 + tests/conftest.py | 15 +- tests/test_achievements.py | 21 +-- tests/test_authenticate.py | 115 +++++++------- tests/test_chunk_messages.py | 29 ++-- tests/test_friends.py | 112 ++++++------- tests/test_game_times.py | 57 +++---- tests/test_install_game.py | 16 +- tests/test_internal.py | 95 ++++++----- tests/test_launch_game.py | 16 +- tests/test_local_games.py | 124 ++++++++------- tests/test_owned_games.py | 211 +++++++++++++------------ tests/test_persistent_cache.py | 70 ++++---- tests/test_shutdown_platform_client.py | 10 +- tests/test_stream_line_reader.py | 64 ++++---- tests/test_uninstall_game.py | 15 +- 17 files changed, 505 insertions(+), 475 deletions(-) diff --git a/src/galaxy/unittest/mock.py b/src/galaxy/unittest/mock.py index a3051cc..d5fc0af 100644 --- a/src/galaxy/unittest/mock.py +++ b/src/galaxy/unittest/mock.py @@ -3,17 +3,24 @@ from unittest.mock import MagicMock class AsyncMock(MagicMock): + """ + ..deprecated:: 0.45 + Use: :class:`MagicMock` with meth:`~.async_return_value`. + """ async def __call__(self, *args, **kwargs): return super(AsyncMock, self).__call__(*args, **kwargs) def coroutine_mock(): + """ + ..deprecated:: 0.45 + Use: :class:`MagicMock` with meth:`~.async_return_value`. + """ coro = MagicMock(name="CoroutineResult") corofunc = MagicMock(name="CoroutineFunction", side_effect=asyncio.coroutine(coro)) corofunc.coro = coro return corofunc - async def skip_loop(iterations=1): for _ in range(iterations): await asyncio.sleep(0) diff --git a/tests/__init__.py b/tests/__init__.py index 2174b66..140adbd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -16,3 +16,4 @@ def get_messages(write_mock): message = json.loads(line) messages.append(message) return messages + diff --git a/tests/conftest.py b/tests/conftest.py index b819f8e..23283a9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,21 +6,19 @@ import pytest from galaxy.api.plugin import Plugin from galaxy.api.consts import Platform -from galaxy.unittest.mock import AsyncMock, coroutine_mock, skip_loop @pytest.fixture() def reader(): stream = MagicMock(name="stream_reader") - stream.read = AsyncMock() + stream.read = MagicMock() yield stream @pytest.fixture() async def writer(): stream = MagicMock(name="stream_writer") stream.write = MagicMock() - stream.drain = AsyncMock(return_value=None) + stream.drain = MagicMock() yield stream - await skip_loop(1) # drain @pytest.fixture() def read(reader): @@ -33,7 +31,7 @@ def write(writer): @pytest.fixture() def plugin(reader, writer): """Return plugin instance with all feature methods mocked""" - async_methods = ( + methods = ( "handshake_complete", "authenticate", "get_owned_games", @@ -46,17 +44,12 @@ def plugin(reader, writer): "get_friends", "get_game_time", "prepare_game_times_context", - "shutdown_platform_client" - ) - - methods = ( + "shutdown_platform_client", "shutdown", "tick" ) with ExitStack() as stack: - for method in async_methods: - stack.enter_context(patch.object(Plugin, method, new_callable=coroutine_mock)) for method in methods: stack.enter_context(patch.object(Plugin, method)) yield Plugin(Platform.Generic, "0.1", reader, writer, "token") diff --git a/tests/test_achievements.py b/tests/test_achievements.py index 311554d..6bf05ee 100644 --- a/tests/test_achievements.py +++ b/tests/test_achievements.py @@ -1,5 +1,4 @@ import json -from unittest.mock import MagicMock import pytest from pytest import raises @@ -21,17 +20,9 @@ def test_initialization_no_id_nor_name(): Achievement(unlock_time=1234567890) -# TODO replace AsyncMocks with MagicMocks in conftest and use async_return_value -@pytest.fixture() -def reader(): - stream = MagicMock(name="stream_reader") - stream.read = MagicMock() - yield stream - - @pytest.mark.asyncio async def test_get_unlocked_achievements_success(plugin, read, write): - plugin.prepare_achievements_context.coro.return_value = 5 + plugin.prepare_achievements_context.return_value = async_return_value(5) request = { "jsonrpc": "2.0", "id": "3", @@ -41,11 +32,11 @@ async def test_get_unlocked_achievements_success(plugin, read, write): } } read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)] - plugin.get_unlocked_achievements.coro.return_value = [ + plugin.get_unlocked_achievements.return_value = async_return_value([ Achievement(achievement_id="lvl10", unlock_time=1548421241), Achievement(achievement_name="Got level 20", unlock_time=1548422395), Achievement(achievement_id="lvl30", achievement_name="Got level 30", unlock_time=1548495633) - ] + ]) await plugin.run() plugin.prepare_achievements_context.assert_called_with(["14"]) plugin.get_unlocked_achievements.assert_called_with("14", 5) @@ -92,6 +83,7 @@ async def test_get_unlocked_achievements_success(plugin, read, write): (KeyError, 0, "Unknown error") ]) async def test_get_unlocked_achievements_error(exception, code, message, plugin, read, write): + plugin.prepare_achievements_context.return_value = async_return_value(None) request = { "jsonrpc": "2.0", "id": "3", @@ -102,7 +94,7 @@ async def test_get_unlocked_achievements_error(exception, code, message, plugin, } read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)] - plugin.get_unlocked_achievements.coro.side_effect = exception + plugin.get_unlocked_achievements.side_effect = exception await plugin.run() plugin.get_unlocked_achievements.assert_called() @@ -132,7 +124,7 @@ async def test_get_unlocked_achievements_error(exception, code, message, plugin, @pytest.mark.asyncio async def test_prepare_get_unlocked_achievements_context_error(plugin, read, write): - plugin.prepare_achievements_context.coro.side_effect = BackendError() + plugin.prepare_achievements_context.side_effect = BackendError() request = { "jsonrpc": "2.0", "id": "3", @@ -158,6 +150,7 @@ async def test_prepare_get_unlocked_achievements_context_error(plugin, read, wri @pytest.mark.asyncio async def test_import_in_progress(plugin, read, write): + plugin.prepare_achievements_context.return_value = async_return_value(None) requests = [ { "jsonrpc": "2.0", diff --git a/tests/test_authenticate.py b/tests/test_authenticate.py index 1d84c60..e43d59c 100644 --- a/tests/test_authenticate.py +++ b/tests/test_authenticate.py @@ -1,6 +1,3 @@ -import asyncio -import json - import pytest from galaxy.api.types import Authentication @@ -8,29 +5,36 @@ from galaxy.api.errors import ( UnknownError, InvalidCredentials, NetworkError, LoggedInElsewhere, ProtocolError, BackendNotAvailable, BackendTimeout, BackendError, TemporaryBlocked, Banned, AccessDenied ) +from galaxy.unittest.mock import async_return_value -def test_success(plugin, read, write): +from tests import create_message, get_messages + + +@pytest.mark.asyncio +async def test_success(plugin, read, write): request = { "jsonrpc": "2.0", "id": "3", "method": "init_authentication" } - - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.authenticate.coro.return_value = Authentication("132", "Zenek") - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek")) + await plugin.run() plugin.authenticate.assert_called_with() - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "3", - "result": { - "user_id": "132", - "user_name": "Zenek" + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "result": { + "user_id": "132", + "user_name": "Zenek" + } } - } + ] + +@pytest.mark.asyncio @pytest.mark.parametrize("error,code,message", [ pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"), pytest.param(BackendNotAvailable, 2, "Backend not available", id="backend_not_available"), @@ -44,29 +48,32 @@ def test_success(plugin, read, write): pytest.param(Banned, 105, "Banned", id="banned"), pytest.param(AccessDenied, 106, "Access denied", id="access_denied"), ]) -def test_failure(plugin, read, write, error, code, message): +async def test_failure(plugin, read, write, error, code, message): request = { "jsonrpc": "2.0", "id": "3", "method": "init_authentication" } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.authenticate.coro.side_effect = error() - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + plugin.authenticate.side_effect = error() + await plugin.run() plugin.authenticate.assert_called_with() - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "3", - "error": { - "code": code, - "message": message + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "error": { + "code": code, + "message": message + } } - } + ] -def test_stored_credentials(plugin, read, write): + +@pytest.mark.asyncio +async def test_stored_credentials(plugin, read, write): request = { "jsonrpc": "2.0", "id": "3", @@ -77,39 +84,37 @@ def test_stored_credentials(plugin, read, write): } } } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.authenticate.coro.return_value = Authentication("132", "Zenek") - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek")) + await plugin.run() plugin.authenticate.assert_called_with(stored_credentials={"token": "ABC"}) write.assert_called() -def test_store_credentials(plugin, write): + +@pytest.mark.asyncio +async def test_store_credentials(plugin, write): credentials = { "token": "ABC" } + plugin.store_credentials(credentials) - async def couritine(): - plugin.store_credentials(credentials) + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "store_credentials", + "params": credentials + } + ] - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "method": "store_credentials", - "params": credentials - } +@pytest.mark.asyncio +async def test_lost_authentication(plugin, write): + plugin.lost_authentication() -def test_lost_authentication(plugin, write): - - async def couritine(): - plugin.lost_authentication() - - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "method": "authentication_lost", - "params": None - } + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "authentication_lost", + "params": None + } + ] diff --git a/tests/test_chunk_messages.py b/tests/test_chunk_messages.py index 68a4b52..340c880 100644 --- a/tests/test_chunk_messages.py +++ b/tests/test_chunk_messages.py @@ -1,7 +1,12 @@ -import asyncio import json -def test_chunked_messages(plugin, read): +import pytest + +from galaxy.unittest.mock import async_return_value + + +@pytest.mark.asyncio +async def test_chunked_messages(plugin, read): request = { "jsonrpc": "2.0", "method": "install_game", @@ -11,11 +16,13 @@ def test_chunked_messages(plugin, read): } message = json.dumps(request).encode() + b"\n" - read.side_effect = [message[:5], message[5:], b""] - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(message[:5]), async_return_value(message[5:]), async_return_value(b"")] + await plugin.run() plugin.install_game.assert_called_with(game_id="3") -def test_joined_messages(plugin, read): + +@pytest.mark.asyncio +async def test_joined_messages(plugin, read): requests = [ { "jsonrpc": "2.0", @@ -34,12 +41,14 @@ def test_joined_messages(plugin, read): ] data = b"".join([json.dumps(request).encode() + b"\n" for request in requests]) - read.side_effect = [data, b""] - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(data), async_return_value(b"")] + await plugin.run() plugin.install_game.assert_called_with(game_id="3") plugin.launch_game.assert_called_with(game_id="3") -def test_not_finished(plugin, read): + +@pytest.mark.asyncio +async def test_not_finished(plugin, read): request = { "jsonrpc": "2.0", "method": "install_game", @@ -49,6 +58,6 @@ def test_not_finished(plugin, read): } message = json.dumps(request).encode() # no new line - read.side_effect = [message, b""] - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(message), async_return_value(b"")] + await plugin.run() plugin.install_game.assert_not_called() diff --git a/tests/test_friends.py b/tests/test_friends.py index 030f029..2917fea 100644 --- a/tests/test_friends.py +++ b/tests/test_friends.py @@ -1,90 +1,94 @@ -import asyncio -import json - from galaxy.api.types import FriendInfo from galaxy.api.errors import UnknownError +from galaxy.unittest.mock import async_return_value + +import pytest + +from tests import create_message, get_messages -def test_get_friends_success(plugin, read, write): +@pytest.mark.asyncio +async def test_get_friends_success(plugin, read, write): request = { "jsonrpc": "2.0", "id": "3", "method": "import_friends" } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.get_friends.coro.return_value = [ + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + plugin.get_friends.return_value = async_return_value([ FriendInfo("3", "Jan"), FriendInfo("5", "Ola") - ] - asyncio.run(plugin.run()) + ]) + await plugin.run() plugin.get_friends.assert_called_with() - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "3", - "result": { - "friend_info_list": [ - {"user_id": "3", "user_name": "Jan"}, - {"user_id": "5", "user_name": "Ola"} - ] + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "result": { + "friend_info_list": [ + {"user_id": "3", "user_name": "Jan"}, + {"user_id": "5", "user_name": "Ola"} + ] + } } - } + ] -def test_get_friends_failure(plugin, read, write): +@pytest.mark.asyncio +async def test_get_friends_failure(plugin, read, write): request = { "jsonrpc": "2.0", "id": "3", "method": "import_friends" } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.get_friends.coro.side_effect = UnknownError() - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + plugin.get_friends.side_effect = UnknownError() + await plugin.run() plugin.get_friends.assert_called_with() - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "3", - "error": { - "code": 0, - "message": "Unknown error", + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "error": { + "code": 0, + "message": "Unknown error", + } } - } + ] -def test_add_friend(plugin, write): +@pytest.mark.asyncio +async def test_add_friend(plugin, write): friend = FriendInfo("7", "Kuba") - async def couritine(): - plugin.add_friend(friend) + plugin.add_friend(friend) - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "method": "friend_added", - "params": { - "friend_info": {"user_id": "7", "user_name": "Kuba"} + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "friend_added", + "params": { + "friend_info": {"user_id": "7", "user_name": "Kuba"} + } } - } + ] -def test_remove_friend(plugin, write): - async def couritine(): - plugin.remove_friend("5") +@pytest.mark.asyncio +async def test_remove_friend(plugin, write): + plugin.remove_friend("5") - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "method": "friend_removed", - "params": { - "user_id": "5" + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "friend_removed", + "params": { + "user_id": "5" + } } - } + ] diff --git a/tests/test_game_times.py b/tests/test_game_times.py index f17c614..334d01a 100644 --- a/tests/test_game_times.py +++ b/tests/test_game_times.py @@ -1,6 +1,4 @@ -import asyncio -import json -from unittest.mock import MagicMock, call +from unittest.mock import call import pytest from galaxy.api.types import GameTime @@ -9,17 +7,10 @@ from galaxy.unittest.mock import async_return_value from tests import create_message, get_messages -# TODO replace AsyncMocks with MagicMocks in conftest and use async_return_value -@pytest.fixture() -def reader(): - stream = MagicMock(name="stream_reader") - stream.read = MagicMock() - yield stream - @pytest.mark.asyncio async def test_get_game_time_success(plugin, read, write): - plugin.prepare_game_times_context.coro.return_value = "abc" + plugin.prepare_game_times_context.return_value = async_return_value("abc") request = { "jsonrpc": "2.0", "id": "3", @@ -29,10 +20,10 @@ async def test_get_game_time_success(plugin, read, write): } } read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)] - plugin.get_game_time.coro.side_effect = [ - GameTime("3", 60, 1549550504), - GameTime("5", 10, None), - GameTime("7", None, 1549550502), + plugin.get_game_time.side_effect = [ + async_return_value(GameTime("3", 60, 1549550504)), + async_return_value(GameTime("5", 10, None)), + async_return_value(GameTime("7", None, 1549550502)), ] await plugin.run() plugin.get_game_time.assert_has_calls([ @@ -92,6 +83,7 @@ async def test_get_game_time_success(plugin, read, write): (KeyError, 0, "Unknown error") ]) async def test_get_game_time_error(exception, code, message, plugin, read, write): + plugin.prepare_game_times_context.return_value = async_return_value(None) request = { "jsonrpc": "2.0", "id": "3", @@ -101,7 +93,7 @@ async def test_get_game_time_error(exception, code, message, plugin, read, write } } read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)] - plugin.get_game_time.coro.side_effect = exception + plugin.get_game_time.side_effect = exception await plugin.run() plugin.get_game_time.assert_called() @@ -132,7 +124,7 @@ async def test_get_game_time_error(exception, code, message, plugin, read, write @pytest.mark.asyncio async def test_prepare_get_game_time_context_error(plugin, read, write): - plugin.prepare_game_times_context.coro.side_effect = BackendError() + plugin.prepare_game_times_context.side_effect = BackendError() request = { "jsonrpc": "2.0", "id": "3", @@ -158,6 +150,7 @@ async def test_prepare_get_game_time_context_error(plugin, read, write): @pytest.mark.asyncio async def test_import_in_progress(plugin, read, write): + plugin.prepare_game_times_context.return_value = async_return_value(None) requests = [ { "jsonrpc": "2.0", @@ -201,23 +194,21 @@ async def test_import_in_progress(plugin, read, write): ] -def test_update_game(plugin, write): +@pytest.mark.asyncio +async def test_update_game(plugin, write): game_time = GameTime("3", 60, 1549550504) + plugin.update_game_time(game_time) - async def couritine(): - plugin.update_game_time(game_time) - - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "method": "game_time_updated", - "params": { - "game_time": { - "game_id": "3", - "time_played": 60, - "last_played_time": 1549550504 + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "game_time_updated", + "params": { + "game_time": { + "game_id": "3", + "time_played": 60, + "last_played_time": 1549550504 + } } } - } + ] diff --git a/tests/test_install_game.py b/tests/test_install_game.py index 744bb1a..fb739f0 100644 --- a/tests/test_install_game.py +++ b/tests/test_install_game.py @@ -1,7 +1,12 @@ -import asyncio -import json +import pytest -def test_success(plugin, read): +from galaxy.unittest.mock import async_return_value + +from tests import create_message + + +@pytest.mark.asyncio +async def test_success(plugin, read): request = { "jsonrpc": "2.0", "method": "install_game", @@ -10,7 +15,6 @@ def test_success(plugin, read): } } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.get_owned_games.return_value = None - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + await plugin.run() plugin.install_game.assert_called_with(game_id="3") diff --git a/tests/test_internal.py b/tests/test_internal.py index ec3dd77..bfd42a0 100644 --- a/tests/test_internal.py +++ b/tests/test_internal.py @@ -1,10 +1,14 @@ -import asyncio -import json +import pytest from galaxy.api.plugin import Plugin from galaxy.api.consts import Platform +from galaxy.unittest.mock import async_return_value -def test_get_capabilites(reader, writer, read, write): +from tests import create_message, get_messages + + +@pytest.mark.asyncio +async def test_get_capabilities(reader, writer, read, write): class PluginImpl(Plugin): #pylint: disable=abstract-method async def get_owned_games(self): pass @@ -16,64 +20,75 @@ def test_get_capabilites(reader, writer, read, write): } token = "token" plugin = PluginImpl(Platform.Generic, "0.1", reader, writer, token) - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - asyncio.run(plugin.run()) - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "3", - "result": { - "platform_name": "generic", - "features": [ - "ImportOwnedGames" - ], - "token": token + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + await plugin.run() + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "result": { + "platform_name": "generic", + "features": [ + "ImportOwnedGames" + ], + "token": token + } } - } + ] -def test_shutdown(plugin, read, write): + +@pytest.mark.asyncio +async def test_shutdown(plugin, read, write): request = { "jsonrpc": "2.0", "id": "5", "method": "shutdown" } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request))] + await plugin.run() plugin.shutdown.assert_called_with() - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "5", - "result": None - } + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "5", + "result": None + } + ] -def test_ping(plugin, read, write): + +@pytest.mark.asyncio +async def test_ping(plugin, read, write): request = { "jsonrpc": "2.0", "id": "7", "method": "ping" } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - asyncio.run(plugin.run()) - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "7", - "result": None - } + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + await plugin.run() + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "7", + "result": None + } + ] -def test_tick_before_handshake(plugin, read): - read.side_effect = [b""] - asyncio.run(plugin.run()) + +@pytest.mark.asyncio +async def test_tick_before_handshake(plugin, read): + read.side_effect = [async_return_value(b"")] + await plugin.run() plugin.tick.assert_not_called() -def test_tick_after_handshake(plugin, read): + +@pytest.mark.asyncio +async def test_tick_after_handshake(plugin, read): request = { "jsonrpc": "2.0", "id": "6", "method": "initialize_cache", "params": {"data": {}} } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + await plugin.run() plugin.tick.assert_called_with() diff --git a/tests/test_launch_game.py b/tests/test_launch_game.py index 551f7cf..4ab22c6 100644 --- a/tests/test_launch_game.py +++ b/tests/test_launch_game.py @@ -1,7 +1,12 @@ -import asyncio -import json +import pytest -def test_success(plugin, read): +from galaxy.unittest.mock import async_return_value + +from tests import create_message + + +@pytest.mark.asyncio +async def test_success(plugin, read): request = { "jsonrpc": "2.0", "method": "launch_game", @@ -10,7 +15,6 @@ def test_success(plugin, read): } } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.get_owned_games.return_value = None - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + await plugin.run() plugin.launch_game.assert_called_with(game_id="3") diff --git a/tests/test_local_games.py b/tests/test_local_games.py index b53056b..74c0636 100644 --- a/tests/test_local_games.py +++ b/tests/test_local_games.py @@ -1,51 +1,55 @@ -import asyncio -import json - import pytest from galaxy.api.types import LocalGame from galaxy.api.consts import LocalGameState from galaxy.api.errors import UnknownError, FailedParsingManifest +from galaxy.unittest.mock import async_return_value -def test_success(plugin, read, write): +from tests import create_message, get_messages + + +@pytest.mark.asyncio +async def test_success(plugin, read, write): request = { "jsonrpc": "2.0", "id": "3", "method": "import_local_games" } + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - - plugin.get_local_games.coro.return_value = [ + plugin.get_local_games.return_value = async_return_value([ LocalGame("1", LocalGameState.Running), LocalGame("2", LocalGameState.Installed), LocalGame("3", LocalGameState.Installed | LocalGameState.Running) - ] - asyncio.run(plugin.run()) + ]) + await plugin.run() plugin.get_local_games.assert_called_with() - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "3", - "result": { - "local_games" : [ - { - "game_id": "1", - "local_game_state": LocalGameState.Running.value - }, - { - "game_id": "2", - "local_game_state": LocalGameState.Installed.value - }, - { - "game_id": "3", - "local_game_state": (LocalGameState.Installed | LocalGameState.Running).value - } - ] + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "result": { + "local_games" : [ + { + "game_id": "1", + "local_game_state": LocalGameState.Running.value + }, + { + "game_id": "2", + "local_game_state": LocalGameState.Installed.value + }, + { + "game_id": "3", + "local_game_state": (LocalGameState.Installed | LocalGameState.Running).value + } + ] + } } - } + ] + +@pytest.mark.asyncio @pytest.mark.parametrize( "error,code,message", [ @@ -53,44 +57,42 @@ def test_success(plugin, read, write): pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", id="failed_parsing") ], ) -def test_failure(plugin, read, write, error, code, message): +async def test_failure(plugin, read, write, error, code, message): request = { "jsonrpc": "2.0", "id": "3", "method": "import_local_games" } - - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.get_local_games.coro.side_effect = error() - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + plugin.get_local_games.side_effect = error() + await plugin.run() plugin.get_local_games.assert_called_with() - response = json.loads(write.call_args[0][0]) - assert response == { - "jsonrpc": "2.0", - "id": "3", - "error": { - "code": code, - "message": message - } - } - -def test_local_game_state_update(plugin, write): - game = LocalGame("1", LocalGameState.Running) - - async def couritine(): - plugin.update_local_game_status(game) - - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "method": "local_game_status_changed", - "params": { - "local_game": { - "game_id": "1", - "local_game_state": LocalGameState.Running.value + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "error": { + "code": code, + "message": message } } - } + ] + +@pytest.mark.asyncio +async def test_local_game_state_update(plugin, write): + game = LocalGame("1", LocalGameState.Running) + plugin.update_local_game_status(game) + + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "local_game_status_changed", + "params": { + "local_game": { + "game_id": "1", + "local_game_state": LocalGameState.Running.value + } + } + } + ] diff --git a/tests/test_owned_games.py b/tests/test_owned_games.py index f455914..64ece04 100644 --- a/tests/test_owned_games.py +++ b/tests/test_owned_games.py @@ -1,19 +1,23 @@ -import asyncio -import json +import pytest from galaxy.api.types import Game, Dlc, LicenseInfo from galaxy.api.consts import LicenseType from galaxy.api.errors import UnknownError +from galaxy.unittest.mock import async_return_value -def test_success(plugin, read, write): +from tests import create_message, get_messages + + +@pytest.mark.asyncio +async def test_success(plugin, read, write): request = { "jsonrpc": "2.0", "id": "3", "method": "import_owned_games" } + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.get_owned_games.coro.return_value = [ + plugin.get_owned_games.return_value = async_return_value([ Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)), Game( "5", @@ -23,129 +27,126 @@ def test_success(plugin, read, write): Dlc("8", "Temerian Armor Set", LicenseInfo(LicenseType.FreeToPlay, None)), ], LicenseInfo(LicenseType.SinglePurchase, None)) - ] - asyncio.run(plugin.run()) + ]) + await plugin.run() plugin.get_owned_games.assert_called_with() - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "id": "3", - "result": { - "owned_games": [ - { - "game_id": "3", - "game_title": "Doom", - "license_info": { - "license_type": "SinglePurchase" - } - }, - { - "game_id": "5", - "game_title": "Witcher 3", - "dlcs": [ - { - "dlc_id": "7", - "dlc_title": "Hearts of Stone", - "license_info": { - "license_type": "SinglePurchase" - } - }, - { - "dlc_id": "8", - "dlc_title": "Temerian Armor Set", - "license_info": { - "license_type": "FreeToPlay" - } + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "result": { + "owned_games": [ + { + "game_id": "3", + "game_title": "Doom", + "license_info": { + "license_type": "SinglePurchase" + } + }, + { + "game_id": "5", + "game_title": "Witcher 3", + "dlcs": [ + { + "dlc_id": "7", + "dlc_title": "Hearts of Stone", + "license_info": { + "license_type": "SinglePurchase" + } + }, + { + "dlc_id": "8", + "dlc_title": "Temerian Armor Set", + "license_info": { + "license_type": "FreeToPlay" + } + } + ], + "license_info": { + "license_type": "SinglePurchase" } - ], - "license_info": { - "license_type": "SinglePurchase" } - } - ] + ] + } } - } + ] -def test_failure(plugin, read, write): + +@pytest.mark.asyncio +async def test_failure(plugin, read, write): request = { "jsonrpc": "2.0", "id": "3", "method": "import_owned_games" } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.get_owned_games.coro.side_effect = UnknownError() - asyncio.run(plugin.run()) + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + plugin.get_owned_games.side_effect = UnknownError() + await plugin.run() plugin.get_owned_games.assert_called_with() - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "id": "3", - "error": { - "code": 0, - "message": "Unknown error" + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": "3", + "error": { + "code": 0, + "message": "Unknown error" + } } - } + ] -def test_add_game(plugin, write): + +@pytest.mark.asyncio +async def test_add_game(plugin, write): game = Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)) - - async def couritine(): - plugin.add_game(game) - - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "method": "owned_game_added", - "params": { - "owned_game": { - "game_id": "3", - "game_title": "Doom", - "license_info": { - "license_type": "SinglePurchase" + plugin.add_game(game) + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "owned_game_added", + "params": { + "owned_game": { + "game_id": "3", + "game_title": "Doom", + "license_info": { + "license_type": "SinglePurchase" + } } } } - } + ] -def test_remove_game(plugin, write): - async def couritine(): - plugin.remove_game("5") - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "method": "owned_game_removed", - "params": { - "game_id": "5" +@pytest.mark.asyncio +async def test_remove_game(plugin, write): + plugin.remove_game("5") + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "owned_game_removed", + "params": { + "game_id": "5" + } } - } + ] -def test_update_game(plugin, write): + +@pytest.mark.asyncio +async def test_update_game(plugin, write): game = Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)) - - async def couritine(): - plugin.update_game(game) - - asyncio.run(couritine()) - response = json.loads(write.call_args[0][0]) - - assert response == { - "jsonrpc": "2.0", - "method": "owned_game_updated", - "params": { - "owned_game": { - "game_id": "3", - "game_title": "Doom", - "license_info": { - "license_type": "SinglePurchase" + plugin.update_game(game) + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": "owned_game_updated", + "params": { + "owned_game": { + "game_id": "3", + "game_title": "Doom", + "license_info": { + "license_type": "SinglePurchase" + } } } } - } + ] diff --git a/tests/test_persistent_cache.py b/tests/test_persistent_cache.py index d2d9d8c..9aaa0f7 100644 --- a/tests/test_persistent_cache.py +++ b/tests/test_persistent_cache.py @@ -1,23 +1,28 @@ -import asyncio -import json - import pytest +from galaxy.unittest.mock import async_return_value + +from tests import create_message, get_messages + def assert_rpc_response(write, response_id, result=None): - assert json.loads(write.call_args[0][0]) == { - "jsonrpc": "2.0", - "id": str(response_id), - "result": result - } + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "id": str(response_id), + "result": result + } + ] def assert_rpc_request(write, method, params=None): - assert json.loads(write.call_args[0][0]) == { - "jsonrpc": "2.0", - "method": method, - "params": {"data": params} - } + assert get_messages(write) == [ + { + "jsonrpc": "2.0", + "method": method, + "params": {"data": params} + } + ] @pytest.fixture @@ -28,7 +33,8 @@ def cache_data(): } -def test_initialize_cache(plugin, read, write, cache_data): +@pytest.mark.asyncio +async def test_initialize_cache(plugin, read, write, cache_data): request_id = 3 request = { "jsonrpc": "2.0", @@ -36,36 +42,32 @@ def test_initialize_cache(plugin, read, write, cache_data): "method": "initialize_cache", "params": {"data": cache_data} } - read.side_effect = [json.dumps(request).encode() + b"\n"] + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] assert {} == plugin.persistent_cache - asyncio.run(plugin.run()) + await plugin.run() plugin.handshake_complete.assert_called_once_with() assert cache_data == plugin.persistent_cache assert_rpc_response(write, response_id=request_id) -def test_set_cache(plugin, write, cache_data): - async def runner(): - assert {} == plugin.persistent_cache +@pytest.mark.asyncio +async def test_set_cache(plugin, write, cache_data): + assert {} == plugin.persistent_cache - plugin.persistent_cache.update(cache_data) - plugin.push_cache() + plugin.persistent_cache.update(cache_data) + plugin.push_cache() - assert_rpc_request(write, "push_cache", cache_data) - assert cache_data == plugin.persistent_cache - - asyncio.run(runner()) + assert_rpc_request(write, "push_cache", cache_data) + assert cache_data == plugin.persistent_cache -def test_clear_cache(plugin, write, cache_data): - async def runner(): - plugin._persistent_cache = cache_data +@pytest.mark.asyncio +async def test_clear_cache(plugin, write, cache_data): + plugin._persistent_cache = cache_data - plugin.persistent_cache.clear() - plugin.push_cache() + plugin.persistent_cache.clear() + plugin.push_cache() - assert_rpc_request(write, "push_cache", {}) - assert {} == plugin.persistent_cache - - asyncio.run(runner()) + assert_rpc_request(write, "push_cache", {}) + assert {} == plugin.persistent_cache diff --git a/tests/test_shutdown_platform_client.py b/tests/test_shutdown_platform_client.py index e6e8eeb..c2dc3f1 100644 --- a/tests/test_shutdown_platform_client.py +++ b/tests/test_shutdown_platform_client.py @@ -1,7 +1,9 @@ -import json - import pytest +from galaxy.unittest.mock import async_return_value + +from tests import create_message + @pytest.mark.asyncio async def test_success(plugin, read): request = { @@ -9,7 +11,7 @@ async def test_success(plugin, read): "method": "shutdown_platform_client" } - read.side_effect = [json.dumps(request).encode() + b"\n", b""] - plugin.shutdown_platform_client.return_value = None + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] + plugin.shutdown_platform_client.return_value = async_return_value(None) await plugin.run() plugin.shutdown_platform_client.assert_called_with() diff --git a/tests/test_stream_line_reader.py b/tests/test_stream_line_reader.py index 2f81e6c..64226f5 100644 --- a/tests/test_stream_line_reader.py +++ b/tests/test_stream_line_reader.py @@ -1,52 +1,46 @@ -from unittest.mock import MagicMock - import pytest from galaxy.reader import StreamLineReader -from galaxy.unittest.mock import AsyncMock +from galaxy.unittest.mock import async_return_value + @pytest.fixture() -def stream_reader(): - reader = MagicMock() - reader.read = AsyncMock() - return reader +def stream_line_reader(reader): + return StreamLineReader(reader) -@pytest.fixture() -def read(stream_reader): - return stream_reader.read - -@pytest.fixture() -def reader(stream_reader): - return StreamLineReader(stream_reader) @pytest.mark.asyncio -async def test_message(reader, read): - read.return_value = b"a\n" - assert await reader.readline() == b"a" +async def test_message(stream_line_reader, read): + read.return_value = async_return_value(b"a\n") + assert await stream_line_reader.readline() == b"a" read.assert_called_once() -@pytest.mark.asyncio -async def test_separate_messages(reader, read): - read.side_effect = [b"a\n", b"b\n"] - assert await reader.readline() == b"a" - assert await reader.readline() == b"b" - assert read.call_count == 2 @pytest.mark.asyncio -async def test_connected_messages(reader, read): - read.return_value = b"a\nb\n" - assert await reader.readline() == b"a" - assert await reader.readline() == b"b" +async def test_separate_messages(stream_line_reader, read): + read.side_effect = [async_return_value(b"a\n"), async_return_value(b"b\n")] + assert await stream_line_reader.readline() == b"a" + assert await stream_line_reader.readline() == b"b" + assert read.call_count == 2 + + +@pytest.mark.asyncio +async def test_connected_messages(stream_line_reader, read): + read.return_value = async_return_value(b"a\nb\n") + assert await stream_line_reader.readline() == b"a" + assert await stream_line_reader.readline() == b"b" read.assert_called_once() -@pytest.mark.asyncio -async def test_cut_message(reader, read): - read.side_effect = [b"a", b"b\n"] - assert await reader.readline() == b"ab" - assert read.call_count == 2 @pytest.mark.asyncio -async def test_half_message(reader, read): - read.side_effect = [b"a", b""] - assert await reader.readline() == b"" +async def test_cut_message(stream_line_reader, read): + read.side_effect = [async_return_value(b"a"), async_return_value(b"b\n")] + assert await stream_line_reader.readline() == b"ab" + assert read.call_count == 2 + + +@pytest.mark.asyncio +async def test_half_message(stream_line_reader, read): + read.side_effect = [async_return_value(b"a"), async_return_value(b"")] + assert await stream_line_reader.readline() == b"" assert read.call_count == 2 diff --git a/tests/test_uninstall_game.py b/tests/test_uninstall_game.py index 40a316b..1ec79a7 100644 --- a/tests/test_uninstall_game.py +++ b/tests/test_uninstall_game.py @@ -1,7 +1,11 @@ -import asyncio -import json +import pytest -def test_success(plugin, read): +from galaxy.unittest.mock import async_return_value + +from tests import create_message + +@pytest.mark.asyncio +async def test_success(plugin, read): request = { "jsonrpc": "2.0", "method": "uninstall_game", @@ -9,8 +13,7 @@ def test_success(plugin, read): "game_id": "3" } } - - read.side_effect = [json.dumps(request).encode() + b"\n", b""] + read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")] plugin.get_owned_games.return_value = None - asyncio.run(plugin.run()) + await plugin.run() plugin.uninstall_game.assert_called_with(game_id="3")