Merge lp:~3v1n0/unity/dual-icon-race-fix into lp:unity

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Christopher Townsend
Approved revision: no longer in the source branch.
Merged at revision: 3564
Proposed branch: lp:~3v1n0/unity/dual-icon-race-fix
Merge into: lp:unity
Prerequisite: lp:~3v1n0/unity/source-manager-remove-all
Diff against target: 368 lines (+228/-27)
5 files modified
launcher/ApplicationLauncherIcon.cpp (+21/-24)
launcher/ApplicationLauncherIcon.h (+1/-0)
launcher/LauncherIcon.cpp (+12/-1)
launcher/SoftwareCenterLauncherIcon.cpp (+0/-1)
tests/test_application_launcher_icon.cpp (+194/-1)
To merge this branch: bzr merge lp:~3v1n0/unity/dual-icon-race-fix
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Christopher Townsend Approve
Review via email: mp+190363@code.launchpad.net

This proposal supersedes a proposal from 2013-10-10.

Commit message

ApplicationLauncherIcon: don't unset the app if the icon has been already removed

In this case the app is unset when removed, doing it twice causes the app->seen flag to
be reset and this breaks the assumtions of the LauncherController, making it to recreate
a new app for the same BamfApplication.

Description of the change

We had an issue caused by the fact that we were setting the app "seen" flag
as false, both when an icon was removed and afterwards (after some seconds
of delay) when destroyed.

Doing this was wrong especially in the case where we were destroying an icon
that was already removed and recently re-used, because we were messing with
the "seen" flag of an application that was now owned by another icon.

Some cleanup, moving stuff to LauncherIcon and a bunch of new unit tests.

To post a comment you must log in.
Revision history for this message
Christopher Townsend (townsend) wrote :

LGTM. Nice tests:)

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) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'launcher/ApplicationLauncherIcon.cpp'
2--- launcher/ApplicationLauncherIcon.cpp 2013-10-09 02:31:52 +0000
3+++ launcher/ApplicationLauncherIcon.cpp 2013-10-10 13:14:39 +0000
4@@ -39,8 +39,6 @@
5 #include <glib/gi18n-lib.h>
6 #include <gio/gdesktopappinfo.h>
7
8-#include <libbamf/bamf-tab.h>
9-
10 namespace unity
11 {
12 namespace launcher
13@@ -97,11 +95,7 @@
14
15 ApplicationLauncherIcon::~ApplicationLauncherIcon()
16 {
17- if (app_)
18- {
19- app_->sticky = false;
20- app_->seen = false;
21- }
22+ UnsetApplication();
23 }
24
25 ApplicationPtr ApplicationLauncherIcon::GetApplication() const
26@@ -120,12 +114,8 @@
27 return;
28 }
29
30- if (app_)
31- {
32- app_->sticky = false;
33- app_->seen = false;
34- signals_conn_.Clear();
35- }
36+ bool was_sticky = IsSticky();
37+ UnsetApplication();
38
39 app_ = app;
40 app_->seen = true;
41@@ -140,10 +130,26 @@
42 app_->desktop_file.changed.emit(app_->desktop_file());
43
44 // Make sure we set the LauncherIcon stick bit too...
45- if (app_->sticky())
46+ if (app_->sticky() || was_sticky)
47 Stick(false); // don't emit the signal
48 }
49
50+void ApplicationLauncherIcon::UnsetApplication()
51+{
52+ if (!app_ || removed())
53+ return;
54+
55+ /* Removing the unity-seen flag to the wrapped bamf application, on remove
56+ * request we make sure that if the application is re-opened while the
57+ * removal process is still ongoing, the application will be shown on the
58+ * launcher. Disconnecting from signals we make sure that this icon won't be
59+ * updated or will change visibility (no duplicated icon). */
60+
61+ signals_conn_.Clear();
62+ app_->sticky = false;
63+ app_->seen = false;
64+}
65+
66 void ApplicationLauncherIcon::SetupApplicationSignalsConnections()
67 {
68 // Lambda functions should be fine here because when the application the icon
69@@ -242,16 +248,7 @@
70 void ApplicationLauncherIcon::Remove()
71 {
72 LogUnityEvent(ApplicationEventType::LEAVE);
73- /* Removing the unity-seen flag to the wrapped bamf application, on remove
74- * request we make sure that if the application is re-opened while the
75- * removal process is still ongoing, the application will be shown on the
76- * launcher. Disconnecting from signals we make sure that this icon won't be
77- * reused (no duplicated icon). */
78- app_->seen = false;
79- app_->sticky = false;
80- // Disconnect all our callbacks.
81- notify_callbacks(); // This is from sigc++::trackable
82- signals_conn_.Clear();
83+ UnsetApplication();
84 SimpleLauncherIcon::Remove();
85 }
86
87
88=== modified file 'launcher/ApplicationLauncherIcon.h'
89--- launcher/ApplicationLauncherIcon.h 2013-10-07 18:35:17 +0000
90+++ launcher/ApplicationLauncherIcon.h 2013-10-10 13:14:39 +0000
91@@ -113,6 +113,7 @@
92 ON_ALL_MONITORS = (1 << 3),
93 };
94
95+ void UnsetApplication();
96 void SetupApplicationSignalsConnections();
97 void EnsureWindowState();
98 void EnsureMenuItemsWindowsReady();
99
100=== modified file 'launcher/LauncherIcon.cpp'
101--- launcher/LauncherIcon.cpp 2013-10-07 23:30:27 +0000
102+++ launcher/LauncherIcon.cpp 2013-10-10 13:14:39 +0000
103@@ -645,7 +645,9 @@
104 _source_manager.AddTimeout(500, [this] {
105 if (!std::equal(_center.begin(), _center.end(), _last_stable.begin()))
106 {
107- OnCenterStabilized(_center);
108+ if (!removed())
109+ OnCenterStabilized(_center);
110+
111 _last_stable = _center;
112 }
113
114@@ -747,8 +749,17 @@
115 if (_quicklist && _quicklist->IsVisible())
116 _quicklist->Hide();
117
118+ if (_tooltip && _tooltip->IsVisible())
119+ _tooltip->Hide();
120+
121 SetQuirk(Quirk::VISIBLE, false);
122 EmitRemove();
123+
124+ // Disconnect all the callbacks that may interact with the icon data
125+ _source_manager.RemoveAll();
126+ sigc::trackable::notify_callbacks();
127+
128+ removed = true;
129 }
130
131 void
132
133=== modified file 'launcher/SoftwareCenterLauncherIcon.cpp'
134--- launcher/SoftwareCenterLauncherIcon.cpp 2013-10-09 02:32:30 +0000
135+++ launcher/SoftwareCenterLauncherIcon.cpp 2013-10-10 13:14:39 +0000
136@@ -220,7 +220,6 @@
137 // exchange the temp Application with the real one
138 auto& app_manager = ApplicationManager::Default();
139 auto const& new_app = app_manager.GetApplicationForDesktopFile(new_desktop_path);
140- if (new_app) new_app->sticky = IsSticky();
141 SetApplication(new_app);
142
143 if (new_app)
144
145=== modified file 'tests/test_application_launcher_icon.cpp'
146--- tests/test_application_launcher_icon.cpp 2013-10-03 13:34:26 +0000
147+++ tests/test_application_launcher_icon.cpp 2013-10-10 13:14:39 +0000
148@@ -70,6 +70,8 @@
149 using ApplicationLauncherIcon::IsFileManager;
150 using ApplicationLauncherIcon::LogUnityEvent;
151 using ApplicationLauncherIcon::Remove;
152+ using ApplicationLauncherIcon::SetApplication;
153+ using ApplicationLauncherIcon::GetApplication;
154 };
155
156 MATCHER_P(AreArgsEqual, a, "")
157@@ -149,6 +151,21 @@
158 return HasMenuItemWithLabel(icon->Menus(), label);
159 }
160
161+ void VerifySignalsDisconnection(MockApplication::Ptr const& app)
162+ {
163+ EXPECT_TRUE(app->closed.empty());
164+ EXPECT_TRUE(app->window_opened.empty());
165+ EXPECT_TRUE(app->window_moved.empty());
166+ EXPECT_TRUE(app->window_closed.empty());
167+ EXPECT_TRUE(app->visible.changed.empty());
168+ EXPECT_TRUE(app->active.changed.empty());
169+ EXPECT_TRUE(app->running.changed.empty());
170+ EXPECT_TRUE(app->urgent.changed.empty());
171+ EXPECT_TRUE(app->desktop_file.changed.empty());
172+ EXPECT_TRUE(app->title.changed.empty());
173+ EXPECT_TRUE(app->icon.changed.empty());
174+ }
175+
176 StandaloneWindowManager* WM;
177 MockApplication::Ptr usc_app;
178 MockApplication::Ptr empty_app;
179@@ -166,7 +183,7 @@
180 EXPECT_FALSE(app->closed.empty());
181 }
182
183- EXPECT_TRUE(app->closed.empty());
184+ VerifySignalsDisconnection(app);
185 }
186
187 TEST_F(TestApplicationLauncherIcon, Position)
188@@ -1129,4 +1146,180 @@
189 EXPECT_TRUE(closes_overlay);
190 }
191
192+TEST_F(TestApplicationLauncherIcon, RemovedPropertyOnRemove)
193+{
194+ ASSERT_FALSE(mock_icon->removed);
195+ mock_icon->Remove();
196+ EXPECT_TRUE(mock_icon->removed);
197+}
198+
199+TEST_F(TestApplicationLauncherIcon, RemoveDisconnectsSignals)
200+{
201+ ASSERT_FALSE(mock_app->closed.empty());
202+ mock_icon->Remove();
203+ VerifySignalsDisconnection(mock_app);
204+}
205+
206+TEST_F(TestApplicationLauncherIcon, RemoveUnsetsAppParameters)
207+{
208+ usc_icon->Stick();
209+ ASSERT_TRUE(usc_app->seen);
210+ ASSERT_TRUE(usc_app->sticky);
211+
212+ usc_icon->Remove();
213+ EXPECT_FALSE(usc_app->seen);
214+ EXPECT_FALSE(usc_app->sticky);
215+}
216+
217+TEST_F(TestApplicationLauncherIcon, DestructionUnsetsAppParameters)
218+{
219+ usc_icon->Stick();
220+ ASSERT_TRUE(usc_app->seen);
221+ ASSERT_TRUE(usc_app->sticky);
222+
223+ usc_icon = nullptr;
224+ EXPECT_FALSE(usc_app->seen);
225+ EXPECT_FALSE(usc_app->sticky);
226+}
227+
228+TEST_F(TestApplicationLauncherIcon, DestructionDontUnsetsAppSeenIfRemoved)
229+{
230+ ASSERT_TRUE(mock_app->seen);
231+
232+ mock_icon->Remove();
233+ ASSERT_FALSE(mock_app->seen);
234+
235+ mock_app->seen = true;
236+ mock_icon = nullptr;
237+
238+ EXPECT_TRUE(mock_app->seen);
239+}
240+
241+TEST_F(TestApplicationLauncherIcon, DestructionDontUnsetsAppSeenIfReplaced)
242+{
243+ ASSERT_TRUE(mock_app->seen);
244+
245+ mock_icon->Remove();
246+ ASSERT_FALSE(mock_app->seen);
247+
248+ MockApplicationLauncherIcon::Ptr new_icon(new NiceMock<MockApplicationLauncherIcon>(mock_app));
249+ mock_icon = nullptr;
250+
251+ EXPECT_TRUE(mock_app->seen);
252+}
253+
254+TEST_F(TestApplicationLauncherIcon, SetApplicationEqual)
255+{
256+ ASSERT_TRUE(usc_app->seen);
257+ usc_icon->SetApplication(usc_app);
258+ EXPECT_EQ(usc_app, usc_icon->GetApplication());
259+ EXPECT_TRUE(usc_app->seen);
260+ EXPECT_FALSE(usc_icon->removed);
261+}
262+
263+TEST_F(TestApplicationLauncherIcon, SetApplicationNull)
264+{
265+ usc_icon->Stick();
266+ ASSERT_TRUE(usc_app->seen);
267+ ASSERT_TRUE(usc_app->sticky);
268+
269+ usc_icon->SetApplication(nullptr);
270+
271+ EXPECT_EQ(usc_app, usc_icon->GetApplication());
272+ EXPECT_FALSE(usc_app->seen);
273+ EXPECT_FALSE(usc_app->sticky);
274+ EXPECT_TRUE(usc_icon->removed);
275+ VerifySignalsDisconnection(usc_app);
276+}
277+
278+TEST_F(TestApplicationLauncherIcon, SetApplicationNewUpdatesApp)
279+{
280+ usc_icon->Stick();
281+ ASSERT_TRUE(usc_app->seen);
282+ ASSERT_TRUE(usc_app->sticky);
283+ ASSERT_TRUE(usc_icon->IsSticky());
284+
285+ auto new_app = std::make_shared<MockApplication::Nice>(UM_DESKTOP);
286+ ASSERT_FALSE(new_app->seen);
287+ ASSERT_FALSE(new_app->sticky);
288+
289+ usc_icon->SetApplication(new_app);
290+
291+ EXPECT_EQ(new_app, usc_icon->GetApplication());
292+ EXPECT_FALSE(usc_app->seen);
293+ EXPECT_FALSE(usc_app->sticky);
294+ EXPECT_FALSE(usc_icon->removed);
295+ VerifySignalsDisconnection(usc_app);
296+
297+ EXPECT_TRUE(new_app->seen);
298+ EXPECT_TRUE(new_app->sticky);
299+ EXPECT_TRUE(usc_icon->IsSticky());
300+}
301+
302+TEST_F(TestApplicationLauncherIcon, SetApplicationNewUpdatesFlags)
303+{
304+ auto new_app = std::make_shared<MockApplication::Nice>(UM_DESKTOP, "um_icon", "um_title");
305+ new_app->active_ = true;
306+ new_app->visible_ = true;
307+ new_app->running_ = true;
308+
309+ // This is needed to really make the new_app active
310+ auto win = std::make_shared<MockApplicationWindow::Nice>(g_random_int());
311+ new_app->windows_ = { win };
312+ WM->AddStandaloneWindow(std::make_shared<StandaloneWindow>(win->window_id()));
313+
314+ ASSERT_NE(usc_app->title(), new_app->title());
315+ ASSERT_NE(usc_app->icon(), new_app->icon());
316+ ASSERT_NE(usc_app->visible(), new_app->visible());
317+ ASSERT_NE(usc_app->active(), new_app->active());
318+ ASSERT_NE(usc_app->running(), new_app->running());
319+ ASSERT_NE(usc_app->desktop_file(), new_app->desktop_file());
320+
321+ usc_icon->SetApplication(new_app);
322+
323+ EXPECT_EQ(new_app->title(), usc_icon->tooltip_text());
324+ EXPECT_EQ(new_app->icon(), usc_icon->icon_name());
325+ EXPECT_EQ(new_app->visible(), usc_icon->IsVisible());
326+ EXPECT_EQ(new_app->active(), usc_icon->IsActive());
327+ EXPECT_EQ(new_app->running(), usc_icon->IsRunning());
328+ EXPECT_EQ(new_app->desktop_file(), usc_icon->DesktopFile());
329+}
330+
331+TEST_F(TestApplicationLauncherIcon, SetApplicationEmitsChangedSignalsInOrder)
332+{
333+ auto new_app = std::make_shared<MockApplication::Nice>(UM_DESKTOP, "um_icon", "um_title");
334+ new_app->active_ = true;
335+ new_app->visible_ = true;
336+ new_app->running_ = true;
337+
338+ struct SignalsHandler
339+ {
340+ MOCK_METHOD1(TitleUpdated, void(std::string const&));
341+ MOCK_METHOD1(IconUpdated, void(std::string const&));
342+ MOCK_METHOD1(DesktopUpdated, void(std::string const&));
343+ MOCK_METHOD1(ActiveUpdated, void(bool));
344+ MOCK_METHOD1(VisibleUpdated, void(bool));
345+ MOCK_METHOD1(RunningUpdated, void(bool));
346+ };
347+
348+ SignalsHandler handler;
349+ new_app->title.changed.connect(sigc::mem_fun(handler, &SignalsHandler::TitleUpdated));
350+ new_app->icon.changed.connect(sigc::mem_fun(handler, &SignalsHandler::IconUpdated));
351+ new_app->visible.changed.connect(sigc::mem_fun(handler, &SignalsHandler::VisibleUpdated));
352+ new_app->active.changed.connect(sigc::mem_fun(handler, &SignalsHandler::ActiveUpdated));
353+ new_app->running.changed.connect(sigc::mem_fun(handler, &SignalsHandler::RunningUpdated));
354+ new_app->desktop_file.changed.connect(sigc::mem_fun(handler, &SignalsHandler::DesktopUpdated));
355+
356+ // The desktop file must be updated as last item!
357+ ExpectationSet unordered_calls;
358+ unordered_calls += EXPECT_CALL(handler, TitleUpdated(new_app->title()));
359+ unordered_calls += EXPECT_CALL(handler, IconUpdated(new_app->icon()));
360+ unordered_calls += EXPECT_CALL(handler, VisibleUpdated(new_app->visible()));
361+ unordered_calls += EXPECT_CALL(handler, ActiveUpdated(new_app->active()));
362+ unordered_calls += EXPECT_CALL(handler, RunningUpdated(new_app->running()));
363+ EXPECT_CALL(handler, DesktopUpdated(new_app->desktop_file())).After(unordered_calls);
364+
365+ usc_icon->SetApplication(new_app);
366+}
367+
368 } // anonymous namespace