ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / ui / views / cocoa / bridged_native_widget_interactive_uitest.mm
blob5911b35e0b24b17ee7c429b80b89443d70f80560
1 // Copyright 2014 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 #import "ui/views/cocoa/bridged_native_widget.h"
7 #import <Cocoa/Cocoa.h>
9 #import "base/mac/mac_util.h"
10 #import "base/mac/sdk_forward_declarations.h"
11 #include "base/run_loop.h"
12 #include "ui/views/test/widget_test.h"
14 @interface NativeWidgetMacNotificationWaiter : NSObject {
15  @private
16   scoped_ptr<base::RunLoop> runLoop_;
17   base::scoped_nsobject<NSWindow> window_;
18   int enterCount_;
19   int exitCount_;
20   int targetEnterCount_;
21   int targetExitCount_;
24 @property(readonly, nonatomic) int enterCount;
25 @property(readonly, nonatomic) int exitCount;
27 // Initialize for the given window and start tracking notifications.
28 - (id)initWithWindow:(NSWindow*)window;
30 // Keep spinning a run loop until the enter and exit counts match.
31 - (void)waitForEnterCount:(int)enterCount exitCount:(int)exitCount;
33 // private:
34 // Exit the RunLoop if there is one and the counts being tracked match.
35 - (void)maybeQuitForChangedArg:(int*)changedArg;
37 - (void)onEnter:(NSNotification*)notification;
38 - (void)onExit:(NSNotification*)notification;
40 @end
42 @implementation NativeWidgetMacNotificationWaiter
44 @synthesize enterCount = enterCount_;
45 @synthesize exitCount = exitCount_;
47 - (id)initWithWindow:(NSWindow*)window {
48   if ((self = [super init])) {
49     window_.reset([window retain]);
50     NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
51     [defaultCenter addObserver:self
52                       selector:@selector(onEnter:)
53                           name:NSWindowDidEnterFullScreenNotification
54                         object:window];
55     [defaultCenter addObserver:self
56                       selector:@selector(onExit:)
57                           name:NSWindowDidExitFullScreenNotification
58                         object:window];
59   }
60   return self;
63 - (void)dealloc {
64   DCHECK(!runLoop_);
65   [[NSNotificationCenter defaultCenter] removeObserver:self];
66   [super dealloc];
69 - (void)waitForEnterCount:(int)enterCount exitCount:(int)exitCount {
70   if (enterCount_ >= enterCount && exitCount_ >= exitCount)
71     return;
73   targetEnterCount_ = enterCount;
74   targetExitCount_ = exitCount;
75   runLoop_.reset(new base::RunLoop);
76   runLoop_->Run();
77   runLoop_.reset();
80 - (void)maybeQuitForChangedArg:(int*)changedArg {
81   ++*changedArg;
82   if (!runLoop_)
83     return;
85   if (enterCount_ >= targetEnterCount_ && exitCount_ >= targetExitCount_)
86     runLoop_->Quit();
89 - (void)onEnter:(NSNotification*)notification {
90   [self maybeQuitForChangedArg:&enterCount_];
93 - (void)onExit:(NSNotification*)notification {
94   [self maybeQuitForChangedArg:&exitCount_];
97 @end
99 namespace views {
101 class BridgedNativeWidgetUITest : public test::WidgetTest {
102  public:
103   BridgedNativeWidgetUITest() {}
105   // testing::Test:
106   void SetUp() override {
107     WidgetTest::SetUp();
108     Widget::InitParams init_params =
109         CreateParams(Widget::InitParams::TYPE_WINDOW);
110     init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
111     widget_.reset(new Widget);
112     widget_->Init(init_params);
113   }
115   void TearDown() override {
116     // Ensures any compositor is removed before ViewsTestBase tears down the
117     // ContextFactory.
118     widget_.reset();
119     WidgetTest::TearDown();
120   }
122   NSWindow* test_window() {
123     return widget_->GetNativeWindow();
124   }
126  protected:
127   scoped_ptr<Widget> widget_;
130 // Tests for correct fullscreen tracking, regardless of whether it is initiated
131 // by the Widget code or elsewhere (e.g. by the user).
132 TEST_F(BridgedNativeWidgetUITest, FullscreenSynchronousState) {
133   EXPECT_FALSE(widget_->IsFullscreen());
134   if (base::mac::IsOSSnowLeopard())
135     return;
137   // Allow user-initiated fullscreen changes on the Window.
138   [test_window()
139       setCollectionBehavior:[test_window() collectionBehavior] |
140                             NSWindowCollectionBehaviorFullScreenPrimary];
142   base::scoped_nsobject<NativeWidgetMacNotificationWaiter> waiter(
143       [[NativeWidgetMacNotificationWaiter alloc] initWithWindow:test_window()]);
144   const gfx::Rect restored_bounds = widget_->GetRestoredBounds();
146   // First show the widget. A user shouldn't be able to initiate fullscreen
147   // unless the window is visible in the first place.
148   widget_->Show();
150   // Simulate a user-initiated fullscreen. Note trying to to this again before
151   // spinning a runloop will cause Cocoa to emit text to stdio and ignore it.
152   [test_window() toggleFullScreen:nil];
153   EXPECT_TRUE(widget_->IsFullscreen());
154   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
156   // Note there's now an animation running. While that's happening, toggling the
157   // state should work as expected, but do "nothing".
158   widget_->SetFullscreen(false);
159   EXPECT_FALSE(widget_->IsFullscreen());
160   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
161   widget_->SetFullscreen(false);  // Same request - should no-op.
162   EXPECT_FALSE(widget_->IsFullscreen());
163   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
165   widget_->SetFullscreen(true);
166   EXPECT_TRUE(widget_->IsFullscreen());
167   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
169   // Always finish out of fullscreen. Otherwise there are 4 NSWindow objects
170   // that Cocoa creates which don't close themselves and will be seen by the Mac
171   // test harness on teardown. Note that the test harness will be waiting until
172   // all animations complete, since these temporary animation windows will not
173   // be removed from the window list until they do.
174   widget_->SetFullscreen(false);
175   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
177   // Now we must wait for the notifications. Since, if the widget is torn down,
178   // the NSWindowDelegate is removed, and the pending request to take out of
179   // fullscreen is lost. Since a message loop has not yet spun up in this test
180   // we can reliably say there will be one enter and one exit, despite all the
181   // toggling above.
182   [waiter waitForEnterCount:1 exitCount:1];
183   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
186 // Test fullscreen without overlapping calls and without changing collection
187 // behavior on the test window.
188 TEST_F(BridgedNativeWidgetUITest, FullscreenEnterAndExit) {
189   base::scoped_nsobject<NativeWidgetMacNotificationWaiter> waiter(
190       [[NativeWidgetMacNotificationWaiter alloc] initWithWindow:test_window()]);
192   EXPECT_FALSE(widget_->IsFullscreen());
193   const gfx::Rect restored_bounds = widget_->GetRestoredBounds();
194   EXPECT_FALSE(restored_bounds.IsEmpty());
196   // Ensure this works without having to change collection behavior as for the
197   // test above. Also check that making a hidden widget fullscreen shows it.
198   EXPECT_FALSE(widget_->IsVisible());
199   widget_->SetFullscreen(true);
200   EXPECT_TRUE(widget_->IsVisible());
201   if (base::mac::IsOSSnowLeopard()) {
202     // On Snow Leopard, SetFullscreen() isn't implemented. But shouldn't crash.
203     EXPECT_FALSE(widget_->IsFullscreen());
204     return;
205   }
207   EXPECT_TRUE(widget_->IsFullscreen());
208   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
210   // Should be zero until the runloop spins.
211   EXPECT_EQ(0, [waiter enterCount]);
212   [waiter waitForEnterCount:1 exitCount:0];
214   // Verify it hasn't exceeded.
215   EXPECT_EQ(1, [waiter enterCount]);
216   EXPECT_EQ(0, [waiter exitCount]);
217   EXPECT_TRUE(widget_->IsFullscreen());
218   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
220   widget_->SetFullscreen(false);
221   EXPECT_FALSE(widget_->IsFullscreen());
222   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
224   [waiter waitForEnterCount:1 exitCount:1];
225   EXPECT_EQ(1, [waiter enterCount]);
226   EXPECT_EQ(1, [waiter exitCount]);
227   EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
230 }  // namespace views