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