blob: 27e8d4a3d3708ec16a69e3823002ea878efe2bba [file] [log] [blame]
// Copyright 2014 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.
#include <set>
#import <Cocoa/Cocoa.h>
#include "base/compiler_specific.h"
#import "base/mac/scoped_nsautorelease_pool.h"
#import "base/mac/scoped_nsobject.h"
#import "base/strings/sys_string_conversions.h"
#include "testing/platform_test.h"
// CocoaTestHelperWindow behaves differently from a regular NSWindow in the
// following ways:
// - It allows -isKeyWindow to be manipulated to test things like focus rings
// (which background windows won't normally display).
// - It ignores its real occlusion state and returns a value based on
// pretendIsOccluded.
// - It ignores the system setting for full keyboard access and returns a value
// based on pretendFullKeyboardAccessIsEnabled.
@interface CocoaTestHelperWindow : NSWindow
// Value to return for -isKeyWindow.
@property(nonatomic) BOOL pretendIsKeyWindow;
// Value to return for -occlusionState. Setting posts a
// NSWindowDidChangeOcclusionStateNotification.
@property(nonatomic) BOOL pretendIsOccluded;
// Value to return for -isOnActiveSpace. Posts
// NSWorkspaceActiveSpaceDidChangeNotification when set.
@property(nonatomic) BOOL pretendIsOnActiveSpace;
// Whether to handle the key view loop as if full keyboard access is enabled.
@property(nonatomic) BOOL pretendFullKeyboardAccessIsEnabled;
// Whether to use or ignore the default contraints for window sizing and
// placement.
@property(nonatomic) BOOL useDefaultConstraints;
// All of the window's valid key views, in order.
@property(nonatomic, readonly) NSArray<NSView*>* validKeyViews;
// Init a borderless non-deferred window with a backing store.
- (instancetype)initWithContentRect:(NSRect)contentRect;
// Init with a default frame.
- (instancetype)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;
- (BOOL)isKeyWindow;
- (NSWindowOcclusionState)occlusionState;
namespace ui {
// A test class that all tests that depend on AppKit should inherit from.
// Sets up paths correctly, and makes sure that any windows created in the test
// are closed down properly by the test.
class CocoaTest : public PlatformTest {
~CocoaTest() override;
// 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.
void TearDown() override;
// 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();
// Allows subclasses to do initialization before calling through to the base
// class's initialization.
void Init();
// Return a set of currently open windows. Avoiding NSArray so
// contents aren't retained, the pointer values can only be used for
// comparison purposes. Using std::set to make progress-checking
// convenient.
static std::set<NSWindow*> ApplicationWindows();
// Return a set of windows which are in |ApplicationWindows()| but
// not |initial_windows_|.
std::set<NSWindow*> WindowsLeft();
bool called_tear_down_;
base::mac::ScopedNSAutoreleasePool pool_;
// Windows which existed at the beginning of the test.
std::set<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_;
} // namespace ui
// 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, test_view) \
TEST_F(test_fixture, test_fixture##_TestViewMacroAddRemove) { \
base::scoped_nsobject<NSView> view([test_view retain]); \
EXPECT_EQ([test_window() contentView], [view superview]); \
[view removeFromSuperview]; \
EXPECT_FALSE([view superview]); \
} \
TEST_F(test_fixture, test_fixture##_TestViewMacroDisplay) { \
[test_view display]; \
// A macro which determines the proper float epsilon for a CGFloat.
// A macro which which determines if two CGFloats are equal taking a
// proper epsilon into consideration.
#define CGFLOAT_EQ(expected, actual) \
(actual >= (expected - CGFLOAT_EPSILON) && \
actual <= (expected + CGFLOAT_EPSILON))
// A test support macro which ascertains if two CGFloats are equal.
#define EXPECT_CGFLOAT_EQ(expected, actual) \
EXPECT_TRUE(CGFLOAT_EQ(expected, actual)) << expected << " != " << actual
// A test support macro which compares two NSRects for equality taking
// the float epsilon into consideration.
#define EXPECT_NSRECT_EQ(expected, actual) \
EXPECT_TRUE(CGFLOAT_EQ(expected.origin.x, actual.origin.x) && \
CGFLOAT_EQ(expected.origin.y, actual.origin.y) && \
CGFLOAT_EQ(expected.size.width, actual.size.width) && \
CGFLOAT_EQ(expected.size.height, actual.size.height)) \
<< "Rects do not match: " \
<< base::SysNSStringToUTF8(NSStringFromRect(expected)) \
<< " != " << base::SysNSStringToUTF8(NSStringFromRect(actual))