Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / bookmarks / bookmark_bar_controller_unittest.mm
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 #import <Cocoa/Cocoa.h>
6
7 #include "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
15 #include "chrome/browser/extensions/test_extension_system.h"
16 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
17 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h"
19 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_unittest_helper.h"
20 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h"
21 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h"
23 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
24 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/test/base/testing_pref_service_syncable.h"
28 #include "chrome/test/base/testing_profile.h"
29 #include "components/bookmarks/browser/bookmark_model.h"
30 #include "components/bookmarks/browser/bookmark_utils.h"
31 #include "components/bookmarks/test/bookmark_test_helpers.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #import "testing/gtest_mac.h"
34 #include "testing/platform_test.h"
35 #import "third_party/ocmock/OCMock/OCMock.h"
36 #include "third_party/ocmock/gtest_support.h"
37 #include "ui/base/cocoa/animation_utils.h"
38 #include "ui/base/theme_provider.h"
39 #include "ui/events/test/cocoa_test_event_utils.h"
40 #include "ui/gfx/image/image_skia.h"
41
42 using base::ASCIIToUTF16;
43
44 // Unit tests don't need time-consuming asynchronous animations.
45 @interface BookmarkBarControllerTestable : BookmarkBarController {
46 }
47
48 @end
49
50 @implementation BookmarkBarControllerTestable
51
52 - (id)initWithBrowser:(Browser*)browser
53          initialWidth:(CGFloat)initialWidth
54              delegate:(id<BookmarkBarControllerDelegate>)delegate
55        resizeDelegate:(id<ViewResizer>)resizeDelegate {
56   if ((self = [super initWithBrowser:browser
57                         initialWidth:initialWidth
58                             delegate:delegate
59                       resizeDelegate:resizeDelegate])) {
60     [self setStateAnimationsEnabled:NO];
61     [self setInnerContentAnimationsEnabled:NO];
62   }
63   return self;
64 }
65
66 @end
67
68 // Just like a BookmarkBarController but openURL: is stubbed out.
69 @interface BookmarkBarControllerNoOpen : BookmarkBarControllerTestable {
70  @public
71   std::vector<GURL> urls_;
72   std::vector<WindowOpenDisposition> dispositions_;
73 }
74 @end
75
76 @implementation BookmarkBarControllerNoOpen
77 - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition {
78   urls_.push_back(url);
79   dispositions_.push_back(disposition);
80 }
81 - (void)clear {
82   urls_.clear();
83   dispositions_.clear();
84 }
85 @end
86
87
88 // NSCell that is pre-provided with a desired size that becomes the
89 // return value for -(NSSize)cellSize:.
90 @interface CellWithDesiredSize : NSCell {
91  @private
92   NSSize cellSize_;
93 }
94 @property (nonatomic, readonly) NSSize cellSize;
95 @end
96
97 @implementation CellWithDesiredSize
98
99 @synthesize cellSize = cellSize_;
100
101 - (id)initTextCell:(NSString*)string desiredSize:(NSSize)size {
102   if ((self = [super initTextCell:string])) {
103     cellSize_ = size;
104   }
105   return self;
106 }
107
108 @end
109
110 // Remember the number of times we've gotten a frameDidChange notification.
111 @interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen {
112  @private
113   int toggles_;
114 }
115 @property (nonatomic, readonly) int toggles;
116 @end
117
118 @implementation BookmarkBarControllerTogglePong
119
120 @synthesize toggles = toggles_;
121
122 - (void)frameDidChange {
123   toggles_++;
124 }
125
126 @end
127
128 // Remembers if a notification callback was called.
129 @interface BookmarkBarControllerNotificationPong : BookmarkBarControllerNoOpen {
130   BOOL windowWillCloseReceived_;
131   BOOL windowDidResignKeyReceived_;
132 }
133 @property (nonatomic, readonly) BOOL windowWillCloseReceived;
134 @property (nonatomic, readonly) BOOL windowDidResignKeyReceived;
135 @end
136
137 @implementation BookmarkBarControllerNotificationPong
138 @synthesize windowWillCloseReceived = windowWillCloseReceived_;
139 @synthesize windowDidResignKeyReceived = windowDidResignKeyReceived_;
140
141 // Override NSNotificationCenter callback.
142 - (void)parentWindowWillClose:(NSNotification*)notification {
143   windowWillCloseReceived_ = YES;
144 }
145
146 // NSNotificationCenter callback.
147 - (void)parentWindowDidResignKey:(NSNotification*)notification {
148   windowDidResignKeyReceived_ = YES;
149 }
150 @end
151
152 // Remembers if and what kind of openAll was performed.
153 @interface BookmarkBarControllerOpenAllPong : BookmarkBarControllerNoOpen {
154   WindowOpenDisposition dispositionDetected_;
155 }
156 @property (nonatomic) WindowOpenDisposition dispositionDetected;
157 @end
158
159 @implementation BookmarkBarControllerOpenAllPong
160 @synthesize dispositionDetected = dispositionDetected_;
161
162 // Intercede for the openAll:disposition: method.
163 - (void)openAll:(const BookmarkNode*)node
164     disposition:(WindowOpenDisposition)disposition {
165   [self setDispositionDetected:disposition];
166 }
167
168 @end
169
170 // Just like a BookmarkBarController but intercedes when providing
171 // pasteboard drag data.
172 @interface BookmarkBarControllerDragData : BookmarkBarControllerTestable {
173   const BookmarkNode* dragDataNode_;  // Weak
174 }
175 - (void)setDragDataNode:(const BookmarkNode*)node;
176 @end
177
178 @implementation BookmarkBarControllerDragData
179
180 - (id)initWithBrowser:(Browser*)browser
181          initialWidth:(CGFloat)initialWidth
182              delegate:(id<BookmarkBarControllerDelegate>)delegate
183        resizeDelegate:(id<ViewResizer>)resizeDelegate {
184   if ((self = [super initWithBrowser:browser
185                         initialWidth:initialWidth
186                             delegate:delegate
187                       resizeDelegate:resizeDelegate])) {
188     dragDataNode_ = NULL;
189   }
190   return self;
191 }
192
193 - (void)setDragDataNode:(const BookmarkNode*)node {
194   dragDataNode_ = node;
195 }
196
197 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
198   std::vector<const BookmarkNode*> dragDataNodes;
199   if(dragDataNode_) {
200     dragDataNodes.push_back(dragDataNode_);
201   }
202   return dragDataNodes;
203 }
204
205 @end
206
207
208 class FakeTheme : public ui::ThemeProvider {
209  public:
210   FakeTheme(NSColor* color) : color_(color) {}
211   base::scoped_nsobject<NSColor> color_;
212
213   virtual bool UsingSystemTheme() const OVERRIDE {
214     return true;
215   }
216   virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE {
217     return NULL;
218   }
219   virtual SkColor GetColor(int id) const OVERRIDE { return SkColor(); }
220   virtual int GetDisplayProperty(int id) const OVERRIDE {
221     return -1;
222   }
223   virtual bool ShouldUseNativeFrame() const OVERRIDE { return false; }
224   virtual bool HasCustomImage(int id) const OVERRIDE { return false; }
225   virtual base::RefCountedMemory* GetRawData(
226       int id,
227       ui::ScaleFactor scale_factor) const OVERRIDE {
228     return NULL;
229   }
230   virtual NSImage* GetNSImageNamed(int id) const OVERRIDE {
231     return nil;
232   }
233   virtual NSColor* GetNSImageColorNamed(int id) const OVERRIDE {
234     return nil;
235   }
236   virtual NSColor* GetNSColor(int id) const OVERRIDE {
237     return color_.get();
238   }
239   virtual NSColor* GetNSColorTint(int id) const OVERRIDE {
240     return nil;
241   }
242   virtual NSGradient* GetNSGradient(int id) const OVERRIDE {
243     return nil;
244   }
245 };
246
247
248 @interface FakeDragInfo : NSObject {
249  @public
250   NSPoint dropLocation_;
251   NSDragOperation sourceMask_;
252 }
253 @property (nonatomic, assign) NSPoint dropLocation;
254 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
255 @end
256
257 @implementation FakeDragInfo
258
259 @synthesize dropLocation = dropLocation_;
260
261 - (id)init {
262   if ((self = [super init])) {
263     dropLocation_ = NSZeroPoint;
264     sourceMask_ = NSDragOperationMove;
265   }
266   return self;
267 }
268
269 // NSDraggingInfo protocol functions.
270
271 - (id)draggingPasteboard {
272   return self;
273 }
274
275 - (id)draggingSource {
276   return self;
277 }
278
279 - (NSDragOperation)draggingSourceOperationMask {
280   return sourceMask_;
281 }
282
283 - (NSPoint)draggingLocation {
284   return dropLocation_;
285 }
286
287 // Other functions.
288
289 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
290   sourceMask_ = mask;
291 }
292
293 @end
294
295
296 namespace {
297
298 class BookmarkBarControllerTestBase : public CocoaProfileTest {
299  public:
300   base::scoped_nsobject<NSView> parent_view_;
301   base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
302
303   virtual void SetUp() {
304     CocoaProfileTest::SetUp();
305     ASSERT_TRUE(profile());
306
307     base::FilePath extension_dir;
308     static_cast<extensions::TestExtensionSystem*>(
309         extensions::ExtensionSystem::Get(profile()))->
310         CreateExtensionService(
311             CommandLine::ForCurrentProcess(),
312             extension_dir, false);
313     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
314     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
315     parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
316     [parent_view_ setHidden:YES];
317   }
318
319   void InstallAndToggleBar(BookmarkBarController* bar) {
320     // Force loading of the nib.
321     [bar view];
322     // Awkwardness to look like we've been installed.
323     for (NSView* subView in [parent_view_ subviews])
324       [subView removeFromSuperview];
325     [parent_view_ addSubview:[bar view]];
326     NSRect frame = [[[bar view] superview] frame];
327     frame.origin.y = 100;
328     [[[bar view] superview] setFrame:frame];
329
330     // Make sure it's on in a window so viewDidMoveToWindow is called
331     NSView* contentView = [test_window() contentView];
332     if (![parent_view_ isDescendantOf:contentView])
333       [contentView addSubview:parent_view_];
334
335     // Make sure it's open so certain things aren't no-ops.
336     [bar updateState:BookmarkBar::SHOW
337           changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
338   }
339 };
340
341 class BookmarkBarControllerTest : public BookmarkBarControllerTestBase {
342  public:
343   base::scoped_nsobject<BookmarkBarControllerNoOpen> bar_;
344
345   virtual void SetUp() OVERRIDE {
346     BookmarkBarControllerTestBase::SetUp();
347     ASSERT_TRUE(browser());
348     AddCommandLineSwitches();
349
350     bar_.reset(
351       [[BookmarkBarControllerNoOpen alloc]
352           initWithBrowser:browser()
353              initialWidth:NSWidth([parent_view_ frame])
354                  delegate:nil
355            resizeDelegate:resizeDelegate_.get()]);
356
357     InstallAndToggleBar(bar_.get());
358   }
359
360   virtual void AddCommandLineSwitches() {}
361
362   BookmarkBarControllerNoOpen* noOpenBar() {
363     return (BookmarkBarControllerNoOpen*)bar_.get();
364   }
365 };
366
367 TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) {
368   [bar_ updateState:BookmarkBar::SHOW
369          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
370   EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
371   EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
372   EXPECT_TRUE([bar_ isVisible]);
373   EXPECT_FALSE([bar_ isAnimationRunning]);
374   EXPECT_FALSE([[bar_ view] isHidden]);
375   EXPECT_GT([resizeDelegate_ height], 0);
376   EXPECT_GT([[bar_ view] frame].size.height, 0);
377 }
378
379 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarFalse) {
380   [bar_ updateState:BookmarkBar::HIDDEN
381          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
382   EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
383   EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
384   EXPECT_FALSE([bar_ isVisible]);
385   EXPECT_FALSE([bar_ isAnimationRunning]);
386   EXPECT_TRUE([[bar_ view] isHidden]);
387   EXPECT_EQ(0, [resizeDelegate_ height]);
388   EXPECT_EQ(0, [[bar_ view] frame].size.height);
389 }
390
391 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarTrueButDisabled) {
392   [bar_ setBookmarkBarEnabled:NO];
393   [bar_ updateState:BookmarkBar::SHOW
394          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
395   EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
396   EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
397   EXPECT_FALSE([bar_ isVisible]);
398   EXPECT_FALSE([bar_ isAnimationRunning]);
399   EXPECT_TRUE([[bar_ view] isHidden]);
400   EXPECT_EQ(0, [resizeDelegate_ height]);
401   EXPECT_EQ(0, [[bar_ view] frame].size.height);
402 }
403
404 TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) {
405   [bar_ updateState:BookmarkBar::DETACHED
406          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
407   EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
408   EXPECT_TRUE([bar_ isInState:BookmarkBar::DETACHED]);
409   EXPECT_TRUE([bar_ isVisible]);
410   EXPECT_FALSE([bar_ isAnimationRunning]);
411   EXPECT_FALSE([[bar_ view] isHidden]);
412   EXPECT_GT([resizeDelegate_ height], 0);
413   EXPECT_GT([[bar_ view] frame].size.height, 0);
414
415   // Make sure no buttons fall off the bar, either now or when resized
416   // bigger or smaller.
417   CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 };
418   CGFloat previousX = 0.0;
419   for (unsigned x = 0; x < arraysize(sizes); x++) {
420     // Confirm the buttons moved from the last check (which may be
421     // init but that's fine).
422     CGFloat newX = [[bar_ offTheSideButton] frame].origin.x;
423     EXPECT_NE(previousX, newX);
424     previousX = newX;
425
426     // Confirm the buttons have a reasonable bounds. Recall that |-frame|
427     // returns rectangles in the superview's coordinates.
428     NSRect buttonViewFrame =
429         [[bar_ buttonView] convertRect:[[bar_ buttonView] frame]
430                               fromView:[[bar_ buttonView] superview]];
431     EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]);
432     EXPECT_TRUE(NSContainsRect(buttonViewFrame,
433                                [[bar_ offTheSideButton] frame]));
434     EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]);
435     EXPECT_TRUE(NSContainsRect(buttonViewFrame,
436                                [[bar_ otherBookmarksButton] frame]));
437
438     // Now move them implicitly.
439     // We confirm FrameChangeNotification works in the next unit test;
440     // we simply assume it works here to resize or reposition the
441     // buttons above.
442     NSRect frame = [[bar_ view] frame];
443     frame.size.width += sizes[x];
444     [[bar_ view] setFrame:frame];
445   }
446 }
447
448 // Test whether |-updateState:...| sets currentState as expected. Make
449 // sure things don't crash.
450 TEST_F(BookmarkBarControllerTest, StateChanges) {
451   // First, go in one-at-a-time cycle.
452   [bar_ updateState:BookmarkBar::HIDDEN
453          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
454   EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
455   EXPECT_FALSE([bar_ isVisible]);
456   EXPECT_FALSE([bar_ isAnimationRunning]);
457
458   [bar_ updateState:BookmarkBar::SHOW
459          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
460   EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
461   EXPECT_TRUE([bar_ isVisible]);
462   EXPECT_FALSE([bar_ isAnimationRunning]);
463
464   [bar_ updateState:BookmarkBar::DETACHED
465          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
466   EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
467   EXPECT_TRUE([bar_ isVisible]);
468   EXPECT_FALSE([bar_ isAnimationRunning]);
469
470   // Now try some "jumps".
471   for (int i = 0; i < 2; i++) {
472   [bar_ updateState:BookmarkBar::HIDDEN
473          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
474     EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
475     EXPECT_FALSE([bar_ isVisible]);
476     EXPECT_FALSE([bar_ isAnimationRunning]);
477
478     [bar_ updateState:BookmarkBar::SHOW
479            changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
480     EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
481     EXPECT_TRUE([bar_ isVisible]);
482     EXPECT_FALSE([bar_ isAnimationRunning]);
483   }
484
485   // Now try some "jumps".
486   for (int i = 0; i < 2; i++) {
487     [bar_ updateState:BookmarkBar::SHOW
488            changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
489     EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
490     EXPECT_TRUE([bar_ isVisible]);
491     EXPECT_FALSE([bar_ isAnimationRunning]);
492
493     [bar_ updateState:BookmarkBar::DETACHED
494            changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
495     EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
496     EXPECT_TRUE([bar_ isVisible]);
497     EXPECT_FALSE([bar_ isAnimationRunning]);
498   }
499 }
500
501 // Make sure we're watching for frame change notifications.
502 TEST_F(BookmarkBarControllerTest, FrameChangeNotification) {
503   base::scoped_nsobject<BookmarkBarControllerTogglePong> bar;
504   bar.reset(
505     [[BookmarkBarControllerTogglePong alloc]
506           initWithBrowser:browser()
507              initialWidth:100  // arbitrary
508                  delegate:nil
509            resizeDelegate:resizeDelegate_.get()]);
510   InstallAndToggleBar(bar.get());
511
512   // Send a frame did change notification for the pong's view.
513   [[NSNotificationCenter defaultCenter]
514     postNotificationName:NSViewFrameDidChangeNotification
515                   object:[bar view]];
516
517   EXPECT_GT([bar toggles], 0);
518 }
519
520 // Confirm our "no items" container goes away when we add the 1st
521 // bookmark, and comes back when we delete the bookmark.
522 TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) {
523   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
524   const BookmarkNode* bar = model->bookmark_bar_node();
525
526   [bar_ loaded:model];
527   BookmarkBarView* view = [bar_ buttonView];
528   DCHECK(view);
529   NSView* noItemContainer = [view noItemContainer];
530   DCHECK(noItemContainer);
531
532   EXPECT_FALSE([noItemContainer isHidden]);
533   const BookmarkNode* node = model->AddURL(bar, bar->child_count(),
534                                            ASCIIToUTF16("title"),
535                                            GURL("http://www.google.com"));
536   EXPECT_TRUE([noItemContainer isHidden]);
537   model->Remove(bar, bar->GetIndexOf(node));
538   EXPECT_FALSE([noItemContainer isHidden]);
539
540   // Now try it using a bookmark from the Other Bookmarks.
541   const BookmarkNode* otherBookmarks = model->other_node();
542   node = model->AddURL(otherBookmarks, otherBookmarks->child_count(),
543                        ASCIIToUTF16("TheOther"),
544                        GURL("http://www.other.com"));
545   EXPECT_FALSE([noItemContainer isHidden]);
546   // Move it from Other Bookmarks to the bar.
547   model->Move(node, bar, 0);
548   EXPECT_TRUE([noItemContainer isHidden]);
549   // Move it back to Other Bookmarks from the bar.
550   model->Move(node, otherBookmarks, 0);
551   EXPECT_FALSE([noItemContainer isHidden]);
552 }
553
554 // Confirm off the side button only enabled when reasonable.
555 TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
556   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
557
558   [bar_ loaded:model];
559   EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
560
561   for (int i = 0; i < 2; i++) {
562     bookmarks::AddIfNotBookmarked(
563         model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
564     EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
565   }
566
567   const BookmarkNode* parent = model->bookmark_bar_node();
568   for (int i = 0; i < 20; i++) {
569     model->AddURL(parent, parent->child_count(),
570                   ASCIIToUTF16("super duper wide title"),
571                   GURL("http://superfriends.hall-of-justice.edu"));
572   }
573   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
574
575   // Open the "off the side" and start deleting nodes.  Make sure
576   // deletion of the last node in "off the side" causes the folder to
577   // close.
578   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
579   NSButton* offTheSideButton = [bar_ offTheSideButton];
580   // Open "off the side" menu.
581   [bar_ openOffTheSideFolderFromButton:offTheSideButton];
582   BookmarkBarFolderController* bbfc = [bar_ folderController];
583   EXPECT_TRUE(bbfc);
584   [bbfc setIgnoreAnimations:YES];
585   while (!parent->empty()) {
586     // We've completed the job so we're done.
587     if ([bar_ offTheSideButtonIsHidden])
588       break;
589     // Delete the last button.
590     model->Remove(parent, parent->child_count() - 1);
591     // If last one make sure the menu is closed and the button is hidden.
592     // Else make sure menu stays open.
593     if ([bar_ offTheSideButtonIsHidden]) {
594       EXPECT_FALSE([bar_ folderController]);
595     } else {
596       EXPECT_TRUE([bar_ folderController]);
597     }
598   }
599 }
600
601 // http://crbug.com/46175 is a crash when deleting bookmarks from the
602 // off-the-side menu while it is open.  This test tries to bang hard
603 // in this area to reproduce the crash.
604 TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) {
605   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
606   [bar_ loaded:model];
607
608   // Add a lot of bookmarks (per the bug).
609   const BookmarkNode* parent = model->bookmark_bar_node();
610   for (int i = 0; i < 100; i++) {
611     std::ostringstream title;
612     title << "super duper wide title " << i;
613     model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()),
614                   GURL("http://superfriends.hall-of-justice.edu"));
615   }
616   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
617
618   // Open "off the side" menu.
619   NSButton* offTheSideButton = [bar_ offTheSideButton];
620   [bar_ openOffTheSideFolderFromButton:offTheSideButton];
621   BookmarkBarFolderController* bbfc = [bar_ folderController];
622   EXPECT_TRUE(bbfc);
623   [bbfc setIgnoreAnimations:YES];
624
625   // Start deleting items; try and delete randomish ones in case it
626   // makes a difference.
627   int indices[] = { 2, 4, 5, 1, 7, 9, 2, 0, 10, 9 };
628   while (!parent->empty()) {
629     for (unsigned int i = 0; i < arraysize(indices); i++) {
630       if (indices[i] < parent->child_count()) {
631         // First we mouse-enter the button to make things harder.
632         NSArray* buttons = [bbfc buttons];
633         for (BookmarkButton* button in buttons) {
634           if ([button bookmarkNode] == parent->GetChild(indices[i])) {
635             [bbfc mouseEnteredButton:button event:nil];
636             break;
637           }
638         }
639         // Then we remove the node.  This triggers the button to get
640         // deleted.
641         model->Remove(parent, indices[i]);
642         // Force visual update which is otherwise delayed.
643         [[bbfc window] displayIfNeeded];
644       }
645     }
646   }
647 }
648
649 // Test whether |-dragShouldLockBarVisibility| returns NO iff the bar is
650 // detached.
651 TEST_F(BookmarkBarControllerTest, TestDragShouldLockBarVisibility) {
652   [bar_ updateState:BookmarkBar::HIDDEN
653          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
654   EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
655
656   [bar_ updateState:BookmarkBar::SHOW
657          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
658   EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
659
660   [bar_ updateState:BookmarkBar::DETACHED
661          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
662   EXPECT_FALSE([bar_ dragShouldLockBarVisibility]);
663 }
664
665 TEST_F(BookmarkBarControllerTest, TagMap) {
666   int64 ids[] = { 1, 3, 4, 40, 400, 4000, 800000000, 2, 123456789 };
667   std::vector<int32> tags;
668
669   // Generate some tags
670   for (unsigned int i = 0; i < arraysize(ids); i++) {
671     tags.push_back([bar_ menuTagFromNodeId:ids[i]]);
672   }
673
674   // Confirm reverse mapping.
675   for (unsigned int i = 0; i < arraysize(ids); i++) {
676     EXPECT_EQ(ids[i], [bar_ nodeIdFromMenuTag:tags[i]]);
677   }
678
679   // Confirm uniqueness.
680   std::sort(tags.begin(), tags.end());
681   for (unsigned int i=0; i<(tags.size()-1); i++) {
682     EXPECT_NE(tags[i], tags[i+1]);
683   }
684 }
685
686 TEST_F(BookmarkBarControllerTest, MenuForFolderNode) {
687   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
688
689   // First make sure something (e.g. "(empty)" string) is always present.
690   NSMenu* menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
691   EXPECT_GT([menu numberOfItems], 0);
692
693   // Test two bookmarks.
694   GURL gurl("http://www.foo.com");
695   bookmarks::AddIfNotBookmarked(model, gurl, ASCIIToUTF16("small"));
696   bookmarks::AddIfNotBookmarked(
697       model, GURL("http://www.cnn.com"), ASCIIToUTF16("bigger title"));
698   menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
699   EXPECT_EQ([menu numberOfItems], 2);
700   NSMenuItem *item = [menu itemWithTitle:@"bigger title"];
701   EXPECT_TRUE(item);
702   item = [menu itemWithTitle:@"small"];
703   EXPECT_TRUE(item);
704   if (item) {
705     int64 tag = [bar_ nodeIdFromMenuTag:[item tag]];
706     const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, tag);
707     EXPECT_TRUE(node);
708     EXPECT_EQ(gurl, node->url());
709   }
710
711   // Test with an actual folder as well
712   const BookmarkNode* parent = model->bookmark_bar_node();
713   const BookmarkNode* folder = model->AddFolder(parent,
714                                                 parent->child_count(),
715                                                 ASCIIToUTF16("folder"));
716   model->AddURL(folder, folder->child_count(),
717                 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
718   model->AddURL(folder, folder->child_count(),
719                 ASCIIToUTF16("f2"), GURL("http://framma-lamma-ding-dong.com"));
720   menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
721   EXPECT_EQ([menu numberOfItems], 3);
722
723   item = [menu itemWithTitle:@"folder"];
724   EXPECT_TRUE(item);
725   EXPECT_TRUE([item hasSubmenu]);
726   NSMenu *submenu = [item submenu];
727   EXPECT_TRUE(submenu);
728   EXPECT_EQ(2, [submenu numberOfItems]);
729   EXPECT_TRUE([submenu itemWithTitle:@"f1"]);
730   EXPECT_TRUE([submenu itemWithTitle:@"f2"]);
731 }
732
733 // Confirm openBookmark: forwards the request to the controller's delegate
734 TEST_F(BookmarkBarControllerTest, OpenBookmark) {
735   GURL gurl("http://walla.walla.ding.dong.com");
736   scoped_ptr<BookmarkNode> node(new BookmarkNode(gurl));
737
738   base::scoped_nsobject<BookmarkButtonCell> cell(
739       [[BookmarkButtonCell alloc] init]);
740   [cell setBookmarkNode:node.get()];
741   base::scoped_nsobject<BookmarkButton> button([[BookmarkButton alloc] init]);
742   [button setCell:cell.get()];
743   [cell setRepresentedObject:[NSValue valueWithPointer:node.get()]];
744
745   [bar_ openBookmark:button];
746   EXPECT_EQ(noOpenBar()->urls_[0], node->url());
747   EXPECT_EQ(noOpenBar()->dispositions_[0], CURRENT_TAB);
748 }
749
750 TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) {
751   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
752   NSView* buttonView = [bar_ buttonView];
753   EXPECT_EQ(0U, [[bar_ buttons] count]);
754   unsigned int initial_subview_count = [[buttonView subviews] count];
755
756   // Make sure a redundant call doesn't choke
757   [bar_ clearBookmarkBar];
758   EXPECT_EQ(0U, [[bar_ buttons] count]);
759   EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
760
761   GURL gurl1("http://superfriends.hall-of-justice.edu");
762   // Short titles increase the chances of this test succeeding if the view is
763   // narrow.
764   // TODO(viettrungluu): make the test independent of window/view size, font
765   // metrics, button size and spacing, and everything else.
766   base::string16 title1(ASCIIToUTF16("x"));
767   bookmarks::AddIfNotBookmarked(model, gurl1, title1);
768   EXPECT_EQ(1U, [[bar_ buttons] count]);
769   EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
770
771   GURL gurl2("http://legion-of-doom.gov");
772   base::string16 title2(ASCIIToUTF16("y"));
773   bookmarks::AddIfNotBookmarked(model, gurl2, title2);
774   EXPECT_EQ(2U, [[bar_ buttons] count]);
775   EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
776
777   for (int i = 0; i < 3; i++) {
778     bookmarks::RemoveAllBookmarks(model, gurl2);
779     EXPECT_EQ(1U, [[bar_ buttons] count]);
780     EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
781
782     // and bring it back
783     bookmarks::AddIfNotBookmarked(model, gurl2, title2);
784     EXPECT_EQ(2U, [[bar_ buttons] count]);
785     EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
786   }
787
788   [bar_ clearBookmarkBar];
789   EXPECT_EQ(0U, [[bar_ buttons] count]);
790   EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
791
792   // Explicit test of loaded: since this is a convenient spot
793   [bar_ loaded:model];
794   EXPECT_EQ(2U, [[bar_ buttons] count]);
795   EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
796 }
797
798 // Make sure we don't create too many buttons; we only really need
799 // ones that will be visible.
800 TEST_F(BookmarkBarControllerTest, TestButtonLimits) {
801   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
802   EXPECT_EQ(0U, [[bar_ buttons] count]);
803   // Add one; make sure we see it.
804   const BookmarkNode* parent = model->bookmark_bar_node();
805   model->AddURL(parent, parent->child_count(),
806                 ASCIIToUTF16("title"), GURL("http://www.google.com"));
807   EXPECT_EQ(1U, [[bar_ buttons] count]);
808
809   // Add 30 which we expect to be 'too many'.  Make sure we don't see
810   // 30 buttons.
811   model->Remove(parent, 0);
812   EXPECT_EQ(0U, [[bar_ buttons] count]);
813   for (int i=0; i<30; i++) {
814     model->AddURL(parent, parent->child_count(),
815                   ASCIIToUTF16("title"), GURL("http://www.google.com"));
816   }
817   int count = [[bar_ buttons] count];
818   EXPECT_LT(count, 30L);
819
820   // Add 10 more (to the front of the list so the on-screen buttons
821   // would change) and make sure the count stays the same.
822   for (int i=0; i<10; i++) {
823     model->AddURL(parent, 0,  /* index is 0, so front, not end */
824                   ASCIIToUTF16("title"), GURL("http://www.google.com"));
825   }
826
827   // Finally, grow the view and make sure the button count goes up.
828   NSRect frame = [[bar_ view] frame];
829   frame.size.width += 600;
830   [[bar_ view] setFrame:frame];
831   int finalcount = [[bar_ buttons] count];
832   EXPECT_GT(finalcount, count);
833 }
834
835 // Make sure that each button we add marches to the right and does not
836 // overlap with the previous one.
837 TEST_F(BookmarkBarControllerTest, TestButtonMarch) {
838   base::scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]);
839
840   CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 };
841   for (unsigned int i = 0; i < arraysize(widths); i++) {
842     NSCell* cell = [[CellWithDesiredSize alloc]
843                      initTextCell:@"foo"
844                       desiredSize:NSMakeSize(widths[i], 30)];
845     [cells addObject:cell];
846     [cell release];
847   }
848
849   int x_offset = 0;
850   CGFloat x_end = x_offset;  // end of the previous button
851   for (unsigned int i = 0; i < arraysize(widths); i++) {
852     NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i]
853                                             xOffset:&x_offset];
854     EXPECT_GE(r.origin.x, x_end);
855     x_end = NSMaxX(r);
856   }
857 }
858
859 TEST_F(BookmarkBarControllerTest, CheckForGrowth) {
860   WithNoAnimation at_all; // Turn off Cocoa auto animation in this scope.
861   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
862   GURL gurl1("http://www.google.com");
863   base::string16 title1(ASCIIToUTF16("x"));
864   bookmarks::AddIfNotBookmarked(model, gurl1, title1);
865
866   GURL gurl2("http://www.google.com/blah");
867   base::string16 title2(ASCIIToUTF16("y"));
868   bookmarks::AddIfNotBookmarked(model, gurl2, title2);
869
870   EXPECT_EQ(2U, [[bar_ buttons] count]);
871   CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width;
872   CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x;
873
874   NSButton* first = [[bar_ buttons] objectAtIndex:0];
875   [[first cell] setTitle:@"This is a really big title; watch out mom!"];
876   [bar_ checkForBookmarkButtonGrowth:first];
877
878   // Make sure the 1st button is now wider, the 2nd one is moved over,
879   // and they don't overlap.
880   NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame];
881   NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame];
882   EXPECT_GT(frame_1.size.width, width_1);
883   EXPECT_GT(frame_2.origin.x, x_2);
884   EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width);
885 }
886
887 TEST_F(BookmarkBarControllerTest, DeleteBookmark) {
888   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
889
890   const char* urls[] = { "https://secret.url.com",
891                          "http://super.duper.web.site.for.doodz.gov",
892                          "http://www.foo-bar-baz.com/" };
893   const BookmarkNode* parent = model->bookmark_bar_node();
894   for (unsigned int i = 0; i < arraysize(urls); i++) {
895     model->AddURL(parent, parent->child_count(),
896                   ASCIIToUTF16("title"), GURL(urls[i]));
897   }
898   EXPECT_EQ(3, parent->child_count());
899   const BookmarkNode* middle_node = parent->GetChild(1);
900   model->Remove(middle_node->parent(),
901                 middle_node->parent()->GetIndexOf(middle_node));
902
903   EXPECT_EQ(2, parent->child_count());
904   EXPECT_EQ(parent->GetChild(0)->url(), GURL(urls[0]));
905   // node 2 moved into spot 1
906   EXPECT_EQ(parent->GetChild(1)->url(), GURL(urls[2]));
907 }
908
909 // TODO(jrg): write a test to confirm that nodeFaviconLoaded calls
910 // checkForBookmarkButtonGrowth:.
911
912 TEST_F(BookmarkBarControllerTest, Cell) {
913   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
914   [bar_ loaded:model];
915
916   const BookmarkNode* parent = model->bookmark_bar_node();
917   model->AddURL(parent, parent->child_count(),
918                 ASCIIToUTF16("supertitle"),
919                 GURL("http://superfriends.hall-of-justice.edu"));
920   const BookmarkNode* node = parent->GetChild(0);
921
922   NSCell* cell = [bar_ cellForBookmarkNode:node];
923   EXPECT_TRUE(cell);
924   EXPECT_NSEQ(@"supertitle", [cell title]);
925   EXPECT_EQ(node, [[cell representedObject] pointerValue]);
926   EXPECT_TRUE([cell menu]);
927
928   // Empty cells still have a menu.
929   cell = [bar_ cellForBookmarkNode:nil];
930   EXPECT_TRUE([cell menu]);
931   // Even empty cells have a title (of "(empty)")
932   EXPECT_TRUE([cell title]);
933
934   // cell is autoreleased; no need to release here
935 }
936
937 // Test drawing, mostly to ensure nothing leaks or crashes.
938 TEST_F(BookmarkBarControllerTest, Display) {
939   [[bar_ view] display];
940 }
941
942 // Test that middle clicking on a bookmark button results in an open action,
943 // except for offTheSideButton, as it just opens its folder menu.
944 TEST_F(BookmarkBarControllerTest, MiddleClick) {
945   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
946   GURL gurl1("http://www.google.com/");
947   base::string16 title1(ASCIIToUTF16("x"));
948   bookmarks::AddIfNotBookmarked(model, gurl1, title1);
949
950   EXPECT_EQ(1U, [[bar_ buttons] count]);
951   NSButton* first = [[bar_ buttons] objectAtIndex:0];
952   EXPECT_TRUE(first);
953
954   [first otherMouseUp:
955       cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
956   EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
957
958   // Test for offTheSideButton.
959   // Add more bookmarks so that offTheSideButton is visible.
960   const BookmarkNode* parent = model->bookmark_bar_node();
961   for (int i = 0; i < 20; i++) {
962     model->AddURL(parent, parent->child_count(),
963                   ASCIIToUTF16("super duper wide title"),
964                   GURL("http://superfriends.hall-of-justice.edu"));
965   }
966   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
967
968   NSButton* offTheSideButton = [bar_ offTheSideButton];
969   EXPECT_TRUE(offTheSideButton);
970   [offTheSideButton otherMouseUp:
971       cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
972
973   // Middle click on offTheSideButton should not open any bookmarks under it,
974   // therefore urls size should still be 1.
975   EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
976
977   // Check that folderController should not be NULL since offTheSideButton
978   // folder is currently open.
979   BookmarkBarFolderController* bbfc = [bar_ folderController];
980   EXPECT_TRUE(bbfc);
981   EXPECT_TRUE([bbfc parentButton] == offTheSideButton);
982
983   // Middle clicking again on it should close the folder.
984   [offTheSideButton otherMouseUp:
985       cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
986   bbfc = [bar_ folderController];
987   EXPECT_FALSE(bbfc);
988 }
989
990 TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) {
991   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
992   [bar_ loaded:model];
993   EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]);
994 }
995
996 TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) {
997   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
998
999   const BookmarkNode* parent = model->bookmark_bar_node();
1000   model->AddURL(parent, parent->child_count(),
1001                 ASCIIToUTF16("title"), GURL("http://one.com"));
1002
1003   [bar_ loaded:model];
1004   EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]);
1005 }
1006
1007 TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) {
1008   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1009
1010   const BookmarkNode* parent = model->bookmark_bar_node();
1011   model->AddURL(parent, parent->child_count(),
1012                 ASCIIToUTF16("title"), GURL("http://one.com"));
1013
1014   [bar_ loaded:model];
1015
1016   // Make sure the internal bookmark button also is the correct height.
1017   NSArray* buttons = [bar_ buttons];
1018   EXPECT_GT([buttons count], 0u);
1019   for (NSButton* button in buttons) {
1020     EXPECT_FLOAT_EQ(
1021         (chrome::kBookmarkBarHeight + bookmarks::kVisualHeightOffset) -
1022             2 * bookmarks::kBookmarkVerticalPadding,
1023         [button frame].size.height);
1024   }
1025 }
1026
1027 TEST_F(BookmarkBarControllerTest, DropBookmarks) {
1028   const char* urls[] = {
1029     "http://qwantz.com",
1030     "http://xkcd.com",
1031     "javascript:alert('lolwut')",
1032     "file://localhost/tmp/local-file.txt"  // As if dragged from the desktop.
1033   };
1034   const char* titles[] = {
1035     "Philosophoraptor",
1036     "Can't draw",
1037     "Inspiration",
1038     "Frum stuf"
1039   };
1040   EXPECT_EQ(arraysize(urls), arraysize(titles));
1041
1042   NSMutableArray* nsurls = [NSMutableArray array];
1043   NSMutableArray* nstitles = [NSMutableArray array];
1044   for (size_t i = 0; i < arraysize(urls); ++i) {
1045     [nsurls addObject:base::SysUTF8ToNSString(urls[i])];
1046     [nstitles addObject:base::SysUTF8ToNSString(titles[i])];
1047   }
1048
1049   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1050   const BookmarkNode* parent = model->bookmark_bar_node();
1051   [bar_ addURLs:nsurls withTitles:nstitles at:NSZeroPoint];
1052   EXPECT_EQ(4, parent->child_count());
1053   for (int i = 0; i < parent->child_count(); ++i) {
1054     GURL gurl = parent->GetChild(i)->url();
1055     if (gurl.scheme() == "http" ||
1056         gurl.scheme() == "javascript") {
1057       EXPECT_EQ(parent->GetChild(i)->url(), GURL(urls[i]));
1058     } else {
1059       // Be flexible if the scheme needed to be added.
1060       std::string gurl_string = gurl.spec();
1061       std::string my_string = parent->GetChild(i)->url().spec();
1062       EXPECT_NE(gurl_string.find(my_string), std::string::npos);
1063     }
1064     EXPECT_EQ(parent->GetChild(i)->GetTitle(), ASCIIToUTF16(titles[i]));
1065   }
1066 }
1067
1068 TEST_F(BookmarkBarControllerTest, TestDragButton) {
1069   WithNoAnimation at_all;
1070   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1071
1072   GURL gurls[] = { GURL("http://www.google.com/a"),
1073                    GURL("http://www.google.com/b"),
1074                    GURL("http://www.google.com/c") };
1075   base::string16 titles[] = { ASCIIToUTF16("a"),
1076                               ASCIIToUTF16("b"),
1077                               ASCIIToUTF16("c") };
1078   for (unsigned i = 0; i < arraysize(titles); i++)
1079     bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1080
1081   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1082   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1083
1084   [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1085                 to:NSZeroPoint
1086               copy:NO];
1087   EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1088   // Make sure a 'copy' did not happen.
1089   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1090
1091   [bar_ dragButton:[[bar_ buttons] objectAtIndex:1]
1092                 to:NSMakePoint(1000, 0)
1093               copy:NO];
1094   EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1095   EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1096   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1097   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1098
1099   // A drop of the 1st between the next 2.
1100   CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1101   x += [[bar_ view] frame].origin.x;
1102   [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1103                 to:NSMakePoint(x, 0)
1104               copy:NO];
1105   EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:0] title]);
1106   EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:1] title]);
1107   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1108   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1109
1110   // A drop on a non-folder button.  (Shouldn't try and go in it.)
1111   x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1112   x += [[bar_ view] frame].origin.x;
1113   [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1114                 to:NSMakePoint(x, 0)
1115               copy:NO];
1116   EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1117
1118   // A drop on a folder button.
1119   const BookmarkNode* folder = model->AddFolder(
1120       model->bookmark_bar_node(), 0, ASCIIToUTF16("awesome folder"));
1121   DCHECK(folder);
1122   model->AddURL(folder, 0, ASCIIToUTF16("already"),
1123                 GURL("http://www.google.com"));
1124   EXPECT_EQ(arraysize(titles) + 1, [[bar_ buttons] count]);
1125   EXPECT_EQ(1, folder->child_count());
1126   x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1127   x += [[bar_ view] frame].origin.x;
1128   base::string16 title =
1129       [[[bar_ buttons] objectAtIndex:2] bookmarkNode]->GetTitle();
1130   [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1131                 to:NSMakePoint(x, 0)
1132               copy:NO];
1133   // Gone from the bar
1134   EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1135   // In the folder
1136   EXPECT_EQ(2, folder->child_count());
1137   // At the end
1138   EXPECT_EQ(title, folder->GetChild(1)->GetTitle());
1139 }
1140
1141 TEST_F(BookmarkBarControllerTest, TestCopyButton) {
1142   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1143
1144   GURL gurls[] = { GURL("http://www.google.com/a"),
1145                    GURL("http://www.google.com/b"),
1146                    GURL("http://www.google.com/c") };
1147   base::string16 titles[] = { ASCIIToUTF16("a"),
1148                               ASCIIToUTF16("b"),
1149                               ASCIIToUTF16("c") };
1150   for (unsigned i = 0; i < arraysize(titles); i++)
1151     bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1152
1153   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1154   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1155
1156   // Drag 'a' between 'b' and 'c'.
1157   CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1158   x += [[bar_ view] frame].origin.x;
1159   [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1160                 to:NSMakePoint(x, 0)
1161               copy:YES];
1162   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1163   EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1164   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1165   EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:3] title]);
1166   EXPECT_EQ([[bar_ buttons] count], 4U);
1167 }
1168
1169 // Fake a theme with colored text.  Apply it and make sure bookmark
1170 // buttons have the same colored text.  Repeat more than once.
1171 TEST_F(BookmarkBarControllerTest, TestThemedButton) {
1172   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1173   bookmarks::AddIfNotBookmarked(
1174       model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
1175   BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1176   EXPECT_TRUE(button);
1177
1178   NSArray* colors = [NSArray arrayWithObjects:[NSColor redColor],
1179                                               [NSColor blueColor],
1180                                               nil];
1181   for (NSColor* color in colors) {
1182     FakeTheme theme(color);
1183     [bar_ updateTheme:&theme];
1184     NSAttributedString* astr = [button attributedTitle];
1185     EXPECT_TRUE(astr);
1186     EXPECT_NSEQ(@"small", [astr string]);
1187     // Pick a char in the middle to test (index 3)
1188     NSDictionary* attributes = [astr attributesAtIndex:3 effectiveRange:NULL];
1189     NSColor* newColor =
1190         [attributes objectForKey:NSForegroundColorAttributeName];
1191     EXPECT_NSEQ(newColor, color);
1192   }
1193 }
1194
1195 // Test that delegates and targets of buttons are cleared on dealloc.
1196 TEST_F(BookmarkBarControllerTest, TestClearOnDealloc) {
1197   // Make some bookmark buttons.
1198   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1199   GURL gurls[] = { GURL("http://www.foo.com/"),
1200                    GURL("http://www.bar.com/"),
1201                    GURL("http://www.baz.com/") };
1202   base::string16 titles[] = { ASCIIToUTF16("a"),
1203                               ASCIIToUTF16("b"),
1204                               ASCIIToUTF16("c") };
1205   for (size_t i = 0; i < arraysize(titles); i++)
1206     bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1207
1208   // Get and retain the buttons so we can examine them after dealloc.
1209   base::scoped_nsobject<NSArray> buttons([[bar_ buttons] retain]);
1210   EXPECT_EQ([buttons count], arraysize(titles));
1211
1212   // Make sure that everything is set.
1213   for (BookmarkButton* button in buttons.get()) {
1214     ASSERT_TRUE([button isKindOfClass:[BookmarkButton class]]);
1215     EXPECT_TRUE([button delegate]);
1216     EXPECT_TRUE([button target]);
1217     EXPECT_TRUE([button action]);
1218   }
1219
1220   // This will dealloc....
1221   bar_.reset();
1222
1223   // Make sure that everything is cleared.
1224   for (BookmarkButton* button in buttons.get()) {
1225     EXPECT_FALSE([button delegate]);
1226     EXPECT_FALSE([button target]);
1227     EXPECT_FALSE([button action]);
1228   }
1229 }
1230
1231 TEST_F(BookmarkBarControllerTest, TestFolders) {
1232   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1233
1234   // Create some folder buttons.
1235   const BookmarkNode* parent = model->bookmark_bar_node();
1236   const BookmarkNode* folder = model->AddFolder(parent,
1237                                                 parent->child_count(),
1238                                                 ASCIIToUTF16("folder"));
1239   model->AddURL(folder, folder->child_count(),
1240                 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
1241   folder = model->AddFolder(parent, parent->child_count(),
1242                             ASCIIToUTF16("empty"));
1243
1244   EXPECT_EQ([[bar_ buttons] count], 2U);
1245
1246   // First confirm mouseEntered does nothing if "menus" aren't active.
1247   NSEvent* event =
1248       cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0);
1249   [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1250   EXPECT_FALSE([bar_ folderController]);
1251
1252   // Make one active.  Entering it is now a no-op.
1253   [bar_ openBookmarkFolderFromButton:[[bar_ buttons] objectAtIndex:0]];
1254   BookmarkBarFolderController* bbfc = [bar_ folderController];
1255   EXPECT_TRUE(bbfc);
1256   [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1257   EXPECT_EQ(bbfc, [bar_ folderController]);
1258
1259   // Enter a different one; a new folderController is active.
1260   [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:1] event:event];
1261   EXPECT_NE(bbfc, [bar_ folderController]);
1262
1263   // Confirm exited is a no-op.
1264   [bar_ mouseExitedButton:[[bar_ buttons] objectAtIndex:1] event:event];
1265   EXPECT_NE(bbfc, [bar_ folderController]);
1266
1267   // Clean up.
1268   [bar_ closeBookmarkFolder:nil];
1269 }
1270
1271 // Verify that the folder menu presentation properly tracks mouse movements
1272 // over the bar. Until there is a click no folder menus should show. After a
1273 // click on a folder folder menus should show until another click on a folder
1274 // button, and a click outside the bar and its folder menus.
1275 TEST_F(BookmarkBarControllerTest, TestFolderButtons) {
1276   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1277   const BookmarkNode* root = model->bookmark_bar_node();
1278   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b 4f:[ 4f1b 4f2b ] ");
1279   test::AddNodesFromModelString(model, root, model_string);
1280
1281   // Validate initial model and that we do not have a folder controller.
1282   std::string actualModelString = test::ModelStringFromNode(root);
1283   EXPECT_EQ(model_string, actualModelString);
1284   EXPECT_FALSE([bar_ folderController]);
1285
1286   // Add a real bookmark so we can click on it.
1287   const BookmarkNode* folder = root->GetChild(3);
1288   model->AddURL(folder, folder->child_count(), ASCIIToUTF16("CLICK ME"),
1289                 GURL("http://www.google.com/"));
1290
1291   // Click on a folder button.
1292   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"4f"];
1293   EXPECT_TRUE(button);
1294   [bar_ openBookmarkFolderFromButton:button];
1295   BookmarkBarFolderController* bbfc = [bar_ folderController];
1296   EXPECT_TRUE(bbfc);
1297
1298   // Make sure a 2nd click on the same button closes things.
1299   [bar_ openBookmarkFolderFromButton:button];
1300   EXPECT_FALSE([bar_ folderController]);
1301
1302   // Next open is a different button.
1303   button = [bar_ buttonWithTitleEqualTo:@"2f"];
1304   EXPECT_TRUE(button);
1305   [bar_ openBookmarkFolderFromButton:button];
1306   EXPECT_TRUE([bar_ folderController]);
1307
1308   // Mouse over a non-folder button and confirm controller has gone away.
1309   button = [bar_ buttonWithTitleEqualTo:@"1b"];
1310   EXPECT_TRUE(button);
1311   NSEvent* event = cocoa_test_event_utils::MouseEventAtPoint([button center],
1312                                                              NSMouseMoved, 0);
1313   [bar_ mouseEnteredButton:button event:event];
1314   EXPECT_FALSE([bar_ folderController]);
1315
1316   // Mouse over the original folder and confirm a new controller.
1317   button = [bar_ buttonWithTitleEqualTo:@"2f"];
1318   EXPECT_TRUE(button);
1319   [bar_ mouseEnteredButton:button event:event];
1320   BookmarkBarFolderController* oldBBFC = [bar_ folderController];
1321   EXPECT_TRUE(oldBBFC);
1322
1323   // 'Jump' over to a different folder and confirm a new controller.
1324   button = [bar_ buttonWithTitleEqualTo:@"4f"];
1325   EXPECT_TRUE(button);
1326   [bar_ mouseEnteredButton:button event:event];
1327   BookmarkBarFolderController* newBBFC = [bar_ folderController];
1328   EXPECT_TRUE(newBBFC);
1329   EXPECT_NE(oldBBFC, newBBFC);
1330 }
1331
1332 // Make sure the "off the side" folder looks like a bookmark folder
1333 // but only contains "off the side" items.
1334 TEST_F(BookmarkBarControllerTest, OffTheSideFolder) {
1335
1336   // It starts hidden.
1337   EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
1338
1339   // Create some buttons.
1340   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1341   const BookmarkNode* parent = model->bookmark_bar_node();
1342   for (int x = 0; x < 30; x++) {
1343     model->AddURL(parent, parent->child_count(),
1344                   ASCIIToUTF16("medium-size-title"),
1345                   GURL("http://framma-lamma.com"));
1346   }
1347   // Add a couple more so we can delete one and make sure its button goes away.
1348   model->AddURL(parent, parent->child_count(),
1349                 ASCIIToUTF16("DELETE_ME"), GURL("http://ashton-tate.com"));
1350   model->AddURL(parent, parent->child_count(),
1351                 ASCIIToUTF16("medium-size-title"),
1352                 GURL("http://framma-lamma.com"));
1353
1354   // Should no longer be hidden.
1355   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
1356
1357   // Open it; make sure we have a folder controller.
1358   EXPECT_FALSE([bar_ folderController]);
1359   [bar_ openOffTheSideFolderFromButton:[bar_ offTheSideButton]];
1360   BookmarkBarFolderController* bbfc = [bar_ folderController];
1361   EXPECT_TRUE(bbfc);
1362
1363   // Confirm the contents are only buttons which fell off the side by
1364   // making sure that none of the nodes in the off-the-side folder are
1365   // found in bar buttons.  Be careful since not all the bar buttons
1366   // may be currently displayed.
1367   NSArray* folderButtons = [bbfc buttons];
1368   NSArray* barButtons = [bar_ buttons];
1369   for (BookmarkButton* folderButton in folderButtons) {
1370     for (BookmarkButton* barButton in barButtons) {
1371       if ([barButton superview]) {
1372         EXPECT_NE([folderButton bookmarkNode], [barButton bookmarkNode]);
1373       }
1374     }
1375   }
1376
1377   // Delete a bookmark in the off-the-side and verify it's gone.
1378   BookmarkButton* button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1379   EXPECT_TRUE(button);
1380   model->Remove(parent, parent->child_count() - 2);
1381   button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1382   EXPECT_FALSE(button);
1383 }
1384
1385 TEST_F(BookmarkBarControllerTest, EventToExitCheck) {
1386   NSEvent* event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0);
1387   EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1388
1389   BookmarkBarFolderWindow* folderWindow = [[[BookmarkBarFolderWindow alloc]
1390                                              init] autorelease];
1391   [[[bar_ view] window] addChildWindow:folderWindow
1392                                ordered:NSWindowAbove];
1393   event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(1,1),
1394                                                                folderWindow);
1395   EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1396
1397   event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
1398       NSMakePoint(100,100), test_window());
1399   EXPECT_TRUE([bar_ isEventAnExitEvent:event]);
1400
1401   // Many components are arbitrary (e.g. location, keycode).
1402   event = [NSEvent keyEventWithType:NSKeyDown
1403                            location:NSMakePoint(1,1)
1404                       modifierFlags:0
1405                           timestamp:0
1406                        windowNumber:0
1407                             context:nil
1408                          characters:@"x"
1409         charactersIgnoringModifiers:@"x"
1410                           isARepeat:NO
1411                             keyCode:87];
1412   EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1413
1414   [[[bar_ view] window] removeChildWindow:folderWindow];
1415 }
1416
1417 TEST_F(BookmarkBarControllerTest, DropDestination) {
1418   // Make some buttons.
1419   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1420   const BookmarkNode* parent = model->bookmark_bar_node();
1421   model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 1"));
1422   model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 2"));
1423   EXPECT_EQ([[bar_ buttons] count], 2U);
1424
1425   // Confirm "off to left" and "off to right" match nothing.
1426   NSPoint p = NSMakePoint(-1, 2);
1427   EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1428   EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1429   p = NSMakePoint(50000, 10);
1430   EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1431   EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1432
1433   // Confirm "right in the center" (give or take a pixel) is a match,
1434   // and confirm "just barely in the button" is not.  Anything more
1435   // specific seems likely to be tweaked.
1436   CGFloat viewFrameXOffset = [[bar_ view] frame].origin.x;
1437   for (BookmarkButton* button in [bar_ buttons]) {
1438     CGFloat x = NSMidX([button frame]) + viewFrameXOffset;
1439     // Somewhere near the center: a match
1440     EXPECT_EQ(button,
1441               [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x-1, 10)]);
1442     EXPECT_EQ(button,
1443               [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x+1, 10)]);
1444     EXPECT_FALSE([bar_ shouldShowIndicatorShownForPoint:NSMakePoint(x, 10)]);;
1445
1446     // On the very edges: NOT a match
1447     x = NSMinX([button frame]) + viewFrameXOffset;
1448     EXPECT_NE(button,
1449               [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 9)]);
1450     x = NSMaxX([button frame]) + viewFrameXOffset;
1451     EXPECT_NE(button,
1452               [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 11)]);
1453   }
1454 }
1455
1456 TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) {
1457   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1458   [bar_ setStateAnimationsEnabled:YES];
1459   const BookmarkNode* parent = model->bookmark_bar_node();
1460   const BookmarkNode* folder = model->AddFolder(parent,
1461                                                 parent->child_count(),
1462                                                 ASCIIToUTF16("folder"));
1463   model->AddFolder(parent, parent->child_count(),
1464                   ASCIIToUTF16("sibbling folder"));
1465   model->AddURL(folder, folder->child_count(), ASCIIToUTF16("title a"),
1466                 GURL("http://www.google.com/a"));
1467   model->AddURL(folder, folder->child_count(),
1468       ASCIIToUTF16("title super duper long long whoa momma title you betcha"),
1469       GURL("http://www.google.com/b"));
1470   BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1471   EXPECT_FALSE([bar_ folderController]);
1472   [bar_ openBookmarkFolderFromButton:button];
1473   BookmarkBarFolderController* bbfc = [bar_ folderController];
1474   // The following tells us that the folder menu is showing. We want to make
1475   // sure the folder menu goes away if the bookmark bar is hidden.
1476   EXPECT_TRUE(bbfc);
1477   EXPECT_TRUE([bar_ isVisible]);
1478
1479   // Hide the bookmark bar.
1480   [bar_ updateState:BookmarkBar::DETACHED
1481          changeType:BookmarkBar::ANIMATE_STATE_CHANGE];
1482   EXPECT_TRUE([bar_ isAnimationRunning]);
1483
1484   // Now that we've closed the bookmark bar (with animation) the folder menu
1485   // should have been closed thus releasing the folderController.
1486   EXPECT_FALSE([bar_ folderController]);
1487
1488   // Stop the pending animation to tear down cleanly.
1489   [bar_ updateState:BookmarkBar::DETACHED
1490          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
1491   EXPECT_FALSE([bar_ isAnimationRunning]);
1492 }
1493
1494 TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) {
1495   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1496   const BookmarkNode* root = model->bookmark_bar_node();
1497   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1498   test::AddNodesFromModelString(model, root, model_string);
1499
1500   // Validate initial model.
1501   std::string actualModelString = test::ModelStringFromNode(root);
1502   EXPECT_EQ(model_string, actualModelString);
1503
1504   // Remember how many buttons are showing.
1505   int oldDisplayedButtons = [bar_ displayedButtonCount];
1506   NSArray* buttons = [bar_ buttons];
1507
1508   // Move a button around a bit.
1509   [bar_ moveButtonFromIndex:0 toIndex:2];
1510   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]);
1511   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]);
1512   EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]);
1513   EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1514   [bar_ moveButtonFromIndex:2 toIndex:0];
1515   EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]);
1516   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1517   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1518   EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1519
1520   // Add a couple of buttons.
1521   const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node.
1522   const BookmarkNode* node = parent->GetChild(0);
1523   [bar_ addButtonForNode:node atIndex:0];
1524   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1525   EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1526   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1527   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1528   EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]);
1529   node = parent->GetChild(1);
1530   [bar_ addButtonForNode:node atIndex:-1];
1531   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1532   EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1533   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1534   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1535   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]);
1536   EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]);
1537
1538   // Remove a couple of buttons.
1539   [bar_ removeButton:4 animate:NO];
1540   [bar_ removeButton:1 animate:NO];
1541   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1542   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1543   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1544   EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1545 }
1546
1547 TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) {
1548   NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0);
1549   NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease];
1550   EXPECT_FALSE([view isHidden]);
1551   [bar_ shrinkOrHideView:view forMaxX:500.0];
1552   EXPECT_EQ(500.0, NSWidth([view frame]));
1553   EXPECT_FALSE([view isHidden]);
1554   [bar_ shrinkOrHideView:view forMaxX:450.0];
1555   EXPECT_EQ(450.0, NSWidth([view frame]));
1556   EXPECT_FALSE([view isHidden]);
1557   [bar_ shrinkOrHideView:view forMaxX:40.0];
1558   EXPECT_EQ(40.0, NSWidth([view frame]));
1559   EXPECT_FALSE([view isHidden]);
1560   [bar_ shrinkOrHideView:view forMaxX:31.0];
1561   EXPECT_EQ(31.0, NSWidth([view frame]));
1562   EXPECT_FALSE([view isHidden]);
1563   [bar_ shrinkOrHideView:view forMaxX:29.0];
1564   EXPECT_TRUE([view isHidden]);
1565 }
1566
1567 TEST_F(BookmarkBarControllerTest, LastBookmarkResizeBehavior) {
1568   // Hide the apps shortcut.
1569   profile()->GetPrefs()->SetBoolean(
1570       bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1571   ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1572
1573   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1574   const BookmarkNode* root = model->bookmark_bar_node();
1575   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1576   test::AddNodesFromModelString(model, root, model_string);
1577   [bar_ frameDidChange];
1578
1579   CGFloat viewWidths[] = { 123.0, 124.0, 151.0, 152.0, 153.0, 154.0, 155.0,
1580                            200.0, 155.0, 154.0, 153.0, 152.0, 151.0, 124.0,
1581                            123.0 };
1582   BOOL offTheSideButtonIsHiddenResults[] = { NO, NO, NO, NO, YES, YES, YES, YES,
1583                                              YES, YES, YES, NO, NO, NO, NO};
1584   int displayedButtonCountResults[] = { 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2,
1585                                         2, 1 };
1586
1587   for (unsigned int i = 0; i < sizeof(viewWidths) / sizeof(viewWidths[0]);
1588        ++i) {
1589     NSRect frame = [[bar_ view] frame];
1590     frame.size.width = viewWidths[i] + bookmarks::kBookmarkRightMargin;
1591     [[bar_ view] setFrame:frame];
1592     EXPECT_EQ(offTheSideButtonIsHiddenResults[i],
1593               [bar_ offTheSideButtonIsHidden]);
1594     EXPECT_EQ(displayedButtonCountResults[i], [bar_ displayedButtonCount]);
1595   }
1596 }
1597
1598 TEST_F(BookmarkBarControllerTest, BookmarksWithAppsPageShortcut) {
1599   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1600   const BookmarkNode* root = model->bookmark_bar_node();
1601   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1602   test::AddNodesFromModelString(model, root, model_string);
1603   [bar_ frameDidChange];
1604
1605   // Apps page shortcut button should be visible.
1606   ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1607
1608   // Bookmarks should be to the right of the Apps page shortcut button.
1609   CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1610   CGFloat right = apps_button_right;
1611   NSArray* buttons = [bar_ buttons];
1612   for (size_t i = 0; i < [buttons count]; ++i) {
1613     EXPECT_LE(right, NSMinX([[buttons objectAtIndex:i] frame]));
1614     right = NSMaxX([[buttons objectAtIndex:i] frame]);
1615   }
1616
1617   // Removing the Apps button should move every bookmark to the left.
1618   profile()->GetPrefs()->SetBoolean(
1619       bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1620   ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1621   EXPECT_GT(apps_button_right, NSMinX([[buttons objectAtIndex:0] frame]));
1622   for (size_t i = 1; i < [buttons count]; ++i) {
1623     EXPECT_LE(NSMaxX([[buttons objectAtIndex:i - 1] frame]),
1624               NSMinX([[buttons objectAtIndex:i] frame]));
1625   }
1626 }
1627
1628 TEST_F(BookmarkBarControllerTest, BookmarksWithoutAppsPageShortcut) {
1629   // The no item containers should be to the right of the Apps button.
1630   ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1631   CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1632   EXPECT_LE(apps_button_right,
1633             NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1634   EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1635             NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1636
1637   // Removing the Apps button should move the no item containers to the left.
1638   profile()->GetPrefs()->SetBoolean(
1639       bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1640   ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1641   EXPECT_GT(apps_button_right,
1642             NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1643   EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1644             NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1645 }
1646
1647 TEST_F(BookmarkBarControllerTest, ManagedShowAppsShortcutInBookmarksBar) {
1648   // By default the pref is not managed and the apps shortcut is shown.
1649   TestingPrefServiceSyncable* prefs = profile()->GetTestingPrefService();
1650   EXPECT_FALSE(prefs->IsManagedPreference(
1651       bookmarks::prefs::kShowAppsShortcutInBookmarkBar));
1652   EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1653
1654   // Hide the apps shortcut by policy, via the managed pref.
1655   prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1656                         new base::FundamentalValue(false));
1657   EXPECT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1658
1659   // And try showing it via policy too.
1660   prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1661                         new base::FundamentalValue(true));
1662   EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1663 }
1664
1665 class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest {
1666 public:
1667   virtual void SetUp() {
1668     BookmarkBarControllerTest::SetUp();
1669     ASSERT_TRUE(profile());
1670
1671     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1672     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1673     bar_.reset(
1674                [[BookmarkBarControllerOpenAllPong alloc]
1675                 initWithBrowser:browser()
1676                    initialWidth:NSWidth(parent_frame)
1677                        delegate:nil
1678                  resizeDelegate:resizeDelegate_.get()]);
1679     [bar_ view];
1680     // Awkwardness to look like we've been installed.
1681     [parent_view_ addSubview:[bar_ view]];
1682     NSRect frame = [[[bar_ view] superview] frame];
1683     frame.origin.y = 100;
1684     [[[bar_ view] superview] setFrame:frame];
1685
1686     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1687     parent_ = model->bookmark_bar_node();
1688     // { one, { two-one, two-two }, three }
1689     model->AddURL(parent_, parent_->child_count(), ASCIIToUTF16("title"),
1690                   GURL("http://one.com"));
1691     folder_ = model->AddFolder(parent_, parent_->child_count(),
1692                                ASCIIToUTF16("folder"));
1693     model->AddURL(folder_, folder_->child_count(),
1694                   ASCIIToUTF16("title"), GURL("http://two-one.com"));
1695     model->AddURL(folder_, folder_->child_count(),
1696                   ASCIIToUTF16("title"), GURL("http://two-two.com"));
1697     model->AddURL(parent_, parent_->child_count(),
1698                   ASCIIToUTF16("title"), GURL("https://three.com"));
1699   }
1700   const BookmarkNode* parent_;  // Weak
1701   const BookmarkNode* folder_;  // Weak
1702 };
1703
1704 // Command-click on a folder should open all the bookmarks in it.
1705 TEST_F(BookmarkBarControllerOpenAllTest, CommandClickOnFolder) {
1706   NSButton* first = [[bar_ buttons] objectAtIndex:0];
1707   EXPECT_TRUE(first);
1708
1709   // Create the right kind of event; mock NSApp so [NSApp
1710   // currentEvent] finds it.
1711   NSEvent* commandClick =
1712       cocoa_test_event_utils::MouseEventAtPoint(NSZeroPoint,
1713                                                 NSLeftMouseDown,
1714                                                 NSCommandKeyMask);
1715   id fakeApp = [OCMockObject partialMockForObject:NSApp];
1716   [[[fakeApp stub] andReturn:commandClick] currentEvent];
1717   id oldApp = NSApp;
1718   NSApp = fakeApp;
1719   size_t originalDispositionCount = noOpenBar()->dispositions_.size();
1720
1721   // Click!
1722   [first performClick:first];
1723
1724   size_t dispositionCount = noOpenBar()->dispositions_.size();
1725   EXPECT_EQ(originalDispositionCount+1, dispositionCount);
1726   EXPECT_EQ(noOpenBar()->dispositions_[dispositionCount-1], NEW_BACKGROUND_TAB);
1727
1728   // Replace NSApp
1729   NSApp = oldApp;
1730 }
1731
1732 class BookmarkBarControllerNotificationTest : public CocoaProfileTest {
1733  public:
1734   virtual void SetUp() {
1735     CocoaProfileTest::SetUp();
1736     ASSERT_TRUE(browser());
1737
1738     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1739     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1740     parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
1741     [parent_view_ setHidden:YES];
1742     bar_.reset(
1743       [[BookmarkBarControllerNotificationPong alloc]
1744           initWithBrowser:browser()
1745              initialWidth:NSWidth(parent_frame)
1746                  delegate:nil
1747            resizeDelegate:resizeDelegate_.get()]);
1748
1749     // Force loading of the nib.
1750     [bar_ view];
1751     // Awkwardness to look like we've been installed.
1752     [parent_view_ addSubview:[bar_ view]];
1753     NSRect frame = [[[bar_ view] superview] frame];
1754     frame.origin.y = 100;
1755     [[[bar_ view] superview] setFrame:frame];
1756
1757     // Do not add the bar to a window, yet.
1758   }
1759
1760   base::scoped_nsobject<NSView> parent_view_;
1761   base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
1762   base::scoped_nsobject<BookmarkBarControllerNotificationPong> bar_;
1763 };
1764
1765 TEST_F(BookmarkBarControllerNotificationTest, DeregistersForNotifications) {
1766   NSWindow* window = [[CocoaTestHelperWindow alloc] init];
1767   [window setReleasedWhenClosed:YES];
1768
1769   // First add the bookmark bar to the temp window, then to another window.
1770   [[window contentView] addSubview:parent_view_];
1771   [[test_window() contentView] addSubview:parent_view_];
1772
1773   // Post a fake windowDidResignKey notification for the temp window and make
1774   // sure the bookmark bar controller wasn't listening.
1775   [[NSNotificationCenter defaultCenter]
1776       postNotificationName:NSWindowDidResignKeyNotification
1777                     object:window];
1778   EXPECT_FALSE([bar_ windowDidResignKeyReceived]);
1779
1780   // Close the temp window and make sure no notification was received.
1781   [window close];
1782   EXPECT_FALSE([bar_ windowWillCloseReceived]);
1783 }
1784
1785
1786 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
1787 // they are hard to test.  Factor out "fire timers" into routines
1788 // which can be overridden to fire immediately to make behavior
1789 // confirmable.
1790
1791 // TODO(jrg): add unit test to make sure "Other Bookmarks" responds
1792 // properly to a hover open.
1793
1794 // TODO(viettrungluu): figure out how to test animations.
1795
1796 class BookmarkBarControllerDragDropTest : public BookmarkBarControllerTestBase {
1797  public:
1798   base::scoped_nsobject<BookmarkBarControllerDragData> bar_;
1799
1800   virtual void SetUp() {
1801     BookmarkBarControllerTestBase::SetUp();
1802     ASSERT_TRUE(browser());
1803
1804     bar_.reset(
1805                [[BookmarkBarControllerDragData alloc]
1806                 initWithBrowser:browser()
1807                    initialWidth:NSWidth([parent_view_ frame])
1808                        delegate:nil
1809                  resizeDelegate:resizeDelegate_.get()]);
1810     InstallAndToggleBar(bar_.get());
1811   }
1812 };
1813
1814 TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) {
1815   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1816   const BookmarkNode* root = model->bookmark_bar_node();
1817   const std::string model_string("1bWithLongName 2fWithLongName:[ "
1818       "2f1bWithLongName 2f2fWithLongName:[ 2f2f1bWithLongName "
1819       "2f2f2bWithLongName 2f2f3bWithLongName 2f4b ] 2f3bWithLongName ] "
1820       "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1821       "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1822       "11bWithLongName 12bWithLongName 13b ");
1823   test::AddNodesFromModelString(model, root, model_string);
1824
1825   // Validate initial model.
1826   std::string actualModelString = test::ModelStringFromNode(root);
1827   EXPECT_EQ(model_string, actualModelString);
1828
1829   // Insure that the off-the-side is not showing.
1830   ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1831
1832   // Remember how many buttons are showing and are available.
1833   int oldDisplayedButtons = [bar_ displayedButtonCount];
1834   int oldChildCount = root->child_count();
1835
1836   // Pop up the off-the-side menu.
1837   BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1838   ASSERT_TRUE(otsButton);
1839   [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1840                            withObject:otsButton];
1841   BookmarkBarFolderController* otsController = [bar_ folderController];
1842   EXPECT_TRUE(otsController);
1843   NSWindow* toWindow = [otsController window];
1844   EXPECT_TRUE(toWindow);
1845   BookmarkButton* draggedButton =
1846       [bar_ buttonWithTitleEqualTo:@"3bWithLongName"];
1847   ASSERT_TRUE(draggedButton);
1848   int oldOTSCount = (int)[[otsController buttons] count];
1849   EXPECT_EQ(oldOTSCount, oldChildCount - oldDisplayedButtons);
1850   BookmarkButton* targetButton = [[otsController buttons] objectAtIndex:0];
1851   ASSERT_TRUE(targetButton);
1852   [otsController dragButton:draggedButton
1853                          to:[targetButton center]
1854                        copy:YES];
1855   // There should still be the same number of buttons in the bar
1856   // and off-the-side should have one more.
1857   int newDisplayedButtons = [bar_ displayedButtonCount];
1858   int newChildCount = root->child_count();
1859   int newOTSCount = (int)[[otsController buttons] count];
1860   EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons);
1861   EXPECT_EQ(oldChildCount + 1, newChildCount);
1862   EXPECT_EQ(oldOTSCount + 1, newOTSCount);
1863   EXPECT_EQ(newOTSCount, newChildCount - newDisplayedButtons);
1864 }
1865
1866 TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) {
1867   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1868   const BookmarkNode* root = model->bookmark_bar_node();
1869   const std::string model_string("1bWithLongName 2bWithLongName "
1870       "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1871       "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1872       "11bWithLongName 12bWithLongName 13bWithLongName 14bWithLongName "
1873       "15bWithLongName 16bWithLongName 17bWithLongName 18bWithLongName "
1874       "19bWithLongName 20bWithLongName ");
1875   test::AddNodesFromModelString(model, root, model_string);
1876
1877   const BookmarkNode* other = model->other_node();
1878   const std::string other_string("1other 2other 3other ");
1879   test::AddNodesFromModelString(model, other, other_string);
1880
1881   // Validate initial model.
1882   std::string actualModelString = test::ModelStringFromNode(root);
1883   EXPECT_EQ(model_string, actualModelString);
1884   std::string actualOtherString = test::ModelStringFromNode(other);
1885   EXPECT_EQ(other_string, actualOtherString);
1886
1887   // Insure that the off-the-side is showing.
1888   ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1889
1890   // Remember how many buttons are showing and are available.
1891   int oldDisplayedButtons = [bar_ displayedButtonCount];
1892   int oldRootCount = root->child_count();
1893   int oldOtherCount = other->child_count();
1894
1895   // Pop up the off-the-side menu.
1896   BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1897   ASSERT_TRUE(otsButton);
1898   [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1899                            withObject:otsButton];
1900   BookmarkBarFolderController* otsController = [bar_ folderController];
1901   EXPECT_TRUE(otsController);
1902   int oldOTSCount = (int)[[otsController buttons] count];
1903   EXPECT_EQ(oldOTSCount, oldRootCount - oldDisplayedButtons);
1904
1905   // Pick an off-the-side button and drag it to the other bookmarks.
1906   BookmarkButton* draggedButton =
1907       [otsController buttonWithTitleEqualTo:@"20bWithLongName"];
1908   ASSERT_TRUE(draggedButton);
1909   BookmarkButton* targetButton = [bar_ otherBookmarksButton];
1910   ASSERT_TRUE(targetButton);
1911   [bar_ dragButton:draggedButton to:[targetButton center] copy:NO];
1912
1913   // There should one less button in the bar, one less in off-the-side,
1914   // and one more in other bookmarks.
1915   int newRootCount = root->child_count();
1916   int newOTSCount = (int)[[otsController buttons] count];
1917   int newOtherCount = other->child_count();
1918   EXPECT_EQ(oldRootCount - 1, newRootCount);
1919   EXPECT_EQ(oldOTSCount - 1, newOTSCount);
1920   EXPECT_EQ(oldOtherCount + 1, newOtherCount);
1921 }
1922
1923 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkData) {
1924   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1925   const BookmarkNode* root = model->bookmark_bar_node();
1926   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1927                                   "2f3b ] 3b 4b ");
1928   test::AddNodesFromModelString(model, root, model_string);
1929   const BookmarkNode* other = model->other_node();
1930   const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
1931                                  "O4f:[ O4f1b O4f2f ] 05b ");
1932   test::AddNodesFromModelString(model, other, other_string);
1933
1934   // Validate initial model.
1935   std::string actual = test::ModelStringFromNode(root);
1936   EXPECT_EQ(model_string, actual);
1937   actual = test::ModelStringFromNode(other);
1938   EXPECT_EQ(other_string, actual);
1939
1940   // Remember the little ones.
1941   int oldChildCount = root->child_count();
1942
1943   BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1944   ASSERT_TRUE(targetButton);
1945
1946   // Gen up some dragging data.
1947   const BookmarkNode* newNode = other->GetChild(2);
1948   [bar_ setDragDataNode:newNode];
1949   base::scoped_nsobject<FakeDragInfo> dragInfo([[FakeDragInfo alloc] init]);
1950   [dragInfo setDropLocation:[targetButton center]];
1951   [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1952
1953   // There should one more button in the bar.
1954   int newChildCount = root->child_count();
1955   EXPECT_EQ(oldChildCount + 1, newChildCount);
1956   // Verify the model.
1957   const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1958                              "2f3b ] O3f:[ O3f1b O3f2f ] 3b 4b ");
1959   actual = test::ModelStringFromNode(root);
1960   EXPECT_EQ(expected, actual);
1961   oldChildCount = newChildCount;
1962
1963   // Now do it over a folder button.
1964   targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
1965   ASSERT_TRUE(targetButton);
1966   NSPoint targetPoint = [targetButton center];
1967   newNode = other->GetChild(2);  // Should be O4f.
1968   EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
1969   [bar_ setDragDataNode:newNode];
1970   [dragInfo setDropLocation:targetPoint];
1971   [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1972
1973   newChildCount = root->child_count();
1974   EXPECT_EQ(oldChildCount, newChildCount);
1975   // Verify the model.
1976   const std::string expected1("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1977                               "2f3b O4f:[ O4f1b O4f2f ] ] O3f:[ O3f1b O3f2f ] "
1978                               "3b 4b ");
1979   actual = test::ModelStringFromNode(root);
1980   EXPECT_EQ(expected1, actual);
1981 }
1982
1983 TEST_F(BookmarkBarControllerDragDropTest, AddURLs) {
1984   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1985   const BookmarkNode* root = model->bookmark_bar_node();
1986   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1987                                  "2f3b ] 3b 4b ");
1988   test::AddNodesFromModelString(model, root, model_string);
1989
1990   // Validate initial model.
1991   std::string actual = test::ModelStringFromNode(root);
1992   EXPECT_EQ(model_string, actual);
1993
1994   // Remember the children.
1995   int oldChildCount = root->child_count();
1996
1997   BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1998   ASSERT_TRUE(targetButton);
1999
2000   NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
2001                    @"http://www.b.com/", nil];
2002   NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
2003   [bar_ addURLs:urls withTitles:titles at:[targetButton center]];
2004
2005   // There should two more nodes in the bar.
2006   int newChildCount = root->child_count();
2007   EXPECT_EQ(oldChildCount + 2, newChildCount);
2008   // Verify the model.
2009   const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2010                              "2f3b ] SiteA SiteB 3b 4b ");
2011   actual = test::ModelStringFromNode(root);
2012   EXPECT_EQ(expected, actual);
2013 }
2014
2015 TEST_F(BookmarkBarControllerDragDropTest, ControllerForNode) {
2016   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2017   const BookmarkNode* root = model->bookmark_bar_node();
2018   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
2019   test::AddNodesFromModelString(model, root, model_string);
2020
2021   // Validate initial model.
2022   std::string actualModelString = test::ModelStringFromNode(root);
2023   EXPECT_EQ(model_string, actualModelString);
2024
2025   // Find the main bar controller.
2026   const void* expectedController = bar_;
2027   const void* actualController = [bar_ controllerForNode:root];
2028   EXPECT_EQ(expectedController, actualController);
2029 }
2030
2031 TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) {
2032   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2033   const BookmarkNode* root = model->bookmark_bar_node();
2034   const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
2035   test::AddNodesFromModelString(model, root, model_string);
2036
2037   // Hide the apps shortcut.
2038   profile()->GetPrefs()->SetBoolean(
2039       bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
2040   ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
2041
2042   // Validate initial model.
2043   std::string actualModel = test::ModelStringFromNode(root);
2044   EXPECT_EQ(model_string, actualModel);
2045
2046   // Test a series of points starting at the right edge of the bar.
2047   BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"1b"];
2048   ASSERT_TRUE(targetButton);
2049   NSPoint targetPoint = [targetButton left];
2050   CGFloat leftMarginIndicatorPosition = bookmarks::kBookmarkLeftMargin - 0.5 *
2051                                         bookmarks::kBookmarkHorizontalPadding;
2052   const CGFloat baseOffset = targetPoint.x;
2053   CGFloat expected = leftMarginIndicatorPosition;
2054   CGFloat actual = [bar_ indicatorPosForDragToPoint:targetPoint];
2055   EXPECT_CGFLOAT_EQ(expected, actual);
2056   targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
2057   actual = [bar_ indicatorPosForDragToPoint:[targetButton right]];
2058   targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
2059   expected = [targetButton left].x - baseOffset + leftMarginIndicatorPosition;
2060   EXPECT_CGFLOAT_EQ(expected, actual);
2061   targetButton = [bar_ buttonWithTitleEqualTo:@"4b"];
2062   targetPoint = [targetButton right];
2063   targetPoint.x += 100;  // Somewhere off to the right.
2064   CGFloat xDelta = 0.5 * bookmarks::kBookmarkHorizontalPadding;
2065   expected = NSMaxX([targetButton frame]) + xDelta;
2066   actual = [bar_ indicatorPosForDragToPoint:targetPoint];
2067   EXPECT_CGFLOAT_EQ(expected, actual);
2068 }
2069
2070 TEST_F(BookmarkBarControllerDragDropTest, PulseButton) {
2071   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2072   const BookmarkNode* root = model->bookmark_bar_node();
2073   GURL gurl("http://www.google.com");
2074   const BookmarkNode* node = model->AddURL(root, root->child_count(),
2075                                            ASCIIToUTF16("title"), gurl);
2076
2077   BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
2078   EXPECT_FALSE([button isContinuousPulsing]);
2079
2080   NSValue *value = [NSValue valueWithPointer:node];
2081   NSDictionary *dict = [NSDictionary
2082                          dictionaryWithObjectsAndKeys:value,
2083                          bookmark_button::kBookmarkKey,
2084                          [NSNumber numberWithBool:YES],
2085                          bookmark_button::kBookmarkPulseFlagKey,
2086                          nil];
2087   [[NSNotificationCenter defaultCenter]
2088         postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2089                       object:nil
2090                     userInfo:dict];
2091   EXPECT_TRUE([button isContinuousPulsing]);
2092
2093   dict = [NSDictionary dictionaryWithObjectsAndKeys:value,
2094                        bookmark_button::kBookmarkKey,
2095                        [NSNumber numberWithBool:NO],
2096                        bookmark_button::kBookmarkPulseFlagKey,
2097                        nil];
2098   [[NSNotificationCenter defaultCenter]
2099         postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2100                       object:nil
2101                     userInfo:dict];
2102   EXPECT_FALSE([button isContinuousPulsing]);
2103 }
2104
2105 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkDataToTrash) {
2106   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2107   const BookmarkNode* root = model->bookmark_bar_node();
2108   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2109                                   "2f3b ] 3b 4b ");
2110   test::AddNodesFromModelString(model, root, model_string);
2111
2112   // Validate initial model.
2113   std::string actual = test::ModelStringFromNode(root);
2114   EXPECT_EQ(model_string, actual);
2115
2116   int oldChildCount = root->child_count();
2117
2118   // Drag a button to the trash.
2119   BookmarkButton* buttonToDelete = [bar_ buttonWithTitleEqualTo:@"3b"];
2120   ASSERT_TRUE(buttonToDelete);
2121   EXPECT_TRUE([bar_ canDragBookmarkButtonToTrash:buttonToDelete]);
2122   [bar_ didDragBookmarkToTrash:buttonToDelete];
2123
2124   // There should be one less button in the bar.
2125   int newChildCount = root->child_count();
2126   EXPECT_EQ(oldChildCount - 1, newChildCount);
2127   // Verify the model.
2128   const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2129                              "2f3b ] 4b ");
2130   actual = test::ModelStringFromNode(root);
2131   EXPECT_EQ(expected, actual);
2132
2133   // Verify that the other bookmark folder can't be deleted.
2134   BookmarkButton *otherButton = [bar_ otherBookmarksButton];
2135   EXPECT_FALSE([bar_ canDragBookmarkButtonToTrash:otherButton]);
2136 }
2137
2138 }  // namespace