Skip to content

Commit

Permalink
feat(tests): add GUI search tests and update dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
chenmozhijin committed Dec 8, 2024
1 parent feb30a0 commit b8fcb5b
Show file tree
Hide file tree
Showing 6 changed files with 343 additions and 9 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,6 @@ test/
plugins/
Logs/

update_ts.ps1
update_ts.ps1

tests/artifacts/
10 changes: 2 additions & 8 deletions requirements-dev.txt
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
5 changes: 5 additions & 0 deletions requirements-tests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-r requirements.txt
pydub
pytest
pytest-qt
pytest-cov
2 changes: 2 additions & 0 deletions tests/__init__.py
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
95 changes: 95 additions & 0 deletions tests/helper.py
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)
236 changes: 236 additions & 0 deletions tests/test_gui_search.py
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")) != []

0 comments on commit b8fcb5b

Please sign in to comment.