-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(tests): add GUI search tests and update dependencies
- Loading branch information
1 parent
feb30a0
commit b8fcb5b
Showing
6 changed files
with
343 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -142,4 +142,6 @@ test/ | |
plugins/ | ||
Logs/ | ||
|
||
update_ts.ps1 | ||
update_ts.ps1 | ||
|
||
tests/artifacts/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,4 @@ | ||
requests | ||
PySide6-Essentials>=6.8.0 | ||
mutagen | ||
diskcache | ||
charset-normalizer | ||
pyaes | ||
psutil | ||
pyobjc-framework-Cocoa; sys_platform == 'darwin' | ||
-r requirements.txt | ||
PySide6>=6.8.0 | ||
hatchling | ||
build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
-r requirements.txt | ||
pydub | ||
pytest | ||
pytest-qt | ||
pytest-cov |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# SPDX-FileCopyrightText: Copyright (C) 2024 沉默の金 <[email protected]> | ||
# SPDX-License-Identifier: GPL-3.0-only |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# SPDX-FileCopyrightText: Copyright (C) 2024 沉默の金 <[email protected]> | ||
# SPDX-License-Identifier: GPL-3.0-only | ||
import os | ||
|
||
from mutagen import File, FileType # type: ignore[reportPrivateImportUsage] mutagen中的File被误定义为私有 quodlibet/mutagen#647 | ||
from mutagen.id3 import USLT # type: ignore[reportPrivateImportUsage] | ||
from pydub import AudioSegment | ||
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget | ||
|
||
from LDDC.backend.fetcher import get_lyrics | ||
from LDDC.utils.enum import Source | ||
|
||
|
||
def verify_lyrics(lyrics_text: str) -> None: | ||
lyrics = get_lyrics(Source.Local, use_cache=False, data=lyrics_text.encode("utf-8"))[0] | ||
if not lyrics.get("orig"): | ||
msg = "Not a verified lyrics" | ||
raise AssertionError(msg) | ||
|
||
|
||
def close_msg_boxs(widget: QWidget) -> None: | ||
for child in widget.children(): | ||
if isinstance(child, QMessageBox): | ||
child.defaultButton().click() | ||
|
||
|
||
def select_file(widget: QWidget, path: str | list[str]) -> None: | ||
for child in widget.children(): | ||
if isinstance(child, QFileDialog): | ||
match child.fileMode(): | ||
case QFileDialog.FileMode.ExistingFile | QFileDialog.FileMode.Directory | QFileDialog.FileMode.AnyFile: | ||
if isinstance(path, list): | ||
path = path[0] | ||
child.fileSelected.emit(path) | ||
case QFileDialog.FileMode.ExistingFiles: | ||
if isinstance(path, str): | ||
path = [path] | ||
child.filesSelected.emit(path) | ||
|
||
child.accept() | ||
child.deleteLater() | ||
break | ||
else: | ||
msg = "No QFileDialog found" | ||
raise RuntimeError(msg) | ||
|
||
|
||
def grab(widget: QWidget, path: str) -> None: | ||
app = QApplication.instance() | ||
if isinstance(app, QApplication): | ||
rect = widget.frameGeometry() | ||
widget.screen().grabWindow(0, rect.x(), rect.y(), rect.width(), rect.height()).save(path) | ||
else: | ||
msg = "No QApplication instance found" | ||
raise RuntimeError(msg) # noqa: TRY004 | ||
|
||
|
||
def create_audio_file(path: str, audio_format: str, duration: int, tags: dict[str, str] | None = None) -> None: | ||
"""创建音频文件 | ||
:param path: 文件路径 | ||
:param format: 文件格式 | ||
:param duration: 音频时长(秒) | ||
""" | ||
if not os.path.exists(os.path.dirname(path)): | ||
os.makedirs(os.path.dirname(path), exist_ok=True) | ||
silent_audio = AudioSegment.silent(duration=duration * 1000, frame_rate=44100) | ||
silent_audio.export(path, format=audio_format, tags=tags) | ||
|
||
|
||
def verify_audio_lyrics(path: str) -> None: | ||
audio = File(path) | ||
count = 0 | ||
|
||
if audio and isinstance(audio, FileType): | ||
if not audio.tags: | ||
msg = f"No tags found in audio file: {path}" | ||
raise AssertionError(msg) | ||
|
||
for tag in audio: | ||
|
||
if tag in ["lyrics", "Lyrics", "WM/LYRICS", "©lyr", "LYRICS", "USLT"] or (isinstance(tag, str) and tag.startswith("USLT")): | ||
try: | ||
t = audio[tag] | ||
if isinstance(t, USLT): | ||
verify_lyrics(t.text) | ||
else: | ||
verify_lyrics(t[0]) | ||
count += 1 | ||
except ValueError: | ||
pass | ||
|
||
if count == 0: | ||
msg = f"No lyrics found in audio file: {path}" | ||
raise AssertionError(msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
# SPDX-FileCopyrightText: Copyright (C) 2024 沉默の金 <[email protected]> | ||
# SPDX-License-Identifier: GPL-3.0-only | ||
|
||
# ruff: noqa: S101 | ||
import os | ||
import shutil | ||
from itertools import combinations | ||
|
||
import pytest | ||
from PySide6.QtCore import QPoint, Qt | ||
from PySide6.QtTest import QTest | ||
from PySide6.QtWidgets import QFileDialog | ||
from pytestqt.qtbot import QtBot | ||
|
||
from LDDC.res import resource_rc | ||
from LDDC.utils.enum import LyricsFormat, SearchType, Source | ||
|
||
from .helper import close_msg_boxs, create_audio_file, grab, select_file, verify_audio_lyrics, verify_lyrics | ||
|
||
resource_rc.qInitResources() | ||
test_artifacts_path = os.path.join(os.path.dirname(__file__), "artifacts") | ||
screenshot_path = os.path.join(test_artifacts_path, "screenshots") | ||
if os.path.exists(test_artifacts_path): | ||
shutil.rmtree(test_artifacts_path) | ||
os.makedirs(test_artifacts_path) | ||
os.makedirs(screenshot_path) | ||
|
||
|
||
def search(qtbot: QtBot, search_type: SearchType, keyword: str) -> None: | ||
from LDDC.view.main_window import main_window | ||
main_window.search_widget.search_keyword_lineEdit.setText(keyword) | ||
main_window.search_widget.search_type_comboBox.setCurrentIndex(search_type.value) | ||
|
||
for i in range(main_window.search_widget.source_comboBox.count() - 1, -1, -1): | ||
main_window.search_widget.source_comboBox.setCurrentIndex(i) | ||
main_window.search_widget.search_pushButton.click() | ||
|
||
def check_table() -> bool: | ||
return main_window.search_widget.results_tableWidget.rowCount() > 0 | ||
qtbot.waitUntil(check_table) | ||
qtbot.wait(20) | ||
grab(main_window, os.path.join(screenshot_path, f"search_{search_type.name.lower()}_{Source(i).name.lower()}.png")) | ||
assert main_window.search_widget.results_tableWidget.rowCount() > 0 | ||
qtbot.wait(200) | ||
|
||
|
||
def test_search_song(qtbot: QtBot) -> None: | ||
from LDDC.view.main_window import main_window | ||
|
||
main_window.show() | ||
main_window.set_current_widget(0) | ||
qtbot.wait(300) # 等待窗口加载完成 | ||
search(qtbot, SearchType.SONG, "鈴木このみ - アルカテイル") | ||
|
||
|
||
def test_preview(qtbot: QtBot) -> None: | ||
from LDDC.view.main_window import main_window | ||
|
||
assert main_window.search_widget.results_tableWidget.rowCount() > 0 | ||
# 双击第一行 | ||
for _ in range(2): | ||
QTest.mouseDClick(main_window.search_widget.results_tableWidget.viewport(), | ||
Qt.MouseButton.LeftButton, | ||
pos=QPoint(int(main_window.search_widget.results_tableWidget.width() * 0.5), | ||
int(main_window.search_widget.results_tableWidget.horizontalHeader().height()))) | ||
|
||
def check_preview_text() -> bool: | ||
return main_window.search_widget.preview_lyric_result is not None | ||
|
||
qtbot.waitUntil(check_preview_text) | ||
grab(main_window, os.path.join(screenshot_path, "preview.png")) | ||
verify_lyrics(main_window.search_widget.preview_plainTextEdit.toPlainText()) | ||
|
||
|
||
def test_save_to_dir() -> None: | ||
from LDDC.view.main_window import main_window | ||
|
||
assert main_window.search_widget.preview_plainTextEdit.toPlainText() != "" | ||
orig_path = main_window.search_widget.save_path_lineEdit.text() | ||
main_window.search_widget.save_path_lineEdit.setText(os.path.join(test_artifacts_path, "lyrics", "search song")) | ||
main_window.search_widget.save_preview_lyric_pushButton.click() | ||
close_msg_boxs(main_window.search_widget) | ||
main_window.search_widget.save_path_lineEdit.setText(orig_path) | ||
|
||
assert os.listdir(os.path.join(test_artifacts_path, "lyrics", "search song")) != [] | ||
|
||
|
||
def test_save_to_tag(monkeypatch: pytest.MonkeyPatch) -> None: | ||
from LDDC.view.main_window import main_window | ||
|
||
monkeypatch.setattr(QFileDialog, "open", lambda *args, **kwargs: None) # noqa: ARG005 | ||
assert main_window.search_widget.preview_plainTextEdit.toPlainText() != "" | ||
for audio_format in ["mp3", 'ogg', 'tta', 'wav', 'flac']: | ||
path = os.path.join(test_artifacts_path, "audio", "search song", f"test.{audio_format}") | ||
create_audio_file(path, audio_format, 1, {"title": "アルカテイル", "artist": "鈴木このみ"}) | ||
main_window.search_widget.save_to_tag_pushButton.click() | ||
select_file(main_window.search_widget, path) | ||
close_msg_boxs(main_window.search_widget) | ||
verify_audio_lyrics(path) | ||
|
||
|
||
def test_lyrics_langs(qtbot: QtBot) -> None: | ||
from LDDC.view.main_window import main_window | ||
|
||
def change_langs(langs: list[str]) -> None: | ||
if set(main_window.search_widget.get_lyric_langs()) != set(langs): | ||
orig_text = main_window.search_widget.preview_plainTextEdit.toPlainText() | ||
|
||
if "orig" in langs: | ||
main_window.search_widget.original_checkBox.setChecked(True) | ||
else: | ||
main_window.search_widget.original_checkBox.setChecked(False) | ||
|
||
if "ts" in langs: | ||
main_window.search_widget.translate_checkBox.setChecked(True) | ||
else: | ||
main_window.search_widget.translate_checkBox.setChecked(False) | ||
|
||
if "roma" in langs: | ||
main_window.search_widget.romanized_checkBox.setChecked(True) | ||
else: | ||
main_window.search_widget.romanized_checkBox.setChecked(False) | ||
|
||
def check_langs_changed() -> bool: | ||
return main_window.search_widget.preview_plainTextEdit.toPlainText() != orig_text | ||
|
||
qtbot.waitUntil(check_langs_changed) | ||
grab(main_window, os.path.join(screenshot_path, f"search_{'_'.join(langs)}.png")) | ||
|
||
for r in range(1, 4): | ||
for langs in combinations(["orig", "ts", "roma"], r): | ||
change_langs(list(langs)) | ||
main_window.search_widget.search_pushButton.click() | ||
|
||
change_langs(["orig", "ts"]) | ||
|
||
|
||
def change_preview_format(qtbot: QtBot, lyrics_format: LyricsFormat) -> str: | ||
from LDDC.view.main_window import main_window | ||
|
||
assert main_window.search_widget.preview_lyric_result is not None | ||
|
||
if main_window.search_widget.lyricsformat_comboBox.currentIndex() != lyrics_format.value: | ||
orig_text = main_window.search_widget.preview_plainTextEdit.toPlainText() | ||
main_window.search_widget.lyricsformat_comboBox.setCurrentIndex(lyrics_format.value) | ||
|
||
def check_preview_result() -> bool: | ||
return bool(len(main_window.search_widget.preview_plainTextEdit.toPlainText()) > 20 and | ||
main_window.search_widget.preview_plainTextEdit.toPlainText() != orig_text) | ||
|
||
qtbot.waitUntil(check_preview_result) | ||
grab(main_window, os.path.join(screenshot_path, f"preview_{lyrics_format.name.lower()}.png")) | ||
return main_window.search_widget.preview_plainTextEdit.toPlainText() | ||
|
||
|
||
def test_preview_linebyline_lrc(qtbot: QtBot) -> None: | ||
verify_lyrics(change_preview_format(qtbot, LyricsFormat.LINEBYLINELRC)) | ||
|
||
|
||
def test_preview_enhanced_lrc(qtbot: QtBot) -> None: | ||
verify_lyrics(change_preview_format(qtbot, LyricsFormat.ENHANCEDLRC)) | ||
|
||
|
||
def test_preview_srt(qtbot: QtBot) -> None: | ||
assert change_preview_format(qtbot, LyricsFormat.SRT) != "" | ||
|
||
|
||
def test_preview_ass(qtbot: QtBot) -> None: | ||
assert change_preview_format(qtbot, LyricsFormat.ASS) != "" | ||
|
||
|
||
def test_preview_verbatimlrc(qtbot: QtBot) -> None: | ||
verify_lyrics(change_preview_format(qtbot, LyricsFormat.VERBATIMLRC)) | ||
|
||
|
||
def test_search_song_list(qtbot: QtBot) -> None: | ||
search(qtbot, SearchType.SONGLIST, "key社") | ||
|
||
|
||
def test_open_song_list(qtbot: QtBot) -> None: | ||
from LDDC.view.main_window import main_window | ||
|
||
assert main_window.search_widget.results_tableWidget.rowCount() > 0 | ||
# 双击第一行 | ||
for _ in range(2): | ||
QTest.mouseDClick(main_window.search_widget.results_tableWidget.viewport(), | ||
Qt.MouseButton.LeftButton, | ||
pos=QPoint(int(main_window.search_widget.results_tableWidget.width() * 0.5), | ||
int(main_window.search_widget.results_tableWidget.horizontalHeader().height()))) | ||
|
||
def check_table() -> bool: | ||
return main_window.search_widget.results_tableWidget.property("result_type")[0] == "songlist" | ||
|
||
qtbot.waitUntil(check_table) | ||
grab(main_window, os.path.join(screenshot_path, "show_songlist.png")) | ||
assert main_window.search_widget.results_tableWidget.rowCount() > 0 | ||
|
||
|
||
def test_search_album(qtbot: QtBot) -> None: | ||
search(qtbot, SearchType.ALBUM, "アスタロア/青き此方/夏の砂時計") | ||
|
||
|
||
def test_save_album_lyrics(qtbot: QtBot) -> None: | ||
from LDDC.view.main_window import main_window | ||
|
||
assert main_window.search_widget.results_tableWidget.rowCount() > 0 | ||
# 双击第一行 | ||
for _ in range(2): | ||
QTest.mouseDClick(main_window.search_widget.results_tableWidget.viewport(), | ||
Qt.MouseButton.LeftButton, | ||
pos=QPoint(int(main_window.search_widget.results_tableWidget.width() * 0.5), | ||
int(main_window.search_widget.results_tableWidget.horizontalHeader().height()))) | ||
|
||
def check_table() -> bool: | ||
return main_window.search_widget.results_tableWidget.property("result_type")[0] == "album" | ||
|
||
qtbot.waitUntil(check_table) | ||
grab(main_window, os.path.join(screenshot_path, "show_album.png")) | ||
assert main_window.search_widget.results_tableWidget.rowCount() > 0 | ||
|
||
# 保存歌词 | ||
orig_path = main_window.search_widget.save_path_lineEdit.text() | ||
main_window.search_widget.save_path_lineEdit.setText(os.path.join(test_artifacts_path, "lyrics", "search album")) | ||
main_window.search_widget.save_list_lyrics_pushButton.click() | ||
|
||
def check_progress() -> bool: | ||
return main_window.search_widget.get_list_lyrics_box.progressBar.value() == main_window.search_widget.get_list_lyrics_box.progressBar.maximum() | ||
qtbot.waitUntil(check_progress, timeout=50000) | ||
close_msg_boxs(main_window.search_widget) | ||
|
||
grab(main_window, os.path.join(screenshot_path, "save_album_lyrics.png")) | ||
|
||
main_window.search_widget.get_list_lyrics_box.pushButton.click() | ||
main_window.search_widget.save_path_lineEdit.setText(orig_path) | ||
|
||
assert os.listdir(os.path.join(test_artifacts_path, "lyrics", "search album")) != [] |