diff options
Diffstat (limited to 'tests/auto/pdfquick')
-rw-r--r-- | tests/auto/pdfquick/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/pdfquick/multipageview/BLACKLIST | 3 | ||||
-rw-r--r-- | tests/auto/pdfquick/multipageview/CMakeLists.txt | 30 | ||||
-rw-r--r-- | tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf | 317 | ||||
-rw-r--r-- | tests/auto/pdfquick/multipageview/data/multiPageView.qml | 8 | ||||
-rw-r--r-- | tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml | 18 | ||||
-rw-r--r-- | tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf | bin | 0 -> 9138 bytes | |||
-rw-r--r-- | tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf | bin | 0 -> 33645 bytes | |||
-rw-r--r-- | tests/auto/pdfquick/multipageview/tst_multipageview.cpp | 357 | ||||
-rw-r--r-- | tests/auto/pdfquick/shared/util.cpp | 110 | ||||
-rw-r--r-- | tests/auto/pdfquick/shared/util.h | 58 |
11 files changed, 902 insertions, 0 deletions
diff --git a/tests/auto/pdfquick/CMakeLists.txt b/tests/auto/pdfquick/CMakeLists.txt new file mode 100644 index 000000000..9f79459b2 --- /dev/null +++ b/tests/auto/pdfquick/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(multipageview) diff --git a/tests/auto/pdfquick/multipageview/BLACKLIST b/tests/auto/pdfquick/multipageview/BLACKLIST new file mode 100644 index 000000000..b608bef1c --- /dev/null +++ b/tests/auto/pdfquick/multipageview/BLACKLIST @@ -0,0 +1,3 @@ +# QTBUG-106072 +[password] +windows diff --git a/tests/auto/pdfquick/multipageview/CMakeLists.txt b/tests/auto/pdfquick/multipageview/CMakeLists.txt new file mode 100644 index 000000000..99bf8a675 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/CMakeLists.txt @@ -0,0 +1,30 @@ +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_multipageview + SOURCES + tst_multipageview.cpp + ../shared/util.cpp ../shared/util.h + PUBLIC_LIBRARIES + Qt::Gui + Qt::Quick + Qt::PdfQuickPrivate + TESTDATA ${test_data} +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_multipageview CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=\\\":/data\\\" +) + +qt_internal_extend_target(tst_multipageview CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" +) + diff --git a/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf b/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf new file mode 100644 index 000000000..aa0b99039 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf @@ -0,0 +1,317 @@ +%PDF-1.7
+
+1 0 obj
+<<
+ /Type /Catalog
+ /PageMode /FullScreen
+ /Outlines 6 0 R
+ /Pages 2 0 R
+ /Names 50 0 R
+ /PageLabels 23 0 R
+ /ViewerPreferences<</NonFullScreenPageMode (UseThumbs)>>
+>>
+endobj
+
+50 0 obj
+<<
+ /Dests <</Names [ (ToTest2) [4 0 R /XYZ 300 300 1] (ToTest3) [5 0 R /XYZ 290 10 0.5] (ToTest1) [3 0 R /XYZ 600 800 1] ]>>
+>>
+endobj
+
+23 0 obj
+<<
+ /Nums [0 <</S /D /P(test )>> 3 <</S /A >> 4<</S /R/St >> 5<</S /r/St >> ]
+ /Limits [0 5]
+>>
+endobj
+
+2 0 obj
+<<
+ /Type /Pages
+ /Kids [3 0 R 4 0 R 5 0 R]
+ /Count 3
+>>
+endobj
+
+3 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 10 600 800]
+ /Annots [24 0 R 25 0 R]
+ /Contents 16 0 R
+ /Resources <<
+ /Font <</F1 18 0 R>>
+ >>
+>>
+endobj
+
+24 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /A << /Type /Action
+ /S /GoTo
+ /D [5 0 R /FitR ¨C4 399 199 533]
+ >>
+ /Rect [10 690 150 720]
+
+>>
+endobj
+
+25 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest3)
+ /Rect [10 630 150 650]
+>>
+endobj
+
+
+16 0 obj
+<< /Length 0 >>
+ stream
+ BT
+ /F1 72 Tf
+ 200 200 TD
+ 0 0 1 RG
+ 5 Tr
+ (Test_1) Tj
+ 0 800 m
+ 600 0 l S
+ /F1 30 Tf
+ 0 1 0 RG
+ 1 Tr
+ -190 490 TD
+ (GO Test_2) Tj
+ 0 -50 TD
+ 5 w
+ 2 Tr
+ 1 0 0 RG
+ (GO Test_3) Tj
+ ET
+ endstream
+endobj
+
+
+endobj
+
+18 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F1
+ /BaseFont /Helvetica
+>>
+endobj
+
+4 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [10 0 500 700]
+ /Annots [60 0 R]
+ /Contents 19 0 R
+ /Resources <<
+ /Font <</F2 20 0 R>>
+ >>
+>>
+endobj
+
+19 0 obj
+<< /Length 0 >>
+stream
+BT
+ 1 -0.7 0 1 30 100 cm
+ /F2 50 Tf
+ 10 50 TD
+ (TEST_2) Tj
+
+ 1 0.7 0 1 -30 -100 cm
+ /F2 25 Tf
+ 1 0 1 RG
+ 7 w
+ 100 60 TD
+
+ (GO Test_1) Tj
+ 100 100 140 40 re S f
+ET
+endstream
+endobj
+
+20 0 obj
+<<
+ /Type /Font
+ /Subtype /TrueType
+ /Name /F2
+ /BaseFont /NewYork , Bold
+ /FirstChar 0
+ /LastChar 255
+ /Widths 23 0 R
+ /FontDescriptor 7 0 R
+ /Encoding /MacRomanEncoding
+>>
+endobj
+
+60 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest1)
+ /Rect [110 110 230 150]
+>>
+endobj
+
+5 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [-10 -10 400 600]
+ /Annots [61 0 R]
+ /Contents 21 0 R
+ /Resources << /Font <</F3 22 0 R>> >>
+>>
+endobj
+
+21 0 obj
+<< /Length 0 >>
+stream
+BT
+ /F3 30 Tf
+ 290 10 TD
+ (TEST_3) Tj
+ -50 90 TD
+ (GO Test_2)Tj
+ET
+endstream
+endobj
+
+22 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F3
+ /BaseFont /Courier-Bold
+>>
+endobj
+
+61 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /Rect [240 90 400 130]
+>>
+
+6 0 obj
+<<
+ /Type /Outlines
+ /First 7 0 R
+ /Last 11 0 R
+ /Count 4 0 R
+>>
+endobj
+
+7 0 obj
+<<
+ /Title (First)
+ /Parent 6 0 R
+ /Next 8 0 R
+ /C [1 0 0]
+ /Dest [ 3 0 R /XYZ 600 800 0.5 ]
+>>
+endobj
+
+8 0 obj
+<<
+ /Title (Second)
+ /Parent 6 0 R
+ /Prev 7 0 R
+ /Next 9 0 R
+ /C [0 1 0]
+ % /Dest [ 4 0 R /XYZ 500 700 null ]
+/Dest (ToTest2)
+>>
+endobj
+
+9 0 obj
+<<
+ /Title (Third)
+ /Parent 6 0 R
+ /Prev 8 0 R
+ /Next 10 0 R
+ /C [0 0 1]
+ /Dest [ 5 0 R /XYZ 400 600 0.8 ]
+>>
+endobj
+
+10 0 obj
+<<
+ /Title (Fourth)
+ /Parent 6 0 R
+ /Prev 9 0 R
+ /Next 11 0 R
+>>
+endobj
+
+11 0 obj
+<<
+ /Title (Fivth)
+ /Parent 6 0 R
+ /Prev 10 0 R
+ /First 12 0 R
+ /Last 15 0 R
+ /Count 4
+>>
+endobj
+
+12 0 obj
+<<
+ /Title (Fivth_1)
+ /Parent 11 0 R
+ /Next 13 0 R
+>>
+endobj
+
+13 0 obj
+<<
+ /Title (Fivth_2)
+ /Parent 11 0 R
+ /Prev 12 0 R
+ /Next 14 0 R
+>>
+endobj
+
+14 0 obj
+<<
+ /Title (Fivth_3)
+ /Parent 11 0 R
+ /Prev 13 0 R
+ /Next 15 0 R
+>>
+endobj
+
+15 0 obj
+<<
+ /Title (Fivth_4)
+ /Parent 11 0 R
+ /Prev 14 0 R
+>>
+endobj
+
+
+
+
+xref
+0000000000 65536 f
+
+trailer
+<<
+ /Size 0
+ /Root 1 0 R
+>>
+startxref
+0
+%%EOF
diff --git a/tests/auto/pdfquick/multipageview/data/multiPageView.qml b/tests/auto/pdfquick/multipageview/data/multiPageView.qml new file mode 100644 index 000000000..bf88180ce --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/multiPageView.qml @@ -0,0 +1,8 @@ +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + width: 480; height: 480 + property alias source: document.source + document: PdfDocument { id: document } +} diff --git a/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml b/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml new file mode 100644 index 000000000..93a556c97 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + id: view + property point hoverPos: hover.point.position + width: 640; height: 480 + document: PdfDocument { } + + // mouse hover feedback for test development + Rectangle { + width: 200 + height: hoverPosLabel.implicitHeight + 12 + color: "beige" + Text { id: hoverPosLabel; x: 6; y: 6; text: view.hoverPos.x + ", " + view.hoverPos.y } + } + HoverHandler { id: hover } +} diff --git a/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf b/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf Binary files differnew file mode 100644 index 000000000..d76fdd1a6 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf diff --git a/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf b/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf Binary files differnew file mode 100644 index 000000000..4abc76f6d --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf diff --git a/tests/auto/pdfquick/multipageview/tst_multipageview.cpp b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp new file mode 100644 index 000000000..eb70ebf17 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp @@ -0,0 +1,357 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QSignalSpy> +#include <QTest> +#include <QtCore/QLoggingCategory> +#include <QtGui/QClipboard> +#include <QtGui/QPointingDevice> +#include <QtQuick/QQuickView> +#include <QtPdfQuick/private/qquickpdflinkmodel_p.h> +#include <QtPdfQuick/private/qquickpdfsearchmodel_p.h> +#include <QtPdfQuick/private/qquickpdfpageimage_p.h> +#include "../shared/util.h" + +Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests") + +class tst_MultiPageView : public QQuickDataTest +{ + Q_OBJECT + +private Q_SLOTS: + void internalLink_data(); + void internalLink(); + void navigation_data(); + void navigation(); + void password(); + void selectionAndClipboard(); + void search(); + +public: + enum NavigationAction { + Back, + Forward, + GotoPage, + GotoLocation, + ClickLink + }; + Q_ENUM(NavigationAction) + + struct NavigationCommand { + NavigationAction action; + int index; + QPointF location; + qreal zoom; + QPointF expectedContentPos; + int expectedCurrentPage; + }; + +private: + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); +}; + +void tst_MultiPageView::internalLink_data() +{ + QTest::addColumn<int>("linkIndex"); + QTest::addColumn<int>("expectedPage"); + QTest::addColumn<qreal>("expectedZoom"); + QTest::addColumn<QPoint>("expectedScroll"); + + QTest::newRow("first link") << 0 << 1 << qreal(1) << QPoint(134, 1276); + // TODO fails because it zooms out, and the view leaves gaps between pages currently +// QTest::newRow("second link") << 1 << 2 << qreal(0.5) << QPoint(0, 717); +} + +void tst_MultiPageView::internalLink() +{ + QFETCH(int, linkIndex); + QFETCH(int, expectedPage); + QFETCH(qreal, expectedZoom); + QFETCH(QPoint, expectedScroll); + + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + pdfView->setProperty("source", "bookmarksAndLinks.pdf"); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + QQuickItem *firstPage = tableViewItemAtCell(table, 0, 0); + QVERIFY(firstPage); + QQuickPdfLinkModel *linkModel = firstPage->findChild<QQuickPdfLinkModel*>(); + QVERIFY(linkModel); + QQuickItem *repeater = qobject_cast<QQuickItem *>(linkModel->parent()); + QVERIFY(repeater); + QVERIFY(repeater->property("count").toInt() > linkIndex); + + QCOMPARE(pdfView->property("backEnabled").toBool(), false); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), false); + + // get the PdfLinkDelegate instance, which has a TapHandler declared inside + QQuickItem *linkDelegate = repeaterItemAt(repeater, linkIndex); + QVERIFY(linkDelegate); + const auto modelIdx = linkModel->index(linkIndex); + const int linkPage = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Page)).toInt(); + QVERIFY(linkPage >= 0); + const QPointF linkLocation = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Location)).toPointF(); + const qreal linkZoom = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Zoom)).toReal(); + + // click on it, and check whether it went to the right place + const auto point = linkDelegate->position().toPoint() + QPoint(15, 15); + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, point); + QTRY_COMPARE(tableViewContentPos(table).y(), expectedScroll.y()); + const auto linkScrollPos = tableViewContentPos(table); + qCDebug(lcTests, "clicked link @ %d, %d and expected scrolling to %d, %d; actually scrolled to %d, %d", + point.x(), point.y(), expectedScroll.x(), expectedScroll.y(), linkScrollPos.x(), linkScrollPos.y()); + QVERIFY(qAbs(linkScrollPos.x() - expectedScroll.x()) < 15); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + QCOMPARE(pdfView->property("currentPage").toInt(), linkPage); + QCOMPARE(linkPage, expectedPage); + QCOMPARE(pdfView->property("renderScale").toReal(), linkZoom); + QCOMPARE(linkZoom, expectedZoom); + qCDebug(lcTests, "link %d goes to page %d location {%lf,%lf} zoom %lf scroll to {%lf,%lf}", + linkIndex, linkPage, linkLocation.x(), linkLocation.y(), linkZoom, + table->property("contentX").toReal(), table->property("contentY").toReal()); + + // check that we can go back to where we came from + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), false); + QVERIFY(QMetaObject::invokeMethod(pdfView, "back")); + QTRY_COMPARE(tableViewContentPos(table), QPoint(0, 0)); + QCOMPARE(pdfView->property("currentPage").toInt(), 0); + QCOMPARE(pdfView->property("renderScale").toReal(), qreal(1)); + + // and then forward again + QCOMPARE(pdfView->property("backEnabled").toBool(), false); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), true); + QVERIFY(QMetaObject::invokeMethod(pdfView, "forward")); + QTRY_COMPARE(tableViewContentPos(table), linkScrollPos); + QCOMPARE(pdfView->property("currentPage").toInt(), linkPage); + QCOMPARE(pdfView->property("renderScale").toReal(), linkZoom); +} + +void tst_MultiPageView::navigation_data() +{ + QTest::addColumn<QList<NavigationCommand>>("actions"); + const int totalPageSpacing = 832; // 826 points + 6 px (rowSpacing) + + QList<NavigationCommand> actions; + actions << NavigationCommand {NavigationAction::GotoPage, 2, {}, 0, {0, 1664}, 2} + << NavigationCommand {NavigationAction::GotoPage, 3, {}, 0, {0, 2496}, 3} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 1664}, 2} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("goto and back") << actions; + + actions.clear(); + actions // first link is "More..." going to page 0, location 8, 740 + << NavigationCommand {NavigationAction::ClickLink, 0, {465, 65}, 0, {0, 740}, 0} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0} + // link "setPdfVersion()" going to page 3, location 8, 295 + << NavigationCommand {NavigationAction::ClickLink, 0, {255, 455}, 0, {0, totalPageSpacing * 3 + 295}, 3} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("click links and go back, twice") << actions; + + actions.clear(); + actions // first link is "More..." going to page 0, location 8, 740 + << NavigationCommand {NavigationAction::ClickLink, 0, {465, 65}, 0, {0, 740}, 0} + // link "newPage()" going to page 1, location 8, 290 + << NavigationCommand {NavigationAction::ClickLink, 0, {480, 40}, 0, {0, totalPageSpacing + 290}, 1} // fails, goes back to page 0 + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {8, 740}, 0} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("click two links in series and then go back") << actions; +} + +void tst_MultiPageView::navigation() +{ + QFETCH(QList<NavigationCommand>, actions); + + QQuickView window; + window.setColor(Qt::gray); + window.setSource(testFileUrl("multiPageViewWithFeedback.qml")); + QTRY_COMPARE(window.status(), QQuickView::Ready); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QObject *doc = pdfView->property("document").value<QObject *>(); + QVERIFY(doc); + doc->setProperty("source", testFileUrl("qpdfwriter.pdf")); + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + // Expect that contentY == destination y after a jump, for ease of comparison. + // 0.01 is close enough to 0 that we can compare int positions accurately, + // but nonzero so that QRectF::isValid() is true in tableView.positionViewAtCell() + table->setProperty("jumpLocationMargin", QPointF(0.01, 0.01)); + + window.show(); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QTRY_COMPARE(table->property("contentHeight").toInt(), 3322); + QCOMPARE(table->property("contentY").toInt(), 0); + + for (const NavigationCommand &nav : actions) { + switch (nav.action) { + case NavigationAction::Back: + QVERIFY(QMetaObject::invokeMethod(pdfView, "back")); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), true); + break; + case NavigationAction::Forward: + QVERIFY(QMetaObject::invokeMethod(pdfView, "forward")); + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + break; + case NavigationAction::GotoPage: + QVERIFY(QMetaObject::invokeMethod(pdfView, "goToPage", + Q_ARG(QVariant, QVariant(nav.index)))); + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + break; + case NavigationAction::GotoLocation: + QVERIFY(QMetaObject::invokeMethod(pdfView, "goToLocation", + Q_ARG(QVariant, QVariant(nav.index)), + Q_ARG(QVariant, QVariant(nav.location)), + Q_ARG(QVariant, QVariant(nav.zoom)) )); + break; + case NavigationAction::ClickLink: + // Link delegates don't exist until page rendering is done + QTRY_VERIFY(pdfView->property("currentPageRenderingStatus").toInt() == 1); // QQuickImage::Status::Ready + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, nav.location.toPoint()); + // Wait for the destination page to be rendered + QTRY_VERIFY(pdfView->property("currentPageRenderingStatus").toInt() == 1); // QQuickImage::Status::Ready + break; + } + qCDebug(lcTests) << "action" << nav.action << "index" << nav.index + << "contentX,Y" << table->property("contentX").toInt() << table->property("contentY").toInt() + << "expected" << nav.expectedContentPos; + QTRY_COMPARE(table->property("contentY").toInt(), nav.expectedContentPos.y()); + // some minor side-to-side scrolling happens, in practice + QVERIFY(qAbs(table->property("contentX").toInt() - nav.expectedContentPos.x()) < 10); + QCOMPARE(pdfView->property("currentPage").toInt(), nav.expectedCurrentPage); + } + + QCOMPARE(pdfView->property("backEnabled").toBool(), false); +} + +void tst_MultiPageView::password() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QPdfDocument *cppDoc = static_cast<QPdfDocument *>(qmlExtendedObject(doc)); + QVERIFY(cppDoc); + QSignalSpy passwordRequiredSpy(doc, SIGNAL(passwordRequired())); + // actually QPdfDocument::passwordRequired, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(passwordRequiredSpy.isValid()); + QSignalSpy passwordChangedSpy(doc, SIGNAL(passwordChanged())); + // actually QPdfDocument::passwordChanged, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(passwordChangedSpy.isValid()); + QSignalSpy statusChangedSpy(doc, SIGNAL(statusChanged(QPdfDocument::Status))); + // actually QPdfDocument::statusChanged, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(statusChangedSpy.isValid()); + QSignalSpy pageCountChangedSpy(doc, SIGNAL(pageCountChanged(int))); + // QPdfDocument::pageCountChanged(int), but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(pageCountChangedSpy.isValid()); + QSignalSpy extPageCountChangedSpy(cppDoc, &QPdfDocument::pageCountChanged); + // actual QPdfDocument::pageCountChanged(int), for comparison with the illusory QQuickPdfDocument::pageCountChanged + QVERIFY(extPageCountChangedSpy.isValid()); + + QVERIFY(pdfView->setProperty("source", u"pdf-sample.protected.pdf"_qs)); + + QTRY_COMPARE(passwordRequiredSpy.count(), 1); + qCDebug(lcTests) << "error while awaiting password" << doc->error() + << "passwordRequired count" << passwordRequiredSpy.count() + << "statusChanged count" << statusChangedSpy.count(); + QCOMPARE(doc->property("status").toInt(), int(QPdfDocument::Status::Error)); + QCOMPARE(pageCountChangedSpy.count(), 0); + QCOMPARE(extPageCountChangedSpy.count(), 0); + QCOMPARE(statusChangedSpy.count(), 2); // Loading and then Error + statusChangedSpy.clear(); + QVERIFY(doc->setProperty("password", u"Qt"_qs)); + QCOMPARE(passwordChangedSpy.count(), 1); + QTRY_COMPARE(doc->property("status").toInt(), int(QPdfDocument::Status::Ready)); + qCDebug(lcTests) << "after setPassword" << doc->error() + << "passwordChanged count" << passwordChangedSpy.count() + << "statusChanged count" << statusChangedSpy.count() + << "pageCountChanged count" << pageCountChangedSpy.count(); + QCOMPARE(statusChangedSpy.count(), 2); // Loading and then Ready + QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(extPageCountChangedSpy.count(), pageCountChangedSpy.count()); +} + +void tst_MultiPageView::selectionAndClipboard() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QVERIFY(doc->setProperty("password", u"Qt"_qs)); + QVERIFY(pdfView->setProperty("source", u"pdf-sample.protected.pdf"_qs)); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + + QVERIFY(QMetaObject::invokeMethod(pdfView, "selectAll")); + QString sel = pdfView->property("selectedText").toString(); + QCOMPARE(sel.length(), 1073); + +#if QT_CONFIG(clipboard) + QClipboard *clip = qApp->clipboard(); + if (clip->supportsSelection()) + QCOMPARE(clip->text(QClipboard::Selection), sel); + QVERIFY(QMetaObject::invokeMethod(pdfView, "copySelectionToClipboard")); + QCOMPARE(clip->text(QClipboard::Clipboard), sel); +#endif // clipboard +} + +void tst_MultiPageView::search() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + window.setResizeMode(QQuickView::SizeRootObjectToView); + window.resize(200, 200); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QTRY_COMPARE(pdfView->width(), 200); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QVERIFY(doc->setProperty("password", u"Qt"_qs)); + QVERIFY(pdfView->setProperty("source", u"pdf-sample.protected.pdf"_qs)); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + QPdfSearchModel *searchModel = pdfView->property("searchModel").value<QPdfSearchModel*>(); + QVERIFY(searchModel); + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + QQuickItem *firstPage = tableViewItemAtCell(table, 0, 0); + QVERIFY(firstPage); + QObject *multiline = findFirstChild(firstPage, "QQuickPathMultiline"); + QVERIFY(multiline); + + pdfView->setProperty("searchString", u"PDF"_qs); + QTRY_COMPARE(searchModel->rowCount(QModelIndex()), 7); // occurrences of the word "PDF" in this file + const int count = searchModel->rowCount(QModelIndex()); + QList<QList<QPointF>> resultOutlines = multiline->property("paths").value<QList<QList<QPointF>>>(); + QCOMPARE(resultOutlines.count(), 7); + QPoint contentPos = tableViewContentPos(table); + int movements = 0; + for (int i = 0; i < count; ++i) { + // only one page, so IndexOnPage data is the same as overall index + QCOMPARE(i, searchModel->data(searchModel->index(i), int(QPdfSearchModel::Role::IndexOnPage)).toInt()); + QCOMPARE(resultOutlines.at(i).count(), 5); // 5-point polygon is a rectangle (including drawing back to the start, to close it) + QCOMPARE(resultOutlines.at(i).first(), searchModel->data(searchModel->index(i), int(QPdfSearchModel::Role::Location)).toPointF()); + + QVERIFY(QMetaObject::invokeMethod(pdfView, "searchForward")); + QTest::qWait(500); // animation time; but it doesn't always need to move + // TODO maybe: if movement starts, wait for it to stop somehow? + qCDebug(lcTests) << i << resultOutlines.at(i) << "scrolled to" << tableViewContentPos(table); + if (tableViewContentPos(table) != contentPos) + ++movements; + contentPos = tableViewContentPos(table); + } + qCDebug(lcTests) << "total movements" << movements; + QVERIFY(movements > 4); +} + +QTEST_MAIN(tst_MultiPageView) +#include "tst_multipageview.moc" diff --git a/tests/auto/pdfquick/shared/util.cpp b/tests/auto/pdfquick/shared/util.cpp new file mode 100644 index 000000000..c540ebfa6 --- /dev/null +++ b/tests/auto/pdfquick/shared/util.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "util.h" +#include <QtQuick/QQuickItem> + +QQuickDataTest::QQuickDataTest() : + m_initialized(false), +#ifdef QT_TESTCASE_BUILDDIR + m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0, QT_TESTCASE_BUILDDIR)), +#else + m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0)), +#endif + + m_dataDirectoryUrl(m_dataDirectory.startsWith(QLatin1Char(':')) + ? QUrl(QLatin1String("qrc") + m_dataDirectory) + : QUrl::fromLocalFile(m_dataDirectory + QLatin1Char('/'))) +{ +} + +QQuickDataTest::~QQuickDataTest() +{ +} + +void QQuickDataTest::initTestCase() +{ + QVERIFY2(!m_dataDirectory.isEmpty(), "'data' directory not found"); + m_directory = QFileInfo(m_dataDirectory).absolutePath(); + if (m_dataDirectoryUrl.scheme() != QLatin1String("qrc")) + QVERIFY2(QDir::setCurrent(m_directory), qPrintable(QLatin1String("Could not chdir to ") + m_directory)); + + if (QGuiApplication::platformName() == QLatin1String("offscreen") + || QGuiApplication::platformName() == QLatin1String("minimal")) + { + QSKIP("Skipping visual tests due to running with offscreen/minimal"); + } + + m_initialized = true; +} + +void QQuickDataTest::cleanupTestCase() +{ + m_initialized = false; +} + +QString QQuickDataTest::testFile(const QString &fileName) const +{ + if (m_directory.isEmpty()) + qFatal("QQuickDataTest::initTestCase() not called."); + QString result = m_dataDirectory; + result += QLatin1Char('/'); + result += fileName; + return result; +} + +QObject *QQuickDataTest::findFirstChild(QObject *parent, const char *className) +{ + const auto children = parent->findChildren<QObject*>(); + for (QObject *child : children) { + if (child->inherits(className)) + return child; + } + return nullptr; +} + +bool QQuickDataTest::showView(QQuickView &view, const QUrl &url) +{ + view.setSource(url); + while (view.status() == QQuickView::Loading) + QTest::qWait(10); + if (view.status() != QQuickView::Ready) + return false; + const QRect screenGeometry = view.screen()->availableGeometry(); + const QSize size = view.size(); + const QPoint offset = QPoint(size.width() / 2, size.height() / 2); + view.setFramePosition(screenGeometry.center() - offset); +#if QT_CONFIG(cursor) // Get the cursor out of the way. + QCursor::setPos(view.geometry().topRight() + QPoint(100, 100)); +#endif + view.show(); + if (!QTest::qWaitForWindowExposed(&view)) + return false; + if (!view.rootObject()) + return false; + return true; +} + +QQuickItem *QQuickDataTest::repeaterItemAt(QQuickItem *repeater, int i) +{ + static const QMetaMethod itemAtMethod = repeater->metaObject()->method( + repeater->metaObject()->indexOfMethod("itemAt(int)")); + QQuickItem *ret = nullptr; + itemAtMethod.invoke(repeater, Qt::DirectConnection, Q_RETURN_ARG(QQuickItem*, ret), Q_ARG(int, i)); + return ret; +} + +QQuickItem *QQuickDataTest::tableViewItemAtCell(QQuickItem *table, int col, int row) +{ + static const QMetaMethod itemAtCellMethod = table->metaObject()->method( + table->metaObject()->indexOfMethod("itemAtCell(int,int)")); + QQuickItem *ret = nullptr; + itemAtCellMethod.invoke(table, Qt::DirectConnection, + Q_RETURN_ARG(QQuickItem*, ret), Q_ARG(int, col), Q_ARG(int, row)); + return ret; +} + +QPoint QQuickDataTest::tableViewContentPos(QQuickItem *table) +{ + return QPoint(table->property("contentX").toInt(), table->property("contentY").toInt()); +} diff --git a/tests/auto/pdfquick/shared/util.h b/tests/auto/pdfquick/shared/util.h new file mode 100644 index 000000000..9ceb711af --- /dev/null +++ b/tests/auto/pdfquick/shared/util.h @@ -0,0 +1,58 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QUICK_VISUAL_TEST_UTIL_H +#define QUICK_VISUAL_TEST_UTIL_H + +#include <QtCore/QUrl> +#include <QtQuick/QQuickView> +#include <QtTest/QTest> + +/*! \internal + Base class for tests with data that are located in a "data" subfolder. +*/ +class QQuickDataTest : public QObject +{ + Q_OBJECT +public: + QQuickDataTest(); + ~QQuickDataTest(); + + bool initialized() const { return m_initialized; } + + bool showView(QQuickView &view, const QUrl &url); + + QString testFile(const QString &fileName) const; + inline QString testFile(const char *fileName) const + { return testFile(QLatin1String(fileName)); } + inline QUrl testFileUrl(const QString &fileName) const + { + const QString fn = testFile(fileName); + return fn.startsWith(QLatin1Char(':')) + ? QUrl(QLatin1String("qrc") + fn) + : QUrl::fromLocalFile(fn); + } + inline QUrl testFileUrl(const char *fileName) const + { return testFileUrl(QLatin1String(fileName)); } + + inline QString dataDirectory() const { return m_dataDirectory; } + inline QUrl dataDirectoryUrl() const { return m_dataDirectoryUrl; } + inline QString directory() const { return m_directory; } + + QObject *findFirstChild(QObject *parent, const char *className); + QQuickItem *repeaterItemAt(QQuickItem *repeater, int i); + QQuickItem *tableViewItemAtCell(QQuickItem *table, int col, int row); + QPoint tableViewContentPos(QQuickItem *table); + +public slots: + virtual void initTestCase(); + virtual void cleanupTestCase(); + +private: + bool m_initialized; + QString m_dataDirectory; + QUrl m_dataDirectoryUrl; + QString m_directory; +}; + +#endif |