Feature batch edit 949 (#1001)
* Initial implementation of batch-edit, #949 * Added tests for batch-edit, #949
This commit is contained in:
@@ -26,6 +26,9 @@ A couple of tests require interaction with Photos and configuring a specific tes
|
||||
--addalbum: test --add-to-album options
|
||||
--timewarp: test `osxphotos timewarp`
|
||||
--test-import: test `osxphotos import`
|
||||
--test-sync: test `osxphotos sync`
|
||||
--test-add-locations: test `osxphotos add-locations`
|
||||
--test-batch-edit: test `osxphotos batch-edit`
|
||||
|
||||
## Test Photo Libraries
|
||||
**Important**: The test code uses several test photo libraries created on various version of MacOS. If you need to inspect one of these or modify one for a test, make a copy of the library (for example, copy it to your ~/Pictures folder) then open the copy in Photos. Once done, copy the revised library back to the tests/ folder. If you do not do this, the Photos background process photoanalysisd will forever try to process the library resulting in updates to the database which will cause git to see changes to the file you didn't intend. I'm not aware of any way to disassociate photoanalysisd from the library once you've opened it in Photos.
|
||||
|
||||
@@ -26,6 +26,9 @@ TEST_SYNC = False
|
||||
# run add-locations tests (configured with --test-add-locations)
|
||||
TEST_ADD_LOCATIONS = False
|
||||
|
||||
# run batch-edit tests (configured with --test-batch-edit)
|
||||
TEST_BATCH_EDIT = False
|
||||
|
||||
# don't clean up crash logs (configured with --no-cleanup)
|
||||
NO_CLEANUP = False
|
||||
|
||||
@@ -124,6 +127,7 @@ def pytest_addoption(parser):
|
||||
default=False,
|
||||
help="run `osxphotos import` tests",
|
||||
)
|
||||
parser.addoption("--test-batch-edit", action="store_true", default=False)
|
||||
parser.addoption(
|
||||
"--test-sync",
|
||||
action="store_true",
|
||||
@@ -153,12 +157,14 @@ def pytest_configure(config):
|
||||
config.getoption("--timewarp"),
|
||||
config.getoption("--test-import"),
|
||||
config.getoption("--test-sync"),
|
||||
config.getoption("--test-batch-edit"),
|
||||
0,
|
||||
]
|
||||
)
|
||||
> 1
|
||||
):
|
||||
pytest.exit(
|
||||
"--addalbum, --timewarp, --test-import, --test-sync are mutually exclusive"
|
||||
"--addalbum, --timewarp, --test-import, --test-sync, --test-batch-edit are mutually exclusive"
|
||||
)
|
||||
|
||||
config.addinivalue_line(
|
||||
@@ -177,6 +183,9 @@ def pytest_configure(config):
|
||||
"markers",
|
||||
"test_add_locations: mark test as requiring --test-add-locations to run",
|
||||
)
|
||||
config.addinivalue_line(
|
||||
"markers", "test_batch_edit: mark test as requiring --test-batch-edit to run"
|
||||
)
|
||||
|
||||
# this is hacky but I can't figure out how to check config options in other fixtures
|
||||
if config.getoption("--timewarp"):
|
||||
@@ -199,40 +208,44 @@ def pytest_configure(config):
|
||||
global NO_CLEANUP
|
||||
NO_CLEANUP = True
|
||||
|
||||
if config.getoption("--test-batch-edit"):
|
||||
global TEST_BATCH_EDIT
|
||||
TEST_BATCH_EDIT = True
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
if not (config.getoption("--addalbum") and TEST_LIBRARY is not None):
|
||||
skip_addalbum = pytest.mark.skip(
|
||||
reason="need --addalbum option and MacOS Catalina to run"
|
||||
)
|
||||
skip_addalbum = pytest.mark.skip(reason="need --addalbum option to run")
|
||||
for item in items:
|
||||
if "addalbum" in item.keywords:
|
||||
item.add_marker(skip_addalbum)
|
||||
|
||||
if not (config.getoption("--timewarp") and TEST_LIBRARY_TIMEWARP is not None):
|
||||
skip_timewarp = pytest.mark.skip(
|
||||
reason="need --timewarp option and MacOS Catalina to run"
|
||||
)
|
||||
skip_timewarp = pytest.mark.skip(reason="need --timewarp option to run")
|
||||
for item in items:
|
||||
if "timewarp" in item.keywords:
|
||||
item.add_marker(skip_timewarp)
|
||||
|
||||
if not (config.getoption("--test-import") and TEST_LIBRARY_IMPORT is not None):
|
||||
skip_test_import = pytest.mark.skip(
|
||||
reason="need --test-import option and MacOS Catalina or Ventura to run"
|
||||
)
|
||||
skip_test_import = pytest.mark.skip(reason="need --test-import option to run")
|
||||
for item in items:
|
||||
if "test_import" in item.keywords:
|
||||
item.add_marker(skip_test_import)
|
||||
|
||||
if not (config.getoption("--test-sync") and TEST_LIBRARY_SYNC is not None):
|
||||
skip_test_sync = pytest.mark.skip(
|
||||
reason="need --test-sync option and MacOS Catalina to run"
|
||||
)
|
||||
skip_test_sync = pytest.mark.skip(reason="need --test-sync option to run")
|
||||
for item in items:
|
||||
if "test_sync" in item.keywords:
|
||||
item.add_marker(skip_test_sync)
|
||||
|
||||
if not (config.getoption("--test-batch-edit")):
|
||||
skip_test_batch_edit = pytest.mark.skip(
|
||||
reason="need --test-batch-edit option to run"
|
||||
)
|
||||
for item in items:
|
||||
if "test_batch_edit" in item.keywords:
|
||||
item.add_marker(skip_test_batch_edit)
|
||||
|
||||
if not (
|
||||
config.getoption("--test-add-locations")
|
||||
and TEST_LIBRARY_ADD_LOCATIONS is not None
|
||||
|
||||
166
tests/test_cli_batch_edit.py
Normal file
166
tests/test_cli_batch_edit.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""Test osxphotos batch-edit command"""
|
||||
|
||||
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
|
||||
|
||||
# set timezone to avoid issues with comparing dates
|
||||
os.environ["TZ"] = "US/Pacific"
|
||||
time.tzset()
|
||||
|
||||
|
||||
TEST_DATA_BATCH_EDIT = {
|
||||
"uuid": "F12384F6-CD17-4151-ACBA-AE0E3688539E", # Pumkins1.jpg,
|
||||
"data": [
|
||||
(
|
||||
["--title", "Pumpkin Farm {created.year}-{created.mm}-{created.dd}"],
|
||||
{"title": "Pumpkin Farm 2018-09-28"},
|
||||
),
|
||||
(
|
||||
[
|
||||
"--description",
|
||||
"Pumpkin Farm {created.year}",
|
||||
"--keyword",
|
||||
"kids",
|
||||
"--keyword",
|
||||
"holiday",
|
||||
],
|
||||
{
|
||||
"description": "Pumpkin Farm 2018",
|
||||
"keywords": sorted(["kids", "holiday"]),
|
||||
},
|
||||
),
|
||||
(
|
||||
["--location", "34.052235", "-118.243683"],
|
||||
{"location": (34.052235, -118.243683)},
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def say(msg: str) -> None:
|
||||
"""Say message with text to speech"""
|
||||
os.system(f"say {msg}")
|
||||
|
||||
|
||||
def ask_user_to_make_selection(
|
||||
photoslib: photoscript.PhotosLibrary, suspend_capture, msg: str
|
||||
) -> list[photoscript.Photo]:
|
||||
"""Ask user to make selection in Photos and press enter when done"""
|
||||
with suspend_capture:
|
||||
photoslib.activate()
|
||||
say(f"Select the photo of the {msg} in Photos and press enter when done")
|
||||
input("Press enter when done")
|
||||
return photoslib.selection
|
||||
|
||||
|
||||
@pytest.mark.test_batch_edit
|
||||
def test_select_photo(photoslib, suspend_capture):
|
||||
"""Test batch-edit command"""
|
||||
photos = ask_user_to_make_selection(
|
||||
photoslib, suspend_capture, "children lifting the pumpkins"
|
||||
)
|
||||
assert len(photos) == 1
|
||||
photo = photos[0]
|
||||
assert photo.uuid == TEST_DATA_BATCH_EDIT["uuid"]
|
||||
|
||||
# initialize the photo's metadata
|
||||
photo.title = None
|
||||
photo.description = None
|
||||
photo.keywords = None
|
||||
photo.location = None
|
||||
|
||||
|
||||
@pytest.mark.test_batch_edit
|
||||
@pytest.mark.parametrize("args,expected", TEST_DATA_BATCH_EDIT["data"])
|
||||
def test_batch_edit(args, expected):
|
||||
"""Test batch-edit command"""
|
||||
with CliRunner().isolated_filesystem():
|
||||
result = CliRunner().invoke(
|
||||
batch_edit,
|
||||
[*args, "--dry-run"],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
|
||||
for key, expected_value in expected.items():
|
||||
got = getattr(photo, key)
|
||||
if isinstance(got, list):
|
||||
got = sorted(got)
|
||||
assert got != expected_value
|
||||
|
||||
result = CliRunner().invoke(
|
||||
batch_edit,
|
||||
[*args],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
|
||||
for key, expected_value in expected.items():
|
||||
got = getattr(photo, key)
|
||||
if isinstance(got, list):
|
||||
got = sorted(got)
|
||||
assert got == expected_value
|
||||
|
||||
|
||||
@pytest.mark.test_batch_edit
|
||||
def test_batch_edit_undo(photoslib):
|
||||
"""Test batch-edit command with --undo"""
|
||||
photo = photoslib.selection[0]
|
||||
assert photo.uuid == TEST_DATA_BATCH_EDIT["uuid"]
|
||||
photo.title = "Pumpkin Farm"
|
||||
photo.description = "Pumpkin Farm"
|
||||
photo.keywords = ["kids"]
|
||||
photo.location = (41.256566, -95.940257)
|
||||
|
||||
with CliRunner().isolated_filesystem():
|
||||
result = CliRunner().invoke(
|
||||
batch_edit,
|
||||
[
|
||||
"--title",
|
||||
"Test",
|
||||
"--description",
|
||||
"Test",
|
||||
"--keyword",
|
||||
"test",
|
||||
"--location",
|
||||
"34.052235",
|
||||
"-118.243683",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
|
||||
assert photo.title == "Test"
|
||||
assert photo.description == "Test"
|
||||
assert photo.keywords == ["test"]
|
||||
assert photo.location == (34.052235, -118.243683)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
batch_edit,
|
||||
["--undo", "--dry-run"],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
|
||||
assert photo.title == "Test"
|
||||
assert photo.description == "Test"
|
||||
assert photo.keywords == ["test"]
|
||||
assert photo.location == (34.052235, -118.243683)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
batch_edit,
|
||||
["--undo"],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
photo = osxphotos.PhotosDB().get_photo(TEST_DATA_BATCH_EDIT["uuid"])
|
||||
assert photo.title == "Pumpkin Farm"
|
||||
assert photo.description == "Pumpkin Farm"
|
||||
assert photo.keywords == ["kids"]
|
||||
assert photo.location == (41.256566, -95.940257)
|
||||
Reference in New Issue
Block a user