# Path: mugen/mixins/Filterable.py
# class ContextFilter(Filter):
#     """
#     A Filter which keeps a running memory of objects to pass to the filter function
# 
#     Attributes
#     ----------
#     memory
#         A list of objects to use as memory for the given filter
# 
#     Requires the filter function to take a 'memory' parameter
#     """
# 
#     memory: List[Any] = None
# 
#     def __init__(self, function, memory: Optional[List[Any]] = None):
#         super().__init__(function)
# 
#         if memory:
#             self.memory = memory
# 
#     def __call__(self, *args, **kwargs) -> bool:
#         return self.function(*args, memory=self.memory, **kwargs)
# 
# class Filter:
#     """
#     A function used to filter an object based its sources
# 
#     Attributes
#     ----------
#     name
#         Name of the filter function
# 
#     function
#         The function to filter with
#     """
# 
#     name: str
#     function: Callable[..., bool]
# 
#     def __init__(self, function: Callable[..., bool]):
#         self.name = function.__name__
#         self.function = function
# 
#     def __repr__(self):
#         return f"<{self.__class__.__name__}: {self.name}>"
# 
#     def __call__(self, *args, **kwargs) -> bool:
#         return self.function(*args, **kwargs)
# 
# class Filterable:
#     """
#     Mixin for running filters against an object and caching the results
#     """
# 
#     passed_filters: List[Filter]
#     failed_filters: List[Filter]
# 
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
# 
#         self.passed_filters = []
#         self.failed_filters = []
# 
#     def apply_filters(
#         self, filters: List[Filter], short_circuit: bool = True
#     ) -> Tuple[List[Filter], List[Filter]]:
#         """
#         Parameters
#         ----------
#         filters
#             Filters to test
# 
#         short_circuit
#             Whether or not the function should exit early on a filter failure
# 
#         Returns
#         -------
#         True if all filters passed, false otherwise
#         """
#         passed_filters = []
#         failed_filters = []
#         for filter in filters:
#             if filter(self):
#                 passed_filters.append(filter)
#             else:
#                 failed_filters.append(filter)
# 
#                 if short_circuit:
#                     break
# 
#         return passed_filters, failed_filters


import pytest
from mugen.mixins.Filterable import ContextFilter, Filter, Filterable

def get_failing_filter_combo_short_circuit():
    return [
        get_has_majority_green_color_filter(),
        get_has_text_filter(),
        get_has_majority_red_color_filter(),
    ]


def get_passing_filter_combo():
    return [
        get_has_text_filter(),
        get_has_majority_color_filter(),
        get_is_repeat_context_filter(),
    ]


@pytest.mark.parametrize(
    "filters, expected_num_passed, expected_num_failed",
    [(get_failing_filter_combo(), 2, 1), (get_passing_filter_combo(), 3, 0)],
)
def test_apply_filters__returns_proper_number_passed_failed(
    filters, expected_num_passed, expected_num_failed
):
    passed_filters, failed_filters = Filterable().apply_filters(filters)
    assert len(passed_filters) == expected_num_passed
    assert len(failed_filters) == expected_num_failed


def test_apply_filters___tests_all_filters_when_short_circuit_is_false():

# Path: mugen/mixins/Filterable.py
# class ContextFilter(Filter):
#     """
#     A Filter which keeps a running memory of objects to pass to the filter function
# 
#     Attributes
#     ----------
#     memory
#         A list of objects to use as memory for the given filter
# 
#     Requires the filter function to take a 'memory' parameter
#     """
# 
#     memory: List[Any] = None
# 
#     def __init__(self, function, memory: Optional[List[Any]] = None):
#         super().__init__(function)
# 
#         if memory:
#             self.memory = memory
# 
#     def __call__(self, *args, **kwargs) -> bool:
#         return self.function(*args, memory=self.memory, **kwargs)
# 
# class Filter:
#     """
#     A function used to filter an object based its sources
# 
#     Attributes
#     ----------
#     name
#         Name of the filter function
# 
#     function
#         The function to filter with
#     """
# 
#     name: str
#     function: Callable[..., bool]
# 
#     def __init__(self, function: Callable[..., bool]):
#         self.name = function.__name__
#         self.function = function
# 
#     def __repr__(self):
#         return f"<{self.__class__.__name__}: {self.name}>"
# 
#     def __call__(self, *args, **kwargs) -> bool:
#         return self.function(*args, **kwargs)
# 
# class Filterable:
#     """
#     Mixin for running filters against an object and caching the results
#     """
# 
#     passed_filters: List[Filter]
#     failed_filters: List[Filter]
# 
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
# 
#         self.passed_filters = []
#         self.failed_filters = []
# 
#     def apply_filters(
#         self, filters: List[Filter], short_circuit: bool = True
#     ) -> Tuple[List[Filter], List[Filter]]:
#         """
#         Parameters
#         ----------
#         filters
#             Filters to test
# 
#         short_circuit
#             Whether or not the function should exit early on a filter failure
# 
#         Returns
#         -------
#         True if all filters passed, false otherwise
#         """
#         passed_filters = []
#         failed_filters = []
#         for filter in filters:
#             if filter(self):
#                 passed_filters.append(filter)
#             else:
#                 failed_filters.append(filter)
# 
#                 if short_circuit:
#                     break
# 
#         return passed_filters, failed_filters


# Path: tests/unit/mixins/test_Filterable.py
import pytest
from mugen.mixins.Filterable import ContextFilter, Filter, Filterable


def get_failing_filter_combo_short_circuit():
    return [
        get_has_majority_green_color_filter(),
        get_has_text_filter(),
        get_has_majority_red_color_filter(),
    ]


def get_passing_filter_combo():
    return [
        get_has_text_filter(),
        get_has_majority_color_filter(),
        get_is_repeat_context_filter(),
    ]


@pytest.mark.parametrize(
    "filters, expected_num_passed, expected_num_failed",
    [(get_failing_filter_combo(), 2, 1), (get_passing_filter_combo(), 3, 0)],
)
def test_apply_filters__returns_proper_number_passed_failed(
    filters, expected_num_passed, expected_num_failed
):
    passed_filters, failed_filters = Filterable().apply_filters(filters)
    assert len(passed_filters) == expected_num_passed
    assert len(failed_filters) == expected_num_failed


def test_apply_filters___tests_all_filters_when_short_circuit_is_false():

    passed_filters, failed_filters = Filterable().apply_filters(