Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / status_bubble_mac_unittest.mm
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <Cocoa/Cocoa.h>
6
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #import "chrome/browser/ui/cocoa/bubble_view.h"
12 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
13 #import "chrome/browser/ui/cocoa/status_bubble_mac.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #import "testing/gtest_mac.h"
16 #include "testing/platform_test.h"
17 #import "third_party/ocmock/OCMock/OCMock.h"
18 #include "ui/gfx/point.h"
19 #include "url/gurl.h"
20
21 using base::UTF8ToUTF16;
22
23 // The test delegate records all of the status bubble object's state
24 // transitions.
25 @interface StatusBubbleMacTestDelegate : NSObject {
26  @private
27   NSWindow* window_;  // Weak.
28   NSPoint baseFrameOffset_;
29   std::vector<StatusBubbleMac::StatusBubbleState> states_;
30 }
31 - (id)initWithWindow:(NSWindow*)window;
32 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset;
33 - (NSRect)statusBubbleBaseFrame;
34 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state;
35 @end
36 @implementation StatusBubbleMacTestDelegate
37 - (id)initWithWindow:(NSWindow*)window {
38   if ((self = [super init])) {
39     window_ = window;
40     baseFrameOffset_ = NSZeroPoint;
41   }
42   return self;
43 }
44 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset {
45   baseFrameOffset_ = baseFrameOffset;
46 }
47 - (NSRect)statusBubbleBaseFrame {
48   NSView* contentView = [window_ contentView];
49   NSRect baseFrame = [contentView convertRect:[contentView frame] toView:nil];
50   if (baseFrameOffset_.x > 0 || baseFrameOffset_.y > 0) {
51     baseFrame = NSOffsetRect(baseFrame, baseFrameOffset_.x, baseFrameOffset_.y);
52     baseFrame.size.width -= baseFrameOffset_.x;
53     baseFrame.size.height -= baseFrameOffset_.y;
54   }
55   return baseFrame;
56 }
57 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state {
58   states_.push_back(state);
59 }
60 - (std::vector<StatusBubbleMac::StatusBubbleState>*)states {
61   return &states_;
62 }
63 @end
64
65 // This class implements, for testing purposes, a subclass of |StatusBubbleMac|
66 // whose |MouseMoved()| method does nothing. This lets the tests fake the mouse
67 // position and avoid being affected by the true mouse position.
68 class StatusBubbleMacIgnoreMouseMoved : public StatusBubbleMac {
69  public:
70   StatusBubbleMacIgnoreMouseMoved(NSWindow* parent, id delegate)
71       : StatusBubbleMac(parent, delegate), mouseLocation_(0, 0) {
72     // Set the fake mouse position to the top right of the content area.
73     NSRect contentBounds = [[parent contentView] bounds];
74     mouseLocation_.SetPoint(NSMaxX(contentBounds), NSMaxY(contentBounds));
75   }
76
77   void MouseMoved(const gfx::Point& location, bool left_content) override {}
78
79   gfx::Point GetMouseLocation() override { return mouseLocation_; }
80
81   void SetMouseLocationForTesting(int x, int y) {
82     mouseLocation_.SetPoint(x, y);
83     StatusBubbleMac::MouseMoved(gfx::Point(x, y), false);
84   }
85
86  private:
87   gfx::Point mouseLocation_;
88 };
89
90 class StatusBubbleMacTest : public CocoaTest {
91  public:
92   virtual void SetUp() {
93     CocoaTest::SetUp();
94     NSWindow* window = test_window();
95     EXPECT_TRUE(window);
96     delegate_.reset(
97         [[StatusBubbleMacTestDelegate alloc] initWithWindow: window]);
98     EXPECT_TRUE(delegate_.get());
99     bubble_ = new StatusBubbleMacIgnoreMouseMoved(window, delegate_);
100     EXPECT_TRUE(bubble_);
101
102     // Turn off delays and transitions for test mode.  This doesn't just speed
103     // things along, it's actually required to get StatusBubbleMac to behave
104     // synchronously, because the tests here don't know how to wait for
105     // results.  This allows these tests to be much more complete with a
106     // minimal loss of coverage and without any heinous rearchitecting.
107     bubble_->immediate_ = true;
108
109     EXPECT_TRUE(bubble_->window_);  // immediately creates window
110   }
111
112   virtual void TearDown() {
113     // Not using a scoped_ptr because bubble must be deleted before calling
114     // TearDown to get rid of bubble's window.
115     delete bubble_;
116     CocoaTest::TearDown();
117   }
118
119   bool IsVisible() {
120     if (![bubble_->window_ isVisible])
121       return false;
122     return [bubble_->window_ alphaValue] > 0.0;
123   }
124   NSString* GetText() {
125     return bubble_->status_text_;
126   }
127   NSString* GetURLText() {
128     return bubble_->url_text_;
129   }
130   NSString* GetBubbleViewText() {
131     BubbleView* bubbleView = [bubble_->window_ contentView];
132     return [bubbleView content];
133   }
134   StatusBubbleWindow* GetWindow() {
135     return bubble_->window_;
136   }
137   NSWindow* parent() {
138     return bubble_->parent_;
139   }
140   StatusBubbleMac::StatusBubbleState GetState() {
141     return bubble_->state_;
142   }
143   void SetState(StatusBubbleMac::StatusBubbleState state) {
144     bubble_->SetState(state);
145   }
146   std::vector<StatusBubbleMac::StatusBubbleState>* States() {
147     return [delegate_ states];
148   }
149   StatusBubbleMac::StatusBubbleState StateAt(int index) {
150     return (*States())[index];
151   }
152
153   bool IsPointInBubble(int x, int y) {
154     return NSPointInRect(NSMakePoint(x, y), [GetWindow() frame]);
155   }
156
157   void SetMouseLocation(int relative_x, int relative_y) {
158     // Convert to screen coordinates.
159     NSRect window_frame = [test_window() frame];
160     int x = relative_x + window_frame.origin.x;
161     int y = relative_y + window_frame.origin.y;
162
163     ((StatusBubbleMacIgnoreMouseMoved*)
164       bubble_)->SetMouseLocationForTesting(x, y);
165   }
166
167   // Test helper for moving the fake mouse location, and checking that
168   // the bubble avoids that location.
169   // For convenience & clarity, coordinates are relative to the main window.
170   bool CheckAvoidsMouse(int relative_x, int relative_y) {
171     SetMouseLocation(relative_x, relative_y);
172     return !IsPointInBubble(relative_x, relative_y);
173   }
174
175   base::MessageLoop message_loop_;
176   base::scoped_nsobject<StatusBubbleMacTestDelegate> delegate_;
177   StatusBubbleMac* bubble_;  // Strong.
178 };
179
180 TEST_F(StatusBubbleMacTest, SetStatus) {
181   bubble_->SetStatus(base::string16());
182   bubble_->SetStatus(UTF8ToUTF16("This is a test"));
183   EXPECT_NSEQ(@"This is a test", GetText());
184   EXPECT_TRUE(IsVisible());
185
186   // Set the status to the exact same thing again
187   bubble_->SetStatus(UTF8ToUTF16("This is a test"));
188   EXPECT_NSEQ(@"This is a test", GetText());
189
190   // Hide it
191   bubble_->SetStatus(base::string16());
192   EXPECT_FALSE(IsVisible());
193 }
194
195 TEST_F(StatusBubbleMacTest, SetURL) {
196   bubble_->SetURL(GURL(), std::string());
197   EXPECT_FALSE(IsVisible());
198   bubble_->SetURL(GURL("bad url"), std::string());
199   EXPECT_FALSE(IsVisible());
200   bubble_->SetURL(GURL("http://"), std::string());
201   EXPECT_TRUE(IsVisible());
202   EXPECT_NSEQ(@"http:", GetURLText());
203   bubble_->SetURL(GURL("about:blank"), std::string());
204   EXPECT_TRUE(IsVisible());
205   EXPECT_NSEQ(@"about:blank", GetURLText());
206   bubble_->SetURL(GURL("foopy://"), std::string());
207   EXPECT_TRUE(IsVisible());
208   EXPECT_NSEQ(@"foopy://", GetURLText());
209   bubble_->SetURL(GURL("http://www.cnn.com"), std::string());
210   EXPECT_TRUE(IsVisible());
211   EXPECT_NSEQ(@"www.cnn.com", GetURLText());
212 }
213
214 // Test hiding bubble that's already hidden.
215 TEST_F(StatusBubbleMacTest, Hides) {
216   bubble_->SetStatus(UTF8ToUTF16("Showing"));
217   EXPECT_TRUE(IsVisible());
218   bubble_->Hide();
219   EXPECT_FALSE(IsVisible());
220   bubble_->Hide();
221   EXPECT_FALSE(IsVisible());
222 }
223
224 // Test the "main"/"backup" behavior in StatusBubbleMac::SetText().
225 TEST_F(StatusBubbleMacTest, SetStatusAndURL) {
226   EXPECT_FALSE(IsVisible());
227   bubble_->SetStatus(UTF8ToUTF16("Status"));
228   EXPECT_TRUE(IsVisible());
229   EXPECT_NSEQ(@"Status", GetBubbleViewText());
230   bubble_->SetURL(GURL("http://www.nytimes.com"), std::string());
231   EXPECT_TRUE(IsVisible());
232   EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
233   bubble_->SetURL(GURL(), std::string());
234   EXPECT_TRUE(IsVisible());
235   EXPECT_NSEQ(@"Status", GetBubbleViewText());
236   bubble_->SetStatus(base::string16());
237   EXPECT_FALSE(IsVisible());
238   bubble_->SetURL(GURL("http://www.nytimes.com"), std::string());
239   EXPECT_TRUE(IsVisible());
240   EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
241   bubble_->SetStatus(UTF8ToUTF16("Status"));
242   EXPECT_TRUE(IsVisible());
243   EXPECT_NSEQ(@"Status", GetBubbleViewText());
244   bubble_->SetStatus(base::string16());
245   EXPECT_TRUE(IsVisible());
246   EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
247   bubble_->SetURL(GURL(), std::string());
248   EXPECT_FALSE(IsVisible());
249 }
250
251 // Test that the status bubble goes through the correct delay and fade states.
252 // The delay and fade duration are simulated and not actually experienced
253 // during the test because StatusBubbleMacTest sets immediate_ mode.
254 TEST_F(StatusBubbleMacTest, StateTransitions) {
255   // First, some sanity
256
257   EXPECT_FALSE(IsVisible());
258   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
259
260   States()->clear();
261   EXPECT_TRUE(States()->empty());
262
263   bubble_->SetStatus(base::string16());
264   EXPECT_FALSE(IsVisible());
265   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
266   EXPECT_TRUE(States()->empty());  // no change from initial kBubbleHidden state
267
268   // Next, a few ordinary cases
269
270   // Test StartShowing from kBubbleHidden
271   bubble_->SetStatus(UTF8ToUTF16("Status"));
272   EXPECT_TRUE(IsVisible());
273   // Check GetState before checking States to make sure that all state
274   // transitions have been flushed to States.
275   EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
276   EXPECT_EQ(3u, States()->size());
277   EXPECT_EQ(StatusBubbleMac::kBubbleShowingTimer, StateAt(0));
278   EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(1));
279   EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(2));
280
281   // Test StartShowing from kBubbleShown with the same message
282   States()->clear();
283   bubble_->SetStatus(UTF8ToUTF16("Status"));
284   EXPECT_TRUE(IsVisible());
285   EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
286   EXPECT_TRUE(States()->empty());
287
288   // Test StartShowing from kBubbleShown with a different message
289   bubble_->SetStatus(UTF8ToUTF16("New Status"));
290   EXPECT_TRUE(IsVisible());
291   EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
292   EXPECT_TRUE(States()->empty());
293
294   // Test StartHiding from kBubbleShown
295   bubble_->SetStatus(base::string16());
296   EXPECT_FALSE(IsVisible());
297   // Check GetState before checking States to make sure that all state
298   // transitions have been flushed to States.
299   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
300   EXPECT_EQ(3u, States()->size());
301   EXPECT_EQ(StatusBubbleMac::kBubbleHidingTimer, StateAt(0));
302   EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(1));
303   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(2));
304
305   // Test StartHiding from kBubbleHidden
306   States()->clear();
307   bubble_->SetStatus(base::string16());
308   EXPECT_FALSE(IsVisible());
309   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
310   EXPECT_TRUE(States()->empty());
311
312   // Now, the edge cases
313
314   // Test StartShowing from kBubbleShowingTimer
315   bubble_->SetStatus(UTF8ToUTF16("Status"));
316   SetState(StatusBubbleMac::kBubbleShowingTimer);
317   [GetWindow() setAlphaValue:0.0];
318   States()->clear();
319   EXPECT_TRUE(States()->empty());
320   bubble_->SetStatus(UTF8ToUTF16("Status"));
321   EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
322   EXPECT_EQ(2u, States()->size());
323   EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0));
324   EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1));
325
326   // Test StartShowing from kBubbleShowingFadeIn
327   bubble_->SetStatus(UTF8ToUTF16("Status"));
328   SetState(StatusBubbleMac::kBubbleShowingFadeIn);
329   [GetWindow() setAlphaValue:0.5];
330   States()->clear();
331   EXPECT_TRUE(States()->empty());
332   bubble_->SetStatus(UTF8ToUTF16("Status"));
333   // The actual state values can't be tested in immediate_ mode because
334   // the window wasn't actually fading in.  Without immediate_ mode,
335   // expect kBubbleShown.
336   bubble_->SetStatus(base::string16());  // Go back to a deterministic state.
337
338   // Test StartShowing from kBubbleHidingTimer
339   bubble_->SetStatus(base::string16());
340   SetState(StatusBubbleMac::kBubbleHidingTimer);
341   [GetWindow() setAlphaValue:1.0];
342   States()->clear();
343   EXPECT_TRUE(States()->empty());
344   bubble_->SetStatus(UTF8ToUTF16("Status"));
345   EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
346   EXPECT_EQ(1u, States()->size());
347   EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(0));
348
349   // Test StartShowing from kBubbleHidingFadeOut
350   bubble_->SetStatus(base::string16());
351   SetState(StatusBubbleMac::kBubbleHidingFadeOut);
352   [GetWindow() setAlphaValue:0.5];
353   States()->clear();
354   EXPECT_TRUE(States()->empty());
355   bubble_->SetStatus(UTF8ToUTF16("Status"));
356   EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
357   EXPECT_EQ(2u, States()->size());
358   EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0));
359   EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1));
360
361   // Test StartHiding from kBubbleShowingTimer
362   bubble_->SetStatus(UTF8ToUTF16("Status"));
363   SetState(StatusBubbleMac::kBubbleShowingTimer);
364   [GetWindow() setAlphaValue:0.0];
365   States()->clear();
366   EXPECT_TRUE(States()->empty());
367   bubble_->SetStatus(base::string16());
368   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
369   EXPECT_EQ(1u, States()->size());
370   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
371
372   // Test StartHiding from kBubbleShowingFadeIn
373   bubble_->SetStatus(UTF8ToUTF16("Status"));
374   SetState(StatusBubbleMac::kBubbleShowingFadeIn);
375   [GetWindow() setAlphaValue:0.5];
376   States()->clear();
377   EXPECT_TRUE(States()->empty());
378   bubble_->SetStatus(base::string16());
379   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
380   EXPECT_EQ(2u, States()->size());
381   EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0));
382   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1));
383
384   // Test StartHiding from kBubbleHidingTimer
385   bubble_->SetStatus(base::string16());
386   SetState(StatusBubbleMac::kBubbleHidingTimer);
387   [GetWindow() setAlphaValue:1.0];
388   States()->clear();
389   EXPECT_TRUE(States()->empty());
390   bubble_->SetStatus(base::string16());
391   // The actual state values can't be tested in immediate_ mode because
392   // the timer wasn't actually running.  Without immediate_ mode, expect
393   // kBubbleHidingFadeOut and kBubbleHidden.
394   // Go back to a deterministic state.
395   bubble_->SetStatus(UTF8ToUTF16("Status"));
396
397   // Test StartHiding from kBubbleHidingFadeOut
398   bubble_->SetStatus(base::string16());
399   SetState(StatusBubbleMac::kBubbleHidingFadeOut);
400   [GetWindow() setAlphaValue:0.5];
401   States()->clear();
402   EXPECT_TRUE(States()->empty());
403   bubble_->SetStatus(base::string16());
404   // The actual state values can't be tested in immediate_ mode because
405   // the window wasn't actually fading out.  Without immediate_ mode, expect
406   // kBubbleHidden.
407   // Go back to a deterministic state.
408   bubble_->SetStatus(UTF8ToUTF16("Status"));
409
410   // Test Hide from kBubbleHidden
411   bubble_->SetStatus(base::string16());
412   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
413   States()->clear();
414   EXPECT_TRUE(States()->empty());
415   bubble_->Hide();
416   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
417   EXPECT_TRUE(States()->empty());
418
419   // Test Hide from kBubbleShowingTimer
420   bubble_->SetStatus(UTF8ToUTF16("Status"));
421   SetState(StatusBubbleMac::kBubbleShowingTimer);
422   [GetWindow() setAlphaValue:0.0];
423   States()->clear();
424   EXPECT_TRUE(States()->empty());
425   bubble_->Hide();
426   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
427   EXPECT_EQ(1u, States()->size());
428   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
429
430   // Test Hide from kBubbleShowingFadeIn
431   bubble_->SetStatus(UTF8ToUTF16("Status"));
432   SetState(StatusBubbleMac::kBubbleShowingFadeIn);
433   [GetWindow() setAlphaValue:0.5];
434   States()->clear();
435   EXPECT_TRUE(States()->empty());
436   bubble_->Hide();
437   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
438   EXPECT_EQ(2u, States()->size());
439   EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0));
440   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1));
441
442   // Test Hide from kBubbleShown
443   bubble_->SetStatus(UTF8ToUTF16("Status"));
444   States()->clear();
445   EXPECT_TRUE(States()->empty());
446   bubble_->Hide();
447   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
448   EXPECT_EQ(1u, States()->size());
449   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
450
451   // Test Hide from kBubbleHidingTimer
452   bubble_->SetStatus(UTF8ToUTF16("Status"));
453   SetState(StatusBubbleMac::kBubbleHidingTimer);
454   States()->clear();
455   EXPECT_TRUE(States()->empty());
456   bubble_->Hide();
457   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
458   EXPECT_EQ(1u, States()->size());
459   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
460
461   // Test Hide from kBubbleHidingFadeOut
462   bubble_->SetStatus(UTF8ToUTF16("Status"));
463   SetState(StatusBubbleMac::kBubbleHidingFadeOut);
464   [GetWindow() setAlphaValue:0.5];
465   States()->clear();
466   EXPECT_TRUE(States()->empty());
467   bubble_->Hide();
468   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
469   EXPECT_EQ(1u, States()->size());
470   EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
471 }
472
473 TEST_F(StatusBubbleMacTest, Delete) {
474   NSWindow* window = test_window();
475   // Create and delete immediately.
476   StatusBubbleMac* bubble = new StatusBubbleMac(window, nil);
477   delete bubble;
478
479   // Create then delete while visible.
480   bubble = new StatusBubbleMac(window, nil);
481   bubble->SetStatus(UTF8ToUTF16("showing"));
482   delete bubble;
483 }
484
485 TEST_F(StatusBubbleMacTest, UpdateSizeAndPosition) {
486   // Test |UpdateSizeAndPosition()| when status bubble does not exist (shouldn't
487   // crash; shouldn't create window).
488   EXPECT_TRUE(GetWindow());
489   bubble_->UpdateSizeAndPosition();
490   EXPECT_TRUE(GetWindow());
491
492   // Create a status bubble (with contents) and call resize (without actually
493   // resizing); the frame size shouldn't change.
494   bubble_->SetStatus(UTF8ToUTF16("UpdateSizeAndPosition test"));
495   ASSERT_TRUE(GetWindow());
496   NSRect rect_before = [GetWindow() frame];
497   bubble_->UpdateSizeAndPosition();
498   NSRect rect_after = [GetWindow() frame];
499   EXPECT_TRUE(NSEqualRects(rect_before, rect_after));
500
501   // Move the window and call resize; only the origin should change.
502   NSWindow* window = test_window();
503   ASSERT_TRUE(window);
504   NSRect frame = [window frame];
505   rect_before = [GetWindow() frame];
506   frame.origin.x += 10.0;  // (fairly arbitrary nonzero value)
507   frame.origin.y += 10.0;  // (fairly arbitrary nonzero value)
508   [window setFrame:frame display:YES];
509   bubble_->UpdateSizeAndPosition();
510   rect_after = [GetWindow() frame];
511   EXPECT_NE(rect_before.origin.x, rect_after.origin.x);
512   EXPECT_NE(rect_before.origin.y, rect_after.origin.y);
513   EXPECT_EQ(rect_before.size.width, rect_after.size.width);
514   EXPECT_EQ(rect_before.size.height, rect_after.size.height);
515
516   // Resize the window (without moving). The origin shouldn't change. The width
517   // should change (in the current implementation), but not the height.
518   frame = [window frame];
519   rect_before = [GetWindow() frame];
520   frame.size.width += 50.0;   // (fairly arbitrary nonzero value)
521   frame.size.height += 50.0;  // (fairly arbitrary nonzero value)
522   [window setFrame:frame display:YES];
523   bubble_->UpdateSizeAndPosition();
524   rect_after = [GetWindow() frame];
525   EXPECT_EQ(rect_before.origin.x, rect_after.origin.x);
526   EXPECT_EQ(rect_before.origin.y, rect_after.origin.y);
527   EXPECT_NE(rect_before.size.width, rect_after.size.width);
528   EXPECT_EQ(rect_before.size.height, rect_after.size.height);
529 }
530
531 TEST_F(StatusBubbleMacTest, MovingWindowUpdatesPosition) {
532   NSWindow* window = test_window();
533
534   // Show the bubble and make sure it has the same origin as |window|.
535   bubble_->SetStatus(UTF8ToUTF16("Showing"));
536   StatusBubbleWindow* child = GetWindow();
537   EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
538
539   // Hide the bubble, move the window, and show it again.
540   bubble_->Hide();
541   NSRect frame = [window frame];
542   frame.origin.x += 50;
543   [window setFrame:frame display:YES];
544   bubble_->SetStatus(UTF8ToUTF16("Reshowing"));
545
546   // The bubble should reattach in the correct location.
547   child = GetWindow();
548   EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
549 }
550
551 TEST_F(StatusBubbleMacTest, StatuBubbleRespectsBaseFrameLimits) {
552   NSWindow* window = test_window();
553
554   // Show the bubble and make sure it has the same origin as |window|.
555   bubble_->SetStatus(UTF8ToUTF16("Showing"));
556   StatusBubbleWindow* child = GetWindow();
557   EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
558
559   // Hide the bubble, change base frame offset, and show it again.
560   bubble_->Hide();
561
562   NSPoint baseFrameOffset = NSMakePoint(0, [window frame].size.height / 3);
563   EXPECT_GT(baseFrameOffset.y, 0);
564   [delegate_ forceBaseFrameOffset:baseFrameOffset];
565
566   bubble_->SetStatus(UTF8ToUTF16("Reshowing"));
567
568   // The bubble should reattach in the correct location.
569   child = GetWindow();
570   NSPoint expectedOrigin = [window frame].origin;
571   expectedOrigin.x += baseFrameOffset.x;
572   expectedOrigin.y += baseFrameOffset.y;
573   EXPECT_TRUE(NSEqualPoints(expectedOrigin, [child frame].origin));
574 }
575
576 TEST_F(StatusBubbleMacTest, ExpandBubble) {
577   NSWindow* window = test_window();
578
579   // The system font changes between OSX 10.9 and OSX 10.10. Use the system
580   // font from OSX 10.9 for this test.
581   id mockContentView =
582       [OCMockObject partialMockForObject:[GetWindow() contentView]];
583   [[[mockContentView stub]
584       andReturn:[NSFont fontWithName:@"Lucida Grande" size:11]] font];
585
586   ASSERT_TRUE(window);
587   NSRect window_frame = [window frame];
588   window_frame.size.width = 600.0;
589   [window setFrame:window_frame display:YES];
590
591   // Check basic expansion
592   bubble_->SetStatus(UTF8ToUTF16("Showing"));
593   EXPECT_TRUE(IsVisible());
594   bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"),
595                   std::string());
596   EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
597   bubble_->ExpandBubble();
598   EXPECT_TRUE(IsVisible());
599   EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText());
600   bubble_->Hide();
601
602   // Make sure bubble resets after hide.
603   bubble_->SetStatus(UTF8ToUTF16("Showing"));
604   bubble_->SetURL(GURL("http://www.snickersnee.com/pioneer_fishstix.html"),
605                   std::string());
606   EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
607   // ...and that it expands again properly.
608   bubble_->ExpandBubble();
609   EXPECT_NSEQ(@"www.snickersnee.com/pioneer_fishstix.html", GetURLText());
610   // ...again, again!
611   bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"),
612                   std::string());
613   bubble_->ExpandBubble();
614   EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText());
615   bubble_->Hide();
616
617   window_frame = [window frame];
618   window_frame.size.width = 300.0;
619   [window setFrame:window_frame display:YES];
620
621   // Very long URL's will be cut off even in the expanded state.
622   bubble_->SetStatus(UTF8ToUTF16("Showing"));
623   const char veryLongUrl[] =
624       "http://www.diewahrscheinlichlaengstepralinederwelt.com/duuuuplo.html";
625   bubble_->SetURL(GURL(veryLongUrl), std::string());
626   EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
627   bubble_->ExpandBubble();
628   EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
629 }
630
631 TEST_F(StatusBubbleMacTest, BubbleAvoidsMouse) {
632   NSWindow* window = test_window();
633
634   // All coordinates here are relative to the window origin.
635
636   // Initially, the bubble should appear in the bottom left.
637   bubble_->SetStatus(UTF8ToUTF16("Showing"));
638   EXPECT_TRUE(IsPointInBubble(0, 0));
639   bubble_->Hide();
640
641   // Check that the bubble doesn't appear in the left corner if the
642   // mouse is currently located there.
643   SetMouseLocation(0, 0);
644   bubble_->SetStatus(UTF8ToUTF16("Showing"));
645   EXPECT_FALSE(IsPointInBubble(0, 0));
646
647   // Leave the bubble visible, and try moving the mouse around.
648   int smallValue = NSHeight([GetWindow() frame]) / 2;
649   EXPECT_TRUE(CheckAvoidsMouse(0, 0));
650   EXPECT_TRUE(CheckAvoidsMouse(smallValue, 0));
651   EXPECT_TRUE(CheckAvoidsMouse(0, smallValue));
652   EXPECT_TRUE(CheckAvoidsMouse(smallValue, smallValue));
653
654   // Simulate moving the mouse down from the top of the window.
655   for (int y = NSHeight([window frame]); y >= 0; y -= smallValue) {
656     ASSERT_TRUE(CheckAvoidsMouse(smallValue, y));
657   }
658
659   // Simulate moving the mouse from left to right.
660   int windowWidth = NSWidth([window frame]);
661   for (int x = 0; x < windowWidth; x += smallValue) {
662     ASSERT_TRUE(CheckAvoidsMouse(x, smallValue));
663   }
664
665   // Simulate moving the mouse from right to left.
666   for (int x = windowWidth; x >= 0; x -= smallValue) {
667     ASSERT_TRUE(CheckAvoidsMouse(x, smallValue));
668   }
669 }