* Starting to implement catch_errors for sidecar_template * Fixed error printing * Fixed error printing * Added tests for catch_errors
595 lines
19 KiB
Python
595 lines
19 KiB
Python
"""Test export with --sidecar-template (#1123)"""
|
|
|
|
import csv
|
|
import json
|
|
import os
|
|
import pathlib
|
|
import sqlite3
|
|
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
|
|
from osxphotos.cli import export
|
|
|
|
PHOTOS_DB = "./tests/Test-10.15.7.photoslibrary"
|
|
|
|
PHOTO_UUID = "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51" # wedding.jpg
|
|
SIDECAR_FILENAME = "wedding.jpg.txt"
|
|
SIDECAR_FILENAME_2 = "wedding.jpg.sidecar"
|
|
SIDECAR_DATA = """
|
|
|
|
|
|
Sidecar: wedding.jpg.txt
|
|
Photo: wedding.jpg
|
|
UUID: E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51
|
|
Rating: ★★★★★
|
|
"""
|
|
SIDECAR_DATA_2 = """
|
|
|
|
|
|
Sidecar: wedding.jpg.sidecar
|
|
Photo: wedding.jpg
|
|
UUID: E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51
|
|
Rating: ★★★★★
|
|
"""
|
|
|
|
PHOTO_UUID_NO_KEYWORD = "4D521201-92AC-43E5-8F7C-59BC41C37A96" # IMG_1997.CR2
|
|
SIDECAR_FILENAME_NO_KEYWORD = "IMG_1997.CR2.txt"
|
|
|
|
|
|
def test_export_sidecar_template_1():
|
|
"""test basic export with --sidecar-template"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
assert sidecar_data == SIDECAR_DATA
|
|
|
|
|
|
def test_export_sidecar_template_option_case():
|
|
"""test basic export with --sidecar-template and option case insensitivity"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"None",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
assert sidecar_data == SIDECAR_DATA
|
|
|
|
|
|
def test_export_sidecar_template_strip_whitespace():
|
|
"""test basic export with --sidecar-template and STRIP_WHITESPACE = True"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"strip_whitespace",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
sidecar_expected = (
|
|
"\n".join(line.strip() for line in SIDECAR_DATA.splitlines()) + "\n"
|
|
)
|
|
assert sidecar_data == sidecar_expected
|
|
|
|
|
|
def test_export_sidecar_template_strip_lines():
|
|
"""test basic export with --sidecar-template and STRIP_LINES = True"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"strip_lines",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
sidecar_expected = "\n".join(
|
|
line for line in SIDECAR_DATA.splitlines() if line.strip()
|
|
)
|
|
assert sidecar_data == sidecar_expected
|
|
|
|
|
|
def test_export_sidecar_template_strip_lines_strip_whitespace():
|
|
"""test basic export with --sidecar-template and STRIP_LINES = True and STRIP_WHITESPACE = True"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"strip_whitespace,strip_lines",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
sidecar_expected = "\n".join(
|
|
line.strip() for line in SIDECAR_DATA.splitlines() if line.strip()
|
|
)
|
|
assert sidecar_data == sidecar_expected
|
|
|
|
|
|
def test_export_sidecar_template_strip_lines_strip_whitespace_option_space():
|
|
"""test basic export with --sidecar-template and STRIP_LINES = True and STRIP_WHITESPACE = True with space in option"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"strip_whitespace, strip_lines",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
sidecar_expected = "\n".join(
|
|
line.strip() for line in SIDECAR_DATA.splitlines() if line.strip()
|
|
)
|
|
assert sidecar_data == sidecar_expected
|
|
|
|
|
|
def test_export_sidecar_template_update_no():
|
|
"""test basic export with --sidecar-template and WRITE_SKIPPED = False, also test --cleanup"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
],
|
|
)
|
|
|
|
# run export again, should not update sidecar
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
"--update",
|
|
"--cleanup",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
sidecar_expected = "\n".join(
|
|
line.strip() for line in SIDECAR_DATA.splitlines() if line.strip()
|
|
)
|
|
assert sidecar_data == SIDECAR_DATA
|
|
assert "Skipping existing sidecar file" in result.output
|
|
assert "Deleted: 0 files, 0 directories" in result.output
|
|
|
|
|
|
def test_export_sidecar_template_update_ues():
|
|
"""test basic export with --sidecar-template and WRITE_SKIPPED = True, also test --cleanup"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
],
|
|
)
|
|
|
|
# run export again, should not update sidecar
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"write_skipped",
|
|
"--update",
|
|
"--cleanup",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
sidecar_expected = "\n".join(
|
|
line.strip() for line in SIDECAR_DATA.splitlines() if line.strip()
|
|
)
|
|
assert sidecar_data == SIDECAR_DATA
|
|
assert "Skipping existing sidecar file" not in result.output
|
|
assert "Writing sidecar file" in result.output
|
|
assert "Deleted: 0 files, 0 directories" in result.output
|
|
|
|
|
|
def test_export_sidecar_template_report_csv():
|
|
"""test basic export with --sidecar-template --report to csv"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
"--report",
|
|
"report.csv",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
# verify report output
|
|
report_file = pathlib.Path("report.csv")
|
|
assert report_file.exists()
|
|
csvreader = csv.DictReader(report_file.open())
|
|
assert "sidecar_user" in csvreader.fieldnames
|
|
|
|
found_sidecar = 0
|
|
for row in csvreader: # sourcery skip: no-loop-in-tests
|
|
# sidecar ends with .txt so verify report has sidecar_user = 1
|
|
if row["filename"].endswith(
|
|
".txt"
|
|
): # sourcery skip: no-conditionals-in-tests
|
|
assert str(row["sidecar_user"]) == "1"
|
|
found_sidecar += 1
|
|
else:
|
|
assert str(row["sidecar_user"]) == "0"
|
|
assert found_sidecar
|
|
|
|
|
|
def test_export_sidecar_template_report_json():
|
|
"""test basic export with --sidecar-template --report to json"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
"--report",
|
|
"report.json",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
# read the json report output and verify it is correct
|
|
report_file = pathlib.Path("report.json")
|
|
assert report_file.exists()
|
|
report_data = json.loads(report_file.read_text())
|
|
assert "sidecar_user" in report_data[0]
|
|
found_sidecar = 0
|
|
for row in report_data: # sourcery skip: no-loop-in-tests
|
|
# sidecar ends with .txt so verify report has sidecar_user = 1
|
|
if row["filename"].endswith(
|
|
".txt"
|
|
): # sourcery skip: no-conditionals-in-tests
|
|
assert row["sidecar_user"]
|
|
found_sidecar += 1
|
|
else:
|
|
assert not row["sidecar_user"]
|
|
assert found_sidecar
|
|
|
|
|
|
def test_export_sidecar_template_report_db():
|
|
"""test basic export with --sidecar-template --report to sqlite db"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
"--report",
|
|
"report.db",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
# read the report sqlite db and verify it is correct
|
|
report_file = pathlib.Path("report.db")
|
|
assert report_file.exists()
|
|
conn = sqlite3.connect(report_file)
|
|
c = conn.cursor()
|
|
c.execute("SELECT filename, sidecar_user FROM report")
|
|
rows = c.fetchall()
|
|
found_sidecar = 0
|
|
for row in rows: # sourcery skip: no-loop-in-tests
|
|
# sidecar ends with .txt so verify report has sidecar_user = 1
|
|
if row[0].endswith(".txt"): # sourcery skip: no-conditionals-in-tests
|
|
assert row[1] == 1
|
|
found_sidecar += 1
|
|
else:
|
|
assert row[1] == 0
|
|
assert found_sidecar
|
|
|
|
|
|
def test_export_sidecar_template_multiple():
|
|
"""test export with multiple --sidecar-template options"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.sidecar",
|
|
"none",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
assert sidecar_data == SIDECAR_DATA
|
|
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME_2)
|
|
assert sidecar_file.exists()
|
|
sidecar_data = sidecar_file.read_text()
|
|
assert sidecar_data == SIDECAR_DATA_2
|
|
|
|
|
|
def test_export_sidecar_template_full_library():
|
|
"""test export with --sidecar-template option against full library (repeated calls to generate sidecar files))"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_export_sidecar_template_skip_zero():
|
|
"""test basic export with --sidecar-template with skip_zero option"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--uuid",
|
|
PHOTO_UUID_NO_KEYWORD,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar_zero.mako"),
|
|
"{filepath}.txt",
|
|
"strip_whitespace,strip_lines,skip_zero",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
assert "Skipping empty sidecar file" in result.output
|
|
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
|
|
assert sidecar_file.exists()
|
|
|
|
sidecar_file = pathlib.Path(SIDECAR_FILENAME_NO_KEYWORD)
|
|
assert not sidecar_file.exists()
|
|
|
|
|
|
def test_export_sidecar_template_error():
|
|
"""test basic export with --sidecar-template that generates an error"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar_bad.mako"),
|
|
"{filepath}.txt",
|
|
"none",
|
|
],
|
|
)
|
|
assert result.exit_code != 0
|
|
|
|
|
|
def test_export_sidecar_template_catch_error():
|
|
"""test basic export with --sidecar-template that catches an error"""
|
|
runner = CliRunner()
|
|
cwd = os.getcwd()
|
|
# pylint: disable=not-context-manager
|
|
with runner.isolated_filesystem():
|
|
result = runner.invoke(
|
|
export,
|
|
[
|
|
"--library",
|
|
os.path.join(cwd, PHOTOS_DB),
|
|
".",
|
|
"-V",
|
|
"--uuid",
|
|
PHOTO_UUID,
|
|
"--sidecar-template",
|
|
os.path.join(cwd, "tests", "custom_sidecar_bad.mako"),
|
|
"{filepath}.txt",
|
|
"catch_errors",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|