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