1 // Copyright 2013 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.
5 #import <Cocoa/Cocoa.h>
7 #import "base/mac/scoped_block.h"
8 #import "base/mac/scoped_nsobject.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "chrome/browser/download/download_shelf.h"
11 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
12 #import "chrome/browser/ui/cocoa/download/download_item_controller.h"
13 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h"
14 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
15 #include "content/public/test/mock_download_item.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "testing/platform_test.h"
18 #import "third_party/ocmock/OCMock/OCMock.h"
19 #import "third_party/ocmock/gtest_support.h"
21 using ::testing::Return;
22 using ::testing::AnyNumber;
24 // Wraps a content::MockDownloadItem so it can be retained by the mock
25 // DownloadItemController.
26 @interface WrappedMockDownloadItem : NSObject {
28 scoped_ptr<content::MockDownloadItem> download_;
30 - (id)initWithMockDownload:(scoped_ptr<content::MockDownloadItem>)download;
31 - (content::DownloadItem*)download;
32 - (content::MockDownloadItem*)mockDownload;
35 @implementation WrappedMockDownloadItem
36 - (id)initWithMockDownload:(scoped_ptr<content::MockDownloadItem>)download {
37 if ((self = [super init])) {
38 download_ = download.Pass();
43 - (content::DownloadItem*)download {
44 return download_.get();
47 - (content::MockDownloadItem*)mockDownload {
48 return download_.get();
52 // Test method for accessing the wrapped MockDownloadItem.
53 @interface DownloadItemController (DownloadShelfControllerTest) {
55 - (WrappedMockDownloadItem*)wrappedMockDownload;
58 @implementation DownloadItemController (DownloadShelfControllerTest)
59 - (WrappedMockDownloadItem*)wrappedMockDownload {
64 @interface DownloadShelfController (Testing)
65 - (void)animationDidEnd:(NSAnimation*)animation;
68 // Subclass of the DownloadShelfController to override scheduleAutoClose and
69 // cancelAutoClose. During regular operation, a scheduled autoClose waits for 5
70 // seconds before closing the shelf (unless it is cancelled during this
71 // time). For testing purposes, we count the number of invocations of
72 // {schedule,cancel}AutoClose instead of actually scheduling and cancelling.
73 @interface CountingDownloadShelfController : DownloadShelfController {
75 int scheduleAutoCloseCount_;
76 int cancelAutoCloseCount_;
77 base::mac::ScopedBlock<dispatch_block_t> closeAnimationHandler_;
80 // Handler will be called at the end of a close animation.
81 - (void)setCloseAnimationHandler:(dispatch_block_t)handler;
84 @implementation CountingDownloadShelfController
86 -(void)scheduleAutoClose {
87 ++scheduleAutoCloseCount_;
90 -(void)cancelAutoClose {
91 ++cancelAutoCloseCount_;
94 - (void)setCloseAnimationHandler:(dispatch_block_t)handler {
95 closeAnimationHandler_.reset(handler);
98 - (void)animationDidEnd:(NSAnimation*)animation {
99 [super animationDidEnd:animation];
100 if (closeAnimationHandler_)
101 closeAnimationHandler_.get()();
108 class DownloadShelfControllerTest : public CocoaProfileTest {
110 virtual void SetUp() override {
111 CocoaProfileTest::SetUp();
112 ASSERT_TRUE(browser());
114 resize_delegate_.reset([[ViewResizerPong alloc] init]);
115 shelf_.reset([[CountingDownloadShelfController alloc]
116 initWithBrowser:browser()
117 resizeDelegate:resize_delegate_.get()]);
118 EXPECT_TRUE([shelf_ view]);
119 [[test_window() contentView] addSubview:[shelf_ view]];
122 virtual void TearDown() override {
127 CocoaProfileTest::TearDown();
131 id CreateItemController();
133 base::scoped_nsobject<CountingDownloadShelfController> shelf_;
134 base::scoped_nsobject<ViewResizerPong> resize_delegate_;
137 id DownloadShelfControllerTest::CreateItemController() {
138 scoped_ptr<content::MockDownloadItem> download(
139 new ::testing::NiceMock<content::MockDownloadItem>);
140 ON_CALL(*download.get(), GetOpened())
141 .WillByDefault(Return(false));
142 ON_CALL(*download.get(), GetState())
143 .WillByDefault(Return(content::DownloadItem::IN_PROGRESS));
145 base::scoped_nsobject<WrappedMockDownloadItem> wrappedMockDownload(
146 [[WrappedMockDownloadItem alloc] initWithMockDownload:download.Pass()]);
149 [OCMockObject mockForClass:[DownloadItemController class]];
150 base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]);
151 [[[item_controller stub] andCall:@selector(download)
152 onObject:wrappedMockDownload.get()] download];
153 [[item_controller stub] updateVisibility:[OCMArg any]];
154 [[[item_controller stub]
155 andReturnValue:[NSValue valueWithSize:NSMakeSize(10,10)]] preferredSize];
156 [[[item_controller stub] andReturn:view.get()] view];
157 [[[item_controller stub]
158 andReturn:wrappedMockDownload.get()] wrappedMockDownload];
159 return [item_controller retain];
162 TEST_VIEW(DownloadShelfControllerTest, [shelf_ view]);
164 // Removing the last download from the shelf should cause it to close
166 TEST_F(DownloadShelfControllerTest, AddAndRemoveDownload) {
167 base::scoped_nsobject<DownloadItemController> item(CreateItemController());
168 [shelf_ showDownloadShelf:YES
170 EXPECT_TRUE([shelf_ isVisible]);
171 EXPECT_TRUE([shelf_ bridge]->IsShowing());
173 [shelf_ remove:item];
174 EXPECT_FALSE([shelf_ isVisible]);
175 EXPECT_FALSE([shelf_ bridge]->IsShowing());
176 // The shelf should be closed without scheduling an autoClose.
177 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
180 // Test that the shelf doesn't close automatically after a removal if there are
181 // active download items still on the shelf.
182 TEST_F(DownloadShelfControllerTest, AddAndRemoveWithActiveItem) {
183 base::scoped_nsobject<DownloadItemController> item1(CreateItemController());
184 base::scoped_nsobject<DownloadItemController> item2(CreateItemController());
185 [shelf_ showDownloadShelf:YES
187 EXPECT_TRUE([shelf_ isVisible]);
188 [shelf_ add:item1.get()];
189 [shelf_ add:item2.get()];
190 [shelf_ remove:item1.get()];
191 EXPECT_TRUE([shelf_ isVisible]);
192 [shelf_ remove:item2.get()];
193 EXPECT_FALSE([shelf_ isVisible]);
194 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
197 // DownloadShelf::Unhide() should cause the shelf to be displayed if there are
198 // active downloads on it.
199 TEST_F(DownloadShelfControllerTest, HideAndUnhide) {
200 base::scoped_nsobject<DownloadItemController> item(CreateItemController());
201 [shelf_ showDownloadShelf:YES
203 EXPECT_TRUE([shelf_ isVisible]);
204 [shelf_ add:item.get()];
205 [shelf_ bridge]->Hide();
206 EXPECT_FALSE([shelf_ isVisible]);
207 [shelf_ bridge]->Unhide();
208 EXPECT_TRUE([shelf_ isVisible]);
209 [shelf_ remove:item.get()];
210 EXPECT_FALSE([shelf_ isVisible]);
213 // DownloadShelf::Unhide() shouldn't cause the shelf to be displayed if all
214 // active downloads are removed from the shelf while the shelf was hidden.
215 TEST_F(DownloadShelfControllerTest, HideAutocloseUnhide) {
216 base::scoped_nsobject<DownloadItemController> item(CreateItemController());
217 [shelf_ showDownloadShelf:YES
219 EXPECT_TRUE([shelf_ isVisible]);
220 [shelf_ add:item.get()];
221 [shelf_ bridge]->Hide();
222 EXPECT_FALSE([shelf_ isVisible]);
223 [shelf_ remove:item.get()];
224 EXPECT_FALSE([shelf_ isVisible]);
225 [shelf_ bridge]->Unhide();
226 EXPECT_FALSE([shelf_ isVisible]);
229 // Test of autoclosing behavior after opening a download item. The mouse is on
230 // the download shelf at the time the autoclose is scheduled.
231 TEST_F(DownloadShelfControllerTest, AutoCloseAfterOpenWithMouseInShelf) {
232 base::scoped_nsobject<DownloadItemController> item(CreateItemController());
233 [shelf_ showDownloadShelf:YES
235 EXPECT_TRUE([shelf_ isVisible]);
236 [shelf_ add:item.get()];
237 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the
239 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_);
240 shelf_.get()->cancelAutoCloseCount_ = 0;
242 // The mouse enters the shelf.
243 [shelf_ mouseEntered:nil];
244 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
246 // The download opens.
247 EXPECT_CALL(*[[item wrappedMockDownload] mockDownload], GetOpened())
248 .WillRepeatedly(Return(true));
249 [shelf_ downloadWasOpened:item.get()];
251 // The shelf should now be waiting for the mouse to exit.
252 EXPECT_TRUE([shelf_ isVisible]);
253 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
254 EXPECT_EQ(0, shelf_.get()->cancelAutoCloseCount_);
256 // The mouse exits the shelf. autoClose should be scheduled now.
257 [shelf_ mouseExited:nil];
258 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_);
259 EXPECT_EQ(0, shelf_.get()->cancelAutoCloseCount_);
261 // The mouse enters the shelf again. The autoClose should be cancelled.
262 [shelf_ mouseEntered:nil];
263 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_);
264 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_);
267 // Test of autoclosing behavior after opening a download item.
268 TEST_F(DownloadShelfControllerTest, AutoCloseAfterOpenWithMouseOffShelf) {
269 base::scoped_nsobject<DownloadItemController> item(CreateItemController());
270 [shelf_ showDownloadShelf:YES
272 EXPECT_TRUE([shelf_ isVisible]);
273 [shelf_ add:item.get()];
275 // The download is opened.
276 EXPECT_CALL(*[[item wrappedMockDownload] mockDownload], GetOpened())
277 .WillRepeatedly(Return(true));
278 [shelf_ downloadWasOpened:item.get()];
280 // The shelf should be closed immediately since the mouse is not over the
282 EXPECT_FALSE([shelf_ isVisible]);
285 // Test that if the shelf is closed while an autoClose is pending, the pending
286 // autoClose is cancelled.
287 TEST_F(DownloadShelfControllerTest, CloseWithPendingAutoClose) {
288 base::scoped_nsobject<DownloadItemController> item(CreateItemController());
289 [shelf_ showDownloadShelf:YES
291 EXPECT_TRUE([shelf_ isVisible]);
292 [shelf_ add:item.get()];
293 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the
295 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_);
296 shelf_.get()->cancelAutoCloseCount_ = 0;
298 // The mouse enters the shelf.
299 [shelf_ mouseEntered:nil];
300 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
302 // The download opens.
303 EXPECT_CALL(*[[item wrappedMockDownload] mockDownload], GetOpened())
304 .WillRepeatedly(Return(true));
305 [shelf_ downloadWasOpened:item.get()];
307 // The shelf should now be waiting for the mouse to exit. autoClose should not
309 EXPECT_TRUE([shelf_ isVisible]);
310 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
311 EXPECT_EQ(0, shelf_.get()->cancelAutoCloseCount_);
313 // The mouse exits the shelf. autoClose should be scheduled now.
314 [shelf_ mouseExited:nil];
315 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_);
316 EXPECT_EQ(0, shelf_.get()->cancelAutoCloseCount_);
318 // Remove the download item. This should cause the download shelf to be hidden
319 // immediately. The pending autoClose should be cancelled.
320 [shelf_ remove:item];
321 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_);
322 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_);
323 EXPECT_FALSE([shelf_ isVisible]);
326 // That that the shelf cancels a pending autoClose if a new download item is
328 TEST_F(DownloadShelfControllerTest, AddItemWithPendingAutoClose) {
329 base::scoped_nsobject<DownloadItemController> item(CreateItemController());
330 [shelf_ showDownloadShelf:YES
332 EXPECT_TRUE([shelf_ isVisible]);
333 [shelf_ add:item.get()];
334 // Expect 2 cancelAutoClose calls: From the showDownloadShelf: call and the
336 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_);
337 shelf_.get()->cancelAutoCloseCount_ = 0;
339 // The mouse enters the shelf.
340 [shelf_ mouseEntered:nil];
341 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
343 // The download opens.
344 EXPECT_CALL(*[[item wrappedMockDownload] mockDownload], GetOpened())
345 .WillRepeatedly(Return(true));
346 [shelf_ downloadWasOpened:item.get()];
348 // The shelf should now be waiting for the mouse to exit. autoClose should not
350 EXPECT_TRUE([shelf_ isVisible]);
351 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
352 EXPECT_EQ(0, shelf_.get()->cancelAutoCloseCount_);
354 // The mouse exits the shelf. autoClose should be scheduled now.
355 [shelf_ mouseExited:nil];
356 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_);
357 EXPECT_EQ(0, shelf_.get()->cancelAutoCloseCount_);
359 // Add a new download item. The pending autoClose should be cancelled.
360 base::scoped_nsobject<DownloadItemController> item2(CreateItemController());
361 [shelf_ add:item.get()];
362 EXPECT_EQ(1, shelf_.get()->scheduleAutoCloseCount_);
363 EXPECT_EQ(1, shelf_.get()->cancelAutoCloseCount_);
364 EXPECT_TRUE([shelf_ isVisible]);
367 // Test that pending autoClose calls are cancelled when exiting.
368 TEST_F(DownloadShelfControllerTest, CancelAutoCloseOnExit) {
369 base::scoped_nsobject<DownloadItemController> item(CreateItemController());
370 [shelf_ showDownloadShelf:YES
372 EXPECT_TRUE([shelf_ isVisible]);
373 [shelf_ add:item.get()];
374 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
375 EXPECT_EQ(2, shelf_.get()->cancelAutoCloseCount_);
378 EXPECT_EQ(0, shelf_.get()->scheduleAutoCloseCount_);
379 EXPECT_EQ(3, shelf_.get()->cancelAutoCloseCount_);
383 // The view should not be hidden when the shelf is shown.
384 // The view should be hidden after the closing animation.
385 TEST_F(DownloadShelfControllerTest, ViewVisibility) {
386 [shelf_ showDownloadShelf:YES isUserAction:NO];
387 EXPECT_FALSE([[shelf_ view] isHidden]);
389 [shelf_ setCloseAnimationHandler:^{
390 base::MessageLoop::current()->QuitNow();
392 [shelf_ showDownloadShelf:NO isUserAction:NO];
393 base::MessageLoop::current()->Run();
394 EXPECT_TRUE([[shelf_ view] isHidden]);
396 [shelf_ showDownloadShelf:YES isUserAction:NO];
397 EXPECT_FALSE([[shelf_ view] isHidden]);