Add pytest support

This commit is contained in:
Hidorikun
2024-05-06 12:06:30 +03:00
parent 57a15f651e
commit fb7c73d96b
12 changed files with 182 additions and 178 deletions

View File

@@ -72,7 +72,12 @@ TagStudio is a photo & file organization application with an underlying system t
- Linux/macOS: `source .venv/bin/activate`
3. Install the required packages:
`pip install -r requirements.txt`
- required to run the app: `pip install -r requirements.txt`
- required to develop: `pip install -r requirements-dev.txt`
To run all the tests use `python -m pytest tests/` from the `tagstudio` folder.
_Learn more about setting up a virtual environment [here](https://docs.python.org/3/tutorial/venv.html)._

View File

@@ -1,2 +1,3 @@
ruff==0.4.2
pre-commit==3.7.0
pytest==8.2.0

View File

@@ -0,0 +1,137 @@
VERSION: str = "9.2.0" # Major.Minor.Patch
VERSION_BRANCH: str = "Alpha" # 'Alpha', 'Beta', or '' for Full Release
# The folder & file names where TagStudio keeps its data relative to a library.
TS_FOLDER_NAME: str = ".TagStudio"
BACKUP_FOLDER_NAME: str = "backups"
COLLAGE_FOLDER_NAME: str = "collages"
LIBRARY_FILENAME: str = "ts_library.json"
# TODO: Turn this whitelist into a user-configurable blacklist.
IMAGE_TYPES: list[str] = [
"png",
"jpg",
"jpeg",
"jpg_large",
"jpeg_large",
"jfif",
"gif",
"tif",
"tiff",
"heic",
"heif",
"webp",
"bmp",
"svg",
"avif",
"apng",
"jp2",
"j2k",
"jpg2",
]
VIDEO_TYPES: list[str] = [
"mp4",
"webm",
"mov",
"hevc",
"mkv",
"avi",
"wmv",
"flv",
"gifv",
"m4p",
"m4v",
"3gp",
]
AUDIO_TYPES: list[str] = [
"mp3",
"mp4",
"mpeg4",
"m4a",
"aac",
"wav",
"flac",
"alac",
"wma",
"ogg",
"aiff",
]
DOC_TYPES: list[str] = ["txt", "rtf", "md", "doc", "docx", "pdf", "tex", "odt", "pages"]
PLAINTEXT_TYPES: list[str] = [
"txt",
"md",
"css",
"html",
"xml",
"json",
"js",
"ts",
"ini",
"htm",
"csv",
"php",
"sh",
"bat",
]
SPREADSHEET_TYPES: list[str] = ["csv", "xls", "xlsx", "numbers", "ods"]
PRESENTATION_TYPES: list[str] = ["ppt", "pptx", "key", "odp"]
ARCHIVE_TYPES: list[str] = ["zip", "rar", "tar", "tar.gz", "tgz", "7z"]
PROGRAM_TYPES: list[str] = ["exe", "app"]
SHORTCUT_TYPES: list[str] = ["lnk", "desktop", "url"]
ALL_FILE_TYPES: list[str] = (
IMAGE_TYPES
+ VIDEO_TYPES
+ AUDIO_TYPES
+ DOC_TYPES
+ SPREADSHEET_TYPES
+ PRESENTATION_TYPES
+ ARCHIVE_TYPES
+ PROGRAM_TYPES
+ SHORTCUT_TYPES
)
BOX_FIELDS = ["tag_box", "text_box"]
TEXT_FIELDS = ["text_line", "text_box"]
DATE_FIELDS = ["datetime"]
TAG_COLORS = [
"",
"black",
"dark gray",
"gray",
"light gray",
"white",
"light pink",
"pink",
"red",
"red orange",
"orange",
"yellow orange",
"yellow",
"lime",
"light green",
"mint",
"green",
"teal",
"cyan",
"light blue",
"blue",
"blue violet",
"violet",
"purple",
"lavender",
"berry",
"magenta",
"salmon",
"auburn",
"dark brown",
"brown",
"light brown",
"blonde",
"peach",
"warm gray",
"cool gray",
"olive",
]

View File

@@ -17,9 +17,9 @@ from enum import Enum
import ujson
from src.core.json_typing import JsonCollation, JsonEntry, JsonLibary, JsonTag
from src.core import ts_core
from src.core.utils.str import strip_punctuation
from src.core.utils.web import strip_web_protocol
from src.core.constants import BACKUP_FOLDER_NAME, COLLAGE_FOLDER_NAME, TEXT_FIELDS, TS_FOLDER_NAME, VERSION
TYPE = ["file", "meta", "alt", "mask"]
@@ -438,8 +438,8 @@ class Library:
path = os.path.normpath(path).rstrip("\\")
# If '.TagStudio' is included in the path, trim the path up to it.
if ts_core.TS_FOLDER_NAME in path:
path = path.split(ts_core.TS_FOLDER_NAME)[0]
if TS_FOLDER_NAME in path:
path = path.split(TS_FOLDER_NAME)[0]
try:
self.clear_internal_vars()
@@ -456,12 +456,12 @@ class Library:
def verify_ts_folders(self) -> None:
"""Verifies/creates folders required by TagStudio."""
full_ts_path = os.path.normpath(f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}")
full_ts_path = os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}")
full_backup_path = os.path.normpath(
f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}/{ts_core.BACKUP_FOLDER_NAME}"
f"{self.library_dir}/{TS_FOLDER_NAME}/{BACKUP_FOLDER_NAME}"
)
full_collage_path = os.path.normpath(
f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}/{ts_core.COLLAGE_FOLDER_NAME}"
f"{self.library_dir}/{TS_FOLDER_NAME}/{COLLAGE_FOLDER_NAME}"
)
if not os.path.isdir(full_ts_path):
@@ -499,16 +499,16 @@ class Library:
path = os.path.normpath(path).rstrip("\\")
# If '.TagStudio' is included in the path, trim the path up to it.
if ts_core.TS_FOLDER_NAME in path:
path = path.split(ts_core.TS_FOLDER_NAME)[0]
if TS_FOLDER_NAME in path:
path = path.split(TS_FOLDER_NAME)[0]
if os.path.exists(
os.path.normpath(f"{path}/{ts_core.TS_FOLDER_NAME}/ts_library.json")
os.path.normpath(f"{path}/{TS_FOLDER_NAME}/ts_library.json")
):
try:
with open(
os.path.normpath(
f"{path}/{ts_core.TS_FOLDER_NAME}/ts_library.json"
f"{path}/{TS_FOLDER_NAME}/ts_library.json"
),
"r",
encoding="utf-8",
@@ -718,10 +718,10 @@ class Library:
# If the Library is loaded, continue other processes.
if return_code == 1:
if not os.path.exists(
os.path.normpath(f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}")
os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}")
):
os.makedirs(
os.path.normpath(f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}")
os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}")
)
self._map_filenames_to_entry_ids()
@@ -769,7 +769,7 @@ class Library:
"""
file_to_save: JsonLibary = {
"ts-version": ts_core.VERSION,
"ts-version": VERSION,
"ignored_extensions": [],
"tags": [],
"collations": [],
@@ -807,7 +807,7 @@ class Library:
self.verify_ts_folders()
with open(
os.path.normpath(f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}/{filename}"),
os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}/{filename}"),
"w",
encoding="utf-8",
) as outfile:
@@ -836,7 +836,7 @@ class Library:
self.verify_ts_folders()
with open(
os.path.normpath(
f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}/{ts_core.BACKUP_FOLDER_NAME}/{filename}"
f"{self.library_dir}/{TS_FOLDER_NAME}/{BACKUP_FOLDER_NAME}/{filename}"
),
"w",
encoding="utf-8",
@@ -893,13 +893,13 @@ class Library:
# Scans the directory for files, keeping track of:
# - Total file count
# - Files without library entries
# for type in ts_core.TYPES:
# for type in TYPES:
start_time = time.time()
for f in glob.glob(self.library_dir + "/**/*", recursive=True):
# p = Path(os.path.normpath(f))
if (
"$RECYCLE.BIN" not in f
and ts_core.TS_FOLDER_NAME not in f
and TS_FOLDER_NAME not in f
and "tagstudio_thumbs" not in f
and not os.path.isdir(f)
):
@@ -2113,7 +2113,7 @@ class Library:
# entry = self.entries[entry_index]
entry = self.get_entry(entry_id)
field_type = self.get_field_obj(field_id)["type"]
if field_type in ts_core.TEXT_FIELDS:
if field_type in TEXT_FIELDS:
entry.fields.append({int(field_id): ""})
elif field_type == "tag_box":
entry.fields.append({int(field_id): []})

View File

@@ -9,144 +9,6 @@ import os
from src.core.library import Entry, Library
VERSION: str = "9.2.0" # Major.Minor.Patch
VERSION_BRANCH: str = "Alpha" # 'Alpha', 'Beta', or '' for Full Release
# The folder & file names where TagStudio keeps its data relative to a library.
TS_FOLDER_NAME: str = ".TagStudio"
BACKUP_FOLDER_NAME: str = "backups"
COLLAGE_FOLDER_NAME: str = "collages"
LIBRARY_FILENAME: str = "ts_library.json"
# TODO: Turn this whitelist into a user-configurable blacklist.
IMAGE_TYPES: list[str] = [
"png",
"jpg",
"jpeg",
"jpg_large",
"jpeg_large",
"jfif",
"gif",
"tif",
"tiff",
"heic",
"heif",
"webp",
"bmp",
"svg",
"avif",
"apng",
"jp2",
"j2k",
"jpg2",
]
VIDEO_TYPES: list[str] = [
"mp4",
"webm",
"mov",
"hevc",
"mkv",
"avi",
"wmv",
"flv",
"gifv",
"m4p",
"m4v",
"3gp",
]
AUDIO_TYPES: list[str] = [
"mp3",
"mp4",
"mpeg4",
"m4a",
"aac",
"wav",
"flac",
"alac",
"wma",
"ogg",
"aiff",
]
DOC_TYPES: list[str] = ["txt", "rtf", "md", "doc", "docx", "pdf", "tex", "odt", "pages"]
PLAINTEXT_TYPES: list[str] = [
"txt",
"md",
"css",
"html",
"xml",
"json",
"js",
"ts",
"ini",
"htm",
"csv",
"php",
"sh",
"bat",
]
SPREADSHEET_TYPES: list[str] = ["csv", "xls", "xlsx", "numbers", "ods"]
PRESENTATION_TYPES: list[str] = ["ppt", "pptx", "key", "odp"]
ARCHIVE_TYPES: list[str] = ["zip", "rar", "tar", "tar.gz", "tgz", "7z"]
PROGRAM_TYPES: list[str] = ["exe", "app"]
SHORTCUT_TYPES: list[str] = ["lnk", "desktop", "url"]
ALL_FILE_TYPES: list[str] = (
IMAGE_TYPES
+ VIDEO_TYPES
+ AUDIO_TYPES
+ DOC_TYPES
+ SPREADSHEET_TYPES
+ PRESENTATION_TYPES
+ ARCHIVE_TYPES
+ PROGRAM_TYPES
+ SHORTCUT_TYPES
)
BOX_FIELDS = ["tag_box", "text_box"]
TEXT_FIELDS = ["text_line", "text_box"]
DATE_FIELDS = ["datetime"]
TAG_COLORS = [
"",
"black",
"dark gray",
"gray",
"light gray",
"white",
"light pink",
"pink",
"red",
"red orange",
"orange",
"yellow orange",
"yellow",
"lime",
"light green",
"mint",
"green",
"teal",
"cyan",
"light blue",
"blue",
"blue violet",
"violet",
"purple",
"lavender",
"berry",
"magenta",
"salmon",
"auburn",
"dark brown",
"brown",
"light brown",
"blonde",
"peach",
"warm gray",
"cool gray",
"olive",
]
class TagStudioCore:
"""
Instantiate this to establish a TagStudio session.

View File

@@ -20,7 +20,7 @@ from PySide6.QtWidgets import (
from src.core.library import Library, Tag
from src.core.palette import ColorType, get_tag_color
from src.core.ts_core import TAG_COLORS
from src.core.constants import TAG_COLORS
from src.qt.widgets import PanelWidget, PanelModal, TagWidget
from src.qt.modals import TagSearchPanel

View File

@@ -45,9 +45,9 @@ from PySide6.QtWidgets import (
from humanfriendly import format_timespan
from src.core.library import ItemType
from src.core.ts_core import (
from src.core.ts_core import TagStudioCore
from src.core.constants import (
PLAINTEXT_TYPES,
TagStudioCore,
TAG_COLORS,
DATE_FIELDS,
TEXT_FIELDS,

View File

@@ -23,7 +23,7 @@ from PySide6.QtCore import (
)
from src.core.library import Library
from src.core.ts_core import DOC_TYPES, VIDEO_TYPES, IMAGE_TYPES
from src.core.constants import DOC_TYPES, VIDEO_TYPES, IMAGE_TYPES
ERROR = f"[ERROR]"

View File

@@ -24,7 +24,7 @@ from PySide6.QtWidgets import (
)
from src.core.library import ItemType, Library
from src.core.ts_core import AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES
from src.core.constants import AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES
from src.qt.flowlayout import FlowWidget
from src.qt.helpers import FileOpenerHelper
from src.qt.widgets import ThumbRenderer, ThumbButton

View File

@@ -28,7 +28,7 @@ from PySide6.QtWidgets import (
from humanfriendly import format_size
from src.core.library import Entry, ItemType, Library
from src.core.ts_core import VIDEO_TYPES, IMAGE_TYPES
from src.core.constants import VIDEO_TYPES, IMAGE_TYPES
from src.qt.helpers import FileOpenerLabel, FileOpenerHelper, open_file
from src.qt.modals import AddFieldModal
from src.qt.widgets import (

View File

@@ -22,7 +22,7 @@ from PIL import (
)
from PySide6.QtCore import QObject, Signal, QSize
from PySide6.QtGui import QPixmap
from src.core.ts_core import PLAINTEXT_TYPES, VIDEO_TYPES, IMAGE_TYPES
from src.core.constants import PLAINTEXT_TYPES, VIDEO_TYPES, IMAGE_TYPES
ERROR = f"[ERROR]"

View File

@@ -1,18 +1,17 @@
from src.core.library import Tag
class TestTags:
def test_construction(self):
tag = Tag(
id=1,
name="Tag Name",
shorthand="TN",
aliases=["First A", "Second A"],
subtags_ids=[2, 3, 4],
color="",
)
assert tag
def test_construction():
tag = Tag(
id=1,
name="Tag Name",
shorthand="TN",
aliases=["First A", "Second A"],
subtags_ids=[2, 3, 4],
color="",
)
assert tag
def test_empty_construction(self):
tag = Tag(id=1, name="", shorthand="", aliases=[], subtags_ids=[], color="")
assert tag
def test_empty_construction():
tag = Tag(id=1, name="", shorthand="", aliases=[], subtags_ids=[], color="")
assert tag