1// Copyright (c) 2012 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
5#include "chrome/browser/ui/views/status_icons/status_tray_win.h"
6
7#include <commctrl.h>
8
9#include "base/strings/string_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/status_icons/status_icon_menu_model.h"
12#include "chrome/browser/status_icons/status_icon_observer.h"
13#include "chrome/browser/ui/views/status_icons/status_icon_win.h"
14#include "grit/chrome_unscaled_resources.h"
15#include "testing/gtest/include/gtest/gtest.h"
16#include "ui/base/resource/resource_bundle.h"
17#include "ui/gfx/image/image_skia.h"
18
19class FakeStatusTrayStateChangerProxy : public StatusTrayStateChangerProxy {
20 public:
21  FakeStatusTrayStateChangerProxy()
22      : enqueue_called_(false), icon_id_(0), window_(NULL) {}
23
24  virtual void EnqueueChange(UINT icon_id, HWND window) {
25    enqueue_called_ = true;
26    icon_id_ = icon_id;
27    window_ = window;
28  }
29
30  bool enqueue_called() { return enqueue_called_; }
31  UINT icon_id() { return icon_id_; }
32  HWND window() { return window_; }
33
34 private:
35  bool enqueue_called_;
36  UINT icon_id_;
37  HWND window_;
38
39  DISALLOW_COPY_AND_ASSIGN(FakeStatusTrayStateChangerProxy);
40};
41
42class FakeStatusIconObserver : public StatusIconObserver {
43 public:
44  FakeStatusIconObserver()
45      : balloon_clicked_(false),
46        status_icon_click_count_(0) {}
47  virtual void OnStatusIconClicked() {
48    ++status_icon_click_count_;
49  }
50  virtual void OnBalloonClicked() { balloon_clicked_ = true; }
51  bool balloon_clicked() const { return balloon_clicked_; }
52  size_t status_icon_click_count() const {
53    return status_icon_click_count_;
54  }
55
56 private:
57  size_t status_icon_click_count_;
58  bool balloon_clicked_;
59};
60
61TEST(StatusTrayWinTest, CreateTray) {
62  // Just tests creation/destruction.
63  StatusTrayWin tray;
64}
65
66TEST(StatusTrayWinTest, CreateIconAndMenu) {
67  // Create an icon, set the images, tooltip, and context menu, then shut it
68  // down.
69  StatusTrayWin tray;
70  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
71  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
72  StatusIcon* icon = tray.CreateStatusIcon(
73      StatusTray::OTHER_ICON, *image, base::ASCIIToUTF16("tool tip"));
74  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(NULL));
75  menu->AddItem(0, L"foo");
76  icon->SetContextMenu(menu.Pass());
77}
78
79#if !defined(USE_AURA)  // http://crbug.com/156370
80TEST(StatusTrayWinTest, ClickOnIcon) {
81  // Create an icon, send a fake click event, make sure observer is called.
82  StatusTrayWin tray;
83  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
84  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
85
86  StatusIconWin* icon = static_cast<StatusIconWin*>(tray.CreateStatusIcon(
87      StatusTray::OTHER_ICON, *image, base::ASCIIToUTF16("tool tip")));
88  FakeStatusIconObserver observer;
89  icon->AddObserver(&observer);
90  // Mimic a click.
91  tray.WndProc(NULL, icon->message_id(), icon->icon_id(), WM_LBUTTONDOWN);
92  // Mimic a right-click - observer should not be called.
93  tray.WndProc(NULL, icon->message_id(), icon->icon_id(), WM_RBUTTONDOWN);
94  EXPECT_EQ(1, observer.status_icon_click_count());
95  icon->RemoveObserver(&observer);
96}
97
98TEST(StatusTrayWinTest, ClickOnBalloon) {
99  // Create an icon, send a fake click event, make sure observer is called.
100  StatusTrayWin tray;
101  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
102  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
103
104  StatusIconWin* icon = static_cast<StatusIconWin*>(tray.CreateStatusIcon(
105      StatusTray::OTHER_ICON, *image, base::ASCIIToUTF16("tool tip")));
106  FakeStatusIconObserver observer;
107  icon->AddObserver(&observer);
108  // Mimic a click.
109  tray.WndProc(
110      NULL, icon->message_id(), icon->icon_id(), TB_INDETERMINATE);
111  EXPECT_TRUE(observer.balloon_clicked());
112  icon->RemoveObserver(&observer);
113}
114
115TEST(StatusTrayWinTest, HandleOldIconId) {
116  StatusTrayWin tray;
117  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
118  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
119
120  StatusIconWin* icon = static_cast<StatusIconWin*>(tray.CreateStatusIcon(
121      StatusTray::OTHER_ICON, *image, base::ASCIIToUTF16("tool tip")));
122  UINT message_id = icon->message_id();
123  UINT icon_id = icon->icon_id();
124
125  tray.RemoveStatusIcon(icon);
126  tray.WndProc(NULL, message_id, icon_id, WM_LBUTTONDOWN);
127}
128#endif  // !defined(USE_AURA)
129
130TEST(StatusTrayWinTest, EnsureVisibleTest) {
131  StatusTrayWin tray;
132  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
133  gfx::ImageSkia* image = rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
134
135  FakeStatusTrayStateChangerProxy* proxy =
136      new FakeStatusTrayStateChangerProxy();
137  tray.SetStatusTrayStateChangerProxyForTest(
138      scoped_ptr<StatusTrayStateChangerProxy>(proxy));
139
140  StatusIconWin* icon = static_cast<StatusIconWin*>(tray.CreateStatusIcon(
141      StatusTray::OTHER_ICON, *image, base::ASCIIToUTF16("tool tip")));
142
143  icon->ForceVisible();
144  // |proxy| is owned by |tray|, and |tray| lives to the end of the scope,
145  // so calling methods on |proxy| is safe.
146  EXPECT_TRUE(proxy->enqueue_called());
147  EXPECT_EQ(proxy->window(), icon->window());
148  EXPECT_EQ(proxy->icon_id(), icon->icon_id());
149}
150