"""Tests for the filters.
"""
from pathlib import Path

import pytest

from mutatest.filters import CategoryCodeFilter, CoverageFilter
from mutatest.transformers import CATEGORIES


####################################################################################################
# COVERAGE FILTER TESTS
####################################################################################################


@pytest.fixture(scope="module")
def mock_CoverageFilter(mock_coverage_file):
    """Mock CoverageFilter on the mock_coverage_file defined in conftest."""
    print(mock_coverage_file)
    return CoverageFilter(coverage_file=mock_coverage_file)


def test_coverage_file_property(mock_CoverageFilter, mock_coverage_file):
    """Coverage file property should be set from initialization in the mock."""
    covfn = mock_CoverageFilter.coverage_file
    assert covfn == mock_coverage_file


@pytest.mark.parametrize("fn", ["doesntexist", Path("doesntexist")], ids=["str", "path"])
def test_unset_coverage_file(fn):
    """Accessing coverage data on an unset file raises FileNotFoundError"""
    ccf = CoverageFilter(coverage_file=fn)
    with pytest.raises(FileNotFoundError):
        _ = ccf.coverage_data


@pytest.mark.coverage
@pytest.mark.parametrize(
    "invert, expected",
    [(False, [1, 2, 4]), (True, [3, 5])],
    ids=["not_inverted_filter", "inverted_filter"],
)
def test_filter(mock_CoverageFilter, mock_source_and_targets, invert, expected):
    """Coverage filter inverted and not inverted set filtering."""
    # use resolve_source=False for the mocks of relative file names.
    results = mock_CoverageFilter.filter(
        mock_source_and_targets.targets,
        mock_source_and_targets.source_file,
        invert=invert,
        resolve_source=False,
    )
    result_ln = sorted([r.lineno for r in results])
    assert result_ln == expected

    # With an invalid file it will be "all or none" results
    results = mock_CoverageFilter.filter(
        mock_source_and_targets.targets, "invalidfile", invert=invert
    )

    result_ln = sorted([r.lineno for r in results])
    if invert:
        assert result_ln == [1, 2, 3, 4, 5]
    else:
        assert result_ln == []


####################################################################################################
# CATEGORY CODES FILTER TESTS
####################################################################################################


def test_CategoryCodeFilter_properties():
    """Properties of the category codes filter"""
    test_codes = ["if", "bn"]
    ccf = CategoryCodeFilter(codes=test_codes)

    assert ccf.codes == set(test_codes)
    assert ccf.codes.issubset(ccf.valid_codes)

    ccf.add_code("ix")
    assert ccf.codes == set(test_codes + ["ix"])

    # "if" and "ix" combindation
    ccf.discard_code("bn")
    assert ccf.codes == {"if", "ix"}
    assert ccf.valid_mutations == {
        "If_True",
        "If_False",
        "If_Statement",
        "Index_NumPos",
        "Index_NumNeg",
        "Index_NumZero",
    }

    assert ccf.valid_categories == CATEGORIES


@pytest.mark.parametrize("invert", [True, False])
@pytest.mark.parametrize("ast_class", ["BinOp", "AugAssign"])
def test_CategoryCodeFilter_filter(ast_class, invert, augassign_expected_locs, binop_expected_locs):

    all_locs = set(list(augassign_expected_locs) + list(binop_expected_locs))

    ccf = CategoryCodeFilter(codes=(CATEGORIES[ast_class],))
    result = ccf.filter(all_locs, invert=invert)

    for r in result:
        if invert:
            assert r.ast_class != ast_class
        else:
            assert r.ast_class == ast_class

    ccf.codes = set()
    result = ccf.filter(set(all_locs), invert=invert)
    assert result == all_locs


def test_invalid_code():
    """Invalid codes will raise a TypeError."""
    ccf = CategoryCodeFilter()
    with pytest.raises(ValueError):
        ccf.add_code("asdf")