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