Merge lp:~osomon/webbrowser-app/webProcessStatus into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 1026
Merged at revision: 1159
Proposed branch: lp:~osomon/webbrowser-app/webProcessStatus
Merge into: lp:webbrowser-app
Diff against target: 650 lines (+460/-34)
10 files modified
debian/control (+1/-0)
src/app/WebProcessMonitor.qml (+79/-0)
src/app/webbrowser/Browser.qml (+40/-19)
src/app/webbrowser/SadTab.qml (+92/-0)
src/app/webbrowser/tabs-model.cpp (+4/-3)
tests/autopilot/webbrowser_app/emulators/browser.py (+16/-0)
tests/autopilot/webbrowser_app/tests/__init__.py (+11/-0)
tests/autopilot/webbrowser_app/tests/test_sad_tab.py (+74/-0)
tests/unittests/qml/tst_WebProcessMonitor.qml (+108/-0)
tests/unittests/tabs-model/tst_TabsModelTests.cpp (+35/-12)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/webProcessStatus
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Riccardo Padovani (community) Approve
Ubuntu Phablet Team Pending
Review via email: mp+259040@code.launchpad.net

Commit message

Display a friendly message when the renderer process crashes or is killed.
This adds a runtime dependency for webbrowser-app-autopilot on python3-psutil.

To post a comment you must log in.
1009. By Olivier Tilloy

Revert unnecessary version bump.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1010. By Olivier Tilloy

Also bump the build dependency on oxide to allow unit tests to run at build time.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1011. By Olivier Tilloy

Count the number of retries (but keep it limited to one for now).

1012. By Olivier Tilloy

Merge the latest changes from trunk and resolve a conflict.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1013. By Olivier Tilloy

Simplify the code a bit.

1014. By Olivier Tilloy

Merge the latest changes from trunk and resolve a couple of conflicts.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1015. By Olivier Tilloy

Make the SadTab component browser-specific.

1016. By Olivier Tilloy

Updated SadTab to match visual specification.

1017. By Olivier Tilloy

Fix the sad tab disappearing after a minute.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1018. By Olivier Tilloy

Merge the latest changes from trunk and resolve conflicts.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1019. By Olivier Tilloy

Merge the latest changes from trunk and resolve a conflict.

1020. By Olivier Tilloy

Fix autopilot tests on desktop where closing the last open tab exits the application.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1021. By Olivier Tilloy

Merge the latest changes from trunk.

1022. By Olivier Tilloy

Update imports.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Riccardo Padovani (rpadovani) wrote :

Useful function, thanks!

lgtm, just a note in the code I don't understand.

review: Needs Information
1023. By Olivier Tilloy

Merge the latest changes from trunk.

1024. By Olivier Tilloy

Fix closing the current tab (the assumption that the current tab is always the first one is not valid any longer).

1025. By Olivier Tilloy

Use name() instead of exe() to avoid raising an AccessDenied exception.

Revision history for this message
Olivier Tilloy (osomon) wrote :

> I don't understand this: if it should close the current tab,
> why does it close the first?

Very good catch, thanks Riccardo!
The assumption [current tab == first tab] used to be true when we supported only the mobile form factor, where the current tab was always pushed to the top of the stack. This is not valid any longer, so I updated the code to reflect that.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1026. By Olivier Tilloy

Update the tab model’s current index when a tab before the current one is removed.

Revision history for this message
Riccardo Padovani (rpadovani) wrote :

Lgtm now, thanks!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2015-08-25 13:56:58 +0000
3+++ debian/control 2015-09-02 16:48:16 +0000
4@@ -122,6 +122,7 @@
5 autopilot-qt5,
6 python3-autopilot,
7 python3-fixtures,
8+ python3-psutil,
9 ubuntu-ui-toolkit-autopilot,
10 webbrowser-app (>= ${binary:Version}),
11 Description: Ubuntu web browser autopilot tests
12
13=== added file 'src/app/WebProcessMonitor.qml'
14--- src/app/WebProcessMonitor.qml 1970-01-01 00:00:00 +0000
15+++ src/app/WebProcessMonitor.qml 2015-09-02 16:48:16 +0000
16@@ -0,0 +1,79 @@
17+/*
18+ * Copyright 2015 Canonical Ltd.
19+ *
20+ * This file is part of webbrowser-app.
21+ *
22+ * webbrowser-app is free software; you can redistribute it and/or modify
23+ * it under the terms of the GNU General Public License as published by
24+ * the Free Software Foundation; version 3.
25+ *
26+ * webbrowser-app is distributed in the hope that it will be useful,
27+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
28+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29+ * GNU General Public License for more details.
30+ *
31+ * You should have received a copy of the GNU General Public License
32+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
33+ */
34+
35+import QtQuick 2.4
36+import com.canonical.Oxide 1.8 as Oxide
37+
38+Item {
39+ id: monitor
40+
41+ visible: false
42+
43+ property var webview: null
44+
45+ readonly property bool killed: webview &&
46+ (webview.webProcessStatus == Oxide.WebView.WebProcessKilled)
47+ readonly property bool crashed: webview &&
48+ (webview.webProcessStatus == Oxide.WebView.WebProcessCrashed)
49+
50+ // When the renderer process is killed (most likely by the system’s
51+ // OOM killer), try to reload the page once, and if this results in
52+ // the process being killed again within one minute, then display
53+ // the sad tab.
54+
55+ readonly property int killedRetries: internal.killedRetries
56+
57+ QtObject {
58+ id: internal
59+ property int killedRetries: 0
60+ }
61+
62+ Connections {
63+ target: webview
64+ onWebProcessStatusChanged: {
65+ if (webview.webProcessStatus == Oxide.WebView.WebProcessKilled) {
66+ if (internal.killedRetries == 0) {
67+ // Do not attempt reloading right away, this would result in a crash
68+ delayedReload.restart()
69+ }
70+ }
71+ }
72+ }
73+
74+ Timer {
75+ id: delayedReload
76+ interval: 100
77+ onTriggered: {
78+ monitorTimer.restart()
79+ monitor.webview.reload()
80+ internal.killedRetries++
81+ }
82+ }
83+
84+ Timer {
85+ id: monitorTimer
86+ interval: 60000 // 1 minute
87+ onTriggered: internal.killedRetries = 0
88+ }
89+
90+ onWebviewChanged: {
91+ internal.killedRetries = 0
92+ delayedReload.stop()
93+ monitorTimer.stop()
94+ }
95+}
96
97=== modified file 'src/app/webbrowser/Browser.qml'
98--- src/app/webbrowser/Browser.qml 2015-08-27 14:02:05 +0000
99+++ src/app/webbrowser/Browser.qml 2015-09-02 16:48:16 +0000
100@@ -206,6 +206,7 @@
101
102 Loader {
103 id: newTabViewLoader
104+
105 anchors {
106 fill: tabContainer
107 topMargin: (chrome.state == "shown") ? chrome.height : 0
108@@ -290,6 +291,27 @@
109 }
110 }
111
112+ Loader {
113+ anchors {
114+ fill: tabContainer
115+ topMargin: (chrome.state == "shown") ? chrome.height : 0
116+ }
117+
118+ active: webProcessMonitor.crashed || (webProcessMonitor.killed && !currentWebview.loading)
119+
120+ sourceComponent: SadTab {
121+ webview: currentWebview
122+ onCloseTabRequested: internal.closeCurrentTab()
123+ }
124+
125+ WebProcessMonitor {
126+ id: webProcessMonitor
127+ webview: currentWebview
128+ }
129+
130+ asynchronous: true
131+ }
132+
133 SearchEngine {
134 id: currentSearchEngine
135 searchPaths: searchEnginesSearchPaths
136@@ -603,16 +625,7 @@
137 }
138 chromeOffset: chrome.height - invisibleTabChrome.height
139 onTabSelected: recentView.closeAndSwitchToTab(index)
140- onTabClosed: {
141- var tab = tabsModel.remove(index)
142- if (tab) {
143- tab.close()
144- }
145- if (tabsModel.count === 0) {
146- browser.openUrlInNewTab("", true)
147- recentView.reset()
148- }
149- }
150+ onTabClosed: internal.closeTab(index)
151 }
152
153 Toolbar {
154@@ -1192,6 +1205,23 @@
155 }
156 }
157
158+ function closeTab(index) {
159+ var tab = tabsModel.remove(index)
160+ if (tab) {
161+ tab.close()
162+ }
163+ if (tabsModel.count === 0) {
164+ browser.openUrlInNewTab("", true)
165+ recentView.reset()
166+ }
167+ }
168+
169+ function closeCurrentTab() {
170+ if (tabsModel.count > 0) {
171+ closeTab(tabsModel.currentIndex)
172+ }
173+ }
174+
175 function switchToTab(index) {
176 tabsModel.currentIndex = index
177 var tab = tabsModel.currentTab
178@@ -1205,15 +1235,6 @@
179 }
180 }
181
182- function closeCurrentTab() {
183- if (tabsModel.count > 0) {
184- var tab = tabsModel.remove(tabsModel.currentIndex)
185- if (tab) {
186- tab.close()
187- }
188- }
189- }
190-
191 function focusAddressBar(selectContent) {
192 chrome.forceActiveFocus()
193 Qt.inputMethod.show() // work around http://pad.lv/1316057
194
195=== added file 'src/app/webbrowser/SadTab.qml'
196--- src/app/webbrowser/SadTab.qml 1970-01-01 00:00:00 +0000
197+++ src/app/webbrowser/SadTab.qml 2015-09-02 16:48:16 +0000
198@@ -0,0 +1,92 @@
199+/*
200+ * Copyright 2015 Canonical Ltd.
201+ *
202+ * This file is part of webbrowser-app.
203+ *
204+ * webbrowser-app is free software; you can redistribute it and/or modify
205+ * it under the terms of the GNU General Public License as published by
206+ * the Free Software Foundation; version 3.
207+ *
208+ * webbrowser-app is distributed in the hope that it will be useful,
209+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
210+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
211+ * GNU General Public License for more details.
212+ *
213+ * You should have received a copy of the GNU General Public License
214+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
215+ */
216+
217+import QtQuick 2.4
218+import Ubuntu.Components 1.3
219+import com.canonical.Oxide 1.8 as Oxide
220+
221+Rectangle {
222+ property var webview
223+
224+ signal closeTabRequested()
225+
226+ Column {
227+ anchors {
228+ fill: parent
229+ margins: units.gu(4)
230+ }
231+ spacing: units.gu(4)
232+
233+ Image {
234+ anchors.horizontalCenter: parent.horizontalCenter
235+ source: "assets/tab-error.png"
236+ }
237+
238+ Label {
239+ anchors {
240+ left: parent.left
241+ right: parent.right
242+ }
243+
244+ wrapMode: Text.Wrap
245+ horizontalAlignment: Text.AlignHCenter
246+ text: webview ? i18n.tr("The rendering process has been closed for this tab.") : ""
247+ }
248+
249+ Label {
250+ anchors {
251+ left: parent.left
252+ right: parent.right
253+ }
254+
255+ wrapMode: Text.Wrap
256+ horizontalAlignment: Text.AlignHCenter
257+ font.weight: Font.Light
258+ text: {
259+ if (!webview) {
260+ return ""
261+ } else if (webview.webProcessStatus == Oxide.WebView.WebProcessCrashed) {
262+ // TRANSLATORS: %1 is the URL of the page that crashed the renderer process
263+ return i18n.tr("Something went wrong while displaying %1.").arg(webview.url)
264+ } else if (webview.webProcessStatus == Oxide.WebView.WebProcessKilled) {
265+ return i18n.tr("The system is low on memory and can't display this webpage. Try closing unneeded tabs and reloading.")
266+ } else {
267+ return ""
268+ }
269+ }
270+ }
271+
272+ Row {
273+ anchors.horizontalCenter: parent.horizontalCenter
274+ spacing: units.gu(2)
275+
276+ Button {
277+ objectName: "closeTabButton"
278+ text: i18n.tr("Close tab")
279+ onClicked: closeTabRequested()
280+ }
281+
282+ Button {
283+ objectName: "reloadButton"
284+ text: i18n.tr("Reload")
285+ color: UbuntuColors.green
286+ onClicked: webview.reload()
287+ }
288+ }
289+ }
290+}
291
292=== added file 'src/app/webbrowser/assets/tab-error@27.png'
293Binary files src/app/webbrowser/assets/tab-error@27.png 1970-01-01 00:00:00 +0000 and src/app/webbrowser/assets/tab-error@27.png 2015-09-02 16:48:16 +0000 differ
294=== modified file 'src/app/webbrowser/tabs-model.cpp'
295--- src/app/webbrowser/tabs-model.cpp 2015-06-22 11:46:54 +0000
296+++ src/app/webbrowser/tabs-model.cpp 2015-09-02 16:48:16 +0000
297@@ -154,10 +154,11 @@
298 if (!checkValidTabIndex(index)) {
299 m_currentIndex = m_tabs.count() - 1;
300 Q_EMIT currentIndexChanged();
301- Q_EMIT currentTabChanged();
302- } else {
303- Q_EMIT currentTabChanged();
304 }
305+ Q_EMIT currentTabChanged();
306+ } else if (m_currentIndex > index) {
307+ m_currentIndex -= 1;
308+ Q_EMIT currentIndexChanged();
309 }
310 return tab;
311 }
312
313=== modified file 'tests/autopilot/webbrowser_app/emulators/browser.py'
314--- tests/autopilot/webbrowser_app/emulators/browser.py 2015-08-25 13:56:58 +0000
315+++ tests/autopilot/webbrowser_app/emulators/browser.py 2015-09-02 16:48:16 +0000
316@@ -109,6 +109,9 @@
317 def get_error_sheet(self):
318 return self.select_single("ErrorSheet")
319
320+ def get_sad_tab(self):
321+ return self.wait_select_single(SadTab)
322+
323 def get_suggestions(self):
324 return self.select_single(Suggestions)
325
326@@ -314,6 +317,19 @@
327 key=lambda item: item.globalRect.y)
328
329
330+class SadTab(uitk.UbuntuUIToolkitCustomProxyObjectBase):
331+
332+ @autopilot.logging.log_action(logger.info)
333+ def click_close_tab_button(self):
334+ button = self.select_single("Button", objectName="closeTabButton")
335+ self.pointing_device.click_object(button)
336+
337+ @autopilot.logging.log_action(logger.info)
338+ def click_reload_button(self):
339+ button = self.select_single("Button", objectName="reloadButton")
340+ self.pointing_device.click_object(button)
341+
342+
343 class GeolocationPermissionRequest(uitk.UbuntuUIToolkitCustomProxyObjectBase):
344
345 def get_deny_button(self):
346
347=== modified file 'tests/autopilot/webbrowser_app/tests/__init__.py'
348--- tests/autopilot/webbrowser_app/tests/__init__.py 2015-08-12 12:31:56 +0000
349+++ tests/autopilot/webbrowser_app/tests/__init__.py 2015-09-02 16:48:16 +0000
350@@ -18,11 +18,13 @@
351
352 import os
353 import shutil
354+import signal
355 import tempfile
356 import time
357 import urllib.request
358
359 import fixtures
360+import psutil
361 from testtools.matchers import Equals, NotEquals
362
363 from autopilot.matchers import Eventually
364@@ -213,6 +215,15 @@
365 ping = urllib.request.urlopen(url)
366 self.assertThat(ping.read(), Equals(b"pong"))
367
368+ def kill_web_processes(self, signal=signal.SIGKILL):
369+ children = psutil.Process(self.app.pid).children(True)
370+ for child in children:
371+ if child.name() == 'oxide-renderer':
372+ for arg in child.cmdline():
373+ if '--type=renderer' in arg:
374+ os.kill(child.pid, signal)
375+ break
376+
377
378 class StartOpenRemotePageTestCaseBase(BrowserTestCaseBase):
379
380
381=== added file 'tests/autopilot/webbrowser_app/tests/test_sad_tab.py'
382--- tests/autopilot/webbrowser_app/tests/test_sad_tab.py 1970-01-01 00:00:00 +0000
383+++ tests/autopilot/webbrowser_app/tests/test_sad_tab.py 2015-09-02 16:48:16 +0000
384@@ -0,0 +1,74 @@
385+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
386+#
387+# Copyright 2015 Canonical
388+#
389+# This program is free software: you can redistribute it and/or modify it
390+# under the terms of the GNU General Public License version 3, as published
391+# by the Free Software Foundation.
392+#
393+# This program is distributed in the hope that it will be useful,
394+# but WITHOUT ANY WARRANTY; without even the implied warranty of
395+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
396+# GNU General Public License for more details.
397+#
398+# You should have received a copy of the GNU General Public License
399+# along with this program. If not, see <http://www.gnu.org/licenses/>.
400+
401+import signal
402+import time
403+
404+from autopilot.platform import model
405+
406+from webbrowser_app.tests import StartOpenRemotePageTestCaseBase
407+
408+
409+class TestSadTab(StartOpenRemotePageTestCaseBase):
410+
411+ def _kill_web_process(self):
412+ self.kill_web_processes()
413+ # The first time the web process is killed, the browser attempts to
414+ # reload the page gracefully (after a short delay), hoping the process
415+ # won’t be killed again.
416+ time.sleep(1)
417+ self.main_window.wait_until_page_loaded(self.url)
418+
419+ self.kill_web_processes()
420+ # The second time around, the browser displays a sad tab.
421+ return self.main_window.get_sad_tab()
422+
423+ def test_reload_web_process_killed(self):
424+ sad_tab = self._kill_web_process()
425+ sad_tab.click_reload_button()
426+ sad_tab.wait_until_destroyed()
427+ self.assert_home_page_eventually_loaded()
428+
429+ def test_close_tab_web_process_killed(self):
430+ sad_tab = self._kill_web_process()
431+ sad_tab.click_close_tab_button()
432+ if model() == 'Desktop':
433+ # On desktop, closing the last open tab exits the application
434+ self.app.process.wait()
435+ return
436+ sad_tab.wait_until_destroyed()
437+ self.main_window.get_new_tab_view()
438+
439+ def _crash_web_process(self):
440+ self.kill_web_processes(signal.SIGSEGV)
441+ # A crash of the web process displays the sad tab right away
442+ return self.main_window.get_sad_tab()
443+
444+ def test_reload_web_process_crashed(self):
445+ sad_tab = self._crash_web_process()
446+ sad_tab.click_reload_button()
447+ sad_tab.wait_until_destroyed()
448+ self.assert_home_page_eventually_loaded()
449+
450+ def test_close_tab_web_process_crashed(self):
451+ sad_tab = self._crash_web_process()
452+ sad_tab.click_close_tab_button()
453+ if model() == 'Desktop':
454+ # On desktop, closing the last open tab exits the application
455+ self.app.process.wait()
456+ return
457+ sad_tab.wait_until_destroyed()
458+ self.main_window.get_new_tab_view()
459
460=== added file 'tests/unittests/qml/tst_WebProcessMonitor.qml'
461--- tests/unittests/qml/tst_WebProcessMonitor.qml 1970-01-01 00:00:00 +0000
462+++ tests/unittests/qml/tst_WebProcessMonitor.qml 2015-09-02 16:48:16 +0000
463@@ -0,0 +1,108 @@
464+/*
465+ * Copyright 2015 Canonical Ltd.
466+ *
467+ * This file is part of webbrowser-app.
468+ *
469+ * webbrowser-app is free software; you can redistribute it and/or modify
470+ * it under the terms of the GNU General Public License as published by
471+ * the Free Software Foundation; version 3.
472+ *
473+ * webbrowser-app is distributed in the hope that it will be useful,
474+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
475+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
476+ * GNU General Public License for more details.
477+ *
478+ * You should have received a copy of the GNU General Public License
479+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
480+ */
481+
482+import QtQuick 2.4
483+import QtTest 1.0
484+import com.canonical.Oxide 1.8 as Oxide
485+import "../../../src/app"
486+
487+WebProcessMonitor {
488+ id: monitor
489+
490+ Item {
491+ id: webviewMock
492+
493+ property int webProcessStatus
494+
495+ property int reloadCalled
496+ function reload() {
497+ webProcessStatus = Oxide.WebView.WebProcessRunning
498+ reloadCalled++
499+ }
500+ }
501+
502+ TestCase {
503+ name: "WebProcessMonitor"
504+
505+ function init() {
506+ webviewMock.webProcessStatus = Oxide.WebView.WebProcessRunning
507+ webviewMock.reloadCalled = 0
508+ }
509+
510+ function test_no_webview() {
511+ monitor.webview = null
512+ compare(monitor.killedRetries, 0)
513+ verify(!monitor.killed)
514+ verify(!monitor.crashed)
515+ }
516+
517+ function test_killed() {
518+ monitor.webview = webviewMock
519+ compare(monitor.killedRetries, 0)
520+
521+ webviewMock.webProcessStatus = Oxide.WebView.WebProcessKilled
522+ verify(monitor.killed)
523+ verify(!monitor.crashed)
524+ tryCompare(monitor, "killedRetries", 1)
525+ tryCompare(webviewMock, "reloadCalled", 1)
526+ verify(!monitor.killed)
527+ verify(!monitor.crashed)
528+ compare(monitor.killedRetries, 1)
529+
530+ webviewMock.webProcessStatus = Oxide.WebView.WebProcessKilled
531+ verify(monitor.killed)
532+ verify(!monitor.crashed)
533+ compare(monitor.killedRetries, 1)
534+ compare(webviewMock.reloadCalled, 1)
535+ }
536+
537+ function test_crashed() {
538+ monitor.webview = webviewMock
539+ compare(monitor.killedRetries, 0)
540+
541+ webviewMock.webProcessStatus = Oxide.WebView.WebProcessCrashed
542+ verify(!monitor.killed)
543+ verify(monitor.crashed)
544+ compare(monitor.killedRetries, 0)
545+ compare(webviewMock.reloadCalled, 0)
546+
547+ webviewMock.webProcessStatus = Oxide.WebView.WebProcessRunning
548+ verify(!monitor.killed)
549+ verify(!monitor.crashed)
550+ compare(monitor.killedRetries, 0)
551+ compare(webviewMock.reloadCalled, 0)
552+ }
553+
554+ function test_change_webview() {
555+ monitor.webview = webviewMock
556+ compare(monitor.killedRetries, 0)
557+ verify(!monitor.killed)
558+ verify(!monitor.crashed)
559+
560+ webviewMock.webProcessStatus = Oxide.WebView.WebProcessKilled
561+ verify(monitor.killed)
562+ verify(!monitor.crashed)
563+ tryCompare(monitor, "killedRetries", 1)
564+
565+ monitor.webview = null
566+ compare(monitor.killedRetries, 0)
567+ verify(!monitor.killed)
568+ verify(!monitor.crashed)
569+ }
570+ }
571+}
572
573=== modified file 'tests/unittests/tabs-model/tst_TabsModelTests.cpp'
574--- tests/unittests/tabs-model/tst_TabsModelTests.cpp 2015-08-10 15:22:00 +0000
575+++ tests/unittests/tabs-model/tst_TabsModelTests.cpp 2015-09-02 16:48:16 +0000
576@@ -246,39 +246,62 @@
577
578 void shouldUpdateCurrentTabWhenRemoving()
579 {
580- QSignalSpy spy(model, SIGNAL(currentTabChanged()));
581+ QSignalSpy tabSpy(model, SIGNAL(currentTabChanged()));
582+ QSignalSpy indexSpy(model, SIGNAL(currentIndexChanged()));
583
584 // Adding a tab to an empty model should update the current tab.
585 // Removing the last tab from the model should update it too.
586 model->add(createTab());
587+ tabSpy.clear();
588+ indexSpy.clear();
589 delete model->remove(0);
590- QCOMPARE(spy.count(), 2);
591+ QCOMPARE(tabSpy.count(), 1);
592+ QCOMPARE(indexSpy.count(), 1);
593
594- // When removing a tab after the current one,
595- // the current tab shouldn’t change.
596+ // When removing a tab after the current one, neither the
597+ // current tab nor the current index should change.
598 QQuickItem* tab1 = createTab();
599 model->add(tab1);
600 model->add(createTab());
601- spy.clear();
602+ tabSpy.clear();
603+ indexSpy.clear();
604 delete model->remove(1);
605 QCOMPARE(model->currentTab(), tab1);
606- QVERIFY(spy.isEmpty());
607+ QVERIFY(tabSpy.isEmpty());
608+ QVERIFY(indexSpy.isEmpty());
609
610- // When removing the current tab, if there is a tab after it,
611- // it becomes the current one.
612+ // When removing the current tab, if there is a tab after it, it
613+ // becomes the current one, and thus the current index doesn’t change.
614 QQuickItem* tab2 = createTab();
615 model->add(tab2);
616- spy.clear();
617+ tabSpy.clear();
618+ indexSpy.clear();
619 delete model->remove(0);
620- QCOMPARE(spy.count(), 1);
621+ QCOMPARE(tabSpy.count(), 1);
622+ QVERIFY(indexSpy.isEmpty());
623 QCOMPARE(model->currentTab(), tab2);
624
625+ // When removing a tab before the current one, the current
626+ // tab doesn’t change but the current index is updated.
627+ QQuickItem* tab3 = createTab();
628+ model->add(tab3);
629+ model->setCurrentIndex(1);
630+ tabSpy.clear();
631+ indexSpy.clear();
632+ delete model->remove(0);
633+ QVERIFY(tabSpy.isEmpty());
634+ QCOMPARE(indexSpy.count(), 1);
635+ QCOMPARE(model->currentIndex(), 0);
636+
637 // When removing the current tab, if it was the last one, the
638 // current tab should be reset to 0.
639- spy.clear();
640+ tabSpy.clear();
641+ indexSpy.clear();
642 delete model->remove(0);
643- QCOMPARE(spy.count(), 1);
644+ QCOMPARE(tabSpy.count(), 1);
645+ QCOMPARE(indexSpy.count(), 1);
646 QCOMPARE(model->currentTab(), (QObject*) nullptr);
647+ QCOMPARE(model->currentIndex(), -1);
648 }
649
650 void shouldReturnData()

Subscribers

People subscribed via source and target branches

to status/vote changes: