"""Test suite for prance.util.fs ."""

__author__ = "Jens Finkhaeuser"
__copyright__ = "Copyright (c) 2016-2021 Jens Finkhaeuser"
__license__ = "MIT"
__all__ = ()


import os
import sys

import pytest

from prance.util import fs

from . import sandbox, platform


@pytest.fixture
def create_symlink():
    # Defined as a fixture so we can selectively use it for tests."
    testname = "tests/specs/symlink_test"
    import os.path

    if os.path.islink(testname):
        return testname

    if os.path.exists(testname):
        os.unlink(testname)

    target = "with_externals.yaml"
    os.symlink(target, testname)

    return testname


@pytest.mark.skipif(platform("win32"), reason="Skip on win32")
def test_canonical(create_symlink):
    res = fs.canonical_filename(create_symlink)
    expected = os.path.join(os.getcwd(), "tests/specs/with_externals.yaml")
    assert res == expected


def test_to_posix_rel():
    test = "tests/specs/with_externals.yaml"
    assert fs.to_posix(os.path.normpath(test)) == test


@pytest.mark.skipif(platform("win32"), reason="Skip on win32")
def test_to_posix_abs_posix():
    test = "/etc/passwd"
    expected = test
    assert fs.to_posix(test) == expected


@pytest.mark.skipif(platform("!win32"), reason="Skip on !win32")
def test_to_posix_abs_win32():
    test = "c:\\windows\\notepad.exe"
    expected = "/c:/windows/notepad.exe"
    assert fs.to_posix(test) == expected


def test_from_posix_rel():
    test = "tests/specs/with_externals.yaml"
    assert fs.from_posix(test) == os.path.normpath(test)


@pytest.mark.skipif(platform("win32"), reason="Skip on win32")
def test_from_posix_abs_posix():
    test = "/etc/passwd"
    expected = test
    assert fs.from_posix(test) == expected


@pytest.mark.skipif(platform("!win32"), reason="Skip on !win32")
def test_from_posix_abs_win32():
    test = "/c:/windows/notepad.exe"
    expected = "c:\\windows\\notepad.exe"
    assert fs.from_posix(test) == expected


def test_abspath_basics():
    testname = os.path.normpath("tests/specs/with_externals.yaml")
    res = fs.abspath(testname)
    expected = fs.to_posix(os.path.join(os.getcwd(), testname))
    assert res == expected


def test_abspath_relative():
    testname = "error.json"
    relative = os.path.join(os.getcwd(), "tests/specs/with_externals.yaml")
    res = fs.abspath(testname, relative)
    expected = fs.to_posix(os.path.join(os.getcwd(), "tests", "specs", testname))
    assert res == expected


def test_abspath_relative_dir():
    testname = "error.json"
    relative = os.path.join(os.getcwd(), "tests", "specs")
    res = fs.abspath(testname, relative)
    expected = fs.to_posix(os.path.join(os.getcwd(), "tests", "specs", testname))
    assert res == expected


def test_detect_encoding():
    # Quick detection should yield utf-8 for the petstore file.
    assert fs.detect_encoding("tests/specs/petstore.yaml") == "utf-8"

    # Without defaulting to utf-8, it should also work.
    assert (
        fs.detect_encoding("tests/specs/petstore.yaml", default_to_utf8=False)
        == "utf-8"
    )

    # Deep inspection should not change anything because the file size is
    # too small.
    assert (
        fs.detect_encoding(
            "tests/specs/petstore.yaml", default_to_utf8=False, read_all=True
        )
        == "utf-8"
    )

    # The UTF-8 file with BOM should be detected properly
    assert fs.detect_encoding("tests/specs/utf8bom.yaml") == "utf-8-sig"


def test_issue_51_detect_encoding():
    # This should be UTF-8, but in issue 51 it was reported that it comes back
    # as iso-8859-2

    # Detection should be ok if the entire file is read
    assert (
        fs.detect_encoding("tests/specs/issue_51/openapi-part.yaml", read_all=True)
        == "utf-8"
    )

    # After the heuristic change, it reads the whole file anyway.
    assert fs.detect_encoding("tests/specs/issue_51/openapi-part.yaml") == "utf-8"

    # Specifically re-encoded as iso-8859-2 should fail - but not as
    # a call to the detect_encoding() function. Instead, we can only return
    # a badly detected encoding. Chardet sends iso-8859-1 here.
    assert (
        fs.detect_encoding("tests/specs/issue_51/openapi-part-iso-8859-2.yaml")
        == "iso-8859-1"
    )


def test_load_nobom():
    contents = fs.read_file("tests/specs/petstore.yaml")
    assert contents.index("Swagger Petstore") >= 0, "File reading failed!"


def test_load_utf8bom():
    contents = fs.read_file("tests/specs/utf8bom.yaml")
    assert contents.index("söme välüe") >= 0, "UTF-8 BOM handling failed!"


def test_load_utf8bom_override():
    with pytest.raises(UnicodeDecodeError):
        fs.read_file("tests/specs/utf8bom.yaml", "ascii")


def test_write_file(tmpdir):
    with sandbox.sandbox(tmpdir):
        test_text = "söme täxt"
        fs.write_file("test.out", test_text)

        # File must have been written
        files = [f for f in os.listdir(".") if os.path.isfile(f)]
        assert "test.out" in files

        # File contents must work
        contents = fs.read_file("test.out")
        assert test_text == contents


def test_write_file_bom(tmpdir):
    with sandbox.sandbox(tmpdir):
        test_text = "söme täxt"
        fs.write_file("test.out", test_text, "utf-8-sig")

        # File must have been written
        files = [f for f in os.listdir(".") if os.path.isfile(f)]
        assert "test.out" in files

        # Encoding must match the one we've given
        encoding = fs.detect_encoding("test.out")
        assert encoding == "utf-8-sig"

        # File contents must work
        contents = fs.read_file("test.out")
        assert test_text == contents


def test_valid_pathname():
    # A URL should not be valid
    from prance.util.fs import is_pathname_valid

    assert False == is_pathname_valid("\x00o.bar.org")

    # However, the current path should be.
    import os

    assert True == is_pathname_valid(os.getcwd())

    # Can't put non-strings into this function
    assert True == is_pathname_valid("foo")
    assert False == is_pathname_valid(123)

    # Can't accept too long components
    assert False == is_pathname_valid("a" * 256)
