summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/cocoa_test_helper.mm
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-07-29 17:14:53 +0100
committerBen Murdoch <benm@google.com>2010-08-04 14:29:45 +0100
commitc407dc5cd9bdc5668497f21b26b09d988ab439de (patch)
tree7eaf8707c0309516bdb042ad976feedaf72b0bb1 /chrome/browser/cocoa/cocoa_test_helper.mm
parent0998b1cdac5733f299c12d88bc31ef9c8035b8fa (diff)
downloadexternal_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.zip
external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.gz
external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.bz2
Merge Chromium src@r53293
Change-Id: Ia79acf8670f385cee48c45b0a75371d8e950af34
Diffstat (limited to 'chrome/browser/cocoa/cocoa_test_helper.mm')
-rw-r--r--chrome/browser/cocoa/cocoa_test_helper.mm192
1 files changed, 192 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/cocoa_test_helper.mm b/chrome/browser/cocoa/cocoa_test_helper.mm
new file mode 100644
index 0000000..ae3c7cc
--- /dev/null
+++ b/chrome/browser/cocoa/cocoa_test_helper.mm
@@ -0,0 +1,192 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#import "chrome/browser/chrome_browser_application_mac.h"
+#import "base/logging.h"
+
+@implementation CocoaTestHelperWindow
+
+- (id)initWithContentRect:(NSRect)contentRect {
+ return [self initWithContentRect:contentRect
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO];
+}
+
+- (id)init {
+ return [self initWithContentRect:NSMakeRect(0, 0, 800, 600)];
+}
+
+- (void)dealloc {
+ // Just a good place to put breakpoints when having problems with
+ // unittests and CocoaTestHelperWindow.
+ [super dealloc];
+}
+
+- (void)makePretendKeyWindowAndSetFirstResponder:(NSResponder*)responder {
+ EXPECT_TRUE([self makeFirstResponder:responder]);
+ [self setPretendIsKeyWindow:YES];
+}
+
+- (void)clearPretendKeyWindowAndFirstResponder {
+ [self setPretendIsKeyWindow:NO];
+ EXPECT_TRUE([self makeFirstResponder:NSApp]);
+}
+
+- (void)setPretendIsKeyWindow:(BOOL)flag {
+ pretendIsKeyWindow_ = flag;
+}
+
+- (BOOL)isKeyWindow {
+ return pretendIsKeyWindow_;
+}
+
+@end
+
+CocoaTest::CocoaTest() : called_tear_down_(false), test_window_(nil) {
+ BootstrapCocoa();
+ // Set the duration of AppKit-evaluated animations (such as frame changes)
+ // to zero for testing purposes. That way they take effect immediately.
+ [[NSAnimationContext currentContext] setDuration:0.0];
+ // Collect the list of windows that were open when the test started so
+ // that we don't wait for them to close in TearDown. Has to be done
+ // after BootstrapCocoa is called.
+ initial_windows_ = ApplicationWindows();
+}
+
+CocoaTest::~CocoaTest() {
+ // Must call CocoaTest's teardown from your overrides.
+ DCHECK(called_tear_down_);
+}
+
+void CocoaTest::BootstrapCocoa() {
+ // Look in the framework bundle for resources.
+ FilePath path;
+ PathService::Get(base::DIR_EXE, &path);
+ path = path.Append(chrome::kFrameworkName);
+ mac_util::SetOverrideAppBundlePath(path);
+
+ // Bootstrap Cocoa. It's very unhappy without this.
+ [CrApplication sharedApplication];
+}
+
+void CocoaTest::TearDown() {
+ called_tear_down_ = true;
+ // Call close on our test_window to clean it up if one was opened.
+ [test_window_ close];
+ test_window_ = nil;
+
+ // Recycle the pool to clean up any stuff that was put on the
+ // autorelease pool due to window or windowcontroller closures.
+ pool_.Recycle();
+
+ // Some controls (NSTextFields, NSComboboxes etc) use
+ // performSelector:withDelay: to clean up drag handlers and other
+ // things (Radar 5851458 "Closing a window with a NSTextView in it
+ // should get rid of it immediately"). The event loop must be spun
+ // to get everything cleaned up correctly. It normally only takes
+ // one to two spins through the event loop to see a change.
+
+ // NOTE(shess): Under valgrind, -nextEventMatchingMask:* in one test
+ // needed to run twice, once taking .2 seconds, the next time .6
+ // seconds. The loop exit condition attempts to be scalable.
+
+ // Get the set of windows which weren't present when the test
+ // started.
+ std::set<NSWindow*> windows_left(WindowsLeft());
+
+ while (windows_left.size() > 0) {
+ // Cover delayed actions by spinning the loop at least once after
+ // this timeout. Made large to accomocate valgrind and the wall
+ // clock delay of a [NSApplication endSheet:].
+ const NSTimeInterval kCloseTimeout = 5.0;
+
+ // Cover chains of delayed actions by spinning the loop at least
+ // this many times.
+ const int kCloseSpins = 3;
+
+ // Track the set of remaining windows so that everything can be
+ // reset if progress is made.
+ std::set<NSWindow*> still_left = windows_left;
+
+ NSDate* start_date = [NSDate date];
+ bool one_more_time = true;
+ int spins = 0;
+ while (still_left.size() == windows_left.size() &&
+ (spins < kCloseSpins || one_more_time)) {
+ // Check the timeout before pumping events, so that we'll spin
+ // the loop once after the timeout.
+ one_more_time = ([start_date timeIntervalSinceNow] > -kCloseTimeout);
+
+ // Autorelease anything thrown up by the event loop.
+ {
+ base::ScopedNSAutoreleasePool pool;
+ ++spins;
+ NSEvent *next_event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:nil
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ [NSApp sendEvent:next_event];
+ [NSApp updateWindows];
+ }
+
+ // Refresh the outstanding windows.
+ still_left = WindowsLeft();
+ }
+
+ // If no progress is being made, log a failure and continue.
+ if (still_left.size() == windows_left.size()) {
+ // NOTE(shess): Failing this expectation means that the test
+ // opened windows which have not been fully released. Either
+ // there is a leak, or perhaps one of |kCloseTimeout| or
+ // |kCloseSpins| needs adjustment.
+ EXPECT_EQ(0U, windows_left.size());
+ for (std::set<NSWindow*>::iterator iter = windows_left.begin();
+ iter != windows_left.end(); ++iter) {
+ const char* desc = [[*iter description] UTF8String];
+ LOG(WARNING) << "Didn't close window " << desc;
+ }
+ break;
+ }
+
+ windows_left = still_left;
+ }
+ PlatformTest::TearDown();
+}
+
+std::set<NSWindow*> CocoaTest::ApplicationWindows() {
+ // This must NOT retain the windows it is returning.
+ std::set<NSWindow*> windows;
+
+ // Must create a pool here because [NSApp windows] has created an array
+ // with retains on all the windows in it.
+ base::ScopedNSAutoreleasePool pool;
+ NSArray *appWindows = [NSApp windows];
+ for (NSWindow *window in appWindows) {
+ windows.insert(window);
+ }
+ return windows;
+}
+
+std::set<NSWindow*> CocoaTest::WindowsLeft() {
+ const std::set<NSWindow*> windows(ApplicationWindows());
+ std::set<NSWindow*> windows_left;
+ std::set_difference(windows.begin(), windows.end(),
+ initial_windows_.begin(), initial_windows_.end(),
+ std::inserter(windows_left, windows_left.begin()));
+ return windows_left;
+}
+
+CocoaTestHelperWindow* CocoaTest::test_window() {
+ if (!test_window_) {
+ test_window_ = [[CocoaTestHelperWindow alloc] init];
+ if (DebugUtil::BeingDebugged()) {
+ [test_window_ orderFront:nil];
+ } else {
+ [test_window_ orderBack:nil];
+ }
+ }
+ return test_window_;
+}