Files
OmaPhoto/tests/LevelsFailureTests.cpp
T
雷电芽衣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

96 lines
3.7 KiB
C++

#include "AddressSpaceLimit.h"
#include "LevelsFixtures.h"
#include "Rendering/RasterSnapshot.h"
#include <malloc.h>
// Levels when memory runs out, each on a fresh heap.
namespace {
// A painted asset without full pixels: a two-pixel base.
std::unique_ptr<EditorSession> rasterSession(int width, int height)
{
QImage base(2, 2, QImage::Format_RGBA8888_Premultiplied);
base.fill(Qt::red);
auto session = std::make_unique<EditorSession>();
session->createDocument(20, 10);
session->insert(ImportedImage(std::make_shared<const RasterSnapshot>(width, height, base, QRectF(0, 0, 2, 2), std::vector<BrushPatch>{}),
QImage(), QStringLiteral("Painted")));
return session;
}
LevelsSettings inverted()
{
LevelsSettings settings;
settings.setCurrent(LevelRange{0, 1, 255, 255, 0});
return settings;
}
}
// Earlier tests leave free heap that big asks would reuse.
class LevelsFailureTests : public QObject {
Q_OBJECT
private slots:
// First: it leaves no free heap behind for the rest.
void aFailedCountOrPreviewLeavesNothing();
void failuresReachTheSessionsError();
};
void LevelsFailureTests::failuresReachTheSessionsError()
{
// Workers allocate in their own arenas: only large asks fail.
{
// 10000 by 9999 in tiles: the scaled copy, 256 MB.
const std::unique_ptr<EditorSession> session = rasterSession(10000, 9999);
{
// Freed heap returns first: the room is real.
malloc_trim(0);
const AddressSpaceLimit limit(16ll * 1024 * 1024);
session->beginLevels();
}
QVERIFY(!session->levels() && session->brushError());
}
// 30000 by 3000: a small copy; the flatten, 360 MB.
const std::unique_ptr<EditorSession> session = rasterSession(30000, 3000);
session->beginLevels();
QTRY_VERIFY(session->levels().value().histogramReady);
session->updateLevels(inverted(), false);
const ImageIdentity before = session->activeLayer().value().asset.value().identity();
const int count = session->history.undoCount();
session->setLevelsSampleMode(LevelsSample::gray);
{
malloc_trim(0);
const AddressSpaceLimit limit(16ll * 1024 * 1024);
// A sample flattens the pixels first.
session->sampleLevels(QPointF(5, 5));
QVERIFY(session->brushError());
session->setBrushError(std::nullopt);
commit(*session);
}
QVERIFY(session->brushError() && !session->levels() && !session->isProjectBusy());
QCOMPARE(session->activeLayer().value().asset.value().identity(), before);
QCOMPARE(session->history.undoCount(), count);
}
void LevelsFailureTests::aFailedCountOrPreviewLeavesNothing()
{
// Flattened first; the count's and preview's copies need 67 MB.
const std::unique_ptr<EditorSession> session = rasterSession(8000, 2100);
QVERIFY(!session->activeLayer().value().asset.value().image().isNull());
{
malloc_trim(0);
const AddressSpaceLimit limit(16ll * 1024 * 1024);
session->beginLevels();
QTRY_VERIFY(session->levels().value().histogramReady);
for (const std::array<double, 256> &channel : session->levels().value().histogram)
QVERIFY(std::all_of(channel.begin(), channel.end(), [](double bin) { return bin == 0; }));
// A failed preview lands as none, and redraws.
const int revision = session->brushRevision();
session->updateLevels(inverted(), true);
QTRY_COMPARE(session->brushRevision(), revision + 1);
QVERIFY(!session->levels().value().preparedPreview);
}
session->cancelLevels();
}
QTEST_GUILESS_MAIN(LevelsFailureTests)
#include "LevelsFailureTests.moc"