Merge lp:~mardy/ubuntu-system-settings/other-app-access into lp:ubuntu-system-settings
- other-app-access
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Ken VanDine |
Approved revision: | 823 |
Merged at revision: | 907 |
Proposed branch: | lp:~mardy/ubuntu-system-settings/other-app-access |
Merge into: | lp:ubuntu-system-settings |
Diff against target: |
1079 lines (+965/-2) 12 files modified
debian/control (+1/-0) plugins/security-privacy/AppAccess.qml (+74/-0) plugins/security-privacy/AppAccessControl.qml (+53/-0) plugins/security-privacy/CMakeLists.txt (+20/-1) plugins/security-privacy/PageComponent.qml (+1/-1) plugins/security-privacy/plugin.cpp (+2/-0) plugins/security-privacy/trust-store-model.cpp (+333/-0) plugins/security-privacy/trust-store-model.h (+74/-0) po/CMakeLists.txt (+1/-0) tests/plugins/CMakeLists.txt (+1/-0) tests/plugins/security-privacy/CMakeLists.txt (+11/-0) tests/plugins/security-privacy/tst_trust_store_model.cpp (+394/-0) |
To merge this branch: | bzr merge lp:~mardy/ubuntu-system-settings/other-app-access |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ken VanDine | Approve | ||
Thomas Voß (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Sebastien Bacher (community) | Needs Information | ||
Review via email: mp+228299@code.launchpad.net |
Commit message
Implement the "Other App Access" screen
Description of the change
Implement the "Other App Access" screen
As per https:/
The UI strings needs to be confirmed with design, before landing this.
Testing instructions
=======
At the moment, the CameraService and PulseAudio helper services are not implemented, so the UI will always show "0" applications using them. However, it's possible to test the feature by using a couple of command line utilities from this branch: https:/
(replace "x86_64-linux-gnu" with your architecture)
1) Run this on a terminal:
/usr/
2) Run this on another terminal:
/usr/
3) Launch an application which has is registered in "~/.local/
<pid> 1000 0
4) Go to the terminal from step #2, and either accept or deny the request
5) (desktop only) Fire up "sqlitebrowser ~/.local/
Repeat steps 3-5 as many times as you want (with different apps).
6) Start system-settings, go to "Security & Privacy" -> "Other App Access"; play with the UI, and verify that it behaves as expected. From time to time, close it and re-open it, and make sure that it preserves the last state.
PS Jenkins bot (ps-jenkins) wrote : | # |
Sebastien Bacher (seb128) wrote : | # |
Thanks, should the listmodel values be translated? Or do you plan to do that when used in an U.I (btw are the title/caption used somewhere, they are pushed to the appaccesscontrol page but don't seem used there)
Alberto Mardegan (mardy) wrote : | # |
I'll push one more commit to add translations for those strings (I first wanted to read the designers' feedback, which just came).
As for the properties,
"title" is set to the ItemPage's title
"caption" is set as the captionLabel.text
so it seems that they are all used.
Sebastien Bacher (seb128) wrote : | # |
Where did you get feedback from the designerS?
> "title" is set to the ItemPage's title
I didn't see it used in the other files, I guess pushing with a title property is enough to make it work by magic, right?
Alberto Mardegan (mardy) wrote : | # |
> Where did you get feedback from the designerS?
JohnLea on IRC :-)
> > "title" is set to the ItemPage's title
>
> I didn't see it used in the other files, I guess pushing with a title property
> is enough to make it work by magic, right?
Yes, ItemPage has a "title" property, and we are specifying that when calling pageStack.push()
- 819. By Alberto Mardegan
-
Translate all texts
- 820. By Alberto Mardegan
-
Merge trunk
[ Ken VanDine ]
* [sound] Added volume slider to the sound pane
[ jonas-drange ]
* Cellular panel changes: new carrier design. Carrier selection for
two sims (individual selection). Using either sim1 or sim2 for
cellular data. Data technology preference selection refactor to work
for dual sim scenario as well.
[ Ken VanDine ]
* [phone] Call forwarding implementation
[ CI bot ]
* Resync trunk
[ Oliver Grawert ]
* Add DeveloperMode page to "About this device" (see: .
https://wiki.ubuntu. com/AboutThisDe vice#developer- mode) Enable
toggling of the adb usb gadget driver in DeveloperMode page through
the dbus-property-service interface. Add shortcut from DeveloperMode
page to Lock screen settings (since the final dev mode will require
that you can lock before enabling it). Depend on suru-icon-theme
since we are using the security alert icon on the Developer Mode
page. Depend on dbus-property-service on the arches where it is
available since we use it in Developer Mode .
[ jonas-drange ]
* Hi, this branch seeks to address most of the design changes to the
Background panel in System Settings (post Malta) The current spec
and designs are here:
https://wiki.ubuntu. com/Appearance# Phone.2BAC8- tablet Related bugs:
https://bugs.launchpad .net/ubuntu/ +source/ ubuntu- system-
settings/+bug/1297418 https:/ /bugs.launchpad .net/ubuntu- autopilot-
tests/+bug/1322074 Changes: Removed all functionality and code
related to changing scope background. Added autopilot tests. New
header design that collapses/expands. Not part of this branch:
Scaling in preview has not been implemented. It should have its own
branch. After rotating the phone in system settings, the default
background will appear to have been deselected. This is because the
current background is now the default tablet background. Confirmed
on mako. Will file bug. Thank you. (LP: #1322074)
[ Iain Lane ]
* Add 1 gu of spacing between rows in the main screen's grid
[ Pawel Stolowski ]
* Added X-Ubuntu-Default- Department- ID key to the desktop file. This
is required by click scope to support departments for preinstalled
applications.
[ Ubuntu daily release ]
* debian/ubuntu- system- settings. maintscript: auto-update to released
version
[ Iain Lane ]
* Remove leftover /etc/apparmor.d/usr.bin. system- settings conffile.
* Use the last updated date returned by 'Info' on system-image's D-Bus
interface instead of reading a file on the fs, which is an
implementation detail.
* Clean up AP tests to try to fix test failures. Run ofono and system-
image tests on the about page under mocks.
Alberto Mardegan (mardy) wrote : | # |
I marked the strings as translatable.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:820
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Ken VanDine (ken-vandine) wrote : | # |
I suspect the author line in the new QML files should be mardy instead of seb128.
Would there be any value in providing (in the future) TrustStoreModel bindings separately, or is use outside of system-settings unlikely?
- 821. By Alberto Mardegan
-
Fix author line
Alberto Mardegan (mardy) wrote : | # |
I fixed the author lines.
As for providing the TrustStoreModel bindings separately, I don't think it will be needed, since I'd thing that most trusted helpers would use the C++ bindings. But anyway this is something that can be changed later, should the need arise.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:821
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Thomas Voß (thomas-voss) wrote : | # |
I'm not sure about the general system settings policy, but build-time tests for the model are missing. Having standalone tests in place that we can use to exercise the model should help a lot in tracking down errors.
- 822. By Alberto Mardegan
-
Added unit tests
- 823. By Alberto Mardegan
-
merge from trunk
[ Michael Terry ]
* Only turn the wizard off if the user actively presses the Finished
button. That is, don't disable the wizard if we crash or the user
turns off the phone while still in the wizard. (LP: #1348577)
* Avoid making a method call that can return undefined results,
potentially causing password or language setting to not work
correctly.
[ jonas-drange ]
* [reset] bring back reset and hook it up to the backend. Added happy
path tests.
[ CI bot ]
* Resync trunk
[ Oliver Grawert ]
* Tie activity of the developer mode toggle to the selected password
type (only make the toggle switch active if the password type is not
set to "swipe")
[ Michael Terry ]
* Stop the wizard from crashing on exit by not redefining a property
that blew up when the wifi list was emptied. (LP: #1335298)
[ John R. Lenton ]
* Fix the depends to be on the right gsettings schema version.
[ Ken VanDine ]
* handle qtdeclarative5-ubuntu- content1 package rename
[ John R. Lenton ]
* This makes the category grid a little bit looser. (LP: #1352866)
[ Iain Lane ]
* Add a 'reset' API so that plugins can reset their settings to
default. They can do this in one of two ways: by providing a
function 'bool reset()' in their 'plugin' which returns true or by
providing a top-level javascript function 'reset' in their page
component.
[ Diego Sarmentero ]
* Add a plugin for enabling/disabling push notification per-app.
[ Roberto Alsina ]
* Add a plugin for enabling/disabling push notification per-app.
[ Mathieu Trudel-Lapierre ]
* Bluetooth UI redesign:
- Redesign the UI to follow the specification.
- Create devices only when attempting to connect.
- Bugfixes for displaying icons for devices.
- Show new icons for common device types.
- Support more device types.
- Show Trusted (known) devices in a separate list from the devices detected
by Discovery.
- Allow for a way to forget (unpair) a known device. (LP: #1336197)
- Follow the state of the Bluetooth adapter for whether Bluetooth
is enabled. (LP: #1272317)
- Miscellaneous bug fixes.
* debian/control: add Build-Depends on libqtdbusmock1-dev and
libqtdbustest1-dev for Bluetooth testing.
[ Ken VanDine ]
* [sound] exposed silentMode and vibrate settings
* Set anchorToKeyboard in the MainView, to fix issues found on screens
with multiple input fields. Most noticeable is in the apneditor
branch that is still being worked on.
[ CI bot ]
* Resync trunk
[ Ubuntu daily release ]
* New rebuild forced
[ Michael Terry ]
* Make the buttons in the password change dialog conform to the color
scheme preferred by design (green for accept buttons, grey for
neutral buttons).
[ jonas-drange ]
* [cellular] add sim name editor for dual sim scenario
[ CI bot ]
* Resync trunk
[ Iain Lane ]
* Move the arch-restricted Depends on qtmir to the wizard binary
package which uses it, and lower it to Recommends since there's a
non-mir path too.
[ Adolfo Jayme Barrientos ]
* Fix typo in user-facing strings: user → use (LP: #1349855)
[ Michael Terry ]
* Stop storing user's password in plaintext. Instead, store it in PAM
the same way the Desktop images do. Also, expose the UI to set it.
(LP: #1234983)
[ Gerry Boland ]
* Use QtMir instead of the older Unity-Mir Need for Qt Compositor.
[ Iain Lane ]
* Update pot
[ Michał Sawicz ]
* Use QtMir instead of the older Unity-Mir Need for Qt Compositor.
[ Robert Bruce Park ]
* Add a build-dep on qtmir to avoid building on inappropriate arches.
[ Ubuntu daily release ]
* New rebuild forced
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:822
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:823
http://
Executed test runs:
None: http://
FAILURE: http://
FAILURE: http://
None: http://
FAILURE: http://
None: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Alberto Mardegan (mardy) wrote : | # |
The build times out but otherwise is successful:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:823
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:823
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Ken VanDine (ken-vandine) wrote : | # |
Thanks, looks good.
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2014-08-06 05:57:42 +0000 |
3 | +++ debian/control 2014-08-11 11:06:25 +0000 |
4 | @@ -17,6 +17,7 @@ |
5 | libpolkit-agent-1-dev, |
6 | libqmenumodel-dev, |
7 | libtimezonemap1-dev (>= 0.4.1), |
8 | + libtrust-store-dev, |
9 | libunity-api-dev, |
10 | libupower-glib-dev, |
11 | pkg-config, |
12 | |
13 | === added file 'plugins/security-privacy/AppAccess.qml' |
14 | --- plugins/security-privacy/AppAccess.qml 1970-01-01 00:00:00 +0000 |
15 | +++ plugins/security-privacy/AppAccess.qml 2014-08-11 11:06:25 +0000 |
16 | @@ -0,0 +1,74 @@ |
17 | +/* |
18 | + * Copyright (C) 2013 Canonical Ltd |
19 | + * |
20 | + * This program is free software: you can redistribute it and/or modify |
21 | + * it under the terms of the GNU General Public License version 3 as |
22 | + * published by the Free Software Foundation. |
23 | + * |
24 | + * This program is distributed in the hope that it will be useful, |
25 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
27 | + * GNU General Public License for more details. |
28 | + * |
29 | + * You should have received a copy of the GNU General Public License |
30 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
31 | + * |
32 | + * Authors: |
33 | + * Alberto Mardegan <alberto.mardegan@canonical.com> |
34 | + */ |
35 | + |
36 | +import QtQuick 2.0 |
37 | +import Ubuntu.Components 0.1 |
38 | +import Ubuntu.Components.ListItems 0.1 as ListItem |
39 | +import Ubuntu.SystemSettings.SecurityPrivacy 1.0 |
40 | +import SystemSettings 1.0 |
41 | + |
42 | +ItemPage { |
43 | + id: root |
44 | + title: i18n.tr("Other app access") |
45 | + |
46 | + Column { |
47 | + anchors.left: parent.left |
48 | + anchors.right: parent.right |
49 | + |
50 | + ListItem.Caption { |
51 | + text: i18n.tr("Apps that you have granted and have requested access to:") |
52 | + } |
53 | + |
54 | + ListModel { |
55 | + id: appsModel |
56 | + ListElement { |
57 | + name: QT_TR_NOOP("Camera") |
58 | + caption: QT_TR_NOOP("Apps that have requested access to your camera") |
59 | + trustStoreService: "CameraService" |
60 | + } |
61 | + ListElement { |
62 | + name: QT_TR_NOOP("Mic") |
63 | + caption: QT_TR_NOOP("Apps that have requested access to your mic") |
64 | + trustStoreService: "PulseAudio" |
65 | + } |
66 | + } |
67 | + |
68 | + Repeater { |
69 | + model: appsModel |
70 | + |
71 | + ListItem.SingleValue { |
72 | + text: model.name |
73 | + enabled: trustStoreModel.count > 0 |
74 | + value: trustStoreModel.count > 0 ? |
75 | + i18n.tr("%1/%2").arg(trustStoreModel.grantedCount).arg(trustStoreModel.count) : |
76 | + i18n.tr("0") |
77 | + onClicked: pageStack.push(Qt.resolvedUrl("AppAccessControl.qml"), { |
78 | + "title": i18n.tr(model.name), |
79 | + "caption": i18n.tr(model.caption), |
80 | + "model": trustStoreModel, |
81 | + }) |
82 | + |
83 | + TrustStoreModel { |
84 | + id: trustStoreModel |
85 | + serviceName: model.trustStoreService |
86 | + } |
87 | + } |
88 | + } |
89 | + } |
90 | +} |
91 | |
92 | === added file 'plugins/security-privacy/AppAccessControl.qml' |
93 | --- plugins/security-privacy/AppAccessControl.qml 1970-01-01 00:00:00 +0000 |
94 | +++ plugins/security-privacy/AppAccessControl.qml 2014-08-11 11:06:25 +0000 |
95 | @@ -0,0 +1,53 @@ |
96 | +/* |
97 | + * Copyright (C) 2013 Canonical Ltd |
98 | + * |
99 | + * This program is free software: you can redistribute it and/or modify |
100 | + * it under the terms of the GNU General Public License version 3 as |
101 | + * published by the Free Software Foundation. |
102 | + * |
103 | + * This program is distributed in the hope that it will be useful, |
104 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
105 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
106 | + * GNU General Public License for more details. |
107 | + * |
108 | + * You should have received a copy of the GNU General Public License |
109 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
110 | + * |
111 | + * Authors: |
112 | + * Alberto Mardegan <alberto.mardegan@canonical.com> |
113 | + */ |
114 | + |
115 | +import QtQuick 2.0 |
116 | +import Ubuntu.Components 0.1 |
117 | +import Ubuntu.Components.ListItems 0.1 as ListItem |
118 | +import SystemSettings 1.0 |
119 | + |
120 | +ItemPage { |
121 | + id: root |
122 | + |
123 | + property alias model: repeater.model |
124 | + property alias caption: captionLabel.text |
125 | + |
126 | + Column { |
127 | + anchors.left: parent.left |
128 | + anchors.right: parent.right |
129 | + |
130 | + ListItem.Caption { |
131 | + id: captionLabel |
132 | + } |
133 | + |
134 | + Repeater { |
135 | + id: repeater |
136 | + |
137 | + ListItem.Standard { |
138 | + text: model.applicationName |
139 | + iconSource: "image://theme/" + model.iconName |
140 | + control: Switch { |
141 | + id: welcomeStatsSwitch |
142 | + checked: model.granted |
143 | + onClicked: root.model.setEnabled(index, !model.granted) |
144 | + } |
145 | + } |
146 | + } |
147 | + } |
148 | +} |
149 | |
150 | === modified file 'plugins/security-privacy/CMakeLists.txt' |
151 | --- plugins/security-privacy/CMakeLists.txt 2014-07-13 23:57:50 +0000 |
152 | +++ plugins/security-privacy/CMakeLists.txt 2014-08-11 11:06:25 +0000 |
153 | @@ -17,6 +17,8 @@ |
154 | add_definitions(-DHELPER_EXEC="${PLUG_DIR}/UbuntuSecurityPrivacyHelper" -DQT_NO_KEYWORDS) |
155 | |
156 | set(QML_SOURCES |
157 | + AppAccess.qml |
158 | + AppAccessControl.qml |
159 | Dash.qml |
160 | Location.qml |
161 | LockSecurity.qml |
162 | @@ -26,13 +28,30 @@ |
163 | |
164 | set(PANEL_SOURCES |
165 | plugin.cpp |
166 | + plugin.h |
167 | securityprivacy.cpp |
168 | + securityprivacy.h |
169 | + trust-store-model.cpp |
170 | + trust-store-model.h |
171 | + ${QML_SOURCES} |
172 | ) |
173 | |
174 | add_library(UbuntuSecurityPrivacyPanel MODULE ${PANEL_SOURCES} ${QML_SOURCES}) |
175 | qt5_use_modules(UbuntuSecurityPrivacyPanel Qml Quick DBus) |
176 | |
177 | -target_link_libraries (UbuntuSecurityPrivacyPanel uss-accountsservice ${ACCOUNTSSERVICE_LDFLAGS}) |
178 | +pkg_check_modules(TRUST_STORE REQUIRED trust-store) |
179 | + |
180 | +include_directories( |
181 | + ${CMAKE_CURRENT_BINARY_DIR} |
182 | + ${TRUST_STORE_INCLUDE_DIRS} |
183 | +) |
184 | + |
185 | +set(PLUG_DIR ${PLUGIN_PRIVATE_MODULE_DIR}/Ubuntu/SystemSettings/SecurityPrivacy) |
186 | +target_link_libraries (UbuntuSecurityPrivacyPanel |
187 | + uss-accountsservice |
188 | + ${ACCOUNTSSERVICE_LDFLAGS} |
189 | + ${TRUST_STORE_LDFLAGS} |
190 | +) |
191 | install(TARGETS UbuntuSecurityPrivacyPanel UbuntuSecurityPrivacyHelper DESTINATION ${PLUG_DIR}) |
192 | install(FILES qmldir DESTINATION ${PLUG_DIR}) |
193 | install(FILES ${QML_SOURCES} DESTINATION ${PLUGIN_QML_DIR}/security-privacy) |
194 | |
195 | === modified file 'plugins/security-privacy/PageComponent.qml' |
196 | --- plugins/security-privacy/PageComponent.qml 2014-07-13 23:57:50 +0000 |
197 | +++ plugins/security-privacy/PageComponent.qml 2014-08-11 11:06:25 +0000 |
198 | @@ -173,7 +173,7 @@ |
199 | ListItem.SingleValue { |
200 | text: i18n.tr("Other app access") |
201 | progression: true |
202 | - visible: showAllUI |
203 | + onClicked: pageStack.push(Qt.resolvedUrl("AppAccess.qml")) |
204 | } |
205 | ListItem.SingleValue { |
206 | text: i18n.tr("Diagnostics") |
207 | |
208 | === modified file 'plugins/security-privacy/plugin.cpp' |
209 | --- plugins/security-privacy/plugin.cpp 2013-08-29 14:20:50 +0000 |
210 | +++ plugins/security-privacy/plugin.cpp 2014-08-11 11:06:25 +0000 |
211 | @@ -18,6 +18,7 @@ |
212 | |
213 | #include "plugin.h" |
214 | #include "securityprivacy.h" |
215 | +#include "trust-store-model.h" |
216 | |
217 | #include <QtQml/QtQml> |
218 | |
219 | @@ -25,6 +26,7 @@ |
220 | { |
221 | Q_ASSERT(uri == QLatin1String("Ubuntu.SystemSettings.SecurityPrivacy")); |
222 | qmlRegisterType<SecurityPrivacy>(uri, 1, 0, "UbuntuSecurityPrivacyPanel"); |
223 | + qmlRegisterType<TrustStoreModel>(uri, 1, 0, "TrustStoreModel"); |
224 | } |
225 | |
226 | void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
227 | |
228 | === added file 'plugins/security-privacy/trust-store-model.cpp' |
229 | --- plugins/security-privacy/trust-store-model.cpp 1970-01-01 00:00:00 +0000 |
230 | +++ plugins/security-privacy/trust-store-model.cpp 2014-08-11 11:06:25 +0000 |
231 | @@ -0,0 +1,333 @@ |
232 | +/* |
233 | + * Copyright (C) 2014 Canonical, Ltd. |
234 | + * |
235 | + * This program is free software; you can redistribute it and/or modify |
236 | + * it under the terms of the GNU General Public License as published by |
237 | + * the Free Software Foundation; version 3. |
238 | + * |
239 | + * This program is distributed in the hope that it will be useful, |
240 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
241 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
242 | + * GNU General Public License for more details. |
243 | + * |
244 | + * You should have received a copy of the GNU General Public License |
245 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
246 | + * |
247 | + * Authors: Alberto Mardegan <alberto.mardegan@canonical.com> |
248 | + */ |
249 | + |
250 | +#include "trust-store-model.h" |
251 | + |
252 | +#include <QDebug> |
253 | +#include <QList> |
254 | +#include <QMap> |
255 | +#include <QSet> |
256 | +#include <QSettings> |
257 | +#include <QStandardPaths> |
258 | + |
259 | +#include <core/trust/resolve.h> |
260 | +#include <core/trust/store.h> |
261 | + |
262 | +class Application |
263 | +{ |
264 | +public: |
265 | + Application() {} |
266 | + |
267 | + void setId(const QString &id) { |
268 | + this->id = id; |
269 | + |
270 | + QString localShare = |
271 | + QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); |
272 | + QSettings desktopFile(QString("%1/applications/%2.desktop"). |
273 | + arg(localShare).arg(id), |
274 | + QSettings::IniFormat); |
275 | + desktopFile.beginGroup("Desktop Entry"); |
276 | + displayName = desktopFile.value("Name").toString(); |
277 | + iconName = desktopFile.value("Icon").toString(); |
278 | + } |
279 | + |
280 | + void addRequest(const core::trust::Request &request) { |
281 | + if (request.answer == core::trust::Request::Answer::granted) { |
282 | + grantedFeatures.insert(request.feature.value); |
283 | + } else { |
284 | + grantedFeatures.remove(request.feature.value); |
285 | + } |
286 | + } |
287 | + |
288 | + bool hasGrants() const { return !grantedFeatures.isEmpty(); } |
289 | + |
290 | + QString id; |
291 | + QString displayName; |
292 | + QString iconName; |
293 | + QSet<std::uint64_t> grantedFeatures; |
294 | +}; |
295 | + |
296 | +class TrustStoreModelPrivate: public QObject |
297 | +{ |
298 | + Q_OBJECT |
299 | + Q_DECLARE_PUBLIC(TrustStoreModel) |
300 | + |
301 | +public: |
302 | + TrustStoreModelPrivate(TrustStoreModel *model); |
303 | + ~TrustStoreModelPrivate(); |
304 | + |
305 | + void update(); |
306 | + void updateRow(int row); |
307 | + void updateGrantedCount(); |
308 | + |
309 | +private: |
310 | + QHash<int, QByteArray> roleNames; |
311 | + bool componentCompleted; |
312 | + QString serviceName; |
313 | + int grantedCount; |
314 | + std::shared_ptr<core::trust::Store> trustStore; |
315 | + QList<Application> applications; |
316 | + mutable TrustStoreModel *q_ptr; |
317 | +}; |
318 | + |
319 | +TrustStoreModelPrivate::TrustStoreModelPrivate(TrustStoreModel *model): |
320 | + QObject(model), |
321 | + componentCompleted(false), |
322 | + grantedCount(0), |
323 | + q_ptr(model) |
324 | +{ |
325 | +} |
326 | + |
327 | +TrustStoreModelPrivate::~TrustStoreModelPrivate() |
328 | +{ |
329 | +} |
330 | + |
331 | +void TrustStoreModelPrivate::update() |
332 | +{ |
333 | + Q_Q(TrustStoreModel); |
334 | + |
335 | + if (!componentCompleted) return; |
336 | + |
337 | + q->beginResetModel(); |
338 | + |
339 | + if (trustStore) { |
340 | + trustStore.reset(); |
341 | + } |
342 | + |
343 | + trustStore = core::trust::resolve_store_in_session_with_name( |
344 | + serviceName.toStdString()); |
345 | + |
346 | + /* Test is the trustStore is valid; ideally, there should be an API on the |
347 | + * trust-store to check this. See |
348 | + * https://bugs.launchpad.net/bugs/1348215 */ |
349 | + try { |
350 | + auto query = trustStore->query(); |
351 | + } catch (std::exception &e) { |
352 | + qWarning() << "Exception " << e.what(); |
353 | + trustStore.reset(); |
354 | + } |
355 | + |
356 | + QMap<QString,Application> appMap; |
357 | + |
358 | + if (trustStore) { |
359 | + auto query = trustStore->query(); |
360 | + query->execute(); |
361 | + |
362 | + while (query->status() != core::trust::Store::Query::Status::eor) { |
363 | + auto r = query->current(); |
364 | + |
365 | + QString applicationId = QString::fromStdString(r.from); |
366 | + |
367 | + Application &app = appMap[applicationId]; |
368 | + app.setId(applicationId); |
369 | + app.addRequest(r); |
370 | + |
371 | + query->next(); |
372 | + } |
373 | + } |
374 | + |
375 | + applications = appMap.values(); |
376 | + updateGrantedCount(); |
377 | + |
378 | + q->endResetModel(); |
379 | +} |
380 | + |
381 | +void TrustStoreModelPrivate::updateRow(int row) |
382 | +{ |
383 | + Q_Q(TrustStoreModel); |
384 | + |
385 | + Q_ASSERT(trustStore); |
386 | + Q_ASSERT(row >= 0 && row < d->applications.count()); |
387 | + |
388 | + Application &app = applications[row]; |
389 | + app.grantedFeatures.clear(); |
390 | + |
391 | + auto query = trustStore->query(); |
392 | + query->for_application_id(app.id.toStdString()); |
393 | + query->execute(); |
394 | + |
395 | + while (query->status() != core::trust::Store::Query::Status::eor) { |
396 | + auto r = query->current(); |
397 | + |
398 | + app.addRequest(r); |
399 | + |
400 | + query->next(); |
401 | + } |
402 | + |
403 | + updateGrantedCount(); |
404 | + |
405 | + /* Let the model emit the change notification */ |
406 | + QModelIndex index = q->index(row); |
407 | + q->dataChanged(index, index); |
408 | +} |
409 | + |
410 | +void TrustStoreModelPrivate::updateGrantedCount() |
411 | +{ |
412 | + Q_Q(TrustStoreModel); |
413 | + |
414 | + int count = 0; |
415 | + |
416 | + Q_FOREACH(const Application &app, applications) { |
417 | + if (app.hasGrants()) count++; |
418 | + } |
419 | + |
420 | + if (count != grantedCount) { |
421 | + grantedCount = count; |
422 | + Q_EMIT q->grantedCountChanged(); |
423 | + } |
424 | +} |
425 | + |
426 | +TrustStoreModel::TrustStoreModel(QObject *parent): |
427 | + QAbstractListModel(parent), |
428 | + d_ptr(new TrustStoreModelPrivate(this)) |
429 | +{ |
430 | + Q_D(TrustStoreModel); |
431 | + d->roleNames[Qt::DisplayRole] = "applicationName"; |
432 | + d->roleNames[ApplicationIdRole] = "applicationId"; |
433 | + d->roleNames[IconNameRole] = "iconName"; |
434 | + d->roleNames[GrantedRole] = "granted"; |
435 | + |
436 | + QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &,int,int)), |
437 | + this, SIGNAL(countChanged())); |
438 | + QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &,int,int)), |
439 | + this, SIGNAL(countChanged())); |
440 | + QObject::connect(this, SIGNAL(modelReset()), |
441 | + this, SIGNAL(countChanged())); |
442 | +} |
443 | + |
444 | +TrustStoreModel::~TrustStoreModel() |
445 | +{ |
446 | +} |
447 | + |
448 | +void TrustStoreModel::classBegin() |
449 | +{ |
450 | +} |
451 | + |
452 | +void TrustStoreModel::componentComplete() |
453 | +{ |
454 | + Q_D(TrustStoreModel); |
455 | + d->componentCompleted = true; |
456 | + d->update(); |
457 | +} |
458 | + |
459 | +void TrustStoreModel::setServiceName(const QString &serviceName) |
460 | +{ |
461 | + Q_D(TrustStoreModel); |
462 | + |
463 | + if (serviceName == d->serviceName) return; |
464 | + d->serviceName = serviceName; |
465 | + d->update(); |
466 | + Q_EMIT serviceNameChanged(); |
467 | +} |
468 | + |
469 | +QString TrustStoreModel::serviceName() const |
470 | +{ |
471 | + Q_D(const TrustStoreModel); |
472 | + return d->serviceName; |
473 | +} |
474 | + |
475 | +int TrustStoreModel::grantedCount() const |
476 | +{ |
477 | + Q_D(const TrustStoreModel); |
478 | + return d->grantedCount; |
479 | +} |
480 | + |
481 | +void TrustStoreModel::setEnabled(int row, bool enabled) |
482 | +{ |
483 | + Q_D(TrustStoreModel); |
484 | + |
485 | + if (Q_UNLIKELY(!d->trustStore)) { |
486 | + qWarning() << "Trust store is NULL on setEnabled call"; |
487 | + return; |
488 | + } |
489 | + |
490 | + if (Q_UNLIKELY(row >= d->applications.count())) return; |
491 | + |
492 | + const Application &app = d->applications.at(row); |
493 | + |
494 | + core::trust::Request r; |
495 | + r.from = app.id.toStdString(); |
496 | + r.feature = core::trust::Feature(core::trust::Request::default_feature); |
497 | + r.answer = enabled ? |
498 | + core::trust::Request::Answer::granted : core::trust::Request::Answer::denied; |
499 | + r.when = std::chrono::system_clock::now(); |
500 | + |
501 | + d->trustStore->add(r); |
502 | + |
503 | + /* When disabling, we must disable all the features */ |
504 | + if (!enabled) { |
505 | + Q_FOREACH(std::int64_t feature, app.grantedFeatures) { |
506 | + /* Skip the default feature, we already disabled it */ |
507 | + if (feature == core::trust::Request::default_feature) continue; |
508 | + |
509 | + r.feature = core::trust::Feature(feature); |
510 | + d->trustStore->add(r); |
511 | + } |
512 | + } |
513 | + |
514 | + /* Reload the application from the trust store */ |
515 | + d->updateRow(row); |
516 | +} |
517 | + |
518 | +QVariant TrustStoreModel::get(int row, const QString &roleName) const |
519 | +{ |
520 | + int role = roleNames().key(roleName.toLatin1(), -1); |
521 | + return data(index(row), role); |
522 | +} |
523 | + |
524 | +int TrustStoreModel::rowCount(const QModelIndex &parent) const |
525 | +{ |
526 | + Q_D(const TrustStoreModel); |
527 | + Q_UNUSED(parent); |
528 | + return d->applications.count(); |
529 | +} |
530 | + |
531 | +QVariant TrustStoreModel::data(const QModelIndex &index, int role) const |
532 | +{ |
533 | + Q_D(const TrustStoreModel); |
534 | + |
535 | + if (index.row() >= d->applications.count()) return QVariant(); |
536 | + |
537 | + const Application &app = d->applications.at(index.row()); |
538 | + QVariant ret; |
539 | + |
540 | + switch (role) { |
541 | + case Qt::DisplayRole: |
542 | + ret = app.displayName; |
543 | + break; |
544 | + case IconNameRole: |
545 | + ret = app.iconName; |
546 | + break; |
547 | + case ApplicationIdRole: |
548 | + ret = app.id; |
549 | + break; |
550 | + case GrantedRole: |
551 | + ret = app.hasGrants(); |
552 | + break; |
553 | + } |
554 | + |
555 | + return ret; |
556 | +} |
557 | + |
558 | +QHash<int, QByteArray> TrustStoreModel::roleNames() const |
559 | +{ |
560 | + Q_D(const TrustStoreModel); |
561 | + return d->roleNames; |
562 | +} |
563 | + |
564 | +#include "trust-store-model.moc" |
565 | |
566 | === added file 'plugins/security-privacy/trust-store-model.h' |
567 | --- plugins/security-privacy/trust-store-model.h 1970-01-01 00:00:00 +0000 |
568 | +++ plugins/security-privacy/trust-store-model.h 2014-08-11 11:06:25 +0000 |
569 | @@ -0,0 +1,74 @@ |
570 | +/* |
571 | + * Copyright (C) 2014 Canonical, Ltd. |
572 | + * |
573 | + * This program is free software; you can redistribute it and/or modify |
574 | + * it under the terms of the GNU General Public License as published by |
575 | + * the Free Software Foundation; version 3. |
576 | + * |
577 | + * This program is distributed in the hope that it will be useful, |
578 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
579 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
580 | + * GNU General Public License for more details. |
581 | + * |
582 | + * You should have received a copy of the GNU General Public License |
583 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
584 | + * |
585 | + * Authors: Alberto Mardegan <alberto.mardegan@canonical.com> |
586 | + */ |
587 | + |
588 | +#ifndef TRUST_STORE_MODEL_H |
589 | +#define TRUST_STORE_MODEL_H |
590 | + |
591 | +#include <QAbstractListModel> |
592 | +#include <QQmlParserStatus> |
593 | +#include <QString> |
594 | + |
595 | +class TrustStoreModelPrivate; |
596 | + |
597 | +class TrustStoreModel: public QAbstractListModel, public QQmlParserStatus |
598 | +{ |
599 | + Q_OBJECT |
600 | + Q_INTERFACES(QQmlParserStatus) |
601 | + Q_PROPERTY(QString serviceName READ serviceName WRITE setServiceName |
602 | + NOTIFY serviceNameChanged) |
603 | + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
604 | + Q_PROPERTY(int grantedCount READ grantedCount NOTIFY grantedCountChanged) |
605 | + |
606 | +public: |
607 | + explicit TrustStoreModel(QObject *parent = 0); |
608 | + ~TrustStoreModel(); |
609 | + |
610 | + enum Roles { |
611 | + ApplicationIdRole = Qt::UserRole + 1, |
612 | + IconNameRole, |
613 | + GrantedRole, |
614 | + }; |
615 | + |
616 | + void setServiceName(const QString &serviceName); |
617 | + QString serviceName() const; |
618 | + |
619 | + int grantedCount() const; |
620 | + |
621 | + Q_INVOKABLE void setEnabled(int row, bool enabled); |
622 | + Q_INVOKABLE QVariant get(int row, const QString &roleName) const; |
623 | + |
624 | + // reimplemented virtual methods |
625 | + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; |
626 | + QVariant data(const QModelIndex &index, |
627 | + int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; |
628 | + QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE; |
629 | + |
630 | + void classBegin() Q_DECL_OVERRIDE; |
631 | + void componentComplete() Q_DECL_OVERRIDE; |
632 | + |
633 | +Q_SIGNALS: |
634 | + void serviceNameChanged(); |
635 | + void countChanged(); |
636 | + void grantedCountChanged(); |
637 | + |
638 | +private: |
639 | + TrustStoreModelPrivate *d_ptr; |
640 | + Q_DECLARE_PRIVATE(TrustStoreModel) |
641 | +}; |
642 | + |
643 | +#endif // TRUST_STORE_MODEL_H |
644 | |
645 | === modified file 'po/CMakeLists.txt' |
646 | --- po/CMakeLists.txt 2013-12-31 19:30:03 +0000 |
647 | +++ po/CMakeLists.txt 2014-08-11 11:06:25 +0000 |
648 | @@ -23,6 +23,7 @@ |
649 | --copyright=\"Canonical Ltd.\" |
650 | --package-name ubuntu-system-settings |
651 | --qt --c++ --add-comments=TRANSLATORS |
652 | + --keyword=QT_TR_NOOP |
653 | --keyword=tr --keyword=tr:1,2 --from-code=UTF-8 |
654 | ${QMLFILES} "${CMAKE_CURRENT_BINARY_DIR}/settings.js" |
655 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" |
656 | |
657 | === modified file 'tests/plugins/CMakeLists.txt' |
658 | --- tests/plugins/CMakeLists.txt 2014-07-31 13:43:09 +0000 |
659 | +++ tests/plugins/CMakeLists.txt 2014-08-11 11:06:25 +0000 |
660 | @@ -1,2 +1,3 @@ |
661 | +add_subdirectory(security-privacy) |
662 | add_subdirectory(system-update) |
663 | add_subdirectory(bluetooth) |
664 | |
665 | === added directory 'tests/plugins/security-privacy' |
666 | === added file 'tests/plugins/security-privacy/CMakeLists.txt' |
667 | --- tests/plugins/security-privacy/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
668 | +++ tests/plugins/security-privacy/CMakeLists.txt 2014-08-11 11:06:25 +0000 |
669 | @@ -0,0 +1,11 @@ |
670 | +set(XVFB_CMD xvfb-run -a -s "-screen 0 640x480x24") |
671 | +include_directories(${CMAKE_CURRENT_BINARY_DIR} ../../../plugins/security-privacy) |
672 | +add_definitions(-DTESTS) |
673 | + |
674 | +add_executable(tst-trust-store-model |
675 | + tst_trust_store_model.cpp |
676 | + ../../../plugins/security-privacy/trust-store-model.cpp |
677 | +) |
678 | + |
679 | +qt5_use_modules(tst-trust-store-model Core DBus Qml Test) |
680 | +add_test(NAME tst-trust-store-model COMMAND ${XVFB_CMD} ${CMAKE_CURRENT_BINARY_DIR}/tst-trust-store-model) |
681 | |
682 | === added file 'tests/plugins/security-privacy/tst_trust_store_model.cpp' |
683 | --- tests/plugins/security-privacy/tst_trust_store_model.cpp 1970-01-01 00:00:00 +0000 |
684 | +++ tests/plugins/security-privacy/tst_trust_store_model.cpp 2014-08-11 11:06:25 +0000 |
685 | @@ -0,0 +1,394 @@ |
686 | +/* |
687 | + * Copyright 2013 Canonical Ltd. |
688 | + * |
689 | + * This library is free software; you can redistribute it and/or |
690 | + * modify it under the terms of version 3 of the GNU Lesser General Public |
691 | + * License as published by the Free Software Foundation. |
692 | + * |
693 | + * This program is distributed in the hope that it will be useful, |
694 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
695 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
696 | + * General Public License for more details. |
697 | + * |
698 | + * You should have received a copy of the GNU Lesser General Public |
699 | + * License along with this library; if not, write to the |
700 | + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
701 | + * Boston, MA 02110-1301, USA. |
702 | + */ |
703 | + |
704 | +#include <QTest> |
705 | +#include <QSignalSpy> |
706 | +#include <QtQml> |
707 | + |
708 | +#include <core/trust/resolve.h> |
709 | +#include <core/trust/store.h> |
710 | + |
711 | +#include "trust-store-model.h" |
712 | + |
713 | +/* mocking trust-store { */ |
714 | +namespace |
715 | +{ |
716 | +namespace mock |
717 | +{ |
718 | +struct Store: public core::trust::Store |
719 | +{ |
720 | + std::string m_name; |
721 | + std::list<core::trust::Request> m_allRequests; |
722 | + static std::shared_ptr<mock::Store> m_instance; |
723 | + |
724 | + Store() |
725 | + { |
726 | + } |
727 | + |
728 | + ~Store() |
729 | + { |
730 | + } |
731 | + |
732 | + static void setInstance(std::shared_ptr<mock::Store> store) { |
733 | + m_instance = store; |
734 | + } |
735 | + |
736 | + struct Query: public core::trust::Store::Query |
737 | + { |
738 | + mock::Store *m_store; |
739 | + std::list<core::trust::Request> m_requests; |
740 | + std::list<core::trust::Request>::iterator m_it; |
741 | + std::string m_applicationFilter; |
742 | + core::trust::Store::Query::Status m_status; |
743 | + |
744 | + Query(mock::Store *store): |
745 | + m_store(store) |
746 | + { |
747 | + } |
748 | + |
749 | + ~Query() |
750 | + { |
751 | + } |
752 | + |
753 | + void all() |
754 | + { |
755 | + m_applicationFilter.clear(); |
756 | + } |
757 | + |
758 | + core::trust::Request current() |
759 | + { |
760 | + if (m_it == m_requests.end()) |
761 | + { |
762 | + throw core::trust::Store::Query::Errors::NoCurrentResult{}; |
763 | + } |
764 | + |
765 | + return *m_it; |
766 | + } |
767 | + |
768 | + void erase() |
769 | + { |
770 | + } |
771 | + |
772 | + void execute() |
773 | + { |
774 | + if (m_applicationFilter.empty()) { |
775 | + m_requests = m_store->m_allRequests; |
776 | + } else { |
777 | + m_requests.clear(); |
778 | + for (auto it = m_store->m_allRequests.begin(); |
779 | + it != m_store->m_allRequests.end(); |
780 | + it++) { |
781 | + if (it->from == m_applicationFilter) { |
782 | + m_requests.push_back(*it); |
783 | + } |
784 | + } |
785 | + } |
786 | + m_it = m_requests.begin(); |
787 | + m_status = (m_it == m_requests.end()) ? |
788 | + core::trust::Store::Query::Status::eor : |
789 | + core::trust::Store::Query::Status::has_more_results; |
790 | + } |
791 | + |
792 | + void for_answer(core::trust::Request::Answer) |
793 | + { |
794 | + } |
795 | + |
796 | + void for_application_id(const std::string &id) |
797 | + { |
798 | + m_applicationFilter = id; |
799 | + } |
800 | + |
801 | + void for_feature(core::trust::Feature) |
802 | + { |
803 | + } |
804 | + |
805 | + void for_interval(const core::trust::Request::Timestamp &, |
806 | + const core::trust::Request::Timestamp &) |
807 | + { |
808 | + } |
809 | + |
810 | + void next() |
811 | + { |
812 | + m_it++; |
813 | + m_status = (m_it == m_requests.end()) ? |
814 | + core::trust::Store::Query::Status::eor : |
815 | + core::trust::Store::Query::Status::has_more_results; |
816 | + } |
817 | + |
818 | + core::trust::Store::Query::Status status() const |
819 | + { |
820 | + return m_status; |
821 | + } |
822 | + }; |
823 | + |
824 | + void add(const core::trust::Request &r) |
825 | + { |
826 | + m_allRequests.push_back(r); |
827 | + } |
828 | + |
829 | + void reset() |
830 | + { |
831 | + } |
832 | + |
833 | + std::shared_ptr<core::trust::Store::Query> query() |
834 | + { |
835 | + auto query = |
836 | + std::shared_ptr<core::trust::Store::Query>(new mock::Store::Query( |
837 | + this)); |
838 | + |
839 | + return query; |
840 | + } |
841 | +}; |
842 | +} |
843 | +} |
844 | + |
845 | +std::shared_ptr<mock::Store> mock::Store::m_instance; |
846 | + |
847 | +std::shared_ptr<core::trust::Store> core::trust::resolve_store_in_session_with_name( |
848 | + const std::string &name) |
849 | +{ |
850 | + if (name.empty()) |
851 | + throw Errors::ServiceNameMustNotBeEmpty{}; |
852 | + |
853 | + mock::Store::m_instance->m_name = name; |
854 | + return mock::Store::m_instance; |
855 | +} |
856 | +/* } mocking trust-store */ |
857 | + |
858 | +class TrustStoreModelTest: public QObject |
859 | +{ |
860 | + Q_OBJECT |
861 | + |
862 | +private Q_SLOTS: |
863 | + void initTestCase(); |
864 | + void init(); |
865 | + void cleanup(); |
866 | + void testEmpty(); |
867 | + void testList_data(); |
868 | + void testList(); |
869 | + void testAppEnabling_data(); |
870 | + void testAppEnabling(); |
871 | + |
872 | +private: |
873 | + QVariant get(const QAbstractListModel *model, int row, QString roleName); |
874 | + |
875 | +private: |
876 | + std::shared_ptr<mock::Store> m_store; |
877 | +}; |
878 | + |
879 | +QVariant TrustStoreModelTest::get(const QAbstractListModel *model, int row, |
880 | + QString roleName) |
881 | +{ |
882 | + QHash<int, QByteArray> roleNames = model->roleNames(); |
883 | + |
884 | + int role = roleNames.key(roleName.toLatin1(), -1); |
885 | + return model->data(model->index(row), role); |
886 | +} |
887 | + |
888 | +void TrustStoreModelTest::initTestCase() |
889 | +{ |
890 | + qmlRegisterType<TrustStoreModel>("TrustStore.Test", 0, 1, |
891 | + "TrustStoreModel"); |
892 | +} |
893 | + |
894 | +void TrustStoreModelTest::init() |
895 | +{ |
896 | + m_store = std::shared_ptr<mock::Store>(new mock::Store); |
897 | + mock::Store::setInstance(m_store); |
898 | +} |
899 | + |
900 | +void TrustStoreModelTest::cleanup() |
901 | +{ |
902 | + m_store.reset(); |
903 | +} |
904 | + |
905 | +void TrustStoreModelTest::testEmpty() |
906 | +{ |
907 | + QQmlEngine engine; |
908 | + QQmlComponent component(&engine); |
909 | + component.setData("import TrustStore.Test 0.1\n" |
910 | + "TrustStoreModel {\n" |
911 | + " serviceName: \"storeTest\"\n" |
912 | + "}", |
913 | + QUrl()); |
914 | + QObject *object = component.create(); |
915 | + QVERIFY(object != 0); |
916 | + QAbstractListModel *model = qobject_cast<QAbstractListModel*>(object); |
917 | + QVERIFY(model != 0); |
918 | + |
919 | + QCOMPARE(model->rowCount(), 0); |
920 | + |
921 | + QCOMPARE(m_store->m_name, std::string("storeTest")); |
922 | +} |
923 | + |
924 | +void TrustStoreModelTest::testList_data() |
925 | +{ |
926 | + QTest::addColumn<QStringList>("appIds"); |
927 | + QTest::addColumn<QList<bool> >("appGrants"); |
928 | + QTest::addColumn<QStringList>("expectedAppIds"); |
929 | + QTest::addColumn<QList<bool> >("expectedAppGrants"); |
930 | + |
931 | + QTest::newRow("No repetitions") << |
932 | + (QStringList() << "Calendar" << "Gallery" << "MyApp") << |
933 | + (QList<bool>() << true << false << true) << |
934 | + (QStringList() << "Calendar" << "Gallery" << "MyApp") << |
935 | + (QList<bool>() << true << false << true); |
936 | + |
937 | + QTest::newRow("With repetitions") << |
938 | + (QStringList() << "Calendar" << "Gallery" << "MyApp" << "Calendar") << |
939 | + (QList<bool>() << true << false << true << false) << |
940 | + (QStringList() << "Calendar" << "Gallery" << "MyApp") << |
941 | + (QList<bool>() << false << false << true); |
942 | +} |
943 | + |
944 | +void TrustStoreModelTest::testList() |
945 | +{ |
946 | + QFETCH(QStringList, appIds); |
947 | + QFETCH(QList<bool>, appGrants); |
948 | + QFETCH(QStringList, expectedAppIds); |
949 | + QFETCH(QList<bool>, expectedAppGrants); |
950 | + |
951 | + for (int i = 0; i < appIds.count(); i++) { |
952 | + core::trust::Request r; |
953 | + r.from = appIds[i].toStdString(); |
954 | + r.feature = core::trust::Feature(core::trust::Request::default_feature); |
955 | + r.answer = appGrants[i] ? |
956 | + core::trust::Request::Answer::granted : core::trust::Request::Answer::denied; |
957 | + r.when = std::chrono::system_clock::now(); |
958 | + m_store->add(r); |
959 | + } |
960 | + |
961 | + QQmlEngine engine; |
962 | + QQmlComponent component(&engine); |
963 | + component.setData("import TrustStore.Test 0.1\n" |
964 | + "TrustStoreModel {\n" |
965 | + " serviceName: \"storeTest\"\n" |
966 | + "}", |
967 | + QUrl()); |
968 | + QObject *object = component.create(); |
969 | + QVERIFY(object != 0); |
970 | + QAbstractListModel *model = qobject_cast<QAbstractListModel*>(object); |
971 | + QVERIFY(model != 0); |
972 | + |
973 | + QCOMPARE(model->rowCount(), expectedAppIds.count()); |
974 | + |
975 | + for (int i = 0; i < model->rowCount(); i++) { |
976 | + QCOMPARE(get(model, i, "applicationId").toString(), expectedAppIds[i]); |
977 | + QCOMPARE(get(model, i, "granted").toBool(), expectedAppGrants[i]); |
978 | + } |
979 | +} |
980 | + |
981 | +void TrustStoreModelTest::testAppEnabling_data() |
982 | +{ |
983 | + /* In this test the model will be pre-populated with three applications: |
984 | + * - Calendar: enabled |
985 | + * - Gallery: disabled |
986 | + * - MyApp: enabled |
987 | + * We change the enabled status of each one of them at a time, and we check |
988 | + * that the results are consistent. */ |
989 | + QTest::addColumn<int>("row"); |
990 | + QTest::addColumn<bool>("mustEnable"); |
991 | + QTest::addColumn<QStringList>("expectedEnabledApps"); |
992 | + |
993 | + QTest::newRow("Enabling Calendar") << |
994 | + 0 << true << |
995 | + (QStringList() << "Calendar" << "MyApp"); |
996 | + |
997 | + QTest::newRow("Disabling Calendar") << |
998 | + 0 << false << |
999 | + (QStringList() << "MyApp"); |
1000 | + |
1001 | + QTest::newRow("Enabling Gallery") << |
1002 | + 1 << true << |
1003 | + (QStringList() << "Calendar" << "Gallery" << "MyApp"); |
1004 | + |
1005 | + QTest::newRow("Disabling Gallery") << |
1006 | + 1 << false << |
1007 | + (QStringList() << "Calendar" << "MyApp"); |
1008 | + |
1009 | + QTest::newRow("Enabling MyApp") << |
1010 | + 2 << true << |
1011 | + (QStringList() << "Calendar" << "MyApp"); |
1012 | + |
1013 | + QTest::newRow("Disabling MyApp") << |
1014 | + 2 << false << |
1015 | + (QStringList() << "Calendar"); |
1016 | +} |
1017 | + |
1018 | +void TrustStoreModelTest::testAppEnabling() |
1019 | +{ |
1020 | + QFETCH(int, row); |
1021 | + QFETCH(bool, mustEnable); |
1022 | + QFETCH(QStringList, expectedEnabledApps); |
1023 | + |
1024 | + /* Pre-populate the model (update the comment on top of |
1025 | + * testAppEnabling_data() if you change this). */ |
1026 | + QStringList apps; |
1027 | + apps << "Calendar" << "Gallery" << "MyApp"; |
1028 | + QList<bool> appStatuses; |
1029 | + appStatuses << true << false << true; |
1030 | + for (int i = 0; i < apps.count(); i++) { |
1031 | + core::trust::Request r; |
1032 | + r.from = apps[i].toStdString(); |
1033 | + r.feature = core::trust::Feature(core::trust::Request::default_feature); |
1034 | + r.answer = appStatuses[i] ? |
1035 | + core::trust::Request::Answer::granted : core::trust::Request::Answer::denied; |
1036 | + r.when = std::chrono::system_clock::now(); |
1037 | + m_store->add(r); |
1038 | + } |
1039 | + |
1040 | + QQmlEngine engine; |
1041 | + QQmlComponent component(&engine); |
1042 | + component.setData("import TrustStore.Test 0.1\n" |
1043 | + "TrustStoreModel {\n" |
1044 | + " serviceName: \"storeTest\"\n" |
1045 | + "}", |
1046 | + QUrl()); |
1047 | + QObject *object = component.create(); |
1048 | + QVERIFY(object != 0); |
1049 | + QAbstractListModel *model = qobject_cast<QAbstractListModel*>(object); |
1050 | + QVERIFY(model != 0); |
1051 | + |
1052 | + QCOMPARE(model->rowCount(), apps.count()); |
1053 | + |
1054 | + QSignalSpy dataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&))); |
1055 | + |
1056 | + /* Enable or disable an application */ |
1057 | + bool ok; |
1058 | + ok = QMetaObject::invokeMethod(object, "setEnabled", |
1059 | + Q_ARG(int, row), |
1060 | + Q_ARG(bool, mustEnable)); |
1061 | + QVERIFY(ok); |
1062 | + |
1063 | + QTRY_COMPARE(dataChanged.count(), 1); |
1064 | + |
1065 | + QStringList enabledApps; |
1066 | + for (int i = 0; i < model->rowCount(); i++) { |
1067 | + if (get(model, i, "granted").toBool()) { |
1068 | + enabledApps.append(get(model, i, "applicationId").toString()); |
1069 | + } |
1070 | + } |
1071 | + |
1072 | + QCOMPARE(enabledApps, expectedEnabledApps); |
1073 | + QCOMPARE(object->property("grantedCount").toInt(), |
1074 | + expectedEnabledApps.count()); |
1075 | +} |
1076 | + |
1077 | +QTEST_MAIN(TrustStoreModelTest) |
1078 | + |
1079 | +#include "tst_trust_store_model.moc" |
PASSED: Continuous integration, rev:818 jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- ci/1065/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- utopic- touch/2508 jenkins. qa.ubuntu. com/job/ generic- mediumtests- utopic/ 2024 jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- utopic- amd64-ci/ 257 jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- utopic- armhf-ci/ 257 jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- utopic- armhf-ci/ 257/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- utopic- i386-ci/ 257 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- mako/2659 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/3751 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/3751/ artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 10436 jenkins. qa.ubuntu. com/job/ autopilot- testrunner- otto-utopic/ 1693 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/2277 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/2277/ artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- system- settings- ci/1065/ rebuild
http://