Merge lp:~thomas-voss/unity-mir/refactor-oom-score-adj-to-rely-on-process-cpp into lp:unity-mir

Proposed by Thomas Voß
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
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.

To post a comment you must log in.
Revision history for this message
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

Missing debian/control entry.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
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?

review: Needs Fixing
Revision history for this message
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

there's a src/modules/Unity/Application/Application.pro.THIS file taht probably has to go away.

review: Needs Fixing
Revision history for this message
Thomas Voß (thomas-voss) wrote : Posted in a previous version of this proposal

> there's a src/modules/Unity/Application/Application.pro.THIS file taht
> probably has to go away.

Good catch, fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

Which this i am getting libunity-mir.so.0.0.1 while with the master branch i get libunity-mir.so.1.0.0

I don't think this change is intentional, no?

review: Needs Fixing
Revision history for this message
Thomas Voß (thomas-voss) wrote : Posted in a previous version of this proposal

> Which this i am getting libunity-mir.so.0.0.1 while with the master branch i
> get libunity-mir.so.1.0.0
>
> I don't think this change is intentional, no?

Nope, it is not. Also fixed in prerequisite branch.

Revision history for this message
Albert Astals Cid (aacid) wrote :

Looks good to me, would prefer someone with more unity-mir knowledge to top-approve though :-)

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

First off, thank you for the tests! Great to have them. Here are my comments:

For ApplicationController, I dislike the naming, the difference in purpose between "ApplicationManager" and "ApplicationController" is now not obvious to me.

Your making separate TaskController and ApplicationController is clear to me - yes I see that ApplicationController is implementation specific (i.e. we 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 ApplicationController).

It would also help remove this

+ the_application_manager = new ApplicationManager(
+ QSharedPointer<TaskController>(
+ new TaskController(
+ nullptr,
+ QSharedPointer<ApplicationController>(
+ new upstart::ApplicationController()))),
+ QSharedPointer<DesktopFileReader::Factory>(
+ new DesktopFileReader::Factory()));
which is ugly to my eyes.

I prefer appending "Interface" or "Base" to the names of interfaces classes too.

review: Needs Fixing
Revision history for this message
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/$pid/oom_something files and read/write values? If so it's a pretty thin layer, and I could do the equivalent in a few lines of code in Qt.

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::this_process::instance() >> shellScore; int score = shellScore.value;
less readable than
+ int score = core::posix::this_process::instance().get_score();
Using >> and << operators means it's not obvious at all what's being read and written. Again, personal preference

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

+ // static TaskController* singleton();
Junk

+ void onApplicationAboutToBeStarted(QString id);
Please use "const QString" here and in the other slots

+ connect(app_controller.data(),
+ SIGNAL(applicationAboutToBeStarted(QString)),
+ this,
+ SLOT(onApplicationAboutToBeStarted(QString)));
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(app_controller.data(), &applicationAboutToBeStarted, this, &onApplicationAboutToBeStarted);

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

+ auto pid = app_controller->primaryPidForAppId(id);
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/Unity/Application/upstart/application_controller.cpp
This is a very un-Qt piece of code, using some Qt methods. Making upstart::ApplicationController::ApplicationController not be a QObject means you loose Q_EMIT and have to go down the QMetaObject::invokeMethod road, 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?

+ auto thiz = static_cast<upstart::ApplicationController*>(userData);
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.

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

+# Build with system gmock and embedded gtest
+set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
+set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
+set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory")
Can't cmake read this from the pkgconfig file?

+TEST(TaskController, startingAnApplicationCallsCorrectlyIntoApplicationController)
+ const QString appId{"com.canonical.does.not.exist"};
+ taskController.start("com.canonical.does.not.exist", QStringList());
Why not just use appId on the latter line? Is case for all tests in tests/taskcontroller_test.cpp

Revision history for this message
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.

Revision history for this message
Thomas Voß (thomas-voss) wrote :

> + auto pid = app_controller->primaryPidForAppId(id);
> 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/Unity/Application/upstart/application_controller.cpp
> This is a very un-Qt piece of code, using some Qt methods. Making
> upstart::ApplicationController::ApplicationController not be a QObject means
> you loose Q_EMIT and have to go down the QMetaObject::invokeMethod road, 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.

> + auto thiz = static_cast<upstart::ApplicationController*>(userData);
> 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.

Revision history for this message
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 ApplicationController, I dislike the naming, the difference in purpose
> between "ApplicationManager" and "ApplicationController" is now not obvious to
> me.
>
> Your making separate TaskController and ApplicationController is clear to me -
> yes I see that ApplicationController is implementation specific (i.e. we 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 ApplicationController).
>

Okay, I can see that we should fold the ApplicationController into the 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::TaskController implementation, I would propose 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?

> It would also help remove this
>
> + the_application_manager = new ApplicationManager(
> + QSharedPointer<TaskController>(
> + new TaskController(
> + nullptr,
> + QSharedPointer<ApplicationController>(
> + new upstart::ApplicationController()))),
> + QSharedPointer<DesktopFileReader::Factory>(
> + new DesktopFileReader::Factory()));
> 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

Revision history for this message
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/$pid/oom_something files and read/write values? If
> 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::this_process::instance() >> shellScore; int score =
> shellScore.value;
> less readable than
> + int score = core::posix::this_process::instance().get_score();
> 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.

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

> > For ApplicationController, I dislike the naming, the difference in purpose
> > between "ApplicationManager" and "ApplicationController" is now not obvious
> to
> > me.
> >
> > Your making separate TaskController and ApplicationController is clear to me
> -
> > yes I see that ApplicationController is implementation specific (i.e. we
> 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 ApplicationController).
> >
>
> Okay, I can see that we should fold the ApplicationController into the
> 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::TaskController implementation, I would propose 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,Application,Task}Controller. I'll maybe do a 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?

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

> > +++ src/modules/Unity/Application/upstart/application_controller.cpp
> > This is a very un-Qt piece of code, using some Qt methods. Making
> > upstart::ApplicationController::ApplicationController not be a QObject means
> > you loose Q_EMIT and have to go down the QMetaObject::invokeMethod road,
> 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::valueChanged(int _t1)
{
    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

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::invokeMethod, you are not taking advantage of this compile time check. If the signal you're invoking is removed, the code will still compile, but you'll get run-time errors only.

QMetaObject::activate is a Qt-internal equivalent to QMetaObject::invokeMethod - 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.

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

Functionally everything works ok. Please address my couple of comments above and then this will be good to go!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Thomas Voß (thomas-voss) wrote :

> > > +++ src/modules/Unity/Application/upstart/application_controller.cpp
> > > This is a very un-Qt piece of code, using some Qt methods. Making
> > > upstart::ApplicationController::ApplicationController not be a QObject
> means
> > > you loose Q_EMIT and have to go down the QMetaObject::invokeMethod road,
> > 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::valueChanged(int _t1)
> {
> void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1))
> };
> QMetaObject::activate(this, &staticMetaObject, 0, _a);
> }
>
> 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::invokeMethod, you are not taking advantage of this compile
> time check. If the signal you're invoking is removed, the code will still
> compile, but you'll get run-time errors only.
>
> QMetaObject::activate is a Qt-internal equivalent to QMetaObject::invokeMethod
> - 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::DirectConnection in our particular case iiuc.

Revision history for this message
Thomas Voß (thomas-voss) wrote :

> > > For ApplicationController, I dislike the naming, the difference in purpose
> > > between "ApplicationManager" and "ApplicationController" is now not
> obvious
> > to
> > > me.
> > >
> > > Your making separate TaskController and ApplicationController is clear to
> me
> > -
> > > yes I see that ApplicationController is implementation specific (i.e. we
> > 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 ApplicationController).
> > >
> >
> > Okay, I can see that we should fold the ApplicationController into the
> > 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::TaskController implementation, I would propose
> 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,Application,Task}Controller. I'll maybe do a
> 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.

Revision history for this message
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

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

Approved! Thanks

review: Approve
Revision history for this message
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.

Revision history for this message
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

review: Approve
Revision history for this message
Albert Astals Cid (aacid) wrote :

m_focusedApplication is not initialized to null anymore causing a crash in unity8 start when ApplicationManager::focusedApplicationId is called

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
169. By Thomas Voß

Initialize ApplicationManager::m_focusedApplication to nullptr in ctor.

Revision history for this message
Albert Astals Cid (aacid) wrote :

Looks good :-)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches