on xenial help sign in
Add Lockscreens

- A PIN Lockscreen - A Passphrase Lockscreen Whole history available in lp:~unity-team/unity/phablet-pinlock. Approved by PS Jenkins bot, Michael Terry.

commit320fda29ebcb95f18c69c895dfe8a41f36a8c63f authorMichael Zanetti <michael.zanetti@canonical.com> dateWed 12 Jun 2013, 08:46 (13 years ago) parentfe16363479deda
26 files changed+1228−40
Components/WrongPasswordAnimation.qml+46 Greeter/Lockscreen.qml+149 Greeter/LoginList.qml+3−23 Greeter/PassphraseLockscreen.qml+87 Greeter/PinLockscreen.qml+111 Greeter/PinPadButton.qml+59 Shell.qml+52−8 plugins/LightDM/CMakeLists.txt+1−1 run+17−1 run_on_device+18−4 tests/autopilot/unity8/emulators/main_window.py+12 tests/autopilot/unity8/tests/testcases.py → tests/autopilot/unity8/tests/testhud.py+2−2 tests/autopilot/unity8/tests/testlockscreen.py+118 tests/mocks/LightDM/CMakeLists.txt+2 tests/mocks/LightDM/full/GreeterPrivate.cpp+7−1 tests/mocks/LightDM/full/UsersModelPrivate.cpp+1 tests/mocks/LightDM/single-passphrase/CMakeLists.txt+21 tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp+53 tests/mocks/LightDM/single-passphrase/InfographicModelPrivate.cpp+61 tests/mocks/LightDM/single-passphrase/UsersModelPrivate.cpp+33 tests/mocks/LightDM/single-pin/CMakeLists.txt+21 tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+53 tests/mocks/LightDM/single-pin/InfographicModelPrivate.cpp+61 tests/mocks/LightDM/single-pin/UsersModelPrivate.cpp+33 tests/qmltests/CMakeLists.txt+2 tests/qmltests/Greeter/tst_Lockscreen.qml+205
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 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import LightDM 0.1 as LightDM
20+import "../Components"
2021
2122Item {
2223 id: root
@@ -218,30 +219,9 @@ Item {
218219 source: "graphics/icon_arrow.png"
219220 }
220221
221− SequentialAnimation {
222+ WrongPasswordAnimation {
222223 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
239− easing.type: Easing.OutElastic
240− properties: "anchors.horizontalCenterOffset"
241− to: 0
242− duration: 400
243− easing.overshoot: units.gu(2)
244− }
224+ target: passwordInput
245225 }
246226
247227 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 {
6767 }
6868
6969 readonly property bool fullscreenMode: {
70− if (greeter.shown) {
70+ if (greeter.shown || lockscreen.shown) {
7171 return false;
7272 } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
7373 return mainStage.switchingFromFullscreenToFullscreen;
@@ -124,7 +124,6 @@ FocusScope {
124124
125125 Item {
126126 id: underlay
127−
128127 anchors.fill: parent
129128 visible: !(panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
130129 && (stages.fullyHidden
@@ -148,7 +147,7 @@ FocusScope {
148147 Dash {
149148 id: dash
150149
151− available: !greeter.shown
150+ available: !greeter.shown && !lockscreen.shown
152151 hides: [stages, launcher, panel.indicators]
153152 shown: disappearingAnimationProgress !== 1.0
154153 enabled: disappearingAnimationProgress === 0.0
@@ -301,7 +300,7 @@ FocusScope {
301300 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
302301 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
303302
304− available: !greeter.shown && enabled
303+ available: !greeter.shown && !lockscreen.shown && enabled
305304 hides: [launcher, panel.indicators]
306305 shown: false
307306 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
@@ -352,6 +351,46 @@ FocusScope {
352351 orientation: Qt.Horizontal
353352 }
354353
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+
355394 Greeter {
356395 id: greeter
357396
@@ -360,7 +399,6 @@ FocusScope {
360399 shown: true
361400 showAnimation: StandardAnimation { property: "x"; to: greeterRevealer.openedValue }
362401 hideAnimation: StandardAnimation { property: "x"; to: greeterRevealer.closedValue }
363−
364402 y: panel.panelHeight
365403 width: parent.width
366404 height: parent.height - panel.panelHeight
@@ -370,6 +408,12 @@ FocusScope {
370408
371409 onShownChanged: {
372410 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+ }
373417 greeter.forceActiveFocus();
374418 // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
375419 // the stage was actually shown
@@ -397,7 +441,7 @@ FocusScope {
397441
398442 InputFilterArea {
399443 anchors.fill: parent
400− blockInput: greeter.shown
444+ blockInput: greeter.shown || lockscreen.shown
401445 }
402446
403447 Revealer {
@@ -426,7 +470,7 @@ FocusScope {
426470 hides: [launcher]
427471 }
428472 fullscreenMode: shell.fullscreenMode
429− searchVisible: !greeter.shown
473+ searchVisible: !greeter.shown && !lockscreen.shown
430474
431475 InputFilterArea {
432476 anchors.fill: parent
@@ -440,7 +484,7 @@ FocusScope {
440484 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
441485 height: parent.height
442486
443− available: !greeter.shown && !panel.indicators.shown
487+ available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown
444488 shown: false
445489 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
446490 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
4040
4141install(FILES qmldir
4242 DESTINATION ${SHELL_APP_DIR}/plugins/LightDM
43− )
43+ )
run+17−1
@@ -4,6 +4,8 @@ QML_PHONE_SHELL_PATH=./builddir/unity8
44export LD_LIBRARY_PATH=$PWD/../unity_build/build/lib
55GDB=false
66FAKE=false
7+PINLOCK=false
8+KEYLOCK=false
79MOUSE_TOUCH=true
810
911usage() {
@@ -11,6 +13,8 @@ usage() {
1113 echo "Script to run the shell.\n" >&2
1214 echo "OPTIONS:" >&2
1315 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
1418 echo " -g, --gdb Run through gdb." >&2
1519 echo " -h, --help Show this help." >&2
1620 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
@@ -18,7 +22,7 @@ usage() {
1822 exit 1
1923}
2024
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" -- "$@"`
2226[ $? -ne 0 ] && usage
2327eval set -- "$ARGS"
2428
@@ -26,6 +30,8 @@ while [ $# -gt 0 ]
2630do
2731 case "$1" in
2832 -f|--fake) FAKE=true;;
33+ -p|--pinlock) PINLOCK=true;;
34+ -k|--keylock) KEYLOCK=true;;
2935 -g|--gdb) GDB=true;;
3036 -h|--help) usage;;
3137 -m|--nomousetouch) MOUSE_TOUCH=false;;
@@ -39,6 +45,16 @@ if $FAKE; then
3945 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single
4046fi
4147
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+
4258# Force icon theme if running on the desktop, otherwise gnome theme (if running
4359# on Ubuntu Desktop) will be used and icons won't be found
4460if [ -n "$DESKTOP_SESSION" ]; then
run_on_device+18−4
@@ -11,6 +11,8 @@ TARGET_DEBUG_PORT=3768
1111RUN_OPTIONS=-qmljsdebugger=port:$TARGET_DEBUG_PORT
1212SETUP=false
1313GDB=false
14+PINLOCK=false
15+KEYLOCK=false
1416SUDO="echo $PASSWORD | sudo -S"
1517NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
1618
@@ -19,6 +21,8 @@ usage() {
1921 echo "Script to setup a build environment for the shell and sync build and run it on the device\n"
2022 echo "OPTIONS:"
2123 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"
2226 echo ""
2327 echo "IMPORTANT:"
2428 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() {
8690 fi
8791 adb shell pkill qml-phone-shell
8892 adb shell pkill $BINARY
93+ ARGS="--nomousetouch"
8994 if $GDB; then
90− exec_with_ssh "cd $CODE_DIR/ && ./run --gdb --nomousetouch -- $RUN_OPTIONS"
91− else
92− exec_with_ssh "cd $CODE_DIR && ./run --nomousetouch -- $RUN_OPTIONS"
95+ ARGS="$ARGS --gdb"
9396 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+
94106 if [ $CONTAINS_SHELL -eq 0 ]; then
95107 echo "Restoring $SERVICEFILE on device from $SERVICEFILE.backup"
96108 adb shell cp $SERVICEFILE.backup $SERVICEFILE
@@ -98,7 +110,7 @@ run() {
98110 fi
99111}
100112
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" "$@"`
102114
103115# FIXME: giving incorrect arguments does not call usage and exit
104116while [ $# -gt 0 ]
@@ -106,6 +118,8 @@ do
106118 case "$1" in
107119 -s|--setup) SETUP=true;;
108120 -g|--gdb) GDB=true;;
121+ -p|--pinlock) PINLOCK=true;;
122+ -k|--keylock) KEYLOCK=true;;
109123 -h|--help) usage;;
110124 --) shift;break;;
111125 esac
tests/autopilot/unity8/emulators/main_window.py+12
@@ -47,3 +47,15 @@ class MainWindow(object):
4747
4848 def get_launcher(self):
4949 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
4040from os import path
4141
4242
43−class TestShell(ShellTestCase, TestShellHelpers):
43+class TestHud(ShellTestCase, TestShellHelpers):
4444
4545 """Tests the Shell"""
4646
@@ -70,7 +70,7 @@ class TestShell(ShellTestCase, TestShellHelpers):
7070 divisor = 1
7171 while (sg.get_screen_width() < self.app_width / divisor or sg.get_screen_height() < self.app_height / divisor):
7272 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))
7474
7575 dash = self.main_window.get_dash()
7676 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 @@
77add_subdirectory(demo)
88add_subdirectory(full)
99add_subdirectory(single)
10+add_subdirectory(single-pin)
11+add_subdirectory(single-passphrase)
1012
1113include_directories(
1214 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/LightDM/full/GreeterPrivate.cpp+7−1
@@ -53,6 +53,8 @@ void GreeterPrivate::handleAuthenticate()
5353 if (authenticationUser == "no-password") {
5454 authenticated = true;
5555 Q_EMIT q->authenticationComplete();
56+ } else if (authenticationUser == "has-pin"){
57+ Q_EMIT q->showPrompt("PIN", Greeter::PromptTypeSecret);
5658 } else if (authenticationUser == "auth-error") {
5759 authenticated = false;
5860 Q_EMIT q->authenticationComplete();
@@ -85,7 +87,11 @@ void GreeterPrivate::handleRespond(const QString &response)
8587 return;
8688 }
8789
88− authenticated = (response == "password");
90+ if (authenticationUser == "has-pin") {
91+ authenticated = (response == "1234");
92+ } else {
93+ authenticated = (response == "password");
94+ }
8995 Q_EMIT q->authenticationComplete();
9096}
9197
tests/mocks/LightDM/full/UsersModelPrivate.cpp+1
@@ -27,6 +27,7 @@ UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
2727 entries =
2828 {
2929 { "has-password", "Has Password", 0, 0, false, false, 0, 0 },
30+ { "has-pin", "Has PIN", 0, 0, false, false, 0, 0 },
3031 { "different-prompt", "Different Prompt", 0, 0, false, false, 0, 0 },
3132 { "no-password", "No Password", 0, 0, false, false, 0, 0 },
3233 { "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
4747 ${CMAKE_BINARY_DIR}/tests/mocks)
4848add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
4949add_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")
5052add_qml_test(Greeter Tablet IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/mocks
5153 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
5254add_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+}