1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
import os
import pathlib
import shutil
import pytest
from mopidy import exceptions
from mopidy_local import mtimes
import tests
@pytest.fixture
def tmp_dir_path(tmp_path):
yield tmp_path
if tmp_path.is_dir():
shutil.rmtree(str(tmp_path))
def test_find_error_is_a_mopidy_exception():
assert issubclass(mtimes.FindError, exceptions.MopidyException)
def test_find_error_can_store_an_errno():
exc = mtimes.FindError("msg", errno=1234)
assert exc.message == "msg"
assert exc.errno == 1234
def test_names_are_pathlib_objects():
result, errors = mtimes.find_mtimes(str(tests.path_to_data_dir("")))
for name in list(result.keys()) + list(errors.keys()):
assert isinstance(name, pathlib.Path)
def test_nonexistent_dir_is_an_error(tmp_dir_path):
missing_path = tmp_dir_path / "does-not-exist"
result, errors = mtimes.find_mtimes(missing_path)
assert result == {}
assert errors == {missing_path: tests.IsA(mtimes.FindError)}
def test_empty_dirs_are_not_in_the_result(tmp_dir_path):
"""Empty directories should not show up in results"""
dir_path = tmp_dir_path / "empty"
dir_path.mkdir()
result, errors = mtimes.find_mtimes(dir_path)
assert result == {}
assert errors == {}
def test_file_as_the_root_just_returns_the_file(tmp_dir_path):
file_path = tmp_dir_path / "single"
file_path.touch()
result, errors = mtimes.find_mtimes(file_path)
assert result == {file_path: tests.IsA(int)}
assert errors == {}
def test_nested_directories(tmp_dir_path):
# Setup foo/bar and baz directories
foo_path = tmp_dir_path / "foo" / "file"
foo_path.parent.mkdir()
foo_path.touch()
foo_bar_path = tmp_dir_path / "foo" / "bar" / "filee"
foo_bar_path.parent.mkdir()
foo_bar_path.touch()
baz_path = tmp_dir_path / "baz" / "file"
baz_path.parent.mkdir()
baz_path.touch()
result, errors = mtimes.find_mtimes(tmp_dir_path)
assert result == {
foo_path: tests.IsA(int),
foo_bar_path: tests.IsA(int),
baz_path: tests.IsA(int),
}
assert errors == {}
def test_missing_permission_to_file_is_not_an_error(tmp_dir_path):
"""Missing permissions to a file is not a search error"""
file_path = tmp_dir_path / "file"
file_path.touch(mode=0o000)
result, errors = mtimes.find_mtimes(tmp_dir_path)
assert result == {file_path: tests.IsA(int)}
assert errors == {}
file_path.chmod(0o644)
def test_missing_permission_to_directory_is_an_error(tmp_dir_path):
dir_path = tmp_dir_path / "dir"
dir_path.mkdir(mode=0o000)
result, errors = mtimes.find_mtimes(tmp_dir_path)
if os.getuid() == 0:
# Test is run as root, nothing is off limits.
assert errors == {}
else:
assert result == {}
assert errors == {dir_path: tests.IsA(mtimes.FindError)}
dir_path.chmod(0o755)
def test_symlinks_are_by_default_an_error(tmp_dir_path):
"""By default symlinks should be treated as an error"""
file_path = tmp_dir_path / "file"
file_path.touch()
link_path = tmp_dir_path / "link"
link_path.symlink_to(file_path)
result, errors = mtimes.find_mtimes(tmp_dir_path)
assert result == {file_path: tests.IsA(int)}
assert errors == {link_path: tests.IsA(mtimes.FindError)}
def test_with_follow_symlink_to_file_as_root_is_followed(tmp_dir_path):
file_path = tmp_dir_path / "file"
file_path.touch()
link_path = tmp_dir_path / "link"
link_path.symlink_to(file_path)
result, errors = mtimes.find_mtimes(link_path, follow=True)
assert result == {file_path: tests.IsA(int)}
assert errors == {}
def test_symlink_to_directory_is_followed(tmp_dir_path):
file_path = tmp_dir_path / "dir" / "file"
file_path.parent.mkdir()
file_path.touch()
link_path = tmp_dir_path / "link"
link_path.symlink_to(file_path.parent, target_is_directory=True)
result, errors = mtimes.find_mtimes(link_path, follow=True)
assert result == {file_path: tests.IsA(int)}
assert errors == {}
def test_symlink_pointing_at_itself_fails(tmp_dir_path):
link_path = tmp_dir_path / "link"
link_path.symlink_to(link_path)
result, errors = mtimes.find_mtimes(tmp_dir_path, follow=True)
assert result == {}
assert errors == {link_path: tests.IsA(mtimes.FindError)}
def test_symlink_pointing_at_parent_fails(tmp_dir_path):
"""We should detect a loop via the parent and give up on the branch"""
link_path = tmp_dir_path / "link"
link_path.symlink_to(tmp_dir_path, target_is_directory=True)
result, errors = mtimes.find_mtimes(tmp_dir_path, follow=True)
assert result == {}
assert errors == {link_path: tests.IsA(Exception)}
def test_indirect_symlink_loop(tmp_dir_path):
"""More indirect loops should also be detected"""
# Setup tmpdir/directory/loop where loop points to tmpdir
link_path = tmp_dir_path / "dir" / "link"
link_path.parent.mkdir()
link_path.symlink_to(tmp_dir_path, target_is_directory=True)
result, errors = mtimes.find_mtimes(tmp_dir_path, follow=True)
assert result == {}
assert errors == {link_path: tests.IsA(Exception)}
def test_symlink_branches_are_not_excluded(tmp_dir_path):
"""Using symlinks to make a file show up multiple times should work"""
file_path = tmp_dir_path / "dir" / "file"
file_path.parent.mkdir()
file_path.touch()
link1_path = tmp_dir_path / "link1"
link1_path.symlink_to(file_path)
link2_path = tmp_dir_path / "link2"
link2_path.symlink_to(file_path)
result, errors = mtimes.find_mtimes(tmp_dir_path, follow=True)
assert result == {
file_path: tests.IsA(int),
link1_path: tests.IsA(int),
link2_path: tests.IsA(int),
}
assert errors == {}
def test_gives_mtime_in_milliseconds(tmp_dir_path):
file_path = tmp_dir_path / "file"
file_path.touch()
os.utime(str(file_path), (1, 3.14159265))
result, errors = mtimes.find_mtimes(tmp_dir_path)
assert result == {file_path: 3141}
assert errors == {}
|