import os
import sys
import unittest
from unittest import mock

from mkdocs.structure.files import File, Files, _filter_paths, _sort_files, get_files
from mkdocs.tests.base import PathAssertionMixin, load_config, tempdir


class TestFiles(PathAssertionMixin, unittest.TestCase):
    def test_file_eq(self):
        file = File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertTrue(
            file == File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        )

    def test_file_ne(self):
        file = File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        # Different filename
        self.assertTrue(
            file != File('b.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        )
        # Different src_path
        self.assertTrue(
            file != File('a.md', '/path/to/other', '/path/to/site', use_directory_urls=False)
        )
        # Different URL
        self.assertTrue(
            file != File('a.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        )

    @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
    def test_src_path_windows(self):
        f = File('foo\\a.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo/a.md')
        self.assertEqual(f.src_path, 'foo\\a.md')
        f.src_uri = 'foo/b.md'
        self.assertEqual(f.src_uri, 'foo/b.md')
        self.assertEqual(f.src_path, 'foo\\b.md')
        f.src_path = 'foo/c.md'
        self.assertEqual(f.src_uri, 'foo/c.md')
        self.assertEqual(f.src_path, 'foo\\c.md')
        f.src_path = 'foo\\d.md'
        self.assertEqual(f.src_uri, 'foo/d.md')
        self.assertEqual(f.src_path, 'foo\\d.md')
        f.src_uri = 'foo\\e.md'
        self.assertEqual(f.src_uri, 'foo\\e.md')
        self.assertEqual(f.src_path, 'foo\\e.md')

    def test_sort_files(self):
        self.assertEqual(
            _sort_files(['b.md', 'bb.md', 'a.md', 'index.md', 'aa.md']),
            ['index.md', 'a.md', 'aa.md', 'b.md', 'bb.md'],
        )

        self.assertEqual(
            _sort_files(['b.md', 'index.html', 'a.md', 'index.md']),
            ['index.html', 'index.md', 'a.md', 'b.md'],
        )

        self.assertEqual(
            _sort_files(['a.md', 'index.md', 'b.md', 'index.html']),
            ['index.md', 'index.html', 'a.md', 'b.md'],
        )

        self.assertEqual(
            _sort_files(['.md', '_.md', 'a.md', 'index.md', '1.md']),
            ['index.md', '.md', '1.md', '_.md', 'a.md'],
        )

        self.assertEqual(
            _sort_files(['a.md', 'b.md', 'a.md']),
            ['a.md', 'a.md', 'b.md'],
        )

        self.assertEqual(
            _sort_files(['A.md', 'B.md', 'README.md']),
            ['README.md', 'A.md', 'B.md'],
        )

    def test_md_file(self):
        f = File('foo.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo.md')
        self.assertEqual(f.dest_uri, 'foo.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo.html')
        self.assertEqual(f.url, 'foo.html')
        self.assertEqual(f.name, 'foo')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_file_use_directory_urls(self):
        f = File('foo.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'foo.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo.md')
        self.assertEqual(f.dest_uri, 'foo/index.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html')
        self.assertEqual(f.url, 'foo/')
        self.assertEqual(f.name, 'foo')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_file_nested(self):
        f = File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo/bar.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.md')
        self.assertEqual(f.dest_uri, 'foo/bar.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html')
        self.assertEqual(f.url, 'foo/bar.html')
        self.assertEqual(f.name, 'bar')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_file_nested_use_directory_urls(self):
        f = File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'foo/bar.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.md')
        self.assertEqual(f.dest_uri, 'foo/bar/index.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar/index.html')
        self.assertEqual(f.url, 'foo/bar/')
        self.assertEqual(f.name, 'bar')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_index_file(self):
        f = File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'index.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/index.md')
        self.assertEqual(f.dest_uri, 'index.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html')
        self.assertEqual(f.url, 'index.html')
        self.assertEqual(f.name, 'index')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_readme_index_file(self):
        f = File('README.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'README.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/README.md')
        self.assertEqual(f.dest_uri, 'index.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html')
        self.assertEqual(f.url, 'index.html')
        self.assertEqual(f.name, 'index')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_index_file_use_directory_urls(self):
        f = File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'index.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/index.md')
        self.assertEqual(f.dest_uri, 'index.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html')
        self.assertEqual(f.url, './')
        self.assertEqual(f.name, 'index')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_readme_index_file_use_directory_urls(self):
        f = File('README.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'README.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/README.md')
        self.assertEqual(f.dest_uri, 'index.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/index.html')
        self.assertEqual(f.url, './')
        self.assertEqual(f.name, 'index')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_index_file_nested(self):
        f = File('foo/index.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo/index.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/index.md')
        self.assertEqual(f.dest_uri, 'foo/index.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html')
        self.assertEqual(f.url, 'foo/index.html')
        self.assertEqual(f.name, 'index')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_md_index_file_nested_use_directory_urls(self):
        f = File('foo/index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'foo/index.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/index.md')
        self.assertEqual(f.dest_uri, 'foo/index.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/index.html')
        self.assertEqual(f.url, 'foo/')
        self.assertEqual(f.name, 'index')
        self.assertTrue(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_static_file(self):
        f = File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo/bar.html')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.html')
        self.assertEqual(f.dest_uri, 'foo/bar.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html')
        self.assertEqual(f.url, 'foo/bar.html')
        self.assertEqual(f.name, 'bar')
        self.assertFalse(f.is_documentation_page())
        self.assertTrue(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_static_file_use_directory_urls(self):
        f = File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'foo/bar.html')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.html')
        self.assertEqual(f.dest_uri, 'foo/bar.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.html')
        self.assertEqual(f.url, 'foo/bar.html')
        self.assertEqual(f.name, 'bar')
        self.assertFalse(f.is_documentation_page())
        self.assertTrue(f.is_static_page())
        self.assertFalse(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_media_file(self):
        f = File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo/bar.jpg')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.jpg')
        self.assertEqual(f.dest_uri, 'foo/bar.jpg')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.jpg')
        self.assertEqual(f.url, 'foo/bar.jpg')
        self.assertEqual(f.name, 'bar')
        self.assertFalse(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertTrue(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_media_file_use_directory_urls(self):
        f = File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'foo/bar.jpg')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.jpg')
        self.assertEqual(f.dest_uri, 'foo/bar.jpg')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.jpg')
        self.assertEqual(f.url, 'foo/bar.jpg')
        self.assertEqual(f.name, 'bar')
        self.assertFalse(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertTrue(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_javascript_file(self):
        f = File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo/bar.js')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.js')
        self.assertEqual(f.dest_uri, 'foo/bar.js')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.js')
        self.assertEqual(f.url, 'foo/bar.js')
        self.assertEqual(f.name, 'bar')
        self.assertFalse(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertTrue(f.is_media_file())
        self.assertTrue(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_javascript_file_use_directory_urls(self):
        f = File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'foo/bar.js')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.js')
        self.assertEqual(f.dest_uri, 'foo/bar.js')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.js')
        self.assertEqual(f.url, 'foo/bar.js')
        self.assertEqual(f.name, 'bar')
        self.assertFalse(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertTrue(f.is_media_file())
        self.assertTrue(f.is_javascript())
        self.assertFalse(f.is_css())

    def test_css_file(self):
        f = File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo/bar.css')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.css')
        self.assertEqual(f.dest_uri, 'foo/bar.css')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.css')
        self.assertEqual(f.url, 'foo/bar.css')
        self.assertEqual(f.name, 'bar')
        self.assertFalse(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertTrue(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertTrue(f.is_css())

    def test_css_file_use_directory_urls(self):
        f = File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertEqual(f.src_uri, 'foo/bar.css')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo/bar.css')
        self.assertEqual(f.dest_uri, 'foo/bar.css')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo/bar.css')
        self.assertEqual(f.url, 'foo/bar.css')
        self.assertEqual(f.name, 'bar')
        self.assertFalse(f.is_documentation_page())
        self.assertFalse(f.is_static_page())
        self.assertTrue(f.is_media_file())
        self.assertFalse(f.is_javascript())
        self.assertTrue(f.is_css())

    def test_file_name_with_space(self):
        f = File('foo bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        self.assertEqual(f.src_uri, 'foo bar.md')
        self.assertPathsEqual(f.abs_src_path, '/path/to/docs/foo bar.md')
        self.assertEqual(f.dest_uri, 'foo bar.html')
        self.assertPathsEqual(f.abs_dest_path, '/path/to/site/foo bar.html')
        self.assertEqual(f.url, 'foo%20bar.html')
        self.assertEqual(f.name, 'foo bar')

    def test_files(self):
        fs = [
            File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True),
        ]
        files = Files(fs)
        self.assertEqual([f for f in files], fs)
        self.assertEqual(len(files), 6)
        self.assertEqual(files.documentation_pages(), [fs[0], fs[1]])
        self.assertEqual(files.static_pages(), [fs[2]])
        self.assertEqual(files.media_files(), [fs[3], fs[4], fs[5]])
        self.assertEqual(files.javascript_files(), [fs[4]])
        self.assertEqual(files.css_files(), [fs[5]])
        self.assertEqual(files.get_file_from_path('foo/bar.jpg'), fs[3])
        self.assertEqual(files.get_file_from_path('foo/bar.jpg'), fs[3])
        self.assertEqual(files.get_file_from_path('missing.jpg'), None)
        self.assertTrue(fs[2].src_uri in files.src_uris)
        self.assertTrue(fs[2].src_uri in files.src_uris)
        extra_file = File('extra.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertFalse(extra_file.src_uri in files.src_uris)
        files.append(extra_file)
        self.assertEqual(len(files), 7)
        self.assertTrue(extra_file.src_uri in files.src_uris)
        self.assertEqual(files.documentation_pages(), [fs[0], fs[1], extra_file])

    @tempdir(
        files=[
            'favicon.ico',
            'index.md',
        ]
    )
    @tempdir(
        files=[
            'base.html',
            'favicon.ico',
            'style.css',
            'foo.md',
            'README',
            '.ignore.txt',
            '.ignore/file.txt',
            'foo/.ignore.txt',
            'foo/.ignore/file.txt',
        ]
    )
    def test_add_files_from_theme(self, tdir, ddir):
        config = load_config(docs_dir=ddir, theme={'name': None, 'custom_dir': tdir})
        env = config.theme.get_env()
        files = get_files(config)
        self.assertEqual(
            [file.src_path for file in files],
            ['index.md', 'favicon.ico'],
        )
        files.add_files_from_theme(env, config)
        self.assertEqual(
            [file.src_path for file in files],
            ['index.md', 'favicon.ico', 'style.css'],
        )
        # Ensure theme file does not override docs_dir file
        self.assertEqual(
            files.get_file_from_path('favicon.ico').abs_src_path,
            os.path.normpath(os.path.join(ddir, 'favicon.ico')),
        )

    def test_filter_paths(self):
        # Root level file
        self.assertFalse(_filter_paths('foo.md', 'foo.md', False, ['bar.md']))
        self.assertTrue(_filter_paths('foo.md', 'foo.md', False, ['foo.md']))

        # Nested file
        self.assertFalse(_filter_paths('foo.md', 'baz/foo.md', False, ['bar.md']))
        self.assertTrue(_filter_paths('foo.md', 'baz/foo.md', False, ['foo.md']))

        # Wildcard
        self.assertFalse(_filter_paths('foo.md', 'foo.md', False, ['*.txt']))
        self.assertTrue(_filter_paths('foo.md', 'foo.md', False, ['*.md']))

        # Root level dir
        self.assertFalse(_filter_paths('bar', 'bar', True, ['/baz']))
        self.assertFalse(_filter_paths('bar', 'bar', True, ['/baz/']))
        self.assertTrue(_filter_paths('bar', 'bar', True, ['/bar']))
        self.assertTrue(_filter_paths('bar', 'bar', True, ['/bar/']))

        # Nested dir
        self.assertFalse(_filter_paths('bar', 'foo/bar', True, ['/bar']))
        self.assertFalse(_filter_paths('bar', 'foo/bar', True, ['/bar/']))
        self.assertTrue(_filter_paths('bar', 'foo/bar', True, ['bar/']))

        # Files that look like dirs (no extension). Note that `is_dir` is `False`.
        self.assertFalse(_filter_paths('bar', 'bar', False, ['bar/']))
        self.assertFalse(_filter_paths('bar', 'foo/bar', False, ['bar/']))

    def test_get_relative_url_use_directory_urls(self):
        to_files = [
            'index.md',
            'foo/index.md',
            'foo/bar/index.md',
            'foo/bar/baz/index.md',
            'foo.md',
            'foo/bar.md',
            'foo/bar/baz.md',
        ]

        to_file_urls = [
            './',
            'foo/',
            'foo/bar/',
            'foo/bar/baz/',
            'foo/',
            'foo/bar/',
            'foo/bar/baz/',
        ]

        from_file = File('img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        expected = [
            'img.jpg',  # img.jpg relative to .
            '../img.jpg',  # img.jpg relative to foo/
            '../../img.jpg',  # img.jpg relative to foo/bar/
            '../../../img.jpg',  # img.jpg relative to foo/bar/baz/
            '../img.jpg',  # img.jpg relative to foo
            '../../img.jpg',  # img.jpg relative to foo/bar
            '../../../img.jpg',  # img.jpg relative to foo/bar/baz
        ]

        for i, filename in enumerate(to_files):
            file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True)
            self.assertEqual(from_file.url, 'img.jpg')
            self.assertEqual(file.url, to_file_urls[i])
            self.assertEqual(from_file.url_relative_to(file.url), expected[i])
            self.assertEqual(from_file.url_relative_to(file), expected[i])

        from_file = File('foo/img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        expected = [
            'foo/img.jpg',  # foo/img.jpg relative to .
            'img.jpg',  # foo/img.jpg relative to foo/
            '../img.jpg',  # foo/img.jpg relative to foo/bar/
            '../../img.jpg',  # foo/img.jpg relative to foo/bar/baz/
            'img.jpg',  # foo/img.jpg relative to foo
            '../img.jpg',  # foo/img.jpg relative to foo/bar
            '../../img.jpg',  # foo/img.jpg relative to foo/bar/baz
        ]

        for i, filename in enumerate(to_files):
            file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True)
            self.assertEqual(from_file.url, 'foo/img.jpg')
            self.assertEqual(file.url, to_file_urls[i])
            self.assertEqual(from_file.url_relative_to(file.url), expected[i])
            self.assertEqual(from_file.url_relative_to(file), expected[i])

        from_file = File('index.html', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        expected = [
            './',  # . relative to .
            '../',  # . relative to foo/
            '../../',  # . relative to foo/bar/
            '../../../',  # . relative to foo/bar/baz/
            '../',  # . relative to foo
            '../../',  # . relative to foo/bar
            '../../../',  # . relative to foo/bar/baz
        ]

        for i, filename in enumerate(to_files):
            file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True)
            self.assertEqual(from_file.url, './')
            self.assertEqual(file.url, to_file_urls[i])
            self.assertEqual(from_file.url_relative_to(file.url), expected[i])
            self.assertEqual(from_file.url_relative_to(file), expected[i])

        from_file = File('file.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        expected = [
            'file/',  # file relative to .
            '../file/',  # file relative to foo/
            '../../file/',  # file relative to foo/bar/
            '../../../file/',  # file relative to foo/bar/baz/
            '../file/',  # file relative to foo
            '../../file/',  # file relative to foo/bar
            '../../../file/',  # file relative to foo/bar/baz
        ]

        for i, filename in enumerate(to_files):
            file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=True)
            self.assertEqual(from_file.url, 'file/')
            self.assertEqual(file.url, to_file_urls[i])
            self.assertEqual(from_file.url_relative_to(file.url), expected[i])
            self.assertEqual(from_file.url_relative_to(file), expected[i])

    def test_get_relative_url(self):
        to_files = [
            'index.md',
            'foo/index.md',
            'foo/bar/index.md',
            'foo/bar/baz/index.md',
            'foo.md',
            'foo/bar.md',
            'foo/bar/baz.md',
        ]

        to_file_urls = [
            'index.html',
            'foo/index.html',
            'foo/bar/index.html',
            'foo/bar/baz/index.html',
            'foo.html',
            'foo/bar.html',
            'foo/bar/baz.html',
        ]

        from_file = File('img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        expected = [
            'img.jpg',  # img.jpg relative to .
            '../img.jpg',  # img.jpg relative to foo/
            '../../img.jpg',  # img.jpg relative to foo/bar/
            '../../../img.jpg',  # img.jpg relative to foo/bar/baz/
            'img.jpg',  # img.jpg relative to foo.html
            '../img.jpg',  # img.jpg relative to foo/bar.html
            '../../img.jpg',  # img.jpg relative to foo/bar/baz.html
        ]

        for i, filename in enumerate(to_files):
            with self.subTest(from_file=from_file.src_path, to_file=filename):
                file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False)
                self.assertEqual(from_file.url, 'img.jpg')
                self.assertEqual(file.url, to_file_urls[i])
                self.assertEqual(from_file.url_relative_to(file.url), expected[i])
                self.assertEqual(from_file.url_relative_to(file), expected[i])

        from_file = File('foo/img.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        expected = [
            'foo/img.jpg',  # foo/img.jpg relative to .
            'img.jpg',  # foo/img.jpg relative to foo/
            '../img.jpg',  # foo/img.jpg relative to foo/bar/
            '../../img.jpg',  # foo/img.jpg relative to foo/bar/baz/
            'foo/img.jpg',  # foo/img.jpg relative to foo.html
            'img.jpg',  # foo/img.jpg relative to foo/bar.html
            '../img.jpg',  # foo/img.jpg relative to foo/bar/baz.html
        ]

        for i, filename in enumerate(to_files):
            with self.subTest(from_file=from_file.src_path, to_file=filename):
                file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False)
                self.assertEqual(from_file.url, 'foo/img.jpg')
                self.assertEqual(file.url, to_file_urls[i])
                self.assertEqual(from_file.url_relative_to(file.url), expected[i])
                self.assertEqual(from_file.url_relative_to(file), expected[i])

        from_file = File('index.html', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        expected = [
            'index.html',  # index.html relative to .
            '../index.html',  # index.html relative to foo/
            '../../index.html',  # index.html relative to foo/bar/
            '../../../index.html',  # index.html relative to foo/bar/baz/
            'index.html',  # index.html relative to foo.html
            '../index.html',  # index.html relative to foo/bar.html
            '../../index.html',  # index.html relative to foo/bar/baz.html
        ]

        for i, filename in enumerate(to_files):
            with self.subTest(from_file=from_file.src_path, to_file=filename):
                file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False)
                self.assertEqual(from_file.url, 'index.html')
                self.assertEqual(file.url, to_file_urls[i])
                self.assertEqual(from_file.url_relative_to(file.url), expected[i])
                self.assertEqual(from_file.url_relative_to(file), expected[i])

        from_file = File('file.html', '/path/to/docs', '/path/to/site', use_directory_urls=False)
        expected = [
            'file.html',  # file.html relative to .
            '../file.html',  # file.html relative to foo/
            '../../file.html',  # file.html relative to foo/bar/
            '../../../file.html',  # file.html relative to foo/bar/baz/
            'file.html',  # file.html relative to foo.html
            '../file.html',  # file.html relative to foo/bar.html
            '../../file.html',  # file.html relative to foo/bar/baz.html
        ]

        for i, filename in enumerate(to_files):
            with self.subTest(from_file=from_file.src_path, to_file=filename):
                file = File(filename, '/path/to/docs', '/path/to/site', use_directory_urls=False)
                self.assertEqual(from_file.url, 'file.html')
                self.assertEqual(file.url, to_file_urls[i])
                self.assertEqual(from_file.url_relative_to(file.url), expected[i])
                self.assertEqual(from_file.url_relative_to(file), expected[i])

    @tempdir(
        files=[
            'index.md',
            'readme.md',
            'bar.css',
            'bar.html',
            'bar.jpg',
            'bar.js',
            'bar.md',
            '.dotfile',
            'templates/foo.html',
        ]
    )
    def test_get_files(self, tdir):
        config = load_config(docs_dir=tdir, extra_css=['bar.css'], extra_javascript=['bar.js'])
        files = get_files(config)
        expected = ['index.md', 'bar.css', 'bar.html', 'bar.jpg', 'bar.js', 'bar.md', 'readme.md']
        self.assertIsInstance(files, Files)
        self.assertEqual(len(files), len(expected))
        self.assertEqual([f.src_path for f in files], expected)

    @tempdir(
        files=[
            'README.md',
            'foo.md',
        ]
    )
    def test_get_files_include_readme_without_index(self, tdir):
        config = load_config(docs_dir=tdir)
        files = get_files(config)
        expected = ['README.md', 'foo.md']
        self.assertIsInstance(files, Files)
        self.assertEqual(len(files), len(expected))
        self.assertEqual([f.src_path for f in files], expected)

    @tempdir(
        files=[
            'index.md',
            'README.md',
            'foo.md',
        ]
    )
    def test_get_files_exclude_readme_with_index(self, tdir):
        config = load_config(docs_dir=tdir)
        with self.assertLogs('mkdocs') as cm:
            files = get_files(config)
        self.assertRegex(
            '\n'.join(cm.output),
            r"^WARNING:mkdocs.structure.files:Both index.md and README.md found. Skipping README.md .+$",
        )
        expected = ['index.md', 'foo.md']
        self.assertIsInstance(files, Files)
        self.assertEqual(len(files), len(expected))
        self.assertEqual([f.src_path for f in files], expected)

    @tempdir()
    @tempdir(files={'test.txt': 'source content'})
    def test_copy_file(self, src_dir, dest_dir):
        file = File('test.txt', src_dir, dest_dir, use_directory_urls=False)
        dest_path = os.path.join(dest_dir, 'test.txt')
        self.assertPathNotExists(dest_path)
        file.copy_file()
        self.assertPathIsFile(dest_path)

    @tempdir(files={'test.txt': 'source content'})
    def test_copy_file_same_file(self, dest_dir):
        file = File('test.txt', dest_dir, dest_dir, use_directory_urls=False)
        dest_path = os.path.join(dest_dir, 'test.txt')
        file.copy_file()
        self.assertPathIsFile(dest_path)
        with open(dest_path, encoding='utf-8') as f:
            self.assertEqual(f.read(), 'source content')

    @tempdir(files={'test.txt': 'destination content'})
    @tempdir(files={'test.txt': 'source content'})
    def test_copy_file_clean_modified(self, src_dir, dest_dir):
        file = File('test.txt', src_dir, dest_dir, use_directory_urls=False)
        file.is_modified = mock.Mock(return_value=True)
        dest_path = os.path.join(dest_dir, 'test.txt')
        file.copy_file(dirty=False)
        self.assertPathIsFile(dest_path)
        with open(dest_path, encoding='utf-8') as f:
            self.assertEqual(f.read(), 'source content')

    @tempdir(files={'test.txt': 'destination content'})
    @tempdir(files={'test.txt': 'source content'})
    def test_copy_file_dirty_modified(self, src_dir, dest_dir):
        file = File('test.txt', src_dir, dest_dir, use_directory_urls=False)
        file.is_modified = mock.Mock(return_value=True)
        dest_path = os.path.join(dest_dir, 'test.txt')
        file.copy_file(dirty=True)
        self.assertPathIsFile(dest_path)
        with open(dest_path, encoding='utf-8') as f:
            self.assertEqual(f.read(), 'source content')

    @tempdir(files={'test.txt': 'destination content'})
    @tempdir(files={'test.txt': 'source content'})
    def test_copy_file_dirty_not_modified(self, src_dir, dest_dir):
        file = File('test.txt', src_dir, dest_dir, use_directory_urls=False)
        file.is_modified = mock.Mock(return_value=False)
        dest_path = os.path.join(dest_dir, 'test.txt')
        file.copy_file(dirty=True)
        self.assertPathIsFile(dest_path)
        with open(dest_path, encoding='utf-8') as f:
            self.assertEqual(f.read(), 'destination content')

    def test_files_append_remove_src_paths(self):
        fs = [
            File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.html', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.jpg', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.js', '/path/to/docs', '/path/to/site', use_directory_urls=True),
            File('foo/bar.css', '/path/to/docs', '/path/to/site', use_directory_urls=True),
        ]
        files = Files(fs)
        self.assertEqual(len(files), 6)
        self.assertEqual(len(files.src_uris), 6)
        extra_file = File('extra.md', '/path/to/docs', '/path/to/site', use_directory_urls=True)
        self.assertFalse(extra_file.src_uri in files.src_uris)
        files.append(extra_file)
        self.assertEqual(len(files), 7)
        self.assertEqual(len(files.src_uris), 7)
        self.assertTrue(extra_file.src_uri in files.src_uris)
        files.remove(extra_file)
        self.assertEqual(len(files), 6)
        self.assertEqual(len(files.src_uris), 6)
        self.assertFalse(extra_file.src_uri in files.src_uris)
