summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa
diff options
context:
space:
mode:
authordmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-23 19:41:04 +0000
committerdmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-23 19:41:04 +0000
commitc743b0591ddaa6b2d550be938153c273853e167e (patch)
tree7aec146b0deef8ef7899d1c12237f7809cc0c731 /chrome/browser/cocoa
parent72512a9af1270285600905f0084cd0cd730688b2 (diff)
downloadchromium_src-c743b0591ddaa6b2d550be938153c273853e167e.zip
chromium_src-c743b0591ddaa6b2d550be938153c273853e167e.tar.gz
chromium_src-c743b0591ddaa6b2d550be938153c273853e167e.tar.bz2
Basic test class that is going to replace CocoaTestHelper and CocoaNoWindowTestHelper.
Is responsible for bootstrapping cocoa and verifying that all windows are closed down correctly between tests. Also includes a macro for doing some very repetitive view testing that we had multiple copies of. I included a single changed over test so that you can see the difference. BUG=none TEST=none Review URL: http://codereview.chromium.org/327010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29928 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r--chrome/browser/cocoa/cocoa_test_helper.h100
-rw-r--r--chrome/browser/cocoa/cocoa_test_helper.mm128
-rw-r--r--chrome/browser/cocoa/sad_tab_view_unittest.mm31
3 files changed, 229 insertions, 30 deletions
diff --git a/chrome/browser/cocoa/cocoa_test_helper.h b/chrome/browser/cocoa/cocoa_test_helper.h
index 1627391..f980790 100644
--- a/chrome/browser/cocoa/cocoa_test_helper.h
+++ b/chrome/browser/cocoa/cocoa_test_helper.h
@@ -6,18 +6,21 @@
#define CHROME_BROWSER_COCOA_COCOA_TEST_HELPER_H_
#import <Cocoa/Cocoa.h>
+#include <vector>
#include "base/debug_util.h"
#include "base/file_path.h"
#include "base/mac_util.h"
#include "base/path_service.h"
+#import "base/scoped_nsautorelease_pool.h"
#import "base/scoped_nsobject.h"
+#include "base/scoped_ptr.h"
#include "chrome/common/chrome_constants.h"
+#include "testing/platform_test.h"
// Background windows normally will not display things such as focus
// rings. This class allows -isKeyWindow to be manipulated to test
// such things.
-
@interface CocoaTestHelperWindow : NSWindow {
@private
BOOL pretendIsKeyWindow_;
@@ -29,6 +32,15 @@
// Init with a default frame.
- (id)init;
+// Sets the responder passed in as first responder, and sets the window
+// so that it will return "YES" if asked if it key window. It does not actually
+// make the window key.
+- (void)makePretendKeyWindowAndSetFirstResponder:(NSResponder*)responder;
+
+// Clears the first responder duty for the window and returns the window
+// to being non-key.
+- (void)clearPretendKeyWindowAndFirstResponder;
+
// Set value to return for -isKeyWindow.
- (void)setPretendIsKeyWindow:(BOOL)isKeyWindow;
@@ -36,6 +48,77 @@
@end
+// A test class that all tests that depend on AppKit should inherit from.
+// Sets up NSApplication and paths correctly, and makes sure that any windows
+// created in the test are closed down properly by the test. If you need to
+// inherit from a different test class, but need to set up the AppKit runtime
+// environment, you can call BootstrapCocoa directly from your test class. You
+// will have to deal with windows on your own though.
+class CocoaTest : public PlatformTest {
+ public:
+ // Sets up AppKit and paths correctly for unit tests. If you can't inherit
+ // from CocoaTest but are going to be using any AppKit features directly,
+ // or indirectly, you should be calling this from the c'tor or SetUp methods
+ // of your test class.
+ static void BootstrapCocoa();
+
+ CocoaTest();
+ virtual ~CocoaTest();
+
+ // Must be called by subclasses that override TearDown. We verify that it
+ // is called in our destructor. Takes care of making sure that all windows
+ // are closed off correctly. If your tests open windows, they must be sure
+ // to close them before CocoaTest::TearDown is called. A standard way of doing
+ // this would be to create them in SetUp (after calling CocoaTest::Setup) and
+ // then close them in TearDown before calling CocoaTest::TearDown.
+ virtual void TearDown();
+
+ // Retuns a test window that can be used by views and other UI objects
+ // as part of their tests. Is created lazily, and will be closed correctly
+ // in CocoaTest::TearDown. Note that it is a CocoaTestHelperWindow which
+ // has special handling for being Key.
+ CocoaTestHelperWindow* test_window();
+
+ private:
+ // Return a vector of currently open windows. Note that it is a vector
+ // instead of an NSArray because we don't want any retains placed on the
+ // windows in it and that the windows in this list may no longer be valid
+ // NSWindows any time after this returns. You can only use the pointer values
+ // in the vector for comparison purposes.
+ static std::vector<NSWindow*> ApplicationWindows();
+
+ bool called_tear_down_;
+ base::ScopedNSAutoreleasePool pool_;
+ std::vector<NSWindow*> initial_windows_;
+ // Strong. Lazily created. This isn't wrapped in a scoped_nsobject because
+ // we want to call [close] to destroy it rather than calling [release]. We
+ // want to verify that [close] is actually removing our window and that it's
+ // not hanging around because releaseWhenClosed was set to "no" on the window.
+ // It isn't wrapped in a different wrapper class to close it because we
+ // need to close it at a very specific time; just before we enter our clean
+ // up loop in TearDown.
+ CocoaTestHelperWindow* test_window_;
+};
+
+// A macro defining a standard set of tests to run on a view. Since we can't
+// inherit tests, this macro saves us a lot of duplicate code. Handles simply
+// displaying the view to make sure it won't crash, as well as removing it
+// from a window. All tests that work with NSView subclasses and/or
+// NSViewController subclasses should use it.
+#define TEST_VIEW(test_fixture, view_member_name) \
+ TEST_F(test_fixture, AddRemove##test_fixture) { \
+ scoped_nsobject<NSView> view([view_member_name retain]); \
+ EXPECT_EQ([test_window() contentView], [view_member_name superview]); \
+ [view_member_name removeFromSuperview]; \
+ EXPECT_FALSE([view_member_name superview]); \
+ } \
+ TEST_F(test_fixture, Display##test_fixture) { \
+ [view_member_name display]; \
+ }
+
+// The classes below are deprecated and will be removed shortly. Do not write
+// any tests based on them.
+
// A class that initializes Cocoa and sets up resources for many of our
// Cocoa controller unit tests. It does several key things:
// - Creates and displays an empty Cocoa window for views to live in
@@ -48,10 +131,10 @@
// testing::Test.
// Provides the Cocoa goodness without the extraneous window.
-// TODO(shess): It might make more sense to have CocoaTest as a
-// PlatformTest subclass which adds the Cocoa magic, then
-// CocoaViewTest as a further subclass which provides a convenience
-// window.
+
+// DEPRECATED
+// TODO(dmaclach): remove as soon as I can get my other CLs in that get rid
+// of any dependencies on this. 10/30/09 at the latest.
class CocoaNoWindowTestHelper {
public:
CocoaNoWindowTestHelper() {
@@ -70,6 +153,9 @@ class CocoaNoWindowTestHelper {
}
};
+// DEPRECATED
+// TODO(dmaclach): remove as soon as I can get my other CLs in that get rid
+// of any dependencies on this. 10/30/09 at the latest.
class CocoaTestHelper : public CocoaNoWindowTestHelper {
public:
CocoaTestHelper() {
@@ -88,14 +174,14 @@ class CocoaTestHelper : public CocoaNoWindowTestHelper {
// Set |window_| to pretend to be key and make |aView| its
// firstResponder.
void makeFirstResponder(NSView* aView) {
- [window_ setPretendIsKeyWindow:YES];
[window_ makeFirstResponder:aView];
+ [window_ setPretendIsKeyWindow:YES];
}
// Clear |window_| firstResponder and stop pretending to be key.
void clearFirstResponder() {
- [window_ makeFirstResponder:nil];
[window_ setPretendIsKeyWindow:NO];
+ [window_ makeFirstResponder:nil];
}
private:
diff --git a/chrome/browser/cocoa/cocoa_test_helper.mm b/chrome/browser/cocoa/cocoa_test_helper.mm
index efe5c5c..462b10a 100644
--- a/chrome/browser/cocoa/cocoa_test_helper.mm
+++ b/chrome/browser/cocoa/cocoa_test_helper.mm
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#import "base/logging.h"
@implementation CocoaTestHelperWindow
@@ -17,6 +18,22 @@
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;
}
@@ -26,3 +43,114 @@
}
@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.
+ [NSApplication 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.
+ // Note that many controls (NSTextFields, NSComboboxes etc) may call
+ // performSelector:withDelay: to clean up drag handlers and other things.
+ // We must spin the event loop a bit to make sure that everything gets cleaned
+ // up correctly. We will wait up to one second for windows to clean themselves
+ // up (normally only takes one to two loops through the event loop).
+ // Radar 5851458 "Closing a window with a NSTextView in it should get rid of
+ // it immediately"
+ pool_.Recycle();
+ NSDate *start_date = [NSDate date];
+ const std::vector<NSWindow*> windows_waiting(ApplicationWindows());
+
+ bool loop = windows_waiting.size() > 0;
+ while (loop) {
+ {
+ // Need an autorelease pool to wrap our event loop.
+ base::ScopedNSAutoreleasePool pool;
+ NSEvent *next_event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:nil
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ [NSApp sendEvent:next_event];
+ [NSApp updateWindows];
+ }
+ // Check the windows after we have released the event loop pool so that
+ // all retains are cleaned up.
+ const std::vector<NSWindow*> current_windows(ApplicationWindows());
+ std::vector<NSWindow*> windows_left;
+ std::set_difference(current_windows.begin(),
+ current_windows.end(),
+ initial_windows_.begin(),
+ initial_windows_.end(),
+ inserter(windows_left, windows_left.begin()));
+
+ if (windows_left.size() == 0) {
+ // All our windows are closed.
+ break;
+ }
+ if ([start_date timeIntervalSinceNow] < -1.0) {
+ // Took us over a second to shut down, and windows still exist.
+ // Log a failure and continue.
+ EXPECT_EQ(windows_left.size(), 0U);
+ for (size_t i = 0; i < windows_left.size(); ++i) {
+ const char* desc = [[windows_left[i] description] UTF8String];
+ LOG(WARNING) << "Didn't close window " << desc;
+ }
+ break;
+ }
+ }
+ PlatformTest::TearDown();
+}
+
+std::vector<NSWindow*> CocoaTest::ApplicationWindows() {
+ // This must NOT retain the windows it is returning.
+ std::vector<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.push_back(window);
+ }
+ return windows;
+}
+
+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_;
+}
diff --git a/chrome/browser/cocoa/sad_tab_view_unittest.mm b/chrome/browser/cocoa/sad_tab_view_unittest.mm
index 49c24e3..382fdb6 100644
--- a/chrome/browser/cocoa/sad_tab_view_unittest.mm
+++ b/chrome/browser/cocoa/sad_tab_view_unittest.mm
@@ -2,39 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#import <Cocoa/Cocoa.h>
-
-#include "base/scoped_nsobject.h"
#import "chrome/browser/cocoa/sad_tab_view.h"
#import "chrome/browser/cocoa/cocoa_test_helper.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
namespace {
-class SadTabViewTest : public PlatformTest {
+class SadTabViewTest : public CocoaTest {
public:
SadTabViewTest() {
- NSRect content_frame = [cocoa_helper_.contentView() frame];
- view_.reset([[SadTabView alloc] initWithFrame:content_frame]);
- [cocoa_helper_.contentView() addSubview:view_.get()];
+ NSRect content_frame = [[test_window() contentView] frame];
+ scoped_nsobject<SadTabView> view([[SadTabView alloc]
+ initWithFrame:content_frame]);
+ view_ = view.get();
+ [[test_window() contentView] addSubview:view_];
}
- CocoaTestHelper cocoa_helper_; // Inits Cocoa, creates window, etc...
- scoped_nsobject<SadTabView> view_;
+ SadTabView* view_; // Weak. Owned by the view hierarchy.
};
-// Test adding/removing from the view hierarchy, mostly to ensure nothing
-// leaks or crashes.
-TEST_F(SadTabViewTest, AddRemove) {
- EXPECT_EQ(cocoa_helper_.contentView(), [view_ superview]);
- [view_.get() removeFromSuperview];
- EXPECT_FALSE([view_ superview]);
-}
-
-// Test drawing, mostly to ensure nothing leaks or crashes.
-TEST_F(SadTabViewTest, Display) {
- [view_ display];
-}
+TEST_VIEW(SadTabViewTest, view_)
} // namespace