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

Proposed by Marco Trevisan (Treviño)
Status: Superseded
Proposed branch: lp:~3v1n0/unity/dual-icon-race-fix
Merge into: lp:unity
Diff against target: 450 lines (+256/-34)
8 files modified
UnityCore/GLibSource.cpp (+7/-4)
UnityCore/GLibSource.h (+1/-0)
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)
tests/test_glib_source.cpp (+20/-3)
To merge this branch: bzr merge lp:~3v1n0/unity/dual-icon-race-fix
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+190360@code.launchpad.net

This proposal has been superseded by 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.

Preview Diff

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