# coding: utf-8
"""Tests for profile-related functions.

Currently only the startup-dir functionality is tested, but more tests should
be added for:

    * ipython profile create
    * ipython profile list
    * ipython profile create --parallel
    * security dir permissions

Authors
-------

* MinRK

"""

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

import shutil
import sys
import tempfile
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest import TestCase

import pytest

from IPython.core.profileapp import list_bundled_profiles, list_profiles_in
from IPython.core.profiledir import ProfileDir
from IPython.testing import decorators as dec
from IPython.testing import tools as tt
from IPython.utils.process import getoutput

#-----------------------------------------------------------------------------
# Globals
#-----------------------------------------------------------------------------
TMP_TEST_DIR = Path(tempfile.mkdtemp())
HOME_TEST_DIR = TMP_TEST_DIR / "home_test_dir"
IP_TEST_DIR = HOME_TEST_DIR / ".ipython"

#
# Setup/teardown functions/decorators
#

def setup_module():
    """Setup test environment for the module:

            - Adds dummy home dir tree
    """
    # Do not mask exceptions here.  In particular, catching WindowsError is a
    # problem because that exception is only defined on Windows...
    (Path.cwd() / IP_TEST_DIR).mkdir(parents=True)


def teardown_module():
    """Teardown test environment for the module:

            - Remove dummy home dir tree
    """
    # Note: we remove the parent test dir, which is the root of all test
    # subdirs we may have created.  Use shutil instead of os.removedirs, so
    # that non-empty directories are all recursively removed.
    shutil.rmtree(TMP_TEST_DIR)


#-----------------------------------------------------------------------------
# Test functions
#-----------------------------------------------------------------------------
class ProfileStartupTest(TestCase):
    def setUp(self):
        # create profile dir
        self.pd = ProfileDir.create_profile_dir_by_name(IP_TEST_DIR, "test")
        self.options = ["--ipython-dir", IP_TEST_DIR, "--profile", "test"]
        self.fname = TMP_TEST_DIR / "test.py"

    def tearDown(self):
        # We must remove this profile right away so its presence doesn't
        # confuse other tests.
        shutil.rmtree(self.pd.location)

    def init(self, startup_file, startup, test):
        # write startup python file
        with open(Path(self.pd.startup_dir) / startup_file, "w", encoding="utf-8") as f:
            f.write(startup)
        # write simple test file, to check that the startup file was run
        with open(self.fname, "w", encoding="utf-8") as f:
            f.write(test)

    def validate(self, output):
        tt.ipexec_validate(self.fname, output, "", options=self.options)

    def test_startup_py(self):
        self.init('00-start.py', 'zzz=123\n', 'print(zzz)\n')
        self.validate('123')

    def test_startup_ipy(self):
        self.init('00-start.ipy', '%xmode plain\n', '')
        self.validate('Exception reporting mode: Plain')


@pytest.mark.skipif(
    sys.implementation.name == "pypy"
    and ((7, 3, 13) < sys.implementation.version < (7, 3, 16)),
    reason="Unicode issues with scandir on PyPy, see https://github.com/pypy/pypy/issues/4860",
)
def test_list_profiles_in():
    # No need to remove these directories and files, as they will get nuked in
    # the module-level teardown.
    td = Path(tempfile.mkdtemp(dir=TMP_TEST_DIR))
    for name in ("profile_foo", "profile_hello", "not_a_profile"):
        Path(td / name).mkdir(parents=True)
    if dec.unicode_paths:
        Path(td / "profile_ünicode").mkdir(parents=True)

    with open(td / "profile_file", "w", encoding="utf-8") as f:
        f.write("I am not a profile directory")
    profiles = list_profiles_in(td)

    # unicode normalization can turn u'ünicode' into u'u\0308nicode',
    # so only check for *nicode, and that creating a ProfileDir from the
    # name remains valid
    found_unicode = False
    for p in list(profiles):
        if p.endswith('nicode'):
            pd = ProfileDir.find_profile_dir_by_name(td, p)
            profiles.remove(p)
            found_unicode = True
            break
    if dec.unicode_paths:
        assert found_unicode is True
    assert set(profiles) == {"foo", "hello"}


def test_list_bundled_profiles():
    # This variable will need to be updated when a new profile gets bundled
    bundled = sorted(list_bundled_profiles())
    assert bundled == []


def test_profile_create_ipython_dir():
    """ipython profile create respects --ipython-dir"""
    with TemporaryDirectory() as td:
        getoutput(
            [
                sys.executable,
                "-m",
                "IPython",
                "profile",
                "create",
                "foo",
                "--ipython-dir=%s" % td,
            ]
        )
        profile_dir = Path(td) / "profile_foo"
        assert Path(profile_dir).exists()
        ipython_config = profile_dir / "ipython_config.py"
        assert Path(ipython_config).exists()