diff options
Diffstat (limited to 'tests/auto/gui')
31 files changed, 1091 insertions, 62 deletions
diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index d5c5752f95..01971ee3c1 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -103,6 +103,10 @@ private slots: void setPixel_data(); void setPixel(); + void setPixelWithAlpha_data(); + void setPixelWithAlpha(); + void setPixelColorWithAlpha_data(); + void setPixelColorWithAlpha(); void defaultColorTable_data(); void defaultColorTable(); @@ -147,6 +151,7 @@ private slots: void fillColor_data(); void fillColor(); + void fillColorWithAlpha_data(); void fillColorWithAlpha(); void fillRGB888(); @@ -1477,6 +1482,62 @@ void tst_QImage::setPixel() } } +void tst_QImage::setPixelWithAlpha_data() +{ + QTest::addColumn<QImage::Format>("format"); + + for (int c = QImage::Format_RGB32; c < QImage::NImageFormats; ++c) { + if (c == QImage::Format_Grayscale8) + continue; + if (c == QImage::Format_Grayscale16) + continue; + if (c == QImage::Format_Alpha8) + continue; + QTest::newRow(qPrintable(formatToString(QImage::Format(c)))) << QImage::Format(c); + } +} + +void tst_QImage::setPixelWithAlpha() +{ + QFETCH(QImage::Format, format); + QImage image(1, 1, format); + QRgb referenceColor = qRgba(0, 170, 85, 170); + image.setPixel(0, 0, referenceColor); + + if (!image.hasAlphaChannel()) + referenceColor = 0xff000000 | referenceColor; + + QRgb color = image.pixel(0, 0); + QCOMPARE(qRed(color) & 0xf0, qRed(referenceColor) & 0xf0); + QCOMPARE(qGreen(color) & 0xf0, qGreen(referenceColor) & 0xf0); + QCOMPARE(qBlue(color) & 0xf0, qBlue(referenceColor) & 0xf0); + QCOMPARE(qAlpha(color) & 0xf0, qAlpha(referenceColor) & 0xf0); +} + +void tst_QImage::setPixelColorWithAlpha_data() +{ + setPixelWithAlpha_data(); +} + +void tst_QImage::setPixelColorWithAlpha() +{ + QFETCH(QImage::Format, format); + QImage image(1, 1, format); + image.setPixelColor(0, 0, QColor(170, 85, 255, 170)); + QRgb referenceColor = qRgba(170, 85, 255, 170); + + if (!image.hasAlphaChannel()) + referenceColor = 0xff000000 | referenceColor; + else if (image.pixelFormat().premultiplied() == QPixelFormat::Premultiplied) + referenceColor = qPremultiply(referenceColor); + + QRgb color = image.pixel(0, 0); + QCOMPARE(qRed(color) & 0xf0, qRed(referenceColor) & 0xf0); + QCOMPARE(qGreen(color) & 0xf0, qGreen(referenceColor) & 0xf0); + QCOMPARE(qBlue(color) & 0xf0, qBlue(referenceColor) & 0xf0); + QCOMPARE(qAlpha(color) & 0xf0, qAlpha(referenceColor) & 0xf0); +} + void tst_QImage::convertToFormatPreserveDotsPrMeter() { QImage img(100, 100, QImage::Format_ARGB32_Premultiplied); @@ -2342,15 +2403,28 @@ void tst_QImage::fillColor() } } +void tst_QImage::fillColorWithAlpha_data() +{ + setPixelWithAlpha_data(); +} + void tst_QImage::fillColorWithAlpha() { - QImage argb32(1, 1, QImage::Format_ARGB32); - argb32.fill(QColor(255, 0, 0, 127)); - QCOMPARE(argb32.pixel(0, 0), qRgba(255, 0, 0, 127)); + QFETCH(QImage::Format, format); + QImage image(1, 1, format); + image.fill(QColor(255, 170, 85, 170)); + QRgb referenceColor = qRgba(255, 170, 85, 170); - QImage argb32pm(1, 1, QImage::Format_ARGB32_Premultiplied); - argb32pm.fill(QColor(255, 0, 0, 127)); - QCOMPARE(argb32pm.pixel(0, 0), 0x7f7f0000u); + if (!image.hasAlphaChannel()) + referenceColor = 0xff000000 | referenceColor; + else if (image.pixelFormat().premultiplied() == QPixelFormat::Premultiplied) + referenceColor = qPremultiply(referenceColor); + + QRgb color = image.pixel(0, 0); + QCOMPARE(qRed(color) & 0xf0, qRed(referenceColor) & 0xf0); + QCOMPARE(qGreen(color) & 0xf0, qGreen(referenceColor) & 0xf0); + QCOMPARE(qBlue(color) & 0xf0, qBlue(referenceColor) & 0xf0); + QCOMPARE(qAlpha(color) & 0xf0, qAlpha(referenceColor) & 0xf0); } void tst_QImage::fillRGB888() @@ -2376,10 +2450,13 @@ void tst_QImage::fillPixel_data() QTest::newRow("RGB16, transparent") << QImage::Format_RGB16 << 0x0u << 0xff000000u; QTest::newRow("RGB32, transparent") << QImage::Format_RGB32 << 0x0u << 0xff000000u; + QTest::newRow("RGB444, transparent") << QImage::Format_RGB444 << 0x0u << 0xff000000u; + QTest::newRow("RGB666, transparent") << QImage::Format_RGB666 << 0x0u << 0xff000000u; QTest::newRow("RGBx8888, transparent") << QImage::Format_RGBX8888 << 0x0u << 0xff000000u; QTest::newRow("ARGB32, transparent") << QImage::Format_ARGB32 << 0x0u << 0x00000000u; QTest::newRow("ARGB32pm, transparent") << QImage::Format_ARGB32_Premultiplied << 0x0u << 0x00000000u; QTest::newRow("RGBA8888pm, transparent") << QImage::Format_RGBA8888_Premultiplied << 0x0u << 0x00000000u; + QTest::newRow("Grayscale8, transparent") << QImage::Format_Grayscale8 << 0x0u << 0xff000000u; QTest::newRow("Alpha8, transparent") << QImage::Format_Alpha8 << 0x0u << 0x00000000u; QTest::newRow("RGB16, red") << QImage::Format_RGB16 << (uint)qConvertRgb32To16(0xffff0000) << 0xffff0000u; @@ -2387,13 +2464,14 @@ void tst_QImage::fillPixel_data() QTest::newRow("ARGB32, red") << QImage::Format_ARGB32 << 0xffff0000u << 0xffff0000u; QTest::newRow("RGBA8888, red") << QImage::Format_RGBA8888 << 0xff0000ffu << 0xffff0000u; - QTest::newRow("Grayscale8, grey") << QImage::Format_Grayscale8 << 0xff808080u << 0xff808080u; + QTest::newRow("Grayscale8, grey") << QImage::Format_Grayscale8 << 0x80u << 0xff808080u; QTest::newRow("RGB32, semi-red") << QImage::Format_RGB32 << 0x80ff0000u << 0xffff0000u; QTest::newRow("ARGB32, semi-red") << QImage::Format_ARGB32 << 0x80ff0000u << 0x80ff0000u; QTest::newRow("ARGB32pm, semi-red") << QImage::Format_ARGB32 << 0x80800000u << 0x80800000u; QTest::newRow("RGBA8888pm, semi-red") << QImage::Format_RGBA8888_Premultiplied << 0x80000080u << 0x80800000u; - QTest::newRow("Alpha8, semi-red") << QImage::Format_Alpha8 << 0x80000080u << 0x80000000u; + + QTest::newRow("Alpha8, semi-transparent") << QImage::Format_Alpha8 << 0x80u << 0x80000000u; } void tst_QImage::fillPixel() @@ -2406,6 +2484,8 @@ void tst_QImage::fillPixel() image.fill(color); QCOMPARE(image.pixel(0, 0), pixelValue); + if (image.depth() == 8) + QCOMPARE(*(const uchar *)image.constBits(), color); } void tst_QImage::rgbSwapped_data() @@ -3492,6 +3572,13 @@ void tst_QImage::metadataPassthrough() QCOMPARE(converted.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(converted.devicePixelRatio(), a.devicePixelRatio()); + QVector<QRgb> clut({ 0xFFFF0000, 0xFF00FF00, 0xFF0000FF }); + QImage convertedWithClut = a.convertToFormat(QImage::Format_Indexed8, clut); + QCOMPARE(convertedWithClut.text(QStringLiteral("Test")), a.text(QStringLiteral("Test"))); + QCOMPARE(convertedWithClut.dotsPerMeterX(), a.dotsPerMeterX()); + QCOMPARE(convertedWithClut.dotsPerMeterY(), a.dotsPerMeterY()); + QCOMPARE(convertedWithClut.devicePixelRatio(), a.devicePixelRatio()); + QImage copied = a.copy(0, 0, a.width() / 2, a.height() / 2); QCOMPARE(copied.text(QStringLiteral("Test")), a.text(QStringLiteral("Test"))); QCOMPARE(copied.dotsPerMeterX(), a.dotsPerMeterX()); @@ -3536,6 +3623,14 @@ void tst_QImage::pixelColor() // Try setting an invalid color. QTest::ignoreMessage(QtWarningMsg, "QImage::setPixelColor: color is invalid"); argb32.setPixelColor(0, 0, QColor()); + + // Test correct premultiplied handling of RGBA64 as well + QImage rgba64(1, 1, QImage::Format_RGBA64); + QImage rgba64pm(1, 1, QImage::Format_RGBA64_Premultiplied); + rgba64.setPixelColor(QPoint(0, 0), c); + rgba64pm.setPixelColor(QPoint(0, 0), c); + QCOMPARE(rgba64.pixelColor(QPoint(0, 0)), c); + QCOMPARE(rgba64pm.pixelColor(QPoint(0, 0)), c); } void tst_QImage::pixel() diff --git a/tests/auto/gui/image/qpixmap/tst_qpixmap.cpp b/tests/auto/gui/image/qpixmap/tst_qpixmap.cpp index 1d77f70919..417514607f 100644 --- a/tests/auto/gui/image/qpixmap/tst_qpixmap.cpp +++ b/tests/auto/gui/image/qpixmap/tst_qpixmap.cpp @@ -120,6 +120,7 @@ private slots: void copy(); void deepCopyPreservesDpr(); + void fillPreservesDpr(); void dprPassthrough(); void depthOfNullObjects(); @@ -1156,6 +1157,19 @@ void tst_QPixmap::deepCopyPreservesDpr() QCOMPARE(dest.devicePixelRatio(), dpr); } +// Check that the DPR is preserved after doing a fill after an +// assigned copy of the QPixmap +void tst_QPixmap::fillPreservesDpr() +{ + const qreal dpr = 2; + QPixmap src(32, 32); + src.setDevicePixelRatio(dpr); + src.fill(Qt::red); + QPixmap dest = src; + dest.fill(Qt::blue); + QCOMPARE(dest.devicePixelRatio(), dpr); +} + void tst_QPixmap::dprPassthrough() { const qreal dpr = 2; diff --git a/tests/auto/gui/kernel/qbackingstore/tst_qbackingstore.cpp b/tests/auto/gui/kernel/qbackingstore/tst_qbackingstore.cpp index fb4f80f085..ac3123814c 100644 --- a/tests/auto/gui/kernel/qbackingstore/tst_qbackingstore.cpp +++ b/tests/auto/gui/kernel/qbackingstore/tst_qbackingstore.cpp @@ -43,8 +43,47 @@ class tst_QBackingStore : public QObject private slots: void flush(); + + void scrollRectInImage_data(); + void scrollRectInImage(); }; +void tst_QBackingStore::scrollRectInImage_data() +{ + QTest::addColumn<QRect>("rect"); + QTest::addColumn<QPoint>("offset"); + + QTest::newRow("empty rect") << QRect() << QPoint(); + QTest::newRow("rect outside image") << QRect(-100, -100, 1000, 1000) << QPoint(10, 10); + QTest::newRow("scroll outside positive") << QRect(10, 10, 10, 10) << QPoint(1000, 1000); + QTest::newRow("scroll outside negative") << QRect(10, 10, 10, 10) << QPoint(-1000, -1000); + + QTest::newRow("sub-rect positive scroll") << QRect(100, 100, 50, 50) << QPoint(10, 10); + QTest::newRow("sub-rect negative scroll") << QRect(100, 100, 50, 50) << QPoint(-10, -10); + + QTest::newRow("positive vertical only") << QRect(100, 100, 50, 50) << QPoint(0, 10); + QTest::newRow("negative vertical only") << QRect(100, 100, 50, 50) << QPoint(0, -10); + QTest::newRow("positive horizontal only") << QRect(100, 100, 50, 50) << QPoint(10, 0); + QTest::newRow("negative horizontal only") << QRect(100, 100, 50, 50) << QPoint(-10, 0); + + QTest::newRow("whole rect positive") << QRect(0, 0, 250, 250) << QPoint(10, 10); + QTest::newRow("whole rect negative") << QRect(0, 0, 250, 250) << QPoint(-10, -10); +} + +QT_BEGIN_NAMESPACE +Q_GUI_EXPORT void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &); +QT_END_NAMESPACE + +void tst_QBackingStore::scrollRectInImage() +{ + QImage test(250, 250, QImage::Format_ARGB32_Premultiplied); + + QFETCH(QRect, rect); + QFETCH(QPoint, offset); + + qt_scrollRectInImage(test, rect, offset); +} + class Window : public QWindow { public: diff --git a/tests/auto/gui/kernel/qclipboard/test/BLACKLIST b/tests/auto/gui/kernel/qclipboard/test/BLACKLIST new file mode 100644 index 0000000000..ee50796efa --- /dev/null +++ b/tests/auto/gui/kernel/qclipboard/test/BLACKLIST @@ -0,0 +1,4 @@ +[modes] +winrt +[testSignals] +winrt diff --git a/tests/auto/gui/kernel/qguitimer/BLACKLIST b/tests/auto/gui/kernel/qguitimer/BLACKLIST new file mode 100644 index 0000000000..cb678120cd --- /dev/null +++ b/tests/auto/gui/kernel/qguitimer/BLACKLIST @@ -0,0 +1,7 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format +[zeroTimer] # QTBUG-108556 +ci ubuntu-20.04 +ci sles-15.4 + +[singleShotToFunctors] +ci ubuntu-20.04 # QTBUG-108554 diff --git a/tests/auto/gui/kernel/qwindow/BLACKLIST b/tests/auto/gui/kernel/qwindow/BLACKLIST index 44a55316ab..0a767e57e2 100644 --- a/tests/auto/gui/kernel/qwindow/BLACKLIST +++ b/tests/auto/gui/kernel/qwindow/BLACKLIST @@ -10,9 +10,6 @@ macos ci # QTBUG-66851 # QTBUG-69160 opensuse-leap -[setVisible] -# QTBUG-69154 -android [modalWindowEnterEventOnHide_QTBUG35109] osx [spuriousMouseMove] @@ -20,16 +17,7 @@ osx windows-10 [testInputEvents] rhel-7.4 -[exposeEventOnShrink_QTBUG54040] -# QTBUG-69155 -android -opensuse -[initialSize] -# QTBUG-69159 -android +sles-15.4 [modalWindowPosition] # QTBUG-69161 android -[childWindowPositioning:show] -# QTBUG-69156 -android diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index 34de756ab5..682b277470 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -1113,14 +1113,18 @@ void tst_QWindow::touchToMouseTranslation() QVERIFY(QTest::qWaitForWindowExposed(&window)); QList<QWindowSystemInterface::TouchPoint> points; - QWindowSystemInterface::TouchPoint tp1, tp2; + QWindowSystemInterface::TouchPoint tp1, tp2, tp3; const QRectF pressArea(101, 102, 4, 4); + const QRectF pressArea1(107, 110, 4, 4); const QRectF moveArea(105, 108, 4, 4); tp1.id = 1; tp1.state = Qt::TouchPointPressed; tp1.area = QHighDpi::toNativePixels(pressArea, &window); tp2.id = 2; tp2.state = Qt::TouchPointPressed; + tp3.id = 3; + tp3.state = Qt::TouchPointPressed; + tp3.area = QHighDpi::toNativePixels(pressArea1, &window); points << tp1 << tp2; QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); // Now an update but with changed list order. The mouse event should still @@ -1176,6 +1180,68 @@ void tst_QWindow::touchToMouseTranslation() // mouse event synthesizing disabled QTRY_COMPARE(window.mousePressButton, 0); QTRY_COMPARE(window.mouseReleaseButton, 0); + + points.clear(); + points.append(tp2); + points[0].state = Qt::TouchPointPressed; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + points.clear(); + points.append(tp1); + points[0].state = Qt::TouchPointPressed; + points.append(tp2); + points[1].state = Qt::TouchPointStationary; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + QTRY_COMPARE(window.mousePressButton, 1); + + points.clear(); + points.append(tp2); + points[0].state = Qt::TouchPointReleased; + points.append(tp1); + points[1].state = Qt::TouchPointStationary; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + points.clear(); + points.append(tp1); + points[0].state = Qt::TouchPointReleased; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + QTRY_COMPARE(window.mouseReleaseButton, 1); + + points.clear(); + points.append(tp1); + points[0].state = Qt::TouchPointPressed; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + points.clear(); + points.append(tp2); + points[0].state = Qt::TouchPointPressed; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + points.clear(); + points.append(tp3); + points[0].state = Qt::TouchPointPressed; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + QTRY_COMPARE(window.mousePressButton, 1); + + points.clear(); + points.append(tp2); + points[0].state = Qt::TouchPointReleased; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + points.clear(); + points.append(tp3); + points[0].state = Qt::TouchPointReleased; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + points.clear(); + points.append(tp1); + points[0].state = Qt::TouchPointReleased; + QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); + QCoreApplication::processEvents(); + QTRY_COMPARE(window.mouseReleaseButton, 1); } void tst_QWindow::touchToMouseTranslationForDevices() diff --git a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp index 8041fb5439..ed7a6a4d5d 100644 --- a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp +++ b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp @@ -1088,35 +1088,67 @@ void tst_QQuaternion::fromEulerAngles_data() QTest::addColumn<float>("yaw"); QTest::addColumn<float>("roll"); + QTest::addColumn<QQuaternion>("quaternion"); + QTest::newRow("null") - << 0.0f << 0.0f << 0.0f; + << 0.0f << 0.0f << 0.0f << QQuaternion(1.0f, 0.0f, 0.0f, 0.0f); QTest::newRow("xonly") - << 90.0f << 0.0f << 0.0f; + << 90.0f << 0.0f << 0.0f << QQuaternion(0.707107f, 0.707107f, 0.0f, 0.0f); QTest::newRow("yonly") - << 0.0f << 180.0f << 0.0f; + << 0.0f << 180.0f << 0.0f << QQuaternion(0.0f, 0.0f, 1.0f, 0.0f); QTest::newRow("zonly") - << 0.0f << 0.0f << 270.0f; + << 0.0f << 0.0f << 270.0f << QQuaternion(-0.707107f, 0.0f, 0.0f, 0.707107f); QTest::newRow("x+z") - << 30.0f << 0.0f << 45.0f; + << 30.0f << 0.0f << 45.0f << QQuaternion(0.892399f, 0.239118f, -0.099046f, 0.369644f); QTest::newRow("x+y") - << 30.0f << 90.0f << 0.0f; + << 30.0f << 90.0f << 0.0f << QQuaternion(0.683013f, 0.183013f, 0.683013f, -0.183013f); QTest::newRow("y+z") - << 0.0f << 45.0f << 30.0f; + << 0.0f << 45.0f << 30.0f << QQuaternion(0.892399f, 0.099046f, 0.369644f, 0.239118f); QTest::newRow("complex") - << 30.0f << 240.0f << -45.0f; + << 30.0f << 240.0f << -45.0f << QQuaternion(-0.531976f, -0.43968f, 0.723317f, -0.02226f); + + // Three gimbal_lock cases are not unique for the conversions from quaternion + // to euler, Qt will use only XY rotations for these cases. + // For example, QQuaternion(0.5f, 0.5f, -0.5f, 0.5f) can be EulerXYZ(90.0f, 0.0f, 90.0f), too. + // But Qt will always convert it to EulerXYZ(90.0f, -90.0f, 0.0f) without Z-rotation. + QTest::newRow("gimbal_lock_1") + << 90.0f << -90.0f << 0.0f << QQuaternion(0.5f, 0.5f, -0.5f, 0.5f); + + QTest::newRow("gimbal_lock_2") + << 90.0f << 40.0f << 0.0f << QQuaternion(0.664463f, 0.664463f, 0.241845f, -0.241845f); + + QTest::newRow("gimbal_lock_3") << 90.0f << 170.0f << 0.0f + << QQuaternion(0.0616285f, 0.0616285f, 0.704416f, -0.704416f); + + // These four examples have a fraction of errors that would bypass normalize() threshold + // and could make Gimbal lock detection fail. + QTest::newRow("gimbal_lock_fraction_1") + << -90.0f << 90.001152f << 0.0f << QQuaternion(0.499989986f, -0.5f, 0.5f, 0.5f); + + QTest::newRow("gimbal_lock_fraction_2") + << -90.0f << -179.999985f << 0.0f + << QQuaternion(1.00000001e-07f, 1.00000001e-10f, -0.707106769f, -0.707105756f); + + QTest::newRow("gimbal_lock_fraction_3") + << -90.0f << 90.0011597f << 0.0f << QQuaternion(0.499989986f, -0.49999994f, 0.5f, 0.5f); + + QTest::newRow("gimbal_lock_fraction_4") + << -90.0f << -180.0f << 0.0f + << QQuaternion(9.99999996e-12f, 9.99999996e-12f, -0.707106769f, -0.707096756f); } void tst_QQuaternion::fromEulerAngles() { QFETCH(float, pitch); QFETCH(float, yaw); QFETCH(float, roll); + QFETCH(QQuaternion, quaternion); // Use a straight-forward implementation of the algorithm at: // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60 @@ -1132,11 +1164,22 @@ void tst_QQuaternion::fromEulerAngles() QVERIFY(myFuzzyCompare(answer.z(), result.z())); QVERIFY(myFuzzyCompare(answer.scalar(), result.scalar())); + // quaternion should be the same as the result + QVERIFY(myFuzzyCompare(answer.x(), quaternion.x())); + QVERIFY(myFuzzyCompare(answer.y(), quaternion.y())); + QVERIFY(myFuzzyCompare(answer.z(), quaternion.z())); + QVERIFY(myFuzzyCompare(answer.scalar(), quaternion.scalar())); + { QVector3D answerEulerAngles = answer.toEulerAngles(); QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.x(), pitch)); QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.y(), yaw)); QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.z(), roll)); + + QVector3D quaternionEulerAngles = quaternion.toEulerAngles(); + QVERIFY(myFuzzyCompareDegrees(quaternionEulerAngles.x(), pitch)); + QVERIFY(myFuzzyCompareDegrees(quaternionEulerAngles.y(), yaw)); + QVERIFY(myFuzzyCompareDegrees(quaternionEulerAngles.z(), roll)); } answer = QQuaternion::fromEulerAngles(pitch, yaw, roll); @@ -1151,6 +1194,12 @@ void tst_QQuaternion::fromEulerAngles() QVERIFY(myFuzzyCompareDegrees(answerPitch, pitch)); QVERIFY(myFuzzyCompareDegrees(answerYaw, yaw)); QVERIFY(myFuzzyCompareDegrees(answerRoll, roll)); + + float quaternionPitch, quaternionYaw, quaternionRoll; + quaternion.getEulerAngles(&quaternionPitch, &quaternionYaw, &quaternionRoll); + QVERIFY(myFuzzyCompareDegrees(quaternionPitch, pitch)); + QVERIFY(myFuzzyCompareDegrees(quaternionYaw, yaw)); + QVERIFY(myFuzzyCompareDegrees(quaternionRoll, roll)); } } diff --git a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp index 99fb3d3e72..7bfd2f7495 100644 --- a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp +++ b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp @@ -59,6 +59,10 @@ private slots: void imageConversion_data(); void imageConversion(); + void imageConversion64_data(); + void imageConversion64(); + void imageConversionOverLargerGamut_data(); + void imageConversionOverLargerGamut(); void loadImage(); @@ -234,6 +238,7 @@ void tst_QColorSpace::imageConversion_data() QTest::newRow("Adobe RGB -> sRGB") << QColorSpace::AdobeRgb << QColorSpace::SRgb << 2; QTest::newRow("Display-P3 -> Adobe RGB") << QColorSpace::DisplayP3 << QColorSpace::AdobeRgb << 2; QTest::newRow("sRGB -> sRGB Linear") << QColorSpace::SRgb << QColorSpace::SRgbLinear << 0; + QTest::newRow("sRGB Linear -> sRGB") << QColorSpace::SRgbLinear << QColorSpace::SRgb << 0; } void tst_QColorSpace::imageConversion() @@ -284,6 +289,120 @@ void tst_QColorSpace::imageConversion() } } +void tst_QColorSpace::imageConversion64_data() +{ + QTest::addColumn<QColorSpace::NamedColorSpace>("fromColorSpace"); + QTest::addColumn<QColorSpace::NamedColorSpace>("toColorSpace"); + + QTest::newRow("sRGB -> Display-P3") << QColorSpace::SRgb << QColorSpace::DisplayP3; + QTest::newRow("sRGB -> Adobe RGB") << QColorSpace::SRgb << QColorSpace::AdobeRgb; + QTest::newRow("Display-P3 -> sRGB") << QColorSpace::DisplayP3 << QColorSpace::SRgb; + QTest::newRow("Display-P3 -> Adobe RGB") << QColorSpace::DisplayP3 << QColorSpace::AdobeRgb; + QTest::newRow("sRGB -> sRGB Linear") << QColorSpace::SRgb << QColorSpace::SRgbLinear; + QTest::newRow("sRGB Linear -> sRGB") << QColorSpace::SRgbLinear << QColorSpace::SRgb; +} + +void tst_QColorSpace::imageConversion64() +{ + QFETCH(QColorSpace::NamedColorSpace, fromColorSpace); + QFETCH(QColorSpace::NamedColorSpace, toColorSpace); + + QImage testImage(256, 1, QImage::Format_RGBX64); + + for (int i = 0; i < 256; ++i) + testImage.setPixel(i, 0, qRgb(i, i, i)); + + testImage.setColorSpace(fromColorSpace); + QCOMPARE(testImage.colorSpace(), QColorSpace(fromColorSpace)); + + testImage.convertToColorSpace(toColorSpace); + QCOMPARE(testImage.colorSpace(), QColorSpace(toColorSpace)); + + int lastRed = 0; + int lastGreen = 0; + int lastBlue = 0; + for (int i = 0; i < 256; ++i) { + QRgb p = testImage.pixel(i, 0); + QVERIFY(qRed(p) >= lastRed); + QVERIFY(qGreen(p) >= lastGreen); + QVERIFY(qBlue(p) >= lastBlue); + lastRed = qRed(p); + lastGreen = qGreen(p); + lastBlue = qBlue(p); + } + + lastRed = 0; + lastGreen = 0; + lastBlue = 0; + testImage.convertToColorSpace(fromColorSpace); + QCOMPARE(testImage.colorSpace(), QColorSpace(fromColorSpace)); + for (int i = 0; i < 256; ++i) { + QRgb p = testImage.pixel(i, 0); + QCOMPARE(qRed(p), qGreen(p)); + QCOMPARE(qRed(p), qBlue(p)); + QVERIFY((lastRed - qRed(p)) <= 0); + QVERIFY((lastGreen - qGreen(p)) <= 0); + QVERIFY((lastBlue - qBlue(p)) <= 0); + lastRed = qRed(p); + lastGreen = qGreen(p); + lastBlue = qBlue(p); + } +} + +void tst_QColorSpace::imageConversionOverLargerGamut_data() +{ + QTest::addColumn<QColorSpace::NamedColorSpace>("fromColorSpace"); + QTest::addColumn<QColorSpace::NamedColorSpace>("toColorSpace"); + + QTest::newRow("sRGB -> Display-P3") << QColorSpace::SRgb << QColorSpace::DisplayP3; + QTest::newRow("sRGB -> Adobe RGB") << QColorSpace::SRgb << QColorSpace::AdobeRgb; + QTest::newRow("sRGB -> ProPhoto RGB") << QColorSpace::SRgb << QColorSpace::ProPhotoRgb; + QTest::newRow("Display-P3 -> ProPhoto RGB") << QColorSpace::DisplayP3 << QColorSpace::ProPhotoRgb; + QTest::newRow("Adobe RGB -> ProPhoto RGB") << QColorSpace::AdobeRgb << QColorSpace::ProPhotoRgb; +} + +void tst_QColorSpace::imageConversionOverLargerGamut() +{ + QFETCH(QColorSpace::NamedColorSpace, fromColorSpace); + QFETCH(QColorSpace::NamedColorSpace, toColorSpace); + + QColorSpace csfrom(fromColorSpace); + QColorSpace csto(toColorSpace); + csfrom.setTransferFunction(QColorSpace::TransferFunction::Linear); + csto.setTransferFunction(QColorSpace::TransferFunction::Linear); + + QImage testImage(256, 256, QImage::Format_RGBX64); + testImage.setColorSpace(csfrom); + for (int y = 0; y < 256; ++y) + for (int x = 0; x < 256; ++x) + testImage.setPixel(x, y, qRgb(x, y, 0)); + + QImage resultImage = testImage.convertedToColorSpace(csto); + for (int y = 0; y < 256; ++y) { + int lastRed = 0; + for (int x = 0; x < 256; ++x) { + QRgb p = resultImage.pixel(x, y); + QVERIFY(qRed(p) >= lastRed); + lastRed = qRed(p); + } + } + for (int x = 0; x < 256; ++x) { + int lastGreen = 0; + for (int y = 0; y < 256; ++y) { + QRgb p = resultImage.pixel(x, y); + QVERIFY(qGreen(p) >= lastGreen); + lastGreen = qGreen(p); + } + } + + resultImage.convertToColorSpace(csfrom); + // The images are not exactly identical at 4x16bit, but they preserve 4x8bit accuracy. + for (int y = 0; y < 256; ++y) { + for (int x = 0; x < 256; ++x) { + QCOMPARE(resultImage.pixel(x, y), testImage.pixel(x, y)); + } + } +} void tst_QColorSpace::loadImage() { diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index 42e98ce363..d7c3f95f1d 100644 --- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -308,6 +308,7 @@ private slots: void fillPolygon(); void drawImageAtPointF(); + void scaledDashes(); private: void fillData(); @@ -5468,6 +5469,36 @@ void tst_QPainter::drawImageAtPointF() paint.end(); } +void tst_QPainter::scaledDashes() +{ + // Test that we do not hit the limit-huge-number-of-dashes path + QRgb fore = qRgb(0, 0, 0xff); + QRgb back = qRgb(0xff, 0xff, 0); + QImage image(5, 32, QImage::Format_RGB32); + image.fill(back); + QPainter p(&image); + QPen pen(QColor(fore), 3, Qt::DotLine); + p.setPen(pen); + p.scale(1, 2); + p.drawLine(2, 0, 2, 16); + p.end(); + + bool foreFound = false; + bool backFound = false; + int i = 0; + while (i < 32 && (!foreFound || !backFound)) { + QRgb pix = image.pixel(3, i); + if (pix == fore) + foreFound = true; + else if (pix == back) + backFound = true; + i++; + } + + QVERIFY(foreFound); + QVERIFY(backFound); +} + QTEST_MAIN(tst_QPainter) #include "tst_qpainter.moc" diff --git a/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp b/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp index 86a8965cec..b34a63cc6c 100644 --- a/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp +++ b/tests/auto/gui/painting/qpainterpath/tst_qpainterpath.cpp @@ -173,8 +173,18 @@ void tst_QPainterPath::clear() p1.setFillRule(Qt::WindingFill); QVERIFY(p1 != p3); p1.clear(); - QCOMPARE(p1.fillRule(), Qt::OddEvenFill); + QVERIFY(p1 != p3); + p1.setFillRule(Qt::OddEvenFill); QCOMPARE(p1, p2); + + QPainterPath p4; + QCOMPARE(p4.fillRule(), Qt::OddEvenFill); + p4.setFillRule(Qt::WindingFill); + QCOMPARE(p4.fillRule(), Qt::WindingFill); + p4.clear(); + QCOMPARE(p4.fillRule(), Qt::WindingFill); + p4 = QPainterPath(); + QCOMPARE(p4.fillRule(), Qt::OddEvenFill); } void tst_QPainterPath::reserveAndCapacity() diff --git a/tests/auto/gui/painting/qtransform/tst_qtransform.cpp b/tests/auto/gui/painting/qtransform/tst_qtransform.cpp index f31e2bf41b..48e4f4c9c0 100644 --- a/tests/auto/gui/painting/qtransform/tst_qtransform.cpp +++ b/tests/auto/gui/painting/qtransform/tst_qtransform.cpp @@ -447,8 +447,9 @@ void tst_QTransform::types() QCOMPARE(m1.inverted().type(), QTransform::TxScale); m1.rotate(45.0f); - QCOMPARE(m1.type(), QTransform::TxRotate); - QCOMPARE(m1.inverted().type(), QTransform::TxRotate); + // Rotation after non-uniform scaling -> shearing. Uniform scale + rotate tested below. + QCOMPARE(m1.type(), QTransform::TxShear); + QCOMPARE(m1.inverted().type(), QTransform::TxShear); m1.shear(0.5f, 0.25f); QCOMPARE(m1.type(), QTransform::TxShear); diff --git a/tests/auto/gui/qvulkan/tst_qvulkan.cpp b/tests/auto/gui/qvulkan/tst_qvulkan.cpp index c80c3fed97..3315ae5225 100644 --- a/tests/auto/gui/qvulkan/tst_qvulkan.cpp +++ b/tests/auto/gui/qvulkan/tst_qvulkan.cpp @@ -153,8 +153,15 @@ void tst_QVulkan::vulkanVersionRequest() inst.destroy(); inst.setApiVersion(QVersionNumber(10, 0, 0)); - QVERIFY(!inst.create()); - QCOMPARE(inst.errorCode(), VK_ERROR_INCOMPATIBLE_DRIVER); + + bool result = inst.create(); + + // Starting with Vulkan 1.1 the spec does not allow the implementation to + // fail the instance creation. So check for the 1.0 behavior only when + // create() failed, skip this verification with 1.1+ (where create() will + // succeed for any bogus api version). + if (!result) + QCOMPARE(inst.errorCode(), VK_ERROR_INCOMPATIBLE_DRIVER); } static void waitForUnexposed(QWindow *w) diff --git a/tests/auto/gui/text/qfont/BLACKLIST b/tests/auto/gui/text/qfont/BLACKLIST new file mode 100644 index 0000000000..abe3ffb2c4 --- /dev/null +++ b/tests/auto/gui/text/qfont/BLACKLIST @@ -0,0 +1,8 @@ +[defaultFamily:cursive] +rhel-8.4 +ubuntu-20.04 +ubuntu-22.04 +[defaultFamily:fantasy] +rhel-8.4 +ubuntu-20.04 +ubuntu-22.04 diff --git a/tests/auto/gui/text/qfont/tst_qfont.cpp b/tests/auto/gui/text/qfont/tst_qfont.cpp index 2d72eef459..9b70308092 100644 --- a/tests/auto/gui/text/qfont/tst_qfont.cpp +++ b/tests/auto/gui/text/qfont/tst_qfont.cpp @@ -134,6 +134,21 @@ void tst_QFont::exactMatch() QVERIFY(!QFont("serif").exactMatch()); QVERIFY(!QFont("monospace").exactMatch()); + // Confirm that exactMatch is true for a valid font + QFontDatabase db; + const QString family = db.families().first(); + const QString style = db.styles(family).first(); + const int pointSize = db.pointSizes(family, style).first(); + font = db.font(family, style, pointSize); + QVERIFY(font.exactMatch()); + + if (db.families().contains("Arial")) { + font = QFont("Arial"); + QVERIFY(font.exactMatch()); + font = QFont(QString()); + font.setFamilies({"Arial"}); + QVERIFY(font.exactMatch()); + } } void tst_QFont::italicOblique() diff --git a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp index 12e8083622..15e0ecadaa 100644 --- a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp +++ b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp @@ -33,6 +33,8 @@ #include <qfontmetrics.h> #include <qtextlayout.h> #include <private/qrawfont_p.h> +#include <private/qfont_p.h> +#include <private/qfontengine_p.h> #include <qpa/qplatformfontdatabase.h> Q_LOGGING_CATEGORY(lcTests, "qt.text.tests") @@ -277,7 +279,7 @@ void tst_QFontDatabase::addAppFont() QVERIFY(QFontDatabase::removeApplicationFont(id)); QCOMPARE(fontDbChangedSpy.count(), 2); - QCOMPARE(db.families(), oldFamilies); + QVERIFY(db.families().count() <= oldFamilies.count()); } void tst_QFontDatabase::addTwoAppFontsFromFamily() @@ -309,7 +311,17 @@ void tst_QFontDatabase::aliases() QFontDatabase db; const QStringList families = db.families(); QVERIFY(!families.isEmpty()); - const QString firstFont = families.front(); + QString firstFont; + for (int i = 0; i < families.size(); ++i) { + if (!families.at(i).contains('[')) { + firstFont = families.at(i); + break; + } + } + + if (firstFont.isEmpty()) + QSKIP("Skipped because there are no unambiguous font families on the system."); + QVERIFY(db.hasFamily(firstFont)); const QString alias = QStringLiteral("AliasToFirstFont") + firstFont; QVERIFY(!db.hasFamily(alias)); @@ -401,7 +413,11 @@ void tst_QFontDatabase::condensedFontMatching() tfcByStyleName.setStyleName("Condensed"); #ifdef Q_OS_WIN - QEXPECT_FAIL("","No matching of sub-family by stretch on Windows", Continue); + QFont f; + f.setStyleStrategy(QFont::NoFontMerging); + QFontPrivate *font_d = QFontPrivate::get(f); + if (font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::Freetype) + QEXPECT_FAIL("","No matching of sub-family by stretch on Windows", Continue); #endif #ifdef Q_OS_ANDROID diff --git a/tests/auto/gui/text/qglyphrun/BLACKLIST b/tests/auto/gui/text/qglyphrun/BLACKLIST index 57f32c683d..c9d0900296 100644 --- a/tests/auto/gui/text/qglyphrun/BLACKLIST +++ b/tests/auto/gui/text/qglyphrun/BLACKLIST @@ -1,3 +1,7 @@ [mixedScripts] ubuntu-18.04 +ubuntu-20.04 +ubuntu-22.04 b2qt +[drawRightToLeft] +opensuse-leap-15.4 # QTBUG-89169 diff --git a/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp b/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp index 1429e4cb7f..98e07919ef 100644 --- a/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp +++ b/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp @@ -66,6 +66,7 @@ private slots: void boundingRect(); void mixedScripts(); void multiLineBoundingRect(); + void defaultIgnorables(); private: int m_testFontId; @@ -761,6 +762,21 @@ void tst_QGlyphRun::multiLineBoundingRect() QVERIFY(firstLineGlyphRun.boundingRect().height() < allGlyphRun.boundingRect().height()); } +void tst_QGlyphRun::defaultIgnorables() +{ + QTextLayout layout; + layout.setFont(QFont("QtsSpecialTestFont")); + layout.setText(QChar(0x200D)); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QList<QGlyphRun> runs = layout.glyphRuns(); + QCOMPARE(runs.size(), 1); + QCOMPARE(runs.at(0).glyphIndexes().size(), 1); + QCOMPARE(runs.at(0).glyphIndexes()[0], 0); +} + #endif // QT_NO_RAWFONT QTEST_MAIN(tst_QGlyphRun) diff --git a/tests/auto/gui/text/qinputcontrol/tst_qinputcontrol.cpp b/tests/auto/gui/text/qinputcontrol/tst_qinputcontrol.cpp index 173e137d17..de39f30135 100644 --- a/tests/auto/gui/text/qinputcontrol/tst_qinputcontrol.cpp +++ b/tests/auto/gui/text/qinputcontrol/tst_qinputcontrol.cpp @@ -58,6 +58,52 @@ void tst_QInputControl::isAcceptableInput_data() QTest::newRow("printable-latin-with-ctrl-shift") << QString(QLatin1Char('a')) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << false; QTest::newRow("printable-hebrew") << QString(QChar(0x2135)) << Qt::KeyboardModifiers() << true; QTest::newRow("private-use-area") << QString(QChar(0xE832)) << Qt::KeyboardModifiers() << true; + QTest::newRow("good-surrogate-0") << QString::fromUtf16(u"\U0001F44D") << Qt::KeyboardModifiers() << true; + { + const QChar data[] = { QChar(0xD800), QChar(0xDC00) }; + const QString str = QString(data, 2); + QTest::newRow("good-surrogate-1") << str << Qt::KeyboardModifiers() << true; + } + { + const QChar data[] = { QChar(0xD800), QChar(0xDFFF) }; + const QString str = QString(data, 2); + QTest::newRow("good-surrogate-2") << str << Qt::KeyboardModifiers() << true; + } + { + const QChar data[] = { QChar(0xDBFF), QChar(0xDC00) }; + const QString str = QString(data, 2); + QTest::newRow("good-surrogate-3") << str << Qt::KeyboardModifiers() << true; + } + { + const QChar data[] = { QChar(0xDBFF), QChar(0xDFFF) }; + const QString str = QString(data, 2); + QTest::newRow("good-surrogate-4") << str << Qt::KeyboardModifiers() << true; + } + { + const QChar data[] = { QChar(0xD7FF), QChar(0xDC00) }; + const QString str = QString(data, 2); + QTest::newRow("bad-surrogate-1") << str << Qt::KeyboardModifiers() << false; + } + { + const QChar data[] = { QChar(0xD7FF), QChar(0xDFFF) }; + const QString str = QString(data, 2); + QTest::newRow("bad-surrogate-2") << str << Qt::KeyboardModifiers() << false; + } + { + const QChar data[] = { QChar(0xDC00), QChar(0xDC00) }; + const QString str = QString(data, 2); + QTest::newRow("bad-surrogate-3") << str << Qt::KeyboardModifiers() << false; + } + { + const QChar data[] = { QChar(0xD800), QChar(0xE000) }; + const QString str = QString(data, 2); + QTest::newRow("bad-surrogate-4") << str << Qt::KeyboardModifiers() << false; + } + { + const QChar data[] = { QChar(0xD800) }; + const QString str = QString(data, 1); + QTest::newRow("bad-surrogate-5") << str << Qt::KeyboardModifiers() << false; + } QTest::newRow("multiple-printable") << QStringLiteral("foobar") << Qt::KeyboardModifiers() << true; QTest::newRow("rlm") << QString(QChar(0x200F)) << Qt::KeyboardModifiers() << true; QTest::newRow("rlm-with-ctrl") << QString(QChar(0x200F)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true; diff --git a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp index a32fb1d25b..704215a24a 100644 --- a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp +++ b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp @@ -654,6 +654,7 @@ void tst_QRawFont::fromFont_data() QTest::addColumn<QFont::HintingPreference>("hintingPreference"); QTest::addColumn<QString>("familyName"); QTest::addColumn<QFontDatabase::WritingSystem>("writingSystem"); + QTest::addColumn<QFont::StyleStrategy>("styleStrategy"); for (int i=QFont::PreferDefaultHinting; i<=QFont::PreferFullHinting; ++i) { QString titleBase = QString::fromLatin1("%2, hintingPreference=%1, writingSystem=%3") @@ -667,7 +668,8 @@ void tst_QRawFont::fromFont_data() << fileName << QFont::HintingPreference(i) << "QtBidiTestFont" - << writingSystem; + << writingSystem + << QFont::PreferDefault; } { @@ -679,7 +681,8 @@ void tst_QRawFont::fromFont_data() << fileName << QFont::HintingPreference(i) << "QtBidiTestFont" - << writingSystem; + << writingSystem + << QFont::PreferDefault; } { @@ -691,9 +694,24 @@ void tst_QRawFont::fromFont_data() << fileName << QFont::HintingPreference(i) << "QtBidiTestFont" - << writingSystem; + << writingSystem + << QFont::PreferDefault; } } + + { + QString fileName = testFont; + QFontDatabase::WritingSystem writingSystem = QFontDatabase::Arabic; + + QString title = QStringLiteral("No font merging + unsupported script"); + QTest::newRow(qPrintable(title)) + << fileName + << QFont::PreferDefaultHinting + << "QtBidiTestFont" + << writingSystem + << QFont::NoFontMerging; + } + } void tst_QRawFont::fromFont() @@ -702,6 +720,7 @@ void tst_QRawFont::fromFont() QFETCH(QFont::HintingPreference, hintingPreference); QFETCH(QString, familyName); QFETCH(QFontDatabase::WritingSystem, writingSystem); + QFETCH(QFont::StyleStrategy, styleStrategy); QFontDatabase fontDatabase; int id = fontDatabase.addApplicationFont(fileName); @@ -710,6 +729,8 @@ void tst_QRawFont::fromFont() QFont font(familyName); font.setHintingPreference(hintingPreference); font.setPixelSize(26.0); + if (styleStrategy != QFont::PreferDefault) + font.setStyleStrategy(styleStrategy); QRawFont rawFont = QRawFont::fromFont(font, writingSystem); QVERIFY(rawFont.isValid()); diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index fdc7997d35..de12862383 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -197,6 +197,12 @@ private slots: void clearUndoRedoStacks(); void mergeFontFamilies(); + void contentsChangeIndices_data(); + void contentsChangeIndices(); + + void insertHtmlWithComments_data(); + void insertHtmlWithComments(); + private: void backgroundImage_checkExpectedHtml(const QTextDocument &doc); void buildRegExpData(); @@ -1772,6 +1778,59 @@ void tst_QTextDocument::toHtml_data() << QString("EMPTYBLOCK") + QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>"); } + { + CREATE_DOC_AND_CURSOR(); + const QString listHtml = "<ul><li>item-1</li><li>item-2<ul><li>item-2.1</li><li>item-2.2" + "<ul><li>item-2.2.1</li></ul></li><li>item-2.3<ul><li>item-2.3.1" + "</li></ul></li></ul></li><li>item-3</li></ul>"; + cursor.insertHtml(listHtml); + + QTest::newRow("nested-lists-one") << QTextDocumentFragment(&doc) + << QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; " + "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">" + "item-1</li>\n<li DEFAULTBLOCKSTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li " + "DEFAULTBLOCKSTYLE>item-2.1</li>\n<li DEFAULTBLOCKSTYLE>item-2.2\n<ul " + "DEFAULTULSTYLE 3;\"><li DEFAULTBLOCKSTYLE>item-2.2.1</li></ul></li>\n" + "<li DEFAULTBLOCKSTYLE>item-2.3\n<ul DEFAULTULSTYLE 3;\"><li DEFAULTBLOCKSTYLE>" + "item-2.3.1</li></ul></li></ul></li>\n<li DEFAULTLASTLISTYLE>item-3</li></ul>"); + } + { + CREATE_DOC_AND_CURSOR(); + const QString listHtml = "<ul><li>item-1</li><li>item-2<ul><li>item-2.1</li></ul></li></ul>"; + cursor.insertHtml(listHtml); + + QTest::newRow("nested-lists-two") << QTextDocumentFragment(&doc) + << QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; " + "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">" + "item-1</li>\n<li DEFAULTLASTLISTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li " + "DEFAULTBLOCKSTYLE>item-2.1</li></ul></li></ul>"); + } + { + CREATE_DOC_AND_CURSOR(); + const QString listHtml = "<ul><li>item-1</li><li>item-2<ul><li>item-2.1</li><li>item-2.2" + "</li></ul></li></ul>"; + cursor.insertHtml(listHtml); + + QTest::newRow("nested-lists-three") << QTextDocumentFragment(&doc) + << QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; " + "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">" + "item-1</li>\n<li DEFAULTLASTLISTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li " + "DEFAULTBLOCKSTYLE>item-2.1</li>\n<li DEFAULTBLOCKSTYLE>item-2.2</li></ul>" + "</li></ul>"); + } + { + CREATE_DOC_AND_CURSOR(); + const QString listHtml = "<ul><li>item-1.1</li><li>item-1.2<li></ul>" + "<ul><li>item-2.1</li></ul>"; + cursor.insertHtml(listHtml); + + QTest::newRow("not-nested-list") << QTextDocumentFragment(&doc) + << QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; " + "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">" + "item-1.1</li>\n<li DEFAULTBLOCKSTYLE>item-1.2</li></ul>\n<ul DEFAULTULSTYLE 1;\">" + "<li style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; " + "margin-right:0px; -qt-block-indent:0; text-indent:0px;\">item-2.1</li></ul>"); + } } void tst_QTextDocument::toHtml() @@ -1786,6 +1845,11 @@ void tst_QTextDocument::toHtml() expectedOutput.replace("OPENDEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"); expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""); expectedOutput.replace("EMPTYBLOCK", "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"); + expectedOutput.replace("DEFAULTULSTYLE", "style=\"margin-top: 0px; margin-bottom: 0px; " + "margin-left: 0px; margin-right: 0px; -qt-list-indent:"); + expectedOutput.replace("DEFAULTLASTLISTYLE", "style=\" margin-top:0px; margin-bottom:12px; " + "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""); + if (expectedOutput.endsWith(QLatin1Char('\n'))) expectedOutput.chop(1); expectedOutput.append(htmlTail); @@ -3658,5 +3722,100 @@ void tst_QTextDocument::clearUndoRedoStacks() } +void tst_QTextDocument::contentsChangeIndices_data() +{ + QTest::addColumn<QString>("html"); + // adding list entries change the entire block, so change position is + // not the same as the cursor position if this value is >= 0 + QTest::addColumn<int>("expectedBegin"); + + QTest::addRow("text") << "Test" << -1; + QTest::addRow("unnumbered list") << "<ul><li>Test</li></ul>" << 0; + QTest::addRow("numbered list") << "<ol><li>Test</li></ol>" << 0; + QTest::addRow("table") << "<table><tr><td>Test</td></tr></table>" << -1; +} + +void tst_QTextDocument::contentsChangeIndices() +{ + QFETCH(QString, html); + QFETCH(int, expectedBegin); + + QTextDocument doc; + QTestDocumentLayout *layout = new QTestDocumentLayout(&doc); + doc.setDocumentLayout(layout); + doc.setHtml(QString("<html><body>%1</body></html>").arg(html)); + + int documentLength = 0; + int cursorLength = 0; + int changeBegin = 0; + int changeRemoved = 0; + int changeAdded = 0; + connect(&doc, &QTextDocument::contentsChange, this, [&](int pos, int removed, int added){ + documentLength = doc.characterCount(); + + QTextCursor cursor(&doc); + cursor.movePosition(QTextCursor::End); + // includes end-of-paragraph character + cursorLength = cursor.position() + 1; + + changeBegin = pos; + changeRemoved = removed; + changeAdded = added; + }); + + QTextCursor cursor(&doc); + cursor.movePosition(QTextCursor::End); + if (expectedBegin < 0) + expectedBegin = cursor.position(); + cursor.insertBlock(); + + const int changeEnd = changeBegin + changeAdded; + + QVERIFY(documentLength > 0); + QCOMPARE(documentLength, cursorLength); + QVERIFY(documentLength >= changeEnd); + QCOMPARE(changeBegin, expectedBegin); + QCOMPARE(changeAdded - changeRemoved, 1); +} + +void tst_QTextDocument::insertHtmlWithComments_data() +{ + QTest::addColumn<QString>("html"); + QTest::addColumn<QStringList>("expectedBlocks"); + + QTest::newRow("commentless") << "<p>first</p><p>second</p><p>third</p>" + << QStringList { "first", "second", "third" }; + QTest::newRow("normal") << "<p>first</p><!--<p>second</p>--><p>third</p>" + << QStringList { "first", "third" }; + QTest::newRow("nonClosing") << "<p>first</p><!--<p>second</p><p>third</p>" + << QStringList { "first" }; + QTest::newRow("immediatelyClosing") << "<p>first</p><!----><p>second</p><p>third</p>" + << QStringList { "first", "second", "third" }; + QTest::newRow("fake") << "<p>first</p><!-<p>second</p><p>third</p>" + << QStringList { "first", "second", "third" }; + QTest::newRow("endingNonExistant") << "<p>first</p>--><p>second</p><p>third</p>" + << QStringList { "first", "-->", "second", "third" }; +} + +void tst_QTextDocument::insertHtmlWithComments() +{ + QFETCH(QString, html); + QFETCH(QStringList, expectedBlocks); + + QTextDocument doc; + doc.setHtml(html); + + QCOMPARE(doc.blockCount(), expectedBlocks.count()); + + QStringList blockContent; + auto currentBlock = doc.begin(); + while (currentBlock != doc.end()) { + blockContent.append(currentBlock.text()); + currentBlock = currentBlock.next(); + } + + QCOMPARE(blockContent, expectedBlocks); +} + QTEST_MAIN(tst_QTextDocument) #include "tst_qtextdocument.moc" diff --git a/tests/auto/gui/text/qtextimagehandler/data/image.png b/tests/auto/gui/text/qtextimagehandler/data/image.png Binary files differnew file mode 100644 index 0000000000..dd589dd49c --- /dev/null +++ b/tests/auto/gui/text/qtextimagehandler/data/image.png diff --git a/tests/auto/gui/text/qtextimagehandler/data/image@2x.png b/tests/auto/gui/text/qtextimagehandler/data/image@2x.png Binary files differnew file mode 100644 index 0000000000..6b4c0bd059 --- /dev/null +++ b/tests/auto/gui/text/qtextimagehandler/data/image@2x.png diff --git a/tests/auto/gui/text/qtextimagehandler/qtextimagehandler.pro b/tests/auto/gui/text/qtextimagehandler/qtextimagehandler.pro new file mode 100644 index 0000000000..6214171377 --- /dev/null +++ b/tests/auto/gui/text/qtextimagehandler/qtextimagehandler.pro @@ -0,0 +1,4 @@ +CONFIG += testcase +TARGET = tst_qtextimagehandler +QT += core-private gui gui-private testlib +SOURCES += tst_qtextimagehandler.cpp diff --git a/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp b/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp new file mode 100644 index 0000000000..ee79c36ba5 --- /dev/null +++ b/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** + ** + ** Copyright (C) 2020 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the test suite of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://www.qt.io/contact-us. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 3 as published by the Free Software + ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-3.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QPainter> +#include <private/qtextimagehandler_p.h> + +class tst_QTextImageHandler : public QObject +{ + Q_OBJECT + +public: + tst_QTextImageHandler(); + +private slots: + void init(); + void cleanup(); + void cleanupTestCase(); + void loadAtNImages(); +}; + +tst_QTextImageHandler::tst_QTextImageHandler() +{ +} + +void tst_QTextImageHandler::init() +{ +} + +void tst_QTextImageHandler::cleanup() +{ +} + +void tst_QTextImageHandler::cleanupTestCase() +{ +} + +void tst_QTextImageHandler::loadAtNImages() +{ + QTextDocument doc; + QTextCursor c(&doc); + c.insertHtml("<img src=\"data/image.png\">"); + QTextImageHandler handler; + QTextImageFormat fmt; + fmt.setName("data/image.png"); + + for (int i = 1; i < 3; ++i) { + QImage img(20, 20, QImage::Format_ARGB32_Premultiplied); + img.fill(Qt::white); + img.setDevicePixelRatio(i); + QPainter p(&img); + handler.drawObject(&p, QRect(0, 0, 20, 20), &doc, 0, fmt); + p.end(); + QVERIFY(!img.isNull()); + const auto expectedColor = i == 1 ? Qt::red : Qt::green; + QCOMPARE(img.pixelColor(0, 0), expectedColor); + } +} + +QTEST_MAIN(tst_QTextImageHandler) +#include "tst_qtextimagehandler.moc" diff --git a/tests/auto/gui/text/qtextlayout/BLACKLIST b/tests/auto/gui/text/qtextlayout/BLACKLIST new file mode 100644 index 0000000000..c05b0de4eb --- /dev/null +++ b/tests/auto/gui/text/qtextlayout/BLACKLIST @@ -0,0 +1,3 @@ +[softHyphens] +winrt + diff --git a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp index afcae2f4b7..f3bb5eaffb 100644 --- a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp +++ b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp @@ -141,6 +141,7 @@ private slots: void showLineAndParagraphSeparatorsCrash(); void koreanWordWrap(); void tooManyDirectionalCharctersCrash_qtbug77819(); + void softHyphens_data(); void softHyphens(); void min_maximumWidth(); @@ -1895,6 +1896,34 @@ void tst_QTextLayout::longText() QVERIFY(line.isValid()); QVERIFY(line.cursorToX(line.textLength() - 1) > 0); } + + { + QTextLayout layout(QString("Qt rocks! ").repeated(200000)); + layout.setCacheEnabled(true); + layout.beginLayout(); + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + } + layout.endLayout(); + QFontMetricsF fm(layout.font()); + QVERIFY(layout.maximumWidth() - fm.horizontalAdvance(' ') <= QFIXED_MAX); + } + + { + QTextLayout layout(QString("AAAAAAAA").repeated(200000)); + layout.setCacheEnabled(true); + layout.beginLayout(); + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + } + layout.endLayout(); + QFontMetricsF fm(layout.font()); + QVERIFY(layout.maximumWidth() - fm.horizontalAdvance('A') <= QFIXED_MAX); + } } void tst_QTextLayout::widthOfTabs() @@ -2401,22 +2430,45 @@ void tst_QTextLayout::tooManyDirectionalCharctersCrash_qtbug77819() tl.endLayout(); } +void tst_QTextLayout::softHyphens_data() +{ + QTest::addColumn<int>("fontSize"); + + QTest::newRow("12") << 12; + QTest::newRow("14") << 14; + QTest::newRow("16") << 16; +} + void tst_QTextLayout::softHyphens() { + QFETCH(int, fontSize); QString text = QStringLiteral("xxxx\u00ad") + QStringLiteral("xxxx\u00ad"); QFont font; - font.setPixelSize(14); + font.setPixelSize(fontSize); font.setHintingPreference(QFont::PreferNoHinting); - const float xAdvance = QFontMetricsF(font).horizontalAdvance(QChar('x')); - const float shyAdvance = QFontMetricsF(font).horizontalAdvance(QChar::SoftHyphen); - if (xAdvance < (shyAdvance + 1.0f)) - QSKIP("Default font not suitable for this test."); + const float xAdvance = QFontMetricsF(font).horizontalAdvance(QChar::fromLatin1('x')); + float shyWidth = 0.0f; QTextLayout layout(text, font); QTextOption option; option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); layout.setTextOption(option); - + { + // Calculate the effective width of a line-ending hyphen + // This calculation is currently done to work-around odditities on + // macOS 11 (see QTBUG-90698). + QTextLayout test(QStringLiteral("x\u00ad"), font); + // Note: This only works because Qt show the soft-hyphen when ending a text. + // This _could_ be considered a bug and the test would need to be changed + // if we stop doing that. + test.beginLayout(); + QTextLine line = test.createLine(); + line.setLineWidth(10 * xAdvance); + line.setPosition(QPoint(0, 0)); + shyWidth = line.naturalTextWidth() - xAdvance; + test.endLayout(); + } + qreal linefit; // Loose fit // xxxx- | // xxxx- | @@ -2425,21 +2477,22 @@ void tst_QTextLayout::softHyphens() int y = 0; layout.beginLayout(); QTextLine line = layout.createLine(); - line.setLineWidth(qCeil(5 * xAdvance) + 1); + line.setLineWidth(qCeil(5 * xAdvance + shyWidth) + 1); line.setPosition(QPoint(0, y)); QCOMPARE(line.textStart(), pos); QCOMPARE(line.textLength(), 5); - QVERIFY(qAbs(line.naturalTextWidth() - (4 * xAdvance + shyAdvance)) <= 1); + linefit = line.naturalTextWidth(); + QVERIFY(qAbs(linefit - qCeil(4 * xAdvance + shyWidth)) <= 1.0); pos += line.textLength(); y += qRound(line.ascent() + line.descent()); line = layout.createLine(); - line.setLineWidth(qCeil(5 * xAdvance) + 1); + line.setLineWidth(qCeil(5 * xAdvance + shyWidth) + 1); line.setPosition(QPoint(0, y)); QCOMPARE(line.textStart(), pos); QCOMPARE(line.textLength(), 5); - QVERIFY(qAbs(line.naturalTextWidth() - (4 * xAdvance + shyAdvance)) <= 1); + QVERIFY(qAbs(line.naturalTextWidth() - linefit) <= 1.0); layout.endLayout(); } @@ -2451,21 +2504,21 @@ void tst_QTextLayout::softHyphens() int y = 0; layout.beginLayout(); QTextLine line = layout.createLine(); - line.setLineWidth(qCeil(4 * xAdvance + shyAdvance) + 1); + line.setLineWidth(qCeil(linefit) + 1); line.setPosition(QPoint(0, y)); QCOMPARE(line.textStart(), pos); QCOMPARE(line.textLength(), 5); - QVERIFY(qAbs(line.naturalTextWidth() - (4 * xAdvance + shyAdvance)) <= 1); + QVERIFY(qAbs(line.naturalTextWidth() - linefit) <= 1.0); pos += line.textLength(); y += qRound(line.ascent() + line.descent()); line = layout.createLine(); - line.setLineWidth(qCeil(4 * xAdvance + shyAdvance) + 1); + line.setLineWidth(qCeil(linefit) + 1); line.setPosition(QPoint(0, y)); QCOMPARE(line.textStart(), pos); QCOMPARE(line.textLength(), 5); - QVERIFY(qAbs(line.naturalTextWidth() - (4 * xAdvance + shyAdvance)) <= 1); + QVERIFY(qAbs(line.naturalTextWidth() - linefit) <= 1.0); layout.endLayout(); } @@ -2482,7 +2535,7 @@ void tst_QTextLayout::softHyphens() line.setPosition(QPoint(0, y)); QCOMPARE(line.textStart(), pos); QCOMPARE(line.textLength(), 4); - QVERIFY(qAbs(line.naturalTextWidth() - 4 * xAdvance) <= 1); + QVERIFY(qAbs(line.naturalTextWidth() - qCeil(4 * xAdvance)) <= 1.0); pos += line.textLength(); y += qRound(line.ascent() + line.descent()); @@ -2492,7 +2545,7 @@ void tst_QTextLayout::softHyphens() line.setPosition(QPoint(0, y)); QCOMPARE(line.textStart(), pos); QCOMPARE(line.textLength(), 5); - QVERIFY(qAbs(line.naturalTextWidth() - 4 * xAdvance) <= 1); + QVERIFY(qAbs(line.naturalTextWidth() - qCeil(4 * xAdvance)) <= 1.0); pos += line.textLength(); y += qRound(line.ascent() + line.descent()); @@ -2502,7 +2555,7 @@ void tst_QTextLayout::softHyphens() line.setPosition(QPoint(0, y)); QCOMPARE(line.textStart(), pos); QCOMPARE(line.textLength(), 1); - QVERIFY(qAbs(line.naturalTextWidth() - shyAdvance) <= 1); + QVERIFY(qAbs(line.naturalTextWidth() - shyWidth) <= 1.0); layout.endLayout(); } } diff --git a/tests/auto/gui/text/qtextmarkdownimporter/tst_qtextmarkdownimporter.cpp b/tests/auto/gui/text/qtextmarkdownimporter/tst_qtextmarkdownimporter.cpp index 78cc1da36e..2737feba31 100644 --- a/tests/auto/gui/text/qtextmarkdownimporter/tst_qtextmarkdownimporter.cpp +++ b/tests/auto/gui/text/qtextmarkdownimporter/tst_qtextmarkdownimporter.cpp @@ -73,6 +73,7 @@ public: Mono = 0x08, Link = 0x10 }; + Q_ENUM(CharFormat) Q_DECLARE_FLAGS(CharFormats, CharFormat) }; @@ -207,6 +208,9 @@ void tst_QTextMarkdownImporter::lists_data() QTest::newRow("numeric lists nested in empty lists") << "- \n 1. a\n 2. b\n- c\n 1.\n + d\n" << 4 << false << "- \n 1. a\n 2. b\n- c 1. + d\n"; + QTest::newRow("styled spans in list items") + << "1. normal text\n2. **bold** text\n3. `code` in the item\n4. *italic* text\n5. _underlined_ text\n" << 5 << false + << "1. normal text\n2. **bold** text\n3. `code` in the item\n4. *italic* text\n5. *underlined* text\n"; } void tst_QTextMarkdownImporter::lists() @@ -218,11 +222,22 @@ void tst_QTextMarkdownImporter::lists() QTextDocument doc; doc.setMarkdown(input); // QTBUG-78870 : don't crash + +#ifdef DEBUG_WRITE_HTML + { + QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".html"); + out.open(QFile::WriteOnly); + out.write(doc.toHtml().toLatin1()); + out.close(); + } +#endif + QTextFrame::iterator iterator = doc.rootFrame()->begin(); QTextFrame *currentFrame = iterator.currentFrame(); int i = 0; int itemCount = 0; bool emptyItems = true; + QString firstItemFontFamily; while (!iterator.atEnd()) { // There are no child frames QCOMPARE(iterator.currentFrame(), currentFrame); @@ -235,6 +250,21 @@ void tst_QTextMarkdownImporter::lists() } qCDebug(lcTests, "%d %s%s", i, (block.textList() ? "<li>" : "<p>"), qPrintable(block.text())); + QTextCharFormat listItemFmt = block.charFormat(); + QFont listItemFont = listItemFmt.font(); + // QTextDocumentLayoutPrivate::drawListItem() uses listItemFont to render numbers in an ordered list. + // We want that to be consistent, regardless whether the list item's text begins with a styled span. + if (firstItemFontFamily.isEmpty()) + firstItemFontFamily = listItemFont.family(); + else + QCOMPARE(listItemFont.family(), firstItemFontFamily); + QCOMPARE(listItemFont.bold(), false); + QCOMPARE(listItemFont.italic(), false); + QCOMPARE(listItemFont.underline(), false); + QCOMPARE(listItemFont.fixedPitch(), false); + QCOMPARE(listItemFmt.fontItalic(), false); + QCOMPARE(listItemFmt.fontUnderline(), false); + QCOMPARE(listItemFmt.fontFixedPitch(), false); ++iterator; ++i; } @@ -324,12 +354,14 @@ void tst_QTextMarkdownImporter::nestedSpans() << "weight" << fmt.fontWeight() << "italic" << fmt.fontItalic() << "strikeout" << fmt.fontStrikeOut() << "anchor" << fmt.isAnchor() << "monospace" << QFontInfo(fmt.font()).fixedPitch() // depends on installed fonts (QTBUG-75649) - << fmt.fontFixedPitch() // returns false even when font family is "monospace" - << fmt.hasProperty(QTextFormat::FontFixedPitch); // works - QCOMPARE(fmt.fontWeight() > 50, expectedFormat.testFlag(Bold)); + << fmt.fontFixedPitch() + << fmt.hasProperty(QTextFormat::FontFixedPitch) + << "expected" << expectedFormat; + QCOMPARE(fmt.fontWeight() > QFont::Normal, expectedFormat.testFlag(Bold)); QCOMPARE(fmt.fontItalic(), expectedFormat.testFlag(Italic)); QCOMPARE(fmt.fontStrikeOut(), expectedFormat.testFlag(Strikeout)); QCOMPARE(fmt.isAnchor(), expectedFormat.testFlag(Link)); + QCOMPARE(fmt.fontFixedPitch(), expectedFormat.testFlag(Mono)); QCOMPARE(fmt.hasProperty(QTextFormat::FontFixedPitch), expectedFormat.testFlag(Mono)); ++iterator; } diff --git a/tests/auto/gui/text/qtextmarkdownwriter/tst_qtextmarkdownwriter.cpp b/tests/auto/gui/text/qtextmarkdownwriter/tst_qtextmarkdownwriter.cpp index 13449299cb..b60b07ea30 100644 --- a/tests/auto/gui/text/qtextmarkdownwriter/tst_qtextmarkdownwriter.cpp +++ b/tests/auto/gui/text/qtextmarkdownwriter/tst_qtextmarkdownwriter.cpp @@ -51,6 +51,7 @@ private slots: void testWriteParagraph(); void testWriteList(); void testWriteEmptyList(); + void testWriteCheckboxListItemEndingWithCode(); void testWriteNestedBulletLists_data(); void testWriteNestedBulletLists(); void testWriteNestedNumericLists(); @@ -133,6 +134,34 @@ void tst_QTextMarkdownWriter::testWriteEmptyList() QCOMPARE(documentToUnixMarkdown(), QString::fromLatin1("- \n")); } +void tst_QTextMarkdownWriter::testWriteCheckboxListItemEndingWithCode() +{ + QTextCursor cursor(document); + QTextList *list = cursor.createList(QTextListFormat::ListDisc); + cursor.insertText("Image.originalSize property (not necessary; PdfDocument.pagePointSize() substitutes)"); + list->add(cursor.block()); + { + auto fmt = cursor.block().blockFormat(); + fmt.setMarker(QTextBlockFormat::MarkerType::Unchecked); + cursor.setBlockFormat(fmt); + } + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor, 4); + QCOMPARE(cursor.selectedText(), QString::fromLatin1("PdfDocument.pagePointSize()")); + auto fmt = cursor.charFormat(); + fmt.setFontFixedPitch(true); + cursor.setCharFormat(fmt); + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 5); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor, 4); + QCOMPARE(cursor.selectedText(), QString::fromLatin1("Image.originalSize")); + cursor.setCharFormat(fmt); + + QCOMPARE(documentToUnixMarkdown(), QString::fromLatin1( + "- [ ] `Image.originalSize` property (not necessary; `PdfDocument.pagePointSize()`\n substitutes)\n")); +} + void tst_QTextMarkdownWriter::testWriteNestedBulletLists_data() { QTest::addColumn<bool>("checkbox"); diff --git a/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp b/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp index 474079037b..7d4921203c 100644 --- a/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp +++ b/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp @@ -29,7 +29,7 @@ #include <QtTest/QtTest> - +#include <qbuffer.h> #include <qtextdocument.h> #include <qtextdocumentfragment.h> #include <qtexttable.h> @@ -44,6 +44,7 @@ #include <QPainter> #include <QPaintEngine> #endif +#include <private/qtextdocumentlayout_p.h> #include <private/qpagedpaintdevice_p.h> typedef QList<int> IntList; @@ -100,6 +101,13 @@ private slots: void checkBorderAttributes_data(); void checkBorderAttributes(); +#ifndef QT_NO_WIDGETS + void columnWidthWithSpans(); + + void columnWidthWithImage_data(); + void columnWidthWithImage(); +#endif + private: QTextTable *create2x2Table(); QTextTable *create4x4Table(); @@ -1278,5 +1286,84 @@ void tst_QTextTable::checkBorderAttributes() } } +#ifndef QT_NO_WIDGETS +void tst_QTextTable::columnWidthWithSpans() +{ + cleanup(); + init(); + QTextTable *table = cursor.insertTable(4, 4); + QTextEdit textEdit; + textEdit.setDocument(doc); + textEdit.show(); + QVERIFY(QTest::qWaitForWindowExposed(&textEdit)); + + for (int i = 0; i < table->columns(); ++i) + table->cellAt(0, i).firstCursorPosition().insertText(QString("Header %1").arg(i)); + + QTextBlock block = table->cellAt(0, 0).firstCursorPosition().block(); + const QRectF beforeRect = table->document()->documentLayout()->blockBoundingRect(block); + table->mergeCells(1, 0, 1, table->columns()); + block = table->cellAt(0, 0).firstCursorPosition().block(); + const QRectF afterRect = table->document()->documentLayout()->blockBoundingRect(block); + QCOMPARE(afterRect, beforeRect); +} + +void tst_QTextTable::columnWidthWithImage_data() +{ + const auto imageHtml = [](int width, int height) { + QImage image(width, height, QImage::Format_RGB32); + image.fill(Qt::red); + QByteArray imageBytes; + QBuffer buffer(&imageBytes); + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "png"); + return QString("<td><img src='data:image/png;base64,%1'/></td>") + .arg(QString::fromLatin1(imageBytes.toBase64())); + }; + + QTest::addColumn<QString>("leftHtml"); + QTest::addColumn<QString>("rightHtml"); + QTest::addColumn<QSize>("imageSize"); + QTest::addRow("image") + << imageHtml(500, 32) << "<td></td>" << QSize(500, 32); + QTest::addRow("image, text") + << imageHtml(32, 32) << "<td>abc</td>" << QSize(32, 32); + QTest::addRow("image, 100%% text") + << imageHtml(32, 32) << "<td style='background-color: grey' width='100%'>abc</td>" + << QSize(32, 32); + QTest::addRow("image, image") + << imageHtml(256, 32) << imageHtml(256, 32) << QSize(256, 32); +} + +void tst_QTextTable::columnWidthWithImage() +{ + const QString tableTemplate = "<table><tr>%1 %2</tr></table>"; + + QFETCH(QString, leftHtml); + QFETCH(QString, rightHtml); + QFETCH(QSize, imageSize); + + QTextDocument doc; + doc.setHtml(tableTemplate.arg(leftHtml).arg(rightHtml)); + QTextEdit textEdit; + textEdit.setDocument(&doc); + textEdit.show(); + QVERIFY(QTest::qWaitForWindowExposed(&textEdit)); + + QTextCursor cursor(doc.firstBlock()); + cursor.movePosition(QTextCursor::Right); + + QTextTable *currentTable = cursor.currentTable(); + QVERIFY(currentTable); + + QTextBlock block = currentTable->cellAt(0, 0).firstCursorPosition().block(); + const QRectF leftRect = currentTable->document()->documentLayout()->blockBoundingRect(block); + block = currentTable->cellAt(0, 1).firstCursorPosition().block(); + const QRectF rightRect = currentTable->document()->documentLayout()->blockBoundingRect(block); + QCOMPARE(leftRect.size().toSize(), imageSize); + QVERIFY(rightRect.left() > leftRect.right()); +} +#endif + QTEST_MAIN(tst_QTextTable) #include "tst_qtexttable.moc" diff --git a/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp b/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp index 4d95345de9..a53524e956 100644 --- a/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp +++ b/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp @@ -69,6 +69,10 @@ public slots: } }; +#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) +# define CAN_IMPLICITLY_UNSET +#endif + void tst_qdesktopservices::handlers() { MyUrlHandler fooHandler; @@ -76,6 +80,12 @@ void tst_qdesktopservices::handlers() QDesktopServices::setUrlHandler(QString("foo"), &fooHandler, "handle"); QDesktopServices::setUrlHandler(QString("bar"), &barHandler, "handle"); +#ifndef CAN_IMPLICITLY_UNSET + const auto unsetHandlers = qScopeGuard([] { + QDesktopServices::unsetUrlHandler(u"bar"_qs); + QDesktopServices::unsetUrlHandler(u"foo"_qs); + }); +#endif QUrl fooUrl("foo://blub/meh"); QUrl barUrl("bar://hmm/hmmmm"); @@ -85,6 +95,15 @@ void tst_qdesktopservices::handlers() QCOMPARE(fooHandler.lastHandledUrl.toString(), fooUrl.toString()); QCOMPARE(barHandler.lastHandledUrl.toString(), barUrl.toString()); + +#ifdef CAN_IMPLICITLY_UNSET + for (int i = 0; i < 2; ++i) + QTest::ignoreMessage(QtWarningMsg, + "Please call QDesktopServices::unsetUrlHandler() before destroying a " + "registered URL handler object.\n" + "Support for destroying a registered URL handler object is deprecated, " + "and will be removed in Qt 6.6."); +#endif } #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) |