notification_processor_unittest.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/components/phonehub/notification_processor.h"
  5. #include "ash/components/phonehub/fake_notification_manager.h"
  6. #include "ash/components/phonehub/phone_model_test_util.h"
  7. #include "ash/constants/ash_features.h"
  8. #include "base/memory/ptr_util.h"
  9. #include "base/test/scoped_feature_list.h"
  10. #include "base/test/task_environment.h"
  11. #include "testing/gtest/include/gtest/gtest.h"
  12. #include "ui/gfx/image/image_skia.h"
  13. #include "ui/gfx/image/image_unittest_util.h"
  14. namespace ash {
  15. namespace phonehub {
  16. namespace {
  17. constexpr int64_t kNotificationIdA = 1;
  18. constexpr int64_t kNotificationIdB = 2;
  19. constexpr int64_t kNotificationIdC = 3;
  20. constexpr int64_t kInlineReplyIdA = 3;
  21. constexpr int64_t kInlineReplyIdB = 4;
  22. constexpr int64_t kOpenableActionId = -2;
  23. constexpr int64_t kAnswerActionId = 1;
  24. constexpr int64_t kDeclineActionId = 2;
  25. constexpr int64_t kHangupActionId = 3;
  26. const char kIconDataA[] = "icon_a";
  27. const char kIconDataB[] = "icon_b";
  28. const char kSharedImageA[] = "shared_image_a";
  29. const char kSharedImageB[] = "shared_image_b";
  30. const char kContactImageA[] = "contact_image_a";
  31. const char kContactImageB[] = "contact_image_b";
  32. // Garbage color for the purpose of verification in these tests.
  33. const SkColor kIconColor = SkColorSetRGB(0x12, 0x34, 0x56);
  34. SkBitmap TestBitmap() {
  35. SkBitmap bitmap;
  36. bitmap.allocN32Pixels(1, 1);
  37. return bitmap;
  38. }
  39. gfx::Image TestImage() {
  40. gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(TestBitmap());
  41. image_skia.MakeThreadSafe();
  42. return gfx::Image(image_skia);
  43. }
  44. } // namespace
  45. class NotificationProcessorTest : public testing::Test {
  46. public:
  47. friend class NotificationProcessor;
  48. NotificationProcessorTest() {
  49. scoped_feature_list_.InitWithFeatures(
  50. /*enabled_features=*/{features::kEcheSWA,
  51. features::kPhoneHubCallNotification},
  52. /*disabled_features=*/{});
  53. }
  54. NotificationProcessorTest(const NotificationProcessorTest&) = delete;
  55. NotificationProcessorTest& operator=(const NotificationProcessorTest&) =
  56. delete;
  57. ~NotificationProcessorTest() override = default;
  58. class FakeImageDecoderDelegate
  59. : public NotificationProcessor::ImageDecoderDelegate {
  60. public:
  61. using DecodeImageCallback = NotificationProcessor::DecodeImageCallback;
  62. FakeImageDecoderDelegate() = default;
  63. ~FakeImageDecoderDelegate() override = default;
  64. void PerformImageDecode(
  65. const std::string& data,
  66. DecodeImageCallback single_image_decoded_closure) override {
  67. decode_image_callbacks_.push(std::move(single_image_decoded_closure));
  68. }
  69. size_t NumberOfDecodeImageCallbacks() {
  70. return decode_image_callbacks_.size();
  71. }
  72. void RunNextCallback(SkBitmap bitmap) {
  73. std::move(decode_image_callbacks_.front()).Run(bitmap);
  74. decode_image_callbacks_.pop();
  75. }
  76. void RunAllCallbacks() {
  77. while (!decode_image_callbacks_.empty())
  78. RunNextCallback(TestBitmap());
  79. }
  80. void RunAllCallbacksWithEmpty() {
  81. while (!decode_image_callbacks_.empty())
  82. RunNextCallback(SkBitmap());
  83. }
  84. std::queue<DecodeImageCallback> decode_image_callbacks_;
  85. };
  86. // testing::Test:
  87. void SetUp() override {
  88. fake_notification_manager_ = std::make_unique<FakeNotificationManager>();
  89. notification_processor_ = base::WrapUnique(new NotificationProcessor(
  90. fake_notification_manager_.get(),
  91. std::make_unique<FakeImageDecoderDelegate>()));
  92. }
  93. FakeImageDecoderDelegate* image_decoder_delegate() {
  94. return static_cast<FakeImageDecoderDelegate*>(
  95. notification_processor_->delegate_.get());
  96. }
  97. NotificationProcessor* notification_processor() {
  98. return notification_processor_.get();
  99. }
  100. FakeNotificationManager* fake_notification_manager() {
  101. return fake_notification_manager_.get();
  102. }
  103. size_t NumPendingRequests() {
  104. return notification_processor()->pending_notification_requests_.size();
  105. }
  106. proto::Notification CreateNewInlineReplyableOpenableNotification(
  107. int64_t notification_id,
  108. int64_t inline_reply_id,
  109. Notification::InteractionBehavior behavior) {
  110. return CreateNewInlineReplyableNotification(
  111. notification_id, inline_reply_id,
  112. /* icon= */ std::string(),
  113. /* shared_image= */ std::string(),
  114. /* contact_image= */ std::string(), behavior);
  115. }
  116. proto::Notification CreateNewInlineReplyableNotification(
  117. int64_t notification_id,
  118. int64_t inline_reply_id,
  119. std::string icon = std::string(),
  120. std::string shared_image = std::string(),
  121. std::string contact_image = std::string(),
  122. Notification::InteractionBehavior behavior =
  123. Notification::InteractionBehavior::kNone) {
  124. auto origin_app = std::make_unique<proto::App>();
  125. origin_app->set_icon(icon);
  126. proto::Notification notification;
  127. notification.set_id(notification_id);
  128. notification.set_allocated_origin_app(origin_app.release());
  129. notification.set_contact_image(contact_image);
  130. notification.set_shared_image(shared_image);
  131. notification.set_category(
  132. proto::Notification::Category::Notification_Category_CONVERSATION);
  133. notification.add_actions();
  134. proto::Action* mutable_action = notification.mutable_actions(0);
  135. mutable_action->set_id(inline_reply_id);
  136. mutable_action->set_type(proto::Action_InputType::Action_InputType_TEXT);
  137. if (behavior == Notification::InteractionBehavior::kOpenable) {
  138. notification.add_actions();
  139. proto::Action* open_action = notification.mutable_actions(1);
  140. open_action->set_id(kOpenableActionId);
  141. open_action->set_type(proto::Action_InputType::Action_InputType_OPEN);
  142. }
  143. return notification;
  144. }
  145. proto::Notification CreateNewInlineReplyableMonochromeIconNotification(
  146. int64_t notification_id,
  147. int64_t inline_reply_id,
  148. absl::optional<SkColor> icon_color = absl::nullopt,
  149. std::string icon = std::string()) {
  150. proto::Notification notification = CreateNewInlineReplyableNotification(
  151. notification_id, inline_reply_id, icon);
  152. proto::App* origin_app = notification.mutable_origin_app();
  153. origin_app->set_icon_styling(
  154. proto::NotificationIconStyling::ICON_STYLE_MONOCHROME_SMALL_ICON);
  155. if (icon_color != absl::nullopt) {
  156. auto color_rgb = std::make_unique<proto::ColorRgb>();
  157. color_rgb->set_red(SkColorGetR(*icon_color));
  158. color_rgb->set_green(SkColorGetG(*icon_color));
  159. color_rgb->set_blue(SkColorGetB(*icon_color));
  160. origin_app->set_allocated_icon_color(color_rgb.release());
  161. }
  162. return notification;
  163. }
  164. proto::Notification CreateNonTextTypeNotification(
  165. int64_t notification_id,
  166. int64_t inline_reply_id,
  167. std::string icon = std::string()) {
  168. auto origin_app = std::make_unique<proto::App>();
  169. origin_app->set_icon(icon);
  170. proto::Notification notification;
  171. notification.set_id(notification_id);
  172. notification.set_allocated_origin_app(origin_app.release());
  173. notification.add_actions();
  174. proto::Action* open_action = notification.mutable_actions(0);
  175. open_action->set_id(kOpenableActionId);
  176. open_action->set_type(proto::Action_InputType::Action_InputType_OPEN);
  177. return notification;
  178. }
  179. proto::Notification CreateIncomingCallNotification(int64_t notification_id) {
  180. auto origin_app = std::make_unique<proto::App>();
  181. origin_app->set_icon(std::string());
  182. proto::Notification notification;
  183. notification.set_id(notification_id);
  184. notification.set_allocated_origin_app(origin_app.release());
  185. notification.set_contact_image(std::string());
  186. notification.set_shared_image(std::string());
  187. notification.set_category(
  188. proto::Notification::Category::Notification_Category_INCOMING_CALL);
  189. notification.add_actions();
  190. proto::Action* answer_action = notification.mutable_actions(0);
  191. answer_action->set_id(kAnswerActionId);
  192. answer_action->set_call_action(
  193. proto::Action_CallAction::Action_CallAction_ANSWER);
  194. notification.add_actions();
  195. proto::Action* decline_action = notification.mutable_actions(1);
  196. decline_action->set_id(kDeclineActionId);
  197. decline_action->set_call_action(
  198. proto::Action_CallAction::Action_CallAction_DECLINE);
  199. return notification;
  200. }
  201. proto::Notification CreateOngoingCallNotification(int64_t notification_id) {
  202. auto origin_app = std::make_unique<proto::App>();
  203. origin_app->set_icon(std::string());
  204. proto::Notification notification;
  205. notification.set_id(notification_id);
  206. notification.set_allocated_origin_app(origin_app.release());
  207. notification.set_contact_image(std::string());
  208. notification.set_shared_image(std::string());
  209. notification.set_category(
  210. proto::Notification::Category::Notification_Category_ONGOING_CALL);
  211. notification.add_actions();
  212. proto::Action* hangup_action = notification.mutable_actions(0);
  213. hangup_action->set_id(kHangupActionId);
  214. hangup_action->set_call_action(
  215. proto::Action_CallAction::Action_CallAction_HANGUP);
  216. notification.add_actions();
  217. proto::Action* open_action = notification.mutable_actions(1);
  218. open_action->set_id(kOpenableActionId);
  219. open_action->set_type(proto::Action_InputType::Action_InputType_OPEN);
  220. return notification;
  221. }
  222. private:
  223. std::unique_ptr<FakeNotificationManager> fake_notification_manager_;
  224. std::unique_ptr<NotificationProcessor> notification_processor_;
  225. base::test::ScopedFeatureList scoped_feature_list_;
  226. base::test::SingleThreadTaskEnvironment task_environment_;
  227. };
  228. TEST_F(NotificationProcessorTest, FailedToDecodeImage) {
  229. std::vector<proto::Notification> first_set_of_notifications;
  230. // The icon should be an empty image as the decoder failed to decode the
  231. // image.
  232. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  233. kNotificationIdA, kInlineReplyIdA, kIconDataA));
  234. notification_processor()->AddNotifications(first_set_of_notifications);
  235. image_decoder_delegate()->RunNextCallback(SkBitmap());
  236. const Notification* notification =
  237. fake_notification_manager()->GetNotification(kNotificationIdA);
  238. EXPECT_TRUE(notification->app_metadata().icon.IsEmpty());
  239. EXPECT_FALSE(notification->shared_image().has_value());
  240. EXPECT_FALSE(notification->contact_image().has_value());
  241. }
  242. TEST_F(NotificationProcessorTest, ShouldSkipDecodeImageIfNotAvailable) {
  243. std::vector<proto::Notification> first_set_of_notifications;
  244. first_set_of_notifications.emplace_back(CreateNonTextTypeNotification(
  245. kNotificationIdA, kInlineReplyIdA, kIconDataA));
  246. notification_processor()->AddNotifications(first_set_of_notifications);
  247. EXPECT_EQ(0u, image_decoder_delegate()->NumberOfDecodeImageCallbacks());
  248. image_decoder_delegate()->RunAllCallbacks();
  249. EXPECT_EQ(0u, fake_notification_manager()->num_notifications());
  250. }
  251. TEST_F(NotificationProcessorTest, MonochromeIconFieldsPopulatedCorrectly) {
  252. std::vector<proto::Notification> first_set_of_notifications;
  253. // Legacy notifications don't supply color and should not be filled in.
  254. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  255. kNotificationIdA, kInlineReplyIdA, kIconDataA));
  256. notification_processor()->AddNotifications(first_set_of_notifications);
  257. image_decoder_delegate()->RunAllCallbacks();
  258. const Notification* notification =
  259. fake_notification_manager()->GetNotification(kNotificationIdA);
  260. EXPECT_FALSE(notification->app_metadata().icon_is_monochrome);
  261. EXPECT_TRUE(gfx::test::AreImagesEqual(notification->app_metadata().icon,
  262. TestImage()));
  263. EXPECT_FALSE(notification->app_metadata().icon_color.has_value());
  264. // Monochrome notifications without a color should not have icon_color filled.
  265. first_set_of_notifications.clear();
  266. first_set_of_notifications.emplace_back(
  267. CreateNewInlineReplyableMonochromeIconNotification(
  268. kNotificationIdA, kInlineReplyIdA, absl::nullopt, kIconDataA));
  269. notification_processor()->AddNotifications(first_set_of_notifications);
  270. image_decoder_delegate()->RunAllCallbacks();
  271. notification = fake_notification_manager()->GetNotification(kNotificationIdA);
  272. EXPECT_TRUE(notification->app_metadata().icon_is_monochrome);
  273. EXPECT_TRUE(gfx::test::AreImagesEqual(notification->app_metadata().icon,
  274. TestImage()));
  275. EXPECT_FALSE(notification->app_metadata().icon_color.has_value());
  276. // Monochrome notifications with a color should have icon_color filled.
  277. first_set_of_notifications.clear();
  278. first_set_of_notifications.emplace_back(
  279. CreateNewInlineReplyableMonochromeIconNotification(
  280. kNotificationIdA, kInlineReplyIdA, kIconColor, kIconDataA));
  281. notification_processor()->AddNotifications(first_set_of_notifications);
  282. image_decoder_delegate()->RunAllCallbacks();
  283. notification = fake_notification_manager()->GetNotification(kNotificationIdA);
  284. EXPECT_TRUE(notification->app_metadata().icon_is_monochrome);
  285. EXPECT_TRUE(gfx::test::AreImagesEqual(notification->app_metadata().icon,
  286. TestImage()));
  287. EXPECT_TRUE(notification->app_metadata().icon_color.has_value());
  288. EXPECT_TRUE(*notification->app_metadata().icon_color == kIconColor);
  289. // Monochrome notifications without an icon should not fill in color.
  290. first_set_of_notifications.clear();
  291. first_set_of_notifications.emplace_back(
  292. CreateNewInlineReplyableMonochromeIconNotification(
  293. kNotificationIdA, kInlineReplyIdA, kIconColor,
  294. /*icon=*/std::string()));
  295. notification_processor()->AddNotifications(first_set_of_notifications);
  296. image_decoder_delegate()->RunAllCallbacksWithEmpty();
  297. notification = fake_notification_manager()->GetNotification(kNotificationIdA);
  298. EXPECT_TRUE(notification->app_metadata().icon_is_monochrome);
  299. EXPECT_TRUE(notification->app_metadata().icon.IsEmpty());
  300. EXPECT_FALSE(notification->app_metadata().icon_color.has_value());
  301. }
  302. TEST_F(NotificationProcessorTest, ImageFieldPopulatedCorrectly) {
  303. std::vector<proto::Notification> first_set_of_notifications;
  304. // The icon should be populated. The shared and contact image should be null.
  305. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  306. kNotificationIdA, kInlineReplyIdA, kIconDataA));
  307. notification_processor()->AddNotifications(first_set_of_notifications);
  308. image_decoder_delegate()->RunAllCallbacks();
  309. const Notification* notification =
  310. fake_notification_manager()->GetNotification(kNotificationIdA);
  311. EXPECT_TRUE(gfx::test::AreImagesEqual(notification->app_metadata().icon,
  312. TestImage()));
  313. EXPECT_FALSE(notification->shared_image().has_value());
  314. EXPECT_FALSE(notification->contact_image().has_value());
  315. // The icon and shared image should be populated. The contact image should be
  316. // null.
  317. first_set_of_notifications.clear();
  318. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  319. kNotificationIdA, kInlineReplyIdA, kIconDataA, kSharedImageA));
  320. notification_processor()->AddNotifications(first_set_of_notifications);
  321. image_decoder_delegate()->RunAllCallbacks();
  322. notification = fake_notification_manager()->GetNotification(kNotificationIdA);
  323. EXPECT_TRUE(gfx::test::AreImagesEqual(notification->app_metadata().icon,
  324. TestImage()));
  325. EXPECT_TRUE(
  326. gfx::test::AreImagesEqual(*notification->shared_image(), TestImage()));
  327. EXPECT_FALSE(notification->contact_image().has_value());
  328. // The icon and contact image should be populated. The shared image should be
  329. // null.
  330. first_set_of_notifications.clear();
  331. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  332. kNotificationIdA, kInlineReplyIdA, kIconDataA, std::string(),
  333. kContactImageA));
  334. notification_processor()->AddNotifications(first_set_of_notifications);
  335. image_decoder_delegate()->RunAllCallbacks();
  336. notification = fake_notification_manager()->GetNotification(kNotificationIdA);
  337. EXPECT_TRUE(gfx::test::AreImagesEqual(notification->app_metadata().icon,
  338. TestImage()));
  339. EXPECT_FALSE(notification->shared_image().has_value());
  340. EXPECT_TRUE(
  341. gfx::test::AreImagesEqual(*notification->contact_image(), TestImage()));
  342. // All images should be should be populated.
  343. first_set_of_notifications.clear();
  344. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  345. kNotificationIdA, kInlineReplyIdA, kIconDataA, kSharedImageA,
  346. kContactImageA));
  347. notification_processor()->AddNotifications(first_set_of_notifications);
  348. image_decoder_delegate()->RunAllCallbacks();
  349. EXPECT_TRUE(gfx::test::AreImagesEqual(notification->app_metadata().icon,
  350. TestImage()));
  351. EXPECT_TRUE(
  352. gfx::test::AreImagesEqual(*notification->shared_image(), TestImage()));
  353. EXPECT_TRUE(
  354. gfx::test::AreImagesEqual(*notification->contact_image(), TestImage()));
  355. }
  356. TEST_F(NotificationProcessorTest, AddRemoveClearWithoutRace) {
  357. // Add 2 notifications with all images populated.
  358. std::vector<proto::Notification> first_set_of_notifications;
  359. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  360. kNotificationIdA, kInlineReplyIdA, kIconDataA, kSharedImageA,
  361. kContactImageA));
  362. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  363. kNotificationIdB, kInlineReplyIdB, kIconDataB, kSharedImageB,
  364. kContactImageB));
  365. notification_processor()->AddNotifications(first_set_of_notifications);
  366. // 6 image decode callbacks will occur for kIconDataA, kSharedImageA,
  367. // kContactImageA, kIconDataB, kSharedImageB, and kContactImageB.
  368. EXPECT_EQ(6u, image_decoder_delegate()->NumberOfDecodeImageCallbacks());
  369. image_decoder_delegate()->RunAllCallbacks();
  370. EXPECT_EQ(2u, fake_notification_manager()->num_notifications());
  371. EXPECT_TRUE(fake_notification_manager()->GetNotification(kNotificationIdA));
  372. EXPECT_TRUE(fake_notification_manager()->GetNotification(kNotificationIdB));
  373. // Remove notification with id kNotificationIdA.
  374. base::flat_set<int64_t> ids_of_notifications_to_remove;
  375. ids_of_notifications_to_remove.emplace(kNotificationIdA);
  376. notification_processor()->RemoveNotifications(ids_of_notifications_to_remove);
  377. EXPECT_EQ(1u, fake_notification_manager()->num_notifications());
  378. EXPECT_FALSE(fake_notification_manager()->GetNotification(kNotificationIdA));
  379. EXPECT_TRUE(fake_notification_manager()->GetNotification(kNotificationIdB));
  380. // Clear all notifications.
  381. notification_processor()->ClearNotificationsAndPendingUpdates();
  382. EXPECT_EQ(0u, fake_notification_manager()->num_notifications());
  383. }
  384. TEST_F(NotificationProcessorTest, AddRemoveWithRace) {
  385. // Add 2 notifications.
  386. std::vector<proto::Notification> first_set_of_notifications;
  387. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  388. kNotificationIdA, kInlineReplyIdA, kIconDataA, kSharedImageA));
  389. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  390. kNotificationIdB, kInlineReplyIdB, kIconDataB));
  391. notification_processor()->AddNotifications(first_set_of_notifications);
  392. // One pending requests because |first_set_of_notifications| processing
  393. // occurred immediately.
  394. EXPECT_EQ(1u, NumPendingRequests());
  395. // Remove notification with id kNotificationIdA while
  396. // |first_set_of_notifications| is still being processed.
  397. base::flat_set<int64_t> ids_of_notifications_to_remove;
  398. ids_of_notifications_to_remove.emplace(kNotificationIdA);
  399. notification_processor()->RemoveNotifications(ids_of_notifications_to_remove);
  400. // Pending delete request, first in the queue.
  401. EXPECT_EQ(2u, NumPendingRequests());
  402. EXPECT_EQ(0u, fake_notification_manager()->num_notifications());
  403. // Add a set of notifications such that only one image needs to be decoded,
  404. // when neither the first set has completed processing more the remove request
  405. // has been fully processed.
  406. std::vector<proto::Notification> second_set_of_notifications;
  407. second_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  408. kNotificationIdA, kInlineReplyIdA, kIconDataA));
  409. notification_processor()->AddNotifications(second_set_of_notifications);
  410. // Pending add request, second in the queue.
  411. EXPECT_EQ(3u, NumPendingRequests());
  412. EXPECT_EQ(0u, fake_notification_manager()->num_notifications());
  413. // 3 image decode callbacks will occur. When the last image decode callback is
  414. // finished running, which in this case is icon2, it will cause the next
  415. // notification edit request to be executed.
  416. EXPECT_EQ(3u, image_decoder_delegate()->NumberOfDecodeImageCallbacks());
  417. EXPECT_EQ(3u, NumPendingRequests());
  418. image_decoder_delegate()->RunNextCallback(TestBitmap());
  419. EXPECT_EQ(2u, image_decoder_delegate()->NumberOfDecodeImageCallbacks());
  420. EXPECT_EQ(3u, NumPendingRequests());
  421. image_decoder_delegate()->RunNextCallback(TestBitmap());
  422. EXPECT_EQ(1u, image_decoder_delegate()->NumberOfDecodeImageCallbacks());
  423. EXPECT_EQ(3u, NumPendingRequests());
  424. // The scheduled remove callback will occur, then subsequently the add
  425. // notification with 1 image.
  426. image_decoder_delegate()->RunNextCallback(TestBitmap());
  427. EXPECT_EQ(1u, NumPendingRequests());
  428. EXPECT_EQ(1u, image_decoder_delegate()->NumberOfDecodeImageCallbacks());
  429. EXPECT_EQ(1u, fake_notification_manager()->num_notifications());
  430. EXPECT_FALSE(fake_notification_manager()->GetNotification(kNotificationIdA));
  431. EXPECT_TRUE(fake_notification_manager()->GetNotification(kNotificationIdB));
  432. // 1 image decode callback will occur.
  433. image_decoder_delegate()->RunAllCallbacks();
  434. EXPECT_EQ(0u, NumPendingRequests());
  435. EXPECT_EQ(2u, fake_notification_manager()->num_notifications());
  436. EXPECT_TRUE(fake_notification_manager()->GetNotification(kNotificationIdA));
  437. EXPECT_TRUE(fake_notification_manager()->GetNotification(kNotificationIdB));
  438. }
  439. TEST_F(NotificationProcessorTest, AddClearAllWithRace) {
  440. std::vector<proto::Notification> first_set_of_notifications;
  441. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  442. kNotificationIdA, kInlineReplyIdA, kIconDataA, kSharedImageA));
  443. first_set_of_notifications.emplace_back(CreateNewInlineReplyableNotification(
  444. kNotificationIdB, kInlineReplyIdB, kIconDataA));
  445. notification_processor()->AddNotifications(first_set_of_notifications);
  446. // Clearing Notifications will invalidate all callbacks in process and
  447. // immediately clear all pointers.
  448. notification_processor()->ClearNotificationsAndPendingUpdates();
  449. EXPECT_EQ(0u, fake_notification_manager()->num_notifications());
  450. image_decoder_delegate()->RunAllCallbacks();
  451. EXPECT_EQ(0u, NumPendingRequests());
  452. EXPECT_EQ(0u, fake_notification_manager()->num_notifications());
  453. EXPECT_FALSE(fake_notification_manager()->GetNotification(kNotificationIdA));
  454. EXPECT_FALSE(fake_notification_manager()->GetNotification(kNotificationIdB));
  455. }
  456. TEST_F(NotificationProcessorTest, InteractionBehaviorPopulatedCorrectly) {
  457. std::vector<proto::Notification> first_set_of_notifications;
  458. // The notification should be openable if a OPEN action is specified.
  459. first_set_of_notifications.emplace_back(
  460. CreateNewInlineReplyableOpenableNotification(
  461. kNotificationIdA, kInlineReplyIdA,
  462. Notification::InteractionBehavior::kOpenable));
  463. notification_processor()->AddNotifications(first_set_of_notifications);
  464. image_decoder_delegate()->RunAllCallbacks();
  465. const Notification* notification =
  466. fake_notification_manager()->GetNotification(kNotificationIdA);
  467. EXPECT_EQ(Notification::InteractionBehavior::kOpenable,
  468. notification->interaction_behavior());
  469. EXPECT_EQ(Notification::Category::kConversation, notification->category());
  470. // The notification should not specify interaction behaviors if none are
  471. // available.
  472. first_set_of_notifications.clear();
  473. first_set_of_notifications.emplace_back(
  474. CreateNewInlineReplyableNotification(kNotificationIdA, kInlineReplyIdA));
  475. notification_processor()->AddNotifications(first_set_of_notifications);
  476. image_decoder_delegate()->RunAllCallbacks();
  477. notification = fake_notification_manager()->GetNotification(kNotificationIdA);
  478. EXPECT_EQ(Notification::InteractionBehavior::kNone,
  479. notification->interaction_behavior());
  480. EXPECT_EQ(Notification::Category::kConversation, notification->category());
  481. // The notification has IncomingCall category if answer action is
  482. // found.
  483. first_set_of_notifications.clear();
  484. first_set_of_notifications.emplace_back(
  485. CreateIncomingCallNotification(kNotificationIdA));
  486. notification_processor()->AddNotifications(first_set_of_notifications);
  487. image_decoder_delegate()->RunAllCallbacks();
  488. notification = fake_notification_manager()->GetNotification(kNotificationIdA);
  489. EXPECT_EQ(Notification::Category::kIncomingCall, notification->category());
  490. // The notification has OngoingCall category if hangup action is
  491. // found.
  492. first_set_of_notifications.clear();
  493. first_set_of_notifications.emplace_back(
  494. CreateOngoingCallNotification(kNotificationIdC));
  495. notification_processor()->AddNotifications(first_set_of_notifications);
  496. image_decoder_delegate()->RunAllCallbacks();
  497. notification = fake_notification_manager()->GetNotification(kNotificationIdC);
  498. EXPECT_EQ(Notification::Category::kOngoingCall, notification->category());
  499. }
  500. TEST_F(NotificationProcessorTest, ActionIdMapPopulatedCorrectly) {
  501. std::vector<proto::Notification> first_set_of_notifications;
  502. // The inline reply notification should have reply action id.
  503. first_set_of_notifications.emplace_back(
  504. CreateNewInlineReplyableNotification(kNotificationIdA, kInlineReplyIdA));
  505. notification_processor()->AddNotifications(first_set_of_notifications);
  506. image_decoder_delegate()->RunAllCallbacks();
  507. const Notification* notification =
  508. fake_notification_manager()->GetNotification(kNotificationIdA);
  509. EXPECT_EQ(1u, notification->action_id_map().size());
  510. EXPECT_EQ(kInlineReplyIdA, notification->action_id_map().at(
  511. Notification::ActionType::kInlineReply));
  512. // The incoming call notification should have answer and decline action ids.
  513. first_set_of_notifications.clear();
  514. first_set_of_notifications.emplace_back(
  515. CreateIncomingCallNotification(kNotificationIdA));
  516. notification_processor()->AddNotifications(first_set_of_notifications);
  517. image_decoder_delegate()->RunAllCallbacks();
  518. notification = fake_notification_manager()->GetNotification(kNotificationIdA);
  519. EXPECT_EQ(2u, notification->action_id_map().size());
  520. EXPECT_EQ(kAnswerActionId, notification->action_id_map().at(
  521. Notification::ActionType::kAnswer));
  522. EXPECT_EQ(kDeclineActionId, notification->action_id_map().at(
  523. Notification::ActionType::kDecline));
  524. // The ongoing call notification should have hangup action id.
  525. first_set_of_notifications.clear();
  526. first_set_of_notifications.emplace_back(
  527. CreateOngoingCallNotification(kNotificationIdC));
  528. notification_processor()->AddNotifications(first_set_of_notifications);
  529. image_decoder_delegate()->RunAllCallbacks();
  530. notification = fake_notification_manager()->GetNotification(kNotificationIdC);
  531. EXPECT_EQ(1u, notification->action_id_map().size());
  532. EXPECT_EQ(kHangupActionId, notification->action_id_map().at(
  533. Notification::ActionType::kHangup));
  534. }
  535. } // namespace phonehub
  536. } // namespace ash