Merge lp:~mardy/ubuntu-system-settings-online-accounts/lp1373279 into lp:~online-accounts/ubuntu-system-settings-online-accounts/master

Proposed by Alberto Mardegan
Status: Merged
Merged at revision: 227
Proposed branch: lp:~mardy/ubuntu-system-settings-online-accounts/lp1373279
Merge into: lp:~online-accounts/ubuntu-system-settings-online-accounts/master
Diff against target: 580 lines (+505/-8)
8 files modified
.bzrignore (+1/-0)
system-settings-plugin/online-accounts.settings (+2/-6)
system-settings-plugin/plugin.cpp (+116/-0)
system-settings-plugin/plugin.h (+36/-0)
system-settings-plugin/system-settings-plugin.pro (+24/-1)
tests/system-settings-plugin/system-settings-plugin.pro (+32/-0)
tests/system-settings-plugin/tst_plugin.cpp (+292/-0)
tests/tests.pro (+2/-1)
To merge this branch: bzr merge lp:~mardy/ubuntu-system-settings-online-accounts/lp1373279
Reviewer Review Type Date Requested Status
Alexandre Abreu (community) Approve
Review via email: mp+247453@code.launchpad.net

Commit message

Add account data as search keywords

Description of the change

Add account data as search keywords

To post a comment you must log in.
Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

small comment inline

review: Needs Fixing
226. By Alberto Mardegan

Avoid inserting duplicate keywords

227. By Alberto Mardegan

Added tests

228. By Alberto Mardegan

Remove unnecessary friend

Revision history for this message
Alberto Mardegan (mardy) wrote :

I fixed the code as suggested by Alex, and added some unit tests.

Revision history for this message
Alexandre Abreu (abreu-alexandre) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2014-12-11 12:51:46 +0000
3+++ .bzrignore 2015-02-02 15:22:49 +0000
4@@ -57,3 +57,4 @@
5 /tests/online-accounts-ui/tst_notification
6 /tests/online-accounts-ui/tst_signonui_request
7 /tests/plugin/tst_application_manager
8+/tests/system-settings-plugin/tst_plugin
9
10=== modified file 'system-settings-plugin/online-accounts.settings'
11--- system-settings-plugin/online-accounts.settings 2014-09-24 19:05:32 +0000
12+++ system-settings-plugin/online-accounts.settings 2015-02-02 15:22:49 +0000
13@@ -14,14 +14,10 @@
14 "access",
15 "authorize",
16 "revoke",
17- "web",
18- "facebook",
19- "twitter",
20- "flickr",
21- "google",
22- "gmail"
23+ "web"
24 ],
25 "has-dynamic-keywords": true,
26 "has-dynamic-visibility": false,
27+ "plugin": "online-accounts",
28 "page-component": "MainPage.qml"
29 }
30
31=== added file 'system-settings-plugin/plugin.cpp'
32--- system-settings-plugin/plugin.cpp 1970-01-01 00:00:00 +0000
33+++ system-settings-plugin/plugin.cpp 2015-02-02 15:22:49 +0000
34@@ -0,0 +1,116 @@
35+/*
36+ * Copyright (C) 2015 Canonical Ltd.
37+ *
38+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
39+ *
40+ * This program is free software: you can redistribute it and/or modify it
41+ * under the terms of the GNU General Public License version 3, as published
42+ * by the Free Software Foundation.
43+ *
44+ * This program is distributed in the hope that it will be useful, but
45+ * WITHOUT ANY WARRANTY; without even the implied warranties of
46+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
47+ * PURPOSE. See the GNU General Public License for more details.
48+ *
49+ * You should have received a copy of the GNU General Public License along
50+ * with this program. If not, see <http://www.gnu.org/licenses/>.
51+ */
52+
53+#include "plugin.h"
54+
55+#include <Accounts/Account>
56+#include <Accounts/Manager>
57+#include <Accounts/Provider>
58+#include <Accounts/Service>
59+#include <QDebug>
60+#include <QStringList>
61+#include <SystemSettings/ItemBase>
62+#include <libintl.h>
63+
64+using namespace SystemSettings;
65+
66+class Item: public ItemBase
67+{
68+ Q_OBJECT
69+
70+public:
71+ Item(const QVariantMap &staticData, QObject *parent = 0);
72+ ~Item();
73+
74+private:
75+ void computeKeywords();
76+};
77+
78+Item::Item(const QVariantMap &staticData, QObject *parent):
79+ ItemBase(staticData, parent)
80+{
81+ computeKeywords();
82+}
83+
84+Item::~Item()
85+{
86+}
87+
88+static QStringList translations(const QString &text, const QString &domain)
89+{
90+ /* Return a list of keywords based on a translatable name:
91+ * - the untranslated text (lowercase, split into words)
92+ * - the translated text (lowercase, split into words)
93+ */
94+ QStringList keys;
95+ keys = text.toLower().split(" ", QString::SkipEmptyParts);
96+ if (!domain.isEmpty()) {
97+ QByteArray baText = text.toUtf8();
98+ QByteArray baDomain = domain.toUtf8();
99+ QString translated = QString::fromUtf8(dgettext(baDomain.constData(),
100+ baText.constData()));
101+ if (translated != text) {
102+ keys.append(translated.toLower().split(" ",
103+ QString::SkipEmptyParts));
104+ }
105+ }
106+ return keys;
107+}
108+
109+void Item::computeKeywords()
110+{
111+ Accounts::Manager *manager = new Accounts::Manager;
112+
113+ QStringList keywords;
114+
115+ /* List available providers, and add their names to the search keywords */
116+ Q_FOREACH(const Accounts::Provider &provider, manager->providerList()) {
117+ keywords.append(provider.name().toLower());
118+ keywords.append(translations(provider.displayName(),
119+ provider.trCatalog()));
120+ }
121+
122+ /* Same for services */
123+ Q_FOREACH(const Accounts::Service &service, manager->serviceList()) {
124+ keywords.append(service.name().toLower());
125+ keywords.append(translations(service.displayName(),
126+ service.trCatalog()));
127+ }
128+
129+ /* Also add the account display names */
130+ Q_FOREACH(Accounts::AccountId id, manager->accountList()) {
131+ Accounts::Account *account = manager->account(id);
132+ if (Q_UNLIKELY(!account)) continue;
133+ QString name = account->displayName().toLower();
134+ if (!name.isEmpty()) {
135+ keywords.append(name);
136+ }
137+ }
138+
139+ delete manager;
140+
141+ setKeywords(keywords);
142+}
143+
144+ItemBase *Plugin::createItem(const QVariantMap &staticData,
145+ QObject *parent)
146+{
147+ return new Item(staticData, parent);
148+}
149+
150+#include "plugin.moc"
151
152=== added file 'system-settings-plugin/plugin.h'
153--- system-settings-plugin/plugin.h 1970-01-01 00:00:00 +0000
154+++ system-settings-plugin/plugin.h 2015-02-02 15:22:49 +0000
155@@ -0,0 +1,36 @@
156+/*
157+ * Copyright (C) 2015 Canonical Ltd.
158+ *
159+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
160+ *
161+ * This program is free software: you can redistribute it and/or modify it
162+ * under the terms of the GNU General Public License version 3, as published
163+ * by the Free Software Foundation.
164+ *
165+ * This program is distributed in the hope that it will be useful, but
166+ * WITHOUT ANY WARRANTY; without even the implied warranties of
167+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
168+ * PURPOSE. See the GNU General Public License for more details.
169+ *
170+ * You should have received a copy of the GNU General Public License along
171+ * with this program. If not, see <http://www.gnu.org/licenses/>.
172+ */
173+
174+#ifndef ONLINE_ACCOUNTS_SYSTEM_SETTINGS_PLUGIN_H
175+#define ONLINE_ACCOUNTS_SYSTEM_SETTINGS_PLUGIN_H
176+
177+#include <QObject>
178+#include <SystemSettings/PluginInterface>
179+
180+class Plugin: public QObject, public SystemSettings::PluginInterface2
181+{
182+ Q_OBJECT
183+ Q_PLUGIN_METADATA(IID "com.ubuntu.SystemSettings.PluginInterface/2.0")
184+ Q_INTERFACES(SystemSettings::PluginInterface2)
185+
186+public:
187+ SystemSettings::ItemBase *createItem(const QVariantMap &staticData,
188+ QObject *parent = 0);
189+};
190+
191+#endif // ONLINE_ACCOUNTS_SYSTEM_SETTINGS_PLUGIN_H
192
193=== modified file 'system-settings-plugin/system-settings-plugin.pro'
194--- system-settings-plugin/system-settings-plugin.pro 2014-08-13 13:11:06 +0000
195+++ system-settings-plugin/system-settings-plugin.pro 2015-02-02 15:22:49 +0000
196@@ -1,7 +1,27 @@
197 include(../common-project-config.pri)
198 include($${TOP_SRC_DIR}/common-vars.pri)
199
200-TEMPLATE=aux
201+TEMPLATE=lib
202+TARGET = online-accounts
203+
204+CONFIG += \
205+ link_pkgconfig \
206+ plugin \
207+ qt
208+
209+QT += \
210+ core \
211+ qml
212+
213+PKGCONFIG += \
214+ SystemSettings \
215+ accounts-qt5
216+
217+SOURCES += \
218+ plugin.cpp
219+
220+HEADERS += \
221+ plugin.h
222
223 QML_SOURCES = \
224 AccountEditPage.qml \
225@@ -19,6 +39,9 @@
226 settings.path = $${PLUGIN_MANIFEST_DIR}
227 INSTALLS += settings
228
229+target.path = $${PLUGIN_MODULE_DIR}
230+INSTALLS += target
231+
232 image.files = settings-accounts.svg
233 image.path = $${PLUGIN_MANIFEST_DIR}/icons
234 INSTALLS += image
235
236=== added directory 'tests/system-settings-plugin'
237=== added file 'tests/system-settings-plugin/system-settings-plugin.pro'
238--- tests/system-settings-plugin/system-settings-plugin.pro 1970-01-01 00:00:00 +0000
239+++ tests/system-settings-plugin/system-settings-plugin.pro 2015-02-02 15:22:49 +0000
240@@ -0,0 +1,32 @@
241+include(../../common-project-config.pri)
242+
243+TARGET = tst_plugin
244+
245+CONFIG += \
246+ debug \
247+ link_pkgconfig
248+
249+QT += \
250+ core \
251+ qml \
252+ testlib
253+
254+PKGCONFIG += \
255+ SystemSettings \
256+ accounts-qt5
257+
258+SYSTEM_SETTINGS_PLUGIN_DIR = $${TOP_SRC_DIR}/system-settings-plugin
259+
260+INCLUDEPATH += \
261+ $${SYSTEM_SETTINGS_PLUGIN_DIR}
262+
263+SOURCES += \
264+ $${SYSTEM_SETTINGS_PLUGIN_DIR}/plugin.cpp \
265+ tst_plugin.cpp
266+
267+HEADERS += \
268+ $${SYSTEM_SETTINGS_PLUGIN_DIR}/plugin.h
269+
270+check.commands = "xvfb-run -s '-screen 0 640x480x24' -a dbus-test-runner -t ./$${TARGET}"
271+check.depends = $${TARGET}
272+QMAKE_EXTRA_TARGETS += check
273
274=== added file 'tests/system-settings-plugin/tst_plugin.cpp'
275--- tests/system-settings-plugin/tst_plugin.cpp 1970-01-01 00:00:00 +0000
276+++ tests/system-settings-plugin/tst_plugin.cpp 2015-02-02 15:22:49 +0000
277@@ -0,0 +1,292 @@
278+/*
279+ * Copyright (C) 2014 Canonical Ltd.
280+ *
281+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
282+ *
283+ * This file is part of online-accounts-ui
284+ *
285+ * This program is free software: you can redistribute it and/or modify it
286+ * under the terms of the GNU General Public License version 3, as published
287+ * by the Free Software Foundation.
288+ *
289+ * This program is distributed in the hope that it will be useful, but
290+ * WITHOUT ANY WARRANTY; without even the implied warranties of
291+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
292+ * PURPOSE. See the GNU General Public License for more details.
293+ *
294+ * You should have received a copy of the GNU General Public License along
295+ * with this program. If not, see <http://www.gnu.org/licenses/>.
296+ */
297+
298+#include <Accounts/Account>
299+#include <Accounts/Manager>
300+
301+#include <QDebug>
302+#include <QDir>
303+#include <QDomDocument>
304+#include <QDomElement>
305+#include <QFile>
306+#include <QSignalSpy>
307+#include <QTemporaryDir>
308+#include <QTest>
309+
310+#include <SystemSettings/ItemBase>
311+
312+#include "plugin.h"
313+
314+namespace QTest {
315+template<>
316+char *toString(const QSet<QString> &set)
317+{
318+ QByteArray ba = "QSet<QString>(";
319+ QStringList list = set.toList();
320+ ba += list.join(", ");
321+ ba += ")";
322+ return qstrdup(ba.data());
323+}
324+} // QTest namespace
325+
326+/* mocking libintl { */
327+static QHash<QString,QHash<QByteArray,QByteArray> > m_translations;
328+extern "C" {
329+char *dgettext(const char *domainname, const char *msgid)
330+{
331+ return (char *)m_translations[domainname].value(msgid).data();
332+}
333+char *dcgettext(const char *__domainname, const char *__msgid, int __category)
334+{
335+ Q_UNUSED(__category);
336+ return dgettext(__domainname, __msgid);
337+}
338+} // extern C
339+/* } mocking libintl */
340+
341+class PluginTest: public QObject
342+{
343+ Q_OBJECT
344+
345+ struct FileData {
346+ FileData() {}
347+ FileData(const QString &name, const QString &domain):
348+ name(name), domain(domain) {}
349+ QString name;
350+ QString domain;
351+ };
352+
353+public:
354+ PluginTest();
355+
356+private Q_SLOTS:
357+ void testKeywords_data();
358+ void testKeywords();
359+
360+private:
361+ void setDataDir(const QTemporaryDir &dir);
362+ void writeProvider(const QString &id, const QString &name,
363+ const QString &domain);
364+ void writeService(const QString &id, const QString &name,
365+ const QString &domain);
366+ void writeLibaccountsFile(const QString &type, const QString &id,
367+ const QString &name, const QString &domain);
368+ void createAccount(const QString &name);
369+
370+private:
371+ QHash<QString,FileData> m_providersData;
372+ QHash<QString,FileData> m_servicesData;
373+ QDir m_dataDir;
374+};
375+
376+PluginTest::PluginTest():
377+ QObject(0)
378+{
379+ m_providersData["provider_noname"] = FileData("", "");
380+ m_providersData["provider_nodomain"] = FileData("Happy", "");
381+ m_providersData["provider_translated"] = FileData("Joyful", "translations1");
382+
383+ m_servicesData["service_nodomain"] = FileData("Sad", "");
384+ m_servicesData["service_translated"] = FileData("Depressed", "translations1");
385+ m_servicesData["service_translated2"] = FileData("Depressed", "translations2");
386+
387+ m_translations["translations1"]["Happy"] = "Contento";
388+ m_translations["translations1"]["Joyful"] = "Gioioso";
389+ m_translations["translations1"]["Sad"] = "Triste";
390+ m_translations["translations1"]["Depressed"] = "Depresso1";
391+ m_translations["translations1"]["Black"] = "Nero";
392+ m_translations["translations1"]["White"] = "Bianco";
393+
394+ m_translations["translations2"]["Depressed"] = "Depresso2";
395+}
396+
397+void PluginTest::setDataDir(const QTemporaryDir &dir)
398+{
399+ QVERIFY(dir.isValid());
400+
401+ m_dataDir = QDir(dir.path());
402+
403+ QByteArray dataPath = dir.path().toUtf8();
404+ qputenv("ACCOUNTS", dataPath);
405+ qputenv("AG_PROVIDERS", dataPath);
406+ qputenv("AG_SERVICES", dataPath);
407+}
408+
409+void PluginTest::writeProvider(const QString &id, const QString &name,
410+ const QString &domain)
411+{
412+ writeLibaccountsFile("provider", id, name, domain);
413+}
414+
415+void PluginTest::writeService(const QString &id, const QString &name,
416+ const QString &domain)
417+{
418+ writeLibaccountsFile("service", id, name, domain);
419+}
420+
421+void PluginTest::writeLibaccountsFile(const QString &type, const QString &id,
422+ const QString &name, const QString &domain)
423+{
424+ QDomDocument doc;
425+ QDomElement root = doc.createElement(type);
426+ root.setAttribute("id", id);
427+ doc.appendChild(root);
428+
429+ QDomElement nameTag = doc.createElement("name");
430+ nameTag.appendChild(doc.createTextNode(name));
431+ root.appendChild(nameTag);
432+
433+ QDomElement domainTag = doc.createElement("translations");
434+ domainTag.appendChild(doc.createTextNode(domain));
435+ root.appendChild(domainTag);
436+
437+ if (type == "service") {
438+ QDomElement typeTag = doc.createElement("type");
439+ typeTag.appendChild(doc.createTextNode("something"));
440+ root.appendChild(typeTag);
441+ }
442+
443+ QFile file(m_dataDir.filePath(id + "." + type));
444+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
445+ qWarning() << "Could not write file" << file.fileName();
446+ return;
447+ }
448+
449+ file.write(doc.toString().toUtf8());
450+}
451+
452+void PluginTest::createAccount(const QString &name)
453+{
454+ Accounts::Manager *manager = new Accounts::Manager;
455+
456+ Accounts::Account *account = manager->createAccount("any");
457+ account->setDisplayName(name);
458+ account->syncAndBlock();
459+
460+ delete manager;
461+}
462+
463+void PluginTest::testKeywords_data()
464+{
465+ QTest::addColumn<QStringList>("providers");
466+ QTest::addColumn<QStringList>("services");
467+ QTest::addColumn<QStringList>("accountNames");
468+ QTest::addColumn<QStringList>("keywords");
469+
470+ QTest::newRow("provider, no name") <<
471+ (QStringList() << "provider_noname") <<
472+ QStringList() <<
473+ QStringList() <<
474+ (QStringList() << "provider_noname");
475+
476+ QTest::newRow("provider, untranslated") <<
477+ (QStringList() << "provider_nodomain") <<
478+ QStringList() <<
479+ QStringList() <<
480+ (QStringList() << "provider_nodomain" << "happy");
481+
482+ QTest::newRow("provider, translated") <<
483+ (QStringList() << "provider_translated") <<
484+ QStringList() <<
485+ QStringList() <<
486+ (QStringList() << "provider_translated" << "joyful" << "gioioso");
487+
488+ QTest::newRow("service, untranslated") <<
489+ QStringList() <<
490+ (QStringList() << "service_nodomain") <<
491+ QStringList() <<
492+ (QStringList() << "service_nodomain" << "sad");
493+
494+ QTest::newRow("service, translated 1") <<
495+ QStringList() <<
496+ (QStringList() << "service_translated") <<
497+ QStringList() <<
498+ (QStringList() << "service_translated" << "depressed" << "depresso1");
499+
500+ QTest::newRow("service, translated 2") <<
501+ QStringList() <<
502+ (QStringList() << "service_translated2") <<
503+ QStringList() <<
504+ (QStringList() << "service_translated2" << "depressed" << "depresso2");
505+
506+ QTest::newRow("one account, one word") <<
507+ QStringList() <<
508+ QStringList() <<
509+ (QStringList() << "tom@example.com") <<
510+ (QStringList() << "tom@example.com");
511+
512+ QTest::newRow("one account, many words") <<
513+ QStringList() <<
514+ QStringList() <<
515+ (QStringList() << "My little sweet account") <<
516+ (QStringList() << "my little sweet account");
517+
518+ QTest::newRow("combined") <<
519+ (QStringList() << "provider_translated" << "provider_nodomain") <<
520+ (QStringList() << "service_translated" << "service_translated2") <<
521+ (QStringList() << "john@invalid" << "harry@mysite.com") <<
522+ (QStringList() << "provider_translated" << "joyful" << "gioioso" <<
523+ "provider_nodomain" << "happy" <<
524+ "service_translated" << "depressed" << "depresso1" <<
525+ "service_translated2" << "depressed" << "depresso2" <<
526+ "john@invalid" << "harry@mysite.com");
527+}
528+
529+void PluginTest::testKeywords()
530+{
531+ QFETCH(QStringList, providers);
532+ QFETCH(QStringList, services);
533+ QFETCH(QStringList, accountNames);
534+ QFETCH(QStringList, keywords);
535+
536+ QTemporaryDir dataDir;
537+
538+ setDataDir(dataDir);
539+
540+ /* Create the needed files */
541+ Q_FOREACH(const QString &providerId, providers) {
542+ writeProvider(providerId,
543+ m_providersData[providerId].name,
544+ m_providersData[providerId].domain);
545+ }
546+
547+ Q_FOREACH(const QString &serviceId, services) {
548+ writeService(serviceId,
549+ m_servicesData[serviceId].name,
550+ m_servicesData[serviceId].domain);
551+ }
552+
553+ /* create the accounts */
554+ Q_FOREACH(const QString &displayName, accountNames) {
555+ createAccount(displayName);
556+ }
557+
558+ /* Now do the actual test */
559+ Plugin plugin;
560+
561+ SystemSettings::ItemBase *item = plugin.createItem(QVariantMap());
562+ QStringList pluginKeywords = item->keywords();
563+
564+ QCOMPARE(pluginKeywords.toSet(), keywords.toSet());
565+}
566+
567+QTEST_MAIN(PluginTest);
568+
569+#include "tst_plugin.moc"
570
571=== modified file 'tests/tests.pro'
572--- tests/tests.pro 2014-10-03 14:56:11 +0000
573+++ tests/tests.pro 2015-02-02 15:22:49 +0000
574@@ -5,4 +5,5 @@
575 client \
576 online-accounts-service \
577 online-accounts-ui \
578- plugin
579+ plugin \
580+ system-settings-plugin

Subscribers

People subscribed via source and target branches