diff options
| author | Brianna Rainey | 2026-02-12 15:38:54 -0500 |
|---|---|---|
| committer | GitHub | 2026-02-12 15:38:54 -0500 |
| commit | f03a3a686c7304588dd434322c73506531e53595 (patch) | |
| tree | ee41d920873e9a77c41f4a65857af019e71a4754 /tests | |
| parent | 48a9105eab94e64101470402427564203e1d8970 (diff) | |
v2.2.4 - Quiet FFmpeg; add "invert" option to Classic Vis; fix CLI parsing for Image component (#96)
* change noisiness of terminal output
ffmpeg no longer prints everything into the terminal unless we're in `--verbose` mode. percentage progress text stays on one line while not in verbose mode.
* Added hint to run `avp --verbose` if `avp --log` is run with no avp_debug.log file present
* Classic Visualizer: add invert option
* Image component: fix path commandline option
* Image component: restrict file formats in CLI to match GUI
* Color component: add tooltip to color2 picker (second color of gradients)
* change tests to work with pytest-xdist
avp core stores its config (location of `settings.ini`) in temp directories if using multiple workers to run tests, so they don't interfere with each other. when using a single worker, the `tests/data/config` directory is still used
* check alt comp names when parsing cmdline
* rename `original.py` to `classic.py`
* move `component.py` into subpackage
* rename comp_original to comp_classic
* show traceback if renderFrame() raises exception
* do not try to insert non-existent components from project files
* add "composite" property for components
if a component returns "composite" then it will receive a frame to draw on during calls to previewRender and frameRender
* more tests of projects, actions, waveform, spectrum, image, color, classic
* do not change presetDir to "projects" within PresetManager
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/__init__.py | 48 | ||||
| -rw-r--r-- | tests/test_commandline_export.py | 3 | ||||
| -rw-r--r-- | tests/test_comp_classic.py | 103 | ||||
| -rw-r--r-- | tests/test_comp_color.py | 12 | ||||
| -rw-r--r-- | tests/test_comp_image.py | 17 | ||||
| -rw-r--r-- | tests/test_comp_original.py | 67 | ||||
| -rw-r--r-- | tests/test_comp_spectrum.py | 17 | ||||
| -rw-r--r-- | tests/test_comp_waveform.py | 26 | ||||
| -rw-r--r-- | tests/test_core_init.py | 8 | ||||
| -rw-r--r-- | tests/test_mainwindow_comp_actions.py (renamed from tests/test_mainwindow_undostack.py) | 2 | ||||
| -rw-r--r-- | tests/test_mainwindow_list_actions.py | 52 | ||||
| -rw-r--r-- | tests/test_mainwindow_projects.py | 17 | ||||
| -rw-r--r-- | tests/test_toolkit_ffmpeg.py | 10 |
13 files changed, 286 insertions, 96 deletions
diff --git a/tests/__init__.py b/tests/__init__.py index bb35f72..b08a6bd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,4 +1,5 @@ import os +import tempfile import numpy from avp.core import Core @@ -8,10 +9,19 @@ from avp.toolkit.ffmpeg import readAudioFile from pytest import fixture +PYTEST_XDIST_WORKER_COUNT = os.environ.get("PYTEST_XDIST_WORKER_COUNT", 0) + + +@fixture +def settings(): + """Doesn't instantiate core: just calls a static method to store `settings.ini`""" + initCore() + yield None + + @fixture def audioData(): """Fixture that gives a tuple of (completeAudioArray, duration)""" - # Core.storeSettings() needed to store ffmpeg bin location initCore() soundFile = getTestDataPath("inputfiles/test.ogg") yield readAudioFile(soundFile, MockVideoWorker()) @@ -28,6 +38,8 @@ def command(qtbot): @fixture def window(qtbot): initCore() + # patch out any modal dialog that might happen + MainWindow.showMessage = lambda self, msg, **kwargs: print(msg) window = MainWindow(None, None) window.clear() qtbot.addWidget(window) @@ -43,13 +55,41 @@ def getTestDataPath(filename=""): def initCore(): - testDataDir = getTestDataPath("config") + """ + Initializes the Core by creating `settings.ini` + Returns the temp directory path where settings.ini was created + or None if multiple pytest workers are not enabled. + """ + try: + numWorkers = int(PYTEST_XDIST_WORKER_COUNT) + except ValueError: + numWorkers = 0 + if numWorkers > 0: + # use temporary directories for multiple workers + # so they don't interfere with each other + configDir = tempfile.mkdtemp(prefix="avp-config-") + else: + # use test data path so we can easily see it after + # a failed test, and help us understand the config + configDir = getTestDataPath("config") unwanted = ["autosave.avp", "settings.ini"] for file in unwanted: - filename = os.path.join(testDataDir, "autosave.avp") + filename = os.path.join(configDir, "autosave.avp") if os.path.exists(filename): os.remove(filename) - Core.storeSettings(testDataDir) + Core.storeSettings(configDir) + return configDir if numWorkers > 0 else None + + +def preFrameRender(audioData, comp): + """Prepares a component for calls to frameRender()""" + comp.preFrameRender( + audioFile=getTestDataPath("inputfiles/test.ogg"), + completeAudioArray=audioData[0], + sampleSize=1470, + progressBarSetText=MockSignal(), + progressBarUpdate=MockSignal(), + ) class MockSignal: diff --git a/tests/test_commandline_export.py b/tests/test_commandline_export.py index 6d7f068..6eb533d 100644 --- a/tests/test_commandline_export.py +++ b/tests/test_commandline_export.py @@ -8,13 +8,14 @@ from pytestqt import qtbot def test_commandline_classic_export(qtbot, command): """Run Qt event loop and create a video in the system /tmp or /temp""" soundFile = getTestDataPath("inputfiles/test.ogg") - outputDir = tempfile.mkdtemp(prefix="avp-test-") + outputDir = tempfile.mkdtemp(prefix="avp-export-") outputFilename = os.path.join(outputDir, "output.mp4") sys.argv = [ "", "-c", "0", "classic", + "color=255,255,255", "-i", soundFile, "-o", diff --git a/tests/test_comp_classic.py b/tests/test_comp_classic.py new file mode 100644 index 0000000..a942d89 --- /dev/null +++ b/tests/test_comp_classic.py @@ -0,0 +1,103 @@ +from avp.toolkit.visualizer import transformData +from pytestqt import qtbot +from pytest import fixture, mark +from . import audioData, command, imageDataSum, preFrameRender + + +sampleSize = 1470 # 44100 / 30 = 1470 + + +def createSpectrumArray(audioData): + """Creates enough `spectrumArray` for one call to Component.drawBars()""" + spectrumArray = {0: transformData(0, audioData[0], sampleSize, 0.08, 0.8, None, 20)} + for i in range(sampleSize, len(audioData[0]), sampleSize): + spectrumArray[i] = transformData( + i, + audioData[0], + sampleSize, + 0.08, + 0.8, + spectrumArray[i - sampleSize].copy(), + 20, + ) + return spectrumArray + + +@fixture +def coreWithClassicComp(qtbot, command): + """Fixture providing a Command object with Classic Visualizer component added""" + command.core.insertComponent( + 0, command.core.moduleIndexFor("Classic Visualizer"), command + ) + yield command.core + + +def test_comp_classic_added(coreWithClassicComp): + """Add Classic Visualizer to core""" + assert len(coreWithClassicComp.selectedComponents) == 1 + + +def test_comp_classic_removed(coreWithClassicComp): + """Remove Classic Visualizer from core""" + coreWithClassicComp.removeComponent(0) + assert len(coreWithClassicComp.selectedComponents) == 0 + + +@mark.parametrize("layout", (0, 1, 2, 3)) +def test_comp_classic_drawBars(coreWithClassicComp, audioData, layout): + """Call drawBars after creating audio spectrum data manually.""" + spectrumArray = createSpectrumArray(audioData) + comp = coreWithClassicComp.selectedComponents[0] + image = comp.drawBars( + 1920, 1080, spectrumArray[sampleSize * 4], (0, 0, 0), layout, None + ) + imageSize = 37872316 + assert imageDataSum(image) == imageSize if layout < 2 else imageSize / 2 + + +def test_comp_classic_drawBars_using_preFrameRender(coreWithClassicComp, audioData): + """Call drawBars after creating audio spectrum data using preFrameRender.""" + comp = coreWithClassicComp.selectedComponents[0] + preFrameRender(audioData, comp) + image = comp.drawBars( + 1920, + 1080, + coreWithClassicComp.selectedComponents[0].spectrumArray[sampleSize * 4], + (0, 0, 0), + 0, + None, + ) + assert imageDataSum(image) == 37872316 + + +def test_comp_classic_command_layout(coreWithClassicComp): + comp = coreWithClassicComp.selectedComponents[0] + comp.command("layout=top") + assert comp.layout == 3 + + +def test_comp_classic_command_color(coreWithClassicComp): + comp = coreWithClassicComp.selectedComponents[0] + comp.command("color=111,111,111") + assert comp.visColor == (111, 111, 111) + + +def test_comp_classic_command_preset(coreWithClassicComp): + comp = coreWithClassicComp.selectedComponents[0] + saveValueStore = comp.savePreset() + saveValueStore["preset"] = "testPreset" + coreWithClassicComp.createPresetFile( + comp.name, comp.version, "testPreset", saveValueStore + ) + comp.command("preset=testPreset") + assert comp.currentPreset == "testPreset" + + +def test_comp_classic_loadPreset(coreWithClassicComp): + comp = coreWithClassicComp.selectedComponents[0] + comp.scale = 99 + saveValueStore = comp.savePreset() + saveValueStore["preset"] = "testPreset" + comp.scale = 20 + comp.loadPreset(saveValueStore, "testPreset") + assert comp.scale == 99 diff --git a/tests/test_comp_color.py b/tests/test_comp_color.py index 48b07ff..2aa1f2c 100644 --- a/tests/test_comp_color.py +++ b/tests/test_comp_color.py @@ -14,8 +14,18 @@ def coreWithColorComp(qtbot, command): def test_comp_color_set_color(coreWithColorComp): - "Set imagePath of Image component" + """Set imagePath of Image component""" comp = coreWithColorComp.selectedComponents[0] comp.page.lineEdit_color1.setText("111,111,111") image = comp.previewRender() assert imageDataSum(image) == 1219276800 + + +def test_comp_color_gradient(coreWithColorComp): + """Test changing fill type to a gradient""" + comp = coreWithColorComp.selectedComponents[0] + comp.page.comboBox_fill.setCurrentIndex(1) + comp.page.lineEdit_color1.setText("0,0,0") + comp.page.lineEdit_color2.setText("255,255,255") + image = comp.previewRender() + assert imageDataSum(image) == 1849285965 diff --git a/tests/test_comp_image.py b/tests/test_comp_image.py index c580d5a..b221df4 100644 --- a/tests/test_comp_image.py +++ b/tests/test_comp_image.py @@ -1,3 +1,4 @@ +import os from avp.command import Command from pytestqt import qtbot from pytest import fixture @@ -5,6 +6,7 @@ from . import audioData, command, MockSignal, imageDataSum, getTestDataPath sampleSize = 1470 # 44100 / 30 = 1470 +testFile = "inputfiles/test.jpg" @fixture @@ -19,7 +21,7 @@ def coreWithImageComp(qtbot, command): def test_comp_image_set_path(coreWithImageComp): "Set imagePath of Image component" comp = coreWithImageComp.selectedComponents[0] - comp.imagePath = getTestDataPath("inputfiles/test.jpg") + comp.imagePath = getTestDataPath(testFile) image = comp.previewRender() assert imageDataSum(image) == 463711601 @@ -27,7 +29,7 @@ def test_comp_image_set_path(coreWithImageComp): def test_comp_image_scale_50_1080p(coreWithImageComp): """Image component stretches image to 50% at 1080p""" comp = coreWithImageComp.selectedComponents[0] - comp.imagePath = getTestDataPath("inputfiles/test.jpg") + comp.imagePath = getTestDataPath(testFile) image = comp.previewRender() sum = imageDataSum(image) comp.page.spinBox_scale.setValue(50) @@ -37,7 +39,7 @@ def test_comp_image_scale_50_1080p(coreWithImageComp): def test_comp_image_scale_50_720p(coreWithImageComp): """Image component stretches image to 50% at 720p""" comp = coreWithImageComp.selectedComponents[0] - comp.imagePath = getTestDataPath("inputfiles/test.jpg") + comp.imagePath = getTestDataPath(testFile) comp.page.spinBox_scale.setValue(50) image = comp.previewRender() sum = imageDataSum(image) @@ -47,3 +49,12 @@ def test_comp_image_scale_50_720p(coreWithImageComp): assert image.width == 1920 assert newImage.width == 1280 assert imageDataSum(comp.previewRender()) == sum + + +def test_comp_image_command_path(coreWithImageComp): + """Image component accepts commandline argument: + `path=test.jpg`""" + imgPath = os.path.realpath(getTestDataPath(testFile)) + comp = coreWithImageComp.selectedComponents[0] + comp.command(f"path={imgPath}") + assert comp.imagePath == imgPath diff --git a/tests/test_comp_original.py b/tests/test_comp_original.py deleted file mode 100644 index 8cd00a4..0000000 --- a/tests/test_comp_original.py +++ /dev/null @@ -1,67 +0,0 @@ -from avp.command import Command -from avp.toolkit.visualizer import transformData -from pytestqt import qtbot -from pytest import fixture -from . import audioData, command, MockSignal, imageDataSum - - -sampleSize = 1470 # 44100 / 30 = 1470 - - -@fixture -def coreWithClassicComp(qtbot, command): - """Fixture providing a Command object with Classic Visualizer component added""" - command.core.insertComponent( - 0, command.core.moduleIndexFor("Classic Visualizer"), command - ) - yield command.core - - -def test_comp_classic_added(coreWithClassicComp): - """Add Classic Visualizer to core""" - assert len(coreWithClassicComp.selectedComponents) == 1 - - -def test_comp_classic_removed(coreWithClassicComp): - """Remove Classic Visualizer from core""" - coreWithClassicComp.removeComponent(0) - assert len(coreWithClassicComp.selectedComponents) == 0 - - -def test_comp_classic_drawBars(coreWithClassicComp, audioData): - """Call drawBars after creating audio spectrum data manually.""" - - spectrumArray = {0: transformData(0, audioData[0], sampleSize, 0.08, 0.8, None, 20)} - for i in range(sampleSize, len(audioData[0]), sampleSize): - spectrumArray[i] = transformData( - i, - audioData[0], - sampleSize, - 0.08, - 0.8, - spectrumArray[i - sampleSize].copy(), - 20, - ) - image = coreWithClassicComp.selectedComponents[0].drawBars( - 1920, 1080, spectrumArray[sampleSize * 4], (0, 0, 0), 0 - ) - assert imageDataSum(image) == 37872316 - - -def test_comp_classic_drawBars_using_preFrameRender(coreWithClassicComp, audioData): - """Call drawBars after creating audio spectrum data using preFrameRender.""" - comp = coreWithClassicComp.selectedComponents[0] - comp.preFrameRender( - completeAudioArray=audioData[0], - sampleSize=sampleSize, - progressBarSetText=MockSignal(), - progressBarUpdate=MockSignal(), - ) - image = comp.drawBars( - 1920, - 1080, - coreWithClassicComp.selectedComponents[0].spectrumArray[sampleSize * 4], - (0, 0, 0), - 0, - ) - assert imageDataSum(image) == 37872316 diff --git a/tests/test_comp_spectrum.py b/tests/test_comp_spectrum.py index 870185c..5dd4e2d 100644 --- a/tests/test_comp_spectrum.py +++ b/tests/test_comp_spectrum.py @@ -1,7 +1,12 @@ from avp.command import Command from pytestqt import qtbot from pytest import fixture -from . import imageDataSum, command +from . import ( + imageDataSum, + command, + preFrameRender, + audioData, +) @fixture @@ -13,7 +18,15 @@ def coreWithSpectrumComp(qtbot, command): yield command.core -def test_comp_waveform_previewRender(coreWithSpectrumComp): +def test_comp_spectrum_previewRender(coreWithSpectrumComp): comp = coreWithSpectrumComp.selectedComponents[0] image = comp.previewRender() assert imageDataSum(image) == 71992628 + + +def test_comp_spectrum_renderFrame(coreWithSpectrumComp, audioData): + comp = coreWithSpectrumComp.selectedComponents[0] + preFrameRender(audioData, comp) + image = comp.frameRender(0) + comp.postFrameRender() + assert imageDataSum(image) == 117 diff --git a/tests/test_comp_waveform.py b/tests/test_comp_waveform.py index eb5800d..d295dbe 100644 --- a/tests/test_comp_waveform.py +++ b/tests/test_comp_waveform.py @@ -1,11 +1,14 @@ from pytestqt import qtbot from pytest import fixture -from . import command +from avp.toolkit.ffmpeg import checkFfmpegVersion +from . import command, imageDataSum, audioData, preFrameRender @fixture def coreWithWaveformComp(qtbot, command): """Fixture providing a Command object with Waveform component added""" + command.settings.setValue("outputWidth", 1920) + command.settings.setValue("outputHeight", 1080) command.core.insertComponent(0, command.core.moduleIndexFor("Waveform"), command) yield command.core @@ -14,3 +17,24 @@ def test_comp_waveform_setColor(coreWithWaveformComp): comp = coreWithWaveformComp.selectedComponents[0] comp.page.lineEdit_color.setText("255,255,255") assert comp.color == (255, 255, 255) + + +def test_comp_waveform_previewRender(coreWithWaveformComp): + comp = coreWithWaveformComp.selectedComponents[0] + comp.page.lineEdit_color.setText("255,255,255") + _, version = checkFfmpegVersion() + if version > 6: + # FFmpeg 8 has different colors from 6 + # TODO check version 7 + assert imageDataSum(comp.previewRender()) == 36114120 + else: + assert imageDataSum(comp.previewRender()) == 37210620 + + +def test_comp_waveform_renderFrame(coreWithWaveformComp, audioData): + comp = coreWithWaveformComp.selectedComponents[0] + comp.page.lineEdit_color.setText("255,255,255") + preFrameRender(audioData, comp) + image = comp.frameRender(0) + comp.postFrameRender() + assert imageDataSum(image) == 8331360 diff --git a/tests/test_core_init.py b/tests/test_core_init.py index e1f2dbb..30477ef 100644 --- a/tests/test_core_init.py +++ b/tests/test_core_init.py @@ -1,10 +1,9 @@ import os from avp.core import Core -from . import getTestDataPath, initCore +from . import getTestDataPath, settings -def test_component_names(): - initCore() +def test_component_names(settings): core = Core() assert core.compNames == [ "Classic Visualizer", @@ -19,8 +18,7 @@ def test_component_names(): ] -def test_moduleindex(): - initCore() +def test_moduleindex(settings): core = Core() assert core.moduleIndexFor("Classic Visualizer") == 0 diff --git a/tests/test_mainwindow_undostack.py b/tests/test_mainwindow_comp_actions.py index ceaf87e..5d3cc7a 100644 --- a/tests/test_mainwindow_undostack.py +++ b/tests/test_mainwindow_comp_actions.py @@ -1,3 +1,5 @@ +"""Tests of MainWindow undoing certain ComponentActions (changes to component settings)""" + from pytest import fixture from pytestqt import qtbot from avp.gui.mainwindow import MainWindow diff --git a/tests/test_mainwindow_list_actions.py b/tests/test_mainwindow_list_actions.py new file mode 100644 index 0000000..5f8bde4 --- /dev/null +++ b/tests/test_mainwindow_list_actions.py @@ -0,0 +1,52 @@ +"""Tests of `actions.py` - MainWindow component list manipulation via undoable actions""" + +from PyQt6 import QtCore +import os +from pytest import fixture +from pytestqt import qtbot +from . import getTestDataPath, window + + +def test_mainwindow_addComponent(qtbot, window): + window.compMenu.actions()[0].trigger() + assert len(window.core.selectedComponents) == 1 + + +def test_mainwindow_removeComponent(qtbot, window): + window.compMenu.actions()[0].trigger() # add component + window.pushButton_removeComponent.click() # remove it + assert len(window.core.selectedComponents) == 0 + + +def test_mainwindow_moveComponent(qtbot, window): + # add first two components from menu + window.compMenu.actions()[0].trigger() + window.compMenu.actions()[1].trigger() + comp0 = window.core.selectedComponents[0].ui + window.pushButton_listMoveDown.click() + # check if 0 is now 1 + assert window.core.selectedComponents[1].ui == comp0 + + +def test_mainwindow_addComponent_undo(qtbot, window): + window.compMenu.actions()[0].trigger() + window.undoStack.undo() + assert len(window.core.selectedComponents) == 0 + + +def test_mainwindow_removeComponent_undo(qtbot, window): + window.compMenu.actions()[0].trigger() # add component + window.pushButton_removeComponent.click() # remove it + window.undoStack.undo() + assert len(window.core.selectedComponents) == 1 + + +def test_mainwindow_moveComponent_undo(qtbot, window): + # add first two components from menu + window.compMenu.actions()[0].trigger() + window.compMenu.actions()[1].trigger() + comp0 = window.core.selectedComponents[0].ui + window.pushButton_listMoveDown.click() + window.undoStack.undo() + # check if 0 is still 0 after undo + assert window.core.selectedComponents[1].ui != comp0 diff --git a/tests/test_mainwindow_projects.py b/tests/test_mainwindow_projects.py index 6b49799..a6df476 100644 --- a/tests/test_mainwindow_projects.py +++ b/tests/test_mainwindow_projects.py @@ -2,7 +2,15 @@ from PyQt6 import QtCore import os from pytest import fixture from pytestqt import qtbot -from . import getTestDataPath, window +from avp.gui.mainwindow import MainWindow +from . import getTestDataPath, window, settings + + +def test_mainwindow_init_with_project(qtbot, settings): + window = MainWindow(getTestDataPath("config/projects/testproject.avp"), None) + qtbot.addWidget(window) + assert window.core.selectedComponents[0].name == "Classic Visualizer" + assert window.core.selectedComponents[1].name == "Color" def test_mainwindow_clear(qtbot, window): @@ -11,11 +19,8 @@ def test_mainwindow_clear(qtbot, window): def test_mainwindow_presetDir_in_tests(qtbot, window): - # FIXME presetDir gets set to projectDir for some reason - assert ( - os.path.basename(os.path.dirname(window.core.settings.value("presetDir"))) - == "config" - ) + """`presetDir` is the filepath on which "Import Preset" file picker opens""" + assert os.path.basename(window.core.settings.value("presetDir")) == "presets" def test_mainwindow_openProject(qtbot, window): diff --git a/tests/test_toolkit_ffmpeg.py b/tests/test_toolkit_ffmpeg.py index 363eba1..cc56495 100644 --- a/tests/test_toolkit_ffmpeg.py +++ b/tests/test_toolkit_ffmpeg.py @@ -1,8 +1,6 @@ import pytest -from avp.core import Core -from avp.command import Command from avp.toolkit.ffmpeg import createFfmpegCommand -from . import audioData, getTestDataPath, initCore +from . import audioData, getTestDataPath, command def test_readAudioFile_data(audioData): @@ -14,14 +12,14 @@ def test_readAudioFile_duration(audioData): @pytest.mark.parametrize("width, height", ((1920, 1080), (1280, 720))) -def test_createFfmpegCommand(width, height): - initCore() - command = Command() +def test_createFfmpegCommand(command, width, height): command.settings.setValue("outputWidth", width) command.settings.setValue("outputHeight", height) ffmpegCmd = createFfmpegCommand("test.ogg", "/tmp", command.core.selectedComponents) assert ffmpegCmd == [ "ffmpeg", + "-loglevel", + "info", "-thread_queue_size", "512", "-y", |
