Files
OmaPhoto/tests/FilterSheetTests.cpp
雷电芽衣andClaude c864304695
Release / deb (push) Canceled after 0s
Release / rpm (push) Canceled after 0s
Release / arch (push) Canceled after 0s
Release / publish (push) Canceled after 0s
OmaPhoto 1.0.0: the Linux port of Compositor
A layered image editor for Linux and Omarchy, ported from Compositor
(github.com/robbietilton/Compositor), whose Swift source is its
specification. It reads and writes Compositor's .comp projects.

Packages for Ubuntu, Fedora and Arch come from the release workflow;
scripts/install/ holds one install script per distribution family,
and flake.nix serves NixOS.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-26 00:44:52 -04:00

174 lines
7.7 KiB
C++

#include "Document/BrushStroke.h"
#include "ContentView.h"
#include "SelectionFixtures.h"
#include "UI/FilterSheet.h"
#include <QCheckBox>
#include <QDialog>
#include <QProgressBar>
#include <QPushButton>
#include <QtTest>
// The filter panel, driven as a user drives it.
namespace {
template <typename Widget> Widget &find(QWidget &root, const char *name)
{
Widget *found = root.findChild<Widget *>(QString::fromLatin1(name));
if (!found)
throw std::runtime_error(std::string("no widget named ") + name);
return *found;
}
// A blue field, a red object, a selection, an editor.
struct Editor {
EditorSession session;
ContentView view{session};
Editor()
{
session.createDocument(64, 48, true);
QImage image = BrushRaster::context(64, 48, false);
image.fill(QColor(51, 153, 204));
QPainter painter(&image);
painter.fillRect(QRect(20, 16, 12, 10), Qt::red);
painter.end();
session.insert(ImportedImage(image, image, "Object"));
session.applySelection(rectPath(QRectF(20, 16, 12, 10)), SelectionMode::replace, "Select");
view.show();
}
// The panel's shown sheet; a closed panel's goes later.
FilterSheet *sheet()
{
for (FilterSheet *each : view.findChildren<FilterSheet *>()) {
if (each->isVisible())
return each;
}
return nullptr;
}
QDialog &panel() { return *qobject_cast<QDialog *>(sheet()->window()); }
bool prepared() { return QTest::qWaitFor([&] { return session.filterEdit().has_value() && !session.filterEdit().value().preparing; }, 20000); }
};
}
class FilterSheetTests : public QObject {
Q_OBJECT
private slots:
void thePanelShowsWithTheEditAndSaysWhatItWaitsFor();
void previewCancelAndOKReachTheSession();
void escapeAndClosingCancelReturnCommits();
void noSourceShowsTheErrorAndHoldsOK();
};
void FilterSheetTests::thePanelShowsWithTheEditAndSaysWhatItWaitsFor()
{
Editor editor;
QVERIFY(!editor.sheet());
editor.session.beginFilter(FilterKind::contentAwareFill);
FilterSheet &sheet = *editor.sheet();
QCOMPARE(editor.panel().objectName(), QString("filterPanel"));
QCOMPARE(editor.panel().windowTitle(), QString("Content-Aware Fill"));
QCOMPARE(sheet.width(), 380);
QCOMPARE(find<QLabel>(sheet, "filterWords").text(), QString("Fill the selection using surrounding pixels from this layer."));
QVERIFY(find<QCheckBox>(sheet, "filterPreview").isChecked());
QVERIFY(find<QLabel>(sheet, "filterLimited").isVisible());
QVERIFY(!find<QLabel>(sheet, "filterError").isVisible());
// Working: OK waits with the spinner.
QVERIFY(!find<QPushButton>(sheet, "filterOK").isEnabled());
QVERIFY(find<QProgressBar>(sheet, "filterSpinner").isVisible());
QCOMPARE(find<QProgressBar>(sheet, "filterSpinner").maximum(), 0);
QCOMPARE(find<QLabel>(sheet, "filterActivity").text(), QString("Working…"));
QVERIFY(editor.prepared());
QVERIFY(find<QPushButton>(sheet, "filterOK").isEnabled());
QVERIFY(!find<QProgressBar>(sheet, "filterSpinner").isVisible());
QVERIFY(!find<QLabel>(sheet, "filterActivity").isVisible());
// Applying: everything rests until the result lands.
bool done = false;
editor.session.commitFilter([&] { done = true; });
QVERIFY(!sheet.isEnabled());
QCOMPARE(find<QLabel>(sheet, "filterActivity").text(), QString("Applying…"));
QVERIFY(find<QProgressBar>(sheet, "filterSpinner").isVisible());
QTRY_VERIFY(done);
QVERIFY(!editor.sheet() && !editor.session.filterEdit().has_value());
QCOMPARE(editor.session.history.undoName(), QString("Content-Aware Fill"));
}
void FilterSheetTests::previewCancelAndOKReachTheSession()
{
Editor editor;
editor.session.beginFilter(FilterKind::contentAwareFill);
QVERIFY(editor.prepared());
const QUuid id = editor.session.activeLayerID().value();
QVERIFY(editor.session.filterEdit().value().previewImage(id).has_value());
QCheckBox &preview = find<QCheckBox>(*editor.sheet(), "filterPreview");
preview.click();
QVERIFY(!editor.session.filterEdit().value().preview);
QVERIFY(!editor.session.filterEdit().value().previewImage(id).has_value());
preview.click();
QVERIFY(editor.session.filterEdit().value().previewImage(id).has_value());
find<QPushButton>(*editor.sheet(), "filterCancel").click();
QVERIFY(!editor.session.filterEdit().has_value() && !editor.sheet());
editor.session.beginFilter(FilterKind::contentAwareFill);
QVERIFY(editor.prepared());
const int count = editor.session.history.undoCount();
find<QPushButton>(*editor.sheet(), "filterOK").click();
QTRY_VERIFY(!editor.session.filterEdit().has_value());
QCOMPARE(editor.session.history.undoCount(), count + 1);
}
void FilterSheetTests::escapeAndClosingCancelReturnCommits()
{
Editor editor;
editor.session.beginFilter(FilterKind::contentAwareFill);
QVERIFY(editor.prepared());
QTest::keyClick(editor.sheet(), Qt::Key_Escape);
QVERIFY(!editor.session.filterEdit().has_value() && !editor.sheet());
editor.session.beginFilter(FilterKind::contentAwareFill);
editor.panel().close();
QVERIFY(!editor.session.filterEdit().has_value() && !editor.sheet());
// Return is OK while it is enabled, nothing while waiting.
editor.session.beginFilter(FilterKind::contentAwareFill);
QTest::keyClick(editor.sheet(), Qt::Key_Return);
QVERIFY(editor.session.filterEdit().has_value() && !editor.session.filterEdit().value().committing);
QVERIFY(editor.prepared());
QTest::keyClick(editor.sheet(), Qt::Key_Return);
QVERIFY(editor.session.filterEdit().value().committing);
QTRY_VERIFY(!editor.session.filterEdit().has_value());
QCOMPARE(editor.session.history.undoName(), QString("Content-Aware Fill"));
// Return on Cancel is still OK's: nothing while OK waits.
const int count = editor.session.history.undoCount();
editor.session.beginFilter(FilterKind::contentAwareFill);
QPushButton &cancel = find<QPushButton>(*editor.sheet(), "filterCancel");
cancel.setFocus();
QTest::keyClick(&cancel, Qt::Key_Return);
QVERIFY(editor.session.filterEdit().has_value() && !editor.session.filterEdit().value().committing);
QVERIFY(editor.prepared());
QTest::keyClick(&cancel, Qt::Key_Return);
QVERIFY(editor.session.filterEdit().value().committing);
QTRY_VERIFY(!editor.session.filterEdit().has_value());
QCOMPARE(editor.session.history.undoCount(), count + 1);
}
void FilterSheetTests::noSourceShowsTheErrorAndHoldsOK()
{
Editor editor;
editor.session.selectAll();
editor.session.beginFilter(FilterKind::contentAwareFill);
QVERIFY(editor.prepared());
QLabel &error = find<QLabel>(*editor.sheet(), "filterError");
QVERIFY(error.isVisible());
QCOMPARE(error.text(), QString("Not enough unselected, opaque image pixels to synthesize a fill. Use a smaller selection with some surrounding image."));
QCOMPARE(error.foregroundRole(), QPalette::BrightText);
QVERIFY(!find<QPushButton>(*editor.sheet(), "filterOK").isEnabled());
QTest::keyClick(editor.sheet(), Qt::Key_Return);
QVERIFY(editor.session.filterEdit().has_value() && !editor.session.filterEdit().value().committing);
// Without a selection, others open without the note.
find<QPushButton>(*editor.sheet(), "filterCancel").click();
editor.session.deselect();
QVERIFY(!editor.session.canContentAwareFill());
editor.session.beginFilter(FilterKind::gaussianBlur);
QVERIFY(!find<QLabel>(*editor.sheet(), "filterLimited").isVisible());
// Only an automatic kind holds OK while it prepares.
QVERIFY(editor.session.filterEdit().value().preparing && find<QPushButton>(*editor.sheet(), "filterOK").isEnabled());
}
QTEST_MAIN(FilterSheetTests)
#include "FilterSheetTests.moc"