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