OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
6 | 6 |
7 #import "base/mac/scoped_nsobject.h" | 7 #import "base/mac/scoped_nsobject.h" |
8 #include "base/strings/utf_string_conversions.h" | 8 #include "base/strings/utf_string_conversions.h" |
9 #include "chrome/browser/ui/cocoa/cocoa_test_helper.h" | 9 #include "chrome/browser/ui/cocoa/cocoa_test_helper.h" |
10 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h" | 10 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h" |
11 #import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h" | 11 #import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h" |
12 #import "chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h" | 12 #import "chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h" |
| 13 #include "grit/theme_resources.h" |
| 14 #include "grit/ui_resources.h" |
13 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
14 #import "testing/gtest_mac.h" | 16 #import "testing/gtest_mac.h" |
15 #include "testing/platform_test.h" | 17 #include "testing/platform_test.h" |
| 18 #include "ui/base/resource/resource_bundle.h" |
16 | 19 |
17 // Implements the target interface for the tab, which gets sent messages when | 20 // Implements the target interface for the tab, which gets sent messages when |
18 // the tab is clicked on by the user and when its close box is clicked. | 21 // the tab is clicked on by the user and when its close box is clicked. |
19 @interface TabControllerTestTarget : NSObject<TabControllerTarget> { | 22 @interface TabControllerTestTarget : NSObject<TabControllerTarget> { |
20 @private | 23 @private |
21 bool selected_; | 24 bool selected_; |
22 bool closed_; | 25 bool closed_; |
23 base::scoped_nsobject<TabStripDragController> dragController_; | 26 base::scoped_nsobject<TabStripDragController> dragController_; |
24 } | 27 } |
25 - (bool)selected; | 28 - (bool)selected; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 namespace { | 87 namespace { |
85 | 88 |
86 CGFloat LeftMargin(NSRect superFrame, NSRect subFrame) { | 89 CGFloat LeftMargin(NSRect superFrame, NSRect subFrame) { |
87 return NSMinX(subFrame) - NSMinX(superFrame); | 90 return NSMinX(subFrame) - NSMinX(superFrame); |
88 } | 91 } |
89 | 92 |
90 CGFloat RightMargin(NSRect superFrame, NSRect subFrame) { | 93 CGFloat RightMargin(NSRect superFrame, NSRect subFrame) { |
91 return NSMaxX(superFrame) - NSMaxX(subFrame); | 94 return NSMaxX(superFrame) - NSMaxX(subFrame); |
92 } | 95 } |
93 | 96 |
| 97 // Helper to create an NSImageView that contains an image fetched from |
| 98 // ui::ResourceBundle. |
| 99 NSImageView* CreateImageViewFromResourceBundle(int resource_id) { |
| 100 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 101 NSImage* const image = rb.GetNativeImageNamed(resource_id).ToNSImage(); |
| 102 CHECK(!!image); |
| 103 NSRect frame; |
| 104 frame.size = [image size]; |
| 105 NSImageView* const view = [[NSImageView alloc] initWithFrame:frame]; |
| 106 [view setImage:image]; |
| 107 return view; |
| 108 } |
| 109 |
94 // The dragging code in TabView makes heavy use of autorelease pools so | 110 // The dragging code in TabView makes heavy use of autorelease pools so |
95 // inherit from CocoaTest to have one created for us. | 111 // inherit from CocoaTest to have one created for us. |
96 class TabControllerTest : public CocoaTest { | 112 class TabControllerTest : public CocoaTest { |
97 public: | 113 public: |
98 TabControllerTest() { } | 114 TabControllerTest() { } |
| 115 |
| 116 static void CheckForExpectedLayoutAndVisibilityOfSubviews( |
| 117 const TabController* controller) { |
| 118 // Check whether subviews should be visible when they are supposed to be, |
| 119 // given Tab size and TabRendererData state. |
| 120 if ([controller mini]) { |
| 121 if ([controller projecting]) |
| 122 EXPECT_TRUE([controller shouldShowIcon]); |
| 123 else |
| 124 EXPECT_TRUE([controller shouldShowIcon] != |
| 125 [controller shouldShowAudioIndicator]); |
| 126 EXPECT_FALSE([controller shouldShowCloseButton]); |
| 127 } else if ([controller selected]) { |
| 128 EXPECT_TRUE([controller shouldShowCloseButton]); |
| 129 switch ([controller iconCapacity]) { |
| 130 case 0: |
| 131 case 1: |
| 132 EXPECT_FALSE([controller shouldShowIcon]); |
| 133 EXPECT_FALSE([controller shouldShowAudioIndicator]); |
| 134 break; |
| 135 case 2: |
| 136 if ([controller projecting]) |
| 137 EXPECT_TRUE([controller shouldShowIcon]); |
| 138 else |
| 139 EXPECT_TRUE([controller shouldShowIcon] != |
| 140 [controller shouldShowAudioIndicator]); |
| 141 break; |
| 142 default: |
| 143 EXPECT_LE(3, [controller iconCapacity]); |
| 144 EXPECT_TRUE([controller shouldShowIcon]); |
| 145 if ([controller projecting]) |
| 146 EXPECT_FALSE([controller shouldShowAudioIndicator]); |
| 147 else |
| 148 EXPECT_TRUE(!![controller audioIndicatorView] == |
| 149 [controller shouldShowAudioIndicator]); |
| 150 break; |
| 151 } |
| 152 } else { // Tab not selected/active and not mini tab. |
| 153 switch ([controller iconCapacity]) { |
| 154 case 0: |
| 155 EXPECT_FALSE([controller shouldShowCloseButton]); |
| 156 EXPECT_FALSE([controller shouldShowIcon]); |
| 157 EXPECT_FALSE([controller shouldShowAudioIndicator]); |
| 158 break; |
| 159 case 1: |
| 160 EXPECT_FALSE([controller shouldShowCloseButton]); |
| 161 if ([controller projecting]) |
| 162 EXPECT_TRUE([controller shouldShowIcon]); |
| 163 else |
| 164 EXPECT_TRUE([controller shouldShowIcon] != |
| 165 [controller shouldShowAudioIndicator]); |
| 166 break; |
| 167 default: |
| 168 EXPECT_LE(2, [controller iconCapacity]); |
| 169 EXPECT_TRUE([controller shouldShowIcon]); |
| 170 if ([controller projecting]) |
| 171 EXPECT_FALSE([controller shouldShowAudioIndicator]); |
| 172 else |
| 173 EXPECT_TRUE(!![controller audioIndicatorView] == |
| 174 [controller shouldShowAudioIndicator]); |
| 175 break; |
| 176 } |
| 177 } |
| 178 |
| 179 // Make sure the NSView's "isHidden" state jives with the "shouldShowXXX." |
| 180 EXPECT_TRUE([controller shouldShowIcon] == |
| 181 (!![controller iconView] && ![[controller iconView] isHidden])); |
| 182 EXPECT_TRUE([controller mini] == [[controller titleView] isHidden]); |
| 183 EXPECT_TRUE([controller shouldShowAudioIndicator] == |
| 184 (!![controller audioIndicatorView] && |
| 185 ![[controller audioIndicatorView] isHidden])); |
| 186 EXPECT_TRUE([controller shouldShowCloseButton] != |
| 187 [[controller closeButton] isHidden]); |
| 188 |
| 189 // Check positioning of elements with respect to each other, and that they |
| 190 // are fully within the tab frame. |
| 191 const NSRect tabFrame = [[controller view] frame]; |
| 192 const NSRect titleFrame = [[controller titleView] frame]; |
| 193 if ([controller shouldShowIcon]) { |
| 194 const NSRect iconFrame = [[controller iconView] frame]; |
| 195 EXPECT_LE(NSMinX(tabFrame), NSMinX(iconFrame)); |
| 196 if (NSWidth(titleFrame) > 0) |
| 197 EXPECT_LE(NSMaxX(iconFrame), NSMinX(titleFrame)); |
| 198 EXPECT_LE(NSMinY(tabFrame), NSMinY(iconFrame)); |
| 199 EXPECT_LE(NSMaxY(iconFrame), NSMaxY(tabFrame)); |
| 200 } |
| 201 if ([controller shouldShowIcon] && [controller shouldShowAudioIndicator]) { |
| 202 EXPECT_LE(NSMaxX([[controller iconView] frame]), |
| 203 NSMinX([[controller audioIndicatorView] frame])); |
| 204 } |
| 205 if ([controller shouldShowAudioIndicator]) { |
| 206 const NSRect audioIndicatorFrame = |
| 207 [[controller audioIndicatorView] frame]; |
| 208 if (NSWidth(titleFrame) > 0) |
| 209 EXPECT_LE(NSMaxX(titleFrame), NSMinX(audioIndicatorFrame)); |
| 210 EXPECT_LE(NSMaxX(audioIndicatorFrame), NSMaxX(tabFrame)); |
| 211 EXPECT_LE(NSMinY(tabFrame), NSMinY(audioIndicatorFrame)); |
| 212 EXPECT_LE(NSMaxY(audioIndicatorFrame), NSMaxY(tabFrame)); |
| 213 } |
| 214 if ([controller shouldShowAudioIndicator] && |
| 215 [controller shouldShowCloseButton]) { |
| 216 EXPECT_LE(NSMaxX([[controller audioIndicatorView] frame]), |
| 217 NSMinX([[controller closeButton] frame])); |
| 218 } |
| 219 if ([controller shouldShowCloseButton]) { |
| 220 const NSRect closeButtonFrame = [[controller closeButton] frame]; |
| 221 if (NSWidth(titleFrame) > 0) |
| 222 EXPECT_LE(NSMaxX(titleFrame), NSMinX(closeButtonFrame)); |
| 223 EXPECT_LE(NSMaxX(closeButtonFrame), NSMaxX(tabFrame)); |
| 224 EXPECT_LE(NSMinY(tabFrame), NSMinY(closeButtonFrame)); |
| 225 EXPECT_LE(NSMaxY(closeButtonFrame), NSMaxY(tabFrame)); |
| 226 } |
| 227 } |
99 }; | 228 }; |
100 | 229 |
101 // Tests creating the controller, sticking it in a window, and removing it. | 230 // Tests creating the controller, sticking it in a window, and removing it. |
102 TEST_F(TabControllerTest, Creation) { | 231 TEST_F(TabControllerTest, Creation) { |
103 NSWindow* window = test_window(); | 232 NSWindow* window = test_window(); |
104 base::scoped_nsobject<TabController> controller([[TabController alloc] init]); | 233 base::scoped_nsobject<TabController> controller([[TabController alloc] init]); |
105 [[window contentView] addSubview:[controller view]]; | 234 [[window contentView] addSubview:[controller view]]; |
106 EXPECT_TRUE([controller tabView]); | 235 EXPECT_TRUE([controller tabView]); |
107 EXPECT_EQ([[controller view] window], window); | 236 EXPECT_EQ([[controller view] window], window); |
108 [[controller view] display]; // Test drawing to ensure nothing leaks/crashes. | 237 [[controller view] display]; // Test drawing to ensure nothing leaks/crashes. |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 EXPECT_GT(NSWidth([[controller view] frame]), | 477 EXPECT_GT(NSWidth([[controller view] frame]), |
349 NSWidth([[controller titleView] frame])); | 478 NSWidth([[controller titleView] frame])); |
350 EXPECT_EQ(LeftMargin(originalTabFrame, originalTitleFrame), | 479 EXPECT_EQ(LeftMargin(originalTabFrame, originalTitleFrame), |
351 LeftMargin([[controller view] frame], | 480 LeftMargin([[controller view] frame], |
352 [[controller titleView] frame])); | 481 [[controller titleView] frame])); |
353 EXPECT_EQ(RightMargin(originalTabFrame, originalTitleFrame), | 482 EXPECT_EQ(RightMargin(originalTabFrame, originalTitleFrame), |
354 RightMargin([[controller view] frame], | 483 RightMargin([[controller view] frame], |
355 [[controller titleView] frame])); | 484 [[controller titleView] frame])); |
356 } | 485 } |
357 | 486 |
| 487 // A comprehensive test of the layout and visibility of all elements (favicon, |
| 488 // throbber indicators, titile text, audio indicator, and close button) over all |
| 489 // relevant combinations of tab state. This test overlaps with parts of the |
| 490 // other tests above. |
| 491 TEST_F(TabControllerTest, LayoutAndVisibilityOfSubviews) { |
| 492 NSWindow* const window = test_window(); |
| 493 |
| 494 // Create TabController instance and place its view into the test window. |
| 495 base::scoped_nsobject<TabController> controller([[TabController alloc] init]); |
| 496 [[window contentView] addSubview:[controller view]]; |
| 497 |
| 498 // Create favicon and audio indicator icon views. |
| 499 base::scoped_nsobject<NSImageView> faviconView( |
| 500 CreateImageViewFromResourceBundle(IDR_DEFAULT_FAVICON)); |
| 501 base::scoped_nsobject<NSImageView> audioIndicatorView( |
| 502 CreateImageViewFromResourceBundle(IDR_TAB_AUDIO_INDICATOR)); |
| 503 |
| 504 [controller setIconView:faviconView]; |
| 505 |
| 506 // Perform layout over all possible combinations, checking for correct |
| 507 // results. |
| 508 for (int is_mini_tab = 0; is_mini_tab < 2; ++is_mini_tab) { |
| 509 for (int is_active_tab = 0; is_active_tab < 2; ++is_active_tab) { |
| 510 for (int is_audio_playing = 0; is_audio_playing < 2; ++is_audio_playing) { |
| 511 for (int is_capturing = 0; is_capturing < 2; ++is_capturing) { |
| 512 SCOPED_TRACE(::testing::Message() |
| 513 << (is_active_tab ? "Active" : "Inactive") << ' ' |
| 514 << (is_mini_tab ? "Mini " : "") |
| 515 << "Tab with is_audio_playing=" << !!is_audio_playing |
| 516 << " and is_capturing=" << !!is_capturing); |
| 517 |
| 518 // Simulate what tab_strip_controller would do to set up the |
| 519 // TabController state. |
| 520 [controller setMini:(is_mini_tab ? YES : NO)]; |
| 521 [controller setActive:(is_active_tab ? YES : NO)]; |
| 522 if (is_capturing) { |
| 523 [controller setProjecting:YES]; |
| 524 [controller setAudioIndicatorView:nil]; |
| 525 } else { |
| 526 [controller setProjecting:NO]; |
| 527 if (is_audio_playing) |
| 528 [controller setAudioIndicatorView:audioIndicatorView]; |
| 529 else |
| 530 [controller setAudioIndicatorView:nil]; |
| 531 } |
| 532 |
| 533 // Test layout for every width from maximum to minimum. |
| 534 NSRect tabFrame = [[controller view] frame]; |
| 535 int min_width; |
| 536 if (is_mini_tab) { |
| 537 tabFrame.size.width = min_width = [TabController miniTabWidth]; |
| 538 } else { |
| 539 tabFrame.size.width = [TabController maxTabWidth]; |
| 540 min_width = is_active_tab ? [TabController minSelectedTabWidth] : |
| 541 [TabController minTabWidth]; |
| 542 } |
| 543 while (NSWidth(tabFrame) >= min_width) { |
| 544 SCOPED_TRACE(::testing::Message() |
| 545 << "width=" << tabFrame.size.width); |
| 546 [[controller view] setFrame:tabFrame]; |
| 547 CheckForExpectedLayoutAndVisibilityOfSubviews(controller); |
| 548 --tabFrame.size.width; |
| 549 } |
| 550 } |
| 551 } |
| 552 } |
| 553 } |
| 554 } |
| 555 |
358 } // namespace | 556 } // namespace |
OLD | NEW |