import os
import platform
import stat
import time

import pytest

import audeer


@pytest.fixture(scope='function', autouse=False)
def tree(tmpdir, request):
    r"""Create file tree."""

    files = request.param
    paths = []

    for path in files:
        if os.name == 'nt':
            path = path.replace('/', os.path.sep)
        if path.endswith(os.path.sep):
            path = audeer.path(tmpdir, path)
            path = audeer.mkdir(path)
            path = path + os.path.sep
            paths.append(path)
        else:
            path = audeer.path(tmpdir, path)
            audeer.mkdir(os.path.dirname(path))
            path = audeer.touch(path)
            paths.append(path)

    yield paths


@pytest.mark.parametrize(
    'tree, root, files, archive_create, archive_extract, destination, '
    'expected',
    [
        (  # empty
            [],
            '.',
            [],
            'archive.zip',
            'archive.zip',
            '.',
            [],
        ),
        (
            [],
            '.',
            None,
            'archive.zip',
            'archive.zip',
            '.',
            [],
        ),
        (  # single file
            ['file.txt'],
            '.',
            'file.txt',
            'archive.zip',
            'archive.zip',
            '.',
            ['file.txt'],
        ),
        (  # archive folder does not exist
            ['file.txt'],
            '.',
            'file.txt',
            'sub/archive.zip',
            'sub/archive.zip',
            '.',
            ['file.txt'],
        ),
        (  # file in sub folder
            ['file.txt', 'sub/a/b/file.txt'],
            '.',
            ['sub/a/b/file.txt', 'file.txt'],
            'archive.zip',
            'archive.zip',
            '.',
            ['sub/a/b/file.txt', 'file.txt'],
        ),
        (
            ['file.txt', 'sub/a/b/file.txt'],
            '.',
            ['sub/a/b/file.txt'],
            'archive.zip',
            'archive.zip',
            '.',
            ['sub/a/b/file.txt'],
        ),
        (
            ['file.txt', 'sub/a/b/file.txt'],
            '.',
            ['file.txt'],
            'archive.zip',
            'archive.zip',
            '.',
            ['file.txt'],
        ),
        (  # include all files
            ['file.txt', 'sub/a/b/file.txt', '.hidden'],
            '.',
            None,
            'archive.zip',
            'archive.zip',
            '.',
            ['.hidden', 'file.txt', 'sub/a/b/file.txt'],
        ),
        (  # exclude empty folder
            ['file.txt', 'sub/a/b/file.txt', '.hidden', 'empty/'],
            '.',
            None,
            'archive.zip',
            'archive.zip',
            '.',
            ['.hidden', 'file.txt', 'sub/a/b/file.txt'],
        ),
        (  # tar.gz
            ['file.txt', 'sub/a/b/file.txt'],
            '.',
            ['sub/a/b/file.txt', 'file.txt'],
            'archive.tar.gz',
            'archive.tar.gz',
            '.',
            ['sub/a/b/file.txt', 'file.txt'],
        ),
        (  # root is sub folder
            ['sub/file.txt'],
            './sub',
            ['file.txt'],
            'archive.zip',
            'archive.zip',
            '.',
            ['file.txt'],
        ),
        (
            ['sub/file.txt'],
            './sub',
            None,
            'archive.zip',
            'archive.zip',
            '.',
            ['file.txt'],
        ),
        (  # destitation is sub folder
            ['file.txt'],
            '.',
            ['file.txt'],
            'archive.zip',
            'archive.zip',
            './sub',
            ['file.txt'],
        ),
        (  # relative path with ../
            ['sub/file.txt'],
            './sub',
            ['../sub/file.txt'],
            'archive.zip',
            'archive.zip',
            '.',
            ['file.txt'],
        ),
        pytest.param(  # root does not exit
            [],
            './sub',
            None,
            'archive.zip',
            'archive.zip',
            '.',
            None,
            marks=pytest.mark.xfail(raises=FileNotFoundError),
        ),
        pytest.param(  # root is not a directory
            ['file.txt'],
            'file.txt',
            None,
            'archive.zip',
            'archive.zip',
            '.',
            None,
            marks=pytest.mark.xfail(raises=NotADirectoryError),
        ),
        pytest.param(  # destination is not a directory
            ['file.txt'],
            '.',
            [],
            'archive.zip',
            'archive.zip',
            'file.txt',
            [],
            marks=pytest.mark.xfail(raises=NotADirectoryError),
        ),
        pytest.param(  # archive to be extracted does not exit
            [],
            '.',
            [],
            'archive.zip',
            'bad.zip',
            '.',
            None,
            marks=pytest.mark.xfail(raises=FileNotFoundError),
        ),
        pytest.param(  # archive to be extracted is a directory
            ['sub/'],
            '.',
            [],
            'archive.zip',
            'sub',
            '.',
            None,
            marks=pytest.mark.xfail(raises=IsADirectoryError),
        ),
        pytest.param(  # file does not exit
            [],
            '.',
            ['file.txt'],
            'archive.zip',
            'archive.zip',
            '.',
            None,
            marks=pytest.mark.xfail(raises=FileNotFoundError),
        ),
        pytest.param(  # file not below root
            ['file.txt', 'sub/'],
            './sub',
            ['../file.txt'],
            'archive.zip',
            'archive.zip',
            '.',
            None,
            marks=pytest.mark.xfail(raises=RuntimeError),
        ),
        pytest.param(  # archive type not supported
            [],
            '.',
            [],
            'archive.bad',
            'archive.zip',
            '.',
            None,
            marks=pytest.mark.xfail(raises=RuntimeError),
        ),
        pytest.param(
            ['archive.bad'],
            '.',
            [],
            'archive.zip',
            'archive.bad',
            '.',
            None,
            marks=pytest.mark.xfail(raises=RuntimeError),
        ),
        pytest.param(  # broken archive
            ['archive.zip'],
            '.',
            [],
            'archive.tar.gz',
            'archive.zip',
            '.',
            None,
            marks=pytest.mark.xfail(raises=RuntimeError),
        ),
    ],
    indirect=['tree'],
)
def test_archives(tmpdir, tree, root, files, archive_create,
                  archive_extract, destination, expected):

    root = audeer.path(tmpdir, root)
    destination = audeer.path(tmpdir, destination)
    archive_create = audeer.path(tmpdir, archive_create)
    archive_extract = audeer.path(tmpdir, archive_extract)

    if os.name == 'nt':
        if expected is not None:
            expected = [file.replace('/', os.path.sep) for file in expected]
        if isinstance(files, str):
            files = files.replace('/', os.path.sep)
        elif files is not None:
            files = [file.replace('/', os.path.sep) for file in files]

    # relative path

    audeer.create_archive(
        root,
        files,
        archive_create,
    )
    result = audeer.extract_archive(
        archive_extract,
        root,
        keep_archive=True,
    )
    assert result == expected
    for file in result:
        assert os.path.exists(audeer.path(root, file))
    assert os.path.exists(archive_extract)

    # list of archives

    result = audeer.extract_archives(
        [archive_extract, archive_extract],
        destination,
        keep_archive=True,
    )
    assert result == expected + expected
    for file in result:
        assert os.path.exists(audeer.path(root, file))
    assert os.path.exists(archive_extract)

    # absolute path

    if files is not None:
        if isinstance(files, str):
            files = audeer.path(root, files)
        else:
            files = [audeer.path(root, file) for file in files]
        audeer.create_archive(
            root,
            files,
            archive_create,
        )
        result = audeer.extract_archive(
            archive_extract,
            destination,
            keep_archive=True,
        )
        assert result == expected
        for file in result:
            assert os.path.exists(audeer.path(root, file))
        assert os.path.exists(archive_extract)

    # delete archive

    audeer.extract_archive(
        archive_extract,
        destination,
        keep_archive=False,
    )
    assert not os.path.exists(archive_extract)


@pytest.mark.parametrize('path,ext,basename', [
    ('~/.bashrc', None, '.bashrc'),
    ('file.tar.gz', None, 'file.tar'),
    ('/a/c.d/g', None, 'g'),
    (b'/a/c.d/g', None, 'g'),
    ('/a/c.d/g.exe', 'exe', 'g'),
    ('../.././README.md', '.md', 'README'),
    ('folder/file.txt', None, 'file'),
    ('folder/file.txt', 'txt', 'file'),
    (b'folder/file.txt', 'txt', 'file'),
])
def test_basename_wo_ext(path, ext, basename):
    b = audeer.basename_wo_ext(path, ext=ext)
    assert b == basename
    assert type(b) is str


@pytest.mark.parametrize('dirs,expected', [
    ([], ''),
    (
        [
            '/home/user/tmp/coverage/test',
            '/home/user/tmp/covert/operator',
            '/home/user/tmp/coven/members',
        ], '/home/user/tmp',
    ),
    (
        [
            '/home/user/tmp/coverage/test',
            '/home/user/tmp/covert/operator',
            '/home/user/tmp',
        ], '/home/user/tmp',
    ),
    (
        [
            '~/tmp/coverage/test',
            '~/tmp/covert/operator',
            '~/tmp/coven/members',
        ], f'{os.path.expanduser("~")}/tmp',
    ),
    (
        [
            '/etc/tmp/coverage/test',
            '/home/user/tmp/covert/operator',
            '/home/user/tmp/coven/members',
        ], '',
    ),
    (
        [
            '/home/user/tmp',
            '/home/user/tmp',
        ], '/home/user/tmp',
    ),
    (
        [
            '/home1/user/tmp',
            '/home2/user/tmp',
        ], '',
    ),
])
def test_common_directory(dirs, expected):
    common = audeer.common_directory(dirs)
    # Change paths always to Linux syntax
    _, common = os.path.splitdrive(common)
    _, expected = os.path.splitdrive(expected)
    common = common.replace('\\', '/')
    expected = expected.replace('\\', '/')
    # On MacOS we get a '/System/Volumes/Data' in front
    common = common.replace('/System/Volumes/Data', '')
    assert common == expected


def test_download_url(tmpdir):
    url = 'https://audeering.github.io/audeer/_static/favicon.png'
    audeer.download_url(url, tmpdir)
    audeer.download_url(url, tmpdir)
    dst = audeer.download_url(url, tmpdir, force_download=True)
    assert dst == os.path.join(tmpdir, os.path.basename(url))


@pytest.mark.parametrize('path,extension', [
    ('', ''),
    ('~/.bashrc', ''),
    ('file.tar.gz', 'gz'),
    ('/a/c.d/g', ''),
    ('/a/c.d/g.exe', 'exe'),
    ('../.././README.md', 'md'),
    (b'../.././README.md', 'md'),
    ('folder/file.txt', 'txt'),
    (b'folder/file.txt', 'txt'),
    ('test.WAV', 'WAV'),
    ('test.WaV', 'WaV'),
])
def test_file_extension(path, extension):
    ext = audeer.file_extension(path)
    assert ext == extension
    assert type(ext) is str


@pytest.mark.parametrize(
    'dir_list,expected,recursive,hidden',
    [
        ([], [], False, False),
        ([], [], True, False),
        (['a', 'b', 'c'], ['a', 'b', 'c'], False, False),
        (['a', 'b', 'c'], ['a', 'b', 'c'], True, False),
        (['a'], ['a'], False, False),
        (['a'], ['a'], True, False),
        (
            ['a', os.path.join('a', 'b'), os.path.join('a', 'b', 'c')],
            ['a', os.path.join('a', 'b'), os.path.join('a', 'b', 'c')],
            True,
            False,
        ),
        # hidden
        (['a', '.b'], ['a'], True, False),
        (['a', '.b'], ['.b', 'a'], True, True),
        (
            ['a', '.b', os.path.join('a', '.b'), os.path.join('a', '.b', 'c')],
            ['a'],
            True,
            False,
        ),
        (
            ['a', '.b', os.path.join('a', '.b'), os.path.join('a', '.b', 'c')],
            ['.b', 'a', os.path.join('a', '.b'), os.path.join('a', '.b', 'c')],
            True,
            True,
        ),
    ],
)
def test_list_dir_names(tmpdir, dir_list, expected, recursive, hidden):

    dir_tmp = tmpdir.mkdir('folder')
    directories = []
    for directory in dir_list:
        directory = os.path.join(str(dir_tmp), directory)
        directories.append(audeer.mkdir(directory))

    for directory in directories:
        assert os.path.isdir(directory)

    path = os.path.join(str(dir_tmp), '.')
    dirs = audeer.list_dir_names(
        path,
        basenames=False,
        recursive=recursive,
        hidden=hidden,
    )
    assert dirs == [audeer.path(dir_tmp, d) for d in expected]
    assert type(dirs) is list

    # test basenames
    dirs = audeer.list_dir_names(
        path,
        basenames=True,
        recursive=recursive,
        hidden=hidden,
    )
    assert dirs == expected


def test_list_dir_names_errors(tmpdir):
    with pytest.raises(NotADirectoryError):
        file = audeer.touch(audeer.path(tmpdir, 'file.txt'))
        audeer.list_dir_names(file)
    with pytest.raises(FileNotFoundError):
        audeer.list_dir_names('not-existent')


@pytest.mark.parametrize(
    'files,path,filetype,expected,recursive,hidden',
    [
        # empty
        ([], '.', '', [], False, False),
        ([], '.', '', [], True, False),
        ([], '.', 'wav', [], False, False),
        ([], '.', 'wav', [], True, False),
        # file
        (
            ['file.txt'],
            'file.txt',
            '',
            ['file.txt'],
            False,
            False,
        ),
        pytest.param(
            [os.path.join('sub', 'file.txt')],
            'file.txt',
            '',
            [],
            False,
            False,
            marks=pytest.mark.xfail(raises=NotADirectoryError),
        ),
        (
            [os.path.join('sub', 'file.txt')],
            'file.txt',
            '',
            [os.path.join('sub', 'file.txt')],
            True,
            False,
        ),
        pytest.param(
            [],
            'file',
            '',
            None,
            False,
            False,
            marks=pytest.mark.xfail(raises=NotADirectoryError),
        ),
        (
            [
                't1.wav',
                't2.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 't2.wav'),
            ],
            't1.wav',
            '',
            [os.path.join('sub', 't1.wav'), 't1.wav'],
            True,
            False,
        ),
        # folder
        (
            ['t3.ogg', 't2.wav', 't1.wav'],
            '.',
            '',
            ['t1.wav', 't2.wav', 't3.ogg'],
            False,
            False,
        ),
        (
            [
                't3.ogg',
                't2.wav',
                't1.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 't1.ogg'),
            ],
            '.',
            '',
            ['t1.wav', 't2.wav', 't3.ogg'],
            False,
            False,
        ),
        (
            [
                't3.ogg',
                't2.wav',
                't1.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 'sub', 't1.ogg'),
            ],
            '.',
            '',
            [
                os.path.join('sub', 'sub', 't1.ogg'),
                os.path.join('sub', 't1.wav'),
                't1.wav',
                't2.wav',
                't3.ogg',
            ],
            True,
            False,
        ),
        pytest.param(
            [],
            'does-not-exist',
            '',
            None,
            False,
            False,
            marks=pytest.mark.xfail(raises=NotADirectoryError),
        ),
        pytest.param(
            [],
            os.path.join('does', 'not', 'exist'),
            '',
            None,
            False,
            False,
            marks=pytest.mark.xfail(raises=NotADirectoryError),
        ),
        # filetype
        (
            [
                't3.ogg',
                't2.wav',
                't1.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 'sub', 't1.ogg'),
            ],
            '.',
            'ogg',
            ['t3.ogg'],
            False,
            False,
        ),
        (
            [
                't3.ogg',
                't2.wav',
                't1.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 'sub', 't1.ogg'),
            ],
            '.',
            'ogg',
            [
                os.path.join('sub', 'sub', 't1.ogg'),
                't3.ogg',
            ],
            True,
            False,
        ),
        (
            [
                't1.wav',
                os.path.join('sub', 't1.wav'),
            ],
            't1.wav',
            '',
            ['t1.wav'],
            False,
            False,
        ),
        # pattern
        (
            [
                't1.wav',
                't2.ogg',
                's3.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 't2.ogg'),
                os.path.join('sub', 's3.wav'),
            ],
            't*',
            '',
            [
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 't2.ogg'),
                't1.wav',
                't2.ogg'
            ],
            True,
            False,
        ),
        (
            [
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 't2.ogg'),
                os.path.join('sub', 's3.wav'),
            ],
            't*',
            '',
            [
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 't2.ogg'),
            ],
            True,
            False,
        ),
        (
            [
                't1.wav',
                't2.ogg',
                's3.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 't2.ogg'),
                os.path.join('sub', 's3.wav'),
            ],
            'x*',
            '',
            [],
            True,
            False,
        ),
        (
            [
                't1.wav',
                't2.wav',
                's1.wav',
                's2.wav',
            ],
            't?.wav',
            '',
            ['t1.wav', 't2.wav'],
            False,
            False,
        ),
        (
            [
                't1.wav',
                't2.wav',
                's1.wav',
                's2.wav',
            ],
            '[ts]1.wav',
            '',
            ['s1.wav', 't1.wav'],
            False,
            False,
        ),
        (
            [
                't1.wav',
                't2.wav',
                's1.wav',
                's.wav',
            ],
            '[ts]?.wav',
            '',
            ['s1.wav', 't1.wav', 't2.wav'],
            False,
            False,
        ),
        (
            [
                't1.wav',
                't2.wav',
                's1.wav',
                's.wav',
                'x.wav',
            ],
            '[ts]*.wav',
            '',
            ['s.wav', 's1.wav', 't1.wav', 't2.wav'],
            False,
            False,
        ),
        (
            [],
            'file*',
            '',
            [],
            False,
            False,
        ),
        (
            [],
            '?ile',
            '',
            [],
            False,
            False,
        ),
        pytest.param(
            [],
            os.path.join('does', 'not', 'exist', 'file*'),
            '',
            None,
            False,
            False,
            marks=pytest.mark.xfail(raises=NotADirectoryError),
        ),
        pytest.param(
            [],
            os.path.join('not!a[pattern'),
            '',
            None,
            False,
            False,
            marks=pytest.mark.xfail(raises=NotADirectoryError),
        ),
        # pattern + filetype
        (
            [
                't1.wav',
                't2.ogg',
                's3.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', 't2.ogg'),
                os.path.join('sub', 's3.wav'),
            ],
            't*',
            'ogg',
            [
                os.path.join('sub', 't2.ogg'),
                't2.ogg'
            ],
            True,
            False,
        ),
        # hidden
        (
            ['.file.txt'],
            '.file.txt',
            '',
            [],
            False,
            False,
        ),
        (
            ['.file.txt'],
            '.file.txt',
            '',
            ['.file.txt'],
            False,
            True,
        ),
        (
            [os.path.join('sub', '.file.txt')],
            '.file.txt',
            '',
            [],
            True,
            False,
        ),
        (
            [os.path.join('sub', '.file.txt')],
            '.file.txt',
            '',
            [os.path.join('sub', '.file.txt')],
            True,
            True,
        ),
        (
            [
                't1.wav',
                '.t2.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', '.t2.wav'),
            ],
            '',
            '',
            [
                't1.wav',
            ],
            False,
            False,
        ),
        (
            [
                't1.wav',
                '.t2.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', '.t2.wav'),
            ],
            '',
            '',
            [
                os.path.join('sub', 't1.wav'),
                't1.wav',
            ],
            True,
            False,
        ),
        (
            [
                't1.wav',
                '.t2.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', '.t2.wav'),
            ],
            '',
            '',
            [
                '.t2.wav',
                't1.wav',
            ],
            False,
            True,
        ),
        (
            [
                't1.wav',
                '.t2.wav',
                os.path.join('sub', 't1.wav'),
                os.path.join('sub', '.t2.wav'),
            ],
            '',
            '',
            [
                '.t2.wav',
                os.path.join('sub', '.t2.wav'),
                os.path.join('sub', 't1.wav'),
                't1.wav',
            ],
            True,
            True,
        ),
    ],
)
def test_list_file_names(tmpdir, files, path, filetype, expected,
                         recursive, hidden):
    dir_tmp = tmpdir.mkdir('folder')
    dir_tmp.mkdir('subfolder')
    path = os.path.join(str(dir_tmp), path)
    for file in files:
        # Create the files
        file_tmp = dir_tmp.join(file)
        audeer.mkdir(os.path.dirname(file_tmp))
        file_tmp.write('')
    f = audeer.list_file_names(
        path,
        filetype=filetype,
        basenames=False,
        recursive=recursive,
        hidden=hidden,
    )
    # test full path
    assert f == [audeer.path(dir_tmp, f) for f in expected]
    assert type(f) is list
    # test basenames
    f = audeer.list_file_names(
        path,
        filetype=filetype,
        basenames=True,
        recursive=recursive,
        hidden=hidden,
    )
    assert f == expected


def test_md5_errors():
    with pytest.raises(FileNotFoundError):
        audeer.md5('does/not/exist')


@pytest.mark.parametrize(
    'file, content, expected',
    [
        (  # empty file
            'file.txt',
            None,
            'd41d8cd98f00b204e9800998ecf8427e',
        ),
        (  # different content
            'file.txt',
            'hello world',
            '5eb63bbbe01eeed093cb22bb8f5acdc3',
        ),
        (
            'file.txt',
            'Hello World',
            'b10a8db164e0754105b7a99be72e3fe5',
        ),
        (  # different filename
            'file.TXT',
            'Hello World',
            'b10a8db164e0754105b7a99be72e3fe5',
        ),
    ],
)
def test_md5_file(tmpdir, file, content, expected):

    path = audeer.path(tmpdir, file)
    with open(path, 'w') as fp:
        if content is not None:
            fp.write(content)

    assert audeer.md5(path) == expected


@pytest.mark.parametrize(
    'tree, content, expected',
    [
        (  # empty folder
            [],
            None,
            'd41d8cd98f00b204e9800998ecf8427e',
        ),
        (  # folder with different content
            ['f'],
            None,
            '8fa14cdd754f91cc6554c9e71929cce7',
        ),
        (
            ['sub/f'],
            None,
            '1af042d5a4ec129583f6093f98f64118',
        ),
        (
            ['f', 'sub/f'],
            None,
            'b540f38948f445622adc657a757f4b0d',
        ),
        (
            ['f', 'sub/g'],
            None,
            '305107efbb15f9334d22ae4fbeec4de6',
        ),
        (
            ['f', 'sub/g'],
            'hello world',
            '47829eb8ef287d0d72e0fed9b96d258d',
        ),
        (
            ['f', 'sub/g'],
            'Hello World',
            '442d96d7c43bb18f247888408e5d6977',
        ),
        (  # with empty sub folder
            ['f', 'sub/g', 'sub/'],
            None,
            '305107efbb15f9334d22ae4fbeec4de6',
        ),
        (  # with hidden file
            ['f', 'sub/g', '.hidden'],
            None,
            '97490b233a7717aec19023e28443a1bf',
        ),
        (  # umlaute
            ['ä', 'ö', 'ü', 'ß'],
            None,
            '622165ad36122984c6b2c7ba466aa262',
        ),
    ],
    indirect=['tree'],
)
def test_md5_folder(tmpdir, tree, content, expected):

    if content is not None:
        for path in tree:
            with open(path, 'w') as fp:
                fp.write(content)

    assert audeer.md5(tmpdir) == expected


def test_mkdir(tmpdir):
    # New dir
    path = str(tmpdir.mkdir('folder1'))
    p = audeer.mkdir(path)
    assert os.path.isdir(p) is True
    assert p == path
    # Existing dir
    p = audeer.mkdir(path)
    assert os.path.isdir(p) is True
    assert p == path
    # Existing dir with content
    dir_tmp = tmpdir.mkdir('folder2')
    f = dir_tmp.join('file.txt')
    f.write('')
    path = str(dir_tmp)
    p = audeer.mkdir(path)
    assert os.path.isdir(p) is True
    assert p == path
    # Relative path
    path = str(tmpdir.mkdir('folder3'))
    current_path = os.getcwd()
    os.chdir(path)
    p = audeer.mkdir('folder4')
    os.chdir(current_path)
    assert os.path.isdir(p) is True
    assert p == os.path.join(path, 'folder4')
    # Subdirectories
    os.chdir(path)
    p = audeer.mkdir('folder5/folder6')
    os.chdir(current_path)
    assert os.path.isdir(p) is True
    assert p == os.path.join(path, 'folder5', 'folder6')
    # Path in bytes
    path = str(tmpdir.mkdir('folder7'))
    path = bytes(path, 'utf8')
    p = audeer.mkdir(path)
    assert os.path.isdir(p) is True
    assert p == path.decode('utf8')
    # Empty dir
    path = ''
    p = audeer.mkdir(path)
    assert p == path
    # Mode, see https://stackoverflow.com/a/705088
    # Default mode
    path = os.path.join(str(tmpdir.mkdir('folder8')), 'sub-folder')
    os.umask(0)
    p = audeer.mkdir(path)
    mode = stat.S_IMODE(os.stat(p).st_mode)
    expected_mode = int('777', 8)
    assert mode == expected_mode
    # Non-default modes
    # Under Windows, changing permissions does not work,
    # there we always expect 777
    path = os.path.join(str(tmpdir.mkdir('folder9')), 'sub-folder')
    os.umask(0)
    p = audeer.mkdir(path, mode=0o775)
    expected_mode = '775'
    if platform.system() == 'Windows':
        expected_mode = '777'
    mode = stat.S_IMODE(os.stat(p).st_mode)
    assert mode == int(expected_mode, 8)
    assert mode != int('755', 8)
    path = os.path.join(str(tmpdir.mkdir('folder10')), 'sub-folder')
    os.umask(0)
    p = audeer.mkdir(path, mode=0o755)
    expected_mode = '755'
    if platform.system() == 'Windows':
        expected_mode = '777'
    mode = stat.S_IMODE(os.stat(p).st_mode)
    assert mode == int(expected_mode, 8)
    assert mode != int('775', 8)


@pytest.mark.parametrize(
    'src_file, dst_file',
    [
        (
            'file1',
            'file1',
        ),
        (
            'file1',
            'file2',
        ),
    ]
)
def test_move_file(tmpdir, src_file, dst_file):

    tmp_path = str(tmpdir.mkdir('folder'))
    tmp_path = audeer.mkdir(tmp_path)

    src_path = audeer.touch(os.path.join(tmp_path, src_file))
    dst_path = os.path.join(tmp_path, dst_file)

    audeer.move_file(src_path, dst_path)

    if src_file != dst_file:
        assert not os.path.exists(src_path)
    assert os.path.exists(dst_path)


@pytest.mark.parametrize(
    'path, new_extension, ext, expected_path',
    [
        ('', '', None, ''),
        ('', 'txt', None, ''),
        ('', '', 'rst', ''),
        ('', 'txt', 'rst', ''),
        ('file', '', None, 'file'),
        ('file', 'txt', None, 'file.txt'),
        ('file.txt', 'wav', None, 'file.wav'),
        ('test/file.txt', 'wav', None, 'test/file.wav'),
        ('a/b/../file.txt', 'wav', None, 'a/b/../file.wav'),
        ('file.txt', 'wav', 'txt', 'file.wav'),
        ('file.txt', 'wav', '.txt', 'file.wav'),
        ('file.txt', '.wav', 'txt', 'file.wav'),
        ('file.txt', '.wav', '.txt', 'file.wav'),
        ('file.a.b', 'wav', 'a.b', 'file.wav'),
        ('file.a.b', 'wav', '.a.b', 'file.wav'),
        ('file', 'wav', 'ext', 'file'),
        ('file.txt', 'wav', 'ext', 'file.txt'),
        ('file.txt', 'wav', 't', 'file.txt'),
    ]
)
def test_replace_file_extension(path, new_extension, ext, expected_path):
    new_path = audeer.replace_file_extension(path, new_extension, ext=ext)
    assert new_path == expected_path


def test_rmdir(tmpdir):
    # Non existing dir
    audeer.rmdir('non-esitent')
    # Folder with file content
    dir_tmp = tmpdir.mkdir('folder')
    f = dir_tmp.join('file.txt')
    f.write('')
    path = str(dir_tmp)
    p = audeer.mkdir(path)
    with pytest.raises(NotADirectoryError):
        audeer.rmdir(os.path.join(p, 'file.txt'))
    audeer.rmdir(p)
    assert not os.path.exists(p)
    # Folder with folder content
    path = str(tmpdir.mkdir('folder'))
    p = audeer.mkdir(os.path.join(path, 'sub-folder'))
    audeer.rmdir(os.path.dirname(p))
    assert not os.path.exists(p)
    assert not os.path.exists(os.path.dirname(p))
    # Relative path
    path = str(tmpdir.mkdir('folder'))
    current_path = os.getcwd()
    os.chdir(os.path.dirname(path))
    assert os.path.exists(path)
    audeer.rmdir('folder')
    assert not os.path.exists(path)
    os.chdir(current_path)


def test_touch(tmpdir):
    path = str(tmpdir.mkdir('folder1'))
    path = audeer.mkdir(path)
    path = os.path.join(path, 'file')
    assert not os.path.exists(path)
    audeer.touch(path)
    assert os.path.exists(path)
    stat = os.stat(path)
    time.sleep(.1)
    audeer.touch(path)
    assert os.path.exists(path)
    new_stat = os.stat(path)
    assert stat.st_atime < new_stat.st_atime
    assert stat.st_mtime < new_stat.st_mtime
