on xenial help sign in
Add Lockscreens
320fda2Michael Zanetti
Components/WrongPasswordAnimation.qmlnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+
19+SequentialAnimation {
20+ id: root
21+
22+ property var target
23+
24+ NumberAnimation {
25+ target: root.target
26+ property: "anchors.horizontalCenterOffset"
27+ duration: 50
28+ easing.type: Easing.InQuad
29+ to: units.gu(2)
30+ }
31+ NumberAnimation {
32+ target: root.target
33+ property: "anchors.horizontalCenterOffset"
34+ duration: 100
35+ easing.type: Easing.InOutQuad
36+ to: -units.gu(2)
37+ }
38+ NumberAnimation {
39+ target: root.target
40+ easing.type: Easing.OutElastic
41+ properties: "anchors.horizontalCenterOffset"
42+ to: 0
43+ duration: 400
44+ easing.overshoot: units.gu(2)
45+ }
46+}
Greeter/Lockscreen.qmlnew file+149
@@ -0,0 +1,149 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import "../Components"
20+import LightDM 0.1 as LightDM
21+
22+Showable {
23+ id: root
24+
25+ // Determine if a numeric or alphanumeric pad is used.
26+ property bool alphaNumeric: false
27+
28+ // Placeholder text
29+ property string placeholderText: ""
30+
31+ // In case the Lockscreen can show a greeter message, this is the username
32+ property string username: ""
33+
34+ // As the numeric PIN pad doesn't have a OK button by design, we need to auto-confirm on a certain length.
35+ // This is ignored by the alphaNumeric lockscreen as that one is confirmed with pressing enter on the OSK.
36+ property int pinLength: 4
37+
38+ property url background: ""
39+
40+ signal unlocked()
41+ signal cancel()
42+ signal emergencyCall()
43+
44+ function reset() {
45+ pinPadLoader.resetting = true;
46+ pinPadLoader.resetting = false;
47+ }
48+
49+ Image {
50+ anchors.fill: parent
51+ source: root.required ? root.background : ""
52+ }
53+
54+ MouseArea {
55+ anchors.fill: root
56+ }
57+
58+ Loader {
59+ id: pinPadLoader
60+ objectName: "pinPadLoader"
61+ anchors {
62+ left: parent.left
63+ right: parent.right
64+ verticalCenter: parent.verticalCenter
65+ verticalCenterOffset: root.alphaNumeric ? -units.gu(10) : 0
66+ }
67+ property bool resetting: false
68+
69+ source: (!resetting && root.required) ? (root.alphaNumeric ? "PassphraseLockscreen.qml" : "PinLockscreen.qml") : ""
70+
71+ Connections {
72+ target: pinPadLoader.item
73+
74+ onEntered: {
75+ LightDM.Greeter.respond(passphrase);
76+ }
77+
78+ onCancel: {
79+ root.cancel()
80+ }
81+ }
82+
83+ Binding {
84+ target: pinPadLoader.item
85+ property: "pinLength"
86+ value: root.pinLength
87+ }
88+ Binding {
89+ target: pinPadLoader.item
90+ property: "placeholderText"
91+ value: root.placeholderText
92+ }
93+ Binding {
94+ target: pinPadLoader.item
95+ property: "username"
96+ value: root.username
97+ }
98+ }
99+
100+ Column {
101+ anchors {
102+ left: parent.left
103+ top: pinPadLoader.bottom
104+ topMargin: units.gu(4)
105+ right: parent.right
106+ bottom: parent.bottom
107+ }
108+ spacing: units.gu(1)
109+
110+ Icon {
111+ objectName: "emergencyCallIcon"
112+ height: units.gu(3)
113+ width: height
114+ anchors.horizontalCenter: parent.horizontalCenter
115+ name: "phone-app-call-symbolic"
116+ color: "white"
117+ opacity: 0.9
118+
119+ MouseArea {
120+ anchors.fill: parent
121+ onClicked: root.emergencyCall()
122+ }
123+ }
124+
125+ Label {
126+ text: i18n.tr("Emergency call")
127+ color: "white"
128+ opacity: 0.9
129+ fontSize: "medium"
130+ anchors.horizontalCenter: parent.horizontalCenter
131+ }
132+ }
133+
134+ Connections {
135+ target: LightDM.Greeter
136+
137+ onAuthenticationComplete: {
138+ if (LightDM.Greeter.promptless) {
139+ return;
140+ }
141+ if (LightDM.Greeter.authenticated) {
142+ root.unlocked();
143+ pinPadLoader.item.clear(false);
144+ } else {
145+ pinPadLoader.item.clear(true);
146+ }
147+ }
148+ }
149+}
Greeter/LoginList.qml+3−23
@@ -17,6 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import LightDM 0.1 as LightDM
19import LightDM 0.1 as LightDM
20+import "../Components"
20
21
21Item {
22Item {
22 id: root
23 id: root
@@ -218,30 +219,9 @@ Item {
218 source: "graphics/icon_arrow.png"
219 source: "graphics/icon_arrow.png"
219 }
220 }
220
221
221− SequentialAnimation {
222+ WrongPasswordAnimation {
222 id: wrongPasswordAnimation
223 id: wrongPasswordAnimation
223− NumberAnimation {
224− target: passwordInput
225− property: "anchors.horizontalCenterOffset"
226− duration: 50
227− easing.type: Easing.InQuad
228− to: units.gu(2)
229− }
230− NumberAnimation {
231− target: passwordInput
232− property: "anchors.horizontalCenterOffset"
233− duration: 100
234− easing.type: Easing.InOutQuad
235− to: -units.gu(2)
236− }
237− NumberAnimation {
238− target: passwordInput
224+ target: passwordInput
239− easing.type: Easing.OutElastic
240− properties: "anchors.horizontalCenterOffset"
241− to: 0
242− duration: 400
243− easing.overshoot: units.gu(2)
244− }
245 }
225 }
246
226
247 Connections {
227 Connections {
Greeter/PassphraseLockscreen.qmlnew file+87
@@ -0,0 +1,87 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import "../Components"
20+
21+Item {
22+ id: root
23+ height: highlightItem.height
24+
25+ property alias placeholderText: pinentryField.placeholderText
26+ property string username: ""
27+
28+ signal entered(string passphrase)
29+ signal cancel()
30+
31+ function clear(playAnimation) {
32+ pinentryField.text = "";
33+ if (playAnimation) {
34+ wrongPasswordAnimation.start();
35+ } else {
36+ pinentryField.focus = false
37+ }
38+ }
39+
40+ Rectangle {
41+ id: highlightItem
42+ width: units.gu(32)
43+ height: units.gu(10)
44+ anchors.centerIn: parent
45+ color: Qt.rgba(0.1, 0.1, 0.1, 0.4)
46+ border.color: Qt.rgba(0.4, 0.4, 0.4, 0.4)
47+ border.width: units.dp(1)
48+ radius: units.gu(1.5)
49+ antialiasing: true
50+
51+ Label {
52+ objectName: "greeterLabel"
53+ anchors {
54+ left: parent.left
55+ top: parent.top
56+ right: parent.right
57+ margins: units.gu(1.5)
58+ }
59+ text: root.username.length > 0 ? i18n.tr("Hello %1").arg(root.username) : i18n.tr("Hello")
60+ color: "white"
61+ }
62+
63+ TextField {
64+ id: pinentryField
65+ objectName: "pinentryField"
66+ anchors {
67+ horizontalCenter: parent.horizontalCenter
68+ bottom: parent.bottom
69+ margins: units.gu(1)
70+ }
71+ height: units.gu(4.5)
72+ width: parent.width - units.gu(2)
73+ echoMode: TextInput.Password
74+ opacity: 0.9
75+ hasClearButton: false
76+
77+ onAccepted: {
78+ root.entered(pinentryField.text);
79+ }
80+ }
81+ }
82+
83+ WrongPasswordAnimation {
84+ id: wrongPasswordAnimation
85+ target: pinentryField
86+ }
87+}
Greeter/PinLockscreen.qmlnew file+111
@@ -0,0 +1,111 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import "../Components"
20+
21+Column {
22+ id: root
23+ anchors.centerIn: parent
24+ spacing: units.gu(2.5)
25+
26+ property alias placeholderText: pinentryField.placeholderText
27+ property int padWidth: units.gu(32)
28+ property int pinLength: 4
29+
30+ signal entered(string passphrase)
31+ signal cancel()
32+
33+ function clear(playAnimation) {
34+ pinentryField.text = "";
35+ if (playAnimation) {
36+ wrongPasswordAnimation.start();
37+ }
38+ }
39+
40+ TextField {
41+ id: pinentryField
42+ objectName: "pinentryField"
43+ anchors.horizontalCenter: parent.horizontalCenter
44+ width: units.gu(32)
45+ height: units.gu(5)
46+ echoMode: TextInput.Password
47+ font.pixelSize: units.dp(44)
48+ color: "white"
49+ opacity: 0.9
50+ hasClearButton: false
51+ horizontalAlignment: Text.AlignHCenter
52+
53+ onTextChanged: {
54+ if (pinentryField.text.length === root.pinLength) {
55+ root.entered(pinentryField.text);
56+ }
57+ }
58+
59+ // Using a MouseArea to eat clicks. We don't want to disable the TextField for styling reasons
60+ MouseArea {
61+ anchors.fill: parent
62+ }
63+ }
64+
65+ Grid {
66+ anchors {
67+ left: parent.left
68+ right: parent.right
69+ margins: (parent.width - root.padWidth) / 2
70+ }
71+
72+ columns: 3
73+ spacing: units.gu(1)
74+
75+ Repeater {
76+ model: 9
77+
78+ PinPadButton {
79+ objectName: "pinPadButton" + (index + 1)
80+ text: index + 1
81+
82+ onClicked: {
83+ pinentryField.text = pinentryField.text + text
84+ }
85+ }
86+ }
87+
88+ PinPadButton {
89+ objectName: "pinPadButtonBack"
90+ iconName: "back"
91+ onClicked: root.cancel();
92+
93+ }
94+ PinPadButton {
95+ objectName: "pinPadButton0"
96+ text: "0"
97+ onClicked: pinentryField.text = pinentryField.text + text
98+
99+ }
100+ PinPadButton {
101+ objectName: "pinPadButtonErase"
102+ iconName: "erase"
103+ onClicked: pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1)
104+ }
105+ }
106+
107+ WrongPasswordAnimation {
108+ id: wrongPasswordAnimation
109+ target: pinentryField
110+ }
111+}
Greeter/PinPadButton.qmlnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+
20+UbuntuShape {
21+ id: root
22+ width: units.gu(10)
23+ height: units.gu(7)
24+ radius: "medium"
25+
26+ property alias text: label.text
27+ property alias iconName: icon.name
28+
29+ signal clicked()
30+
31+ Behavior on color {
32+ ColorAnimation { duration: 100 }
33+ }
34+
35+ Label {
36+ id: label
37+ anchors.centerIn: parent
38+ color: "white"
39+ fontSize: "x-large"
40+ font.weight: Font.Light
41+ opacity: 0.9
42+ }
43+
44+ Icon {
45+ id: icon
46+ height: units.gu(3)
47+ width: height
48+ anchors.centerIn: parent
49+ color: "white"
50+ opacity: 0.9
51+ }
52+
53+ MouseArea {
54+ anchors.fill: parent
55+ onClicked: root.clicked()
56+ onPressed: root.color = Qt.rgba(0, 0, 0, 0.3)
57+ onReleased: root.color = Qt.rgba(0, 0, 0, 0)
58+ }
59+}
Shell.qml+52−8
@@ -67,7 +67,7 @@ FocusScope {
67 }
67 }
68
68
69 readonly property bool fullscreenMode: {
69 readonly property bool fullscreenMode: {
70− if (greeter.shown) {
70+ if (greeter.shown || lockscreen.shown) {
71 return false;
71 return false;
72 } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
72 } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
73 return mainStage.switchingFromFullscreenToFullscreen;
73 return mainStage.switchingFromFullscreenToFullscreen;
@@ -124,7 +124,6 @@ FocusScope {
124
124
125 Item {
125 Item {
126 id: underlay
126 id: underlay
127−
128 anchors.fill: parent
127 anchors.fill: parent
129 visible: !(panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
128 visible: !(panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
130 && (stages.fullyHidden
129 && (stages.fullyHidden
@@ -148,7 +147,7 @@ FocusScope {
148 Dash {
147 Dash {
149 id: dash
148 id: dash
150
149
151− available: !greeter.shown
150+ available: !greeter.shown && !lockscreen.shown
152 hides: [stages, launcher, panel.indicators]
151 hides: [stages, launcher, panel.indicators]
153 shown: disappearingAnimationProgress !== 1.0
152 shown: disappearingAnimationProgress !== 1.0
154 enabled: disappearingAnimationProgress === 0.0
153 enabled: disappearingAnimationProgress === 0.0
@@ -301,7 +300,7 @@ FocusScope {
301 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
300 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
302 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
301 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
303
302
304− available: !greeter.shown && enabled
303+ available: !greeter.shown && !lockscreen.shown && enabled
305 hides: [launcher, panel.indicators]
304 hides: [launcher, panel.indicators]
306 shown: false
305 shown: false
307 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
306 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
@@ -352,6 +351,46 @@ FocusScope {
352 orientation: Qt.Horizontal
351 orientation: Qt.Horizontal
353 }
352 }
354
353
354+ Lockscreen {
355+ id: lockscreen
356+ hides: [launcher, panel.indicators, hud]
357+ shown: false
358+ enabled: true
359+ showAnimation: StandardAnimation { property: "opacity"; to: 1 }
360+ hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
361+ y: panel.panelHeight
362+ x: required ? 0 : - width
363+ width: parent.width
364+ height: parent.height - panel.panelHeight
365+ background: shell.background
366+
367+ onUnlocked: lockscreen.hide()
368+ onCancel: greeter.show()
369+
370+ Component.onCompleted: {
371+ if (LightDM.Users.count == 1) {
372+ LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
373+ }
374+ }
375+ }
376+
377+ Connections {
378+ target: LightDM.Greeter
379+
380+ onShowPrompt: {
381+ if (LightDM.Users.count == 1) {
382+ // TODO: There's no better way for now to determine if its a PIN or a passphrase.
383+ if (text == "PIN") {
384+ lockscreen.alphaNumeric = false
385+ } else {
386+ lockscreen.alphaNumeric = true
387+ }
388+ lockscreen.placeholderText = i18n.tr("Please enter %1:").arg(text);
389+ lockscreen.show();
390+ }
391+ }
392+ }
393+
355 Greeter {
394 Greeter {
356 id: greeter
395 id: greeter
357
396
@@ -360,7 +399,6 @@ FocusScope {
360 shown: true
399 shown: true
361 showAnimation: StandardAnimation { property: "x"; to: greeterRevealer.openedValue }
400 showAnimation: StandardAnimation { property: "x"; to: greeterRevealer.openedValue }
362 hideAnimation: StandardAnimation { property: "x"; to: greeterRevealer.closedValue }
401 hideAnimation: StandardAnimation { property: "x"; to: greeterRevealer.closedValue }
363−
364 y: panel.panelHeight
402 y: panel.panelHeight
365 width: parent.width
403 width: parent.width
366 height: parent.height - panel.panelHeight
404 height: parent.height - panel.panelHeight
@@ -370,6 +408,12 @@ FocusScope {
370
408
371 onShownChanged: {
409 onShownChanged: {
372 if (shown) {
410 if (shown) {
411+ lockscreen.reset();
412+ // If there is only one user, we start authenticating with that one here.
413+ // If there are more users, the Greeter will handle that
414+ if (LightDM.Users.count == 1) {
415+ LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
416+ }
373 greeter.forceActiveFocus();
417 greeter.forceActiveFocus();
374 // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
418 // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
375 // the stage was actually shown
419 // the stage was actually shown
@@ -397,7 +441,7 @@ FocusScope {
397
441
398 InputFilterArea {
442 InputFilterArea {
399 anchors.fill: parent
443 anchors.fill: parent
400− blockInput: greeter.shown
444+ blockInput: greeter.shown || lockscreen.shown
401 }
445 }
402
446
403 Revealer {
447 Revealer {
@@ -426,7 +470,7 @@ FocusScope {
426 hides: [launcher]
470 hides: [launcher]
427 }
471 }
428 fullscreenMode: shell.fullscreenMode
472 fullscreenMode: shell.fullscreenMode
429− searchVisible: !greeter.shown
473+ searchVisible: !greeter.shown && !lockscreen.shown
430
474
431 InputFilterArea {
475 InputFilterArea {
432 anchors.fill: parent
476 anchors.fill: parent
@@ -440,7 +484,7 @@ FocusScope {
440 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
484 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
441 height: parent.height
485 height: parent.height
442
486
443− available: !greeter.shown && !panel.indicators.shown
487+ available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown
444 shown: false
488 shown: false
445 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
489 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
446 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
490 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
plugins/LightDM/CMakeLists.txt+1−1
@@ -40,4 +40,4 @@ install(TARGETS LightDM-qml
40
40
41install(FILES qmldir
41install(FILES qmldir
42 DESTINATION ${SHELL_APP_DIR}/plugins/LightDM
42 DESTINATION ${SHELL_APP_DIR}/plugins/LightDM
43− )
43+ )
run+17−1
@@ -4,6 +4,8 @@ QML_PHONE_SHELL_PATH=./builddir/unity8
4export LD_LIBRARY_PATH=$PWD/../unity_build/build/lib
4export LD_LIBRARY_PATH=$PWD/../unity_build/build/lib
5GDB=false
5GDB=false
6FAKE=false
6FAKE=false
7+PINLOCK=false
8+KEYLOCK=false
7MOUSE_TOUCH=true
9MOUSE_TOUCH=true
8
10
9usage() {
11usage() {
@@ -11,6 +13,8 @@ usage() {
11 echo "Script to run the shell.\n" >&2
13 echo "Script to run the shell.\n" >&2
12 echo "OPTIONS:" >&2
14 echo "OPTIONS:" >&2
13 echo " -f, --fake Force use of fake Qml modules." >&2
15 echo " -f, --fake Force use of fake Qml modules." >&2
16+ echo " -p, --pinlock Use a pin protected user." >&2
17+ echo " -k, --keylock Use a passphrase protected user." >&2
14 echo " -g, --gdb Run through gdb." >&2
18 echo " -g, --gdb Run through gdb." >&2
15 echo " -h, --help Show this help." >&2
19 echo " -h, --help Show this help." >&2
16 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
20 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
@@ -18,7 +22,7 @@ usage() {
18 exit 1
22 exit 1
19}
23}
20
24
21−ARGS=`getopt -n$0 -u -a --longoptions="fake,gdb,help,nomousetouch" -o "fghm" -- "$@"`
25+ARGS=`getopt -n$0 -u -a --longoptions="fake,pinlock,keylock,gdb,help,nomousetouch" -o "fpkghm" -- "$@"`
22[ $? -ne 0 ] && usage
26[ $? -ne 0 ] && usage
23eval set -- "$ARGS"
27eval set -- "$ARGS"
24
28
@@ -26,6 +30,8 @@ while [ $# -gt 0 ]
26do
30do
27 case "$1" in
31 case "$1" in
28 -f|--fake) FAKE=true;;
32 -f|--fake) FAKE=true;;
33+ -p|--pinlock) PINLOCK=true;;
34+ -k|--keylock) KEYLOCK=true;;
29 -g|--gdb) GDB=true;;
35 -g|--gdb) GDB=true;;
30 -h|--help) usage;;
36 -h|--help) usage;;
31 -m|--nomousetouch) MOUSE_TOUCH=false;;
37 -m|--nomousetouch) MOUSE_TOUCH=false;;
@@ -39,6 +45,16 @@ if $FAKE; then
39 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single
45 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single
40fi
46fi
41
47
48+if $PINLOCK; then
49+ export QML2_IMPORT_PATH=$PWD/builddir/tests/mocks:$PWD/builddir/plugins
50+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single-pin
51+fi
52+
53+if $KEYLOCK; then
54+ export QML2_IMPORT_PATH=$PWD/builddir/tests/mocks:$PWD/builddir/plugins
55+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single-passphrase
56+fi
57+
42# Force icon theme if running on the desktop, otherwise gnome theme (if running
58# Force icon theme if running on the desktop, otherwise gnome theme (if running
43# on Ubuntu Desktop) will be used and icons won't be found
59# on Ubuntu Desktop) will be used and icons won't be found
44if [ -n "$DESKTOP_SESSION" ]; then
60if [ -n "$DESKTOP_SESSION" ]; then
run_on_device+18−4
@@ -11,6 +11,8 @@ TARGET_DEBUG_PORT=3768
11RUN_OPTIONS=-qmljsdebugger=port:$TARGET_DEBUG_PORT
11RUN_OPTIONS=-qmljsdebugger=port:$TARGET_DEBUG_PORT
12SETUP=false
12SETUP=false
13GDB=false
13GDB=false
14+PINLOCK=false
15+KEYLOCK=false
14SUDO="echo $PASSWORD | sudo -S"
16SUDO="echo $PASSWORD | sudo -S"
15NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
17NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
16
18
@@ -19,6 +21,8 @@ usage() {
19 echo "Script to setup a build environment for the shell and sync build and run it on the device\n"
21 echo "Script to setup a build environment for the shell and sync build and run it on the device\n"
20 echo "OPTIONS:"
22 echo "OPTIONS:"
21 echo " -s, --setup Setup the build environment"
23 echo " -s, --setup Setup the build environment"
24+ echo " -p, --pinlock Enable a PIN lock screen when running"
25+ echo " -k, --keylock Enable a Keyboard lock screen when running"
22 echo ""
26 echo ""
23 echo "IMPORTANT:"
27 echo "IMPORTANT:"
24 echo " * Make sure to have the networking and PPAs setup on the device beforehand (phablet-deploy-networking && phablet-ppa-fetch)."
28 echo " * Make sure to have the networking and PPAs setup on the device beforehand (phablet-deploy-networking && phablet-ppa-fetch)."
@@ -86,11 +90,19 @@ run() {
86 fi
90 fi
87 adb shell pkill qml-phone-shell
91 adb shell pkill qml-phone-shell
88 adb shell pkill $BINARY
92 adb shell pkill $BINARY
93+ ARGS="--nomousetouch"
89 if $GDB; then
94 if $GDB; then
90− exec_with_ssh "cd $CODE_DIR/ && ./run --gdb --nomousetouch -- $RUN_OPTIONS"
95+ ARGS="$ARGS --gdb"
91− else
92− exec_with_ssh "cd $CODE_DIR && ./run --nomousetouch -- $RUN_OPTIONS"
93 fi
96 fi
97+ if $PINLOCK; then
98+ ARGS="$ARGS -p"
99+ fi
100+ if $KEYLOCK; then
101+ ARGS="$ARGS -k"
102+ fi
103+
104+ exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
105+
94 if [ $CONTAINS_SHELL -eq 0 ]; then
106 if [ $CONTAINS_SHELL -eq 0 ]; then
95 echo "Restoring $SERVICEFILE on device from $SERVICEFILE.backup"
107 echo "Restoring $SERVICEFILE on device from $SERVICEFILE.backup"
96 adb shell cp $SERVICEFILE.backup $SERVICEFILE
108 adb shell cp $SERVICEFILE.backup $SERVICEFILE
@@ -98,7 +110,7 @@ run() {
98 fi
110 fi
99}
111}
100
112
101−set -- `getopt -n$0 -u -a --longoptions="setup,gdb,help" "sgh" "$@"`
113+set -- `getopt -n$0 -u -a --longoptions="setup,gdb,pinlock,keylock,help" "sgpkh" "$@"`
102
114
103# FIXME: giving incorrect arguments does not call usage and exit
115# FIXME: giving incorrect arguments does not call usage and exit
104while [ $# -gt 0 ]
116while [ $# -gt 0 ]
@@ -106,6 +118,8 @@ do
106 case "$1" in
118 case "$1" in
107 -s|--setup) SETUP=true;;
119 -s|--setup) SETUP=true;;
108 -g|--gdb) GDB=true;;
120 -g|--gdb) GDB=true;;
121+ -p|--pinlock) PINLOCK=true;;
122+ -k|--keylock) KEYLOCK=true;;
109 -h|--help) usage;;
123 -h|--help) usage;;
110 --) shift;break;;
124 --) shift;break;;
111 esac
125 esac
tests/autopilot/unity8/emulators/main_window.py+12
@@ -47,3 +47,15 @@ class MainWindow(object):
47
47
48 def get_launcher(self):
48 def get_launcher(self):
49 return self.app.select_single("Launcher")
49 return self.app.select_single("Launcher")
50+
51+ def get_pinPadLoader(self):
52+ return self.app.select_single("QQuickLoader", objectName="pinPadLoader")
53+
54+ def get_pinPadButton(self, buttonId):
55+ return self.app.select_single("PinPadButton", objectName="pinPadButton%i" % buttonId)
56+
57+ def get_lockscreen(self):
58+ return self.app.select_single("Lockscreen")
59+
60+ def get_pinentryField(self):
61+ return self.app.select_single("TextField", objectName="pinentryField")
tests/autopilot/unity8/tests/testcases.py → tests/autopilot/unity8/tests/testhud.pyrenamed+2−2
@@ -40,7 +40,7 @@ import os
40from os import path
40from os import path
41
41
42
42
43−class TestShell(ShellTestCase, TestShellHelpers):
43+class TestHud(ShellTestCase, TestShellHelpers):
44
44
45 """Tests the Shell"""
45 """Tests the Shell"""
46
46
@@ -70,7 +70,7 @@ class TestShell(ShellTestCase, TestShellHelpers):
70 divisor = 1
70 divisor = 1
71 while (sg.get_screen_width() < self.app_width / divisor or sg.get_screen_height() < self.app_height / divisor):
71 while (sg.get_screen_width() < self.app_width / divisor or sg.get_screen_height() < self.app_height / divisor):
72 divisor = divisor * 2
72 divisor = divisor * 2
73− super(TestShell, self).setUp("%sx%s" % (self.app_width / divisor, self.app_height / divisor), "%s" % (self.grid_unit_px / divisor))
73+ super(TestHud, self).setUp("%sx%s" % (self.app_width / divisor, self.app_height / divisor), "%s" % (self.grid_unit_px / divisor))
74
74
75 dash = self.main_window.get_dash()
75 dash = self.main_window.get_dash()
76 self.assertThat(dash.showLensOnLoaded, Eventually(Equals(""), timeout=30))
76 self.assertThat(dash.showLensOnLoaded, Eventually(Equals(""), timeout=30))
tests/autopilot/unity8/tests/testlockscreen.pynew file+118
@@ -0,0 +1,118 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+# Copyright 2013 Canonical
3+#
4+# This program is free software: you can redistribute it and/or modify it
5+# under the terms of the GNU General Public License version 3, as published
6+# by the Free Software Foundation.
7+
8+
9+# This file contains general purpose test cases for Unity.
10+# Each test written in this file will be executed for a variety of
11+# configurations, such as Phone, Tablet or Desktop form factors.
12+#
13+# Sometimes there is the need to disable a certain test for a particular
14+# configuration. To do so, add this in a new line directly below your test:
15+#
16+# test_testname.blacklist = (FormFactors.Tablet, FormFactors.Desktop,)
17+#
18+# Available form factors are:
19+# FormFactors.Phone
20+# FormFactors.Tablet
21+# FormFactors.Desktop
22+
23+
24+"""Tests for the Shell"""
25+
26+from __future__ import absolute_import
27+
28+from unity8.tests import ShellTestCase, FormFactors
29+from unity8.tests.helpers import TestShellHelpers
30+
31+from autopilot.input import Mouse, Touch, Pointer
32+from testtools.matchers import Equals, NotEquals, GreaterThan, MismatchError
33+from autopilot.matchers import Eventually
34+from autopilot.display import Display
35+from autopilot.platform import model
36+
37+import unittest
38+import time
39+import os
40+from os import path
41+
42+
43+class TestLockscreens(ShellTestCase, TestShellHelpers):
44+
45+ """Tests the Lockscreens"""
46+
47+ # Scenarios:
48+ # Fill in the scenarios to run the whole test suite with multiple configurations.
49+ # Use app_width, app_height and grid_unit_px to set the apps geometry.
50+ # Set app_width and app_height to 0 to use fullscreen.
51+ # Set grid_unit_px to 0 to use the current system environment.
52+
53+ if model() == 'Desktop':
54+ scenarios = [
55+ ('Pinlock', dict(app_width=768, app_height=1280, grid_unit_px=18, lightdm_mock="single-pin")),
56+ ('Keylock', dict(app_width=768, app_height=1280, grid_unit_px=18, lightdm_mock="single-passphrase")),
57+ ]
58+ else:
59+ scenarios = [
60+ ('Pinlock', dict(app_width=0, app_height=0, grid_unit_px=0, lightdm_mock="single-pin")),
61+ ('Keylock', dict(app_width=0, app_height=0, grid_unit_px=0, lightdm_mock="single-key")),
62+ ]
63+
64+ def setUp(self):
65+ self.touch = Touch.create()
66+
67+ sg = Display().create()
68+ divisor = 1
69+ while (sg.get_screen_width() < self.app_width / divisor or sg.get_screen_height() < self.app_height / divisor):
70+ divisor = divisor * 2
71+ super(TestLockscreens, self).setUp("%sx%s" % (self.app_width / divisor, self.app_height / divisor), "%s" % (self.grid_unit_px / divisor))
72+
73+ dash = self.main_window.get_dash()
74+ self.assertThat(dash.showLensOnLoaded, Eventually(Equals(""), timeout=30))
75+
76+ def test_unlock(self):
77+ self.unlock_greeter()
78+
79+ pinPadLoader = self.main_window.get_pinPadLoader();
80+ self.assertThat(pinPadLoader.progress, Eventually(Equals(1)))
81+ lockscreen = self.main_window.get_lockscreen();
82+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
83+
84+ if self.lightdm_mock == "single-pin":
85+ self.touch.tap_object(self.main_window.get_pinPadButton(1))
86+ self.touch.tap_object(self.main_window.get_pinPadButton(2))
87+ self.touch.tap_object(self.main_window.get_pinPadButton(3))
88+ self.touch.tap_object(self.main_window.get_pinPadButton(4))
89+ self.assertThat(lockscreen.shown, Eventually(Equals(False)))
90+ else:
91+ pinentryField = self.main_window.get_pinentryField()
92+ self.touch.tap_object(pinentryField)
93+ self.keyboard.type("password\n")
94+ self.assertThat(lockscreen.shown, Eventually(Equals(False)))
95+
96+ def test_unlock_wrong(self):
97+ self.unlock_greeter()
98+
99+ pinPadLoader = self.main_window.get_pinPadLoader();
100+ self.assertThat(pinPadLoader.progress, Eventually(Equals(1)))
101+ lockscreen = self.main_window.get_lockscreen();
102+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
103+ pinentryField = self.main_window.get_pinentryField()
104+
105+ if self.lightdm_mock == "single-pin":
106+ self.touch.tap_object(self.main_window.get_pinPadButton(4))
107+ self.touch.tap_object(self.main_window.get_pinPadButton(3))
108+ self.touch.tap_object(self.main_window.get_pinPadButton(2))
109+ self.assertThat(pinentryField.text, Eventually(Equals("432")))
110+ self.touch.tap_object(self.main_window.get_pinPadButton(1))
111+
112+ self.assertThat(pinentryField.text, Eventually(Equals("")))
113+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
114+ else:
115+ self.touch.tap_object(pinentryField)
116+ self.keyboard.type("foobar\n")
117+ self.assertThat(pinentryField.text, Eventually(Equals("")))
118+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
tests/mocks/LightDM/CMakeLists.txt+2
@@ -7,6 +7,8 @@
7add_subdirectory(demo)
7add_subdirectory(demo)
8add_subdirectory(full)
8add_subdirectory(full)
9add_subdirectory(single)
9add_subdirectory(single)
10+add_subdirectory(single-pin)
11+add_subdirectory(single-passphrase)
10
12
11include_directories(
13include_directories(
12 ${CMAKE_CURRENT_SOURCE_DIR}
14 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/LightDM/full/GreeterPrivate.cpp+7−1
@@ -53,6 +53,8 @@ void GreeterPrivate::handleAuthenticate()
53 if (authenticationUser == "no-password") {
53 if (authenticationUser == "no-password") {
54 authenticated = true;
54 authenticated = true;
55 Q_EMIT q->authenticationComplete();
55 Q_EMIT q->authenticationComplete();
56+ } else if (authenticationUser == "has-pin"){
57+ Q_EMIT q->showPrompt("PIN", Greeter::PromptTypeSecret);
56 } else if (authenticationUser == "auth-error") {
58 } else if (authenticationUser == "auth-error") {
57 authenticated = false;
59 authenticated = false;
58 Q_EMIT q->authenticationComplete();
60 Q_EMIT q->authenticationComplete();
@@ -85,7 +87,11 @@ void GreeterPrivate::handleRespond(const QString &response)
85 return;
87 return;
86 }
88 }
87
89
90+ if (authenticationUser == "has-pin") {
91+ authenticated = (response == "1234");
92+ } else {
88− authenticated = (response == "password");
93+ authenticated = (response == "password");
94+ }
89 Q_EMIT q->authenticationComplete();
95 Q_EMIT q->authenticationComplete();
90}
96}
91
97
tests/mocks/LightDM/full/UsersModelPrivate.cpp+1
@@ -27,6 +27,7 @@ UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
27 entries =
27 entries =
28 {
28 {
29 { "has-password", "Has Password", 0, 0, false, false, 0, 0 },
29 { "has-password", "Has Password", 0, 0, false, false, 0, 0 },
30+ { "has-pin", "Has PIN", 0, 0, false, false, 0, 0 },
30 { "different-prompt", "Different Prompt", 0, 0, false, false, 0, 0 },
31 { "different-prompt", "Different Prompt", 0, 0, false, false, 0, 0 },
31 { "no-password", "No Password", 0, 0, false, false, 0, 0 },
32 { "no-password", "No Password", 0, 0, false, false, 0, 0 },
32 { "auth-error", "Auth Error", 0, 0, false, false, 0, 0 },
33 { "auth-error", "Auth Error", 0, 0, false, false, 0, 0 },
tests/mocks/LightDM/single-passphrase/CMakeLists.txtnew file+21
@@ -0,0 +1,21 @@
1+set(LibLightDM_SOURCES
2+ ../Greeter.cpp
3+ ../UsersModel.cpp
4+ ../InfographicModel.cpp
5+ GreeterPrivate.cpp
6+ UsersModelPrivate.cpp
7+ InfographicModelPrivate.cpp
8+ ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
9+ )
10+
11+
12+add_library(MockLightDM-single-passphrase SHARED ${LibLightDM_SOURCES})
13+
14+qt5_use_modules(MockLightDM-single-passphrase Gui)
15+
16+set_target_properties(MockLightDM-single-passphrase PROPERTIES
17+ OUTPUT_NAME lightdm-qt5-2)
18+
19+install(TARGETS MockLightDM-single-passphrase
20+ DESTINATION ${SHELL_APP_DIR}/plugins/mocks/LightDM/single-passphrase
21+ )
tests/mocks/LightDM/single-passphrase/GreeterPrivate.cppnew file+53
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "../Greeter.h"
20+#include "../GreeterPrivate.h"
21+
22+#include <QDebug>
23+
24+namespace QLightDM
25+{
26+
27+GreeterPrivate::GreeterPrivate(Greeter* parent)
28+ : authenticated(false),
29+ authenticationUser(),
30+ q_ptr(parent)
31+{
32+}
33+
34+void GreeterPrivate::handleAuthenticate()
35+{
36+ Q_Q(Greeter);
37+
38+ qDebug() << "handleAuthentication called!" << authenticationUser;
39+
40+ Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
41+}
42+
43+void GreeterPrivate::handleRespond(const QString &response)
44+{
45+ Q_Q(Greeter);
46+
47+
48+ authenticated = (response == "password");
49+ qDebug() << "responding" << response << authenticated;
50+ Q_EMIT q->authenticationComplete();
51+}
52+
53+}
tests/mocks/LightDM/single-passphrase/InfographicModelPrivate.cppnew file+61
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Pete Woods <pete.woods@canonical.com>
17+ */
18+
19+#include "../InfographicModelPrivate.h"
20+
21+namespace QLightDM
22+{
23+
24+void InfographicModelPrivate::generateFakeData()
25+{
26+ std::default_random_engine generator;
27+ std::normal_distribution<qreal> distribution(0.5, 0.2);
28+ auto rand = std::bind(distribution, generator);
29+
30+ QColor orange = QColor::fromRgbF(0.9, 0.3, 0.1, 1.0);
31+ QColor yellow = QColor::fromRgbF(1.0, 0.6, 0.0, 1.0);
32+ QColor red = QColor::fromRgbF(0.8, 0.0, 0.0, 1.0);
33+ QColor darkPurple = QColor::fromRgbF(0.5, 0.2, 0.3, 1.0);
34+ QColor lightPurple = QColor::fromRgbF(0.8, 0.1, 0.8, 1.0);
35+ QColor pink(QColor::fromRgbF(0.75, 0.13, 0.75));
36+
37+ InfographicColorTheme orangeTheme(orange, orange, orange);
38+ InfographicColorTheme yellowTheme(yellow, yellow, yellow);
39+ InfographicColorTheme redTheme(red, red, red);
40+ InfographicColorTheme darkPurpleTheme(darkPurple, darkPurple, darkPurple);
41+ InfographicColorTheme lightPurpleTheme(lightPurple, lightPurple,
42+ lightPurple);
43+ InfographicColorTheme pinkTheme(pink, pink, pink);
44+
45+ {
46+ QVariantList firstMonth;
47+ while (firstMonth.size() < 17)
48+ firstMonth.push_back(QVariant(rand()));
49+ while (firstMonth.size() < 31)
50+ firstMonth.push_back(QVariant());
51+ QVariantList secondMonth;
52+ while (secondMonth.size() < 31)
53+ secondMonth.push_back(QVariant(rand()));
54+ QSharedPointer<InfographicData> data(
55+ new InfographicData("<b>5km</b> travelled", yellowTheme,
56+ firstMonth, orangeTheme, secondMonth, this));
57+ m_fakeData.insert("has-pin", data);
58+ }
59+}
60+
61+}
tests/mocks/LightDM/single-passphrase/UsersModelPrivate.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "../UsersModelPrivate.h"
20+
21+namespace QLightDM
22+{
23+
24+UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
25+ : q_ptr(parent)
26+{
27+ entries =
28+ {
29+ { "has-pin", "Has PIN", 0, 0, false, false, 0, 0 },
30+ };
31+}
32+
33+}
tests/mocks/LightDM/single-pin/CMakeLists.txtnew file+21
@@ -0,0 +1,21 @@
1+set(LibLightDM_SOURCES
2+ ../Greeter.cpp
3+ ../UsersModel.cpp
4+ ../InfographicModel.cpp
5+ GreeterPrivate.cpp
6+ UsersModelPrivate.cpp
7+ InfographicModelPrivate.cpp
8+ ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
9+ )
10+
11+
12+add_library(MockLightDM-single-pin SHARED ${LibLightDM_SOURCES})
13+
14+qt5_use_modules(MockLightDM-single-pin Gui)
15+
16+set_target_properties(MockLightDM-single-pin PROPERTIES
17+ OUTPUT_NAME lightdm-qt5-2)
18+
19+install(TARGETS MockLightDM-single-pin
20+ DESTINATION ${SHELL_APP_DIR}/plugins/mocks/LightDM/single-pin
21+ )
tests/mocks/LightDM/single-pin/GreeterPrivate.cppnew file+53
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "../Greeter.h"
20+#include "../GreeterPrivate.h"
21+
22+#include <QDebug>
23+
24+namespace QLightDM
25+{
26+
27+GreeterPrivate::GreeterPrivate(Greeter* parent)
28+ : authenticated(false),
29+ authenticationUser(),
30+ q_ptr(parent)
31+{
32+}
33+
34+void GreeterPrivate::handleAuthenticate()
35+{
36+ Q_Q(Greeter);
37+
38+ qDebug() << "handleAuthentication called!" << authenticationUser;
39+
40+ Q_EMIT q->showPrompt("PIN:", Greeter::PromptTypeSecret);
41+}
42+
43+void GreeterPrivate::handleRespond(const QString &response)
44+{
45+ Q_Q(Greeter);
46+
47+
48+ authenticated = (response == "1234");
49+ qDebug() << "responding" << response << authenticated;
50+ Q_EMIT q->authenticationComplete();
51+}
52+
53+}
tests/mocks/LightDM/single-pin/InfographicModelPrivate.cppnew file+61
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Pete Woods <pete.woods@canonical.com>
17+ */
18+
19+#include "../InfographicModelPrivate.h"
20+
21+namespace QLightDM
22+{
23+
24+void InfographicModelPrivate::generateFakeData()
25+{
26+ std::default_random_engine generator;
27+ std::normal_distribution<qreal> distribution(0.5, 0.2);
28+ auto rand = std::bind(distribution, generator);
29+
30+ QColor orange = QColor::fromRgbF(0.9, 0.3, 0.1, 1.0);
31+ QColor yellow = QColor::fromRgbF(1.0, 0.6, 0.0, 1.0);
32+ QColor red = QColor::fromRgbF(0.8, 0.0, 0.0, 1.0);
33+ QColor darkPurple = QColor::fromRgbF(0.5, 0.2, 0.3, 1.0);
34+ QColor lightPurple = QColor::fromRgbF(0.8, 0.1, 0.8, 1.0);
35+ QColor pink(QColor::fromRgbF(0.75, 0.13, 0.75));
36+
37+ InfographicColorTheme orangeTheme(orange, orange, orange);
38+ InfographicColorTheme yellowTheme(yellow, yellow, yellow);
39+ InfographicColorTheme redTheme(red, red, red);
40+ InfographicColorTheme darkPurpleTheme(darkPurple, darkPurple, darkPurple);
41+ InfographicColorTheme lightPurpleTheme(lightPurple, lightPurple,
42+ lightPurple);
43+ InfographicColorTheme pinkTheme(pink, pink, pink);
44+
45+ {
46+ QVariantList firstMonth;
47+ while (firstMonth.size() < 17)
48+ firstMonth.push_back(QVariant(rand()));
49+ while (firstMonth.size() < 31)
50+ firstMonth.push_back(QVariant());
51+ QVariantList secondMonth;
52+ while (secondMonth.size() < 31)
53+ secondMonth.push_back(QVariant(rand()));
54+ QSharedPointer<InfographicData> data(
55+ new InfographicData("<b>5km</b> travelled", yellowTheme,
56+ firstMonth, orangeTheme, secondMonth, this));
57+ m_fakeData.insert("has-pin", data);
58+ }
59+}
60+
61+}
tests/mocks/LightDM/single-pin/UsersModelPrivate.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "../UsersModelPrivate.h"
20+
21+namespace QLightDM
22+{
23+
24+UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
25+ : q_ptr(parent)
26+{
27+ entries =
28+ {
29+ { "has-pin", "Has PIN", 0, 0, false, false, 0, 0 },
30+ };
31+}
32+
33+}
tests/qmltests/CMakeLists.txt+2
@@ -47,6 +47,8 @@ add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMA
47 ${CMAKE_BINARY_DIR}/tests/mocks)
47 ${CMAKE_BINARY_DIR}/tests/mocks)
48add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
48add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
49add_qml_test(Dash/People PeopleFilterGrid IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
49add_qml_test(Dash/People PeopleFilterGrid IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
50+add_qml_test(Greeter Lockscreen IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/mocks
51+ PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
50add_qml_test(Greeter Tablet IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/mocks
52add_qml_test(Greeter Tablet IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/mocks
51 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
53 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
52add_qml_test(Greeter Phone IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/mocks
54add_qml_test(Greeter Phone IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/mocks
tests/qmltests/Greeter/tst_Lockscreen.qmlnew file+205
@@ -0,0 +1,205 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import ".."
20+import "../../../Greeter"
21+import Ubuntu.Components 0.1
22+import LightDM 0.1 as LightDM
23+import Unity.Test 0.1 as UT
24+
25+Rectangle {
26+ width: units.gu(60)
27+ height: units.gu(80)
28+ color: "orange"
29+
30+ Lockscreen {
31+ id: lockscreen
32+ anchors.fill: parent
33+ anchors.rightMargin: units.gu(18)
34+ placeholderText: "Please enter your PIN"
35+ alphaNumeric: pinPadCheckBox.checked
36+ pinLength: pinLengthTextField.text
37+ username: "Lola"
38+ }
39+
40+ Connections {
41+ target: lockscreen
42+
43+ onEmergencyCall: emergencyCheckBox.checked = true
44+ onUnlocked: unlockedCheckBox.checked = true
45+ }
46+
47+ Connections {
48+ target: LightDM.Greeter
49+
50+ onShowPrompt: {
51+ if (text.indexOf("PIN") >= 0) {
52+ lockscreen.alphaNumeric = false
53+ } else {
54+ lockscreen.alphaNumeric = true
55+ }
56+ lockscreen.placeholderText = text;
57+ }
58+ }
59+
60+ Rectangle {
61+ anchors.fill: parent
62+ anchors.leftMargin: lockscreen.width
63+ color: "lightgray"
64+
65+ Column {
66+ anchors.fill: parent
67+ anchors.margins: units.gu(1)
68+
69+ Row {
70+ CheckBox {
71+ id: pinPadCheckBox
72+ }
73+ Label {
74+ text: "Alphanumeric"
75+ anchors.verticalCenter: parent.verticalCenter
76+ }
77+ }
78+ Row {
79+ CheckBox {
80+ id: emergencyCheckBox
81+ }
82+ Label {
83+ text: "Emergency Call"
84+ anchors.verticalCenter: parent.verticalCenter
85+ }
86+ }
87+ Row {
88+ TextField {
89+ id: pinLengthTextField
90+ width: units.gu(7)
91+ text: "4"
92+ }
93+ Label {
94+ text: "PIN length"
95+ }
96+ }
97+ Label {
98+ id: pinLabel
99+ anchors.verticalCenter: parent.verticalCenter
100+ }
101+ Row {
102+ CheckBox {
103+ id: unlockedCheckBox
104+ }
105+ Label {
106+ text: "Unlocked signal"
107+ anchors.verticalCenter: parent.verticalCenter
108+ }
109+ }
110+ Button {
111+ text: "start auth (1234)"
112+ onClicked: LightDM.Greeter.authenticate("has-pin")
113+ }
114+ Button {
115+ text: "start auth (password)"
116+ onClicked: LightDM.Greeter.authenticate("has-password")
117+ }
118+ }
119+ }
120+
121+ UT.UnityTestCase {
122+ name: "Lockscreen"
123+ when: windowShown
124+
125+ function test_loading_data() {
126+ return [
127+ {tag: "numeric", alphanumeric: false, pinPadAvailable: true },
128+ {tag: "alphanumeric", alphanumeric: true, pinPadAvailable: false }
129+ ]
130+ }
131+
132+ function test_loading(data) {
133+ lockscreen.alphaNumeric = data.alphanumeric
134+ waitForRendering(lockscreen)
135+ if (data.pinPadAvailable) {
136+ compare(findChild(lockscreen, "pinPadButton8").text, "8", "Could not find number 8 on PinPad")
137+ } else {
138+ compare(findChild(lockscreen, "pinPadButton8"), undefined, "Could find number 8 on PinPad even though it should be only OSK")
139+ }
140+ }
141+
142+ function test_emergency_call_data() {
143+ return [
144+ {tag: "numeric", alphanumeric: false },
145+ {tag: "alphanumeric", alphanumeric: true }
146+ ]
147+ }
148+
149+ function test_emergency_call(data) {
150+ emergencyCheckBox.checked = false
151+ lockscreen.alphaNumeric = data.alphanumeric
152+ waitForRendering(lockscreen)
153+ var emergencyButton = findChild(lockscreen, "emergencyCallIcon")
154+ mouseClick(emergencyButton, units.gu(1), units.gu(1))
155+ tryCompare(emergencyCheckBox, "checked", true)
156+
157+ }
158+
159+ function test_labels_data() {
160+ return [
161+ {tag: "numeric", alphanumeric: false, placeholderText: "Please enter your PIN", username: "foobar" },
162+ {tag: "alphanumeric", alphanumeric: true, placeholderText: "Please enter your password", username: "Lola" }
163+ ]
164+ }
165+
166+ function test_labels(data) {
167+ lockscreen.alphaNumeric = data.alphanumeric
168+ lockscreen.placeholderText = data.placeholderText
169+ waitForRendering(lockscreen)
170+ compare(findChild(lockscreen, "pinentryField").placeholderText, data.placeholderText, "Placeholdertext is not what it should be")
171+ if (data.alphanumeric) {
172+ compare(findChild(lockscreen, "greeterLabel").text, "Hello " + data.username, "Greeter is not set correctly")
173+ }
174+ }
175+
176+
177+ function test_unlock_data() {
178+ return [
179+ {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", unlockedSignal: true},
180+ {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", unlockedSignal: true},
181+ {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", unlockedSignal: false},
182+ {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", unlockedSignal: false},
183+ ]
184+ }
185+
186+ function test_unlock(data) {
187+ unlockedCheckBox.checked = false
188+ LightDM.Greeter.authenticate(data.username)
189+
190+ if (data.alphanumeric) {
191+ var inputField = findChild(lockscreen, "pinentryField")
192+ mouseClick(inputField, units.gu(1), units.gu(1))
193+ typeString(data.password)
194+ keyClick(Qt.Key_Enter)
195+ } else {
196+ for (var i = 0; i < data.password.length; ++i) {
197+ var character = data.password.charAt(i)
198+ var button = findChild(lockscreen, "pinPadButton" + character)
199+ mouseClick(button, units.gu(1), units.gu(1))
200+ }
201+ }
202+ tryCompare(unlockedCheckBox, "checked", data.unlockedSignal)
203+ }
204+ }
205+}