unit tests minus logger working

This commit is contained in:
Jesse Bannon 2023-02-24 23:22:02 -08:00
parent b6af2c8de7
commit b9bafc7fe0
7 changed files with 74 additions and 55 deletions

View file

@ -9,6 +9,7 @@ from ytdl_sub.utils.chapters import Chapters
from ytdl_sub.utils.exceptions import ValidationException
from ytdl_sub.utils.file_handler import FileHandler
from ytdl_sub.utils.logger import Logger
from ytdl_sub.utils.system import IS_WINDOWS
logger = Logger.get(name="ffmpeg")
@ -27,7 +28,7 @@ class FFMPEG:
@classmethod
def _ensure_installed(cls):
try:
if sys.platform.startswith("win32"):
if IS_WINDOWS:
subprocess.check_output([".\\ffmpeg", "-version"])
else:
subprocess.check_output(["which", "ffmpeg"])

View file

@ -1,67 +1,62 @@
import errno
import os
import sys
from contextlib import contextmanager
from pathlib import Path
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.utils.exceptions import ValidationException
from ytdl_sub.utils.logger import Logger
from ytdl_sub.utils.system import IS_WINDOWS
logger = Logger.get()
@contextmanager
def _working_directory_lock_unix(config: ConfigFile):
"""
Create and try to lock the file /tmp/working_directory_name
Raises
------
ValidationException
Lock is acquired from another process running ytdl-sub in the same working directory
OSError
Other lock error occurred
"""
# pylint: disable=import-outside-toplevel
import fcntl
working_directory_path = Path(os.getcwd()) / config.config_options.working_directory
lock_file_path = (
Path(os.getcwd())
/ config.config_options.lock_directory
/ str(working_directory_path).replace("/", "_")
)
lock_file = open(lock_file_path, "w", encoding="utf-8")
try:
fcntl.lockf(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError as exc:
if exc.errno in (errno.EACCES, errno.EAGAIN):
raise ValidationException(
"Cannot run two instances of ytdl-sub "
"with the same working directory at the same time"
) from exc
lock_file.close()
raise exc
try:
yield
finally:
fcntl.flock(lock_file, fcntl.LOCK_UN)
lock_file.close()
@contextmanager
def working_directory_lock(config: ConfigFile):
if sys.platform.startswith("win32"):
if IS_WINDOWS:
@contextmanager
def working_directory_lock(config: ConfigFile):
logger.info(
"Working directory lock not supported in Windows. "
"Ensure only one instance of ytdl-sub runs at once using working directory %s",
config.config_options.working_directory,
)
yield
else:
with _working_directory_lock_unix(config):
else:
import fcntl
@contextmanager
def working_directory_lock(config: ConfigFile):
"""
Create and try to lock the file /tmp/working_directory_name
Raises
------
ValidationException
Lock is acquired from another process running ytdl-sub in the same working directory
OSError
Other lock error occurred
"""
working_directory_path = Path(os.getcwd()) / config.config_options.working_directory
lock_file_path = (
Path(os.getcwd())
/ config.config_options.lock_directory
/ str(working_directory_path).replace("/", "_")
)
lock_file = open(lock_file_path, "w", encoding="utf-8")
try:
fcntl.lockf(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError as exc:
if exc.errno in (errno.EACCES, errno.EAGAIN):
raise ValidationException(
"Cannot run two instances of ytdl-sub "
"with the same working directory at the same time"
) from exc
lock_file.close()
raise exc
try:
yield
finally:
fcntl.flock(lock_file, fcntl.LOCK_UN)
lock_file.close()

View file

@ -0,0 +1,4 @@
import sys
IS_WINDOWS = sys.platform.startswith("win32")

View file

@ -1,5 +1,6 @@
import json
import os.path
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import List
@ -9,15 +10,20 @@ from resources import REGENERATE_FIXTURES
from resources import RESOURCE_PATH
from ytdl_sub.utils.file_handler import get_file_md5_hash
from ytdl_sub.utils.system import IS_WINDOWS
_EXPECTED_DOWNLOADS_SUMMARY_PATH = RESOURCE_PATH / "expected_downloads_summaries"
def _get_files_in_directory(relative_directory: Path | str) -> List[Path]:
relative_path_part_idx = 3 # Cuts /tmp/<tmp_folder>
if IS_WINDOWS:
relative_path_part_idx = 7 # Cuts C:\Users\<user>\AppData\Local\Temp\<tmp_folder>
relative_file_paths: List[Path] = []
for path in Path(relative_directory).rglob("*"):
if path.is_file():
relative_path = Path(*path.parts[3:])
relative_path = Path(*path.parts[relative_path_part_idx:])
relative_file_paths.append(relative_path)
return relative_file_paths
@ -70,7 +76,8 @@ class ExpectedDownloads:
full_path = Path(relative_directory) / path
assert os.path.isfile(full_path), f"Expected {path} to be a file but it is not"
if path in ignore_md5_hashes_for or path.endswith(".info.json"):
# TODO: Implement file hash for tests in Windows
if IS_WINDOWS or path in ignore_md5_hashes_for or path.endswith(".info.json"):
continue
md5_hash = get_file_md5_hash(full_file_path=full_path)

View file

@ -6,6 +6,7 @@ import pytest
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.utils.exceptions import ValidationException
from ytdl_sub.utils.file_lock import working_directory_lock
from ytdl_sub.utils.system import IS_WINDOWS
@pytest.fixture
@ -16,6 +17,9 @@ def config() -> ConfigFile:
def test_working_directory_lock(config: ConfigFile):
if IS_WINDOWS:
return
new_pid = os.fork()
if new_pid == 0: # is child
with working_directory_lock(config=config):

View file

@ -95,6 +95,9 @@ class TestLogger:
assert lines == ["[ytdl-sub:name_test] info test\n", "[ytdl-sub:name_test] debug test\n"]
# Ensure the file cleans up too
for handler in logger.handlers:
handler.close()
Logger.cleanup(delete_debug_file=True)
assert not os.path.isfile(Logger._DEBUG_LOGGER_FILE.name)

View file

@ -1,3 +1,5 @@
import os
import re
import tempfile
import pytest
@ -21,10 +23,13 @@ def bad_yaml() -> str:
@pytest.fixture
def bad_yaml_file_path(bad_yaml) -> str:
with tempfile.NamedTemporaryFile(suffix=".yaml") as tmp_file:
# Do not delete the file in the context manager - for Windows compatibility
with tempfile.NamedTemporaryFile(suffix=".yaml", delete=False) as tmp_file:
tmp_file.write(bad_yaml.encode("utf-8"))
tmp_file.flush()
yield tmp_file.name
yield tmp_file.name
os.remove(tmp_file.name)
def test_load_yaml_file_not_found():
@ -36,6 +41,6 @@ def test_load_yaml_file_not_found():
def test_load_yaml_invalid_syntax(bad_yaml_file_path):
with pytest.raises(
InvalidYamlException,
match=f"'{bad_yaml_file_path}' has invalid YAML, copy-paste it into a YAML checker to find the issue.",
match=re.escape(f"'{bad_yaml_file_path}' has invalid YAML, copy-paste it into a YAML checker to find the issue."),
):
load_yaml(file_path=bad_yaml_file_path)