Fix build break
[chromium-blink-merge.git] / ui / base / test / ui_cocoa_test_helper.mm
blob478aa31edaf4156ad4fe132be1d6e757a73d0bef
1 // Copyright (c) 2012 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/base/test/ui_cocoa_test_helper.h"
7 #include "base/debug/debugger.h"
8 #include "base/logging.h"
9 #include "base/test/test_timeouts.h"
11 @implementation CocoaTestHelperWindow
13 - (id)initWithContentRect:(NSRect)contentRect {
14   return [self initWithContentRect:contentRect
15                          styleMask:NSBorderlessWindowMask
16                            backing:NSBackingStoreBuffered
17                              defer:NO];
20 - (id)init {
21   return [self initWithContentRect:NSMakeRect(0, 0, 800, 600)];
24 - (void)dealloc {
25   // Just a good place to put breakpoints when having problems with
26   // unittests and CocoaTestHelperWindow.
27   [super dealloc];
30 - (void)makePretendKeyWindowAndSetFirstResponder:(NSResponder*)responder {
31   EXPECT_TRUE([self makeFirstResponder:responder]);
32   [self setPretendIsKeyWindow:YES];
35 - (void)clearPretendKeyWindowAndFirstResponder {
36   [self setPretendIsKeyWindow:NO];
37   EXPECT_TRUE([self makeFirstResponder:NSApp]);
40 - (void)setPretendIsKeyWindow:(BOOL)flag {
41   pretendIsKeyWindow_ = flag;
44 - (BOOL)isKeyWindow {
45   return pretendIsKeyWindow_;
48 @end
50 namespace ui {
52 CocoaTest::CocoaTest() : called_tear_down_(false), test_window_(nil) {
53   Init();
56 CocoaTest::~CocoaTest() {
57   // Must call CocoaTest's teardown from your overrides.
58   DCHECK(called_tear_down_);
61 void CocoaTest::Init() {
62   // Set the duration of AppKit-evaluated animations (such as frame changes)
63   // to zero for testing purposes. That way they take effect immediately.
64   [[NSAnimationContext currentContext] setDuration:0.0];
66   // The above does not affect window-resize time, such as for an
67   // attached sheet dropping in.  Set that duration for the current
68   // process (this is not persisted).  Empirically, the value of 0.0
69   // is ignored.
70   NSDictionary* dict =
71       [NSDictionary dictionaryWithObject:@"0.01" forKey:@"NSWindowResizeTime"];
72   [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
74   // Collect the list of windows that were open when the test started so
75   // that we don't wait for them to close in TearDown. Has to be done
76   // after BootstrapCocoa is called.
77   initial_windows_ = ApplicationWindows();
80 void CocoaTest::TearDown() {
81   called_tear_down_ = true;
82   // Call close on our test_window to clean it up if one was opened.
83   [test_window_ clearPretendKeyWindowAndFirstResponder];
84   [test_window_ close];
85   test_window_ = nil;
87   // Recycle the pool to clean up any stuff that was put on the
88   // autorelease pool due to window or windowcontroller closures.
89   pool_.Recycle();
91   // Some controls (NSTextFields, NSComboboxes etc) use
92   // performSelector:withDelay: to clean up drag handlers and other
93   // things (Radar 5851458 "Closing a window with a NSTextView in it
94   // should get rid of it immediately").  The event loop must be spun
95   // to get everything cleaned up correctly.  It normally only takes
96   // one to two spins through the event loop to see a change.
98   // NOTE(shess): Under valgrind, -nextEventMatchingMask:* in one test
99   // needed to run twice, once taking .2 seconds, the next time .6
100   // seconds.  The loop exit condition attempts to be scalable.
102   // Get the set of windows which weren't present when the test
103   // started.
104   std::set<NSWindow*> windows_left(WindowsLeft());
106   while (!windows_left.empty()) {
107     // Cover delayed actions by spinning the loop at least once after
108     // this timeout.
109     const NSTimeInterval kCloseTimeoutSeconds =
110         TestTimeouts::action_timeout().InSecondsF();
112     // Cover chains of delayed actions by spinning the loop at least
113     // this many times.
114     const int kCloseSpins = 3;
116     // Track the set of remaining windows so that everything can be
117     // reset if progress is made.
118     std::set<NSWindow*> still_left = windows_left;
120     NSDate* start_date = [NSDate date];
121     bool one_more_time = true;
122     int spins = 0;
123     while (still_left.size() == windows_left.size() &&
124            (spins < kCloseSpins || one_more_time)) {
125       // Check the timeout before pumping events, so that we'll spin
126       // the loop once after the timeout.
127       one_more_time =
128           ([start_date timeIntervalSinceNow] > -kCloseTimeoutSeconds);
130       // Autorelease anything thrown up by the event loop.
131       {
132         base::mac::ScopedNSAutoreleasePool pool;
133         ++spins;
134         NSEvent *next_event = [NSApp nextEventMatchingMask:NSAnyEventMask
135                                                  untilDate:nil
136                                                     inMode:NSDefaultRunLoopMode
137                                                    dequeue:YES];
138         [NSApp sendEvent:next_event];
139         [NSApp updateWindows];
140       }
142       // Refresh the outstanding windows.
143       still_left = WindowsLeft();
144     }
146     // If no progress is being made, log a failure and continue.
147     if (still_left.size() == windows_left.size()) {
148       // NOTE(shess): Failing this expectation means that the test
149       // opened windows which have not been fully released.  Either
150       // there is a leak, or perhaps one of |kCloseTimeoutSeconds| or
151       // |kCloseSpins| needs adjustment.
152       EXPECT_EQ(0U, windows_left.size());
153       for (std::set<NSWindow*>::iterator iter = windows_left.begin();
154            iter != windows_left.end(); ++iter) {
155         const char* desc = [[*iter description] UTF8String];
156         LOG(WARNING) << "Didn't close window " << desc;
157       }
158       break;
159     }
161     windows_left = still_left;
162   }
163   PlatformTest::TearDown();
166 std::set<NSWindow*> CocoaTest::ApplicationWindows() {
167   // This must NOT retain the windows it is returning.
168   std::set<NSWindow*> windows;
170   // Must create a pool here because [NSApp windows] has created an array
171   // with retains on all the windows in it.
172   base::mac::ScopedNSAutoreleasePool pool;
173   NSArray *appWindows = [NSApp windows];
174   for (NSWindow *window in appWindows) {
175     windows.insert(window);
176   }
177   return windows;
180 std::set<NSWindow*> CocoaTest::WindowsLeft() {
181   const std::set<NSWindow*> windows(ApplicationWindows());
182   std::set<NSWindow*> windows_left;
183   std::set_difference(windows.begin(), windows.end(),
184                       initial_windows_.begin(), initial_windows_.end(),
185                       std::inserter(windows_left, windows_left.begin()));
186   return windows_left;
189 CocoaTestHelperWindow* CocoaTest::test_window() {
190   if (!test_window_) {
191     test_window_ = [[CocoaTestHelperWindow alloc] init];
192     if (base::debug::BeingDebugged()) {
193       [test_window_ orderFront:nil];
194     } else {
195       [test_window_ orderBack:nil];
196     }
197   }
198   return test_window_;
201 }  // namespace ui