OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
6 | 6 |
7 #import "chrome/browser/ui/cocoa/toolbar/reload_button.h" | 7 #import "chrome/browser/ui/cocoa/toolbar/reload_button.h" |
8 | 8 |
9 #include "base/memory/scoped_nsobject.h" | 9 #include "base/memory/scoped_nsobject.h" |
10 #include "chrome/app/chrome_command_ids.h" | 10 #include "chrome/app/chrome_command_ids.h" |
11 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" | 11 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" |
| 12 #import "chrome/browser/ui/cocoa/image_button_cell.h" |
12 #import "chrome/browser/ui/cocoa/test_event_utils.h" | 13 #import "chrome/browser/ui/cocoa/test_event_utils.h" |
13 #import "testing/gtest_mac.h" | 14 #import "testing/gtest_mac.h" |
14 #include "testing/platform_test.h" | 15 #include "testing/platform_test.h" |
15 #import "third_party/ocmock/OCMock/OCMock.h" | 16 #import "third_party/ocmock/OCMock/OCMock.h" |
16 | 17 |
| 18 @interface ReloadButton (Testing) |
| 19 + (void)setPendingReloadTimeout:(NSTimeInterval)seconds; |
| 20 @end |
| 21 |
17 @protocol TargetActionMock <NSObject> | 22 @protocol TargetActionMock <NSObject> |
18 - (void)anAction:(id)sender; | 23 - (void)anAction:(id)sender; |
19 @end | 24 @end |
20 | 25 |
21 namespace { | 26 namespace { |
22 | 27 |
23 class ReloadButtonTest : public CocoaTest { | 28 class ReloadButtonTest : public CocoaTest { |
24 public: | 29 public: |
25 ReloadButtonTest() { | 30 ReloadButtonTest() { |
26 NSRect frame = NSMakeRect(0, 0, 20, 20); | 31 NSRect frame = NSMakeRect(0, 0, 20, 20); |
27 scoped_nsobject<ReloadButton> button( | 32 scoped_nsobject<ReloadButton> button( |
28 [[ReloadButton alloc] initWithFrame:frame]); | 33 [[ReloadButton alloc] initWithFrame:frame]); |
29 button_ = button.get(); | 34 button_ = button.get(); |
30 | 35 |
31 // Set things up so unit tests have a reliable baseline. | 36 // Set things up so unit tests have a reliable baseline. |
32 [button_ setTag:IDC_RELOAD]; | 37 [button_ setTag:IDC_RELOAD]; |
33 [button_ awakeFromNib]; | 38 [button_ awakeFromNib]; |
34 | 39 |
35 [[test_window() contentView] addSubview:button_]; | 40 [[test_window() contentView] addSubview:button_]; |
36 } | 41 } |
37 | 42 |
| 43 bool IsMouseInside() { |
| 44 return [[button_ cell] isMouseInside]; |
| 45 } |
| 46 |
| 47 void MouseEnter() { |
| 48 [[button_ cell] mouseEntered:nil]; |
| 49 } |
| 50 |
| 51 void MouseExit() { |
| 52 [[button_ cell] mouseExited:nil]; |
| 53 } |
| 54 |
38 ReloadButton* button_; | 55 ReloadButton* button_; |
39 }; | 56 }; |
40 | 57 |
41 TEST_VIEW(ReloadButtonTest, button_) | 58 TEST_VIEW(ReloadButtonTest, button_) |
42 | 59 |
43 // Test that mouse-tracking is setup and does the right thing. | 60 // Test that mouse-tracking is setup and does the right thing. |
44 TEST_F(ReloadButtonTest, IsMouseInside) { | 61 TEST_F(ReloadButtonTest, IsMouseInside) { |
45 EXPECT_TRUE([[button_ trackingAreas] containsObject:[button_ trackingArea]]); | 62 EXPECT_FALSE(IsMouseInside()); |
46 | 63 MouseEnter(); |
47 EXPECT_FALSE([button_ isMouseInside]); | 64 EXPECT_TRUE(IsMouseInside()); |
48 [button_ mouseEntered:nil]; | 65 MouseExit(); |
49 EXPECT_TRUE([button_ isMouseInside]); | |
50 [button_ mouseExited:nil]; | |
51 } | 66 } |
52 | 67 |
53 // Verify that multiple clicks do not result in multiple messages to | 68 // Verify that multiple clicks do not result in multiple messages to |
54 // the target. | 69 // the target. |
55 TEST_F(ReloadButtonTest, IgnoredMultiClick) { | 70 TEST_F(ReloadButtonTest, IgnoredMultiClick) { |
56 id mock_target = [OCMockObject mockForProtocol:@protocol(TargetActionMock)]; | 71 id mock_target = [OCMockObject mockForProtocol:@protocol(TargetActionMock)]; |
57 [button_ setTarget:mock_target]; | 72 [button_ setTarget:mock_target]; |
58 [button_ setAction:@selector(anAction:)]; | 73 [button_ setAction:@selector(anAction:)]; |
59 | 74 |
60 // Expect the action once. | 75 // Expect the action once. |
(...skipping 24 matching lines...) Expand all Loading... |
85 EXPECT_NSNE(reloadToolTip, stopToolTip); | 100 EXPECT_NSNE(reloadToolTip, stopToolTip); |
86 | 101 |
87 [button_ updateTag:IDC_RELOAD]; | 102 [button_ updateTag:IDC_RELOAD]; |
88 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 103 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
89 EXPECT_NSEQ(reloadToolTip, [button_ toolTip]); | 104 EXPECT_NSEQ(reloadToolTip, [button_ toolTip]); |
90 } | 105 } |
91 | 106 |
92 // Test that when forcing the mode, it takes effect immediately, | 107 // Test that when forcing the mode, it takes effect immediately, |
93 // regardless of whether the mouse is hovering. | 108 // regardless of whether the mouse is hovering. |
94 TEST_F(ReloadButtonTest, SetIsLoadingForce) { | 109 TEST_F(ReloadButtonTest, SetIsLoadingForce) { |
95 EXPECT_FALSE([button_ isMouseInside]); | 110 EXPECT_FALSE(IsMouseInside()); |
96 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 111 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
97 | 112 |
98 // Changes to stop immediately. | 113 // Changes to stop immediately. |
99 [button_ setIsLoading:YES force:YES]; | 114 [button_ setIsLoading:YES force:YES]; |
100 EXPECT_EQ(IDC_STOP, [button_ tag]); | 115 EXPECT_EQ(IDC_STOP, [button_ tag]); |
101 | 116 |
102 // Changes to reload immediately. | 117 // Changes to reload immediately. |
103 [button_ setIsLoading:NO force:YES]; | 118 [button_ setIsLoading:NO force:YES]; |
104 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 119 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
105 | 120 |
106 // Changes to stop immediately when the mouse is hovered, and | 121 // Changes to stop immediately when the mouse is hovered, and |
107 // doesn't change when the mouse exits. | 122 // doesn't change when the mouse exits. |
108 [button_ mouseEntered:nil]; | 123 MouseEnter(); |
109 EXPECT_TRUE([button_ isMouseInside]); | 124 EXPECT_TRUE(IsMouseInside()); |
110 [button_ setIsLoading:YES force:YES]; | 125 [button_ setIsLoading:YES force:YES]; |
111 EXPECT_EQ(IDC_STOP, [button_ tag]); | 126 EXPECT_EQ(IDC_STOP, [button_ tag]); |
112 [button_ mouseExited:nil]; | 127 MouseExit(); |
113 EXPECT_FALSE([button_ isMouseInside]); | 128 EXPECT_FALSE(IsMouseInside()); |
114 EXPECT_EQ(IDC_STOP, [button_ tag]); | 129 EXPECT_EQ(IDC_STOP, [button_ tag]); |
115 | 130 |
116 // Changes to reload immediately when the mouse is hovered, and | 131 // Changes to reload immediately when the mouse is hovered, and |
117 // doesn't change when the mouse exits. | 132 // doesn't change when the mouse exits. |
118 [button_ mouseEntered:nil]; | 133 MouseEnter(); |
119 EXPECT_TRUE([button_ isMouseInside]); | 134 EXPECT_TRUE(IsMouseInside()); |
120 [button_ setIsLoading:NO force:YES]; | 135 [button_ setIsLoading:NO force:YES]; |
121 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 136 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
122 [button_ mouseExited:nil]; | 137 MouseExit(); |
123 EXPECT_FALSE([button_ isMouseInside]); | 138 EXPECT_FALSE(IsMouseInside()); |
124 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 139 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
125 } | 140 } |
126 | 141 |
127 // Test that without force, stop mode is set immediately, but reload | 142 // Test that without force, stop mode is set immediately, but reload |
128 // is affected by the hover status. | 143 // is affected by the hover status. |
129 TEST_F(ReloadButtonTest, SetIsLoadingNoForceUnHover) { | 144 TEST_F(ReloadButtonTest, SetIsLoadingNoForceUnHover) { |
130 EXPECT_FALSE([button_ isMouseInside]); | 145 EXPECT_FALSE(IsMouseInside()); |
131 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 146 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
132 | 147 |
133 // Changes to stop immediately when the mouse is not hovering. | 148 // Changes to stop immediately when the mouse is not hovering. |
134 [button_ setIsLoading:YES force:NO]; | 149 [button_ setIsLoading:YES force:NO]; |
135 EXPECT_EQ(IDC_STOP, [button_ tag]); | 150 EXPECT_EQ(IDC_STOP, [button_ tag]); |
136 | 151 |
137 // Changes to reload immediately when the mouse is not hovering. | 152 // Changes to reload immediately when the mouse is not hovering. |
138 [button_ setIsLoading:NO force:NO]; | 153 [button_ setIsLoading:NO force:NO]; |
139 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 154 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
140 | 155 |
141 // Changes to stop immediately when the mouse is hovered, and | 156 // Changes to stop immediately when the mouse is hovered, and |
142 // doesn't change when the mouse exits. | 157 // doesn't change when the mouse exits. |
143 [button_ mouseEntered:nil]; | 158 MouseEnter(); |
144 EXPECT_TRUE([button_ isMouseInside]); | 159 EXPECT_TRUE(IsMouseInside()); |
145 [button_ setIsLoading:YES force:NO]; | 160 [button_ setIsLoading:YES force:NO]; |
146 EXPECT_EQ(IDC_STOP, [button_ tag]); | 161 EXPECT_EQ(IDC_STOP, [button_ tag]); |
147 [button_ mouseExited:nil]; | 162 MouseExit(); |
148 EXPECT_FALSE([button_ isMouseInside]); | 163 EXPECT_FALSE(IsMouseInside()); |
149 EXPECT_EQ(IDC_STOP, [button_ tag]); | 164 EXPECT_EQ(IDC_STOP, [button_ tag]); |
150 | 165 |
151 // Does not change to reload immediately when the mouse is hovered, | 166 // Does not change to reload immediately when the mouse is hovered, |
152 // changes when the mouse exits. | 167 // changes when the mouse exits. |
153 [button_ mouseEntered:nil]; | 168 MouseEnter(); |
154 EXPECT_TRUE([button_ isMouseInside]); | 169 EXPECT_TRUE(IsMouseInside()); |
155 [button_ setIsLoading:NO force:NO]; | 170 [button_ setIsLoading:NO force:NO]; |
156 EXPECT_EQ(IDC_STOP, [button_ tag]); | 171 EXPECT_EQ(IDC_STOP, [button_ tag]); |
157 [button_ mouseExited:nil]; | 172 MouseExit(); |
158 EXPECT_FALSE([button_ isMouseInside]); | 173 EXPECT_FALSE(IsMouseInside()); |
159 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 174 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
160 } | 175 } |
161 | 176 |
162 // Test that without force, stop mode is set immediately, and reload | 177 // Test that without force, stop mode is set immediately, and reload |
163 // will be set after a timeout. | 178 // will be set after a timeout. |
164 // TODO(shess): Reenable, http://crbug.com/61485 | 179 // TODO(shess): Reenable, http://crbug.com/61485 |
165 TEST_F(ReloadButtonTest, DISABLED_SetIsLoadingNoForceTimeout) { | 180 TEST_F(ReloadButtonTest, DISABLED_SetIsLoadingNoForceTimeout) { |
166 // When the event loop first spins, some delayed tracking-area setup | 181 // When the event loop first spins, some delayed tracking-area setup |
167 // is done, which causes -mouseExited: to be called. Spin it at | 182 // is done, which causes -mouseExited: to be called. Spin it at |
168 // least once, and dequeue any pending events. | 183 // least once, and dequeue any pending events. |
169 // TODO(shess): It would be more reasonable to have an MockNSTimer | 184 // TODO(shess): It would be more reasonable to have an MockNSTimer |
170 // factory for the class to use, which this code could fire | 185 // factory for the class to use, which this code could fire |
171 // directly. | 186 // directly. |
172 while ([NSApp nextEventMatchingMask:NSAnyEventMask | 187 while ([NSApp nextEventMatchingMask:NSAnyEventMask |
173 untilDate:nil | 188 untilDate:nil |
174 inMode:NSDefaultRunLoopMode | 189 inMode:NSDefaultRunLoopMode |
175 dequeue:YES]) { | 190 dequeue:YES]) { |
176 } | 191 } |
177 | 192 |
178 const NSTimeInterval kShortTimeout = 0.1; | 193 const NSTimeInterval kShortTimeout = 0.1; |
179 [ReloadButton setPendingReloadTimeout:kShortTimeout]; | 194 [ReloadButton setPendingReloadTimeout:kShortTimeout]; |
180 | 195 |
181 EXPECT_FALSE([button_ isMouseInside]); | 196 EXPECT_FALSE(IsMouseInside()); |
182 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 197 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
183 | 198 |
184 // Move the mouse into the button and press it. | 199 // Move the mouse into the button and press it. |
185 [button_ mouseEntered:nil]; | 200 MouseEnter(); |
186 EXPECT_TRUE([button_ isMouseInside]); | 201 EXPECT_TRUE(IsMouseInside()); |
187 [button_ setIsLoading:YES force:NO]; | 202 [button_ setIsLoading:YES force:NO]; |
188 EXPECT_EQ(IDC_STOP, [button_ tag]); | 203 EXPECT_EQ(IDC_STOP, [button_ tag]); |
189 | 204 |
190 // Does not change to reload immediately when the mouse is hovered. | 205 // Does not change to reload immediately when the mouse is hovered. |
191 EXPECT_TRUE([button_ isMouseInside]); | 206 EXPECT_TRUE(IsMouseInside()); |
192 [button_ setIsLoading:NO force:NO]; | 207 [button_ setIsLoading:NO force:NO]; |
193 EXPECT_TRUE([button_ isMouseInside]); | 208 EXPECT_TRUE(IsMouseInside()); |
194 EXPECT_EQ(IDC_STOP, [button_ tag]); | 209 EXPECT_EQ(IDC_STOP, [button_ tag]); |
195 EXPECT_TRUE([button_ isMouseInside]); | 210 EXPECT_TRUE(IsMouseInside()); |
196 | 211 |
197 // Spin event loop until the timeout passes. | 212 // Spin event loop until the timeout passes. |
198 NSDate* pastTimeout = [NSDate dateWithTimeIntervalSinceNow:2 * kShortTimeout]; | 213 NSDate* pastTimeout = [NSDate dateWithTimeIntervalSinceNow:2 * kShortTimeout]; |
199 [NSApp nextEventMatchingMask:NSAnyEventMask | 214 [NSApp nextEventMatchingMask:NSAnyEventMask |
200 untilDate:pastTimeout | 215 untilDate:pastTimeout |
201 inMode:NSDefaultRunLoopMode | 216 inMode:NSDefaultRunLoopMode |
202 dequeue:NO]; | 217 dequeue:NO]; |
203 | 218 |
204 // Mouse is still hovered, button is in reload mode. If the mouse | 219 // Mouse is still hovered, button is in reload mode. If the mouse |
205 // is no longer hovered, see comment at top of function. | 220 // is no longer hovered, see comment at top of function. |
206 EXPECT_TRUE([button_ isMouseInside]); | 221 EXPECT_TRUE(IsMouseInside()); |
207 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 222 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
208 } | 223 } |
209 | 224 |
210 // Test that pressing stop after reload mode has been requested | 225 // Test that pressing stop after reload mode has been requested |
211 // doesn't forward the stop message. | 226 // doesn't forward the stop message. |
212 TEST_F(ReloadButtonTest, StopAfterReloadSet) { | 227 TEST_F(ReloadButtonTest, StopAfterReloadSet) { |
213 id mock_target = [OCMockObject mockForProtocol:@protocol(TargetActionMock)]; | 228 id mock_target = [OCMockObject mockForProtocol:@protocol(TargetActionMock)]; |
214 [button_ setTarget:mock_target]; | 229 [button_ setTarget:mock_target]; |
215 [button_ setAction:@selector(anAction:)]; | 230 [button_ setAction:@selector(anAction:)]; |
216 | 231 |
217 EXPECT_FALSE([button_ isMouseInside]); | 232 EXPECT_FALSE(IsMouseInside()); |
218 | 233 |
219 // Get to stop mode. | 234 // Get to stop mode. |
220 [button_ setIsLoading:YES force:YES]; | 235 [button_ setIsLoading:YES force:YES]; |
221 EXPECT_EQ(IDC_STOP, [button_ tag]); | 236 EXPECT_EQ(IDC_STOP, [button_ tag]); |
222 EXPECT_TRUE([button_ isEnabled]); | 237 EXPECT_TRUE([button_ isEnabled]); |
223 | 238 |
224 // Expect the action once. | 239 // Expect the action once. |
225 [[mock_target expect] anAction:button_]; | 240 [[mock_target expect] anAction:button_]; |
226 | 241 |
227 // Clicking in stop mode should send the action and transition to | 242 // Clicking in stop mode should send the action and transition to |
228 // reload mode. | 243 // reload mode. |
229 const std::pair<NSEvent*,NSEvent*> click = | 244 const std::pair<NSEvent*,NSEvent*> click = |
230 test_event_utils::MouseClickInView(button_, 1); | 245 test_event_utils::MouseClickInView(button_, 1); |
231 [NSApp postEvent:click.second atStart:YES]; | 246 [NSApp postEvent:click.second atStart:YES]; |
232 [button_ mouseDown:click.first]; | 247 [button_ mouseDown:click.first]; |
233 EXPECT_EQ(IDC_RELOAD, [button_ tag]); | 248 EXPECT_EQ(IDC_RELOAD, [button_ tag]); |
234 EXPECT_TRUE([button_ isEnabled]); | 249 EXPECT_TRUE([button_ isEnabled]); |
235 | 250 |
236 // Get back to stop mode. | 251 // Get back to stop mode. |
237 [button_ setIsLoading:YES force:YES]; | 252 [button_ setIsLoading:YES force:YES]; |
238 EXPECT_EQ(IDC_STOP, [button_ tag]); | 253 EXPECT_EQ(IDC_STOP, [button_ tag]); |
239 EXPECT_TRUE([button_ isEnabled]); | 254 EXPECT_TRUE([button_ isEnabled]); |
240 | 255 |
241 // If hover prevented reload mode immediately taking effect, clicks should do | 256 // If hover prevented reload mode immediately taking effect, clicks should do |
242 // nothing, because the button should be disabled. | 257 // nothing, because the button should be disabled. |
243 [button_ mouseEntered:nil]; | 258 MouseEnter(); |
244 EXPECT_TRUE([button_ isMouseInside]); | 259 EXPECT_TRUE(IsMouseInside()); |
245 [button_ setIsLoading:NO force:NO]; | 260 [button_ setIsLoading:NO force:NO]; |
246 EXPECT_EQ(IDC_STOP, [button_ tag]); | 261 EXPECT_EQ(IDC_STOP, [button_ tag]); |
247 EXPECT_FALSE([button_ isEnabled]); | 262 EXPECT_FALSE([button_ isEnabled]); |
248 [NSApp postEvent:click.second atStart:YES]; | 263 [NSApp postEvent:click.second atStart:YES]; |
249 [button_ mouseDown:click.first]; | 264 [button_ mouseDown:click.first]; |
250 EXPECT_EQ(IDC_STOP, [button_ tag]); | 265 EXPECT_EQ(IDC_STOP, [button_ tag]); |
251 | 266 |
252 [button_ setTarget:nil]; | 267 [button_ setTarget:nil]; |
253 } | 268 } |
254 | 269 |
255 } // namespace | 270 } // namespace |
OLD | NEW |