on xenial help sign in
Add support for guest sessions in unity8-greeter.

Adds support for the "hasGuestAccount" and "selectGuest" LightDM hints and does so largely by having the users model report a "*guest" user, rather than adding special support in qml. Approved by: Albert Astals Cid, Unity8 CI Bot

commitf8690684fed436d575c7fd0cf0be9a3a8f7726e8 authorMichael Terry <mike@mterry.name> dateTue 24 Jan 2017, 07:39 (10 years ago) parent446b941d31f301
19 files changed+295−64
plugins/LightDM/DBusGreeterList.cpp+3−3 plugins/LightDM/DBusGreeterList.h+1−1 plugins/LightDM/Greeter.cpp+38−3 plugins/LightDM/Greeter.h+6−2 plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp+7−4 plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp+27−22 plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h+1 plugins/LightDM/UsersModel.cpp+68−8 plugins/LightDM/UsersModel.h+4−1 plugins/LightDM/plugin.cpp+3−2 tests/mocks/AccountsService/CMakeLists.txt+1 tests/mocks/liblightdm/MockController.cpp+45−6 tests/mocks/liblightdm/MockController.h+14 tests/mocks/liblightdm/MockGreeter.cpp+10−3 tests/mocks/liblightdm/MockUsersModel.cpp+2 tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+2−3 tests/plugins/LightDM/IntegratedLightDM/dbus.cpp+1 tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+34−4 tests/qmltests/Greeter/tst_Greeter.qml+28−2
plugins/LightDM/DBusGreeterList.cpp+3−3
@@ -43,10 +43,10 @@ bool DBusGreeterList::entryIsLocked() const
4343 return !m_greeter->promptless();
4444}
4545
46−void DBusGreeterList::authenticationUserChangedHandler(const QString &user)
46+void DBusGreeterList::authenticationUserChangedHandler()
4747{
48− notifyPropertyChanged(QStringLiteral("ActiveEntry"), user);
49− Q_EMIT EntrySelected(user);
48+ notifyPropertyChanged(QStringLiteral("ActiveEntry"), m_greeter->authenticationUser());
49+ Q_EMIT EntrySelected(m_greeter->authenticationUser());
5050}
5151
5252void DBusGreeterList::promptlessChangedHandler()
plugins/LightDM/DBusGreeterList.h+1−1
@@ -47,7 +47,7 @@ Q_SIGNALS:
4747 void entryIsLockedChanged();
4848
4949private Q_SLOTS:
50− void authenticationUserChangedHandler(const QString &user);
50+ void authenticationUserChangedHandler();
5151 void promptlessChangedHandler();
5252
5353private:
plugins/LightDM/Greeter.cpp+38−3
@@ -17,8 +17,11 @@
1717
1818#include "Greeter.h"
1919#include "GreeterPrivate.h"
20+#include <QCoreApplication>
2021#include <libintl.h>
2122
23+static Greeter *singleton = nullptr;
24+
2225GreeterPrivate::GreeterPrivate(Greeter* parent)
2326 : m_greeter(new QLightDM::Greeter(parent)),
2427 m_active(false),
@@ -41,9 +44,26 @@ Greeter::Greeter(QObject* parent)
4144 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,
4245 this, &Greeter::authenticationCompleteFilter);
4346
47+ // Don't get stuck waiting for PAM as we shut down.
48+ connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
49+ d->m_greeter, &QLightDM::Greeter::cancelAuthentication);
50+
4451 d->m_greeter->connectSync();
4552}
4653
54+Greeter::~Greeter()
55+{
56+ singleton = nullptr;
57+}
58+
59+Greeter *Greeter::instance()
60+{
61+ if (!singleton) {
62+ singleton = new Greeter;
63+ }
64+ return singleton;
65+}
66+
4767bool Greeter::isActive() const
4868{
4969 Q_D(const Greeter);
@@ -86,7 +106,17 @@ bool Greeter::promptless() const
86106QString Greeter::selectUser() const
87107{
88108 Q_D(const Greeter);
89− return d->m_greeter->selectUserHint();
109+ if (hasGuestAccount() && d->m_greeter->selectGuestHint()) {
110+ return QStringLiteral("*guest");
111+ } else {
112+ return d->m_greeter->selectUserHint();
113+ }
114+}
115+
116+bool Greeter::hasGuestAccount() const
117+{
118+ Q_D(const Greeter);
119+ return d->m_greeter->hasGuestAccountHint();
90120}
91121
92122void Greeter::authenticate(const QString &username)
@@ -98,9 +128,14 @@ void Greeter::authenticate(const QString &username)
98128 Q_EMIT promptlessChanged();
99129 }
100130
101− d->m_greeter->authenticate(username);
131+ if (username == QStringLiteral("*guest")) {
132+ d->m_greeter->authenticateAsGuest();
133+ } else {
134+ d->m_greeter->authenticate(username);
135+ }
136+
102137 Q_EMIT isAuthenticatedChanged();
103− Q_EMIT authenticationUserChanged(username);
138+ Q_EMIT authenticationUserChanged();
104139}
105140
106141void Greeter::respond(const QString &response)
plugins/LightDM/Greeter.h+6−2
@@ -40,7 +40,8 @@ class Greeter : public QObject
4040 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)
4141
4242public:
43− explicit Greeter(QObject* parent=0);
43+ static Greeter *instance();
44+ virtual ~Greeter();
4445
4546 bool isActive() const;
4647 bool isAuthenticated() const;
@@ -48,6 +49,7 @@ public:
4849 QString defaultSessionHint() const;
4950 bool promptless() const;
5051 QString selectUser() const;
52+ bool hasGuestAccount() const;
5153
5254public Q_SLOTS:
5355 void authenticate(const QString &username=QString());
@@ -59,7 +61,7 @@ Q_SIGNALS:
5961 void showMessage(const QString &text, bool isError);
6062 void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
6163 void authenticationComplete();
62− void authenticationUserChanged(const QString &user);
64+ void authenticationUserChanged();
6365 void isActiveChanged();
6466 void isAuthenticatedChanged();
6567 void promptlessChanged();
@@ -71,6 +73,8 @@ Q_SIGNALS:
7173 void requestAuthenticationUser(const QString &user);
7274
7375protected:
76+ explicit Greeter(QObject* parent=0);
77+
7478 GreeterPrivate * const d_ptr;
7579
7680 Q_DECLARE_PRIVATE(Greeter)
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp+7−4
@@ -49,7 +49,7 @@ QString Greeter::authenticationUser() const
4949
5050bool Greeter::hasGuestAccountHint() const
5151{
52− return true;
52+ return false;
5353}
5454
5555QString Greeter::getHint(const QString &name) const
@@ -70,12 +70,12 @@ bool Greeter::hideUsersHint() const
7070
7171bool Greeter::showManualLoginHint() const
7272{
73− return true;
73+ return false;
7474}
7575
7676bool Greeter::showRemoteLoginHint() const
7777{
78− return true;
78+ return false;
7979}
8080
8181QString Greeter::selectUserHint() const
@@ -146,7 +146,10 @@ void Greeter::authenticateRemote(const QString &session, const QString &username
146146}
147147
148148void Greeter::cancelAuthentication()
149−{}
149+{
150+ Q_D(Greeter);
151+ d->cancelAuthentication();
152+}
150153
151154void Greeter::setLanguage (const QString &language)
152155{
plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp+27−22
@@ -206,6 +206,28 @@ public Q_SLOTS:
206206 }
207207 }
208208
209+ void cancelPam()
210+ {
211+ if (pamHandle != nullptr) {
212+ QFuture<int> pamFuture = futureWatcher.future();
213+ pam_handle *handle = pamHandle;
214+ pamHandle = nullptr; // to disable normal finishPam() handling
215+ pamFuture.cancel();
216+
217+ // Note the empty loop, we just want to clear the futures queue.
218+ // Any further prompts from the pam thread will be immediately
219+ // responded to/dismissed in handlePrompt().
220+ while (respond(QString()));
221+
222+ // Now let signal/slot handling happen so the thread can finish
223+ while (!pamFuture.isFinished()) {
224+ QCoreApplication::processEvents();
225+ }
226+
227+ pam_end(handle, PAM_CONV_ERR);
228+ }
229+ }
230+
209231Q_SIGNALS:
210232 void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);
211233 void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
@@ -247,28 +269,6 @@ private Q_SLOTS:
247269 }
248270
249271private:
250− void cancelPam()
251− {
252− if (pamHandle != nullptr) {
253− QFuture<int> pamFuture = futureWatcher.future();
254− pam_handle *handle = pamHandle;
255− pamHandle = nullptr; // to disable normal finishPam() handling
256− pamFuture.cancel();
257−
258− // Note the empty loop, we just want to clear the futures queue.
259− // Any further prompts from the pam thread will be immediately
260− // responded to/dismissed in handlePrompt().
261− while (respond(QString()));
262−
263− // Now let signal/slot handling happen so the thread can finish
264− while (!pamFuture.isFinished()) {
265− QCoreApplication::processEvents();
266− }
267−
268− pam_end(handle, PAM_CONV_ERR);
269− }
270− }
271−
272272 Greeter *greeter;
273273 GreeterPrivate *greeterPrivate;
274274 pam_handle* pamHandle;
@@ -299,6 +299,11 @@ void GreeterPrivate::handleRespond(const QString &response)
299299 m_impl->respond(response);
300300}
301301
302+void GreeterPrivate::cancelAuthentication()
303+{
304+ m_impl->cancelPam();
305+}
306+
302307}
303308
304309#include "GreeterPrivate.moc"
plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h+1
@@ -38,6 +38,7 @@ public:
3838
3939 void handleAuthenticate();
4040 void handleRespond(const QString &response);
41+ void cancelAuthentication();
4142
4243protected:
4344 GreeterImpl *m_impl; // if the backend needs more private data
plugins/LightDM/UsersModel.cpp+68−8
@@ -12,20 +12,22 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1715 */
1816
17+#include "Greeter.h"
1918#include "UsersModel.h"
19+#include <QIdentityProxyModel>
2020#include <QLightDM/UsersModel>
21−#include <QtCore/QSortFilterProxyModel>
21+
22+#include <libintl.h>
2223
2324// First, we define an internal class that wraps LightDM's UsersModel. This
2425// class will modify some of the data coming from LightDM. For example, we
25−// modify any empty Real Names into just normal Names.
26+// modify any empty Real Names into just normal Names. We also add optional
27+// rows, depending on configuration.
2628// (We can't modify the data directly in UsersModel below because it won't sort
2729// using the modified data.)
28−class MangleModel : public QSortFilterProxyModel
30+class MangleModel : public QIdentityProxyModel
2931{
3032 Q_OBJECT
3133
@@ -33,21 +35,41 @@ public:
3335 explicit MangleModel(QObject* parent=0);
3436
3537 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
38+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
39+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
40+
41+private:
42+ int guestRow() const;
3643};
3744
3845MangleModel::MangleModel(QObject* parent)
39− : QSortFilterProxyModel(parent)
46+ : QIdentityProxyModel(parent)
4047{
4148 setSourceModel(new QLightDM::UsersModel(this));
4249}
4350
4451QVariant MangleModel::data(const QModelIndex &index, int role) const
4552{
46− QVariant variantData = QSortFilterProxyModel::data(index, role);
53+ QVariant variantData;
54+
55+ if (index.row() == guestRow() && index.column() == 0) {
56+ switch (role) {
57+ case QLightDM::UsersModel::NameRole:
58+ variantData = QStringLiteral("*guest"); break;
59+ case QLightDM::UsersModel::RealNameRole:
60+ variantData = gettext("Guest Session"); break;
61+ case QLightDM::UsersModel::LoggedInRole:
62+ variantData = false; break;
63+ case QLightDM::UsersModel::SessionRole:
64+ variantData = Greeter::instance()->defaultSessionHint(); break;
65+ }
66+ } else {
67+ variantData = QIdentityProxyModel::data(index, role);
68+ }
4769
4870 // If user's real name is empty, switch to unix name
4971 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
50− variantData = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
72+ variantData = QIdentityProxyModel::data(index, QLightDM::UsersModel::NameRole);
5173 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
5274 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
5375 variantData = stringData;
@@ -56,6 +78,31 @@ QVariant MangleModel::data(const QModelIndex &index, int role) const
5678 return variantData;
5779}
5880
81+int MangleModel::guestRow() const
82+{
83+ return Greeter::instance()->hasGuestAccount() ?
84+ QIdentityProxyModel::rowCount() : -1;
85+}
86+
87+int MangleModel::rowCount(const QModelIndex &parent) const
88+{
89+ auto count = QIdentityProxyModel::rowCount(parent);
90+
91+ if (Greeter::instance()->hasGuestAccount() && !parent.isValid())
92+ count++;
93+
94+ return count;
95+}
96+
97+QModelIndex MangleModel::index(int row, int column, const QModelIndex &parent) const
98+{
99+ if (row == guestRow() && !parent.isValid()) {
100+ return createIndex(row, column);
101+ } else {
102+ return QIdentityProxyModel::index(row, column, parent);
103+ }
104+}
105+
59106// **** Now we continue with actual UsersModel class ****
60107
61108UsersModel::UsersModel(QObject* parent)
@@ -68,4 +115,17 @@ UsersModel::UsersModel(QObject* parent)
68115 sort(0);
69116}
70117
118+bool UsersModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
119+{
120+ auto leftName = source_left.data(QLightDM::UsersModel::NameRole);
121+ auto rightName = source_right.data(QLightDM::UsersModel::NameRole);
122+
123+ if (leftName == QStringLiteral("*guest"))
124+ return false;
125+ if (rightName == QStringLiteral("*guest"))
126+ return true;
127+
128+ return UnitySortFilterProxyModelQML::lessThan(source_left, source_right);
129+}
130+
71131#include "UsersModel.moc"
plugins/LightDM/UsersModel.h+4−1
@@ -21,7 +21,7 @@
2121#pragma once
2222
2323#include <unitysortfilterproxymodelqml.h>
24−#include <QtCore/QObject>
24+#include <QObject>
2525
2626class UsersModel : public UnitySortFilterProxyModelQML
2727{
@@ -29,4 +29,7 @@ class UsersModel : public UnitySortFilterProxyModelQML
2929
3030public:
3131 explicit UsersModel(QObject* parent=0);
32+
33+protected:
34+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
3235};
plugins/LightDM/plugin.cpp+3−2
@@ -32,13 +32,14 @@
3232
3333static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3434{
35− Q_UNUSED(engine)
3635 Q_UNUSED(scriptEngine)
3736
38− Greeter *greeter = new Greeter();
37+ Greeter *greeter = Greeter::instance();
3938 new DBusGreeter(greeter, QStringLiteral("/"));
4039 new DBusGreeterList(greeter, QStringLiteral("/list"));
4140
41+ engine->setObjectOwnership(greeter, QQmlEngine::CppOwnership);
42+
4243 return greeter;
4344}
4445
tests/mocks/AccountsService/CMakeLists.txt+1
@@ -6,6 +6,7 @@ include_directories(
66 )
77
88add_library(MockAccountsService-qml MODULE
9+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
910 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
1011 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
1112 AccountsService.cpp
tests/mocks/liblightdm/MockController.cpp+45−6
@@ -23,6 +23,8 @@ namespace QLightDM
2323
2424MockController::MockController(QObject *parent)
2525 : QObject(parent)
26+ , m_selectGuestHint(false)
27+ , m_hasGuestAccountHint(false)
2628 , m_fullSessions(
2729 {
2830 {"ubuntu", "Ubuntu"},
@@ -37,13 +39,9 @@ MockController::MockController(QObject *parent)
3739 {"xterm", "Recovery Console"},
3840 {"", "Unknown?"}
3941 })
42+ , m_numSessions(0)
4043{
41− m_userMode = qgetenv("LIBLIGHTDM_MOCK_MODE");
42− if (m_userMode.isEmpty()) {
43− m_userMode = "full";
44− }
45− m_sessionMode = "full";
46− m_numSessions = numFullSessions();
44+ reset();
4745}
4846
4947MockController::~MockController()
@@ -59,6 +57,21 @@ MockController *MockController::instance()
5957 return m_instance;
6058}
6159
60+void MockController::reset()
61+{
62+ auto userMode = qgetenv("LIBLIGHTDM_MOCK_MODE");
63+ if (userMode.isEmpty()) {
64+ userMode = "full";
65+ }
66+
67+ setSelectUserHint("");
68+ setSelectGuestHint(false);
69+ setHasGuestAccountHint(false);
70+ setUserMode(userMode);
71+ setSessionMode("full");
72+ setNumSessions(numFullSessions());
73+}
74+
6275QString MockController::selectUserHint() const
6376{
6477 return m_selectUserHint;
@@ -72,6 +85,32 @@ void MockController::setSelectUserHint(const QString &selectUserHint)
7285 }
7386}
7487
88+bool MockController::selectGuestHint() const
89+{
90+ return m_selectGuestHint;
91+}
92+
93+void MockController::setSelectGuestHint(bool selectGuestHint)
94+{
95+ if (m_selectGuestHint != selectGuestHint) {
96+ m_selectGuestHint = selectGuestHint;
97+ Q_EMIT selectGuestHintChanged();
98+ }
99+}
100+
101+bool MockController::hasGuestAccountHint() const
102+{
103+ return m_hasGuestAccountHint;
104+}
105+
106+void MockController::setHasGuestAccountHint(bool hasGuestAccountHint)
107+{
108+ if (m_hasGuestAccountHint != hasGuestAccountHint) {
109+ m_hasGuestAccountHint = hasGuestAccountHint;
110+ Q_EMIT hasGuestAccountHintChanged();
111+ }
112+}
113+
75114QString MockController::userMode() const
76115{
77116 return m_userMode;
tests/mocks/liblightdm/MockController.h+14
@@ -27,6 +27,8 @@ class Q_DECL_EXPORT MockController : public QObject
2727 Q_OBJECT
2828
2929 Q_PROPERTY(QString selectUserHint READ selectUserHint WRITE setSelectUserHint NOTIFY selectUserHintChanged)
30+ Q_PROPERTY(bool selectGuestHint READ selectGuestHint WRITE setSelectGuestHint NOTIFY selectGuestHintChanged)
31+ Q_PROPERTY(bool hasGuestAccountHint READ hasGuestAccountHint WRITE setHasGuestAccountHint NOTIFY hasGuestAccountHintChanged)
3032
3133 // single, single-pin, single-passphrase, full
3234 Q_PROPERTY(QString userMode READ userMode WRITE setUserMode NOTIFY userModeChanged)
@@ -46,9 +48,17 @@ public:
4648 static MockController *instance();
4749 virtual ~MockController();
4850
51+ Q_INVOKABLE void reset();
52+
4953 QString selectUserHint() const;
5054 void setSelectUserHint(const QString &selectUserHint);
5155
56+ bool selectGuestHint() const;
57+ void setSelectGuestHint(bool selectGuestHint);
58+
59+ bool hasGuestAccountHint() const;
60+ void setHasGuestAccountHint(bool hasGuestAccountHint);
61+
5262 QString userMode() const;
5363 void setUserMode(const QString &userMode);
5464
@@ -76,6 +86,8 @@ public:
7686Q_SIGNALS:
7787 void currentUsernameChanged();
7888 void selectUserHintChanged();
89+ void selectGuestHintChanged();
90+ void hasGuestAccountHintChanged();
7991 void userModeChanged();
8092 void sessionModeChanged();
8193 void numSessionsChanged();
@@ -87,6 +99,8 @@ private:
8799 QString m_currentUsername;
88100 QString m_sessionName;
89101 QString m_selectUserHint;
102+ bool m_selectGuestHint;
103+ bool m_hasGuestAccountHint;
90104 QString m_userMode;
91105 QString m_sessionMode;
92106 QList<SessionItem> m_fullSessions;
tests/mocks/liblightdm/MockGreeter.cpp+10−3
@@ -51,7 +51,7 @@ QString Greeter::authenticationUser() const
5151
5252bool Greeter::hasGuestAccountHint() const
5353{
54− return true;
54+ return MockController::instance()->hasGuestAccountHint();
5555}
5656
5757QString Greeter::getHint(const QString &name) const
@@ -87,7 +87,7 @@ QString Greeter::selectUserHint() const
8787
8888bool Greeter::selectGuestHint() const
8989{
90− return false;
90+ return MockController::instance()->selectGuestHint();
9191}
9292
9393QString Greeter::autologinUserHint() const
@@ -173,7 +173,14 @@ void Greeter::handleAuthenticate()
173173}
174174
175175void Greeter::authenticateAsGuest()
176−{}
176+{
177+ Q_D(Greeter);
178+
179+ d->authenticated = true;
180+ d->authenticationUser = QString(); // this is what the real liblightdm does
181+ d->twoFactorDone = false;
182+ QTimer::singleShot(0, this, &Greeter::authenticationComplete);
183+}
177184
178185void Greeter::authenticateAutologin()
179186{}
tests/mocks/liblightdm/MockUsersModel.cpp+2
@@ -65,6 +65,8 @@ UsersModel::UsersModel(QObject *parent)
6565 roles[UidRole] = "uid";
6666 setRoleNames(roles);
6767
68+ connect(MockController::instance(), &MockController::hasGuestAccountHintChanged,
69+ this, &UsersModel::resetEntries);
6870 connect(MockController::instance(), &MockController::userModeChanged,
6971 this, &UsersModel::resetEntries);
7072 connect(MockController::instance(), &MockController::sessionNameChanged,
tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+2−3
@@ -17,7 +17,6 @@ target_link_libraries(GreeterDBusTestExec
1717 )
1818target_include_directories(GreeterDBusTestExec PUBLIC
1919 ${CMAKE_SOURCE_DIR}/plugins/LightDM
20− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
2120 )
2221set_target_properties(GreeterDBusTestExec PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}/mocks/IntegratedLightDM/liblightdm")
2322install(TARGETS GreeterDBusTestExec
@@ -64,7 +63,6 @@ target_link_libraries(GreeterSessionsModelTestExec
6463target_include_directories(GreeterSessionsModelTestExec PUBLIC
6564 ${CMAKE_SOURCE_DIR}/plugins/LightDM
6665 ${CMAKE_SOURCE_DIR}/plugins/Utils
67− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
6866 ${CMAKE_SOURCE_DIR}/tests/mocks/liblightdm/
6967 )
7068install(TARGETS GreeterSessionsModelTestExec
@@ -77,6 +75,7 @@ add_unity8_uitest(GreeterSessions GreeterSessionsModelTestExec
7775# UsersModelTest
7876add_executable(GreeterUsersModelTestExec
7977 usersmodel.cpp
78+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
8079 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
8180 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
8281 )
@@ -90,7 +89,7 @@ target_link_libraries(GreeterUsersModelTestExec
9089target_include_directories(GreeterUsersModelTestExec PUBLIC
9190 ${CMAKE_SOURCE_DIR}/plugins/LightDM
9291 ${CMAKE_SOURCE_DIR}/plugins/Utils
93− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
92+ ${CMAKE_SOURCE_DIR}/tests/mocks/liblightdm
9493 )
9594set_target_properties(GreeterUsersModelTestExec PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}/mocks/IntegratedLightDM/liblightdm")
9695install(TARGETS GreeterUsersModelTestExec
tests/plugins/LightDM/IntegratedLightDM/dbus.cpp+1
@@ -82,6 +82,7 @@ private Q_SLOTS:
8282 void cleanup()
8383 {
8484 delete view;
85+ delete greeter; // reset singleton
8586 }
8687
8788 void testGetActiveEntry()
tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+34−4
@@ -14,6 +14,8 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17+#include "Greeter.h"
18+#include "MockController.h"
1719#include "UsersModel.h"
1820
1921#include <QLightDM/UsersModel>
@@ -40,17 +42,23 @@ private:
4042 return model->data(model->index(i, 0), role).toString();
4143 }
4244
45+ void recreateModel()
46+ {
47+ delete model;
48+ model = new UsersModel();
49+ QVERIFY(model);
50+ }
51+
4352private Q_SLOTS:
4453
4554 void init()
4655 {
47− model = new UsersModel();
48− QVERIFY(model);
56+ recreateModel();
4957 }
5058
5159 void cleanup()
5260 {
53− delete model;
61+ QLightDM::MockController::instance()->reset();
5462 }
5563
5664 void testMangleColor()
@@ -65,8 +73,30 @@ private Q_SLOTS:
6573 QVERIFY(name == "empty-name");
6674 }
6775
76+ void testHasGuest()
77+ {
78+ // sanity check that it doesn't start already present
79+ QCOMPARE(findName(model, QStringLiteral("*guest")), -1);
80+
81+ QLightDM::MockController::instance()->setHasGuestAccountHint(true);
82+ recreateModel();
83+
84+ int i = findName(model, QStringLiteral("*guest"));
85+ QVERIFY(i >= 0);
86+ QCOMPARE(i, model->count() - 1);
87+
88+ auto realName = model->data(i, QLightDM::UsersModel::RealNameRole).toString();
89+ QCOMPARE(realName, QStringLiteral("Guest Session"));
90+
91+ auto loggedIn = model->data(i, QLightDM::UsersModel::LoggedInRole).toBool();
92+ QVERIFY(!loggedIn);
93+
94+ auto session = model->data(i, QLightDM::UsersModel::SessionRole).toString();
95+ QCOMPARE(session, Greeter::instance()->defaultSessionHint());
96+ }
97+
6898private:
69− UsersModel *model;
99+ UsersModel *model = nullptr;
70100};
71101
72102QTEST_MAIN(GreeterUsersModelTest)
tests/qmltests/Greeter/tst_Greeter.qml+28−2
@@ -144,7 +144,7 @@ Item {
144144
145145 function init() {
146146 greeterSettings.lockedOutTime = 0;
147− LightDMController.selectUserHint = "";
147+ LightDMController.reset();
148148 greeter.failedLoginsDelayAttempts = 7;
149149 greeter.failedLoginsDelayMinutes = 5;
150150 teaseSpy.clear();
@@ -201,7 +201,10 @@ Item {
201201 var i = getIndexOf(name);
202202 compare(view.currentIndex, i);
203203 compare(AccountsService.user, name);
204− compare(LightDM.Greeter.authenticationUser, name);
204+ if (name === "*guest") // guest has no authenticationUser set
205+ compare(LightDM.Greeter.authenticationUser, "");
206+ else
207+ compare(LightDM.Greeter.authenticationUser, name);
205208 return i;
206209 }
207210
@@ -586,6 +589,29 @@ Item {
586589 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
587590 }
588591
592+ function test_selectUserHintGuest() {
593+ LightDMController.hasGuestAccountHint = true;
594+ LightDMController.selectGuestHint = true;
595+ LightDMController.selectUserHint = "info-prompt";
596+ resetLoader();
597+ verifySelected("*guest");
598+ }
599+
600+ function test_selectUserHintGuestWithNoGuest() {
601+ LightDMController.selectGuestHint = true;
602+ LightDMController.selectUserHint = "info-prompt";
603+ resetLoader();
604+ verifySelected("info-prompt");
605+ }
606+
607+ function test_hasGuestAccountHint() {
608+ LightDMController.hasGuestAccountHint = true;
609+ resetLoader();
610+ var i = selectUser("*guest");
611+ compare(i, LightDM.Users.count - 1); // guest should be last
612+ verify(!view.locked);
613+ }
614+
589615 function test_fingerprintSuccess() {
590616 var index = selectUser("has-password");
591617 unlockAndShowGreeter(); // turn on lockscreen mode