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
=== modified file 'launcher/ApplicationLauncherIcon.cpp'
--- launcher/ApplicationLauncherIcon.cpp 2013-10-09 02:31:52 +0000
+++ launcher/ApplicationLauncherIcon.cpp 2013-10-10 13:14:39 +0000
@@ -39,8 +39,6 @@
39#include <glib/gi18n-lib.h>39#include <glib/gi18n-lib.h>
40#include <gio/gdesktopappinfo.h>40#include <gio/gdesktopappinfo.h>
4141
42#include <libbamf/bamf-tab.h>
43
44namespace unity42namespace unity
45{43{
46namespace launcher44namespace launcher
@@ -97,11 +95,7 @@
9795
98ApplicationLauncherIcon::~ApplicationLauncherIcon()96ApplicationLauncherIcon::~ApplicationLauncherIcon()
99{97{
100 if (app_)98 UnsetApplication();
101 {
102 app_->sticky = false;
103 app_->seen = false;
104 }
105}99}
106100
107ApplicationPtr ApplicationLauncherIcon::GetApplication() const101ApplicationPtr ApplicationLauncherIcon::GetApplication() const
@@ -120,12 +114,8 @@
120 return;114 return;
121 }115 }
122116
123 if (app_)117 bool was_sticky = IsSticky();
124 {118 UnsetApplication();
125 app_->sticky = false;
126 app_->seen = false;
127 signals_conn_.Clear();
128 }
129119
130 app_ = app;120 app_ = app;
131 app_->seen = true;121 app_->seen = true;
@@ -140,10 +130,26 @@
140 app_->desktop_file.changed.emit(app_->desktop_file());130 app_->desktop_file.changed.emit(app_->desktop_file());
141131
142 // Make sure we set the LauncherIcon stick bit too...132 // Make sure we set the LauncherIcon stick bit too...
143 if (app_->sticky())133 if (app_->sticky() || was_sticky)
144 Stick(false); // don't emit the signal134 Stick(false); // don't emit the signal
145}135}
146136
137void ApplicationLauncherIcon::UnsetApplication()
138{
139 if (!app_ || removed())
140 return;
141
142 /* Removing the unity-seen flag to the wrapped bamf application, on remove
143 * request we make sure that if the application is re-opened while the
144 * removal process is still ongoing, the application will be shown on the
145 * launcher. Disconnecting from signals we make sure that this icon won't be
146 * updated or will change visibility (no duplicated icon). */
147
148 signals_conn_.Clear();
149 app_->sticky = false;
150 app_->seen = false;
151}
152
147void ApplicationLauncherIcon::SetupApplicationSignalsConnections()153void ApplicationLauncherIcon::SetupApplicationSignalsConnections()
148{154{
149 // Lambda functions should be fine here because when the application the icon155 // Lambda functions should be fine here because when the application the icon
@@ -242,16 +248,7 @@
242void ApplicationLauncherIcon::Remove()248void ApplicationLauncherIcon::Remove()
243{249{
244 LogUnityEvent(ApplicationEventType::LEAVE);250 LogUnityEvent(ApplicationEventType::LEAVE);
245 /* Removing the unity-seen flag to the wrapped bamf application, on remove251 UnsetApplication();
246 * request we make sure that if the application is re-opened while the
247 * removal process is still ongoing, the application will be shown on the
248 * launcher. Disconnecting from signals we make sure that this icon won't be
249 * reused (no duplicated icon). */
250 app_->seen = false;
251 app_->sticky = false;
252 // Disconnect all our callbacks.
253 notify_callbacks(); // This is from sigc++::trackable
254 signals_conn_.Clear();
255 SimpleLauncherIcon::Remove();252 SimpleLauncherIcon::Remove();
256}253}
257254
258255
=== modified file 'launcher/ApplicationLauncherIcon.h'
--- launcher/ApplicationLauncherIcon.h 2013-10-07 18:35:17 +0000
+++ launcher/ApplicationLauncherIcon.h 2013-10-10 13:14:39 +0000
@@ -113,6 +113,7 @@
113 ON_ALL_MONITORS = (1 << 3),113 ON_ALL_MONITORS = (1 << 3),
114 };114 };
115115
116 void UnsetApplication();
116 void SetupApplicationSignalsConnections();117 void SetupApplicationSignalsConnections();
117 void EnsureWindowState();118 void EnsureWindowState();
118 void EnsureMenuItemsWindowsReady();119 void EnsureMenuItemsWindowsReady();
119120
=== modified file 'launcher/LauncherIcon.cpp'
--- launcher/LauncherIcon.cpp 2013-10-07 23:30:27 +0000
+++ launcher/LauncherIcon.cpp 2013-10-10 13:14:39 +0000
@@ -645,7 +645,9 @@
645 _source_manager.AddTimeout(500, [this] {645 _source_manager.AddTimeout(500, [this] {
646 if (!std::equal(_center.begin(), _center.end(), _last_stable.begin()))646 if (!std::equal(_center.begin(), _center.end(), _last_stable.begin()))
647 {647 {
648 OnCenterStabilized(_center);648 if (!removed())
649 OnCenterStabilized(_center);
650
649 _last_stable = _center;651 _last_stable = _center;
650 }652 }
651653
@@ -747,8 +749,17 @@
747 if (_quicklist && _quicklist->IsVisible())749 if (_quicklist && _quicklist->IsVisible())
748 _quicklist->Hide();750 _quicklist->Hide();
749751
752 if (_tooltip && _tooltip->IsVisible())
753 _tooltip->Hide();
754
750 SetQuirk(Quirk::VISIBLE, false);755 SetQuirk(Quirk::VISIBLE, false);
751 EmitRemove();756 EmitRemove();
757
758 // Disconnect all the callbacks that may interact with the icon data
759 _source_manager.RemoveAll();
760 sigc::trackable::notify_callbacks();
761
762 removed = true;
752}763}
753764
754void765void
755766
=== modified file 'launcher/SoftwareCenterLauncherIcon.cpp'
--- launcher/SoftwareCenterLauncherIcon.cpp 2013-10-09 02:32:30 +0000
+++ launcher/SoftwareCenterLauncherIcon.cpp 2013-10-10 13:14:39 +0000
@@ -220,7 +220,6 @@
220 // exchange the temp Application with the real one220 // exchange the temp Application with the real one
221 auto& app_manager = ApplicationManager::Default();221 auto& app_manager = ApplicationManager::Default();
222 auto const& new_app = app_manager.GetApplicationForDesktopFile(new_desktop_path);222 auto const& new_app = app_manager.GetApplicationForDesktopFile(new_desktop_path);
223 if (new_app) new_app->sticky = IsSticky();
224 SetApplication(new_app);223 SetApplication(new_app);
225224
226 if (new_app)225 if (new_app)
227226
=== modified file 'tests/test_application_launcher_icon.cpp'
--- tests/test_application_launcher_icon.cpp 2013-10-03 13:34:26 +0000
+++ tests/test_application_launcher_icon.cpp 2013-10-10 13:14:39 +0000
@@ -70,6 +70,8 @@
70 using ApplicationLauncherIcon::IsFileManager;70 using ApplicationLauncherIcon::IsFileManager;
71 using ApplicationLauncherIcon::LogUnityEvent;71 using ApplicationLauncherIcon::LogUnityEvent;
72 using ApplicationLauncherIcon::Remove;72 using ApplicationLauncherIcon::Remove;
73 using ApplicationLauncherIcon::SetApplication;
74 using ApplicationLauncherIcon::GetApplication;
73};75};
7476
75MATCHER_P(AreArgsEqual, a, "")77MATCHER_P(AreArgsEqual, a, "")
@@ -149,6 +151,21 @@
149 return HasMenuItemWithLabel(icon->Menus(), label);151 return HasMenuItemWithLabel(icon->Menus(), label);
150 }152 }
151153
154 void VerifySignalsDisconnection(MockApplication::Ptr const& app)
155 {
156 EXPECT_TRUE(app->closed.empty());
157 EXPECT_TRUE(app->window_opened.empty());
158 EXPECT_TRUE(app->window_moved.empty());
159 EXPECT_TRUE(app->window_closed.empty());
160 EXPECT_TRUE(app->visible.changed.empty());
161 EXPECT_TRUE(app->active.changed.empty());
162 EXPECT_TRUE(app->running.changed.empty());
163 EXPECT_TRUE(app->urgent.changed.empty());
164 EXPECT_TRUE(app->desktop_file.changed.empty());
165 EXPECT_TRUE(app->title.changed.empty());
166 EXPECT_TRUE(app->icon.changed.empty());
167 }
168
152 StandaloneWindowManager* WM;169 StandaloneWindowManager* WM;
153 MockApplication::Ptr usc_app;170 MockApplication::Ptr usc_app;
154 MockApplication::Ptr empty_app;171 MockApplication::Ptr empty_app;
@@ -166,7 +183,7 @@
166 EXPECT_FALSE(app->closed.empty());183 EXPECT_FALSE(app->closed.empty());
167 }184 }
168185
169 EXPECT_TRUE(app->closed.empty());186 VerifySignalsDisconnection(app);
170}187}
171188
172TEST_F(TestApplicationLauncherIcon, Position)189TEST_F(TestApplicationLauncherIcon, Position)
@@ -1129,4 +1146,180 @@
1129 EXPECT_TRUE(closes_overlay);1146 EXPECT_TRUE(closes_overlay);
1130}1147}
11311148
1149TEST_F(TestApplicationLauncherIcon, RemovedPropertyOnRemove)
1150{
1151 ASSERT_FALSE(mock_icon->removed);
1152 mock_icon->Remove();
1153 EXPECT_TRUE(mock_icon->removed);
1154}
1155
1156TEST_F(TestApplicationLauncherIcon, RemoveDisconnectsSignals)
1157{
1158 ASSERT_FALSE(mock_app->closed.empty());
1159 mock_icon->Remove();
1160 VerifySignalsDisconnection(mock_app);
1161}
1162
1163TEST_F(TestApplicationLauncherIcon, RemoveUnsetsAppParameters)
1164{
1165 usc_icon->Stick();
1166 ASSERT_TRUE(usc_app->seen);
1167 ASSERT_TRUE(usc_app->sticky);
1168
1169 usc_icon->Remove();
1170 EXPECT_FALSE(usc_app->seen);
1171 EXPECT_FALSE(usc_app->sticky);
1172}
1173
1174TEST_F(TestApplicationLauncherIcon, DestructionUnsetsAppParameters)
1175{
1176 usc_icon->Stick();
1177 ASSERT_TRUE(usc_app->seen);
1178 ASSERT_TRUE(usc_app->sticky);
1179
1180 usc_icon = nullptr;
1181 EXPECT_FALSE(usc_app->seen);
1182 EXPECT_FALSE(usc_app->sticky);
1183}
1184
1185TEST_F(TestApplicationLauncherIcon, DestructionDontUnsetsAppSeenIfRemoved)
1186{
1187 ASSERT_TRUE(mock_app->seen);
1188
1189 mock_icon->Remove();
1190 ASSERT_FALSE(mock_app->seen);
1191
1192 mock_app->seen = true;
1193 mock_icon = nullptr;
1194
1195 EXPECT_TRUE(mock_app->seen);
1196}
1197
1198TEST_F(TestApplicationLauncherIcon, DestructionDontUnsetsAppSeenIfReplaced)
1199{
1200 ASSERT_TRUE(mock_app->seen);
1201
1202 mock_icon->Remove();
1203 ASSERT_FALSE(mock_app->seen);
1204
1205 MockApplicationLauncherIcon::Ptr new_icon(new NiceMock<MockApplicationLauncherIcon>(mock_app));
1206 mock_icon = nullptr;
1207
1208 EXPECT_TRUE(mock_app->seen);
1209}
1210
1211TEST_F(TestApplicationLauncherIcon, SetApplicationEqual)
1212{
1213 ASSERT_TRUE(usc_app->seen);
1214 usc_icon->SetApplication(usc_app);
1215 EXPECT_EQ(usc_app, usc_icon->GetApplication());
1216 EXPECT_TRUE(usc_app->seen);
1217 EXPECT_FALSE(usc_icon->removed);
1218}
1219
1220TEST_F(TestApplicationLauncherIcon, SetApplicationNull)
1221{
1222 usc_icon->Stick();
1223 ASSERT_TRUE(usc_app->seen);
1224 ASSERT_TRUE(usc_app->sticky);
1225
1226 usc_icon->SetApplication(nullptr);
1227
1228 EXPECT_EQ(usc_app, usc_icon->GetApplication());
1229 EXPECT_FALSE(usc_app->seen);
1230 EXPECT_FALSE(usc_app->sticky);
1231 EXPECT_TRUE(usc_icon->removed);
1232 VerifySignalsDisconnection(usc_app);
1233}
1234
1235TEST_F(TestApplicationLauncherIcon, SetApplicationNewUpdatesApp)
1236{
1237 usc_icon->Stick();
1238 ASSERT_TRUE(usc_app->seen);
1239 ASSERT_TRUE(usc_app->sticky);
1240 ASSERT_TRUE(usc_icon->IsSticky());
1241
1242 auto new_app = std::make_shared<MockApplication::Nice>(UM_DESKTOP);
1243 ASSERT_FALSE(new_app->seen);
1244 ASSERT_FALSE(new_app->sticky);
1245
1246 usc_icon->SetApplication(new_app);
1247
1248 EXPECT_EQ(new_app, usc_icon->GetApplication());
1249 EXPECT_FALSE(usc_app->seen);
1250 EXPECT_FALSE(usc_app->sticky);
1251 EXPECT_FALSE(usc_icon->removed);
1252 VerifySignalsDisconnection(usc_app);
1253
1254 EXPECT_TRUE(new_app->seen);
1255 EXPECT_TRUE(new_app->sticky);
1256 EXPECT_TRUE(usc_icon->IsSticky());
1257}
1258
1259TEST_F(TestApplicationLauncherIcon, SetApplicationNewUpdatesFlags)
1260{
1261 auto new_app = std::make_shared<MockApplication::Nice>(UM_DESKTOP, "um_icon", "um_title");
1262 new_app->active_ = true;
1263 new_app->visible_ = true;
1264 new_app->running_ = true;
1265
1266 // This is needed to really make the new_app active
1267 auto win = std::make_shared<MockApplicationWindow::Nice>(g_random_int());
1268 new_app->windows_ = { win };
1269 WM->AddStandaloneWindow(std::make_shared<StandaloneWindow>(win->window_id()));
1270
1271 ASSERT_NE(usc_app->title(), new_app->title());
1272 ASSERT_NE(usc_app->icon(), new_app->icon());
1273 ASSERT_NE(usc_app->visible(), new_app->visible());
1274 ASSERT_NE(usc_app->active(), new_app->active());
1275 ASSERT_NE(usc_app->running(), new_app->running());
1276 ASSERT_NE(usc_app->desktop_file(), new_app->desktop_file());
1277
1278 usc_icon->SetApplication(new_app);
1279
1280 EXPECT_EQ(new_app->title(), usc_icon->tooltip_text());
1281 EXPECT_EQ(new_app->icon(), usc_icon->icon_name());
1282 EXPECT_EQ(new_app->visible(), usc_icon->IsVisible());
1283 EXPECT_EQ(new_app->active(), usc_icon->IsActive());
1284 EXPECT_EQ(new_app->running(), usc_icon->IsRunning());
1285 EXPECT_EQ(new_app->desktop_file(), usc_icon->DesktopFile());
1286}
1287
1288TEST_F(TestApplicationLauncherIcon, SetApplicationEmitsChangedSignalsInOrder)
1289{
1290 auto new_app = std::make_shared<MockApplication::Nice>(UM_DESKTOP, "um_icon", "um_title");
1291 new_app->active_ = true;
1292 new_app->visible_ = true;
1293 new_app->running_ = true;
1294
1295 struct SignalsHandler
1296 {
1297 MOCK_METHOD1(TitleUpdated, void(std::string const&));
1298 MOCK_METHOD1(IconUpdated, void(std::string const&));
1299 MOCK_METHOD1(DesktopUpdated, void(std::string const&));
1300 MOCK_METHOD1(ActiveUpdated, void(bool));
1301 MOCK_METHOD1(VisibleUpdated, void(bool));
1302 MOCK_METHOD1(RunningUpdated, void(bool));
1303 };
1304
1305 SignalsHandler handler;
1306 new_app->title.changed.connect(sigc::mem_fun(handler, &SignalsHandler::TitleUpdated));
1307 new_app->icon.changed.connect(sigc::mem_fun(handler, &SignalsHandler::IconUpdated));
1308 new_app->visible.changed.connect(sigc::mem_fun(handler, &SignalsHandler::VisibleUpdated));
1309 new_app->active.changed.connect(sigc::mem_fun(handler, &SignalsHandler::ActiveUpdated));
1310 new_app->running.changed.connect(sigc::mem_fun(handler, &SignalsHandler::RunningUpdated));
1311 new_app->desktop_file.changed.connect(sigc::mem_fun(handler, &SignalsHandler::DesktopUpdated));
1312
1313 // The desktop file must be updated as last item!
1314 ExpectationSet unordered_calls;
1315 unordered_calls += EXPECT_CALL(handler, TitleUpdated(new_app->title()));
1316 unordered_calls += EXPECT_CALL(handler, IconUpdated(new_app->icon()));
1317 unordered_calls += EXPECT_CALL(handler, VisibleUpdated(new_app->visible()));
1318 unordered_calls += EXPECT_CALL(handler, ActiveUpdated(new_app->active()));
1319 unordered_calls += EXPECT_CALL(handler, RunningUpdated(new_app->running()));
1320 EXPECT_CALL(handler, DesktopUpdated(new_app->desktop_file())).After(unordered_calls);
1321
1322 usc_icon->SetApplication(new_app);
1323}
1324
1132} // anonymous namespace1325} // anonymous namespace