From eaaca656fd6c3933c3885d505e84b5d400da7b63 Mon Sep 17 00:00:00 2001 From: davfsa Date: Wed, 7 Oct 2020 12:52:03 +0000 Subject: [PATCH 1/2] And more testing --- hikari/internal/aio.py | 5 ++-- hikari/internal/time.py | 28 +++++++++++----------- pipelines/pytest.nox.py | 3 +-- tests/hikari/events/test_message_events.py | 3 +++ tests/hikari/impl/test_entity_factory.py | 15 ++++++------ tests/hikari/internal/test_collections.py | 11 +++++++++ tests/hikari/internal/test_ux.py | 10 ++++++++ tests/hikari/test_colors.py | 3 +++ tests/hikari/test_errors.py | 6 +++++ tests/hikari/test_event_stream.py | 16 ++++++++++++- tests/hikari/test_users.py | 15 ++++++++++++ 11 files changed, 89 insertions(+), 26 deletions(-) diff --git a/hikari/internal/aio.py b/hikari/internal/aio.py index af91bf075c..a197ede126 100644 --- a/hikari/internal/aio.py +++ b/hikari/internal/aio.py @@ -176,8 +176,9 @@ async def all_of( await f except asyncio.CancelledError: pass + + gatherer.cancel() try: - gatherer.cancel() - await gatherer + await gatherer # pragma: no cover - I tried everything to make this work with coverage, but no luck :( except asyncio.CancelledError: pass diff --git a/hikari/internal/time.py b/hikari/internal/time.py index d76c651ff9..c52e49d6ea 100644 --- a/hikari/internal/time.py +++ b/hikari/internal/time.py @@ -110,20 +110,20 @@ def iso8601_datetime_string_to_datetime(datetime_str: str) -> datetime.datetime: return datetime.datetime.fromisoformat(datetime_str) -# MyPy complains if this is not present, so only do this if MyPy isn't running! -if not typing.TYPE_CHECKING: - try: - # CISO8601 is around 600x faster than modules like dateutil, which is - # going to be noticable on big bots where you are parsing hundreds of - # thousands of "joined_at" fields on users on startup. - import ciso8601 - - # Discord appears to actually use RFC-3339, which isn't a true ISO-8601 implementation, - # but somewhat of a subset with some edge cases. - # See https://tools.ietf.org/html/rfc3339#section-5.6 - iso8601_datetime_string_to_datetime = ciso8601.parse_rfc3339 # noqa: F811 redefined function - except ImportError: - pass +try: # pragma: no cover + # CISO8601 is around 600x faster than modules like dateutil, which is + # going to be noticable on big bots where you are parsing hundreds of + # thousands of "joined_at" fields on users on startup. + # + # ciso8601 doesn't have typing available, so ignore it. + import ciso8601 # type: ignore[import] + + # Discord appears to actually use RFC-3339, which isn't a true ISO-8601 implementation, + # but somewhat of a subset with some edge cases. + # See https://tools.ietf.org/html/rfc3339#section-5.6 + iso8601_datetime_string_to_datetime = ciso8601.parse_rfc3339 # noqa: F811 - Redefined function +except ImportError: + pass def discord_epoch_to_datetime(epoch: int, /) -> datetime.datetime: diff --git a/pipelines/pytest.nox.py b/pipelines/pytest.nox.py index 1eb08f79f4..83a6d51710 100644 --- a/pipelines/pytest.nox.py +++ b/pipelines/pytest.nox.py @@ -18,8 +18,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -"""Py.test integration.""" -import os +"""Pytest integration.""" import shutil from pipelines import config diff --git a/tests/hikari/events/test_message_events.py b/tests/hikari/events/test_message_events.py index f7737afa25..ea43bbc1bd 100644 --- a/tests/hikari/events/test_message_events.py +++ b/tests/hikari/events/test_message_events.py @@ -195,6 +195,9 @@ def test_guild_property(self, event): assert result is event.app.cache.get_guild.return_value event.app.cache.get_guild.assert_called_once_with(342123123) + def test_author_property(self, event): + assert event.author is event.message.member + class TestGuildMessageUpdateEvent: @pytest.fixture() diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py index 9a81120b8b..8034338cac 100644 --- a/tests/hikari/impl/test_entity_factory.py +++ b/tests/hikari/impl/test_entity_factory.py @@ -1442,13 +1442,13 @@ def test_deserialize_member_when_guild_id_already_in_role_array( # While this isn't a legitimate case based on the current behaviour of the API, we still want to cover this # to ensure no duplication occurs. member_payload = {**member_payload, "guild_id": "76543325"} - member_payload["role_ids"] = [11111, 22222, 76543325, 33333, 44444] + member_payload["roles"] = [11111, 22222, 76543325, 33333, 44444] member = entity_factory_impl.deserialize_member(member_payload) assert member.app is mock_app assert member.guild_id == 76543325 assert member.user == entity_factory_impl.deserialize_user(user_payload) assert member.nickname == "foobarbaz" - assert member.role_ids == [11111, 22222, 33333, 44444, 76543325] + assert member.role_ids == [11111, 22222, 76543325, 33333, 44444] assert member.joined_at == datetime.datetime(2015, 4, 26, 6, 26, 56, 936000, tzinfo=datetime.timezone.utc) assert member.premium_since == datetime.datetime(2019, 5, 17, 6, 26, 56, 936000, tzinfo=datetime.timezone.utc) assert member.is_deaf is False @@ -2459,18 +2459,19 @@ def test_deserialize_partial_message( def test_deserialize_partial_message_with_partial_fields(self, entity_factory_impl, message_payload): message_payload["edited_timestamp"] = None + message_payload["member"] = None partial_message = entity_factory_impl.deserialize_partial_message(message_payload) assert partial_message.edited_timestamp is None + assert partial_message.guild_id is not None + assert partial_message.member is None - def test_deserialize_partial_message_with_unset_fields(self, entity_factory_impl, mock_app, user_payload): - partial_message = entity_factory_impl.deserialize_partial_message( - {"id": 123, "channel_id": 456, "author": user_payload} - ) + def test_deserialize_partial_message_with_unset_fields(self, entity_factory_impl, mock_app): + partial_message = entity_factory_impl.deserialize_partial_message({"id": 123, "channel_id": 456}) assert partial_message.app is mock_app assert partial_message.id == 123 assert partial_message.channel_id == 456 assert partial_message.guild_id is None - assert partial_message.author is not None + assert partial_message.author is None assert partial_message.member is None assert partial_message.content is undefined.UNDEFINED assert partial_message.timestamp is undefined.UNDEFINED diff --git a/tests/hikari/internal/test_collections.py b/tests/hikari/internal/test_collections.py index 904dece6d2..ed0ff79057 100644 --- a/tests/hikari/internal/test_collections.py +++ b/tests/hikari/internal/test_collections.py @@ -355,6 +355,16 @@ def test_add_all_inserts_items(self, start_with, add_items, expect): # then assert sfs._ids.tolist() == expect + def test_add_all_when_empty_collection_passed(self): + started = [0, 122] + # given + sfs = collections.SnowflakeSet() + sfs._ids.extend(started) + # when + sfs.add_all([]) + # then + assert sfs._ids.tolist() == started + def test_clear_empties_buffer(self): # given sfs = collections.SnowflakeSet(123, 121, 999991, 121, 121, 124, 120) @@ -409,6 +419,7 @@ def test_discard(self, start_with, discard, expect): ([9, 18, 27, 36, 45, 54, 63], 63, True), ([9, 18, 27, 36, 45, 54, 63], 64, False), ([9, 18, 27, 36, 45, 54, 63], 72, False), + ([12], "12", False), ], ) def test_contains(self, start_with, look_for, expect): diff --git a/tests/hikari/internal/test_ux.py b/tests/hikari/internal/test_ux.py index eb34d9a105..3ec3df8025 100644 --- a/tests/hikari/internal/test_ux.py +++ b/tests/hikari/internal/test_ux.py @@ -251,6 +251,16 @@ def test_when_CLICOLOR_and_is_a_tty(self): assert getenv.call_count == 2 getenv.assert_has_calls([mock.call("CLICOLOR_FORCE", "0"), mock.call("CLICOLOR", "0")]) + @pytest.mark.parametrize("colorterm", ["truecolor", "24bit", "TRUECOLOR", "24BIT"]) + def test_when_COLORTERM_has_correct_value(self, colorterm): + with mock.patch.object(os, "getenv", side_effect=["0", "0", colorterm]) as getenv: + assert ux.supports_color(True, False) is True + + assert getenv.call_count == 3 + getenv.assert_has_calls( + [mock.call("CLICOLOR_FORCE", "0"), mock.call("CLICOLOR", "0"), mock.call("COLORTERM", "")] + ) + def test_when_plat_is_Pocket_PC(self): stack = contextlib.ExitStack() getenv = stack.enter_context(mock.patch.object(os, "getenv", return_value="0")) diff --git a/tests/hikari/test_colors.py b/tests/hikari/test_colors.py index 9039e50f7e..ac0ab03aa7 100644 --- a/tests/hikari/test_colors.py +++ b/tests/hikari/test_colors.py @@ -127,6 +127,8 @@ # Mixing ints and floats ("1.0, 1.0, 3", r'Expected exactly 1 decimal point "\." in blue channel'), ("1.0 2 3", r'Expected exactly 1 decimal point "\." in green channel'), + # Weird decimals out of range + ("0.1 2. 0.5", r"Expected green channel to be a decimal in the inclusive range of 0.0 and 1.0"), # Too many int digits ("0 25600 200", r"Expected 1 to 3 digits for green channel, got 5"), # Ints out of range @@ -271,6 +273,7 @@ def test_Color_to_bytes(self): @pytest.mark.parametrize( ("input", "expected_result"), [ + (colors.Color(0xFF051A), colors.Color(0xFF051A)), (0xFF051A, colors.Color(0xFF051A)), ((1, 0.5, 0), colors.Color(0xFF7F00)), ([0xFF, 0x5, 0x1A], colors.Color(0xFF051A)), diff --git a/tests/hikari/test_errors.py b/tests/hikari/test_errors.py index 59b79cbbef..e5d825ef5d 100644 --- a/tests/hikari/test_errors.py +++ b/tests/hikari/test_errors.py @@ -27,6 +27,12 @@ from hikari import intents +class TestShardCloseCode: + @pytest.mark.parametrize(("code", "expected"), [(1000, True), (1001, True), (4000, False), (4014, False)]) + def test_is_standard_property(self, code, expected): + assert errors.ShardCloseCode(code).is_standard is expected + + class TestGatewayError: @pytest.fixture() def error(self): diff --git a/tests/hikari/test_event_stream.py b/tests/hikari/test_event_stream.py index 06b75442be..30f35bfb30 100644 --- a/tests/hikari/test_event_stream.py +++ b/tests/hikari/test_event_stream.py @@ -33,6 +33,20 @@ from tests.hikari import hikari_test_helpers +@pytest.mark.asyncio +async def test__generate_weak_listener_when_method_is_None(): + def test(): + return None + + call_weak_method = event_stream._generate_weak_listener(test) + + with pytest.raises( + TypeError, + match=r"dead weak referenced subscriber method cannot be executed, try actually closing your event streamers", + ): + await call_weak_method(None) + + class TestStreamer: @pytest.fixture(scope="module") def stub_streamer(self): @@ -190,7 +204,7 @@ def test___del___for_active_stream(self): streamer._event_type = events.Event streamer._active = True - with mock.patch.object(asyncio, "ensure_future", return_value=mock_coroutine): + with mock.patch.object(asyncio, "ensure_future", side_effect=RuntimeError): with unittest.TestCase().assertLogs("hikari", level=logging.WARNING) as logging_watcher: del streamer diff --git a/tests/hikari/test_users.py b/tests/hikari/test_users.py index 18d4293aa7..bc13c6c3bf 100644 --- a/tests/hikari/test_users.py +++ b/tests/hikari/test_users.py @@ -41,6 +41,21 @@ def test_str_operator(self): assert str(premium_type) == "NITRO_CLASSIC" +class TestPartialUser: + @pytest.fixture() + def obj(self): + # ABC, so must be stubbed. + return hikari_test_helpers.mock_class_namespace(users.User, slots_=False)() + + @pytest.mark.asyncio + async def test_fetch_self(self, obj): + obj.id = 123 + obj.app = mock.AsyncMock() + + assert await obj.fetch_self() is obj.app.rest.fetch_user.return_value + obj.app.rest.fetch_user.assert_awaited_once_with(user=123) + + class TestUser: @pytest.fixture() def obj(self): From 46feb85262aa36708eaf1ba6d7f6054a86b1513e Mon Sep 17 00:00:00 2001 From: davfsa Date: Wed, 7 Oct 2020 14:53:35 +0200 Subject: [PATCH 2/2] Update "pragma: no cover" comment Co-authored-by: Nekokatt --- hikari/internal/aio.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hikari/internal/aio.py b/hikari/internal/aio.py index a197ede126..0532678ff8 100644 --- a/hikari/internal/aio.py +++ b/hikari/internal/aio.py @@ -179,6 +179,8 @@ async def all_of( gatherer.cancel() try: - await gatherer # pragma: no cover - I tried everything to make this work with coverage, but no luck :( + # coverage.py will complain that this is not fully covered, as the + # "except" block will always be hit. This is intentional. + await gatherer # pragma: no cover except asyncio.CancelledError: pass