Files
OmaPhoto/tests/CommitContinuationTests.cpp
T

174 lines
6.2 KiB
C++

#include "BrushFixtures.h"
#include "Document/EditorSession.h"
#include "SelectionFixtures.h"
// Swift resumes after await in one job: nothing lands between.
namespace {
std::unique_ptr<EditorSession> halves()
{
auto session = std::make_unique<EditorSession>();
session->createDocument(100, 40, true);
QImage image = BrushRaster::context(100, 40, false);
QPainter painter(&image);
painter.fillRect(QRect(0, 0, 50, 40), Qt::red);
painter.fillRect(QRect(50, 0, 50, 40), Qt::blue);
painter.end();
session->insert(ImportedImage(image, image, "Halves"));
session->deleteLayer(session->document().value().layers[0].id);
return session;
}
// Runs `look` at the first event after the project frees.
void atTheNextEvent(EditorSession &session, std::function<void()> look)
{
auto busy = std::make_shared<bool>(false), armed = std::make_shared<bool>(false);
QObject::connect(&session, &EditorSession::changed, &session, [&session, busy, armed, look] {
if (session.isProjectBusy())
*busy = true;
if (!*busy || session.isProjectBusy() || *armed)
return;
*armed = true;
QTimer::singleShot(0, &session, look);
});
}
void pendingGradient(EditorSession &session)
{
session.selectTool(NavigationTool::gradient);
session.beginGradient(QPointF(0.5, 2));
session.moveGradient(std::nullopt, QPointF(99.5, 2));
session.endGradientDrag();
}
bool done(const std::function<void(std::function<void()>)> &call)
{
bool finished = false;
call([&] { finished = true; });
return QTest::qWaitFor([&] { return finished; }, 10000);
}
}
class CommitContinuationTests : public QObject {
Q_OBJECT
private slots:
void theProjectFreesWithTheGradientGone();
void theProjectFreesWithTheMoveGone();
void theFloatingEditIsReadyAtTheNextEvent();
void aToolChosenAfterTheLiftStaysChosen();
void aChainedEditKeepsItsLayer_data();
void aChainedEditKeepsItsLayer();
};
void CommitContinuationTests::theProjectFreesWithTheGradientGone()
{
const auto session = halves();
pendingGradient(*session);
std::optional<bool> pending, editable;
atTheNextEvent(*session, [&] {
pending = session->gradientEdit().has_value();
editable = session->canEditLayers();
});
QVERIFY(done([&](auto finished) { session->commitGradient(finished); }));
QTRY_VERIFY(pending.has_value());
QCOMPARE(pending.value(), false);
QCOMPARE(editable.value(), true);
}
void CommitContinuationTests::theProjectFreesWithTheMoveGone()
{
const auto session = halves();
session->applySelection(rectPath(QRectF(0, 0, 50, 40)), SelectionMode::replace, "Select");
QVERIFY(session->beginPixelMove(false));
session->movePixels(QSizeF(10, 0));
std::optional<bool> moving, editable;
atTheNextEvent(*session, [&] {
moving = session->pixelMove() != nullptr;
editable = session->canEditLayers();
});
QVERIFY(done([&](auto finished) { session->finishPixelMove(finished); }));
QTRY_VERIFY(moving.has_value());
QCOMPARE(moving.value(), false);
QCOMPARE(editable.value(), true);
}
void CommitContinuationTests::theFloatingEditIsReadyAtTheNextEvent()
{
const auto session = halves();
session->applySelection(rectPath(QRectF(10, 10, 10, 10)), SelectionMode::replace, "Select");
std::optional<bool> floating, editable;
atTheNextEvent(*session, [&] {
floating = session->transformEdit().has_value();
editable = session->canEditLayers();
});
QVERIFY(done([&](auto finished) { session->beginSelectionTransform(finished); }));
QTRY_VERIFY(floating.has_value());
QCOMPARE(floating.value(), true);
QCOMPARE(editable.value(), false);
}
void CommitContinuationTests::aToolChosenAfterTheLiftStaysChosen()
{
const auto session = halves();
session->applySelection(rectPath(QRectF(10, 10, 10, 10)), SelectionMode::replace, "Select");
bool chosen = false;
atTheNextEvent(*session, [&] {
session->selectTool(NavigationTool::brush);
chosen = true;
});
QVERIFY(done([&](auto finished) { session->beginSelectionTransform(finished); }));
QTRY_VERIFY(chosen);
// The choice applied the floating edit and stayed.
QCOMPARE(session->tool(), NavigationTool::brush);
QVERIFY(!session->transformEdit().has_value());
}
void CommitContinuationTests::aChainedEditKeepsItsLayer_data()
{
QTest::addColumn<int>("command");
QTest::newRow("filter") << 0;
QTest::newRow("levels") << 1;
QTest::newRow("hue") << 2;
QTest::newRow("invert") << 3;
}
void CommitContinuationTests::aChainedEditKeepsItsLayer()
{
QFETCH(int, command);
const auto session = halves();
const QUuid target = session->activeLayerID().value();
QImage green = BrushRaster::context(100, 40, false);
green.fill(QColor(0, 255, 0));
session->insert(ImportedImage(green, green, "Other"));
const QUuid other = session->activeLayerID().value();
session->selectLayer(target);
pendingGradient(*session);
// Another layer chosen once the gradient lands, if allowed.
atTheNextEvent(*session, [&] {
if (session->canEditLayers())
session->selectLayer(other);
});
if (command == 0) {
session->beginFilter(FilterKind::gaussianBlur);
QTRY_VERIFY(session->filterEdit().has_value());
QCOMPARE(session->filterEdit().value().layerID, target);
} else if (command == 1) {
session->beginLevels();
QTRY_VERIFY(session->levels().has_value());
QCOMPARE(session->levels().value().layerID, target);
} else if (command == 2) {
session->beginHueSaturation();
QTRY_VERIFY(session->hueSaturation().has_value());
QCOMPARE(session->hueSaturation().value().layerID, target);
} else {
QVERIFY(done([&](auto finished) { session->invertPixels(finished); }));
QCOMPARE(session->history.undoName(), QString("Invert"));
QCOMPARE(session->activeLayerID().value(), target);
const ImageLayer kept = session->document().value().layers.back();
QCOMPARE(kept.id, other);
QCOMPARE(kept.asset.value().image().pixelColor(5, 5), QColor(0, 255, 0));
}
}
QTEST_GUILESS_MAIN(CommitContinuationTests)
#include "CommitContinuationTests.moc"