/**************************************************************************** ** ** Copyright (C) 2016 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$ ** ****************************************************************************/ #ifndef QPLACEMANAGERENGINE_TEST_H #define QPLACEMANAGERENGINE_TEST_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE inline uint qHash(const QPlaceCategory &category) { return qHash(QUuid(category.categoryId().toLatin1())); } QT_END_NAMESPACE QT_USE_NAMESPACE class QPlacePrivateDefaultAlt : public QPlacePrivateDefault { public: QPlacePrivateDefaultAlt() {} QPlacePrivateDefaultAlt(const QPlacePrivateDefaultAlt &other) : QPlacePrivateDefault(other) { } ~QPlacePrivateDefaultAlt() {} QPlaceAttribute extendedAttribute(const QString &attributeType) const override { if (attributeType == QStringLiteral("x_provider")) { QPlaceAttribute a; a.setLabel(QStringLiteral("x_provider")); a.setText(QStringLiteral("QPlacePrivateDefaultAlt")); return a; } else { return QPlacePrivateDefault::extendedAttribute(attributeType); } } }; class QPlaceAlt : public QPlace { public: QPlaceAlt() : QPlace(QSharedDataPointer(new QPlacePrivateDefaultAlt())) { } }; class PlaceReply : public QPlaceReply { Q_OBJECT friend class QPlaceManagerEngineTest; public: PlaceReply(QObject *parent = 0) : QPlaceReply(parent) { } Q_INVOKABLE void emitFinished() { emit finished(); } }; class ContentReply : public QPlaceContentReply { Q_OBJECT friend class QPlaceManagerEngineTest; public: ContentReply(QObject *parent = 0) : QPlaceContentReply(parent) {} Q_INVOKABLE void emitError() { emit error(error(), errorString()); } Q_INVOKABLE void emitFinished() { emit finished(); } }; class DetailsReply : public QPlaceDetailsReply { Q_OBJECT friend class QPlaceManagerEngineTest; public: DetailsReply(QObject *parent = 0) : QPlaceDetailsReply(parent) { } Q_INVOKABLE void emitError() { emit error(error(), errorString()); } Q_INVOKABLE void emitFinished() { emit finished(); } }; class IdReply : public QPlaceIdReply { Q_OBJECT friend class QPlaceManagerEngineTest; public: IdReply(QPlaceIdReply::OperationType type, QObject *parent = 0) : QPlaceIdReply(type, parent) { } Q_INVOKABLE void emitError() { emit error(error(), errorString()); } Q_INVOKABLE void emitFinished() { emit finished(); } }; class PlaceSearchReply : public QPlaceSearchReply { Q_OBJECT public: PlaceSearchReply(const QList &results, QObject *parent = 0) : QPlaceSearchReply(parent) { setResults(results); } Q_INVOKABLE void emitError() { emit error(error(), errorString()); } Q_INVOKABLE void emitFinished() { emit finished(); } }; class SuggestionReply : public QPlaceSearchSuggestionReply { Q_OBJECT public: SuggestionReply(const QStringList &suggestions, QObject *parent = 0) : QPlaceSearchSuggestionReply(parent) { setSuggestions(suggestions); } Q_INVOKABLE void emitError() { emit error(error(), errorString()); } Q_INVOKABLE void emitFinished() { emit finished(); } }; class QPlaceManagerEngineTest : public QPlaceManagerEngine { Q_OBJECT public: QPlaceManagerEngineTest(const QVariantMap ¶meters) : QPlaceManagerEngine(parameters) { m_locales << QLocale(); if (parameters.value(QStringLiteral("initializePlaceData"), false).toBool()) { QFile placeData(QFINDTESTDATA("place_data.json")); QVERIFY(placeData.exists()); if (placeData.open(QIODevice::ReadOnly)) { QJsonDocument document = QJsonDocument::fromJson(placeData.readAll()); if (document.isObject()) { QJsonObject o = document.object(); if (o.contains(QStringLiteral("categories"))) { QJsonArray categories = o.value(QStringLiteral("categories")).toArray(); for (int i = 0; i < categories.count(); ++i) { QJsonObject c = categories.at(i).toObject(); QPlaceCategory category; category.setName(c.value(QStringLiteral("name")).toString()); category.setCategoryId(c.value(QStringLiteral("id")).toString()); m_categories.insert(category.categoryId(), category); const QString parentId = c.value(QStringLiteral("parentId")).toString(); m_childCategories[parentId].append(category.categoryId()); } } if (o.contains(QStringLiteral("places"))) { QJsonArray places = o.value(QStringLiteral("places")).toArray(); for (int i = 0; i < places.count(); ++i) { QJsonObject p = places.at(i).toObject(); QPlace place; if (p.value(QStringLiteral("alternateImplementation")).toBool(false)) { place = QPlaceAlt(); QPlaceAttribute att; att.setLabel(QStringLiteral("x_provider")); att.setText(QStringLiteral("42")); // Doesn't matter, wont be used. place.setExtendedAttribute(QStringLiteral("x_provider"), att); } place.setName(p.value(QStringLiteral("name")).toString()); place.setPlaceId(p.value(QStringLiteral("id")).toString()); QList categories; QJsonArray ca = p.value(QStringLiteral("categories")).toArray(); for (int j = 0; j < ca.count(); ++j) { QPlaceCategory c = m_categories.value(ca.at(j).toString()); if (!c.isEmpty()) categories.append(c); } place.setCategories(categories); QGeoCoordinate coordinate; QJsonObject lo = p.value(QStringLiteral("location")).toObject(); coordinate.setLatitude(lo.value(QStringLiteral("latitude")).toDouble()); coordinate.setLongitude(lo.value(QStringLiteral("longitude")).toDouble()); QGeoLocation location; location.setCoordinate(coordinate); place.setLocation(location); m_places.insert(place.placeId(), place); QStringList recommendations; QJsonArray ra = p.value(QStringLiteral("recommendations")).toArray(); for (int j = 0; j < ra.count(); ++j) recommendations.append(ra.at(j).toString()); m_placeRecommendations.insert(place.placeId(), recommendations); QJsonArray revArray = p.value(QStringLiteral("reviews")).toArray(); QList reviews; for (int j = 0; j < revArray.count(); ++j) { QJsonObject ro = revArray.at(j).toObject(); QPlaceReview review; if (ro.contains(QStringLiteral("title"))) review.setTitle(ro.value(QStringLiteral("title")).toString()); if (ro.contains(QStringLiteral("text"))) review.setText(ro.value(QStringLiteral("text")).toString()); if (ro.contains(QStringLiteral("language"))) review.setLanguage(ro.value("language").toString()); if (ro.contains(QStringLiteral("rating"))) review.setRating(ro.value("rating").toDouble()); if (ro.contains(QStringLiteral("dateTime"))) review.setDateTime(QDateTime::fromString( ro.value(QStringLiteral("dateTime")).toString(), QStringLiteral("hh:mm dd-MM-yyyy"))); if (ro.contains(QStringLiteral("reviewId"))) review.setReviewId(ro.value("reviewId").toString()); reviews << review; } m_placeReviews.insert(place.placeId(), reviews); QJsonArray imgArray = p.value(QStringLiteral("images")).toArray(); QList images; for (int j = 0; j < imgArray.count(); ++j) { QJsonObject imgo = imgArray.at(j).toObject(); QPlaceImage image; if (imgo.contains(QStringLiteral("url"))) image.setUrl(imgo.value(QStringLiteral("url")).toString()); if (imgo.contains("imageId")) image.setImageId(imgo.value(QStringLiteral("imageId")).toString()); if (imgo.contains("mimeType")) image.setMimeType(imgo.value(QStringLiteral("mimeType")).toString()); images << image; } m_placeImages.insert(place.placeId(), images); QJsonArray edArray = p.value(QStringLiteral("editorials")).toArray(); QList editorials; for (int j = 0; j < edArray.count(); ++j) { QJsonObject edo = edArray.at(j).toObject(); QPlaceEditorial editorial; if (edo.contains(QStringLiteral("title"))) editorial.setTitle(edo.value(QStringLiteral("title")).toString()); if (edo.contains(QStringLiteral("text"))) editorial.setText(edo.value(QStringLiteral("text")).toString()); if (edo.contains(QStringLiteral("language"))) editorial.setLanguage(edo.value(QStringLiteral("language")).toString()); editorials << editorial; } m_placeEditorials.insert(place.placeId(), editorials); } } } } } } QPlaceDetailsReply *getPlaceDetails(const QString &placeId) override { DetailsReply *reply = new DetailsReply(this); if (placeId.isEmpty() || !m_places.contains(placeId)) { reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist")); QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); } else { reply->setPlace(m_places.value(placeId)); } QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QPlaceContentReply *getPlaceContent(const QPlaceContentRequest &query) override { ContentReply *reply = new ContentReply(this); if (query.placeId().isEmpty() || !m_places.contains(query.placeId())) { reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist")); QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); } else { QPlaceContent::Collection collection; int totalCount = 0; switch (query.contentType()) { case QPlaceContent::ReviewType: totalCount = m_placeReviews.value(query.placeId()).count(); break; case QPlaceContent::ImageType: totalCount = m_placeImages.value(query.placeId()).count(); break; case QPlaceContent::EditorialType: totalCount = m_placeEditorials.value(query.placeId()).count(); default: //do nothing break; } QVariantMap context = query.contentContext().toMap(); int offset = context.value(QStringLiteral("offset"), 0).toInt(); int max = (query.limit() == -1) ? totalCount : qMin(offset + query.limit(), totalCount); for (int i = offset; i < max; ++i) { switch (query.contentType()) { case QPlaceContent::ReviewType: collection.insert(i, m_placeReviews.value(query.placeId()).at(i)); break; case QPlaceContent::ImageType: collection.insert(i, m_placeImages.value(query.placeId()).at(i)); break; case QPlaceContent::EditorialType: collection.insert(i, m_placeEditorials.value(query.placeId()).at(i)); default: //do nothing break; } } reply->setContent(collection); reply->setTotalCount(totalCount); if (max != totalCount) { context.clear(); context.insert(QStringLiteral("offset"), offset + query.limit()); QPlaceContentRequest request = query; request.setContentContext(context); reply->setNextPageRequest(request); } if (offset > 0) { context.clear(); context.insert(QStringLiteral("offset"), qMin(0, offset - query.limit())); QPlaceContentRequest request = query; request.setContentContext(context); reply->setPreviousPageRequest(request); } } QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QPlaceSearchReply *search(const QPlaceSearchRequest &query) override { QList results; if (!query.searchTerm().isEmpty()) { foreach (const QPlace &place, m_places) { if (!place.name().contains(query.searchTerm(), Qt::CaseInsensitive)) continue; QPlaceResult r; r.setPlace(place); r.setTitle(place.name()); results.append(r); } } else if (!query.categories().isEmpty()) { const auto &categoryList = query.categories(); const QSet categories(categoryList.cbegin(), categoryList.cend()); for (const QPlace &place : qAsConst(m_places)) { const auto &placeCategoryList = place.categories(); const QSet placeCategories(placeCategoryList.cbegin(), placeCategoryList.cend()); if (!placeCategories.intersects(categories)) continue; QPlaceResult r; r.setPlace(place); r.setTitle(place.name()); results.append(r); } } else if (!query.recommendationId().isEmpty()) { QStringList recommendations = m_placeRecommendations.value(query.recommendationId()); foreach (const QString &id, recommendations) { QPlaceResult r; r.setPlace(m_places.value(id)); r.setTitle(r.place().name()); results.append(r); } } PlaceSearchReply *reply = new PlaceSearchReply(results, this); QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &query) override { QStringList suggestions; if (query.searchTerm() == QLatin1String("test")) { suggestions << QStringLiteral("test1"); suggestions << QStringLiteral("test2"); suggestions << QStringLiteral("test3"); } SuggestionReply *reply = new SuggestionReply(suggestions, this); QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QPlaceIdReply *savePlace(const QPlace &place) override { IdReply *reply = new IdReply(QPlaceIdReply::SavePlace, this); if (!place.placeId().isEmpty() && !m_places.contains(place.placeId())) { reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist")); QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); } else if (!place.placeId().isEmpty()) { m_places.insert(place.placeId(), place); reply->setId(place.placeId()); } else { QPlace p = place; p.setPlaceId(QUuid::createUuid().toString()); m_places.insert(p.placeId(), p); reply->setId(p.placeId()); } QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QPlaceIdReply *removePlace(const QString &placeId) override { IdReply *reply = new IdReply(QPlaceIdReply::RemovePlace, this); reply->setId(placeId); if (!m_places.contains(placeId)) { reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist")); QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); } else { m_places.remove(placeId); } QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QPlaceIdReply *saveCategory(const QPlaceCategory &category, const QString &parentId) override { IdReply *reply = new IdReply(QPlaceIdReply::SaveCategory, this); if ((!category.categoryId().isEmpty() && !m_categories.contains(category.categoryId())) || (!parentId.isEmpty() && !m_categories.contains(parentId))) { reply->setError(QPlaceReply::CategoryDoesNotExistError, tr("Category does not exist")); QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); } else if (!category.categoryId().isEmpty()) { m_categories.insert(category.categoryId(), category); QStringList children = m_childCategories.value(parentId); for (QStringList &c : m_childCategories) c.removeAll(category.categoryId()); if (!children.contains(category.categoryId())) { children.append(category.categoryId()); m_childCategories.insert(parentId, children); } reply->setId(category.categoryId()); } else { QPlaceCategory c = category; c.setCategoryId(QUuid::createUuid().toString()); m_categories.insert(c.categoryId(), c); QStringList children = m_childCategories.value(parentId); if (!children.contains(c.categoryId())) { children.append(c.categoryId()); m_childCategories.insert(parentId, children); } reply->setId(c.categoryId()); } QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QPlaceIdReply *removeCategory(const QString &categoryId) override { IdReply *reply = new IdReply(QPlaceIdReply::RemoveCategory, this); reply->setId(categoryId); if (!m_categories.contains(categoryId)) { reply->setError(QPlaceReply::CategoryDoesNotExistError, tr("Category does not exist")); QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); } else { m_categories.remove(categoryId); for (auto &c : m_childCategories) c.removeAll(categoryId); } QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QPlaceReply *initializeCategories() override { QPlaceReply *reply = new PlaceReply(this); QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); return reply; } QString parentCategoryId(const QString &categoryId) const override { for (auto i = m_childCategories.cbegin(), end = m_childCategories.cend(); i != end; ++i) { if (i.value().contains(categoryId)) return i.key(); } return QString(); } virtual QStringList childCategoryIds(const QString &categoryId) const override { return m_childCategories.value(categoryId); } virtual QPlaceCategory category(const QString &categoryId) const override { return m_categories.value(categoryId); } QList childCategories(const QString &parentId) const override { QList categories; foreach (const QString &id, m_childCategories.value(parentId)) categories.append(m_categories.value(id)); return categories; } QList locales() const override { return m_locales; } void setLocales(const QList &locales) override { m_locales = locales; } QUrl constructIconUrl(const QPlaceIcon &icon, const QSize &size) const override { QList > candidates; QMap sizeDictionary; sizeDictionary.insert(QStringLiteral("s"), 20); sizeDictionary.insert(QStringLiteral("m"), 30); sizeDictionary.insert(QStringLiteral("l"), 50); QStringList sizeKeys; sizeKeys << QStringLiteral("s") << QStringLiteral("m") << QStringLiteral("l"); foreach (const QString &sizeKey, sizeKeys) { if (icon.parameters().contains(sizeKey)) candidates.append(QPair(sizeDictionary.value(sizeKey), icon.parameters().value(sizeKey).toUrl())); } if (candidates.isEmpty()) return QUrl(); else if (candidates.count() == 1) { return candidates.first().second; } else { //we assume icons are squarish so we can use height to //determine which particular icon to return int requestedHeight = size.height(); for (int i = 0; i < candidates.count() - 1; ++i) { int thresholdHeight = (candidates.at(i).first + candidates.at(i+1).first) / 2; if (requestedHeight < thresholdHeight) return candidates.at(i).second; } return candidates.last().second; } } QPlace compatiblePlace(const QPlace &original) const override { QPlace place; place.setName(original.name()); return place; } private: QList m_locales; QHash m_places; QHash m_categories; QHash m_childCategories; QHash m_placeRecommendations; QHash > m_placeReviews; QHash > m_placeImages; QHash > m_placeEditorials; }; #endif