import unittest

from robot.utils.asserts import assert_equal
from robot.model import TestSuite
from robot.model.filter import Filter


class FilterBaseTest(unittest.TestCase):

    def _create_suite(self):
        self.s1 = TestSuite(name='s1')
        self.s21 = self.s1.suites.create(name='s21')
        self.s31 = self.s21.suites.create(name='s31')
        self.s31.tests.create(name='t1', tags=['t1', 's31'])
        self.s31.tests.create(name='t2', tags=['t2', 's31'])
        self.s31.tests.create(name='t3')
        self.s22 = self.s1.suites.create(name='s22')
        self.s22.tests.create(name='t1', tags=['t1', 's22', 'X'])

    def _test(self, filter, s31_tests, s22_tests):
        self._create_suite()
        self.s1.visit(filter)
        assert_equal([t.name for t in self.s31.tests], s31_tests)
        assert_equal([t.name for t in self.s22.tests], s22_tests)
        assert_equal(self.s1.test_count, len(s31_tests + s22_tests))


class TestFilterByIncludeTags(FilterBaseTest):

    def test_no_filtering(self):
        self._test(Filter(), ['t1', 't2', 't3'], ['t1'])
        self._test(Filter(include_tags=[]), ['t1', 't2', 't3'], ['t1'])

    def test_no_match(self):
        self._test(Filter(include_tags=['no', 'match']), [], [])

    def test_constant(self):
        self._test(Filter(include_tags=['t1']), ['t1'], ['t1'])

    def test_string(self):
        self._test(Filter(include_tags='t1'), ['t1'], ['t1'])

    def test_pattern(self):
        self._test(Filter(include_tags=['t*']), ['t1', 't2'], ['t1'])
        self._test(Filter(include_tags=['xxx', '?2', 's*2']), ['t2'], ['t1'])

    def test_normalization(self):
        self._test(Filter(include_tags=['T 1', '_T_2_']), ['t1', 't2'], ['t1'])

    def test_and_and_not(self):
        self._test(Filter(include_tags=['t1ANDs31']), ['t1'], [])
        self._test(Filter(include_tags=['?1ANDs*2ANDx']), [], ['t1'])
        self._test(Filter(include_tags=['t1ANDs*NOTx']), ['t1'], [])
        self._test(Filter(include_tags=['t1AND?1NOTs*ANDx']), ['t1'], [])


class TestFilterByExcludeTags(FilterBaseTest):

    def test_no_filtering(self):
        self._test(Filter(), ['t1', 't2', 't3'], ['t1'])
        self._test(Filter(exclude_tags=[]), ['t1', 't2', 't3'], ['t1'])

    def test_no_match(self):
        self._test(Filter(exclude_tags=['no', 'match']), ['t1', 't2', 't3'], ['t1'])

    def test_constant(self):
        self._test(Filter(exclude_tags=['t1']), ['t2', 't3'], [])

    def test_string(self):
        self._test(Filter(exclude_tags='t1'), ['t2', 't3'], [])

    def test_pattern(self):
        self._test(Filter(exclude_tags=['t*']), ['t3'], [])
        self._test(Filter(exclude_tags=['xxx', '?2', 's3*']), ['t3'], ['t1'])

    def test_normalization(self):
        self._test(Filter(exclude_tags=['T 1', '_T_2_']), ['t3'], [])

    def test_and_and_not(self):
        self._test(Filter(exclude_tags=['t1ANDs31']), ['t2', 't3'], ['t1'])
        self._test(Filter(exclude_tags=['?1ANDs*2ANDx']), ['t1', 't2', 't3'], [])
        self._test(Filter(exclude_tags=['t1ANDs*NOTx']), ['t2', 't3'], ['t1'])
        self._test(Filter(exclude_tags=['t1AND?1NOTs*ANDx']), ['t2', 't3'], ['t1'])


class TestFilterByTestName(FilterBaseTest):

    def test_no_filtering(self):
        self._test(Filter(), ['t1', 't2', 't3'], ['t1'])
        self._test(Filter(include_tests=[]), ['t1', 't2', 't3'], ['t1'])

    def test_no_match(self):
        self._test(Filter(include_tests=['no match']), [], [])

    def test_constant(self):
        self._test(Filter(include_tests=['t1']), ['t1'], ['t1'])
        self._test(Filter(include_tests=['t2', 'xxx']), ['t2'], [])

    def test_string(self):
        self._test(Filter(include_tests='t1'), ['t1'], ['t1'])

    def test_pattern(self):
        self._test(Filter(include_tests=['t*']), ['t1', 't2', 't3'], ['t1'])
        self._test(Filter(include_tests=['xxx', '*2', '?3']), ['t2', 't3'], [])

    def test_longname(self):
        self._test(Filter(include_tests=['s1.s21.s31.t3', 's1.s?2.*']), ['t3'], ['t1'])

    def test_normalization(self):
        self._test(Filter(include_tests=['T 1', '_T_2_']), ['t1', 't2'], ['t1'])


class TestFilterBySuiteName(FilterBaseTest):

    def test_no_filtering(self):
        self._test(Filter(), ['t1', 't2', 't3'], ['t1'])
        self._test(Filter(include_suites=[]), ['t1', 't2', 't3'], ['t1'])

    def test_no_match(self):
        self._test(Filter(include_suites=['no match']), [], [])

    def test_constant(self):
        self._test(Filter(include_suites=['s22']), [], ['t1'])
        self._test(Filter(include_suites=['s1', 'xxx']), ['t1', 't2', 't3'], ['t1'])

    def test_string(self):
        self._test(Filter(include_suites='s22'), [], ['t1'])

    def test_pattern(self):
        self._test(Filter(include_suites=['s3?']), ['t1', 't2', 't3'], [])

    def test_reuse_filter(self):
        filter = Filter(include_suites=['s22'])
        self._test(filter, [], ['t1'])
        self._test(filter, [], ['t1'])

    def test_parent_name(self):
        self._test(Filter(include_suites=['s1.s21.s31']), ['t1', 't2', 't3'], [])
        self._test(Filter(include_suites=['s2?.s31']), ['t1', 't2', 't3'], [])
        self._test(Filter(include_suites=['*.s22']), [], ['t1'])
        self._test(Filter(include_suites=['xxx.s22']), [], [])

    def test_normalization(self):
        self._test(Filter(include_suites=['_S 2 2_', 'xxx']), [], ['t1'])

    def test_with_other_filters(self):
        self._test(Filter(include_suites=['s21'], include_tests=['t1']), ['t1'], [])
        self._test(Filter(include_suites=['s22'], include_tags=['t*']), [], ['t1'])
        self._test(Filter(include_suites=['s21', 's22'], exclude_tags=['t?']), ['t3'], [])


class TestRemoveEmptySuitesDuringFilter(FilterBaseTest):

    def test_remove_empty_leaf_suite(self):
        self._test(Filter(include_tags='t2'), ['t2'], [])
        assert_equal(list(self.s1.suites), [self.s21])

    def test_remove_branch(self):
        self._test(Filter(include_suites='s22'), [], ['t1'])
        assert_equal(list(self.s1.suites), [self.s22])

    def test_remove_all(self):
        self._test(Filter(include_tests='none'), [], [])
        assert_equal(list(self.s1.suites), [])


if __name__ == '__main__':
    unittest.main()
