Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce list assertions #1

Merged
merged 1 commit into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/expecting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
__all__ = [
'core',
'string',
'list',
]
2 changes: 1 addition & 1 deletion src/expecting/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def __eq__(self, other: Any) -> bool:
return False

def __ne__(self, other: Any) -> bool:
return False
return not self.__eq__(other)


__all__ = [
Expand Down
72 changes: 72 additions & 0 deletions src/expecting/list/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from typing import Any, Sequence

from expecting.core import Expecting

VISITED = object()


class ExpectingListUnordered(Expecting):

def __init__(self, expected: Sequence[Any], strict: bool = True):
self.strict = strict
self.expected = expected

def __repr__(self) -> str:
return f'~= {self.expected!r}'

def __eq__(self, current: Any) -> bool:
if not isinstance(current, (list, tuple)):
return False

expected = [e for e in self.expected]
current = [e for e in current]

for i, element in enumerate(expected):
if element is VISITED:
continue

count_expected = 1
count_other = 0

for j, other_element in enumerate(expected[i+1:]):
if other_element is VISITED:
continue

if element == other_element:
count_expected += 1
expected[j] = VISITED

for j, other_element in enumerate(current):
if other_element is VISITED:
continue

if element == other_element:
count_other += 1
current[j] = VISITED

if self.strict and count_expected != count_other:
return False

if count_other == 0:
return False

if self.strict:
for element in current:
if element is not VISITED:
return False

return True


def containing(elements: Sequence[Any]) -> Expecting:
return ExpectingListUnordered(elements, strict=False)


def unordered(elements: Sequence[Any]) -> Expecting:
return ExpectingListUnordered(elements, strict=True)


__all__ = [
'containing',
'unordered',
]
3 changes: 0 additions & 3 deletions src/expecting/string/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ def __eq__(self, other: Any) -> bool:
except (ValueError, TypeError):
return False

def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)


def iso8601_full() -> Expecting:
return ExpectingDateTimeFormat('%Y-%m-%dT%H:%M:%S.%f%z')
Expand Down
99 changes: 99 additions & 0 deletions tests/test_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from datetime import datetime

import pytest

import expecting.list


@pytest.mark.parametrize(
'current,expected',
(
([], []),
(tuple(), tuple()),
([1], [1]),
((1,), (1,)),
([1, 2], [1]),
([1, 2], [2]),
((1, 2), (1,)),
((1, 2), (2,)),
(['asa', 2, True, object()], [True, 2]),
(('asa', 2, True, object()), (True, 2)),
(['asa', 2, True, object()], (True, 2)),
(('asa', 2, True, object()), [True, 2]),
([True, 'asa', 2, 2, True, object()], [True, 2]),
(('asa', True, 2, 2, True, object()), (True, 2)),
([True, 'asa', 2, 2, True, object()], (True, 2)),
(('asa', True, 2, 2, True, object()), [True, 2]),
((datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%fZ'),), (expecting.string.datetime.iso8601_full(),)),
),
)
def test_list_containing_matches(current, expected) -> None:
assert current == expecting.list.containing(expected)


@pytest.mark.parametrize(
'current,expected',
(
([], [1]),
(tuple(), (1,)),
([1], [2]),
((1,), (2,)),
(['asa', 2, True, object()], [False, 1]),
(('asa', 2, True, object()), (False, 1)),
(['asa', 2, True, object()], (False, 1)),
(('asa', 2, True, object()), [False, 1]),
(['asa', 2, True, object()], [True, 1]),
(('asa', 2, True, object()), (True, 1)),
(['asa', 2, True, object()], (True, 1)),
(('asa', 2, True, object()), [True, 1]),
(['asa', 2, True, object()], [False, 2]),
(('asa', 2, True, object()), (False, 2)),
(['asa', 2, True, object()], (False, 2)),
(('asa', 2, True, object()), [False, 2]),
((datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%fZ'),), (expecting.string.datetime.iso8601_day(),)),
),
)
def test_list_containing_does_not_match(current, expected) -> None:
assert current != expecting.list.containing(expected)


@pytest.mark.parametrize(
'current,expected',
(
([], []),
(tuple(), tuple()),
([1], [1]),
((1,), (1,)),
([1, 2], [1, 2]),
((1, 2), (1, 2)),
([1, 2], [2, 1]),
((1, 2), (2, 1)),
(['asa', 2, True], ['asa', True, 2]),
(('asa', 2, True), ('asa', True, 2)),
((datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%fZ'),), (expecting.string.datetime.iso8601_full(),)),
([datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%fZ')], [expecting.string.datetime.iso8601_full()]),
),
)
def test_list_unordered_matches(current, expected) -> None:
assert current == expecting.list.unordered(expected)


@pytest.mark.parametrize(
'current,expected',
(
([], [1]),
(tuple(), (1,)),
([1], [2]),
((1,), (2,)),
([1, 2], [2, 1, 3]),
((1, 2), (2, 1, 3)),
(['asa', 2, False], ['asa', True, 2]),
(('asa', 2, False), ('asa', True, 2)),
(['asa', 2, True, 2], ['asa', True, 2]),
(('asa', 2, True, 2), ('asa', True, 2)),
((datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%fZ'),), (expecting.string.datetime.iso8601_day(),)),
([datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%fZ')], [expecting.string.datetime.iso8601_day()]),
),
)
def test_list_unordered_does_not_match(current, expected) -> None:
assert current != expecting.list.unordered(expected)
Loading