Merge lp:~thomas-voss/unity-mir/refactor-oom-score-adj-to-rely-on-process-cpp into lp:unity-mir
- refactor-oom-score-adj-to-rely-on-process-cpp
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Albert Astals Cid |
Approved revision: | 169 |
Merged at revision: | 174 |
Proposed branch: | lp:~thomas-voss/unity-mir/refactor-oom-score-adj-to-rely-on-process-cpp |
Merge into: | lp:unity-mir |
Prerequisite: | lp:~thomas-voss/unity-mir/switch-to-cmake-take-2 |
Diff against target: |
2438 lines (+1548/-443) 25 files modified
CMakeLists.txt (+2/-2) debian/control (+2/-0) src/modules/Unity/Application/CMakeLists.txt (+10/-0) src/modules/Unity/Application/application.cpp (+11/-12) src/modules/Unity/Application/application.h (+7/-5) src/modules/Unity/Application/application_manager.cpp (+53/-23) src/modules/Unity/Application/application_manager.h (+9/-4) src/modules/Unity/Application/applicationcontroller.h (+60/-0) src/modules/Unity/Application/desktopfilereader.cpp (+18/-0) src/modules/Unity/Application/desktopfilereader.h (+30/-13) src/modules/Unity/Application/processcontroller.cpp (+128/-0) src/modules/Unity/Application/processcontroller.h (+56/-0) src/modules/Unity/Application/taskcontroller.cpp (+98/-351) src/modules/Unity/Application/taskcontroller.h (+17/-11) src/modules/Unity/Application/upstart/applicationcontroller.cpp (+134/-0) src/modules/Unity/Application/upstart/applicationcontroller.h (+43/-0) tests/CMakeLists.txt (+70/-0) tests/application_manager_test.cpp (+151/-0) tests/auto/modules/Unity/Application/CMakeLists.txt (+20/-21) tests/auto/modules/Unity/Application/main.cpp (+8/-1) tests/mock_application_controller.h (+106/-0) tests/mock_desktop_file_reader.h (+165/-0) tests/mock_oom_controller.h (+33/-0) tests/mock_process_controller.h (+45/-0) tests/taskcontroller_test.cpp (+272/-0) |
To merge this branch: | bzr merge lp:~thomas-voss/unity-mir/refactor-oom-score-adj-to-rely-on-process-cpp |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Albert Astals Cid (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Gerry Boland (community) | Approve | ||
Ubuntu Unity PS integration team | Pending | ||
Michał Sawicz | Pending | ||
Review via email: mp+201145@code.launchpad.net |
This proposal supersedes a proposal from 2013-11-12.
Commit message
Refactor Oom(Score)Adj to rely on process-cpp helper library.
Description of the change
* Are there any related MPs required for this MP to build/function as expected? Please list.
No.
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes, I installed the resulting package and checked that I can still run multiple apps in parallel.
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Yes.
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:144
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:145
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:146
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:146
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal | # |
There's an indentation issue in debian/control (please use spaces).
Why do we fall back to the deprecated method? Are there any cases where we expect process cpp to raise that is an "expected failure"?
"extremal" is not a word?
Should we maybe store shellScore instead of reading it off of the shell process every time?
This would be a good time to auto-test this, would it not?
Thomas Voß (thomas-voss) wrote : Posted in a previous version of this proposal | # |
> There's an indentation issue in debian/control (please use spaces).
>
Fixed.
> Why do we fall back to the deprecated method? Are there any cases where we
> expect process cpp to raise that is an "expected failure"?
>
While the method is deprecated it is still present and common in the Android world.
Process-cpp just raises an exception if it is unable to adjust the score, i.e., unable to write to the respective file. I think the fallback renders the overall implementation more robust in real-world scenarios.
> "extremal" is not a word?
>
Fixed.
> Should we maybe store shellScore instead of reading it off of the shell
> process every time?
>
We need to read it everytime as the score might have been adjusted by the kernel in the meantime.
> This would be a good time to auto-test this, would it not?
True, will take care of that task.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:148
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:150
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal | # |
there's a src/modules/
Thomas Voß (thomas-voss) wrote : Posted in a previous version of this proposal | # |
> there's a src/modules/
> probably has to go away.
Good catch, fixed.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:152
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:153
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:154
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal | # |
Which this i am getting libunity-
I don't think this change is intentional, no?
Thomas Voß (thomas-voss) wrote : Posted in a previous version of this proposal | # |
> Which this i am getting libunity-
> get libunity-
>
> I don't think this change is intentional, no?
Nope, it is not. Also fixed in prerequisite branch.
Albert Astals Cid (aacid) wrote : | # |
Looks good to me, would prefer someone with more unity-mir knowledge to top-approve though :-)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:155
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:155
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:156
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:157
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:158
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : | # |
First off, thank you for the tests! Great to have them. Here are my comments:
For ApplicationCont
Your making separate TaskController and ApplicationCont
But I'd consider your using the process-cpp library an implementation specific detail also - other init sytems may provide their own APIs to configure things for an OOM killer. As a result, I think it makes more sense to have the TaskController interface and allow multiple possible implementations (so removing the ApplicationCont
It would also help remove this
+ the_application
+ QSharedPointer<
+ new TaskController(
+ nullptr,
+ QSharedPointer<
+ new upstart:
+ QSharedPointer<
+ new DesktopFileRead
which is ugly to my eyes.
I prefer appending "Interface" or "Base" to the names of interfaces classes too.
Gerry Boland (gerboland) wrote : | # |
More generally I find it strange that the Process C++ library isn't isolating unity-mir from the implementation details of OOM score setter. Yes I know there are 2 separate scoring systems, but is there not a way to calculate the respective score to suit both backends? The plpp::OomScoreAdj and plpp::OomAdj classes just open the /proc/$
I can't say I'm terribly fond of the API in process-cpp. I'm a fan of defined setters & getters, so I find
+ core::posix:
less readable than
+ int score = core::posix:
Using >> and << operators means it's not obvious at all what's being read and written. Again, personal preference
Gerry Boland (gerboland) wrote : | # |
+ // static TaskController* singleton();
Junk
+ void onApplicationAb
Please use "const QString" here and in the other slots
+ connect(
+ SIGNAL(
+ this,
+ SLOT(onApplicat
If possible you can use the C++11 syntax for signal/slot connections which is checked at compile-time, unlike that syntax (checked run-time):
connect(
Gerry Boland (gerboland) wrote : | # |
+ auto pid = app_controller-
My opinion on "auto" is it should be used with care, as it reduces the clarity of code for the developer. auto should be used only when the actual type is obvious - in for loops or for iterator types of lists, it's handy. But here pid is a pid_t, not an int, which a developer might guess and be subtly wrong about.
+++ src/modules/
This is a very un-Qt piece of code, using some Qt methods. Making upstart:
+ auto thiz = static_
You're comprehensive :) I've not seen a use for userData yet however. Really needed? Here the auto is obvious though so I'm fine with it.
Gerry Boland (gerboland) wrote : | # |
+# Build with system gmock and embedded gtest
+set (GMOCK_INCLUDE_DIR "/usr/include/
+set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
+set (GTEST_INCLUDE_DIR "${GMOCK_
Can't cmake read this from the pkgconfig file?
+TEST(TaskContr
+ const QString appId{"
+ taskController.
Why not just use appId on the latter line? Is case for all tests in tests/taskcontr
Gerry Boland (gerboland) wrote : | # |
Some notes:
I'd also prefer if you maintained the coding style of the project, please keep the opening brace on the same line as the "try" and "catch(...)" commands, and the other places.
+ * Copyright (C) 2013 Canonical, Ltd.
2014
+ * Authored by: Thomas Voß <email address hidden>
some of that was my code you moved around, can you either remove this or add my name too please in the relevant places? I prefer not having it, as it introduces politics over code ownership, who did what, etc. Again, opinion.
Thomas Voß (thomas-voss) wrote : | # |
> + auto pid = app_controller-
> My opinion on "auto" is it should be used with care, as it reduces the clarity
> of code for the developer. auto should be used only when the actual type is
> obvious - in for loops or for iterator types of lists, it's handy. But here
> pid is a pid_t, not an int, which a developer might guess and be subtly wrong
> about.
>
>
Ack, will fix it.
>
> +++ src/modules/
> This is a very un-Qt piece of code, using some Qt methods. Making
> upstart:
> you loose Q_EMIT and have to go down the QMetaObject:
> drops compile-time checking, and deciding if signal can be direct or queued.
> You're also loosing readability, so where's the real gain?
>
I guess I don't understand the difference between Q_EMIT and QMetaObject::invoke then. How can a macro do compile-time checking, specifically in the Qt signal/slot system? I get your point about ::connect(...) with C++11 syntax but not about the emit case. For the direct vs. queued question: ::connect() can only determine the correct setup if the thread affinity of both objects is known at connect time iiuc. That's not necessarily the case here as far as I can see.
> + auto thiz = static_
> You're comprehensive :) I've not seen a use for userData yet however. Really
> needed? Here the auto is obvious though so I'm fine with it.
It's required to rely on the userData when removing the singleton-based approaches that make testing quite difficult.
Thomas Voß (thomas-voss) wrote : | # |
> First off, thank you for the tests! Great to have them. Here are my comments:
>
>
Yup, happy to help.
> For ApplicationCont
> between "ApplicationMan
> me.
>
> Your making separate TaskController and ApplicationCont
> yes I see that ApplicationCont
> have a systemd implementation later), and TaskController is implementation
> agnostic, that's clear.
>
> But I'd consider your using the process-cpp library an implementation specific
> detail also - other init sytems may provide their own APIs to configure things
> for an OOM killer. As a result, I think it makes more sense to have the
> TaskController interface and allow multiple possible implementations (so
> removing the ApplicationCont
>
Okay, I can see that we should fold the ApplicationCont
> It would also help remove this
>
> + the_application
> + QSharedPointer<
> + new TaskController(
> + nullptr,
> + QSharedPointer<
> + new upstart:
> + QSharedPointer<
> + new DesktopFileRead
> which is ugly to my eyes.
>
I can see what I can do.
> I prefer appending "Interface" or "Base" to the names of interfaces classes
> too.
Dito
Thomas Voß (thomas-voss) wrote : | # |
> More generally I find it strange that the Process C++ library isn't isolating
> unity-mir from the implementation details of OOM score setter. Yes I know
> there are 2 separate scoring systems, but is there not a way to calculate the
> respective score to suit both backends? The plpp::OomScoreAdj and plpp::OomAdj
> classes just open the /proc/$
> so it's a pretty thin layer, and I could do the equivalent in a few lines of
> code in Qt.
>
I'm not questioning that. The real problem is testing, though. If you burry that inside the implementation, how do you test for correct behavior. Pulling out the behavior and putting it in its component, testing it and making it available to other parties, too, is the motivation for process-cpp.
>
> I can't say I'm terribly fond of the API in process-cpp. I'm a fan of defined
> setters & getters, so I find
> + core::posix:
> shellScore.value;
> less readable than
> + int score = core::posix:
> Using >> and << operators means it's not obvious at all what's being read and
> written. Again, personal preference
We could certainly apply this inside unity-mir where the scope of operation is limited to oom scores. However, in the bigger picture of features in /proc/pid, placing the actual logic in an external entity and separating concern between the process (providing the pid) and explicit objects for features in /proc/pid helps in keeping the size of the interfaces manageable. I would also argue that a getter should be free of side-effects, but that's me being pedantic.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:159
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:161
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:162
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:163
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:165
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : | # |
> > For ApplicationCont
> > between "ApplicationMan
> to
> > me.
> >
> > Your making separate TaskController and ApplicationCont
> -
> > yes I see that ApplicationCont
> could
> > have a systemd implementation later), and TaskController is implementation
> > agnostic, that's clear.
> >
> > But I'd consider your using the process-cpp library an implementation
> specific
> > detail also - other init sytems may provide their own APIs to configure
> things
> > for an OOM killer. As a result, I think it makes more sense to have the
> > TaskController interface and allow multiple possible implementations (so
> > removing the ApplicationCont
> >
>
> Okay, I can see that we should fold the ApplicationCont
> TaskController interface, but the Oom API I'm modelling here is independent of
> the init system and only leverages kernel facilities. With that, and your
> proposal to have an upstart:
> have an interface ProcessController that includes state control as well as oom
> adjustments. TaskController would then depend on an implementation of
> ProcessController. Makes sense?
While I am averse to adding yet another class, yes that works for me. It does introduce further class naming confusion as how can one easily guess the difference between {Process,
> I can see what I can do.
>
> > I prefer appending "Interface" or "Base" to the names of interfaces classes
> > too.
>
> Dito
Dito meaning you'll do it? Or you object?
Gerry Boland (gerboland) wrote : | # |
> > +++ src/modules/
> > This is a very un-Qt piece of code, using some Qt methods. Making
> > upstart:
> > you loose Q_EMIT and have to go down the QMetaObject:
> which
> > drops compile-time checking, and deciding if signal can be direct or queued.
> > You're also loosing readability, so where's the real gain?
> >
>
> I guess I don't understand the difference between Q_EMIT and
> QMetaObject::invoke then. How can a macro do compile-time checking,
> specifically in the Qt signal/slot system? I get your point about
> ::connect(...) with C++11 syntax but not about the emit case. For the direct
> vs. queued question: ::connect() can only determine the correct setup if the
> thread affinity of both objects is known at connect time iiuc. That's not
> necessarily the case here as far as I can see.
The MOC compiler helps out. For any Q_SIGNAL you declare in the header file, it generates an extra method in that class for it, for Q_SIGNAL void valueChanged(int) something like:
void Class::
{
void *_a[] = { 0, const_cast<
QMetaObject
}
Then in the .cpp file, calling "Q_EMIT valueChanged(5)" reduces to "valueChanged(5)" which is a valid method since it was added by MOC (Q_EMIT is an empty macro, is more a hint for developer).
Using QMetaObject:
QMetaObject:
Gerry Boland (gerboland) wrote : | # |
Functionally everything works ok. Please address my couple of comments above and then this will be good to go!
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:166
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Thomas Voß (thomas-voss) wrote : | # |
> > > +++ src/modules/
> > > This is a very un-Qt piece of code, using some Qt methods. Making
> > > upstart:
> means
> > > you loose Q_EMIT and have to go down the QMetaObject:
> > which
> > > drops compile-time checking, and deciding if signal can be direct or
> queued.
> > > You're also loosing readability, so where's the real gain?
> > >
> >
> > I guess I don't understand the difference between Q_EMIT and
> > QMetaObject::invoke then. How can a macro do compile-time checking,
> > specifically in the Qt signal/slot system? I get your point about
> > ::connect(...) with C++11 syntax but not about the emit case. For the direct
> > vs. queued question: ::connect() can only determine the correct setup if the
> > thread affinity of both objects is known at connect time iiuc. That's not
> > necessarily the case here as far as I can see.
>
> The MOC compiler helps out. For any Q_SIGNAL you declare in the header file,
> it generates an extra method in that class for it, for Q_SIGNAL void
> valueChanged(int) something like:
>
> void Class::
> {
> void *_a[] = { 0, const_cast<
> };
> QMetaObject:
> }
>
> Then in the .cpp file, calling "Q_EMIT valueChanged(5)" reduces to
> "valueChanged(5)" which is a valid method since it was added by MOC (Q_EMIT is
> an empty macro, is more a hint for developer).
>
> Using QMetaObject:
> time check. If the signal you're invoking is removed, the code will still
> compile, but you'll get run-time errors only.
>
> QMetaObject:
> - it works with signal indexes (generated by MOC), not their string names, so
> is slightly more efficient. And it looks after the thread affinity for you.
I switched to Q_EMIT. For thread affinity: Sure, that's certainly true but thread-affinity requires an explicit moveToThread iiuc, and we do not do that, yet. With that, it defaults to Qt::AutoConnection which resolves to Qt::DirectConne
Thomas Voß (thomas-voss) wrote : | # |
> > > For ApplicationCont
> > > between "ApplicationMan
> obvious
> > to
> > > me.
> > >
> > > Your making separate TaskController and ApplicationCont
> me
> > -
> > > yes I see that ApplicationCont
> > could
> > > have a systemd implementation later), and TaskController is implementation
> > > agnostic, that's clear.
> > >
> > > But I'd consider your using the process-cpp library an implementation
> > specific
> > > detail also - other init sytems may provide their own APIs to configure
> > things
> > > for an OOM killer. As a result, I think it makes more sense to have the
> > > TaskController interface and allow multiple possible implementations (so
> > > removing the ApplicationCont
> > >
> >
> > Okay, I can see that we should fold the ApplicationCont
> > TaskController interface, but the Oom API I'm modelling here is independent
> of
> > the init system and only leverages kernel facilities. With that, and your
> > proposal to have an upstart:
> to
> > have an interface ProcessController that includes state control as well as
> oom
> > adjustments. TaskController would then depend on an implementation of
> > ProcessController. Makes sense?
>
> While I am averse to adding yet another class, yes that works for me. It does
> introduce further class naming confusion as how can one easily guess the
> difference between {Process,
> little refactoring to clarify.
>
>
> > I can see what I can do.
> >
> > > I prefer appending "Interface" or "Base" to the names of interfaces
> classes
> > > too.
> >
> > Dito
>
> Dito meaning you'll do it? Or you object?
I'm not a fan of Base or Interface to be honest. And given your proposal to refactor the class structure a little I'm wondering if we should minimize changes to this MP now and postpone any further adjustments to the class structure. What do you think? I'm happy to fuse both classes but I have a feeling that we need some more refactorings anyway.
Gerry Boland (gerboland) wrote : | # |
> I'm not a fan of Base or Interface to be honest. And given your proposal to
> refactor the class structure a little I'm wondering if we should minimize
> changes to this MP now and postpone any further adjustments to the class
> structure. What do you think? I'm happy to fuse both classes but I have a
> feeling that we need some more refactorings anyway.
Yeah ok
Gerry Boland (gerboland) wrote : | # |
Approved! Thanks
Thomas Voß (thomas-voss) wrote : | # |
* Are there any related MPs required for this MP to build/function as expected? Please list.
No.
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes, I installed the resulting package and checked that I can still run multiple apps in parallel.
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Yes.
Gerry Boland (gerboland) 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
Albert Astals Cid (aacid) wrote : | # |
m_focusedApplic
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:168
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 169. By Thomas Voß
-
Initialize ApplicationMana
ger::m_ focusedApplicat ion to nullptr in ctor.
Albert Astals Cid (aacid) wrote : | # |
Looks good :-)
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2014-01-15 14:59:00 +0000 |
3 | +++ CMakeLists.txt 2014-02-11 09:48:32 +0000 |
4 | @@ -2,6 +2,8 @@ |
5 | |
6 | project(Unity-Mir) |
7 | |
8 | +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) |
9 | + |
10 | # Find includes in corresponding build directories |
11 | set(CMAKE_INCLUDE_CURRENT_DIR ON) |
12 | # Instruct CMake to run moc automatically when needed. |
13 | @@ -65,8 +67,6 @@ |
14 | add_subdirectory(src) |
15 | add_subdirectory(data) |
16 | |
17 | -# TODO(tvoss, pitti): With the build system switched to cmake, we can now |
18 | -# start adding unit- and integration tests for unity-mir. |
19 | add_subdirectory(tests) |
20 | |
21 | # TODO(tvoss): Enable coverage reporting once we have tests in place. |
22 | |
23 | === modified file 'debian/control' |
24 | --- debian/control 2014-02-04 16:47:30 +0000 |
25 | +++ debian/control 2014-02-11 09:48:32 +0000 |
26 | @@ -4,10 +4,12 @@ |
27 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
28 | Build-Depends: debhelper (>= 9), |
29 | cmake, |
30 | + google-mock (>= 1.6.0+svn437), |
31 | pkg-config, |
32 | libplatform-api1-dev, |
33 | libmirserver-dev (>= 0.1.4), |
34 | libmirclient-dev (>= 0.1.4), |
35 | + libprocess-cpp-dev, |
36 | libunity-api-dev, |
37 | libupstart-app-launch2-dev, |
38 | qt5-default, |
39 | |
40 | === modified file 'src/modules/Unity/Application/CMakeLists.txt' |
41 | --- src/modules/Unity/Application/CMakeLists.txt 2014-01-09 15:49:18 +0000 |
42 | +++ src/modules/Unity/Application/CMakeLists.txt 2014-02-11 09:48:32 +0000 |
43 | @@ -1,4 +1,5 @@ |
44 | pkg_check_modules(GLIB glib-2.0 REQUIRED) |
45 | +pkg_check_modules(PROCESS_CPP process-cpp REQUIRED) |
46 | pkg_check_modules(UPSTART_APP_LAUNCH upstart-app-launch-2 REQUIRED) |
47 | |
48 | add_definitions(-DQT_PLUGIN) |
49 | @@ -7,6 +8,7 @@ |
50 | ${GLIB_INCLUDE_DIRS} |
51 | ${MIRCOMMON_INCLUDE_DIRS} |
52 | ${MIRSERVER_INCLUDE_DIRS} |
53 | + ${PROCESS_CPP_INCLUDE_DIRS} |
54 | ${UBUNTU_PLATFORM_API_INCLUDE_DIRS} |
55 | ${UPSTART_APP_LAUNCH_INCLUDE_DIRS} |
56 | |
57 | @@ -27,9 +29,15 @@ |
58 | mirsurfacemanager.cpp |
59 | inputarea.cpp |
60 | inputfilterarea.cpp |
61 | + processcontroller.h |
62 | + processcontroller.cpp |
63 | shellinputarea.cpp |
64 | ubuntukeyboardinfo.cpp |
65 | |
66 | + upstart/applicationcontroller.h |
67 | + upstart/applicationcontroller.cpp |
68 | + |
69 | + applicationcontroller.h |
70 | application_manager.h |
71 | application.h |
72 | desktopfilereader.h |
73 | @@ -69,6 +77,8 @@ |
74 | ${UBUNTU_PLATFORM_API_LIBRARIES} |
75 | ${MIRCOMMON_LIBRARIES} |
76 | ${MIRSERVER_LIBRARIES} |
77 | + ${PROCESS_CPP_LIBRARIES} |
78 | + ${UBUNTU_PLATFORM_API_LIBRARIES} |
79 | ${UPSTART_APP_LAUNCH_LIBRARIES} |
80 | |
81 | ubuntu_application_api_mirserver |
82 | |
83 | === modified file 'src/modules/Unity/Application/application.cpp' |
84 | --- src/modules/Unity/Application/application.cpp 2014-01-02 14:54:13 +0000 |
85 | +++ src/modules/Unity/Application/application.cpp 2014-02-11 09:48:32 +0000 |
86 | @@ -25,16 +25,15 @@ |
87 | |
88 | // mir |
89 | #include <mir/shell/session.h> |
90 | - |
91 | -Application::Application(const QString &appId, Application::State state, |
92 | - const QStringList &arguments, QObject *parent) |
93 | - : Application(new DesktopFileReader(appId), state, arguments, parent) |
94 | -{ |
95 | -} |
96 | - |
97 | -Application::Application(DesktopFileReader *desktopFileReader, State state, |
98 | - const QStringList &arguments, QObject *parent) |
99 | +#include <mir/shell/snapshot.h> |
100 | + |
101 | +Application::Application(const QSharedPointer<TaskController>& taskController, |
102 | + DesktopFileReader *desktopFileReader, |
103 | + State state, |
104 | + const QStringList &arguments, |
105 | + QObject *parent) |
106 | : ApplicationInfoInterface(desktopFileReader->appId(), parent) |
107 | + , m_taskController(taskController) |
108 | , m_desktopData(desktopFileReader) |
109 | , m_pid(0) |
110 | , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage) |
111 | @@ -220,17 +219,17 @@ |
112 | void Application::suspend() |
113 | { |
114 | DLOG("Application::suspend (this=%p)", this); |
115 | - TaskController::singleton()->suspend(appId()); |
116 | + m_taskController->suspend(appId()); |
117 | } |
118 | |
119 | void Application::resume() |
120 | { |
121 | DLOG("Application::resume (this=%p)", this); |
122 | - TaskController::singleton()->resume(appId()); |
123 | + m_taskController->resume(appId()); |
124 | } |
125 | |
126 | void Application::respawn() |
127 | { |
128 | DLOG("Application::respawn (this=%p)", this); |
129 | - TaskController::singleton()->start(appId(), m_arguments); |
130 | + m_taskController->start(appId(), m_arguments); |
131 | } |
132 | |
133 | === modified file 'src/modules/Unity/Application/application.h' |
134 | --- src/modules/Unity/Application/application.h 2014-01-02 16:04:19 +0000 |
135 | +++ src/modules/Unity/Application/application.h 2014-02-11 09:48:32 +0000 |
136 | @@ -20,12 +20,10 @@ |
137 | // std |
138 | #include <memory> |
139 | |
140 | -// Mir |
141 | -#include <mir/shell/snapshot.h> |
142 | - |
143 | //Qt |
144 | #include <QtCore/QtCore> |
145 | #include <QImage> |
146 | +#include <QSharedPointer> |
147 | |
148 | // Unity API |
149 | #include <unity/shell/application/ApplicationInfoInterface.h> |
150 | @@ -43,8 +41,11 @@ |
151 | Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged) |
152 | |
153 | public: |
154 | - Application(const QString &appId, State state, const QStringList &arguments, QObject *parent = 0); |
155 | - Application(DesktopFileReader *desktopFileReader, State state, const QStringList &arguments, QObject *parent = 0); |
156 | + Application(const QSharedPointer<TaskController>& taskController, |
157 | + DesktopFileReader *desktopFileReader, |
158 | + State state, |
159 | + const QStringList &arguments, |
160 | + QObject *parent = 0); |
161 | virtual ~Application(); |
162 | |
163 | // ApplicationInfoInterface |
164 | @@ -82,6 +83,7 @@ |
165 | void setSession(const std::shared_ptr<mir::shell::Session>& session); |
166 | void setSessionName(const QString& name); |
167 | |
168 | + QSharedPointer<TaskController> m_taskController; |
169 | DesktopFileReader* m_desktopData; |
170 | qint64 m_pid; |
171 | Stage m_stage; |
172 | |
173 | === modified file 'src/modules/Unity/Application/application_manager.cpp' |
174 | --- src/modules/Unity/Application/application_manager.cpp 2014-01-16 11:57:16 +0000 |
175 | +++ src/modules/Unity/Application/application_manager.cpp 2014-02-11 09:48:32 +0000 |
176 | @@ -19,6 +19,8 @@ |
177 | #include "application.h" |
178 | #include "desktopfilereader.h" |
179 | #include "dbuswindowstack.h" |
180 | +#include "taskcontroller.h" |
181 | +#include "upstart/applicationcontroller.h" |
182 | |
183 | // unity-mir |
184 | #include "qmirserverapplication.h" |
185 | @@ -31,6 +33,7 @@ |
186 | #include "logging.h" |
187 | |
188 | // mir |
189 | +#include <mir/scene/depth_id.h> |
190 | #include <mir/shell/session.h> |
191 | #include <mir/shell/focus_controller.h> |
192 | #include <mir/shell/surface.h> |
193 | @@ -53,23 +56,34 @@ |
194 | ApplicationManager* ApplicationManager::singleton() |
195 | { |
196 | if (!the_application_manager) { |
197 | - the_application_manager = new ApplicationManager(); |
198 | + the_application_manager = new ApplicationManager( |
199 | + QSharedPointer<TaskController>( |
200 | + new TaskController( |
201 | + nullptr, |
202 | + QSharedPointer<ApplicationController>( |
203 | + new upstart::ApplicationController()))), |
204 | + QSharedPointer<DesktopFileReader::Factory>( |
205 | + new DesktopFileReader::Factory())); |
206 | } |
207 | return the_application_manager; |
208 | } |
209 | |
210 | -ApplicationManager::ApplicationManager(QObject *parent) |
211 | -: ApplicationManagerInterface(parent) |
212 | -, m_focusedApplication(nullptr) |
213 | -, m_mainStageApplication(nullptr) |
214 | -, m_sideStageApplication(nullptr) |
215 | -, m_msApplicationToBeFocused(nullptr) |
216 | -, m_ssApplicationToBeFocused(nullptr) |
217 | -, m_lifecycleExceptions(QStringList() << "com.ubuntu.music") |
218 | -, m_taskController(TaskController::singleton()) |
219 | -, m_gridUnitPx(8) |
220 | -, m_fenceNext(false) |
221 | -, m_panelHeight(54) |
222 | +ApplicationManager::ApplicationManager( |
223 | + const QSharedPointer<TaskController>& taskController, |
224 | + const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory, |
225 | + QObject *parent) |
226 | + : ApplicationManagerInterface(parent) |
227 | + , m_focusedApplication(nullptr) |
228 | + , m_mainStageApplication(nullptr) |
229 | + , m_sideStageApplication(nullptr) |
230 | + , m_msApplicationToBeFocused(nullptr) |
231 | + , m_ssApplicationToBeFocused(nullptr) |
232 | + , m_lifecycleExceptions(QStringList() << "com.ubuntu.music") |
233 | + , m_taskController(taskController) |
234 | + , m_desktopFileReaderFactory(desktopFileReaderFactory) |
235 | + , m_gridUnitPx(8) |
236 | + , m_fenceNext(false) |
237 | + , m_panelHeight(54) |
238 | { |
239 | DLOG("ApplicationManager::ApplicationManager (this=%p)", this); |
240 | |
241 | @@ -280,7 +294,12 @@ |
242 | return nullptr; |
243 | } |
244 | |
245 | - Application* application = new Application(appId, Application::Starting, arguments, this); |
246 | + Application* application = new Application( |
247 | + m_taskController, |
248 | + m_desktopFileReaderFactory->createInstanceForAppId(appId), |
249 | + Application::Starting, |
250 | + arguments, |
251 | + this); |
252 | if (!application->isValid()) { |
253 | DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId)); |
254 | return nullptr; |
255 | @@ -307,7 +326,11 @@ |
256 | Application *application = findApplication(appId); |
257 | |
258 | if (!application) { // if shell did not start this application, but upstart did |
259 | - application = new Application(appId, Application::Starting, QStringList(), this); |
260 | + application = new Application( |
261 | + m_taskController, |
262 | + m_desktopFileReaderFactory->createInstanceForAppId(appId), |
263 | + Application::Starting, |
264 | + QStringList(), this); |
265 | if (!application->isValid()) { |
266 | DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId)); |
267 | return; |
268 | @@ -370,7 +393,7 @@ |
269 | removeApplication = true; |
270 | } |
271 | |
272 | - if (application->state() == Application::Running) { |
273 | + if (application->state() == Application::Running || application->state() == Application::Starting) { |
274 | // Application probably crashed, else OOM killer struck. Either way state wasn't saved |
275 | // so just remove application |
276 | removeApplication = true; |
277 | @@ -472,9 +495,9 @@ |
278 | // case we didn't get an existing .desktop file path |
279 | DesktopFileReader* desktopData; |
280 | if (QFileInfo(desktopFileName).exists()) { |
281 | - desktopData = new DesktopFileReader(QFileInfo(desktopFileName)); |
282 | + desktopData = m_desktopFileReaderFactory->createInstanceForDesktopFile(QFileInfo(desktopFileName)); |
283 | } else { |
284 | - desktopData = new DesktopFileReader(desktopFileName); |
285 | + desktopData = m_desktopFileReaderFactory->createInstanceForAppId(desktopFileName); |
286 | } |
287 | |
288 | if (!desktopData->loaded()) { |
289 | @@ -510,7 +533,7 @@ |
290 | |
291 | QString argStr(command.data()); |
292 | QStringList arguments(argStr.split(' ')); |
293 | - application = new Application(desktopData, Application::Starting, arguments, this); |
294 | + application = new Application(m_taskController, desktopData, Application::Starting, arguments, this); |
295 | application->setPid(pid); |
296 | application->setStage(stage); |
297 | add(application); |
298 | @@ -574,9 +597,16 @@ |
299 | // in case application closed not by hand of shell, check again here: |
300 | Application* application = findApplicationWithSession(session); |
301 | if (application) { |
302 | - application->setState(Application::Stopped); |
303 | - application->setSession(nullptr); |
304 | - m_dbusWindowStack->WindowDestroyed(0, application->appId()); |
305 | + bool removeApplication = true; |
306 | + |
307 | + if (application->state() != Application::Starting) { |
308 | + application->setState(Application::Stopped); |
309 | + application->setSession(nullptr); |
310 | + m_dbusWindowStack->WindowDestroyed(0, application->appId()); |
311 | + if (application != m_focusedApplication) { |
312 | + removeApplication = false; |
313 | + } |
314 | + } |
315 | |
316 | if (m_mainStageApplication == application) |
317 | m_mainStageApplication = nullptr; |
318 | @@ -584,7 +614,7 @@ |
319 | if (m_sideStageApplication == application) |
320 | m_sideStageApplication = nullptr; |
321 | |
322 | - if (application == m_focusedApplication) { |
323 | + if (removeApplication) { |
324 | // TODO(greyback) What to do?? Focus next app, or unfocus everything?? |
325 | m_focusedApplication = NULL; |
326 | remove(application); |
327 | |
328 | === modified file 'src/modules/Unity/Application/application_manager.h' |
329 | --- src/modules/Unity/Application/application_manager.h 2014-01-16 11:57:16 +0000 |
330 | +++ src/modules/Unity/Application/application_manager.h 2014-02-11 09:48:32 +0000 |
331 | @@ -17,11 +17,15 @@ |
332 | #ifndef APPLICATIONMANAGER_H |
333 | #define APPLICATIONMANAGER_H |
334 | |
335 | +// local |
336 | +#include "desktopfilereader.h" |
337 | + |
338 | // std |
339 | #include <memory> |
340 | |
341 | // Qt |
342 | #include <QObject> |
343 | +#include <QSharedPointer> |
344 | #include <QStringList> |
345 | |
346 | // Unity API |
347 | @@ -30,8 +34,6 @@ |
348 | // local |
349 | #include "application.h" |
350 | |
351 | -#include <mir/scene/depth_id.h> |
352 | - |
353 | class ShellServerConfiguration; |
354 | class DBusWindowStack; |
355 | class MirSurfaceManager; |
356 | @@ -62,7 +64,9 @@ |
357 | |
358 | static ApplicationManager* singleton(); |
359 | |
360 | - explicit ApplicationManager(QObject *parent = 0); |
361 | + explicit ApplicationManager(const QSharedPointer<TaskController>& taskController, |
362 | + const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory, |
363 | + QObject *parent = 0); |
364 | virtual ~ApplicationManager(); |
365 | |
366 | // ApplicationManagerInterface |
367 | @@ -128,7 +132,8 @@ |
368 | QStringList m_lifecycleExceptions; |
369 | ShellServerConfiguration* m_mirServer; |
370 | DBusWindowStack* m_dbusWindowStack; |
371 | - QScopedPointer<TaskController> m_taskController; |
372 | + QSharedPointer<TaskController> m_taskController; |
373 | + QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory; |
374 | static ApplicationManager* the_application_manager; |
375 | int m_gridUnitPx; |
376 | bool m_fenceNext; |
377 | |
378 | === added file 'src/modules/Unity/Application/applicationcontroller.h' |
379 | --- src/modules/Unity/Application/applicationcontroller.h 1970-01-01 00:00:00 +0000 |
380 | +++ src/modules/Unity/Application/applicationcontroller.h 2014-02-11 09:48:32 +0000 |
381 | @@ -0,0 +1,60 @@ |
382 | +/* |
383 | + * Copyright (C) 2013 Canonical, Ltd. |
384 | + * |
385 | + * This program is free software: you can redistribute it and/or modify it under |
386 | + * the terms of the GNU Lesser General Public License version 3, as published by |
387 | + * the Free Software Foundation. |
388 | + * |
389 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
390 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
391 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
392 | + * Lesser General Public License for more details. |
393 | + * |
394 | + * You should have received a copy of the GNU Lesser General Public License |
395 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
396 | + * |
397 | + */ |
398 | + |
399 | +#ifndef APPLICATION_CONTROLLER_H |
400 | +#define APPLICATION_CONTROLLER_H |
401 | + |
402 | +#include <QObject> |
403 | +#include <QString> |
404 | +#include <QStringList> |
405 | + |
406 | +class ApplicationController : public QObject |
407 | +{ |
408 | + Q_OBJECT |
409 | + |
410 | +public: |
411 | + enum class Error |
412 | + { |
413 | + APPLICATION_CRASHED, |
414 | + APPLICATION_FAILED_TO_START |
415 | + }; |
416 | + |
417 | + ApplicationController(const ApplicationController&) = delete; |
418 | + virtual ~ApplicationController() = default; |
419 | + |
420 | + ApplicationController& operator=(const ApplicationController&) = delete; |
421 | + |
422 | + virtual pid_t primaryPidForAppId(const QString& appId) = 0; |
423 | + virtual bool appIdHasProcessId(pid_t pid, const QString& appId) = 0; |
424 | + |
425 | + virtual bool stopApplicationWithAppId(const QString& appId) = 0; |
426 | + virtual bool startApplicationWithAppIdAndArgs(const QString& appId, const QStringList& arguments) = 0; |
427 | + |
428 | +Q_SIGNALS: |
429 | + void applicationAboutToBeStarted(QString id); |
430 | + void applicationStarted(QString id); |
431 | + void applicationStopped(QString id); |
432 | + void applicationFocusRequest(QString id); |
433 | + void applicationResumeRequest(QString id); |
434 | + |
435 | + void applicationError(QString id, ApplicationController::Error error); |
436 | + |
437 | +protected: |
438 | + ApplicationController() = default; |
439 | +}; |
440 | + |
441 | +#endif // APPLICATION_CONTROLLER_H |
442 | |
443 | === modified file 'src/modules/Unity/Application/desktopfilereader.cpp' |
444 | --- src/modules/Unity/Application/desktopfilereader.cpp 2013-10-14 09:21:43 +0000 |
445 | +++ src/modules/Unity/Application/desktopfilereader.cpp 2014-02-11 09:48:32 +0000 |
446 | @@ -25,6 +25,24 @@ |
447 | #include <QDir> |
448 | #include <QStandardPaths> |
449 | |
450 | +DesktopFileReader::Factory::Factory() |
451 | +{ |
452 | +} |
453 | + |
454 | +DesktopFileReader::Factory::~Factory() |
455 | +{ |
456 | +} |
457 | + |
458 | +DesktopFileReader* DesktopFileReader::Factory::createInstanceForAppId(const QString& appId) |
459 | +{ |
460 | + return new DesktopFileReader(appId); |
461 | +} |
462 | + |
463 | +DesktopFileReader* DesktopFileReader::Factory::createInstanceForDesktopFile(const QFileInfo& fi) |
464 | +{ |
465 | + return new DesktopFileReader(fi); |
466 | +} |
467 | + |
468 | // Retrieves the size of an array at compile time. |
469 | #define ARRAY_SIZE(a) \ |
470 | ((sizeof(a) / sizeof(*(a))) / static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) |
471 | |
472 | === modified file 'src/modules/Unity/Application/desktopfilereader.h' |
473 | --- src/modules/Unity/Application/desktopfilereader.h 2013-10-10 16:26:03 +0000 |
474 | +++ src/modules/Unity/Application/desktopfilereader.h 2014-02-11 09:48:32 +0000 |
475 | @@ -23,20 +23,37 @@ |
476 | |
477 | class DesktopFileReader { |
478 | public: |
479 | + class Factory |
480 | + { |
481 | + public: |
482 | + Factory(); |
483 | + Factory(const Factory&) = delete; |
484 | + virtual ~Factory(); |
485 | + |
486 | + Factory& operator=(const Factory&) = delete; |
487 | + |
488 | + virtual DesktopFileReader* createInstanceForAppId(const QString& appId); |
489 | + virtual DesktopFileReader* createInstanceForDesktopFile(const QFileInfo& fi); |
490 | + }; |
491 | + |
492 | + virtual ~DesktopFileReader(); |
493 | + |
494 | + virtual QString file() const { return file_; } |
495 | + virtual QString appId() const { return appId_; } |
496 | + virtual QString name() const { return entries_[kNameIndex]; } |
497 | + virtual QString comment() const { return entries_[kCommentIndex]; } |
498 | + virtual QString icon() const { return entries_[kIconIndex]; } |
499 | + virtual QString exec() const { return entries_[kExecIndex]; } |
500 | + virtual QString path() const { return entries_[kPathIndex]; } |
501 | + virtual QString stageHint() const { return entries_[kStageHintIndex]; } |
502 | + virtual bool loaded() const { return loaded_; } |
503 | + virtual QString findDesktopFile(const QString &appId) const; |
504 | + |
505 | +protected: |
506 | + friend class DesktopFileReaderFactory; |
507 | + |
508 | DesktopFileReader(const QString &appId); |
509 | DesktopFileReader(const QFileInfo &desktopFile); |
510 | - ~DesktopFileReader(); |
511 | - |
512 | - QString file() const { return file_; } |
513 | - QString appId() const { return appId_; } |
514 | - QString name() const { return entries_[kNameIndex]; } |
515 | - QString comment() const { return entries_[kCommentIndex]; } |
516 | - QString icon() const { return entries_[kIconIndex]; } |
517 | - QString exec() const { return entries_[kExecIndex]; } |
518 | - QString path() const { return entries_[kPathIndex]; } |
519 | - QString stageHint() const { return entries_[kStageHintIndex]; } |
520 | - bool loaded() const { return loaded_; } |
521 | - QString findDesktopFile(const QString &appId) const; |
522 | |
523 | private: |
524 | static const int kNameIndex = 0, |
525 | @@ -47,7 +64,7 @@ |
526 | kStageHintIndex = 5, |
527 | kNumberOfEntries = 6; |
528 | |
529 | - bool loadDesktopFile(QString desktopFile); |
530 | + virtual bool loadDesktopFile(QString desktopFile); |
531 | |
532 | QString appId_; |
533 | QString file_; |
534 | |
535 | === added file 'src/modules/Unity/Application/processcontroller.cpp' |
536 | --- src/modules/Unity/Application/processcontroller.cpp 1970-01-01 00:00:00 +0000 |
537 | +++ src/modules/Unity/Application/processcontroller.cpp 2014-02-11 09:48:32 +0000 |
538 | @@ -0,0 +1,128 @@ |
539 | +/* |
540 | + * Copyright (C) 2014 Canonical, Ltd. |
541 | + * |
542 | + * This program is free software: you can redistribute it and/or modify it under |
543 | + * the terms of the GNU Lesser General Public License version 3, as published by |
544 | + * the Free Software Foundation. |
545 | + * |
546 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
547 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
548 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
549 | + * Lesser General Public License for more details. |
550 | + * |
551 | + * You should have received a copy of the GNU Lesser General Public License |
552 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
553 | + * |
554 | + */ |
555 | + |
556 | +#include "processcontroller.h" |
557 | + |
558 | +#include "logging.h" |
559 | + |
560 | +// Process C++ |
561 | +#include <core/posix/process.h> |
562 | +#include <core/posix/this_process.h> |
563 | +#include <core/posix/linux/proc/process/oom_adj.h> |
564 | +#include <core/posix/linux/proc/process/oom_score_adj.h> |
565 | + |
566 | +// std |
567 | +#include <signal.h> |
568 | +#include <unistd.h> |
569 | + |
570 | +namespace plpp = core::posix::linux::proc::process; |
571 | + |
572 | +ProcessController::OomController::OomController() |
573 | +{ |
574 | +} |
575 | + |
576 | +void ProcessController::OomController::ensureProcessLikelyToBeKilled(pid_t pid) |
577 | +{ |
578 | + // We avoid boundary values for oom_score_adj. For that, we |
579 | + // set it to 80% of the total available range. |
580 | + static const float defaultPercentage = 0.8; |
581 | + |
582 | + core::posix::Process process(pid); |
583 | + |
584 | + try { |
585 | + plpp::OomScoreAdj shellScore; |
586 | + core::posix::this_process::instance() >> shellScore; |
587 | + |
588 | + plpp::OomScoreAdj processScore |
589 | + { |
590 | + static_cast<int>((plpp::OomScoreAdj::max_value() - shellScore.value) * defaultPercentage) + shellScore.value |
591 | + }; |
592 | + |
593 | + process << processScore; |
594 | + } catch(...) { |
595 | + // Accessing OomScoreAdj resulted in an exception being thrown. |
596 | + // Trying with the deprecated OomAdj now as a last resort. |
597 | + try |
598 | + { |
599 | + process << plpp::OomAdj{plpp::OomAdj::max_value()}; |
600 | + } catch(...) |
601 | + { |
602 | + LOG("ensureProcessIsLikelyToBeKilled failed"); |
603 | + } |
604 | + } |
605 | +} |
606 | + |
607 | +void ProcessController::OomController::ensureProcessUnlikelyToBeKilled(pid_t pid) |
608 | +{ |
609 | + // By system default, we set the oom_score_adj of Unity8 to -10 (via lightdm). |
610 | + // As we want to avoid that any app's oom_score_adj is <= Unity8's oom_score_adj, |
611 | + // we choose a default increase of +1. |
612 | + static const int default_increase = 1; |
613 | + |
614 | + core::posix::Process process(pid); |
615 | + |
616 | + try { |
617 | + plpp::OomScoreAdj shellScore; |
618 | + core::posix::this_process::instance() >> shellScore; |
619 | + |
620 | + plpp::OomScoreAdj processScore |
621 | + { |
622 | + shellScore.value + default_increase |
623 | + }; |
624 | + |
625 | + process << processScore; |
626 | + } catch(...) { |
627 | + // Accessing OomScoreAdj resulted in an exception being thrown. |
628 | + // Trying with the deprecated OomAdj now as a last resort. |
629 | + // By system default, we set the oom_score_adj of Unity8 to -10 (via lightdm). |
630 | + // As we want to avoid that any app's oom_score_adj or oom_adj is <= Unity8's oom_score_adj, |
631 | + // we choose a default value of -9 for oom_score_adj and 0 for oom_adj. |
632 | + static const int defaultValue = 0; |
633 | + |
634 | + try |
635 | + { |
636 | + process << plpp::OomAdj{defaultValue}; |
637 | + } catch(...) |
638 | + { |
639 | + LOG("ensureProcessIsUnlikelyToBeKilled failed"); |
640 | + } |
641 | + } |
642 | +} |
643 | + |
644 | +ProcessController::ProcessController() |
645 | + : m_oomController(new ProcessController::OomController()) |
646 | +{ |
647 | +} |
648 | + |
649 | +ProcessController::~ProcessController() |
650 | +{ |
651 | +} |
652 | + |
653 | +const QSharedPointer<ProcessController::OomController>& ProcessController::oomController() const |
654 | +{ |
655 | + return m_oomController; |
656 | +} |
657 | + |
658 | +bool ProcessController::sigStopProcessGroupForPid(pid_t pid) const |
659 | +{ |
660 | + return -1 != kill(-pid, SIGSTOP); |
661 | +} |
662 | + |
663 | +bool ProcessController::sigContinueProcessGroupForPid(pid_t pid) const |
664 | +{ |
665 | + return -1 != kill(-pid, SIGCONT); |
666 | +} |
667 | |
668 | === added file 'src/modules/Unity/Application/processcontroller.h' |
669 | --- src/modules/Unity/Application/processcontroller.h 1970-01-01 00:00:00 +0000 |
670 | +++ src/modules/Unity/Application/processcontroller.h 2014-02-11 09:48:32 +0000 |
671 | @@ -0,0 +1,56 @@ |
672 | +/* |
673 | + * Copyright (C) 2014 Canonical, Ltd. |
674 | + * |
675 | + * This program is free software: you can redistribute it and/or modify it under |
676 | + * the terms of the GNU Lesser General Public License version 3, as published by |
677 | + * the Free Software Foundation. |
678 | + * |
679 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
680 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
681 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
682 | + * Lesser General Public License for more details. |
683 | + * |
684 | + * You should have received a copy of the GNU Lesser General Public License |
685 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
686 | + * |
687 | + */ |
688 | + |
689 | +#ifndef PROCESS_CONTROLLER_H |
690 | +#define PROCESS_CONTROLLER_H |
691 | + |
692 | +#include <QSharedPointer> |
693 | + |
694 | +#include <sys/types.h> |
695 | + |
696 | +class ProcessController |
697 | +{ |
698 | +public: |
699 | + class OomController |
700 | + { |
701 | + public: |
702 | + OomController(); |
703 | + OomController(const OomController&) = delete; |
704 | + virtual ~OomController() = default; |
705 | + |
706 | + OomController& operator=(const OomController&) = delete; |
707 | + |
708 | + virtual void ensureProcessLikelyToBeKilled(pid_t); |
709 | + virtual void ensureProcessUnlikelyToBeKilled(pid_t); |
710 | + }; |
711 | + |
712 | + ProcessController(); |
713 | + ProcessController(const ProcessController&) = delete; |
714 | + virtual ~ProcessController(); |
715 | + |
716 | + ProcessController& operator=(const ProcessController&) = delete; |
717 | + |
718 | + virtual const QSharedPointer<OomController>& oomController() const; |
719 | + |
720 | + virtual bool sigStopProcessGroupForPid(pid_t pid) const; |
721 | + virtual bool sigContinueProcessGroupForPid(pid_t pid) const; |
722 | + |
723 | +protected: |
724 | + QSharedPointer<OomController> m_oomController; |
725 | +}; |
726 | + |
727 | +#endif // PROCESS_CONTROLLER_H |
728 | |
729 | === modified file 'src/modules/Unity/Application/taskcontroller.cpp' |
730 | --- src/modules/Unity/Application/taskcontroller.cpp 2013-12-05 17:30:57 +0000 |
731 | +++ src/modules/Unity/Application/taskcontroller.cpp 2014-02-11 09:48:32 +0000 |
732 | @@ -25,6 +25,12 @@ |
733 | // Qt |
734 | #include <QStringList> |
735 | |
736 | +// Process C++ |
737 | +#include <core/posix/process.h> |
738 | +#include <core/posix/this_process.h> |
739 | +#include <core/posix/linux/proc/process/oom_adj.h> |
740 | +#include <core/posix/linux/proc/process/oom_score_adj.h> |
741 | + |
742 | // STL |
743 | #include <mutex> |
744 | |
745 | @@ -35,362 +41,61 @@ |
746 | #include <signal.h> |
747 | #include <unistd.h> |
748 | |
749 | -// linux specific |
750 | -#include <linux/oom.h> |
751 | - |
752 | -namespace |
753 | -{ |
754 | -/** |
755 | - * From man proc: |
756 | - * |
757 | - * This file can be used to adjust the score used to select which |
758 | - * process should be killed in an out-of-memory (OOM) situation. The |
759 | - * kernel uses this value for a bit-shift opera‐ tion of the process's |
760 | - * oom_score value: valid values are in the range -16 to +15, plus the |
761 | - * special value -17, which disables OOM-killing altogether for this |
762 | - * process. A posi‐ tive score increases the likelihood of this process |
763 | - * being killed by the OOM-killer; a negative score decreases the |
764 | - * likelihood. |
765 | - * |
766 | - * The default value for this file is 0; a new process inherits its |
767 | - * parent's oom_adj setting. A process must be privileged |
768 | - * (CAP_SYS_RESOURCE) to update this file. |
769 | - |
770 | - * Since Linux 2.6.36, use of this file is deprecated in favor of |
771 | - * /proc/[pid]/oom_score_adj. |
772 | - */ |
773 | -struct OomAdjuster |
774 | -{ |
775 | - static int disableOomKillerValue() |
776 | - { |
777 | - return OOM_DISABLE; |
778 | - } |
779 | - |
780 | - static int minValue() |
781 | - { |
782 | - return OOM_ADJUST_MIN; |
783 | - } |
784 | - |
785 | - static int maxValue() |
786 | - { |
787 | - return OOM_ADJUST_MAX; |
788 | - } |
789 | - |
790 | - static OomAdjuster leastLikelyToBeKilled() |
791 | - { |
792 | - // By system default, we set the oom_score_adj of Unity8 to -10 (via lightdm). |
793 | - // As we want to avoid that any app's oom_score_adj or oom_adj is <= Unity8's oom_score_adj, |
794 | - // we choose a default value of -9 for oom_score_adj and 0 for oom_adj. |
795 | - static const int default_value = 0; |
796 | - |
797 | - return OomAdjuster(default_value); |
798 | - } |
799 | - |
800 | - static OomAdjuster mostLikelyToBeKilled() |
801 | - { |
802 | - return OomAdjuster(maxValue()); |
803 | - } |
804 | - |
805 | - OomAdjuster(int value) : value(value) |
806 | - { |
807 | - } |
808 | - |
809 | - bool isValid() const |
810 | - { |
811 | - return !(value < disableOomKillerValue() || value > maxValue()); |
812 | - } |
813 | - |
814 | - bool applyForPid(pid_t pid) const |
815 | - { |
816 | - auto fn = QString("/proc/%1/oom_adj").arg(pid); |
817 | - QFile file(fn); |
818 | - |
819 | - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) |
820 | - return false; |
821 | - |
822 | - QTextStream out(&file); |
823 | - out << value; |
824 | - |
825 | - return true; |
826 | - } |
827 | - |
828 | - int value; |
829 | -}; |
830 | - |
831 | -/** |
832 | - * From man proc: |
833 | - * |
834 | - * This file can be used to adjust the badness heuristic used to |
835 | - * select which process gets killed in out-of-memory conditions. |
836 | - * |
837 | - * The badness heuristic assigns a value to each candidate |
838 | - * task ranging from 0 (never kill) to 1000 (always kill) |
839 | - * to determine which process is targeted. The units are |
840 | - * roughly a proportion along that range of allowed memory |
841 | - * the process may allocate from, based on an estimation of |
842 | - * its current memory and swap use. For example, if a task |
843 | - * is using all allowed memory, its badness score will be |
844 | - * 1000. If it is using half of its allowed memory, its |
845 | - * score will be 500. |
846 | - * |
847 | - * There is an additional factor included in the badness score: root |
848 | - * processes are given 3% extra memory over other tasks. |
849 | - * |
850 | - * The amount of "allowed" memory depends on the context in |
851 | - * which the OOM-killer was called. If it is due to the |
852 | - * memory assigned to the allocating task's cpuset being |
853 | - * exhausted, the allowed memory represents the set of mems |
854 | - * assigned to that cpuset (see cpuset(7)). If it is due |
855 | - * to a mempolicy's node(s) being exhausted, the allowed |
856 | - * memory represents the set of mempolicy nodes. If it is |
857 | - * due to a memory limit (or swap limit) being reached, the |
858 | - * allowed memory is that configured limit. Finally, if it |
859 | - * is due to the entire system being out of memory, the |
860 | - * allowed memory represents all allocatable resources. |
861 | - * |
862 | - * The value of oom_score_adj is added to the badness score before it |
863 | - * is used to determine which task to kill. Acceptable values range |
864 | - * from -1000 (OOM_SCORE_ADJ_MIN) to +1000 (OOM_SCORE_ADJ_MAX). This |
865 | - * allows user space to control the preference for OOM-killing, |
866 | - * ranging from always preferring a certain task or completely |
867 | - * disabling it from OOM- killing. The lowest possible value, -1000, |
868 | - * is equivalent to disabling OOM-killing entirely for that task, |
869 | - * since it will always report a badness score of 0. |
870 | - * |
871 | - * Consequently, it is very simple for user space to define the amount |
872 | - * of memory to consider for each task. Setting a oom_score_adj value |
873 | - * of +500, for example, is roughly equiv‐ alent to allowing the |
874 | - * remainder of tasks sharing the same system, cpuset, mempolicy, or |
875 | - * memory controller resources to use at least 50% more memory. A |
876 | - * value of -500, on the other hand, would be roughly equivalent to |
877 | - * discounting 50% of the task's allowed memory from being considered |
878 | - * as scoring against the task. |
879 | - * |
880 | - * For backward compatibility with previous kernels, |
881 | - * /proc/[pid]/oom_adj can still be used to tune the badness score. |
882 | - * Its value is scaled linearly with oom_score_adj. |
883 | - * |
884 | - * Writing to /proc/[pid]/oom_score_adj or |
885 | - * /proc/[pid]/oom_adj will change the other with its |
886 | - * scaled value. |
887 | - */ |
888 | -struct OomScoreAdjuster |
889 | -{ |
890 | - static int disableOomKillerValue() |
891 | - { |
892 | - return OOM_SCORE_ADJ_MIN; |
893 | - } |
894 | - |
895 | - static int minValue() |
896 | - { |
897 | - return OOM_SCORE_ADJ_MIN; |
898 | - } |
899 | - |
900 | - static int maxValue() |
901 | - { |
902 | - return OOM_SCORE_ADJ_MAX; |
903 | - } |
904 | - |
905 | - static OomScoreAdjuster leastLikelyToBeKilled() |
906 | - { |
907 | - // By system default, we set the oom_score_adj of Unity8 to -10 (via lightdm). |
908 | - // As we want to avoid that any app's oom_score_adj is <= Unity8's oom_score_adj, |
909 | - // we choose a default value of -9, and a default increase of +1. |
910 | - static const int default_value = -9; |
911 | - static const int default_increase = 1; |
912 | - |
913 | - |
914 | - // We could be way more clever here if we knew the distribution |
915 | - // of oom_score_adj values of all app processes. However, we just |
916 | - // make sure that the process is not ignored by the oom killer for now. |
917 | - return OomScoreAdjuster( |
918 | - OomScoreAdjuster::thisProcess().isValid() ? |
919 | - OomScoreAdjuster::thisProcess().value + default_increase : |
920 | - default_value); |
921 | - } |
922 | - |
923 | - static OomScoreAdjuster mostLikelyToBeKilled() |
924 | - { |
925 | - // We avoid extremal values for oom_score_adj. For that, we |
926 | - // set it to 80% of the total available range. If we cannot |
927 | - // determine the oom_score_adj of the current process, i.e., |
928 | - // Unity8, we substract -200 by default. |
929 | - static const float default_percentage = 0.8; |
930 | - static const int default_decrease = -200; |
931 | - |
932 | - return OomScoreAdjuster( |
933 | - OomScoreAdjuster::thisProcess().isValid() ? |
934 | - (maxValue() - OomScoreAdjuster::thisProcess().value) * default_percentage + OomScoreAdjuster::thisProcess().value : |
935 | - maxValue() - default_decrease); |
936 | - } |
937 | - |
938 | - static const OomScoreAdjuster& thisProcess() |
939 | - { |
940 | - // Initialize as invalid. |
941 | - static OomScoreAdjuster adjusterForThisProcess(minValue()-1); |
942 | - |
943 | - static std::once_flag query_once; |
944 | - std::call_once( |
945 | - query_once, |
946 | - []() |
947 | - { |
948 | - pid_t pid = getpid(); |
949 | - |
950 | - auto fn = QString("/proc/%1/oom_score_adj").arg(pid); |
951 | - QFile file(fn); |
952 | - |
953 | - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) |
954 | - return; |
955 | - |
956 | - QTextStream in(&file); |
957 | - int value; in >> value; |
958 | - |
959 | - adjusterForThisProcess.value = value; |
960 | - }); |
961 | - |
962 | - return adjusterForThisProcess; |
963 | - } |
964 | - |
965 | - OomScoreAdjuster(int value) : value(value) |
966 | - { |
967 | - } |
968 | - |
969 | - bool isValid() const |
970 | - { |
971 | - return !(value < disableOomKillerValue() || value > maxValue()); |
972 | - } |
973 | - |
974 | - bool applyForPid(pid_t pid) const |
975 | - { |
976 | - auto fn = QString("/proc/%1/oom_score_adj").arg(pid); |
977 | - QFile file(fn); |
978 | - |
979 | - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) |
980 | - return false; |
981 | - |
982 | - QTextStream out(&file); |
983 | - out << value; |
984 | - |
985 | - return true; |
986 | - } |
987 | - |
988 | - int value; |
989 | -}; |
990 | - |
991 | -void ensureProcessIsUnlikelyToBeKilled(pid_t pid) |
992 | -{ |
993 | - if (!OomScoreAdjuster::leastLikelyToBeKilled().applyForPid(pid)) |
994 | - if (!OomAdjuster::leastLikelyToBeKilled().applyForPid(pid)) |
995 | - LOG("ensureProcessIsUnlikelyToBeKilled failed"); |
996 | -} |
997 | - |
998 | -void ensureProcessIsLikelyToBeKilled(pid_t pid) |
999 | -{ |
1000 | - if (!OomScoreAdjuster::mostLikelyToBeKilled().applyForPid(pid)) |
1001 | - if (!OomAdjuster::mostLikelyToBeKilled().applyForPid(pid)) |
1002 | - LOG("ensureProcessIsLikelyToBeKilled failed"); |
1003 | -} |
1004 | -} |
1005 | - |
1006 | -TaskController* TaskController::m_theTaskController = nullptr; |
1007 | - |
1008 | -TaskController* TaskController::singleton() |
1009 | -{ |
1010 | - if (!m_theTaskController) { |
1011 | - m_theTaskController = new TaskController(); |
1012 | - } |
1013 | - return m_theTaskController; |
1014 | -} |
1015 | - |
1016 | -TaskController::TaskController(QObject *parent) : |
1017 | - QObject(parent) |
1018 | -{ |
1019 | - preStartCallback = [](const gchar * appId, gpointer userData) { |
1020 | - Q_UNUSED(userData) |
1021 | - Q_EMIT TaskController::singleton()->processStartReport(QString(appId), false); |
1022 | - }; |
1023 | - |
1024 | - startedCallback = [](const gchar * appId, gpointer userData) { |
1025 | - Q_UNUSED(userData) |
1026 | - pid_t pid = upstart_app_launch_get_primary_pid(appId); |
1027 | - ensureProcessIsUnlikelyToBeKilled(pid); |
1028 | - }; |
1029 | - |
1030 | - stopCallback = [](const gchar * appId, gpointer userData) { |
1031 | - Q_UNUSED(userData) |
1032 | - Q_EMIT TaskController::singleton()->processStopped(QString(appId), false); |
1033 | - }; |
1034 | - |
1035 | - focusCallback = [](const gchar * appId, gpointer userData) { |
1036 | - Q_UNUSED(userData) |
1037 | - pid_t pid = upstart_app_launch_get_primary_pid(appId); |
1038 | - ensureProcessIsUnlikelyToBeKilled(pid); |
1039 | - Q_EMIT TaskController::singleton()->requestFocus(QString(appId)); |
1040 | - }; |
1041 | - |
1042 | - resumeCallback = [](const gchar * appId, gpointer userData) { |
1043 | - Q_UNUSED(userData) |
1044 | - Q_EMIT TaskController::singleton()->requestResume(QString(appId)); |
1045 | - }; |
1046 | - |
1047 | - failureCallback = [](const gchar * appId, upstart_app_launch_app_failed_t failureType, gpointer userData) { |
1048 | - Q_UNUSED(userData) |
1049 | - if (failureType == UPSTART_APP_LAUNCH_APP_FAILED_CRASH) { |
1050 | - Q_EMIT TaskController::singleton()->processStopped(QString(appId), true); |
1051 | - } else if (failureType == UPSTART_APP_LAUNCH_APP_FAILED_START_FAILURE) { |
1052 | - Q_EMIT TaskController::singleton()->processStartReport(QString(appId), true); |
1053 | - } else { |
1054 | - LOG("TaskController: unknown failure type returned from upstart-app-launch"); |
1055 | - } |
1056 | - Q_EMIT TaskController::singleton()->requestResume(QString(appId)); |
1057 | - }; |
1058 | - |
1059 | - upstart_app_launch_observer_add_app_starting(preStartCallback, nullptr); |
1060 | - upstart_app_launch_observer_add_app_started(startedCallback, nullptr); |
1061 | - upstart_app_launch_observer_add_app_stop(stopCallback, nullptr); |
1062 | - upstart_app_launch_observer_add_app_focus(focusCallback, nullptr); |
1063 | - upstart_app_launch_observer_add_app_resume(resumeCallback, nullptr); |
1064 | - upstart_app_launch_observer_add_app_failed(failureCallback, nullptr); |
1065 | + |
1066 | + |
1067 | +TaskController::TaskController( |
1068 | + QObject* parent, |
1069 | + const QSharedPointer<ApplicationController>& appController, |
1070 | + const QSharedPointer<ProcessController>& processController) : |
1071 | + QObject(parent), |
1072 | + m_appController(appController), |
1073 | + m_processController(processController) |
1074 | +{ |
1075 | + connect(m_appController.data(), |
1076 | + &ApplicationController::applicationAboutToBeStarted, |
1077 | + this, |
1078 | + &TaskController::onApplicationAboutToBeStarted); |
1079 | + |
1080 | + connect(m_appController.data(), |
1081 | + &ApplicationController::applicationStarted, |
1082 | + this, |
1083 | + &TaskController::onApplicationStarted); |
1084 | + |
1085 | + connect(m_appController.data(), |
1086 | + &ApplicationController::applicationStopped, |
1087 | + this, |
1088 | + &TaskController::onApplicationStopped); |
1089 | + |
1090 | + connect(m_appController.data(), |
1091 | + &ApplicationController::applicationFocusRequest, |
1092 | + this, |
1093 | + &TaskController::onApplicationFocusRequest); |
1094 | + |
1095 | + connect(m_appController.data(), |
1096 | + &ApplicationController::applicationResumeRequest, |
1097 | + this, |
1098 | + &TaskController::onApplicationResumeRequest); |
1099 | + |
1100 | + connect(m_appController.data(), |
1101 | + &ApplicationController::applicationError, |
1102 | + this, |
1103 | + &TaskController::onApplicationError); |
1104 | } |
1105 | |
1106 | TaskController::~TaskController() |
1107 | { |
1108 | - upstart_app_launch_observer_delete_app_starting(preStartCallback, nullptr); |
1109 | - upstart_app_launch_observer_delete_app_started(startedCallback, nullptr); |
1110 | - upstart_app_launch_observer_delete_app_stop(stopCallback, nullptr); |
1111 | - upstart_app_launch_observer_delete_app_focus(focusCallback, nullptr); |
1112 | - upstart_app_launch_observer_delete_app_resume(resumeCallback, nullptr); |
1113 | - upstart_app_launch_observer_delete_app_failed(failureCallback, nullptr); |
1114 | } |
1115 | |
1116 | bool TaskController::start(const QString& appId, const QStringList& arguments) |
1117 | { |
1118 | DLOG("TaskController::start appId='%s'", qPrintable(appId)); |
1119 | - gchar ** upstartArgs = nullptr; |
1120 | - bool result = false; |
1121 | - |
1122 | - // Convert arguments QStringList into format suitable for upstart-app-launch |
1123 | - upstartArgs = g_new0(gchar *, arguments.length()); |
1124 | - |
1125 | - for (int i=0; i<arguments.length(); i++) { |
1126 | - upstartArgs[i] = arguments.at(i).toLatin1().data(); |
1127 | - } |
1128 | - |
1129 | - result = upstart_app_launch_start_application(appId.toLatin1().constData(), |
1130 | - static_cast<const gchar * const *>(upstartArgs)); |
1131 | - g_free(upstartArgs); |
1132 | - |
1133 | - DLOG_IF(!result, "TaskController::startApplication appId='%s' FAILED", qPrintable(appId)); |
1134 | - return result; |
1135 | + return m_appController->startApplicationWithAppIdAndArgs(appId, arguments); |
1136 | } |
1137 | |
1138 | bool TaskController::stop(const QString& appId) |
1139 | { |
1140 | DLOG("TaskController::stop appId='%s'", qPrintable(appId)); |
1141 | - bool result = false; |
1142 | - |
1143 | - result = upstart_app_launch_stop_application(appId.toLatin1().constData()); |
1144 | - |
1145 | + auto result = m_appController->stopApplicationWithAppId(appId); |
1146 | DLOG_IF(!result, "TaskController::stopApplication appId='%s' FAILED", qPrintable(appId)); |
1147 | return result; |
1148 | } |
1149 | @@ -398,22 +103,20 @@ |
1150 | bool TaskController::appIdHasProcessId(const QString& appId, const quint64 pid) |
1151 | { |
1152 | DLOG("TaskController::isApplicationPid appId='%s', pid=%lld", qPrintable(appId), pid); |
1153 | - return upstart_app_launch_pid_in_app_id(pid, appId.toLatin1().constData()); |
1154 | + return m_appController->appIdHasProcessId(pid, appId); |
1155 | } |
1156 | |
1157 | bool TaskController::suspend(const QString& appId) |
1158 | { |
1159 | DLOG("TaskController::suspend (this=%p, application=%p)", this, qPrintable(appId)); |
1160 | - pid_t pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData()); |
1161 | - |
1162 | - ensureProcessIsLikelyToBeKilled(pid); |
1163 | + pid_t pid = m_appController->primaryPidForAppId(appId); |
1164 | + m_processController->oomController()->ensureProcessLikelyToBeKilled(pid); |
1165 | |
1166 | if (pid) { |
1167 | // We do assume that the app was launched by upstart and with that, |
1168 | // in its own process group. For that, we interpret the pid as pgid and |
1169 | // sigstop the complete process group on suspend. |
1170 | - kill(-pid, SIGSTOP); |
1171 | - return true; |
1172 | + return m_processController->sigStopProcessGroupForPid(pid); |
1173 | } else { |
1174 | return false; |
1175 | } |
1176 | @@ -422,17 +125,61 @@ |
1177 | bool TaskController::resume(const QString& appId) |
1178 | { |
1179 | DLOG("TaskController::resume (this=%p, application=%p)", this, qPrintable(appId)); |
1180 | - pid_t pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData()); |
1181 | + pid_t pid = m_appController->primaryPidForAppId(appId); |
1182 | |
1183 | - ensureProcessIsUnlikelyToBeKilled(pid); |
1184 | + m_processController->oomController()->ensureProcessUnlikelyToBeKilled(pid); |
1185 | |
1186 | if (pid) { |
1187 | // We do assume that the app was launched by upstart and with that, |
1188 | // in its own process group. For that, we interpret the pid as pgid and |
1189 | // sigcont the complete process group on resume. |
1190 | - kill(-pid, SIGCONT); |
1191 | + return m_processController->sigContinueProcessGroupForPid(pid); |
1192 | return true; |
1193 | } else { |
1194 | return false; |
1195 | } |
1196 | } |
1197 | + |
1198 | +void TaskController::onApplicationAboutToBeStarted(const QString& id) |
1199 | +{ |
1200 | + Q_EMIT processStartReport(id, false); |
1201 | +} |
1202 | + |
1203 | +void TaskController::onApplicationStarted(const QString& id) |
1204 | +{ |
1205 | + pid_t pid = m_appController->primaryPidForAppId(id); |
1206 | + m_processController->oomController()->ensureProcessUnlikelyToBeKilled(pid); |
1207 | +} |
1208 | + |
1209 | +void TaskController::onApplicationStopped(const QString& id) |
1210 | +{ |
1211 | + Q_EMIT processStopped(id, false); |
1212 | +} |
1213 | + |
1214 | +void TaskController::onApplicationFocusRequest(const QString& id) |
1215 | +{ |
1216 | + pid_t pid = m_appController->primaryPidForAppId(id); |
1217 | + m_processController->oomController()->ensureProcessUnlikelyToBeKilled(pid); |
1218 | + Q_EMIT requestFocus(id); |
1219 | +} |
1220 | + |
1221 | +void TaskController::onApplicationResumeRequest(const QString& id) |
1222 | +{ |
1223 | + Q_EMIT requestResume(id); |
1224 | +} |
1225 | + |
1226 | +void TaskController::onApplicationError(const QString& id, ApplicationController::Error error) |
1227 | +{ |
1228 | + switch(error) |
1229 | + { |
1230 | + case ApplicationController::Error::APPLICATION_CRASHED: |
1231 | + Q_EMIT processStopped(id, true); |
1232 | + break; |
1233 | + case ApplicationController::Error::APPLICATION_FAILED_TO_START: |
1234 | + Q_EMIT processStartReport(id, true); |
1235 | + break; |
1236 | + } |
1237 | + |
1238 | + // Is this really the signal we want to emit? |
1239 | + Q_EMIT requestResume(id); |
1240 | +} |
1241 | |
1242 | === modified file 'src/modules/Unity/Application/taskcontroller.h' |
1243 | --- src/modules/Unity/Application/taskcontroller.h 2013-12-05 17:11:36 +0000 |
1244 | +++ src/modules/Unity/Application/taskcontroller.h 2014-02-11 09:48:32 +0000 |
1245 | @@ -22,17 +22,17 @@ |
1246 | #include <QObject> |
1247 | |
1248 | #include "application.h" |
1249 | - |
1250 | -// upstart |
1251 | -extern "C" { |
1252 | - #include "upstart-app-launch.h" |
1253 | -} |
1254 | +#include "applicationcontroller.h" |
1255 | +#include "processcontroller.h" |
1256 | |
1257 | class TaskController : public QObject |
1258 | { |
1259 | Q_OBJECT |
1260 | public: |
1261 | - static TaskController* singleton(); |
1262 | + TaskController( |
1263 | + QObject* parent, |
1264 | + const QSharedPointer<ApplicationController>& app_controller, |
1265 | + const QSharedPointer<ProcessController>& processController = QSharedPointer<ProcessController>(new ProcessController())); |
1266 | ~TaskController(); |
1267 | |
1268 | bool start(const QString& appId, const QStringList& args); |
1269 | @@ -49,12 +49,18 @@ |
1270 | void requestFocus(const QString& appId); |
1271 | void requestResume(const QString& appId); |
1272 | |
1273 | +private Q_SLOTS: |
1274 | + void onApplicationAboutToBeStarted(const QString& id); |
1275 | + void onApplicationStarted(const QString& id); |
1276 | + void onApplicationStopped(const QString& id); |
1277 | + void onApplicationFocusRequest(const QString& id); |
1278 | + void onApplicationResumeRequest(const QString& id); |
1279 | + |
1280 | + void onApplicationError(const QString& id, ApplicationController::Error error); |
1281 | + |
1282 | private: |
1283 | - TaskController(QObject *parent = 0); |
1284 | - |
1285 | - static TaskController* m_theTaskController; |
1286 | - upstart_app_launch_app_observer_t preStartCallback, startedCallback, stopCallback, focusCallback, resumeCallback; |
1287 | - upstart_app_launch_app_failed_observer_t failureCallback; |
1288 | + QSharedPointer<ApplicationController> m_appController; |
1289 | + QSharedPointer<ProcessController> m_processController; |
1290 | }; |
1291 | |
1292 | #endif // TASKCONTROLLER_H |
1293 | |
1294 | === added directory 'src/modules/Unity/Application/upstart' |
1295 | === added file 'src/modules/Unity/Application/upstart/applicationcontroller.cpp' |
1296 | --- src/modules/Unity/Application/upstart/applicationcontroller.cpp 1970-01-01 00:00:00 +0000 |
1297 | +++ src/modules/Unity/Application/upstart/applicationcontroller.cpp 2014-02-11 09:48:32 +0000 |
1298 | @@ -0,0 +1,134 @@ |
1299 | +/* |
1300 | + * Copyright (C) 2014 Canonical, Ltd. |
1301 | + * |
1302 | + * This program is free software: you can redistribute it and/or modify it under |
1303 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1304 | + * the Free Software Foundation. |
1305 | + * |
1306 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1307 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1308 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1309 | + * Lesser General Public License for more details. |
1310 | + * |
1311 | + * You should have received a copy of the GNU Lesser General Public License |
1312 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1313 | + * |
1314 | + */ |
1315 | + |
1316 | +#include "applicationcontroller.h" |
1317 | + |
1318 | +// unity-mir |
1319 | +#include <logging.h> |
1320 | + |
1321 | +// upstart |
1322 | +extern "C" { |
1323 | + #include "upstart-app-launch.h" |
1324 | +} |
1325 | + |
1326 | +struct upstart::ApplicationController::Private |
1327 | +{ |
1328 | + upstart_app_launch_app_observer_t preStartCallback = nullptr; |
1329 | + upstart_app_launch_app_observer_t startedCallback = nullptr; |
1330 | + upstart_app_launch_app_observer_t stopCallback = nullptr; |
1331 | + upstart_app_launch_app_observer_t focusCallback = nullptr; |
1332 | + upstart_app_launch_app_observer_t resumeCallback = nullptr; |
1333 | + upstart_app_launch_app_failed_observer_t failureCallback = nullptr; |
1334 | +}; |
1335 | + |
1336 | +upstart::ApplicationController::ApplicationController() |
1337 | + : ::ApplicationController(), |
1338 | + impl(new Private()) |
1339 | +{ |
1340 | + impl->preStartCallback = [](const gchar * appId, gpointer userData) { |
1341 | + auto thiz = static_cast<upstart::ApplicationController*>(userData); |
1342 | + Q_EMIT(thiz->applicationAboutToBeStarted(appId)); |
1343 | + }; |
1344 | + |
1345 | + impl->startedCallback = [](const gchar * appId, gpointer userData) { |
1346 | + auto thiz = static_cast<upstart::ApplicationController*>(userData); |
1347 | + Q_EMIT(thiz->applicationStarted(appId)); |
1348 | + }; |
1349 | + |
1350 | + impl->stopCallback = [](const gchar * appId, gpointer userData) { |
1351 | + auto thiz = static_cast<upstart::ApplicationController*>(userData); |
1352 | + Q_EMIT(thiz->applicationStopped(appId)); |
1353 | + }; |
1354 | + |
1355 | + impl->focusCallback = [](const gchar * appId, gpointer userData) { |
1356 | + auto thiz = static_cast<upstart::ApplicationController*>(userData); |
1357 | + Q_EMIT(thiz->applicationFocusRequest(appId)); |
1358 | + }; |
1359 | + |
1360 | + impl->resumeCallback = [](const gchar * appId, gpointer userData) { |
1361 | + auto thiz = static_cast<upstart::ApplicationController*>(userData); |
1362 | + Q_EMIT(thiz->applicationResumeRequest(appId)); |
1363 | + }; |
1364 | + |
1365 | + impl->failureCallback = [](const gchar * appId, upstart_app_launch_app_failed_t failureType, gpointer userData) { |
1366 | + ApplicationController::Error error; |
1367 | + switch(failureType) |
1368 | + { |
1369 | + case UPSTART_APP_LAUNCH_APP_FAILED_CRASH: error = ApplicationController::Error::APPLICATION_CRASHED; |
1370 | + case UPSTART_APP_LAUNCH_APP_FAILED_START_FAILURE: error = ApplicationController::Error::APPLICATION_FAILED_TO_START; |
1371 | + } |
1372 | + |
1373 | + auto thiz = static_cast<upstart::ApplicationController*>(userData); |
1374 | + Q_EMIT(thiz->applicationError(appId, error)); |
1375 | + }; |
1376 | + |
1377 | + upstart_app_launch_observer_add_app_starting(impl->preStartCallback, this); |
1378 | + upstart_app_launch_observer_add_app_started(impl->startedCallback, this); |
1379 | + upstart_app_launch_observer_add_app_stop(impl->stopCallback, this); |
1380 | + upstart_app_launch_observer_add_app_focus(impl->focusCallback, this); |
1381 | + upstart_app_launch_observer_add_app_resume(impl->resumeCallback, this); |
1382 | + upstart_app_launch_observer_add_app_failed(impl->failureCallback, this); |
1383 | +} |
1384 | + |
1385 | +upstart::ApplicationController::~ApplicationController() |
1386 | +{ |
1387 | + upstart_app_launch_observer_delete_app_starting(impl->preStartCallback, this); |
1388 | + upstart_app_launch_observer_delete_app_started(impl->startedCallback, this); |
1389 | + upstart_app_launch_observer_delete_app_stop(impl->stopCallback, this); |
1390 | + upstart_app_launch_observer_delete_app_focus(impl->focusCallback, this); |
1391 | + upstart_app_launch_observer_delete_app_resume(impl->resumeCallback, this); |
1392 | + upstart_app_launch_observer_delete_app_failed(impl->failureCallback, this); |
1393 | +} |
1394 | + |
1395 | +pid_t upstart::ApplicationController::primaryPidForAppId(const QString& appId) |
1396 | +{ |
1397 | + GPid pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData()); |
1398 | + DLOG_IF(!pid, "ApplicationController::stopApplication appId='%s' FAILED", qPrintable(appId)); |
1399 | + |
1400 | + return pid; |
1401 | +} |
1402 | + |
1403 | +bool upstart::ApplicationController::appIdHasProcessId(pid_t pid, const QString& appId) |
1404 | +{ |
1405 | + return upstart_app_launch_pid_in_app_id(pid, appId.toLatin1().constData()); |
1406 | +} |
1407 | + |
1408 | +bool upstart::ApplicationController::stopApplicationWithAppId(const QString& appId) |
1409 | +{ |
1410 | + auto result = upstart_app_launch_stop_application(appId.toLatin1().constData()); |
1411 | + DLOG_IF(!result, "upstart::ApplicationController::stopApplicationWithAppId appId='%s' FAILED", qPrintable(appId)); |
1412 | + return result; |
1413 | +} |
1414 | + |
1415 | +bool upstart::ApplicationController::startApplicationWithAppIdAndArgs(const QString& appId, const QStringList& arguments) |
1416 | +{ |
1417 | + // Convert arguments QStringList into format suitable for upstart-app-launch |
1418 | + auto upstartArgs = g_new0(gchar *, arguments.length()); |
1419 | + |
1420 | + for (int i=0; i<arguments.length(); i++) { |
1421 | + upstartArgs[i] = arguments.at(i).toLatin1().data(); |
1422 | + } |
1423 | + |
1424 | + auto result = upstart_app_launch_start_application( |
1425 | + appId.toLatin1().constData(), |
1426 | + static_cast<const gchar * const *>(upstartArgs)); |
1427 | + |
1428 | + g_free(upstartArgs); |
1429 | + |
1430 | + DLOG_IF(!result, "upstart::Application::Controller::startApplicationWithAppIdAndArgs appId='%s' FAILED", qPrintable(appId)); |
1431 | + return result; |
1432 | +} |
1433 | |
1434 | === added file 'src/modules/Unity/Application/upstart/applicationcontroller.h' |
1435 | --- src/modules/Unity/Application/upstart/applicationcontroller.h 1970-01-01 00:00:00 +0000 |
1436 | +++ src/modules/Unity/Application/upstart/applicationcontroller.h 2014-02-11 09:48:32 +0000 |
1437 | @@ -0,0 +1,43 @@ |
1438 | +/* |
1439 | + * Copyright (C) 2013 Canonical, Ltd. |
1440 | + * |
1441 | + * This program is free software: you can redistribute it and/or modify it under |
1442 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1443 | + * the Free Software Foundation. |
1444 | + * |
1445 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1446 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1447 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1448 | + * Lesser General Public License for more details. |
1449 | + * |
1450 | + * You should have received a copy of the GNU Lesser General Public License |
1451 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1452 | + * |
1453 | + */ |
1454 | + |
1455 | +#ifndef UPSTART_APPLICATION_CONTROLLER_H |
1456 | +#define UPSTART_APPLICATION_CONTROLLER_H |
1457 | + |
1458 | +#include "../applicationcontroller.h" |
1459 | + |
1460 | +namespace upstart |
1461 | +{ |
1462 | +class ApplicationController : public ::ApplicationController |
1463 | +{ |
1464 | +public: |
1465 | + ApplicationController(); |
1466 | + ~ApplicationController(); |
1467 | + |
1468 | + pid_t primaryPidForAppId(const QString& appId); |
1469 | + bool appIdHasProcessId(pid_t pid, const QString& appId); |
1470 | + |
1471 | + bool stopApplicationWithAppId(const QString& appId); |
1472 | + bool startApplicationWithAppIdAndArgs(const QString& appId, const QStringList& arguments); |
1473 | + |
1474 | +private: |
1475 | + struct Private; |
1476 | + QScopedPointer<Private> impl; |
1477 | +}; |
1478 | +} |
1479 | + |
1480 | +#endif // UPSTART_APPLICATION_CONTROLLER_H |
1481 | |
1482 | === modified file 'tests/CMakeLists.txt' |
1483 | --- tests/CMakeLists.txt 2014-01-14 16:11:40 +0000 |
1484 | +++ tests/CMakeLists.txt 2014-02-11 09:48:32 +0000 |
1485 | @@ -1,1 +1,71 @@ |
1486 | +find_package(Qt5Core REQUIRED) |
1487 | +find_package(Qt5Quick REQUIRED) |
1488 | +find_package(Qt5DBus REQUIRED) |
1489 | + |
1490 | +# Build with system gmock and embedded gtest |
1491 | +set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory") |
1492 | +set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory") |
1493 | +set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory") |
1494 | + |
1495 | +add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") |
1496 | + |
1497 | +#set (OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) |
1498 | +# Don't treat warnings as errors in 3rd_party/{gmock,cucumber-cpp} |
1499 | +#string (REPLACE " -Werror " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) |
1500 | +#find_package(Gtest REQUIRED) |
1501 | +#include_directories(${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIR}) |
1502 | +#set (CMAKE_CXX_FLAGS ${OLD_CMAKE_CXX_FLAGS}) |
1503 | + |
1504 | +include_directories( |
1505 | + ${CMAKE_SOURCE_DIR}/src/modules |
1506 | + ${GMOCK_INCLUDE_DIR} |
1507 | + ${GTEST_INCLUDE_DIR} |
1508 | +) |
1509 | + |
1510 | +add_executable( |
1511 | + application_manager_test |
1512 | + application_manager_test.cpp |
1513 | +) |
1514 | + |
1515 | +add_executable( |
1516 | + taskcontroller_test |
1517 | + taskcontroller_test.cpp |
1518 | +) |
1519 | + |
1520 | +# We should not need this line according to the Qt5/CMake docs. |
1521 | +# However, when removing it, include paths are not set and linking to Qt5 fails. |
1522 | +qt5_use_modules(application_manager_test Core Quick DBus) |
1523 | +qt5_use_modules(taskcontroller_test Core Quick DBus) |
1524 | + |
1525 | +target_link_libraries( |
1526 | + application_manager_test |
1527 | + |
1528 | + unityapplicationplugin |
1529 | + |
1530 | + Qt5::Core |
1531 | + Qt5::Quick |
1532 | + Qt5::DBus |
1533 | + |
1534 | + gtest |
1535 | + gtest_main |
1536 | + gmock |
1537 | +) |
1538 | + |
1539 | +target_link_libraries( |
1540 | + taskcontroller_test |
1541 | + |
1542 | + unityapplicationplugin |
1543 | + |
1544 | + Qt5::Core |
1545 | + Qt5::Quick |
1546 | + Qt5::DBus |
1547 | + |
1548 | + gtest |
1549 | + gtest_main |
1550 | + gmock |
1551 | +) |
1552 | + |
1553 | +add_test(taskcontroller_test ${CMAKE_CURRENT_BINARY_DIR}/taskcontroller_test) |
1554 | +add_test(application_manager_test ${CMAKE_CURRENT_BINARY_DIR}/application_manager_test) |
1555 | + |
1556 | add_subdirectory(auto) |
1557 | |
1558 | === added file 'tests/application_manager_test.cpp' |
1559 | --- tests/application_manager_test.cpp 1970-01-01 00:00:00 +0000 |
1560 | +++ tests/application_manager_test.cpp 2014-02-11 09:48:32 +0000 |
1561 | @@ -0,0 +1,151 @@ |
1562 | +/* |
1563 | + * Copyright (C) 2013 Canonical, Ltd. |
1564 | + * |
1565 | + * This program is free software: you can redistribute it and/or modify it under |
1566 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1567 | + * the Free Software Foundation. |
1568 | + * |
1569 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1570 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1571 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1572 | + * Lesser General Public License for more details. |
1573 | + * |
1574 | + * You should have received a copy of the GNU Lesser General Public License |
1575 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1576 | + * |
1577 | + */ |
1578 | + |
1579 | +#include <Unity/Application/application_manager.h> |
1580 | + |
1581 | +#include <Unity/Application/applicationcontroller.h> |
1582 | +#include <Unity/Application/taskcontroller.h> |
1583 | + |
1584 | +#include <core/posix/linux/proc/process/oom_score_adj.h> |
1585 | + |
1586 | +#include <gmock/gmock.h> |
1587 | +#include <gtest/gtest.h> |
1588 | + |
1589 | +#include "mock_application_controller.h" |
1590 | +#include "mock_desktop_file_reader.h" |
1591 | +#include "mock_oom_controller.h" |
1592 | +#include "mock_process_controller.h" |
1593 | + |
1594 | +TEST(ApplicationManager, SuspendingAndResumingARunningApplicationResultsInOomScoreAdjustment) |
1595 | +{ |
1596 | + using namespace ::testing; |
1597 | + |
1598 | + const QString appId("com.canonical.does.not.exist"); |
1599 | + |
1600 | + NiceMock<testing::MockOomController> oomController; |
1601 | + QSharedPointer<ProcessController::OomController> oomControllerPtr( |
1602 | + &oomController, |
1603 | + [](ProcessController::OomController*){}); |
1604 | + |
1605 | + NiceMock<testing::MockProcessController> processController(oomControllerPtr); |
1606 | + QSharedPointer<ProcessController> processControllerPtr( |
1607 | + &processController, |
1608 | + [](ProcessController*){}); |
1609 | + |
1610 | + NiceMock<testing::MockApplicationController> appController; |
1611 | + QSharedPointer<ApplicationController> appControllerPtr( |
1612 | + &appController, |
1613 | + [](ApplicationController*){}); |
1614 | + |
1615 | + QSharedPointer<TaskController> taskController( |
1616 | + new TaskController( |
1617 | + nullptr, |
1618 | + appControllerPtr, |
1619 | + processControllerPtr |
1620 | + )); |
1621 | + |
1622 | + NiceMock<MockDesktopFileReaderFactory> desktopFileReaderFactory; |
1623 | + QSharedPointer<DesktopFileReader::Factory> desktopFileReaderFactoryPtr( |
1624 | + &desktopFileReaderFactory, |
1625 | + [](DesktopFileReader::Factory*){}); |
1626 | + |
1627 | + ApplicationManager applicationManager( |
1628 | + taskController, |
1629 | + desktopFileReaderFactoryPtr); |
1630 | + |
1631 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1); |
1632 | + |
1633 | + EXPECT_CALL(desktopFileReaderFactory, createInstanceForAppId(appId)).Times(1); |
1634 | + |
1635 | + EXPECT_CALL(processController, sigStopProcessGroupForPid(_)).Times(1); |
1636 | + EXPECT_CALL(processController, sigContinueProcessGroupForPid(_)).Times(1); |
1637 | + EXPECT_CALL(oomController, ensureProcessUnlikelyToBeKilled(_)).Times(1); |
1638 | + EXPECT_CALL(oomController, ensureProcessLikelyToBeKilled(_)).Times(1); |
1639 | + |
1640 | + auto application = applicationManager.startApplication( |
1641 | + appId, |
1642 | + ApplicationManager::NoFlag, |
1643 | + QStringList()); |
1644 | + |
1645 | + application->suspend(); |
1646 | + application->resume(); |
1647 | +} |
1648 | + |
1649 | +// Currently disabled as we need to make sure that we have a corresponding mir session, too. |
1650 | +TEST(ApplicationManager, DISABLED_FocusingRunningApplicationResultsInOomScoreAdjustment) |
1651 | +{ |
1652 | + using namespace ::testing; |
1653 | + |
1654 | + const QString appId("com.canonical.does.not.exist"); |
1655 | + |
1656 | + NiceMock<testing::MockOomController> oomController; |
1657 | + QSharedPointer<ProcessController::OomController> oomControllerPtr( |
1658 | + &oomController, |
1659 | + [](ProcessController::OomController*){}); |
1660 | + |
1661 | + NiceMock<testing::MockProcessController> processController(oomControllerPtr); |
1662 | + QSharedPointer<ProcessController> processControllerPtr( |
1663 | + &processController, |
1664 | + [](ProcessController*){}); |
1665 | + |
1666 | + NiceMock<testing::MockApplicationController> appController; |
1667 | + QSharedPointer<ApplicationController> appControllerPtr( |
1668 | + &appController, |
1669 | + [](ApplicationController*){}); |
1670 | + |
1671 | + QSharedPointer<TaskController> taskController( |
1672 | + new TaskController( |
1673 | + nullptr, |
1674 | + appControllerPtr, |
1675 | + processControllerPtr |
1676 | + )); |
1677 | + |
1678 | + NiceMock<MockDesktopFileReaderFactory> desktopFileReaderFactory; |
1679 | + QSharedPointer<DesktopFileReader::Factory> desktopFileReaderFactoryPtr( |
1680 | + &desktopFileReaderFactory, |
1681 | + [](DesktopFileReader::Factory*){}); |
1682 | + |
1683 | + ApplicationManager applicationManager( |
1684 | + taskController, |
1685 | + desktopFileReaderFactoryPtr); |
1686 | + |
1687 | + QSet<QString> appIds; |
1688 | + |
1689 | + for (unsigned int i = 0; i < 50; i++) |
1690 | + { |
1691 | + QString appIdFormat("%1.does.not.exist"); |
1692 | + auto appId = appIdFormat.arg(i); |
1693 | + |
1694 | + auto application = applicationManager.startApplication( |
1695 | + appId, |
1696 | + ApplicationManager::NoFlag, |
1697 | + QStringList()); |
1698 | + |
1699 | + EXPECT_NE(nullptr, application); |
1700 | + |
1701 | + appIds.insert(appId); |
1702 | + auto it = appController.children.find(appId); |
1703 | + if (it != appController.children.end()) |
1704 | + EXPECT_CALL(oomController, |
1705 | + ensureProcessUnlikelyToBeKilled(it->pid())).Times(1); |
1706 | + } |
1707 | + |
1708 | + for (auto appId : appIds) |
1709 | + { |
1710 | + applicationManager.focusApplication(appId); |
1711 | + } |
1712 | +} |
1713 | |
1714 | === modified file 'tests/auto/modules/Unity/Application/CMakeLists.txt' |
1715 | --- tests/auto/modules/Unity/Application/CMakeLists.txt 2014-01-14 16:01:18 +0000 |
1716 | +++ tests/auto/modules/Unity/Application/CMakeLists.txt 2014-02-11 09:48:32 +0000 |
1717 | @@ -1,28 +1,27 @@ |
1718 | add_subdirectory(fakeApp) |
1719 | |
1720 | include_directories( |
1721 | - ../../../../../src/modules/Unity/Application |
1722 | - ../../../../../src/unity-mir |
1723 | + ${CMAKE_SOURCE_DIR}/src/modules/Unity/Application |
1724 | + ${CMAKE_SOURCE_DIR}/src/unity-mir |
1725 | + |
1726 | ${MIRSERVER_INCLUDE_DIRS} |
1727 | - ${UPSTART_APP_LAUNCH_INCLUDE_DIRS} |
1728 | - ) |
1729 | - |
1730 | -add_executable(unity-mir-test-app |
1731 | - main.cpp |
1732 | - ../../../../../src/modules/Unity/Application/application_manager.cpp |
1733 | - ../../../../../src/modules/Unity/Application/application.cpp |
1734 | - ../../../../../src/modules/Unity/Application/desktopfilereader.cpp |
1735 | - ../../../../../src/modules/Unity/Application/dbuswindowstack.cpp |
1736 | - ../../../../../src/modules/Unity/Application/taskcontroller.cpp |
1737 | - # We need to pull in some external header files |
1738 | - /usr/include/unity/shell/application/ApplicationManagerInterface.h |
1739 | - /usr/include/unity/shell/application/ApplicationInfoInterface.h |
1740 | - ) |
1741 | + ${UPSTART_APP_LAUNCH_INCLUDE_DIRS}) |
1742 | + |
1743 | +add_executable( |
1744 | + unity-mir-test-app |
1745 | + main.cpp) |
1746 | + |
1747 | qt5_use_modules(unity-mir-test-app Test DBus Gui) |
1748 | -install(TARGETS unity-mir-test-app RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) |
1749 | -target_link_libraries(unity-mir-test-app |
1750 | + |
1751 | +target_link_libraries( |
1752 | + unity-mir-test-app |
1753 | + |
1754 | unity-mir |
1755 | + unityapplicationplugin |
1756 | + |
1757 | ${MIRSERVER_LIBRARIES} |
1758 | - ${UPSTART_APP_LAUNCH_LIBRARIES} |
1759 | - ) |
1760 | -install(TARGETS unity-mir-test-app RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) |
1761 | + ${UPSTART_APP_LAUNCH_LIBRARIES}) |
1762 | + |
1763 | +install( |
1764 | + TARGETS unity-mir-test-app |
1765 | + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) |
1766 | |
1767 | === modified file 'tests/auto/modules/Unity/Application/main.cpp' |
1768 | --- tests/auto/modules/Unity/Application/main.cpp 2014-01-08 12:23:11 +0000 |
1769 | +++ tests/auto/modules/Unity/Application/main.cpp 2014-02-11 09:48:32 +0000 |
1770 | @@ -17,6 +17,9 @@ |
1771 | #include <QtTest/QtTest> |
1772 | |
1773 | #include "application_manager.h" |
1774 | +#include "processcontroller.h" |
1775 | +#include "taskcontroller.h" |
1776 | +#include "upstart/applicationcontroller.h" |
1777 | |
1778 | #include "qmirserverapplication.h" |
1779 | #include "qmirserver.h" |
1780 | @@ -41,7 +44,11 @@ |
1781 | |
1782 | void ApplicationManagerTests::testStartStop() |
1783 | { |
1784 | - ApplicationManager manager; |
1785 | + QSharedPointer<upstart::ApplicationController> appController(new upstart::ApplicationController()); |
1786 | + QSharedPointer<TaskController> taskController(new TaskController(nullptr, appController)); |
1787 | + QSharedPointer<DesktopFileReader::Factory> fileReaderFactory(new DesktopFileReader::Factory()); |
1788 | + |
1789 | + ApplicationManager manager(taskController, fileReaderFactory); |
1790 | Application *app = manager.startApplication("unity-mir-test-helper-app", QStringList()); |
1791 | QVERIFY(app); |
1792 | QCOMPARE(app->desktopFile(), QString("/usr/share/applications/unity-mir-test-helper-app.desktop")); |
1793 | |
1794 | === added file 'tests/mock_application_controller.h' |
1795 | --- tests/mock_application_controller.h 1970-01-01 00:00:00 +0000 |
1796 | +++ tests/mock_application_controller.h 2014-02-11 09:48:32 +0000 |
1797 | @@ -0,0 +1,106 @@ |
1798 | +/* |
1799 | + * Copyright (C) 2013 Canonical, Ltd. |
1800 | + * |
1801 | + * This program is free software: you can redistribute it and/or modify it under |
1802 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1803 | + * the Free Software Foundation. |
1804 | + * |
1805 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1806 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1807 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1808 | + * Lesser General Public License for more details. |
1809 | + * |
1810 | + * You should have received a copy of the GNU Lesser General Public License |
1811 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1812 | + * |
1813 | + */ |
1814 | + |
1815 | +#ifndef MOCK_APPLICATION_CONTROLLER_H |
1816 | +#define MOCK_APPLICATION_CONTROLLER_H |
1817 | + |
1818 | +#include <Unity/Application/applicationcontroller.h> |
1819 | + |
1820 | +#include <core/posix/fork.h> |
1821 | + |
1822 | +#include <gmock/gmock.h> |
1823 | + |
1824 | +namespace testing |
1825 | +{ |
1826 | +struct MockApplicationController : public ApplicationController |
1827 | +{ |
1828 | + MOCK_METHOD1(primaryPidForAppId, pid_t(const QString& appId)); |
1829 | + MOCK_METHOD2(appIdHasProcessId, bool(pid_t, const QString&)); |
1830 | + |
1831 | + MOCK_METHOD1(stopApplicationWithAppId, bool(const QString&)); |
1832 | + MOCK_METHOD2(startApplicationWithAppIdAndArgs, bool(const QString&, const QStringList&)); |
1833 | + |
1834 | + MockApplicationController() |
1835 | + { |
1836 | + using namespace ::testing; |
1837 | + ON_CALL(*this, primaryPidForAppId(_)) |
1838 | + .WillByDefault( |
1839 | + Invoke(this, &MockApplicationController::doPrimaryPidForAppId)); |
1840 | + |
1841 | + ON_CALL(*this, appIdHasProcessId(_, _)) |
1842 | + .WillByDefault( |
1843 | + Invoke(this, &MockApplicationController::doAppIdHasProcessId)); |
1844 | + |
1845 | + ON_CALL(*this, stopApplicationWithAppId(_)) |
1846 | + .WillByDefault( |
1847 | + Invoke(this, &MockApplicationController::doStopApplicationWithAppId)); |
1848 | + |
1849 | + ON_CALL(*this, startApplicationWithAppIdAndArgs(_, _)) |
1850 | + .WillByDefault( |
1851 | + Invoke(this, &MockApplicationController::doStartApplicationWithAppIdAndArgs)); |
1852 | + } |
1853 | + |
1854 | + pid_t doPrimaryPidForAppId(const QString& appId) |
1855 | + { |
1856 | + auto it = children.find(appId); |
1857 | + if (it == children.end()) |
1858 | + return -1; |
1859 | + |
1860 | + return it->pid(); |
1861 | + } |
1862 | + |
1863 | + bool doAppIdHasProcessId(pid_t pid, const QString& appId) |
1864 | + { |
1865 | + auto it = children.find(appId); |
1866 | + if (it == children.end()) |
1867 | + return -1; |
1868 | + |
1869 | + return it->pid() == pid; |
1870 | + } |
1871 | + |
1872 | + bool doStopApplicationWithAppId(const QString& appId) |
1873 | + { |
1874 | + (void) appId; |
1875 | + |
1876 | + return false; |
1877 | + } |
1878 | + |
1879 | + bool doStartApplicationWithAppIdAndArgs(const QString& appId, const QStringList& args) |
1880 | + { |
1881 | + (void) args; |
1882 | + |
1883 | + auto child = core::posix::fork([]() |
1884 | + { |
1885 | + while (true); |
1886 | + |
1887 | + return core::posix::exit::Status::success; |
1888 | + }, core::posix::StandardStream::empty); |
1889 | + |
1890 | + if (child.pid() > 0) |
1891 | + { |
1892 | + children.insert(appId, child); |
1893 | + return true; |
1894 | + } |
1895 | + |
1896 | + return false; |
1897 | + } |
1898 | + |
1899 | + QMap<QString, core::posix::ChildProcess> children; |
1900 | +}; |
1901 | +} |
1902 | + |
1903 | +#endif // MOCK_APPLICATION_CONTROLLER_H |
1904 | |
1905 | === added file 'tests/mock_desktop_file_reader.h' |
1906 | --- tests/mock_desktop_file_reader.h 1970-01-01 00:00:00 +0000 |
1907 | +++ tests/mock_desktop_file_reader.h 2014-02-11 09:48:32 +0000 |
1908 | @@ -0,0 +1,165 @@ |
1909 | +/* |
1910 | + * Copyright (C) 2013 Canonical, Ltd. |
1911 | + * |
1912 | + * This program is free software: you can redistribute it and/or modify it under |
1913 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1914 | + * the Free Software Foundation. |
1915 | + * |
1916 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1917 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1918 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1919 | + * Lesser General Public License for more details. |
1920 | + * |
1921 | + * You should have received a copy of the GNU Lesser General Public License |
1922 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1923 | + * |
1924 | + */ |
1925 | + |
1926 | +#ifndef MOCK_DESKTOP_FILE_READER_H |
1927 | +#define MOCK_DESKTOP_FILE_READER_H |
1928 | + |
1929 | +#include <Unity/Application/desktopfilereader.h> |
1930 | + |
1931 | +#include <gmock/gmock.h> |
1932 | + |
1933 | +namespace testing |
1934 | +{ |
1935 | +struct MockDesktopFileReader : public DesktopFileReader |
1936 | +{ |
1937 | + MockDesktopFileReader(const QString& appId) |
1938 | + : DesktopFileReader(appId) |
1939 | + { |
1940 | + using namespace ::testing; |
1941 | + |
1942 | + ON_CALL(*this, file()).WillByDefault(Invoke(this, &MockDesktopFileReader::doFile)); |
1943 | + ON_CALL(*this, appId()).WillByDefault(Invoke(this, &MockDesktopFileReader::doAppId)); |
1944 | + ON_CALL(*this, name()).WillByDefault(Invoke(this, &MockDesktopFileReader::doName)); |
1945 | + ON_CALL(*this, comment()).WillByDefault(Invoke(this, &MockDesktopFileReader::doComment)); |
1946 | + ON_CALL(*this, icon()).WillByDefault(Invoke(this, &MockDesktopFileReader::doIcon)); |
1947 | + ON_CALL(*this, exec()).WillByDefault(Invoke(this, &MockDesktopFileReader::doExec)); |
1948 | + ON_CALL(*this, path()).WillByDefault(Invoke(this, &MockDesktopFileReader::doPath)); |
1949 | + ON_CALL(*this, stageHint()).WillByDefault(Invoke(this, &MockDesktopFileReader::doStageHint)); |
1950 | + ON_CALL(*this, loaded()).WillByDefault(Invoke(this, &MockDesktopFileReader::doLoaded)); |
1951 | + ON_CALL(*this, findDesktopFile(_)).WillByDefault(Invoke(this, &MockDesktopFileReader::doFindDesktopFile)); |
1952 | + } |
1953 | + |
1954 | + MockDesktopFileReader(const QFileInfo& fileInfo) |
1955 | + : DesktopFileReader(fileInfo) |
1956 | + { |
1957 | + using namespace ::testing; |
1958 | + |
1959 | + ON_CALL(*this, file()).WillByDefault(Invoke(this, &MockDesktopFileReader::doFile)); |
1960 | + ON_CALL(*this, appId()).WillByDefault(Invoke(this, &MockDesktopFileReader::doAppId)); |
1961 | + ON_CALL(*this, name()).WillByDefault(Invoke(this, &MockDesktopFileReader::doName)); |
1962 | + ON_CALL(*this, comment()).WillByDefault(Invoke(this, &MockDesktopFileReader::doComment)); |
1963 | + ON_CALL(*this, icon()).WillByDefault(Invoke(this, &MockDesktopFileReader::doIcon)); |
1964 | + ON_CALL(*this, exec()).WillByDefault(Invoke(this, &MockDesktopFileReader::doExec)); |
1965 | + ON_CALL(*this, path()).WillByDefault(Invoke(this, &MockDesktopFileReader::doPath)); |
1966 | + ON_CALL(*this, stageHint()).WillByDefault(Invoke(this, &MockDesktopFileReader::doStageHint)); |
1967 | + ON_CALL(*this, loaded()).WillByDefault(Invoke(this, &MockDesktopFileReader::doLoaded)); |
1968 | + ON_CALL(*this, findDesktopFile(_)).WillByDefault(Invoke(this, &MockDesktopFileReader::doFindDesktopFile)); |
1969 | + } |
1970 | + |
1971 | + MOCK_CONST_METHOD0(file, QString()); |
1972 | + MOCK_CONST_METHOD0(appId, QString ()); |
1973 | + MOCK_CONST_METHOD0(name, QString()); |
1974 | + MOCK_CONST_METHOD0(comment, QString()); |
1975 | + MOCK_CONST_METHOD0(icon, QString()); |
1976 | + MOCK_CONST_METHOD0(exec, QString()); |
1977 | + MOCK_CONST_METHOD0(path, QString()); |
1978 | + MOCK_CONST_METHOD0(stageHint, QString()); |
1979 | + MOCK_CONST_METHOD0(loaded, bool()); |
1980 | + MOCK_CONST_METHOD1(findDesktopFile, QString(const QString&)); |
1981 | + |
1982 | + QString doFile() const |
1983 | + { |
1984 | + return DesktopFileReader::file(); |
1985 | + } |
1986 | + |
1987 | + QString doAppId() const |
1988 | + { |
1989 | + return DesktopFileReader::appId(); |
1990 | + } |
1991 | + |
1992 | + QString doName() const |
1993 | + { |
1994 | + return DesktopFileReader::name(); |
1995 | + } |
1996 | + |
1997 | + QString doComment() const |
1998 | + { |
1999 | + return DesktopFileReader::comment(); |
2000 | + } |
2001 | + |
2002 | + QString doIcon() const |
2003 | + { |
2004 | + return DesktopFileReader::icon(); |
2005 | + } |
2006 | + |
2007 | + QString doExec() const |
2008 | + { |
2009 | + return DesktopFileReader::exec(); |
2010 | + } |
2011 | + |
2012 | + QString doPath() const |
2013 | + { |
2014 | + return DesktopFileReader::path(); |
2015 | + } |
2016 | + |
2017 | + QString doStageHint() const |
2018 | + { |
2019 | + return DesktopFileReader::stageHint(); |
2020 | + } |
2021 | + |
2022 | + bool doLoaded() const |
2023 | + { |
2024 | + return DesktopFileReader::loaded(); |
2025 | + } |
2026 | + |
2027 | + QString doFindDesktopFile(const QString& appId) const |
2028 | + { |
2029 | + return DesktopFileReader::findDesktopFile(appId); |
2030 | + } |
2031 | +}; |
2032 | + |
2033 | +struct MockDesktopFileReaderFactory : public DesktopFileReader::Factory |
2034 | +{ |
2035 | + MockDesktopFileReaderFactory() |
2036 | + { |
2037 | + using namespace ::testing; |
2038 | + ON_CALL(*this, createInstanceForAppId(_)) |
2039 | + .WillByDefault( |
2040 | + Invoke( |
2041 | + this, |
2042 | + &MockDesktopFileReaderFactory::doCreateInstanceForAppId)); |
2043 | + ON_CALL(*this, createInstanceForDesktopFile(_)) |
2044 | + .WillByDefault( |
2045 | + Invoke( |
2046 | + this, |
2047 | + &MockDesktopFileReaderFactory::doCreateInstanceForDesktopFile)); |
2048 | + } |
2049 | + |
2050 | + virtual DesktopFileReader* doCreateInstanceForAppId(const QString& appId) |
2051 | + { |
2052 | + using namespace ::testing; |
2053 | + auto instance = new NiceMock<MockDesktopFileReader>(appId); |
2054 | + ON_CALL(*instance, loaded()).WillByDefault(Return(true)); |
2055 | + |
2056 | + return instance; |
2057 | + } |
2058 | + |
2059 | + virtual DesktopFileReader* doCreateInstanceForDesktopFile(const QFileInfo& fi) |
2060 | + { |
2061 | + using namespace ::testing; |
2062 | + auto instance = new NiceMock<MockDesktopFileReader>(fi); |
2063 | + ON_CALL(*instance, loaded()).WillByDefault(Return(true)); |
2064 | + |
2065 | + return instance; |
2066 | + } |
2067 | + |
2068 | + MOCK_METHOD1(createInstanceForAppId, DesktopFileReader*(const QString&)); |
2069 | + MOCK_METHOD1(createInstanceForDesktopFile, DesktopFileReader*(const QFileInfo&)); |
2070 | +}; |
2071 | +} |
2072 | + |
2073 | +#endif // MOCK_DESKTOP_FILE_READER_H |
2074 | |
2075 | === added file 'tests/mock_oom_controller.h' |
2076 | --- tests/mock_oom_controller.h 1970-01-01 00:00:00 +0000 |
2077 | +++ tests/mock_oom_controller.h 2014-02-11 09:48:32 +0000 |
2078 | @@ -0,0 +1,33 @@ |
2079 | +/* |
2080 | + * Copyright (C) 2013 Canonical, Ltd. |
2081 | + * |
2082 | + * This program is free software: you can redistribute it and/or modify it under |
2083 | + * the terms of the GNU Lesser General Public License version 3, as published by |
2084 | + * the Free Software Foundation. |
2085 | + * |
2086 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
2087 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
2088 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
2089 | + * Lesser General Public License for more details. |
2090 | + * |
2091 | + * You should have received a copy of the GNU Lesser General Public License |
2092 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2093 | + * |
2094 | + */ |
2095 | +#ifndef MOCK_OOM_CONTROLLER_H |
2096 | +#define MOCK_OOM_CONTROLLER_H |
2097 | + |
2098 | +#include <Unity/Application/processcontroller.h> |
2099 | + |
2100 | +#include <gmock/gmock.h> |
2101 | + |
2102 | +namespace testing |
2103 | +{ |
2104 | +struct MockOomController : public ProcessController::OomController |
2105 | +{ |
2106 | + MOCK_METHOD1(ensureProcessLikelyToBeKilled, void(pid_t)); |
2107 | + MOCK_METHOD1(ensureProcessUnlikelyToBeKilled, void(pid_t)); |
2108 | +}; |
2109 | +} |
2110 | + |
2111 | +#endif // MOCK_OOM_CONTROLLER_H |
2112 | |
2113 | === added file 'tests/mock_process_controller.h' |
2114 | --- tests/mock_process_controller.h 1970-01-01 00:00:00 +0000 |
2115 | +++ tests/mock_process_controller.h 2014-02-11 09:48:32 +0000 |
2116 | @@ -0,0 +1,45 @@ |
2117 | +/* |
2118 | + * Copyright (C) 2013 Canonical, Ltd. |
2119 | + * |
2120 | + * This program is free software: you can redistribute it and/or modify it under |
2121 | + * the terms of the GNU Lesser General Public License version 3, as published by |
2122 | + * the Free Software Foundation. |
2123 | + * |
2124 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
2125 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
2126 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
2127 | + * Lesser General Public License for more details. |
2128 | + * |
2129 | + * You should have received a copy of the GNU Lesser General Public License |
2130 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2131 | + * |
2132 | + */ |
2133 | +#ifndef MOCK_PROCESS_CONTROLLER_H |
2134 | +#define MOCK_PROCESS_CONTROLLER_H |
2135 | + |
2136 | +#include <Unity/Application/processcontroller.h> |
2137 | + |
2138 | +#include <gmock/gmock.h> |
2139 | + |
2140 | +namespace testing |
2141 | +{ |
2142 | +struct MockProcessController : public ProcessController |
2143 | +{ |
2144 | + MockProcessController(const QSharedPointer<ProcessController::OomController>& oomController) |
2145 | + : m_oomController(oomController) |
2146 | + { |
2147 | + } |
2148 | + |
2149 | + const QSharedPointer<OomController>& oomController() const |
2150 | + { |
2151 | + return m_oomController; |
2152 | + } |
2153 | + |
2154 | + MOCK_CONST_METHOD1(sigStopProcessGroupForPid, bool(pid_t)); |
2155 | + MOCK_CONST_METHOD1(sigContinueProcessGroupForPid, bool(pid_t)); |
2156 | + |
2157 | + QSharedPointer<ProcessController::OomController> m_oomController; |
2158 | +}; |
2159 | +} |
2160 | + |
2161 | +#endif // MOCK_PROCESS_CONTROLLER_H |
2162 | |
2163 | === added file 'tests/taskcontroller_test.cpp' |
2164 | --- tests/taskcontroller_test.cpp 1970-01-01 00:00:00 +0000 |
2165 | +++ tests/taskcontroller_test.cpp 2014-02-11 09:48:32 +0000 |
2166 | @@ -0,0 +1,272 @@ |
2167 | +/* |
2168 | + * Copyright (C) 2013 Canonical, Ltd. |
2169 | + * |
2170 | + * This program is free software: you can redistribute it and/or modify it under |
2171 | + * the terms of the GNU Lesser General Public License version 3, as published by |
2172 | + * the Free Software Foundation. |
2173 | + * |
2174 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
2175 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
2176 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
2177 | + * Lesser General Public License for more details. |
2178 | + * |
2179 | + * You should have received a copy of the GNU Lesser General Public License |
2180 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2181 | + * |
2182 | + */ |
2183 | + |
2184 | +#include <Unity/Application/taskcontroller.h> |
2185 | + |
2186 | +#include "mock_oom_controller.h" |
2187 | +#include "mock_process_controller.h" |
2188 | + |
2189 | +#include <core/posix/fork.h> |
2190 | +#include <core/posix/linux/proc/process/oom_score_adj.h> |
2191 | + |
2192 | +#include <gmock/gmock.h> |
2193 | +#include <gtest/gtest.h> |
2194 | + |
2195 | +namespace |
2196 | +{ |
2197 | + |
2198 | +struct TriggerableApplicationController : public ApplicationController |
2199 | +{ |
2200 | + void triggerApplicationStarted(const QString& appId) |
2201 | + { |
2202 | + Q_EMIT applicationStarted(appId); |
2203 | + } |
2204 | + |
2205 | + void triggerApplicationStopped(const QString& appId) |
2206 | + { |
2207 | + Q_EMIT applicationStopped(appId); |
2208 | + } |
2209 | + |
2210 | + void triggerApplicationFocusRequest(const QString& appId) |
2211 | + { |
2212 | + Q_EMIT applicationFocusRequest(appId); |
2213 | + } |
2214 | + |
2215 | + MOCK_METHOD1(primaryPidForAppId, pid_t (const QString&)); |
2216 | + MOCK_METHOD2(appIdHasProcessId, bool(pid_t, const QString&)); |
2217 | + MOCK_METHOD1(stopApplicationWithAppId, bool(const QString&)); |
2218 | + MOCK_METHOD2(startApplicationWithAppIdAndArgs, bool(const QString&, const QStringList&)); |
2219 | +}; |
2220 | +} |
2221 | + |
2222 | +TEST(TaskController, startingAnApplicationCallsCorrectlyIntoApplicationController) |
2223 | +{ |
2224 | + using namespace ::testing; |
2225 | + |
2226 | + const QString appId{"com.canonical.does.not.exist"}; |
2227 | + |
2228 | + testing::NiceMock<TriggerableApplicationController> appController; |
2229 | + QSharedPointer<TriggerableApplicationController> appControllerPtr( |
2230 | + &appController, |
2231 | + [](ApplicationController*){}); |
2232 | + |
2233 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, QStringList())).Times(1).WillRepeatedly(Return(true)); |
2234 | + |
2235 | + testing::NiceMock<MockOomController> oomController; |
2236 | + QSharedPointer<ProcessController::OomController> oomControllerPtr( |
2237 | + &oomController, |
2238 | + [](ProcessController::OomController*){}); |
2239 | + |
2240 | + NiceMock<testing::MockProcessController> processController(oomControllerPtr); |
2241 | + QSharedPointer<ProcessController> processControllerPtr( |
2242 | + &processController, |
2243 | + [](ProcessController*){}); |
2244 | + |
2245 | + TaskController taskController(nullptr, |
2246 | + appControllerPtr, |
2247 | + processControllerPtr); |
2248 | + |
2249 | + taskController.start(appId, QStringList()); |
2250 | +} |
2251 | + |
2252 | +TEST(TaskController, stoppingAnApplicationCallsCorrectlyIntoApplicationController) |
2253 | +{ |
2254 | + using namespace ::testing; |
2255 | + |
2256 | + const QString appId{"com.canonical.does.not.exist"}; |
2257 | + |
2258 | + testing::NiceMock<TriggerableApplicationController> appController; |
2259 | + QSharedPointer<TriggerableApplicationController> appControllerPtr( |
2260 | + &appController, |
2261 | + [](ApplicationController*){}); |
2262 | + |
2263 | + EXPECT_CALL(appController, stopApplicationWithAppId(appId)).Times(1).WillRepeatedly(Return(true)); |
2264 | + |
2265 | + testing::NiceMock<MockOomController> oomController; |
2266 | + QSharedPointer<ProcessController::OomController> oomControllerPtr( |
2267 | + &oomController, |
2268 | + [](ProcessController::OomController*){}); |
2269 | + |
2270 | + NiceMock<testing::MockProcessController> processController(oomControllerPtr); |
2271 | + QSharedPointer<ProcessController> processControllerPtr( |
2272 | + &processController, |
2273 | + [](ProcessController*){}); |
2274 | + |
2275 | + TaskController taskController(nullptr, |
2276 | + appControllerPtr, |
2277 | + processControllerPtr); |
2278 | + |
2279 | + taskController.stop(appId); |
2280 | +} |
2281 | + |
2282 | +TEST(TaskController, suspendingAnApplicationAdjustsOomScoreForCorrectPid) |
2283 | +{ |
2284 | + using namespace ::testing; |
2285 | + |
2286 | + const QString appId{"com.canonical.does.not.exist"}; |
2287 | + |
2288 | + testing::NiceMock<TriggerableApplicationController> appController; |
2289 | + QSharedPointer<TriggerableApplicationController> appControllerPtr( |
2290 | + &appController, |
2291 | + [](ApplicationController*){}); |
2292 | + |
2293 | + EXPECT_CALL(appController, primaryPidForAppId(appId)).Times(1).WillRepeatedly(Return(-1)); |
2294 | + |
2295 | + testing::NiceMock<MockOomController> oomController; |
2296 | + QSharedPointer<ProcessController::OomController> oomControllerPtr( |
2297 | + &oomController, |
2298 | + [](ProcessController::OomController*){}); |
2299 | + |
2300 | + NiceMock<testing::MockProcessController> processController(oomControllerPtr); |
2301 | + QSharedPointer<ProcessController> processControllerPtr( |
2302 | + &processController, |
2303 | + [](ProcessController*){}); |
2304 | + |
2305 | + EXPECT_CALL(oomController, ensureProcessLikelyToBeKilled(-1)).Times(1); |
2306 | + |
2307 | + TaskController taskController(nullptr, |
2308 | + appControllerPtr, |
2309 | + processControllerPtr); |
2310 | + |
2311 | + taskController.suspend(appId); |
2312 | +} |
2313 | + |
2314 | +TEST(TaskController, resumingAnApplicationAdjustsOomScoreForCorrectPid) |
2315 | +{ |
2316 | + using namespace ::testing; |
2317 | + |
2318 | + const QString appId{"com.canonical.does.not.exist"}; |
2319 | + |
2320 | + testing::NiceMock<TriggerableApplicationController> appController; |
2321 | + QSharedPointer<TriggerableApplicationController> appControllerPtr( |
2322 | + &appController, |
2323 | + [](ApplicationController*){}); |
2324 | + |
2325 | + EXPECT_CALL(appController, primaryPidForAppId(appId)).Times(1).WillRepeatedly(Return(-1)); |
2326 | + |
2327 | + testing::NiceMock<MockOomController> oomController; |
2328 | + QSharedPointer<ProcessController::OomController> oomControllerPtr( |
2329 | + &oomController, |
2330 | + [](ProcessController::OomController*){}); |
2331 | + |
2332 | + NiceMock<testing::MockProcessController> processController(oomControllerPtr); |
2333 | + QSharedPointer<ProcessController> processControllerPtr( |
2334 | + &processController, |
2335 | + [](ProcessController*){}); |
2336 | + |
2337 | + EXPECT_CALL(oomController, ensureProcessUnlikelyToBeKilled(-1)).Times(1); |
2338 | + |
2339 | + TaskController taskController(nullptr, |
2340 | + appControllerPtr, |
2341 | + processControllerPtr); |
2342 | + |
2343 | + taskController.resume(appId); |
2344 | +} |
2345 | + |
2346 | +TEST(TaskController, aStartedApplicationIsOomScoreAdjusted) |
2347 | +{ |
2348 | + using namespace ::testing; |
2349 | + |
2350 | + const QString appId{"com.canonical.does.not.exist"}; |
2351 | + |
2352 | + testing::NiceMock<TriggerableApplicationController> appController; |
2353 | + QSharedPointer<TriggerableApplicationController> appControllerPtr( |
2354 | + &appController, |
2355 | + [](ApplicationController*){}); |
2356 | + EXPECT_CALL(appController, primaryPidForAppId(appId)).Times(1).WillRepeatedly(Return(42)); |
2357 | + |
2358 | + testing::NiceMock<MockOomController> oomController; |
2359 | + QSharedPointer<ProcessController::OomController> oomControllerPtr( |
2360 | + &oomController, |
2361 | + [](ProcessController::OomController*){}); |
2362 | + |
2363 | + NiceMock<testing::MockProcessController> processController(oomControllerPtr); |
2364 | + QSharedPointer<ProcessController> processControllerPtr( |
2365 | + &processController, |
2366 | + [](ProcessController*){}); |
2367 | + |
2368 | + EXPECT_CALL(oomController, ensureProcessUnlikelyToBeKilled(42)).Times(1); |
2369 | + |
2370 | + TaskController taskController(nullptr, |
2371 | + appControllerPtr, |
2372 | + processControllerPtr); |
2373 | + |
2374 | + appControllerPtr->triggerApplicationStarted(appId); |
2375 | +} |
2376 | + |
2377 | +TEST(TaskController, aFocusedApplicationIsOomScoreAdjusted) |
2378 | +{ |
2379 | + using namespace ::testing; |
2380 | + |
2381 | + const QString appId{"com.canonical.does.not.exist"}; |
2382 | + |
2383 | + testing::NiceMock<TriggerableApplicationController> appController; |
2384 | + QSharedPointer<TriggerableApplicationController> appControllerPtr( |
2385 | + &appController, |
2386 | + [](ApplicationController*){}); |
2387 | + EXPECT_CALL(appController, primaryPidForAppId(appId)).Times(1).WillRepeatedly(Return(42)); |
2388 | + |
2389 | + testing::NiceMock<MockOomController> oomController; |
2390 | + QSharedPointer<ProcessController::OomController> oomControllerPtr( |
2391 | + &oomController, |
2392 | + [](ProcessController::OomController*){}); |
2393 | + |
2394 | + NiceMock<testing::MockProcessController> processController(oomControllerPtr); |
2395 | + QSharedPointer<ProcessController> processControllerPtr( |
2396 | + &processController, |
2397 | + [](ProcessController*){}); |
2398 | + |
2399 | + EXPECT_CALL(oomController, ensureProcessUnlikelyToBeKilled(42)).Times(1); |
2400 | + |
2401 | + TaskController taskController(nullptr, |
2402 | + appControllerPtr, |
2403 | + processControllerPtr); |
2404 | + |
2405 | + appControllerPtr->triggerApplicationFocusRequest(appId); |
2406 | +} |
2407 | + |
2408 | +TEST(TaskController, oomControllerUpdatesOomScoreAdjCorrectly) |
2409 | +{ |
2410 | + ProcessController::OomController oomController; |
2411 | + |
2412 | + auto child = core::posix::fork([]() |
2413 | + { |
2414 | + while (true); |
2415 | + |
2416 | + return core::posix::exit::Status::success; |
2417 | + }, core::posix::StandardStream::empty); |
2418 | + |
2419 | + EXPECT_GT(child.pid(), 0); |
2420 | + |
2421 | + core::posix::linux::proc::process::OomScoreAdj oomScoreAdj; |
2422 | + child >> oomScoreAdj; |
2423 | + |
2424 | + core::posix::linux::proc::process::OomScoreAdj likelyOomScoreAdj, unlikelyOomScoreAdj; |
2425 | + { |
2426 | + oomController.ensureProcessLikelyToBeKilled(child.pid()); |
2427 | + child >> likelyOomScoreAdj; |
2428 | + |
2429 | + EXPECT_GE(likelyOomScoreAdj.value, oomScoreAdj.value); |
2430 | + } |
2431 | + |
2432 | + { |
2433 | + oomController.ensureProcessUnlikelyToBeKilled(child.pid()); |
2434 | + child >> unlikelyOomScoreAdj; |
2435 | + |
2436 | + EXPECT_LE(unlikelyOomScoreAdj.value, likelyOomScoreAdj.value); |
2437 | + } |
2438 | +} |
Missing debian/control entry.