on xenial help sign in
refactor UsersModel a bit to allow further future custom rows easier (though I don't know of any planned ones); add some tests
commit48cf7e646426c2c4c6c55b1cfcc38a5e5732e36b authorMichael Terry <mike@mterry.name> dateWed 11 Jan 2017, 13:46 (10 years ago) parentea4893e
6 files changed+168−71
plugins/LightDM/UsersModel.cpp+75−68 qml/Greeter/GreeterPrompt.qml+1 tests/mocks/liblightdm/MockUsersModel.cpp+2 tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+53−2 tests/qmltests/Greeter/tst_Greeter.qml+8 tests/qmltests/tst_Shell.qml+29−1
plugins/LightDM/UsersModel.cpp+75−68
@@ -39,54 +39,54 @@ public:
3939 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
4040
4141private:
42− int manualRow() const;
43− int guestRow() const;
42+ struct CustomRow {
43+ QString name;
44+ QString realName;
45+ };
4446
45− void updateShowManual();
47+ void addCustomRow(const CustomRow &newRow);
48+ void removeCustomRow(const QString &rowName);
49+ void updateGuestRow();
50+ void updateManualRow();
4651
47− bool m_showManual;
52+ QList<CustomRow> m_customRows;
4853};
4954
5055MangleModel::MangleModel(QObject* parent)
5156 : QIdentityProxyModel(parent)
52− , m_showManual(false)
5357{
5458 if (!Greeter::instance()->hideUsersHint()) {
5559 setSourceModel(new QLightDM::UsersModel(this));
5660 }
5761
58− updateShowManual();
62+ updateGuestRow();
63+ updateManualRow();
5964
65+ // Would be nice if there were a rowCountChanged signal in the base class
6066 connect(this, &QIdentityProxyModel::modelReset,
61− this, &MangleModel::updateShowManual);
67+ this, &MangleModel::updateManualRow);
6268 connect(this, &QIdentityProxyModel::rowsInserted,
63− this, &MangleModel::updateShowManual);
69+ this, &MangleModel::updateManualRow);
6470 connect(this, &QIdentityProxyModel::rowsRemoved,
65− this, &MangleModel::updateShowManual);
71+ this, &MangleModel::updateManualRow);
6672}
6773
6874QVariant MangleModel::data(const QModelIndex &index, int role) const
6975{
7076 QVariant variantData;
7177
72− if (index.row() == manualRow() && index.column() == 0) {
73− switch (role) {
74− case QLightDM::UsersModel::NameRole: return QStringLiteral("*other");
75− case QLightDM::UsersModel::RealNameRole: return gettext("Login");
76− case QLightDM::UsersModel::LoggedInRole: return false;
77− case QLightDM::UsersModel::SessionRole: return Greeter::instance()->defaultSessionHint();
78− default: return QVariant();
79− }
80− } else if (index.row() == guestRow() && index.column() == 0) {
81− switch (role) {
82− case QLightDM::UsersModel::NameRole:
83− variantData = QStringLiteral("*guest"); break;
84− case QLightDM::UsersModel::RealNameRole:
85− variantData = gettext("Guest Session"); break;
86− case QLightDM::UsersModel::LoggedInRole:
87− variantData = false; break;
88− case QLightDM::UsersModel::SessionRole:
89− variantData = Greeter::instance()->defaultSessionHint(); break;
78+ bool isCustomRow = index.row() >= sourceModel()->rowCount() &&
79+ index.row() < rowCount();
80+ if (isCustomRow && index.column() == 0) {
81+ int customIndex = index.row() - sourceModel()->rowCount();
82+ if (role == QLightDM::UsersModel::NameRole) {
83+ variantData = m_customRows[customIndex].name;
84+ } else if (role == QLightDM::UsersModel::RealNameRole) {
85+ variantData = m_customRows[customIndex].realName;
86+ } else if (role == QLightDM::UsersModel::LoggedInRole) {
87+ variantData = false;
88+ } else if (role == QLightDM::UsersModel::SessionRole) {
89+ variantData = Greeter::instance()->defaultSessionHint();
9090 }
9191 } else {
9292 variantData = QIdentityProxyModel::data(index, role);
@@ -94,7 +94,7 @@ QVariant MangleModel::data(const QModelIndex &index, int role) const
9494
9595 // If user's real name is empty, switch to unix name
9696 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
97− variantData = QIdentityProxyModel::data(index, QLightDM::UsersModel::NameRole);
97+ variantData = data(index, QLightDM::UsersModel::NameRole);
9898 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
9999 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
100100 variantData = stringData;
@@ -103,64 +103,67 @@ QVariant MangleModel::data(const QModelIndex &index, int role) const
103103 return variantData;
104104}
105105
106−void MangleModel::updateShowManual()
106+void MangleModel::addCustomRow(const CustomRow &newRow)
107107{
108− // Show manual login if we are asked to OR if no other entry exists
109− bool showManual = Greeter::instance()->showManualLoginHint() ||
110− (QIdentityProxyModel::rowCount() == 0 &&
111− !Greeter::instance()->hasGuestAccount());
112−
113− if (m_showManual != showManual) {
114− int row = QIdentityProxyModel::rowCount();
115− if (showManual)
116− beginInsertRows(QModelIndex(), row, row);
117− else
118− beginRemoveRows(QModelIndex(), row, row);
119−
120− m_showManual = showManual;
121−
122− if (showManual)
123− endInsertRows();
124− else
125− endRemoveRows();
108+ for (int i = 0; i < m_customRows.size(); i++) {
109+ if (m_customRows[i].name == newRow.name) {
110+ return; // we don't have custom rows that change content yet
111+ }
126112 }
113+
114+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
115+ m_customRows << newRow;
116+ endInsertRows();
127117}
128118
129−int MangleModel::manualRow() const
119+void MangleModel::removeCustomRow(const QString &rowName)
130120{
131− if (!m_showManual)
132− return -1;
133−
134− return QIdentityProxyModel::rowCount();
121+ for (int i = 0; i < m_customRows.size(); i++) {
122+ if (m_customRows[i].name == rowName) {
123+ int rowNum = sourceModel()->rowCount() + i;
124+ beginRemoveRows(QModelIndex(), rowNum, rowNum);
125+ m_customRows.removeAt(i);
126+ endRemoveRows();
127+ break;
128+ }
129+ }
135130}
136131
137−int MangleModel::guestRow() const
132+void MangleModel::updateManualRow()
138133{
139− if (!Greeter::instance()->hasGuestAccount())
140− return -1;
134+ bool hasAnotherEntry = sourceModel()->rowCount() > 0;
135+ for (int i = 0; !hasAnotherEntry && i < m_customRows.size(); i++) {
136+ if (m_customRows[i].name != QStringLiteral("*other")) {
137+ hasAnotherEntry = true;
138+ }
139+ }
141140
142− int row = QIdentityProxyModel::rowCount();
143− if (m_showManual)
144− row++;
141+ // Show manual login if we are asked to OR if no other entry exists
142+ if (Greeter::instance()->showManualLoginHint() || !hasAnotherEntry)
143+ addCustomRow({QStringLiteral("*other"), gettext("Login")});
144+ else
145+ removeCustomRow(QStringLiteral("*other"));
146+}
145147
146− return row;
148+void MangleModel::updateGuestRow()
149+{
150+ if (Greeter::instance()->hasGuestAccount())
151+ addCustomRow({QStringLiteral("*guest"), gettext("Guest Session")});
152+ else
153+ removeCustomRow(QStringLiteral("*guest"));
147154}
148155
149156int MangleModel::rowCount(const QModelIndex &parent) const
150157{
151− auto count = QIdentityProxyModel::rowCount(parent);
152−
153− if (m_showManual && !parent.isValid())
154− count++;
155− if (Greeter::instance()->hasGuestAccount() && !parent.isValid())
156− count++;
157−
158− return count;
158+ return QIdentityProxyModel::rowCount(parent) +
159+ (parent.isValid() ? 0 : m_customRows.size());
159160}
160161
161162QModelIndex MangleModel::index(int row, int column, const QModelIndex &parent) const
162163{
163− if ((row == manualRow() || row == guestRow()) && !parent.isValid()) {
164+ bool isCustomRow = row >= sourceModel()->rowCount() &&
165+ row < rowCount();
166+ if (isCustomRow && !parent.isValid()) {
164167 return createIndex(row, column);
165168 } else {
166169 return QIdentityProxyModel::index(row, column, parent);
@@ -188,6 +191,10 @@ bool UsersModel::lessThan(const QModelIndex &source_left, const QModelIndex &sou
188191 return false;
189192 if (rightName == QStringLiteral("*guest"))
190193 return true;
194+ if (leftName == QStringLiteral("*other"))
195+ return false;
196+ if (rightName == QStringLiteral("*other"))
197+ return true;
191198
192199 return UnitySortFilterProxyModelQML::lessThan(source_left, source_right);
193200}
qml/Greeter/GreeterPrompt.qml+1
@@ -194,6 +194,7 @@ FocusScope {
194194 // palette color, whereas we want raisedSecondaryText.
195195 Label {
196196 id: hint
197+ objectName: "promptHint"
197198 anchors {
198199 left: parent.left
199200 right: parent.right
tests/mocks/liblightdm/MockUsersModel.cpp+2
@@ -67,6 +67,8 @@ UsersModel::UsersModel(QObject *parent)
6767
6868 connect(MockController::instance(), &MockController::hasGuestAccountHintChanged,
6969 this, &UsersModel::resetEntries);
70+ connect(MockController::instance(), &MockController::showManualLoginHintChanged,
71+ this, &UsersModel::resetEntries);
7072 connect(MockController::instance(), &MockController::userModeChanged,
7173 this, &UsersModel::resetEntries);
7274 resetEntries();
tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+53−2
@@ -79,11 +79,9 @@ private Q_SLOTS:
7979 QCOMPARE(findName(model, QStringLiteral("*guest")), -1);
8080
8181 QLightDM::MockController::instance()->setHasGuestAccountHint(true);
82− recreateModel();
8382
8483 int i = findName(model, QStringLiteral("*guest"));
8584 QVERIFY(i >= 0);
86− QCOMPARE(i, model->count() - 1);
8785
8886 auto realName = model->data(i, QLightDM::UsersModel::RealNameRole).toString();
8987 QCOMPARE(realName, QStringLiteral("Guest Session"));
@@ -95,6 +93,59 @@ private Q_SLOTS:
9593 QCOMPARE(session, Greeter::instance()->defaultSessionHint());
9694 }
9795
96+ void testHasManual()
97+ {
98+ // sanity check that it doesn't start already present
99+ QCOMPARE(findName(model, QStringLiteral("*other")), -1);
100+
101+ QLightDM::MockController::instance()->setShowManualLoginHint(true);
102+
103+ int i = findName(model, QStringLiteral("*other"));
104+ QVERIFY(i >= 0);
105+
106+ auto realName = model->data(i, QLightDM::UsersModel::RealNameRole).toString();
107+ QCOMPARE(realName, QStringLiteral("Login"));
108+
109+ auto loggedIn = model->data(i, QLightDM::UsersModel::LoggedInRole).toBool();
110+ QVERIFY(!loggedIn);
111+
112+ auto session = model->data(i, QLightDM::UsersModel::SessionRole).toString();
113+ QCOMPARE(session, Greeter::instance()->defaultSessionHint());
114+ }
115+
116+ void testHideUsers()
117+ {
118+ QLightDM::MockController::instance()->setHideUsersHint(true);
119+
120+ QCOMPARE(model->count(), 1);
121+ auto name = model->data(0, QLightDM::UsersModel::NameRole).toString();
122+ QCOMPARE(name, QStringLiteral("*other"));
123+ }
124+
125+ void testHideUsersWithGuest()
126+ {
127+ QLightDM::MockController::instance()->setHideUsersHint(true);
128+ QLightDM::MockController::instance()->setHasGuestAccountHint(true);
129+
130+ QCOMPARE(model->count(), 1);
131+ auto name = model->data(0, QLightDM::UsersModel::NameRole).toString();
132+ QCOMPARE(name, QStringLiteral("*guest"));
133+ }
134+
135+ void testCustomEntriesAreLast()
136+ {
137+ QLightDM::MockController::instance()->setHasGuestAccountHint(true);
138+ QLightDM::MockController::instance()->setShowManualLoginHint(true);
139+
140+ QVERIFY(model->count() > 2);
141+
142+ int manualIndex = findName(model, QStringLiteral("*other"));
143+ QCOMPARE(manualIndex, model->count() - 2);
144+
145+ int guestIndex = findName(model, QStringLiteral("*guest"));
146+ QCOMPARE(guestIndex, model->count() - 1);
147+ }
148+
98149private:
99150 UsersModel *model = nullptr;
100151};
tests/qmltests/Greeter/tst_Greeter.qml+8
@@ -612,6 +612,14 @@ Item {
612612 verify(!view.locked);
613613 }
614614
615+ function test_hasManualLoginHint() {
616+ LightDMController.showManualLoginHint = true;
617+ resetLoader();
618+ var i = selectUser("*other");
619+ compare(i, LightDM.Users.count - 1); // manual should be last
620+ verify(view.locked);
621+ }
622+
615623 function test_fingerprintSuccess() {
616624 var index = selectUser("has-password");
617625 unlockAndShowGreeter(); // turn on lockscreen mode
tests/qmltests/tst_Shell.qml+29−1
@@ -647,7 +647,8 @@ Rectangle {
647647 // Shell instance gets destroyed.
648648 tryCompare(shellLoader, "itemDestroyed", true);
649649
650− setLightDMMockMode("single"); // back to the default value
650+ LightDMController.reset();
651+ setLightDMMockMode("single"); // these tests default to "single"
651652
652653 AccountsService.demoEdges = false;
653654 AccountsService.demoEdgesCompleted = [];
@@ -1470,6 +1471,33 @@ Rectangle {
14701471 }
14711472 }
14721473
1474+ function test_manualLoginFlow() {
1475+ LightDMController.showManualLoginHint = true;
1476+ setLightDMMockMode("full");
1477+ loadShell("desktop");
1478+
1479+ var i = selectUser("*other");
1480+ var greeter = findChild(shell, "greeter");
1481+ var username = findChild(greeter, "username" + i);
1482+ var promptField = findChild(greeter, "promptField");
1483+ var promptHint = findChild(greeter, "promptHint");
1484+
1485+ compare(username.text, "Login");
1486+ compare(promptHint.text, "Username");
1487+
1488+ tryCompare(promptField, "activeFocus", true);
1489+ typeString("has-password");
1490+ keyClick(Qt.Key_Enter);
1491+
1492+ tryCompare(username, "text", "has-password");
1493+ tryCompare(promptHint, "text", "Passphrase");
1494+
1495+ typeString("password");
1496+ keyClick(Qt.Key_Enter);
1497+
1498+ confirmLoggedIn(true);
1499+ }
1500+
14731501 function test_launcherInverted_data() {
14741502 return [
14751503 {tag: "phone", formFactor: "phone", usageScenario: "phone", launcherInverted: true},