Index: chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm |
diff --git a/chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm b/chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm |
index 0267e79c297c3b7402e505c3c5f73a62f1622777..ab58034023a490e7ace778017eb31daefa64bddb 100644 |
--- a/chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm |
+++ b/chrome/browser/ui/cocoa/tabs/tab_controller_unittest.mm |
@@ -10,9 +10,12 @@ |
#import "chrome/browser/ui/cocoa/tabs/tab_controller.h" |
#import "chrome/browser/ui/cocoa/tabs/tab_controller_target.h" |
#import "chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h" |
+#include "grit/theme_resources.h" |
+#include "grit/ui_resources.h" |
#include "testing/gtest/include/gtest/gtest.h" |
#import "testing/gtest_mac.h" |
#include "testing/platform_test.h" |
+#include "ui/base/resource/resource_bundle.h" |
// Implements the target interface for the tab, which gets sent messages when |
// the tab is clicked on by the user and when its close box is clicked. |
@@ -91,11 +94,137 @@ CGFloat RightMargin(NSRect superFrame, NSRect subFrame) { |
return NSMaxX(superFrame) - NSMaxX(subFrame); |
} |
+// Helper to create an NSImageView that contains an image fetched from |
+// ui::ResourceBundle. |
+NSImageView* CreateImageViewFromResourceBundle(int resource_id) { |
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
+ NSImage* const image = rb.GetNativeImageNamed(resource_id).ToNSImage(); |
+ CHECK(!!image); |
+ NSRect frame; |
+ frame.size = [image size]; |
+ NSImageView* const view = [[NSImageView alloc] initWithFrame:frame]; |
+ [view setImage:image]; |
+ return view; |
+} |
+ |
// The dragging code in TabView makes heavy use of autorelease pools so |
// inherit from CocoaTest to have one created for us. |
class TabControllerTest : public CocoaTest { |
public: |
TabControllerTest() { } |
+ |
+ static void CheckForExpectedLayoutAndVisibilityOfSubviews( |
+ const TabController* controller) { |
+ // Check whether subviews should be visible when they are supposed to be, |
+ // given Tab size and TabRendererData state. |
+ if ([controller mini]) { |
+ if ([controller projecting]) |
+ EXPECT_TRUE([controller shouldShowIcon]); |
+ else |
+ EXPECT_TRUE([controller shouldShowIcon] != |
+ [controller shouldShowAudioIndicator]); |
+ EXPECT_FALSE([controller shouldShowCloseButton]); |
+ } else if ([controller selected]) { |
+ EXPECT_TRUE([controller shouldShowCloseButton]); |
+ switch ([controller iconCapacity]) { |
+ case 0: |
+ case 1: |
+ EXPECT_FALSE([controller shouldShowIcon]); |
+ EXPECT_FALSE([controller shouldShowAudioIndicator]); |
+ break; |
+ case 2: |
+ if ([controller projecting]) |
+ EXPECT_TRUE([controller shouldShowIcon]); |
+ else |
+ EXPECT_TRUE([controller shouldShowIcon] != |
+ [controller shouldShowAudioIndicator]); |
+ break; |
+ default: |
+ EXPECT_LE(3, [controller iconCapacity]); |
+ EXPECT_TRUE([controller shouldShowIcon]); |
+ if ([controller projecting]) |
+ EXPECT_FALSE([controller shouldShowAudioIndicator]); |
+ else |
+ EXPECT_TRUE(!![controller audioIndicatorView] == |
+ [controller shouldShowAudioIndicator]); |
+ break; |
+ } |
+ } else { // Tab not selected/active and not mini tab. |
+ switch ([controller iconCapacity]) { |
+ case 0: |
+ EXPECT_FALSE([controller shouldShowCloseButton]); |
+ EXPECT_FALSE([controller shouldShowIcon]); |
+ EXPECT_FALSE([controller shouldShowAudioIndicator]); |
+ break; |
+ case 1: |
+ EXPECT_FALSE([controller shouldShowCloseButton]); |
+ if ([controller projecting]) |
+ EXPECT_TRUE([controller shouldShowIcon]); |
+ else |
+ EXPECT_TRUE([controller shouldShowIcon] != |
+ [controller shouldShowAudioIndicator]); |
+ break; |
+ default: |
+ EXPECT_LE(2, [controller iconCapacity]); |
+ EXPECT_TRUE([controller shouldShowIcon]); |
+ if ([controller projecting]) |
+ EXPECT_FALSE([controller shouldShowAudioIndicator]); |
+ else |
+ EXPECT_TRUE(!![controller audioIndicatorView] == |
+ [controller shouldShowAudioIndicator]); |
+ break; |
+ } |
+ } |
+ |
+ // Make sure the NSView's "isHidden" state jives with the "shouldShowXXX." |
+ EXPECT_TRUE([controller shouldShowIcon] == |
+ (!![controller iconView] && ![[controller iconView] isHidden])); |
+ EXPECT_TRUE([controller mini] == [[controller titleView] isHidden]); |
+ EXPECT_TRUE([controller shouldShowAudioIndicator] == |
+ (!![controller audioIndicatorView] && |
+ ![[controller audioIndicatorView] isHidden])); |
+ EXPECT_TRUE([controller shouldShowCloseButton] != |
+ [[controller closeButton] isHidden]); |
+ |
+ // Check positioning of elements with respect to each other, and that they |
+ // are fully within the tab frame. |
+ const NSRect tabFrame = [[controller view] frame]; |
+ const NSRect titleFrame = [[controller titleView] frame]; |
+ if ([controller shouldShowIcon]) { |
+ const NSRect iconFrame = [[controller iconView] frame]; |
+ EXPECT_LE(NSMinX(tabFrame), NSMinX(iconFrame)); |
+ if (NSWidth(titleFrame) > 0) |
+ EXPECT_LE(NSMaxX(iconFrame), NSMinX(titleFrame)); |
+ EXPECT_LE(NSMinY(tabFrame), NSMinY(iconFrame)); |
+ EXPECT_LE(NSMaxY(iconFrame), NSMaxY(tabFrame)); |
+ } |
+ if ([controller shouldShowIcon] && [controller shouldShowAudioIndicator]) { |
+ EXPECT_LE(NSMaxX([[controller iconView] frame]), |
+ NSMinX([[controller audioIndicatorView] frame])); |
+ } |
+ if ([controller shouldShowAudioIndicator]) { |
+ const NSRect audioIndicatorFrame = |
+ [[controller audioIndicatorView] frame]; |
+ if (NSWidth(titleFrame) > 0) |
+ EXPECT_LE(NSMaxX(titleFrame), NSMinX(audioIndicatorFrame)); |
+ EXPECT_LE(NSMaxX(audioIndicatorFrame), NSMaxX(tabFrame)); |
+ EXPECT_LE(NSMinY(tabFrame), NSMinY(audioIndicatorFrame)); |
+ EXPECT_LE(NSMaxY(audioIndicatorFrame), NSMaxY(tabFrame)); |
+ } |
+ if ([controller shouldShowAudioIndicator] && |
+ [controller shouldShowCloseButton]) { |
+ EXPECT_LE(NSMaxX([[controller audioIndicatorView] frame]), |
+ NSMinX([[controller closeButton] frame])); |
+ } |
+ if ([controller shouldShowCloseButton]) { |
+ const NSRect closeButtonFrame = [[controller closeButton] frame]; |
+ if (NSWidth(titleFrame) > 0) |
+ EXPECT_LE(NSMaxX(titleFrame), NSMinX(closeButtonFrame)); |
+ EXPECT_LE(NSMaxX(closeButtonFrame), NSMaxX(tabFrame)); |
+ EXPECT_LE(NSMinY(tabFrame), NSMinY(closeButtonFrame)); |
+ EXPECT_LE(NSMaxY(closeButtonFrame), NSMaxY(tabFrame)); |
+ } |
+ } |
}; |
// Tests creating the controller, sticking it in a window, and removing it. |
@@ -355,4 +484,73 @@ TEST_F(TabControllerTest, TitleViewLayout) { |
[[controller titleView] frame])); |
} |
+// A comprehensive test of the layout and visibility of all elements (favicon, |
+// throbber indicators, titile text, audio indicator, and close button) over all |
+// relevant combinations of tab state. This test overlaps with parts of the |
+// other tests above. |
+TEST_F(TabControllerTest, LayoutAndVisibilityOfSubviews) { |
+ NSWindow* const window = test_window(); |
+ |
+ // Create TabController instance and place its view into the test window. |
+ base::scoped_nsobject<TabController> controller([[TabController alloc] init]); |
+ [[window contentView] addSubview:[controller view]]; |
+ |
+ // Create favicon and audio indicator icon views. |
+ base::scoped_nsobject<NSImageView> faviconView( |
+ CreateImageViewFromResourceBundle(IDR_DEFAULT_FAVICON)); |
+ base::scoped_nsobject<NSImageView> audioIndicatorView( |
+ CreateImageViewFromResourceBundle(IDR_TAB_AUDIO_INDICATOR)); |
+ |
+ [controller setIconView:faviconView]; |
+ |
+ // Perform layout over all possible combinations, checking for correct |
+ // results. |
+ for (int is_mini_tab = 0; is_mini_tab < 2; ++is_mini_tab) { |
+ for (int is_active_tab = 0; is_active_tab < 2; ++is_active_tab) { |
+ for (int is_audio_playing = 0; is_audio_playing < 2; ++is_audio_playing) { |
+ for (int is_capturing = 0; is_capturing < 2; ++is_capturing) { |
+ SCOPED_TRACE(::testing::Message() |
+ << (is_active_tab ? "Active" : "Inactive") << ' ' |
+ << (is_mini_tab ? "Mini " : "") |
+ << "Tab with is_audio_playing=" << !!is_audio_playing |
+ << " and is_capturing=" << !!is_capturing); |
+ |
+ // Simulate what tab_strip_controller would do to set up the |
+ // TabController state. |
+ [controller setMini:(is_mini_tab ? YES : NO)]; |
+ [controller setActive:(is_active_tab ? YES : NO)]; |
+ if (is_capturing) { |
+ [controller setProjecting:YES]; |
+ [controller setAudioIndicatorView:nil]; |
+ } else { |
+ [controller setProjecting:NO]; |
+ if (is_audio_playing) |
+ [controller setAudioIndicatorView:audioIndicatorView]; |
+ else |
+ [controller setAudioIndicatorView:nil]; |
+ } |
+ |
+ // Test layout for every width from maximum to minimum. |
+ NSRect tabFrame = [[controller view] frame]; |
+ int min_width; |
+ if (is_mini_tab) { |
+ tabFrame.size.width = min_width = [TabController miniTabWidth]; |
+ } else { |
+ tabFrame.size.width = [TabController maxTabWidth]; |
+ min_width = is_active_tab ? [TabController minSelectedTabWidth] : |
+ [TabController minTabWidth]; |
+ } |
+ while (NSWidth(tabFrame) >= min_width) { |
+ SCOPED_TRACE(::testing::Message() |
+ << "width=" << tabFrame.size.width); |
+ [[controller view] setFrame:tabFrame]; |
+ CheckForExpectedLayoutAndVisibilityOfSubviews(controller); |
+ --tabFrame.size.width; |
+ } |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
} // namespace |