Merge pull request #142 from Hidorikun/test-support-2

Add pytest support
This commit is contained in:
Travis Abendshien
2024-05-17 21:13:41 -07:00
committed by GitHub
12 changed files with 190 additions and 183 deletions

View File

@@ -183,7 +183,12 @@ _Skip these steps if launching from the .sh script on Linux/macOS._
- 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,4 +1,5 @@
ruff==0.4.2
pre-commit==3.7.0
pytest==8.2.0
Pyinstaller==6.6.0
mypy==1.10.0

View File

@@ -0,0 +1,136 @@
VERSION: str = "9.2.1" # Major.Minor.Patch
VERSION_BRANCH: str = "Pre-Release" # '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

@@ -22,9 +22,15 @@ from typing_extensions import Self
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"]
@@ -444,8 +450,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()
@@ -462,12 +468,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):
@@ -505,17 +511,13 @@ 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")
):
if os.path.exists(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"
),
os.path.normpath(f"{path}/{TS_FOLDER_NAME}/ts_library.json"),
"r",
encoding="utf-8",
) as file:
@@ -724,11 +726,9 @@ 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.makedirs(os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}"))
self._map_filenames_to_entry_ids()
@@ -775,7 +775,7 @@ class Library:
"""
file_to_save: JsonLibary = {
"ts-version": ts_core.VERSION,
"ts-version": VERSION,
"ignored_extensions": [],
"tags": [],
"collations": [],
@@ -813,7 +813,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:
@@ -842,7 +842,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",
@@ -899,13 +899,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)
):
@@ -2121,7 +2121,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

@@ -8,143 +8,7 @@ import json
import os
from src.core.library import Entry, Library
VERSION: str = "9.2.1" # Major.Minor.Patch
VERSION_BRANCH: str = "Pre-Release" # '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",
]
from src.core.constants import TS_FOLDER_NAME, TEXT_FIELDS
class TagStudioCore:

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.panel import PanelWidget, PanelModal
from src.qt.widgets.tag import TagWidget
from src.qt.modals.tag_search import TagSearchPanel

View File

@@ -49,9 +49,9 @@ from humanfriendly import format_timespan
from src.core.enums import SettingItems
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

@@ -24,7 +24,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

@@ -23,8 +23,9 @@ from PySide6.QtWidgets import (
QCheckBox,
)
from src.core.library import ItemType, Library, Entry
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.file_opener import FileOpenerHelper
from src.qt.widgets.thumb_renderer import ThumbRenderer

View File

@@ -29,7 +29,7 @@ from humanfriendly import format_size
from src.core.enums import SettingItems, Theme
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.file_opener import FileOpenerLabel, FileOpenerHelper, open_file
from src.qt.modals.add_field import AddFieldModal
from src.qt.widgets.thumb_renderer import ThumbRenderer

View File

@@ -24,7 +24,7 @@ from PIL import (
from PIL.Image import DecompressionBombError
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
ImageFile.LOAD_TRUNCATED_IMAGES = True

View File

@@ -1,18 +1,18 @@
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