Port to non-MacOS platforms (#1026)

* Port to non-MacOS platforms

* Keep NFD normalization on macOS

* Update locale_util.py

Fix lint error from ruff (runs in CI)

* Update query.py

click.Option first arg needs to be a list (different than click.option)

* Dynamically normalize Unicode paths in test

* Fix missing import

---------

Co-authored-by: Rhet Turnbull <rturnbull@gmail.com>
This commit is contained in:
dvdkon
2023-05-07 15:55:56 +02:00
committed by GitHub
parent 0c85298c03
commit ca3da647f2
51 changed files with 726 additions and 360 deletions

View File

@@ -5,14 +5,19 @@ import shutil
import tempfile
import time
import photoscript
import pytest
from applescript import AppleScript
from photoscript.utils import ditto
from osxphotos.utils import is_macos
if is_macos:
import photoscript
from applescript import AppleScript
from photoscript.utils import ditto
from .test_catalina_10_15_7 import UUID_DICT_LOCAL
from osxphotos.exiftool import _ExifToolProc
from .test_catalina_10_15_7 import UUID_DICT_LOCAL
# run timewarp tests (configured with --timewarp)
TEST_TIMEWARP = False
@@ -34,6 +39,9 @@ NO_CLEANUP = False
def get_os_version():
if not is_macos:
return (None, None, None)
import platform
# returns tuple containing OS version
@@ -53,7 +61,7 @@ def get_os_version():
return (ver, major, minor)
OS_VER = get_os_version()
OS_VER = get_os_version() if is_macos else [None, None]
if OS_VER[0] == "10" and OS_VER[1] == "15":
# Catalina
TEST_LIBRARY = "tests/Test-10.15.7.photoslibrary"
@@ -77,28 +85,28 @@ else:
TEST_LIBRARY_ADD_LOCATIONS = None
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture(scope="session", autouse=is_macos)
def setup_photos_timewarp():
if not TEST_TIMEWARP:
return
copy_photos_library(TEST_LIBRARY_TIMEWARP, delay=5)
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture(scope="session", autouse=is_macos)
def setup_photos_import():
if not TEST_IMPORT:
return
copy_photos_library(TEST_LIBRARY_IMPORT, delay=10)
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture(scope="session", autouse=is_macos)
def setup_photos_sync():
if not TEST_SYNC:
return
copy_photos_library(TEST_LIBRARY_SYNC, delay=10)
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture(scope="session", autouse=is_macos)
def setup_photos_add_locations():
if not TEST_ADD_LOCATIONS:
return
@@ -312,7 +320,10 @@ def addalbum_library():
def copy_photos_library_to_path(photos_library_path: str, dest_path: str) -> str:
"""Copy a photos library to a folder"""
ditto(photos_library_path, dest_path)
if is_macos:
ditto(photos_library_path, dest_path)
else:
shutil.copytree(photos_library_path, dest_path)
return dest_path

16
tests/locale_util.py Normal file
View File

@@ -0,0 +1,16 @@
""" Helpers for running locale-dependent tests """
import contextlib
import locale
import pytest
def setlocale(typ, name):
try:
with contextlib.suppress(Exception):
locale.setlocale(typ, name)
# On Linux UTF-8 locales are separate
locale.setlocale(typ, f"{name}.UTF-8")
except locale.Error:
pytest.skip(f"Locale {name} not available")

View File

@@ -15,9 +15,9 @@ import pytest
import osxphotos
from osxphotos._constants import _UNKNOWN_PERSON
from osxphotos.photoexporter import PhotoExporter
from osxphotos.utils import get_macos_version
from osxphotos.utils import is_macos, get_macos_version
OS_VERSION = get_macos_version()
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "15"
PHOTOS_DB_LOCAL = os.path.expanduser("~/Pictures/Photos Library.photoslibrary")
@@ -1448,6 +1448,7 @@ def test_multi_uuid(photosdb):
assert len(photos) == 2
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_detected_text(photosdb):
"""test PhotoInfo.detected_text"""
for uuid, expected_text in UUID_DETECTED_TEXT.items():

View File

@@ -14,10 +14,10 @@ import subprocess
import tempfile
import time
from tempfile import TemporaryDirectory
from bitmath import contextlib
import pytest
from click.testing import CliRunner
from osxmetadata import OSXMetaData, Tag
import osxphotos
from osxphotos._constants import OSXPHOTOS_EXPORT_DB
@@ -36,9 +36,16 @@ from osxphotos.cli import (
)
from osxphotos.exiftool import ExifTool, get_exiftool_path
from osxphotos.fileutil import FileUtil
from osxphotos.utils import noop, normalize_fs_path, normalize_unicode
from osxphotos.utils import is_macos, noop, normalize_fs_path, normalize_unicode
if is_macos:
from osxmetadata import OSXMetaData, Tag
from .conftest import copy_photos_library_to_path
from .locale_util import setlocale
def _normalize_fs_paths(paths):
"""Small helper to prepare path strings for test"""
return [normalize_fs_path(p) for p in paths]
CLI_PHOTOS_DB = "tests/Test-10.15.7.photoslibrary"
LIVE_PHOTOS_DB = "tests/Test-Cloud-10.15.1.photoslibrary"
@@ -66,7 +73,7 @@ SKIP_UUID_FILE = "tests/skip_uuid_from_file.txt"
CLI_OUTPUT_QUERY_UUID = '[{"uuid": "D79B8D77-BFFC-460B-9312-034F2877D35B", "filename": "D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg", "original_filename": "Pumkins2.jpg", "date": "2018-09-28T16:07:07-04:00", "description": "Girl holding pumpkin", "title": "I found one!", "keywords": ["Kids"], "albums": ["Pumpkin Farm", "Test Album", "Multi Keyword"], "persons": ["Katie"], "path": "/tests/Test-10.15.7.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg", "ismissing": false, "hasadjustments": false, "external_edit": false, "favorite": false, "hidden": false, "latitude": 41.256566, "longitude": -95.940257, "path_edited": null, "shared": false, "isphoto": true, "ismovie": false, "uti": "public.jpeg", "burst": false, "live_photo": false, "path_live_photo": null, "iscloudasset": false, "incloud": null}]'
CLI_EXPORT_FILENAMES = [
CLI_EXPORT_FILENAMES = _normalize_fs_paths([
"[2020-08-29] AAF035 (1).jpg",
"[2020-08-29] AAF035 (2).jpg",
"[2020-08-29] AAF035 (3).jpg",
@@ -100,10 +107,10 @@ CLI_EXPORT_FILENAMES = [
"wedding.jpg",
"winebottle (1).jpeg",
"winebottle.jpeg",
]
])
CLI_EXPORT_FILENAMES_DRY_RUN = [
CLI_EXPORT_FILENAMES_DRY_RUN = _normalize_fs_paths([
"[2020-08-29] AAF035.jpg",
"DSC03584.dng",
"Frítest_edited.jpeg",
@@ -130,15 +137,25 @@ CLI_EXPORT_FILENAMES_DRY_RUN = [
"wedding.jpg",
"winebottle.jpeg",
"winebottle.jpeg",
]
])
CLI_EXPORT_IGNORE_SIGNATURE_FILENAMES = ["Tulips.jpg", "wedding.jpg"]
CLI_EXPORT_IGNORE_SIGNATURE_FILENAMES = _normalize_fs_paths([
"Tulips.jpg",
"wedding.jpg"
])
CLI_EXPORT_FILENAMES_ALBUM = ["Pumkins1.jpg", "Pumkins2.jpg", "Pumpkins3.jpg"]
CLI_EXPORT_FILENAMES_ALBUM = _normalize_fs_paths([
"Pumkins1.jpg",
"Pumkins2.jpg",
"Pumpkins3.jpg"
])
CLI_EXPORT_FILENAMES_ALBUM_UNICODE = ["IMG_4547.jpg"]
CLI_EXPORT_FILENAMES_ALBUM_UNICODE = _normalize_fs_paths(["IMG_4547.jpg"])
CLI_EXPORT_FILENAMES_DELETED_TWIN = ["wedding.jpg", "wedding_edited.jpeg"]
CLI_EXPORT_FILENAMES_DELETED_TWIN = _normalize_fs_paths([
"wedding.jpg",
"wedding_edited.jpeg"
])
CLI_EXPORT_EDITED_SUFFIX = "_bearbeiten"
CLI_EXPORT_EDITED_SUFFIX_TEMPLATE = "{edited?_edited,}"
@@ -146,7 +163,7 @@ CLI_EXPORT_ORIGINAL_SUFFIX = "_original"
CLI_EXPORT_ORIGINAL_SUFFIX_TEMPLATE = "{edited?_original,}"
CLI_EXPORT_PREVIEW_SUFFIX = "_lowres"
CLI_EXPORT_FILENAMES_EDITED_SUFFIX = [
CLI_EXPORT_FILENAMES_EDITED_SUFFIX = _normalize_fs_paths([
"[2020-08-29] AAF035 (1).jpg",
"[2020-08-29] AAF035 (2).jpg",
"[2020-08-29] AAF035 (3).jpg",
@@ -180,9 +197,9 @@ CLI_EXPORT_FILENAMES_EDITED_SUFFIX = [
"wedding.jpg",
"winebottle (1).jpeg",
"winebottle.jpeg",
]
])
CLI_EXPORT_FILENAMES_EDITED_SUFFIX_TEMPLATE = [
CLI_EXPORT_FILENAMES_EDITED_SUFFIX_TEMPLATE = _normalize_fs_paths([
"[2020-08-29] AAF035 (1).jpg",
"[2020-08-29] AAF035 (2).jpg",
"[2020-08-29] AAF035 (3).jpg",
@@ -216,9 +233,9 @@ CLI_EXPORT_FILENAMES_EDITED_SUFFIX_TEMPLATE = [
"wedding.jpg",
"winebottle (1).jpeg",
"winebottle.jpeg",
]
])
CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX = [
CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX = _normalize_fs_paths([
"[2020-08-29] AAF035_original (1).jpg",
"[2020-08-29] AAF035_original (2).jpg",
"[2020-08-29] AAF035_original (3).jpg",
@@ -252,9 +269,9 @@ CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX = [
"wedding_original.jpg",
"winebottle_original (1).jpeg",
"winebottle_original.jpeg",
]
])
CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX_TEMPLATE = [
CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX_TEMPLATE = _normalize_fs_paths([
"[2020-08-29] AAF035 (1).jpg",
"[2020-08-29] AAF035 (2).jpg",
"[2020-08-29] AAF035 (3).jpg",
@@ -288,7 +305,7 @@ CLI_EXPORT_FILENAMES_ORIGINAL_SUFFIX_TEMPLATE = [
"wedding_original.jpg",
"winebottle (1).jpeg",
"winebottle.jpeg",
]
])
CLI_EXPORT_FILENAMES_CURRENT = [
"1793FAAB-DE75-4E25-886C-2BD66C780D6A_edited.jpeg", # Frítest.jpg
@@ -326,7 +343,7 @@ CLI_EXPORT_FILENAMES_CURRENT = [
"F207D5DE-EFAD-4217-8424-0764AAC971D0.jpeg",
]
CLI_EXPORT_FILENAMES_CONVERT_TO_JPEG = [
CLI_EXPORT_FILENAMES_CONVERT_TO_JPEG = _normalize_fs_paths([
"[2020-08-29] AAF035 (1).jpg",
"[2020-08-29] AAF035 (2).jpg",
"[2020-08-29] AAF035 (3).jpg",
@@ -360,9 +377,9 @@ CLI_EXPORT_FILENAMES_CONVERT_TO_JPEG = [
"wedding.jpg",
"winebottle (1).jpeg",
"winebottle.jpeg",
]
])
CLI_EXPORT_FILENAMES_CONVERT_TO_JPEG_SKIP_RAW = [
CLI_EXPORT_FILENAMES_CONVERT_TO_JPEG_SKIP_RAW = _normalize_fs_paths([
"[2020-08-29] AAF035 (1).jpg",
"[2020-08-29] AAF035 (2).jpg",
"[2020-08-29] AAF035 (3).jpg",
@@ -394,26 +411,26 @@ CLI_EXPORT_FILENAMES_CONVERT_TO_JPEG_SKIP_RAW = [
"wedding.jpg",
"winebottle (1).jpeg",
"winebottle.jpeg",
]
])
CLI_EXPORT_CONVERT_TO_JPEG_LARGE_FILE = "DSC03584.jpeg"
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES1 = [
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES1 = _normalize_fs_paths([
"2019/April/wedding.jpg",
"2019/July/Tulips.jpg",
"2018/October/St James Park.jpg",
"2018/September/Pumpkins3.jpg",
"2018/September/Pumkins2.jpg",
"2018/September/Pumkins1.jpg",
]
])
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES_LOCALE = [
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES_LOCALE = _normalize_fs_paths([
"2019/September/IMG_9975.JPEG",
"2020/Februar/IMG_1064.JPEG",
"2016/März/IMG_3984.JPEG",
]
])
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES_ALBUM1 = [
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES_ALBUM1 = _normalize_fs_paths([
"Multi Keyword/wedding.jpg",
"_/Tulips.jpg",
"_/St James Park.jpg",
@@ -421,9 +438,9 @@ CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES_ALBUM1 = [
"Pumpkin Farm/Pumkins2.jpg",
"Pumpkin Farm/Pumkins1.jpg",
"Test Album/Pumkins1.jpg",
]
])
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES_ALBUM2 = [
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES_ALBUM2 = _normalize_fs_paths([
"Multi Keyword/wedding.jpg",
"NOALBUM/Tulips.jpg",
"NOALBUM/St James Park.jpg",
@@ -431,28 +448,28 @@ CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES_ALBUM2 = [
"Pumpkin Farm/Pumkins2.jpg",
"Pumpkin Farm/Pumkins1.jpg",
"Test Album/Pumkins1.jpg",
]
])
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES2 = [
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES2 = _normalize_fs_paths([
"St James's Park, Great Britain, Westminster, England, United Kingdom/St James Park.jpg",
"_/Pumpkins3.jpg",
"Omaha, Nebraska, United States/Pumkins2.jpg",
"_/Pumkins1.jpg",
"_/Tulips.jpg",
"_/wedding.jpg",
]
])
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES3 = [
CLI_EXPORTED_DIRECTORY_TEMPLATE_FILENAMES3 = _normalize_fs_paths([
"2019/{foo}/wedding.jpg",
"2019/{foo}/Tulips.jpg",
"2018/{foo}/St James Park.jpg",
"2018/{foo}/Pumpkins3.jpg",
"2018/{foo}/Pumkins2.jpg",
"2018/{foo}/Pumkins1.jpg",
]
])
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES1 = [
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES1 = _normalize_fs_paths([
"2019-wedding.jpg",
"2019-wedding_edited.jpeg",
"2019-Tulips.jpg",
@@ -461,9 +478,9 @@ CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES1 = [
"2018-Pumpkins3.jpg",
"2018-Pumkins2.jpg",
"2018-Pumkins1.jpg",
]
])
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES2 = [
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES2 = _normalize_fs_paths([
"Folder1_SubFolder2_AlbumInFolder-IMG_4547.jpg",
"Folder1_SubFolder2_AlbumInFolder-wedding.jpg",
"Folder1_SubFolder2_AlbumInFolder-wedding_edited.jpeg",
@@ -484,18 +501,18 @@ CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES2 = [
"None-IMG_1693.tif",
"I have a deleted twin-wedding.jpg",
"I have a deleted twin-wedding_edited.jpeg",
]
])
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES_PATHSEP = [
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES_PATHSEP = _normalize_fs_paths([
"2018-10 - Sponsion, Museum, Frühstück, Römermuseum/IMG_4547.jpg",
"Folder1/SubFolder2/AlbumInFolder/IMG_4547.jpg",
"2019-10:11 Paris Clermont/IMG_4547.jpg",
]
])
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES_KEYWORD_PATHSEP = [
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES_KEYWORD_PATHSEP = _normalize_fs_paths([
"foo:bar/foo:bar_IMG_3092.heic"
]
])
CLI_EXPORTED_FILENAME_TEMPLATE_LONG_DESCRIPTION = [
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo"
@@ -519,46 +536,59 @@ CLI_EXPORT_BY_DATE_TOUCH_UUID = [
"F12384F6-CD17-4151-ACBA-AE0E3688539E", # Pumkins1.jpg
]
CLI_EXPORT_BY_DATE_TOUCH_TIMES = [1538165373, 1538163349]
CLI_EXPORT_BY_DATE_NEED_TOUCH = [
CLI_EXPORT_BY_DATE_NEED_TOUCH = _normalize_fs_paths([
"2018/09/28/Pumkins2.jpg",
"2018/10/13/St James Park.jpg",
]
])
CLI_EXPORT_BY_DATE_NEED_TOUCH_UUID = [
"D79B8D77-BFFC-460B-9312-034F2877D35B",
"DC99FBDD-7A52-4100-A5BB-344131646C30",
]
CLI_EXPORT_BY_DATE_NEED_TOUCH_TIMES = [1538165227, 1539436692]
CLI_EXPORT_BY_DATE = ["2018/09/28/Pumpkins3.jpg", "2018/09/28/Pumkins1.jpg"]
CLI_EXPORT_BY_DATE = _normalize_fs_paths([
"2018/09/28/Pumpkins3.jpg",
"2018/09/28/Pumkins1.jpg",
])
CLI_EXPORT_SIDECAR_FILENAMES = ["Pumkins2.jpg", "Pumkins2.jpg.json", "Pumkins2.jpg.xmp"]
CLI_EXPORT_SIDECAR_DROP_EXT_FILENAMES = [
CLI_EXPORT_SIDECAR_FILENAMES = _normalize_fs_paths([
"Pumkins2.jpg",
"Pumkins2.jpg.json",
"Pumkins2.jpg.xmp",
])
CLI_EXPORT_SIDECAR_DROP_EXT_FILENAMES = _normalize_fs_paths([
"Pumkins2.jpg",
"Pumkins2.json",
"Pumkins2.xmp",
]
])
CLI_EXPORT_LIVE = [
"51F2BEF7-431A-4D31-8AC1-3284A57826AE.jpeg",
"51F2BEF7-431A-4D31-8AC1-3284A57826AE.mov",
]
CLI_EXPORT_LIVE_ORIGINAL = ["IMG_0728.JPG", "IMG_0728.mov"]
CLI_EXPORT_LIVE_ORIGINAL = _normalize_fs_paths([
"IMG_0728.JPG",
"IMG_0728.mov",
])
CLI_EXPORT_RAW = ["441DFE2A-A69B-4C79-A69B-3F51D1B9B29C.cr2"]
CLI_EXPORT_RAW_ORIGINAL = ["IMG_0476_2.CR2"]
CLI_EXPORT_RAW_ORIGINAL = _normalize_fs_paths(["IMG_0476_2.CR2"])
CLI_EXPORT_RAW_EDITED = [
"441DFE2A-A69B-4C79-A69B-3F51D1B9B29C.cr2",
"441DFE2A-A69B-4C79-A69B-3F51D1B9B29C_edited.jpeg",
]
CLI_EXPORT_RAW_EDITED_ORIGINAL = ["IMG_0476_2.CR2", "IMG_0476_2_edited.jpeg"]
CLI_EXPORT_RAW_EDITED_ORIGINAL = _normalize_fs_paths([
"IMG_0476_2.CR2",
"IMG_0476_2_edited.jpeg",
])
CLI_UUID_DICT_15_7 = {
"intrash": "71E3E212-00EB-430D-8A63-5E294B268554",
"template": "F12384F6-CD17-4151-ACBA-AE0E3688539E",
}
CLI_TEMPLATE_SIDECAR_FILENAME = "Pumkins1.jpg.json"
CLI_TEMPLATE_FILENAME = "Pumkins1.jpg"
CLI_TEMPLATE_SIDECAR_FILENAME = normalize_fs_path("Pumkins1.jpg.json")
CLI_TEMPLATE_FILENAME = normalize_fs_path("Pumkins1.jpg")
CLI_UUID_DICT_14_6 = {"intrash": "3tljdX43R8+k6peNHVrJNQ"}
@@ -960,12 +990,12 @@ UUID_UNICODE_TITLE = [
"D1D4040D-D141-44E8-93EA-E403D9F63E07", # Frítest.jpg
]
EXPORT_UNICODE_TITLE_FILENAMES = [
EXPORT_UNICODE_TITLE_FILENAMES = _normalize_fs_paths([
"Frítest.jpg",
"Frítest (1).jpg",
"Frítest (2).jpg",
"Frítest (3).jpg",
]
])
# data for --report
UUID_REPORT = [
@@ -987,12 +1017,12 @@ QUERY_EXIF_DATA_CASE_INSENSITIVE = [
EXPORT_EXIF_DATA = [("EXIF:Make", "FUJIFILM", ["Tulips.jpg", "Tulips_edited.jpeg"])]
UUID_LIVE_EDITED = "136A78FA-1B90-46CC-88A7-CCA3331F0353" # IMG_4813.HEIC
CLI_EXPORT_LIVE_EDITED = [
CLI_EXPORT_LIVE_EDITED = _normalize_fs_paths([
"IMG_4813.HEIC",
"IMG_4813.mov",
"IMG_4813_edited.jpeg",
"IMG_4813_edited.mov",
]
])
UUID_FAVORITE = "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51"
FILE_FAVORITE = "wedding.jpg"
@@ -1804,12 +1834,25 @@ def test_export_preview_update():
assert len(files) == 2 # preview + original
@contextlib.contextmanager
def isolated_filesystem_here():
cwd = os.getcwd()
tempdir = tempfile.mkdtemp(dir=cwd) # type: ignore[type-var]
os.chdir(tempdir)
try:
yield tempdir
finally:
os.chdir(cwd)
shutil.rmtree(tempdir)
def test_export_as_hardlink():
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
with isolated_filesystem_here():
result = runner.invoke(
export,
[os.path.join(cwd, CLI_PHOTOS_DB), ".", "--export-as-hardlink", "-V"],
@@ -1829,7 +1872,7 @@ def test_export_as_hardlink_samefile():
photo = photosdb.photos(uuid=[CLI_EXPORT_UUID])[0]
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
with isolated_filesystem_here():
result = runner.invoke(
export,
[
@@ -1854,7 +1897,7 @@ def test_export_using_hardlinks_incompat_options():
photo = photosdb.photos(uuid=[CLI_EXPORT_UUID])[0]
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
with isolated_filesystem_here():
result = runner.invoke(
export,
[
@@ -3717,7 +3760,7 @@ def test_export_raw_edited_original():
def test_export_directory_template_1():
# test export using directory template
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
runner = CliRunner()
cwd = os.getcwd()
@@ -3837,7 +3880,7 @@ def test_export_directory_template_locale():
with runner.isolated_filesystem():
# set locale environment
os.environ["LC_ALL"] = "de_DE.UTF-8"
locale.setlocale(locale.LC_ALL, "")
setlocale(locale.LC_ALL, "")
result = runner.invoke(
export,
[
@@ -3857,7 +3900,7 @@ def test_export_directory_template_locale():
def test_export_filename_template_1():
"""export photos using filename template"""
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
runner = CliRunner()
cwd = os.getcwd()
@@ -3882,7 +3925,7 @@ def test_export_filename_template_1():
def test_export_filename_template_2():
"""export photos using filename template with folder_album and path_sep"""
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
runner = CliRunner()
cwd = os.getcwd()
@@ -3907,7 +3950,7 @@ def test_export_filename_template_2():
def test_export_filename_template_strip():
"""export photos using filename template with --strip"""
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
runner = CliRunner()
cwd = os.getcwd()
@@ -3933,7 +3976,7 @@ def test_export_filename_template_strip():
def test_export_filename_template_pathsep_in_name_1():
"""export photos using filename template with folder_album and "/" in album name"""
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
runner = CliRunner()
cwd = os.getcwd()
@@ -3960,7 +4003,7 @@ def test_export_filename_template_pathsep_in_name_1():
def test_export_filename_template_pathsep_in_name_2():
"""export photos using filename template with keyword and "/" in keyword"""
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
runner = CliRunner()
cwd = os.getcwd()
@@ -3988,7 +4031,7 @@ def test_export_filename_template_pathsep_in_name_2():
def test_export_filename_template_long_description():
"""export photos using filename template with description that exceeds max length"""
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
runner = CliRunner()
cwd = os.getcwd()
@@ -4664,7 +4707,6 @@ def test_export_force_update():
export, [os.path.join(cwd, photos_db_path), ".", "--force-update"]
)
assert result.exit_code == 0
print(result.output)
assert (
f"Processed: {PHOTOS_NOT_IN_TRASH_LEN_15_7} photos, exported: 0, updated: 0, skipped: {PHOTOS_NOT_IN_TRASH_LEN_15_7+PHOTOS_EDITED_15_7}, updated EXIF data: 0, missing: 3, error: 0"
in result.output
@@ -4891,7 +4933,7 @@ def test_export_update_hardlink():
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
with isolated_filesystem_here():
# basic export
result = runner.invoke(
export,
@@ -4924,7 +4966,7 @@ def test_export_update_hardlink_exiftool():
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
with isolated_filesystem_here():
# basic export
result = runner.invoke(
export,
@@ -5069,7 +5111,7 @@ def test_export_then_hardlink():
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
with isolated_filesystem_here():
# basic export
result = runner.invoke(export, [os.path.join(cwd, CLI_PHOTOS_DB), ".", "-V"])
assert result.exit_code == 0
@@ -5177,7 +5219,7 @@ def test_export_update_edits_dry_run():
def test_export_directory_template_1_dry_run():
"""test export using directory template with dry-run flag"""
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
runner = CliRunner()
cwd = os.getcwd()
@@ -6052,7 +6094,7 @@ def test_export_as_hardlink_download_missing():
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
with isolated_filesystem_here():
result = runner.invoke(
export,
[
@@ -6877,6 +6919,7 @@ def test_export_finder_tag_keywords_dry_run():
assert result.exit_code == 0
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_export_finder_tag_keywords():
"""test --finder-tag-keywords"""
@@ -6951,6 +6994,7 @@ def test_export_finder_tag_keywords():
assert sorted(md.tags) == sorted(expected)
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_export_finder_tag_template():
"""test --finder-tag-template"""
@@ -7028,6 +7072,7 @@ def test_export_finder_tag_template():
assert sorted(md.tags) == sorted(expected)
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_export_finder_tag_template_multiple():
"""test --finder-tag-template used more than once"""
@@ -7061,6 +7106,7 @@ def test_export_finder_tag_template_multiple():
assert sorted(md.tags) == sorted(expected)
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_export_finder_tag_template_keywords():
"""test --finder-tag-template with --finder-tag-keywords"""
@@ -7093,6 +7139,7 @@ def test_export_finder_tag_template_keywords():
assert sorted(md.tags) == sorted(expected)
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_export_finder_tag_template_multi_field():
"""test --finder-tag-template with multiple fields (issue #422)"""
@@ -7157,6 +7204,7 @@ def test_export_xattr_template_dry_run():
assert "Writing extended attribute" in result.output
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_export_xattr_template():
"""test --xattr template"""
@@ -7655,14 +7703,14 @@ def test_query_name_unicode():
cwd = os.getcwd()
result = runner.invoke(
query,
["--json", "--db", os.path.join(cwd, PHOTOS_DB_15_7), "--name", "Frtest"],
["--json", "--db", os.path.join(cwd, PHOTOS_DB_15_7), "--name", "Frítest"],
)
assert result.exit_code == 0
json_got = json.loads(result.output)
assert len(json_got) == 4
assert normalize_unicode(json_got[0]["original_filename"]).startswith(
normalize_unicode("Frtest.jpg")
normalize_unicode("Frítest.jpg")
)

View File

@@ -1,10 +1,14 @@
"""Test osxphotos add-locations command"""
import photoscript
import pytest
from click.testing import CliRunner
from osxphotos.cli.add_locations import add_locations
from osxphotos.utils import is_macos
if is_macos:
import photoscript
from osxphotos.cli.add_locations import add_locations
else:
pytest.skip(allow_module_level=True)
UUID_TEST_PHOTO_1 = "F12384F6-CD17-4151-ACBA-AE0E3688539E" # Pumkins1.jpg
UUID_TEST_PHOTO_LOCATION = "D79B8D77-BFFC-460B-9312-034F2877D35B" # Pumkins2.jpg

View File

@@ -2,10 +2,15 @@
import os
import photoscript
import pytest
from click.testing import CliRunner
from osxphotos.utils import is_macos
if is_macos:
import photoscript
else:
pytest.skip(allow_module_level=True)
UUID_EXPORT = {"3DD2C897-F19E-4CA6-8C22-B027D5A71907": {"filename": "IMG_4547.jpg"}}
UUID_MISSING = {
"8E1D7BC9-9321-44F9-8CFB-4083F6B9232A": {"filename": "IMG_2000.JPGssss"}

View File

@@ -24,7 +24,6 @@ TEST_RUN_SCRIPT = "examples/cli_example_1.py"
def runner() -> CliRunner:
return CliRunner()
from osxphotos.cli import (
about,
albums,
@@ -42,10 +41,14 @@ from osxphotos.cli import (
places,
theme,
tutorial,
uuid,
version,
)
from osxphotos.utils import is_macos
if is_macos:
from osxphotos.cli import uuid
def test_about(runner: CliRunner):
with runner.isolated_filesystem():
@@ -68,9 +71,8 @@ def test_about(runner: CliRunner):
persons,
places,
tutorial,
uuid,
version,
],
] + ([uuid] if is_macos else []),
)
def test_cli_comands(runner: CliRunner, command: Callable[..., Any]):
with runner.isolated_filesystem():

View File

@@ -5,12 +5,17 @@ from __future__ import annotations
import os
import time
import photoscript
import pytest
from click.testing import CliRunner
import osxphotos
from osxphotos.cli.batch_edit import batch_edit
from osxphotos.utils import is_macos
if is_macos:
import photoscript
from osxphotos.cli.batch_edit import batch_edit
else:
pytest.skip(allow_module_level=True)
# set timezone to avoid issues with comparing dates
os.environ["TZ"] = "US/Pacific"

View File

@@ -14,17 +14,23 @@ from tempfile import TemporaryDirectory
from typing import Dict
import pytest
from click.testing import CliRunner
from photoscript import Photo
from pytest import MonkeyPatch, approx
from osxphotos import PhotosDB, QueryOptions
from osxphotos._constants import UUID_PATTERN
from osxphotos.cli.import_cli import import_cli
from osxphotos.datetime_utils import datetime_remove_tz
from osxphotos.exiftool import get_exiftool_path
from osxphotos.utils import is_macos
from tests.conftest import get_os_version
if is_macos:
from photoscript import Photo
from osxphotos.cli.import_cli import import_cli
else:
pytest.skip(allow_module_level=True)
TERMINAL_WIDTH = 250
TEST_IMAGES_DIR = "tests/test-images"

View File

@@ -2,11 +2,15 @@
import os
import json
import photoscript
import pytest
from click.testing import CliRunner
from osxphotos.cli.sync import sync
from osxphotos.utils import is_macos
if is_macos:
import photoscript
from osxphotos.cli.sync import sync
else:
pytest.skip(allow_module_level=True)
UUID_TEST_PHOTO_1 = "D79B8D77-BFFC-460B-9312-034F2877D35B" # Pumkins2.jpg
UUID_TEST_PHOTO_2 = "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51" # wedding.jpg

View File

@@ -1,6 +1,8 @@
""" test datetime_formatter.DateTimeFormatter """
import pytest
from .locale_util import setlocale
def test_datetime_formatter_1():
"""Test DateTimeFormatter """
@@ -8,7 +10,7 @@ def test_datetime_formatter_1():
import locale
from osxphotos.datetime_formatter import DateTimeFormatter
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
dt = datetime.datetime(2020, 5, 23, 12, 42, 33)
dtf = DateTimeFormatter(dt)
@@ -32,7 +34,7 @@ def test_datetime_formatter_2():
import locale
from osxphotos.datetime_formatter import DateTimeFormatter
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
dt = datetime.datetime(2020, 5, 23, 14, 42, 33)
dtf = DateTimeFormatter(dt)
@@ -56,7 +58,7 @@ def test_datetime_formatter_3():
import locale
from osxphotos.datetime_formatter import DateTimeFormatter
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
dt = datetime.datetime(2020, 5, 2, 9, 3, 6)
dtf = DateTimeFormatter(dt)

View File

@@ -538,13 +538,13 @@ def test_exiftool_terminate():
ps = subprocess.run(["ps"], capture_output=True)
stdout = ps.stdout.decode("utf-8")
assert "exiftool -stay_open" in stdout
assert "exiftool" in stdout
osxphotos.exiftool.terminate_exiftool()
ps = subprocess.run(["ps"], capture_output=True)
stdout = ps.stdout.decode("utf-8")
assert "exiftool -stay_open" not in stdout
assert "exiftool" not in stdout
# verify we can create a new instance after termination
exif2 = osxphotos.exiftool.ExifTool(TEST_FILE_ONE_KEYWORD)

View File

@@ -2,9 +2,9 @@ import os
import pytest
from osxphotos._constants import _UNKNOWN_PERSON
from osxphotos.utils import get_macos_version
from osxphotos.utils import is_macos, get_macos_version
OS_VERSION = get_macos_version()
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "15"
PHOTOS_DB = os.path.expanduser("~/Pictures/Photos Library.photoslibrary")
pytestmark = pytest.mark.skipif(

View File

@@ -74,10 +74,12 @@ def test_hardlink_file_valid():
temp_dir = tempfile.TemporaryDirectory(prefix="osxphotos_")
src = "tests/test-images/wedding.jpg"
src2 = os.path.join(temp_dir.name, "wedding_src.jpg")
dest = os.path.join(temp_dir.name, "wedding.jpg")
FileUtil.hardlink(src, dest)
FileUtil.copy(src, src2)
FileUtil.hardlink(src2, dest)
assert os.path.isfile(dest)
assert os.path.samefile(src, dest)
assert os.path.samefile(src2, dest)
def test_unlink_file():

View File

@@ -14,9 +14,9 @@ import pytest
import osxphotos
from osxphotos._constants import _UNKNOWN_PERSON
from osxphotos.photoexporter import PhotoExporter
from osxphotos.utils import get_macos_version
from osxphotos.utils import is_macos, get_macos_version
OS_VERSION = get_macos_version()
OS_VERSION = get_macos_version() if is_macos else (None, None, None)
# SKIP_TEST = "OSXPHOTOS_TEST_EXPORT" not in os.environ or OS_VERSION[1] != "17"
SKIP_TEST = True # don't run any of the local library tests
PHOTOS_DB_LOCAL = os.path.expanduser("~/Pictures/Photos Library.photoslibrary")

View File

@@ -6,15 +6,19 @@ import tempfile
import pytest
from osxphotos.photokit import (
LivePhotoAsset,
PhotoAsset,
PhotoLibrary,
VideoAsset,
PHOTOS_VERSION_CURRENT,
PHOTOS_VERSION_ORIGINAL,
PHOTOS_VERSION_UNADJUSTED,
)
from osxphotos.utils import is_macos
if is_macos:
from osxphotos.photokit import (
LivePhotoAsset,
PhotoAsset,
PhotoLibrary,
VideoAsset,
PHOTOS_VERSION_CURRENT,
PHOTOS_VERSION_ORIGINAL,
PHOTOS_VERSION_UNADJUSTED,
)
else:
pytest.skip(allow_module_level=True)
skip_test = "OSXPHOTOS_TEST_EXPORT" not in os.environ
pytestmark = pytest.mark.skipif(

View File

@@ -15,7 +15,9 @@ from osxphotos.phototemplate import (
RenderOptions,
)
from osxphotos.photoinfo import PhotoInfoNone
from osxphotos.utils import is_macos
from .photoinfo_mock import PhotoInfoMock
from .locale_util import setlocale
try:
exiftool = get_exiftool_path()
@@ -549,6 +551,8 @@ def test_lookup_multi(photosdb_places):
lookup_str = re.match(r"\{([^\\,}]+)\}", subst).group(1)
if subst in ["{exiftool}", "{photo}", "{function}", "{format}"]:
continue
if subst == "{detected_text}" and not is_macos:
continue
lookup = template.get_template_value_multi(
lookup_str,
path_sep=os.path.sep,
@@ -562,7 +566,7 @@ def test_subst(photosdb_places):
"""Test that substitutions are correct"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb_places.photos(uuid=[UUID_DICT["place_dc"]])[0]
for template in TEMPLATE_VALUES:
@@ -574,7 +578,7 @@ def test_subst_date_modified(photosdb_places):
"""Test that substitutions are correct for date modified"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb_places.photos(uuid=[UUID_DICT["date_modified"]])[0]
for template in TEMPLATE_VALUES_DATE_MODIFIED:
@@ -586,7 +590,7 @@ def test_subst_date_not_modified(photosdb_places):
"""Test that substitutions are correct for date modified when photo isn't modified"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb_places.photos(uuid=[UUID_DICT["date_not_modified"]])[0]
for template in TEMPLATE_VALUES_DATE_NOT_MODIFIED:
@@ -600,7 +604,7 @@ def test_subst_locale_1(photosdb_places):
# osxphotos.template sets local on load so set the environment first
# set locale to DE
locale.setlocale(locale.LC_ALL, "de_DE.UTF-8")
setlocale(locale.LC_ALL, "de_DE.UTF-8")
photo = photosdb_places.photos(uuid=[UUID_DICT["place_dc"]])[0]
@@ -614,6 +618,9 @@ def test_subst_locale_2(photosdb_places):
import locale
import os
# Check if locale is available
setlocale(locale.LC_ALL, "de_DE.UTF-8")
# osxphotos.template sets local on load so set the environment first
os.environ["LANG"] = "de_DE.UTF-8"
os.environ["LC_COLLATE"] = "de_DE.UTF-8"
@@ -634,7 +641,7 @@ def test_subst_default_val(photosdb_places):
"""Test substitution with default value specified"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb_places.photos(uuid=[UUID_DICT["place_dc"]])[0]
template = "{place.name.area_of_interest,UNKNOWN}"
@@ -646,7 +653,7 @@ def test_subst_default_val_2(photosdb_places):
"""Test substitution with ',' but no default value"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb_places.photos(uuid=[UUID_DICT["place_dc"]])[0]
template = "{place.name.area_of_interest,}"
@@ -658,7 +665,7 @@ def test_subst_unknown_val(photosdb_places):
"""Test substitution with unknown value specified"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb_places.photos(uuid=[UUID_DICT["place_dc"]])[0]
template = "{created.year}/{foo}"
@@ -682,7 +689,7 @@ def test_subst_unknown_val_with_default(photosdb_places):
"""Test substitution with unknown value specified"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb_places.photos(uuid=[UUID_DICT["place_dc"]])[0]
template = "{created.year}/{foo,bar}"
@@ -929,7 +936,7 @@ def test_subst_strftime(photosdb_places):
"""Test that strftime substitutions are correct"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb_places.photos(uuid=[UUID_DICT["place_dc"]])[0]
rendered, unmatched = photo.render_template("{created.strftime,%Y-%m-%d-%H%M%S}")
@@ -1287,6 +1294,7 @@ def test_album_seq(photosdb):
assert rendered[0] == value
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_detected_text(photosdb):
"""Test {detected_text} template"""
photo = photosdb.get_photo(UUID_DETECTED_TEXT)

View File

@@ -5,6 +5,8 @@ import pytest
import osxphotos
from osxphotos.phototemplate import RenderOptions
from .locale_util import setlocale
PHOTOS_DB_PLACES = (
"./tests/Test-Places-Catalina-10_15_1.photoslibrary/database/photos.db"
)
@@ -48,7 +50,7 @@ def test_subst_today(photosdb):
"""Test that substitutions are correct for {today.x}"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb.photos(uuid=[UUID_DICT["place_dc"]])[0]
photo_template = osxphotos.PhotoTemplate(photo)
@@ -64,7 +66,7 @@ def test_subst_strftime_today(photosdb):
"""Test that strftime substitutions are correct for {today.strftime}"""
import locale
locale.setlocale(locale.LC_ALL, "en_US")
setlocale(locale.LC_ALL, "en_US")
photo = photosdb.photos(uuid=[UUID_DICT["place_dc"]])[0]
photo_template = osxphotos.PhotoTemplate(photo)

View File

@@ -8,6 +8,7 @@ from osxphotos.uti import (
get_preferred_uti_extension,
get_uti_for_extension,
)
from osxphotos.utils import is_macos
EXT_DICT = {"heic": "public.heic", "jpg": "public.jpeg", ".jpg": "public.jpeg"}
UTI_DICT = {"public.heic": "heic", "public.jpeg": "jpeg"}
@@ -43,12 +44,14 @@ def test_get_uti_for_extension_no_objc():
osxphotos.uti.OS_VER = OLD_VER
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_get_uti_from_mdls():
"""get _get_uti_from_mdls"""
for ext in EXT_DICT:
assert _get_uti_from_mdls(ext) == EXT_DICT[ext]
@pytest.mark.skipif(not is_macos, reason="Only works on macOS")
def test_get_uti_not_in_dict():
"""get UTI when objc is not available and it's not in the EXT_UTI_DICT"""
# monkey patch the EXT_UTI_DICT