Merge lp:~gerboland/qtmir/dont-delete-qml-cache-on-good-stop into lp:qtmir

Proposed by Gerry Boland
Status: Merged
Approved by: Nick Dedekind
Approved revision: no longer in the source branch.
Merged at revision: 383
Proposed branch: lp:~gerboland/qtmir/dont-delete-qml-cache-on-good-stop
Merge into: lp:qtmir
Diff against target: 325 lines (+202/-6)
4 files modified
src/modules/Unity/Application/application.cpp (+64/-6)
src/modules/Unity/Application/application.h (+3/-0)
src/modules/Unity/Application/application_manager.cpp (+1/-0)
tests/modules/ApplicationManager/application_manager_test.cpp (+134/-0)
To merge this branch: bzr merge lp:~gerboland/qtmir/dont-delete-qml-cache-on-good-stop
Reviewer Review Type Date Requested Status
Michał Sawicz Approve
Nick Dedekind (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+272761@code.launchpad.net

Commit message

Add "Closing" state to Application, use it to distinguish user-induced close from app-induced close. Don't clear QML cache if user-induced.

Much code taken from a partially-related branch by Nick Dedekind:
https://code.launchpad.net/~nick-dedekind/qtmir/polite-close/+merge/262188

Note there is 1 disabled test, which needs additional work to fix.

Description of the change

 * Are there any related MPs required for this MP to build/function as expected? Please list.
N
 * Did you perform an exploratory manual test run of your code change and any related functionality?
Y
 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

To post a comment you must log in.
Revision history for this message
Gerry Boland (gerboland) wrote :

If app crashes, the QML cache may not be cleared. This requires additional work, which I'd prefer to consider in a later MR.

Revision history for this message
Gerry Boland (gerboland) wrote :

To test, get a shell on the phone and watch the contents of the ~/.cache/QML/Apps/ directory. Each app will get a directory with its appId name, which contains its cache. Deleting the cache means deleteing this directory.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

"virtual memory exhausted: Cannot allocate memory" - trying CI again

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

small comment.

review: Needs Fixing
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

small comment.

Revision history for this message
Nick Dedekind (nick-dedekind) :
review: Needs Fixing
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

LGTM

review: Approve
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Yes.

 * Did CI run pass? If not, please explain why.
Yes

review: Approve
Revision history for this message
Michał Sawicz (saviq) wrote :

Approving the uncommit, sorry for the confusion.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/modules/Unity/Application/application.cpp'
--- src/modules/Unity/Application/application.cpp 2015-08-20 16:04:25 +0000
+++ src/modules/Unity/Application/application.cpp 2015-10-01 08:04:31 +0000
@@ -73,10 +73,27 @@
73 qCDebug(QTMIR_APPLICATIONS) << "Application::~Application";73 qCDebug(QTMIR_APPLICATIONS) << "Application::~Application";
7474
75 // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup75 // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup
76 if (m_processState == Application::ProcessUnknown76 if (m_processState == Application::ProcessUnknown) {
77 || state() == Application::Starting77 wipeQMLCache();
78 || state() == Application::Running) {78 }
79 wipeQMLCache();79
80 switch (m_state) {
81 case InternalState::Starting:
82 case InternalState::Running:
83 case InternalState::RunningInBackground:
84 case InternalState::SuspendingWaitSession:
85 case InternalState::SuspendingWaitProcess:
86 wipeQMLCache();
87 break;
88 case InternalState::Closing:
89 case InternalState::Suspended:
90 case InternalState::StoppedResumable:
91 break;
92 case InternalState::Stopped:
93 if (m_processState == Application::ProcessFailed) { // process crashed
94 wipeQMLCache();
95 }
96 break;
80 }97 }
8198
82 if (m_session) {99 if (m_session) {
@@ -203,6 +220,8 @@
203 return "SuspendingWaitProcess";220 return "SuspendingWaitProcess";
204 case InternalState::Suspended:221 case InternalState::Suspended:
205 return "Suspended";222 return "Suspended";
223 case InternalState::Closing:
224 return "Closing";
206 case InternalState::StoppedResumable:225 case InternalState::StoppedResumable:
207 return "StoppedResumable";226 return "StoppedResumable";
208 case InternalState::Stopped:227 case InternalState::Stopped:
@@ -266,6 +285,7 @@
266 case InternalState::RunningInBackground:285 case InternalState::RunningInBackground:
267 case InternalState::SuspendingWaitSession:286 case InternalState::SuspendingWaitSession:
268 case InternalState::SuspendingWaitProcess:287 case InternalState::SuspendingWaitProcess:
288 case InternalState::Closing:
269 return Running;289 return Running;
270 case InternalState::Suspended:290 case InternalState::Suspended:
271 return Suspended;291 return Suspended;
@@ -321,6 +341,8 @@
321 case InternalState::SuspendingWaitProcess:341 case InternalState::SuspendingWaitProcess:
322 // should leave the app alone until it reaches Suspended state342 // should leave the app alone until it reaches Suspended state
323 break;343 break;
344 case InternalState::Closing:
345 break;
324 case InternalState::StoppedResumable:346 case InternalState::StoppedResumable:
325 respawn();347 respawn();
326 break;348 break;
@@ -350,6 +372,9 @@
350 case InternalState::Suspended:372 case InternalState::Suspended:
351 // it's already going where we it's wanted373 // it's already going where we it's wanted
352 break;374 break;
375 case InternalState::Closing:
376 // don't suspend while it is closing
377 break;
353 case InternalState::StoppedResumable:378 case InternalState::StoppedResumable:
354 case InternalState::Stopped:379 case InternalState::Stopped:
355 // the app doesn't have a process in the first place, so there's nothing to suspend380 // the app doesn't have a process in the first place, so there's nothing to suspend
@@ -382,6 +407,32 @@
382 m_pid = pid;407 m_pid = pid;
383}408}
384409
410void Application::close()
411{
412 qCDebug(QTMIR_APPLICATIONS) << "Application::close - appId=" << appId();
413
414 switch (m_state) {
415 case InternalState::Starting:
416 case InternalState::Running:
417 setInternalState(InternalState::Closing);
418 break;
419 case InternalState::RunningInBackground:
420 case InternalState::SuspendingWaitSession:
421 case InternalState::SuspendingWaitProcess:
422 case InternalState::Suspended:
423 setRequestedState(RequestedRunning);
424 setInternalState(InternalState::Closing);
425 break;
426 case InternalState::Closing:
427 // already on the way
428 break;
429 case InternalState::StoppedResumable:
430 case InternalState::Stopped:
431 // too late
432 break;
433 }
434}
435
385void Application::setArguments(const QStringList arguments)436void Application::setArguments(const QStringList arguments)
386{437{
387 m_arguments = arguments;438 m_arguments = arguments;
@@ -411,6 +462,7 @@
411 case InternalState::Starting:462 case InternalState::Starting:
412 case InternalState::Running:463 case InternalState::Running:
413 case InternalState::RunningInBackground:464 case InternalState::RunningInBackground:
465 case InternalState::Closing:
414 m_session->resume();466 m_session->resume();
415 break;467 break;
416 case InternalState::SuspendingWaitSession:468 case InternalState::SuspendingWaitSession:
@@ -474,6 +526,9 @@
474 case InternalState::Suspended:526 case InternalState::Suspended:
475 releaseWakelock();527 releaseWakelock();
476 break;528 break;
529 case InternalState::Closing:
530 acquireWakelock();
531 break;
477 case InternalState::StoppedResumable:532 case InternalState::StoppedResumable:
478 releaseWakelock();533 releaseWakelock();
479 break;534 break;
@@ -546,7 +601,8 @@
546 if (m_state == InternalState::Starting) {601 if (m_state == InternalState::Starting) {
547 // that was way too soon. let it go away602 // that was way too soon. let it go away
548 setInternalState(InternalState::Stopped);603 setInternalState(InternalState::Stopped);
549 } else if (m_state == InternalState::StoppedResumable) {604 } else if (m_state == InternalState::StoppedResumable ||
605 m_state == InternalState::Closing) {
550 // The application stopped nicely, likely closed itself. Thus not meant to be resumed later.606 // The application stopped nicely, likely closed itself. Thus not meant to be resumed later.
551 setInternalState(InternalState::Stopped);607 setInternalState(InternalState::Stopped);
552 } else {608 } else {
@@ -657,7 +713,8 @@
657 case Session::Stopped:713 case Session::Stopped:
658 if (!canBeResumed()714 if (!canBeResumed()
659 || m_state == InternalState::Starting715 || m_state == InternalState::Starting
660 || m_state == InternalState::Running) {716 || m_state == InternalState::Running
717 || m_state == InternalState::Closing) {
661 /* 1. application is not managed by upstart718 /* 1. application is not managed by upstart
662 * 2. application is managed by upstart, but has stopped before it managed719 * 2. application is managed by upstart, but has stopped before it managed
663 * to create a surface, we can assume it crashed on startup, and thus720 * to create a surface, we can assume it crashed on startup, and thus
@@ -665,6 +722,7 @@
665 * 3. application is managed by upstart and is in foreground (i.e. has722 * 3. application is managed by upstart and is in foreground (i.e. has
666 * Running state), if Mir reports the application disconnects, it723 * Running state), if Mir reports the application disconnects, it
667 * either crashed or stopped itself.724 * either crashed or stopped itself.
725 * 4. We're expecting the application to stop after a close request
668 */726 */
669 setInternalState(InternalState::Stopped);727 setInternalState(InternalState::Stopped);
670 } else {728 } else {
671729
=== modified file 'src/modules/Unity/Application/application.h'
--- src/modules/Unity/Application/application.h 2015-08-20 16:10:50 +0000
+++ src/modules/Unity/Application/application.h 2015-10-01 08:04:31 +0000
@@ -72,6 +72,7 @@
72 SuspendingWaitSession,72 SuspendingWaitSession,
73 SuspendingWaitProcess,73 SuspendingWaitProcess,
74 Suspended,74 Suspended,
75 Closing, // The user has requested the app be closed
75 StoppedResumable, // The process stopped but we want to keep the Application object around76 StoppedResumable, // The process stopped but we want to keep the Application object around
76 // so it can be respawned as if it never stopped running in the first place.77 // so it can be respawned as if it never stopped running in the first place.
77 Stopped // It closed itself, crashed or it stopped and we can't respawn it78 Stopped // It closed itself, crashed or it stopped and we can't respawn it
@@ -125,6 +126,8 @@
125126
126 pid_t pid() const;127 pid_t pid() const;
127128
129 void close();
130
128 // for tests131 // for tests
129 InternalState internalState() const { return m_state; }132 InternalState internalState() const { return m_state; }
130133
131134
=== modified file 'src/modules/Unity/Application/application_manager.cpp'
--- src/modules/Unity/Application/application_manager.cpp 2015-08-21 00:00:16 +0000
+++ src/modules/Unity/Application/application_manager.cpp 2015-10-01 08:04:31 +0000
@@ -438,6 +438,7 @@
438 return false;438 return false;
439 }439 }
440440
441 application->close();
441 remove(application);442 remove(application);
442443
443 bool result = m_taskController->stop(application->longAppId());444 bool result = m_taskController->stop(application->longAppId());
444445
=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
--- tests/modules/ApplicationManager/application_manager_test.cpp 2015-08-31 09:51:28 +0000
+++ tests/modules/ApplicationManager/application_manager_test.cpp 2015-10-01 08:04:31 +0000
@@ -1853,3 +1853,137 @@
1853 ASSERT_EQ(Application::InternalState::RunningInBackground, application->internalState());1853 ASSERT_EQ(Application::InternalState::RunningInBackground, application->internalState());
1854 EXPECT_EQ(Application::Running, application->state());1854 EXPECT_EQ(Application::Running, application->state());
1855}1855}
1856
1857/*
1858 * Test that when user stops an application, application does not delete QML cache
1859 */
1860TEST_F(ApplicationManagerTests,QMLcacheRetainedOnAppStop)
1861{
1862 using namespace ::testing;
1863 const QString appId("testAppId1234");
1864 quint64 procId = 5551;
1865
1866 // Set up Mocks & signal watcher
1867 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1868 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1869 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1870
1871 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1872
1873 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1874 .Times(1)
1875 .WillOnce(Return(true));
1876
1877 applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1878 applicationManager.onProcessStarting(appId);
1879 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1880 bool authed = true;
1881 applicationManager.authorizeSession(procId, authed);
1882 onSessionStarting(session);
1883
1884 // Create fake QML cache for this app
1885 QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/") + appId);
1886 QDir dir(path);
1887 dir.mkpath(path);
1888
1889 // Stop app
1890 applicationManager.stopApplication(appId);
1891
1892 EXPECT_EQ(0, applicationManager.count());
1893 EXPECT_TRUE(dir.exists());
1894}
1895
1896/*
1897 * Test that if running application stops unexpectedly, AppMan deletes the QML cache
1898 */
1899TEST_F(ApplicationManagerTests,DISABLED_QMLcacheDeletedOnAppCrash)
1900{
1901 using namespace ::testing;
1902 const QString appId("testAppId12345");
1903 quint64 procId = 5551;
1904
1905 // Set up Mocks & signal watcher
1906 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1907 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1908 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1909
1910 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1911
1912 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1913 .Times(1)
1914 .WillOnce(Return(true));
1915
1916 Application *the_app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1917 applicationManager.onProcessStarting(appId);
1918 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1919 bool authed = true;
1920 applicationManager.authorizeSession(procId, authed);
1921 onSessionStarting(session);
1922
1923 // Have app in fully Running state
1924 FakeMirSurface *aSurface = new FakeMirSurface;
1925 onSessionCreatedSurface(session.get(), aSurface);
1926 aSurface->drawFirstFrame();
1927 ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
1928
1929 // Create fake QML cache for this app
1930 QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/") + appId);
1931 QDir dir(path);
1932 dir.mkpath(path);
1933
1934 // Report app crash
1935 onSessionStopping(session);
1936 // Upstart notifies of **crashing** app
1937 applicationManager.onProcessFailed(appId, true);
1938 applicationManager.onProcessStopped(appId);
1939
1940 EXPECT_EQ(0, applicationManager.count());
1941 EXPECT_FALSE(dir.exists());
1942}
1943
1944/*
1945 * Test that if running application stops itself cleanly, AppMan retains the QML cache
1946 */
1947TEST_F(ApplicationManagerTests,QMLcacheRetainedOnAppShutdown)
1948{
1949 using namespace ::testing;
1950 const QString appId("testAppId123456");
1951 quint64 procId = 5551;
1952
1953 // Set up Mocks & signal watcher
1954 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1955 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1956 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1957
1958 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1959
1960 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1961 .Times(1)
1962 .WillOnce(Return(true));
1963
1964 Application *the_app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1965 applicationManager.onProcessStarting(appId);
1966 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1967 bool authed = true;
1968 applicationManager.authorizeSession(procId, authed);
1969 onSessionStarting(session);
1970
1971 // Have app in fully Running state
1972 FakeMirSurface *aSurface = new FakeMirSurface;
1973 onSessionCreatedSurface(session.get(), aSurface);
1974 aSurface->drawFirstFrame();
1975 ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
1976
1977 // Create fake QML cache for this app
1978 QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/") + appId);
1979 QDir dir(path);
1980 dir.mkpath(path);
1981
1982 // Report app stop
1983 onSessionStopping(session);
1984 // Upstart notifies of stopping app
1985 applicationManager.onProcessStopped(appId);
1986
1987 EXPECT_EQ(0, applicationManager.count());
1988 EXPECT_TRUE(dir.exists());
1989}

Subscribers

People subscribed via source and target branches