# Copyright 2019 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Tests for pw_module.check."""

import logging
import pathlib
import shutil
import tempfile
import unittest

import pw_module.check

_LOG = logging.getLogger(__name__)


class TestWithTempDirectory(unittest.TestCase):
    """Tests for pw_module.check."""

    def setUp(self):
        # Create a temporary directory for the test.
        self.test_dir = tempfile.mkdtemp()

    def tearDown(self):
        # Remove it after the test.
        shutil.rmtree(self.test_dir)

    def create_file(self, path, contents=''):
        """Create a file and any directories assuming '/' path separator"""
        full_file_path = pathlib.Path(self.test_dir, path)
        if full_file_path.exists():
            raise Exception(f'File exists already: {path}')

        # Make parent directories if they don't exsit.
        full_file_path.parent.mkdir(parents=True, exist_ok=True)

        with open(full_file_path, 'w') as fd:
            fd.write(contents)

        return full_file_path

    def assert_no_issues(self, checker, directory=None):
        if directory is not None:
            directory = str(pathlib.Path(self.test_dir, directory))
        else:
            directory = self.test_dir
        return self.assertFalse(list(checker(directory)))

    def assert_issue(self, checker, match, directory=None):
        if directory is not None:
            directory = str(pathlib.Path(self.test_dir, directory))
        else:
            directory = self.test_dir
        issues = list(checker(directory))
        self.assertTrue(any((match in issue.message) for issue in issues))

    # Have Python code --> have setup.py.
    def test_pwck001_have_setup_py(self):
        # Python files; no setup --> error.
        self.create_file('pw_foo/py/pw_foo/__init__.py')
        self.create_file('pw_foo/py/pw_foo/bar.py')
        self.assert_issue(
            pw_module.check.check_python_proper_module, 'setup.py'
        )

        # Python files; have setup.py --> ok.
        self.create_file('pw_foo/py/setup.py')
        self.assert_no_issues(pw_module.check.check_python_proper_module)

    # Have C++ code --> have C++ tests.
    def test_pwck002_have_python_tests(self):
        self.create_file('pw_foo/public/foo.h')
        self.create_file('pw_foo/foo.cc')
        self.assert_issue(pw_module.check.check_have_cc_tests, 'tests')

        self.create_file('pw_foo/foo_test.cc')
        self.assert_no_issues(pw_module.check.check_have_cc_tests)

    # Have Python code --> have Python tests.
    def test_pwck003_have_python_tests(self):
        self.create_file('pw_foo/py/pw_foo/__init__.py')
        self.create_file('pw_foo/py/setup.py')
        self.assert_issue(pw_module.check.check_have_python_tests, 'tests')

        self.create_file('pw_foo/py/foo_test.py')
        self.assert_no_issues(pw_module.check.check_have_python_tests)

    # Have ReST docs of some kind
    def test_pwck004_have_rst_docs(self):
        checker = pw_module.check.check_has_rst_docs
        self.assert_issue(checker, 'ReST')
        self.create_file('pw_foo/docs.rst')
        self.assert_no_issues(checker)

    # Have ReST docs of some kind
    def test_pwck005_have_public_or_override_headers(self):
        checker = pw_module.check.check_has_public_or_override_headers
        module_name = 'pw_foo'

        # Only have a doc? Great.
        self.create_file('pw_foo/docs.rst')
        self.assert_no_issues(checker, directory=module_name)

        # CC files with no public header --> error.
        self.create_file('pw_foo/implementation.cc')
        self.create_file('pw_foo/implementation_test.cc')
        self.assert_issue(checker, 'public/pw_foo', directory=module_name)

        # CC files with public header in unmatched module folder --> error.
        bad_header = self.create_file('pw_foo/public/wrong/foo.h')
        self.assert_issue(checker, 'public/pw_foo', directory=module_name)

        # Remove the "bad" header.
        bad_header_parent = bad_header.parent
        bad_header.unlink()
        bad_header_parent.rmdir()

        # Finally create the correct header.
        self.create_file('pw_foo/public/pw_foo/baz.h')
        self.assert_no_issues(checker, directory=module_name)

        # Reject if there are multiple directories in public/...
        self.create_file('pw_foo/public/fake/fake.h')
        self.assert_issue(checker, 'multiple', directory=module_name)


if __name__ == '__main__':
    import sys

    logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
    unittest.main()
