Files
OmaPhoto/tests/ImageSizeSheetTests.cpp
T

287 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "Document/DocumentLimits.h"
#include "UI/ColorPickerSheet.h"
#include "UI/ImageSizeSheet.h"
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QPushButton>
#include <QApplication>
#include <QWheelEvent>
#include <QtTest>
// Swift's ImageSizeSheet: pixels resampled or print size alone.
class ImageSizeSheetTests : public QObject {
Q_OBJECT
private slots:
void theSheetShowsTheCanvas();
void pixelsFollowUnitsAndTheLock();
void aNewResolutionKeepsThePrintSize();
void withoutResamplingOnlyTheResolutionMoves();
void anInvalidSizeRestsResize();
void returnWithoutTypingKeepsTheNumbers();
void aWheelOverUnitsCommitsTheTypingFirst();
void halfwayNumbersRoundToEven();
};
namespace {
struct Sheet {
std::optional<std::optional<ImageSizeOptions>> answer;
ImageSizeSheet sheet;
explicit Sheet(int width = 1200, int height = 800)
: sheet(CanvasDocument{width, height}, [this](std::optional<ImageSizeOptions> options) { answer = options; })
{
sheet.show();
}
template <typename Widget> Widget &find(const char *name)
{
Widget *found = sheet.findChild<Widget *>(QString::fromLatin1(name));
if (!found)
throw std::runtime_error(std::string("no widget named ") + name);
return *found;
}
void type(const char *name, const QString &text)
{
auto &field = find<PickerField>(name);
field.setFocus();
field.selectAll();
QTest::keyClicks(&field, text);
QTest::keyClick(&field, Qt::Key_Return);
}
void unit(const QString &name)
{
auto &units = find<QComboBox>("imageUnits");
const int index = units.findText(name);
units.setCurrentIndex(index);
emit units.activated(index);
}
QString shown(const char *name) { return find<PickerField>(name).text(); }
QString result() { return find<QLabel>("imageResult").text(); }
QStringList units()
{
QStringList names;
auto &units = find<QComboBox>("imageUnits");
for (int index = 0; index < units.count(); ++index)
names << units.itemText(index);
return names;
}
};
}
void ImageSizeSheetTests::theSheetShowsTheCanvas()
{
Sheet shown;
QStringList texts;
for (const QLabel *label : shown.sheet.findChildren<QLabel *>())
texts << label->text();
QVERIFY(texts.contains("Image Size"));
QVERIFY(texts.contains("Current: 1,200 × 800 pixels"));
QVERIFY(texts.contains("pixels/inch"));
QCOMPARE(shown.sheet.width(), 430);
QCOMPARE(shown.units(), (QStringList{"Pixels", "Percent", "Inches", "Centimeters"}));
QCOMPARE(shown.shown("imageWidth"), QString("1200"));
QCOMPARE(shown.shown("imageResolution"), QString("72"));
QVERIFY(shown.find<QCheckBox>("imageLocked").isChecked());
QVERIFY(shown.find<QCheckBox>("imageResample").isChecked());
auto &sampling = shown.find<QComboBox>("imageSampling");
QCOMPARE(sampling.currentText(), QString("High quality"));
QCOMPARE(sampling.count(), 3);
QCOMPARE(shown.find<QLabel>("imageExplanation").text(), QString("Resizes layer pixels and applies existing transforms. Undo restores the originals."));
QCOMPARE(shown.result(), QString("Result: 1,200 × 800 pixels"));
// Cancel answers with nothing; Resize with the size.
shown.find<QPushButton>("imageCancel").click();
QVERIFY(!shown.answer.value().has_value());
emit sampling.activated(0);
shown.find<QPushButton>("imageResize").click();
const ImageSizeOptions options = shown.answer.value().value();
QVERIFY(options.width == 1200 && options.height == 800 && options.resolution == 72 && options.sampling == LayerSampling::nearest);
}
void ImageSizeSheetTests::pixelsFollowUnitsAndTheLock()
{
Sheet shown;
shown.type("imageWidth", "600");
QCOMPARE(shown.shown("imageHeight"), QString("400"));
shown.find<QCheckBox>("imageLocked").click();
shown.type("imageHeight", "500");
QCOMPARE(shown.shown("imageWidth"), QString("600"));
QCOMPARE(shown.result(), QString("Result: 600 × 500 pixels"));
shown.unit("Percent");
QCOMPARE(shown.shown("imageWidth"), QString("50"));
QCOMPARE(shown.shown("imageHeight"), QString("62.5"));
shown.type("imageWidth", "25");
QCOMPARE(shown.result(), QString("Result: 300 × 500 pixels"));
shown.unit("Inches");
QCOMPARE(shown.shown("imageWidth"), QString("4.167"));
shown.type("imageWidth", "10");
QCOMPARE(shown.result(), QString("Result: 720 × 500 pixels"));
shown.unit("Centimeters");
shown.type("imageWidth", "2.54");
QCOMPARE(shown.result(), QString("Result: 72 × 500 pixels"));
QCOMPARE(shown.shown("imageHeight"), QString("17.639"));
// Percent counts each side against its own original.
shown.unit("Percent");
shown.type("imageHeight", "50");
QCOMPARE(shown.result(), QString("Result: 72 × 400 pixels"));
// Locked, a height takes the width along.
shown.find<QCheckBox>("imageLocked").click();
shown.unit("Pixels");
shown.type("imageHeight", "800");
QCOMPARE(shown.result(), QString("Result: 144 × 800 pixels"));
shown.find<QCheckBox>("imageLocked").click();
shown.type("imageWidth", "600.6");
shown.find<QPushButton>("imageResize").click();
QCOMPARE(shown.answer.value().value().width, 601);
// Nothing, no number and zero are refused, as Swift's guard.
for (const QString &refused : {QString("0"), QString("-3"), QString("wide")}) {
shown.type("imageWidth", refused);
QCOMPARE(shown.result(), QString("Result: 601 × 800 pixels"));
}
}
void ImageSizeSheetTests::aNewResolutionKeepsThePrintSize()
{
Sheet shown;
// In physical units the pixels follow the resolution.
shown.unit("Inches");
shown.type("imageResolution", "144");
QCOMPARE(shown.result(), QString("Result: 2,400 × 1,600 pixels"));
QCOMPARE(shown.shown("imageWidth"), QString("16.667"));
// In pixels they stay.
shown.unit("Pixels");
shown.type("imageResolution", "300");
QCOMPARE(shown.result(), QString("Result: 2,400 × 1,600 pixels"));
shown.find<QPushButton>("imageResize").click();
QCOMPARE(shown.answer.value().value().resolution, 300.0);
// Zero changes nothing but the resolution, which is refused.
shown.unit("Inches");
shown.type("imageResolution", "0");
QCOMPARE(shown.result(), QStringLiteral("Use 1–30,000 pixels per side, up to %1 megapixels, and 1–9,600 pixels/inch.").arg(DocumentLimits::maxSurfaceMegapixels()));
shown.type("imageResolution", "150");
QCOMPARE(shown.result(), QString("Result: 2,400 × 1,600 pixels"));
// Centimetres keep the print size alike.
shown.unit("Centimeters");
shown.type("imageResolution", "300");
QCOMPARE(shown.result(), QString("Result: 4,800 × 3,200 pixels"));
}
void ImageSizeSheetTests::withoutResamplingOnlyTheResolutionMoves()
{
Sheet shown;
shown.type("imageWidth", "600");
shown.find<QCheckBox>("imageLocked").click();
shown.find<QCheckBox>("imageResample").click();
// The pixels go back; physical units alone; locked.
QCOMPARE(shown.units(), (QStringList{"Inches", "Centimeters"}));
QCOMPARE(shown.find<QComboBox>("imageUnits").currentText(), QString("Inches"));
QVERIFY(shown.find<QCheckBox>("imageLocked").isChecked());
QVERIFY(!shown.find<QCheckBox>("imageLocked").isEnabled());
QVERIFY(!shown.find<QWidget>("imageSamplingRow").isVisible());
QCOMPARE(shown.find<QLabel>("imageExplanation").text(), QString("Only print dimensions and resolution change. Pixels stay unchanged."));
QCOMPARE(shown.result(), QString("Result: 1,200 × 800 pixels"));
shown.type("imageWidth", "10");
QCOMPARE(shown.shown("imageResolution"), QString("120"));
QCOMPARE(shown.shown("imageHeight"), QString("6.667"));
shown.unit("Centimeters");
shown.type("imageHeight", "25.4");
QCOMPARE(shown.shown("imageResolution"), QString("80"));
shown.find<QPushButton>("imageResize").click();
const ImageSizeOptions options = shown.answer.value().value();
QVERIFY(options.width == 1200 && options.height == 800 && options.resolution == 80);
// Without resampling a resolution moves no pixels.
shown.type("imageResolution", "160");
QCOMPARE(shown.result(), QString("Result: 1,200 × 800 pixels"));
// Back on, the units return; the pixels are kept.
shown.find<QCheckBox>("imageResample").click();
QCOMPARE(shown.units(), (QStringList{"Pixels", "Percent", "Inches", "Centimeters"}));
QCOMPARE(shown.find<QComboBox>("imageUnits").currentText(), QString("Centimeters"));
QVERIFY(shown.find<QCheckBox>("imageLocked").isEnabled());
}
void ImageSizeSheetTests::anInvalidSizeRestsResize()
{
Sheet shown;
auto &resize = shown.find<QPushButton>("imageResize");
QVERIFY(resize.isDefault() && resize.isEnabled());
shown.find<QCheckBox>("imageLocked").click();
shown.type("imageWidth", "30001");
QCOMPARE(shown.result(), QStringLiteral("Use 1–30,000 pixels per side, up to %1 megapixels, and 1–9,600 pixels/inch.").arg(DocumentLimits::maxSurfaceMegapixels()));
QCOMPARE(shown.find<QLabel>("imageResult").foregroundRole(), QPalette::BrightText);
QVERIFY(!resize.isEnabled());
// Past one surface's 200 megapixels while resampling.
shown.type("imageWidth", "20000");
shown.type("imageHeight", "10001");
QVERIFY(!resize.isEnabled());
shown.type("imageHeight", "10000");
QVERIFY(resize.isEnabled());
QCOMPARE(shown.find<QLabel>("imageResult").foregroundRole(), QPalette::PlaceholderText);
shown.type("imageResolution", "9601");
QVERIFY(!resize.isEnabled());
shown.type("imageResolution", "9600");
QVERIFY(resize.isEnabled());
shown.type("imageResolution", "0.5");
QVERIFY(!resize.isEnabled());
shown.type("imageResolution", "72");
shown.type("imageWidth", "30000");
shown.type("imageHeight", "1");
QVERIFY(resize.isEnabled());
// Without resampling the pixels are the canvas's, however many.
Sheet huge(30'000, 30'000);
QVERIFY(!huge.find<QPushButton>("imageResize").isEnabled());
huge.find<QCheckBox>("imageResample").click();
QVERIFY(huge.find<QPushButton>("imageResize").isEnabled());
}
void ImageSizeSheetTests::returnWithoutTypingKeepsTheNumbers()
{
Sheet shown;
shown.find<QCheckBox>("imageResample").click();
// A size shown to three places would move the resolution.
auto &width = shown.find<PickerField>("imageWidth");
width.setFocus();
QTest::keyClick(&width, Qt::Key_Return);
QCOMPARE(shown.shown("imageResolution"), QString("72"));
shown.type("imageWidth", "7");
auto &resolution = shown.find<PickerField>("imageResolution");
QCOMPARE(resolution.text(), QString("171.429"));
resolution.setFocus();
QTest::keyClick(&resolution, Qt::Key_Return);
shown.find<QPushButton>("imageResize").click();
QCOMPARE(shown.answer.value().value().resolution, 1200.0 / 7);
}
// Scrolled, Units take no focus; the typing meant pixels.
void ImageSizeSheetTests::aWheelOverUnitsCommitsTheTypingFirst()
{
Sheet shown;
// Focus moves only in the active window.
shown.sheet.activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&shown.sheet));
auto &width = shown.find<PickerField>("imageWidth");
width.setFocus();
width.selectAll();
QTest::keyClicks(&width, "600");
auto &units = shown.find<QComboBox>("imageUnits");
QWheelEvent wheel(QPointF(5, 5), units.mapToGlobal(QPointF(5, 5)), QPoint(), QPoint(0, -120), Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
QApplication::sendEvent(&units, &wheel);
QCOMPARE(units.currentText(), QString("Percent"));
QCOMPARE(width.text(), QString("50"));
QCOMPARE(shown.result(), QString("Result: 600 × 400 pixels"));
}
// Foundation rounds halves to even: 17 pixels, 16 per inch.
void ImageSizeSheetTests::halfwayNumbersRoundToEven()
{
CanvasDocument canvas{17, 19};
canvas.resolution = 16;
ImageSizeSheet sheet(canvas, [](std::optional<ImageSizeOptions>) {});
auto &units = *sheet.findChild<QComboBox *>(QStringLiteral("imageUnits"));
const int inches = units.findText(QStringLiteral("Inches"));
units.setCurrentIndex(inches);
emit units.activated(inches);
QCOMPARE(sheet.findChild<PickerField *>(QStringLiteral("imageWidth"))->text(), QString("1.062"));
QCOMPARE(sheet.findChild<PickerField *>(QStringLiteral("imageHeight"))->text(), QString("1.188"));
}
QTEST_MAIN(ImageSizeSheetTests)
#include "ImageSizeSheetTests.moc"