Files
osxphotos/tests/test_gitignorefile_cache.py
Rhet Turnbull e937285a72 Feature keep file 1135 (#1139)
* Added gitignorefile

* Fixed gitignorefile for os.PathLike paths

* --keep now follows .gitignore rules

* Fixed ruff QA error

* Added support for .osxphotos_keep file

* Added reference to .osxphotos_keep

* Added tests for .osxphotos_keep

* Updated help text for --cleanup, --keep
2023-08-02 06:37:29 -07:00

138 lines
5.8 KiB
Python

import io
import itertools
import os
import stat
import tempfile
import unittest
import unittest.mock
import osxphotos.gitignorefile
class TestCache(unittest.TestCase):
def test_simple(self):
def normalize_path(path):
return os.path.abspath(path).replace(os.sep, "/")
class StatResult:
def __init__(self, is_file=False):
self.st_ino = id(self)
self.st_dev = 0
self.st_mode = stat.S_IFREG if is_file else stat.S_IFDIR
def isdir(self):
return self.st_mode == stat.S_IFDIR
def isfile(self):
return self.st_mode == stat.S_IFREG
class Stat:
def __init__(self, directories, files):
self.__filesystem = {}
for path in directories:
self.__filesystem[normalize_path(path)] = StatResult()
for path in files:
self.__filesystem[normalize_path(path)] = StatResult(True)
def __call__(self, path):
try:
return self.__filesystem[normalize_path(path)]
except KeyError:
raise FileNotFoundError()
for ignore_file_name in (".gitignore", ".mylovelytoolignore"):
with self.subTest(ignore_file_name=ignore_file_name):
my_stat = Stat(
[
"/home/vladimir/project/directory/subdirectory",
"/home/vladimir/project/directory",
"/home/vladimir/project",
"/home/vladimir",
"/home",
"/",
],
[
"/home/vladimir/project/directory/subdirectory/subdirectory/file.txt",
"/home/vladimir/project/directory/subdirectory/subdirectory/file2.txt",
"/home/vladimir/project/directory/subdirectory/subdirectory/file3.txt",
"/home/vladimir/project/directory/subdirectory/file.txt",
"/home/vladimir/project/directory/subdirectory/file2.txt",
"/home/vladimir/project/directory/%s" % ignore_file_name,
"/home/vladimir/project/directory/file.txt",
"/home/vladimir/project/directory/file2.txt",
"/home/vladimir/project/file.txt",
"/home/vladimir/project/%s" % ignore_file_name,
"/home/vladimir/file.txt",
],
)
def mock_open(path):
data = {
normalize_path(
"/home/vladimir/project/directory/%s" % ignore_file_name
): ["file.txt"],
normalize_path(
"/home/vladimir/project/%s" % ignore_file_name
): ["file2.txt"],
}
statistics["open"] += 1
try:
return io.StringIO("\n".join(data[normalize_path(path)]))
except KeyError:
raise FileNotFoundError()
def mock_isdir(path):
statistics["isdir"] += 1
try:
return my_stat(path).isdir()
except FileNotFoundError:
return False
def mock_isfile(path):
statistics["isfile"] += 1
try:
return my_stat(path).isfile()
except FileNotFoundError:
return False
data = {
"/home/vladimir/project/directory/subdirectory/file.txt": True,
"/home/vladimir/project/directory/subdirectory/file2.txt": True,
"/home/vladimir/project/directory/subdirectory/subdirectory/file.txt": True,
"/home/vladimir/project/directory/subdirectory/subdirectory/file2.txt": True,
"/home/vladimir/project/directory/subdirectory/subdirectory/file3.txt": False,
"/home/vladimir/project/directory/file.txt": True,
"/home/vladimir/project/directory/file2.txt": True,
"/home/vladimir/project/file.txt": False,
"/home/vladimir/file.txt": False, # No rules and no `isdir` calls for this file.
}
# 9! == 362880 combinations.
for permutation in itertools.islice(
itertools.permutations(data.items()), 0, None, 6 * 8
):
statistics = {"open": 0, "isdir": 0, "isfile": 0}
with unittest.mock.patch("builtins.open", mock_open):
with unittest.mock.patch("os.path.isdir", mock_isdir):
with unittest.mock.patch("os.path.isfile", mock_isfile):
matches = osxphotos.gitignorefile.Cache(
ignore_names=[ignore_file_name]
)
for path, expected in permutation:
self.assertEqual(matches(path), expected)
self.assertEqual(statistics["open"], 2)
self.assertEqual(statistics["isdir"], len(data) - 1)
self.assertEqual(statistics["isfile"], 7) # Unique path fragments.
def test_wrong_symlink(self):
with tempfile.TemporaryDirectory() as d:
matches = osxphotos.gitignorefile.Cache()
os.makedirs(f"{d}/.venv/bin")
os.symlink(f"/nonexistent-path-{id(self)}", f"{d}/.venv/bin/python")
self.assertFalse(matches(f"{d}/.venv/bin/python"))