blob: 6da92b8113d022d9170950e57a6769f5aec1dc03 [file] [log] [blame]
// Copyright 2016 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 "ios/chrome/test/earl_grey/chrome_test_case.h"
#import <objc/runtime.h>
#import <EarlGrey/EarlGrey.h>
#include "base/mac/scoped_block.h"
#include "base/strings/sys_string_conversions.h"
#import "ios/chrome/test/app/chrome_test_util.h"
#include "ios/chrome/test/app/settings_test_util.h"
#include "ios/chrome/test/app/signin_test_util.h"
#import "ios/chrome/test/app/sync_test_util.h"
#import "ios/chrome/test/app/tab_test_util.h"
#import "ios/web/public/test/http_server/http_server.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
NSString* const kFlakyEarlGreyTestTargetSuffix = @"_flaky_egtests";
// Contains a list of test names that run in multitasking test suite.
NSArray* whiteListedMultitaskingTests = @[
// Integration tests
@"testContextMenuOpenInNewTab", // ContextMenuTestCase
@"testSwitchToMain", // CookiesTestCase
@"testSwitchToIncognito", // CookiesTestCase
@"testFindDefaultFormAssistControls", // FormInputTestCase
@"testTabDeletion", // TabUsageRecorderTestCase
@"testAutoTranslate", // TranslateTestCase
// Settings tests
@"testSignInPopUpAccountOnSyncSettings", // AccountCollectionsTestCase
@"testAutofillProfileEditing", // AutofillSettingsTestCase
@"testAccessibilityOfBlockPopupSettings", // BlockPopupsTestCase
@"testClearCookies", // SettingsTestCase
@"testAccessibilityOfTranslateSettings", // TranslateUITestCase
// UI tests
@"testActivityServiceControllerPrintAfterRedirectionToUnprintablePage",
// ActivityServiceControllerTestCase
@"testDismissOnDestroy", // AlertCoordinatorTestCase
@"testAddRemoveBookmark", // BookmarksTestCase
@"testJavaScriptInOmnibox", // BrowserViewControllerTestCase
@"testChooseCastReceiverChooser", // CastReceiverTestCase
@"testErrorPage", // ErrorPageTestCase
@"testFindInPage", // FindInPageTestCase
@"testDismissFirstRun", // FirstRunTestCase
@"testLongPDFScroll", // FullscreenTestCase
@"testDeleteHistory", // HistoryUITestCase
@"testInfobarsDismissOnNavigate", // InfobarTestCase
@"testShowJavaScriptAlert", // JavaScriptDialogTestCase
@"testKeyboardCommands_RecentTabsPresented", // KeyboardCommandsTestCase
@"testAccessibilityOnMostVisited", // NewTabPageTestCase
@"testPrintNormalPage", // PrintControllerTestCase
@"testQRScannerUIIsShown", // QRScannerViewControllerTestCase
@"testMarkMixedEntriesRead", // ReadingListTestCase
@"testClosedTabAppearsInRecentTabsPanel",
// RecentTabsPanelControllerTestCase
@"testSafeModeSendingCrashReport", // SafeModeTestCase
@"testSignInOneUser", // SigninInteractionControllerTestCase
@"testSwitchTabs", // StackViewTestCase
@"testTabStripSwitchTabs", // TabStripTestCase
@"testTabHistoryMenu", // TabHistoryPopupControllerTestCase
@"testEnteringTabSwitcher", // TabSwitcherControllerTestCase
@"testEnterURL", // ToolbarTestCase
@"testOpenAndCloseToolsMenu", // ToolsPopupMenuTestCase
@"testUserFeedbackPageOpenPrivacyPolicy", // UserFeedbackTestCase
@"testVersion", // WebUITestCase
];
const CFTimeInterval kDrainTimeout = 5;
} // namespace
@interface ChromeTestCase () {
// Block to be executed during object tearDown.
ProceduralBlock _tearDownHandler;
BOOL _isHTTPServerStopped;
BOOL _isMockAuthenticationDisabled;
}
// Cleans up mock authentication.
+ (void)disableMockAuthentication;
// Sets up mock authentication.
+ (void)enableMockAuthentication;
// Stops the HTTP server. Should only be called when the server is running.
+ (void)stopHTTPServer;
// Starts the HTTP server. Should only be called when the server is stopped.
+ (void)startHTTPServer;
// Returns a NSArray of test names in this class that contain the prefix
// "FLAKY_".
+ (NSArray*)flakyTestNames;
// Returns a NSArray of test names in this class for multitasking test suite.
+ (NSArray*)multitaskingTestNames;
@end
@implementation ChromeTestCase
// Overrides testInvocations so the set of tests run can be modified, as
// necessary.
+ (NSArray*)testInvocations {
NSError* error = nil;
[[EarlGrey selectElementWithMatcher:grey_systemAlertViewShown()]
assertWithMatcher:grey_nil()
error:&error];
if (error != nil) {
NSLog(@"System alert view is present, so skipping all tests!");
return @[];
}
// Return specific list of tests based on the target.
NSString* targetName = [NSBundle mainBundle].infoDictionary[@"CFBundleName"];
if ([targetName hasSuffix:kFlakyEarlGreyTestTargetSuffix]) {
// Only run FLAKY_ tests for flaky test suites.
return [self flakyTestNames];
} else if ([targetName isEqualToString:@"ios_chrome_multitasking_egtests"]) {
// Only run white listed tests for the multitasking test suite.
return [self multitaskingTestNames];
} else {
return [super testInvocations];
}
}
// Set up called once for the class, to dismiss anything displayed on startup
// and revert browser settings to default. It also starts the HTTP server and
// enables mock authentication.
+ (void)setUp {
[super setUp];
[[self class] startHTTPServer];
[[self class] enableMockAuthentication];
// Sometimes on start up there can be infobars (e.g. restore session), so
// ensure the UI is in a clean state.
[self removeAnyOpenMenusAndInfoBars];
[self closeAllTabs];
chrome_test_util::SetContentSettingsBlockPopups(CONTENT_SETTING_DEFAULT);
}
// Tear down called once for the class, to shutdown mock authentication and
// the HTTP server.
+ (void)tearDown {
[[self class] disableMockAuthentication];
[[self class] stopHTTPServer];
[super tearDown];
}
// Set up called once per test, to open a new tab.
- (void)setUp {
[super setUp];
_isHTTPServerStopped = NO;
_isMockAuthenticationDisabled = NO;
_tearDownHandler = nil;
chrome_test_util::OpenNewTab();
[[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
}
// Tear down called once per test, to close all tabs and menus, and clear the
// tracked tests accounts. It also makes sure mock authentication and the HTTP
// server are running.
- (void)tearDown {
if (_tearDownHandler) {
_tearDownHandler();
}
// Clear any remaining test accounts and signed in users.
chrome_test_util::SignOutAndClearAccounts();
// Re-start anything that was disabled this test, so it is running when the
// next test starts.
if (_isHTTPServerStopped) {
[[self class] startHTTPServer];
_isHTTPServerStopped = NO;
}
if (_isMockAuthenticationDisabled) {
[[self class] enableMockAuthentication];
_isMockAuthenticationDisabled = NO;
}
// Clean up any UI that may remain open so the next test starts in a clean
// state.
[[self class] removeAnyOpenMenusAndInfoBars];
[[self class] closeAllTabs];
[super tearDown];
}
#pragma mark - Public methods
- (void)setTearDownHandler:(ProceduralBlock)tearDownHandler {
// Enforce that only one |_tearDownHandler| is set per test.
DCHECK(!_tearDownHandler);
_tearDownHandler = [tearDownHandler copy];
}
+ (void)removeAnyOpenMenusAndInfoBars {
chrome_test_util::RemoveAllInfoBars();
chrome_test_util::ClearPresentedState();
// After programatically removing UI elements, allow Earl Grey's
// UI synchronization to become idle, so subsequent steps won't start before
// the UI is in a good state.
[[GREYUIThreadExecutor sharedInstance]
drainUntilIdleWithTimeout:kDrainTimeout];
}
+ (void)closeAllTabs {
chrome_test_util::CloseAllTabs();
[[GREYUIThreadExecutor sharedInstance]
drainUntilIdleWithTimeout:kDrainTimeout];
}
- (void)disableMockAuthentication {
// Enforce that disableMockAuthentication can only be called once.
DCHECK(!_isMockAuthenticationDisabled);
[[self class] disableMockAuthentication];
_isMockAuthenticationDisabled = YES;
}
- (void)stopHTTPServer {
// Enforce that the HTTP server can only be stopped once per test. It should
// not be stopped if it is not running.
DCHECK(!_isHTTPServerStopped);
[[self class] stopHTTPServer];
_isHTTPServerStopped = YES;
}
#pragma mark - Private methods
+ (void)disableMockAuthentication {
// Make sure local data is cleared, before disabling mock authentication,
// where data may be sent to real servers.
chrome_test_util::SignOutAndClearAccounts();
chrome_test_util::TearDownFakeSyncServer();
chrome_test_util::TearDownMockAccountReconcilor();
chrome_test_util::TearDownMockAuthentication();
}
+ (void)enableMockAuthentication {
chrome_test_util::SetUpMockAuthentication();
chrome_test_util::SetUpMockAccountReconcilor();
chrome_test_util::SetUpFakeSyncServer();
}
+ (void)stopHTTPServer {
web::test::HttpServer& server = web::test::HttpServer::GetSharedInstance();
DCHECK(server.IsRunning());
server.Stop();
}
+ (void)startHTTPServer {
web::test::HttpServer& server = web::test::HttpServer::GetSharedInstance();
server.StartOrDie();
}
+ (NSArray*)flakyTestNames {
const char kFlakyTestPrefix[] = "FLAKY";
unsigned int count = 0;
Method* methods = class_copyMethodList(self, &count);
NSMutableArray* flakyTestNames = [NSMutableArray array];
for (unsigned int i = 0; i < count; i++) {
SEL selector = method_getName(methods[i]);
if (std::string(sel_getName(selector)).find(kFlakyTestPrefix) == 0) {
NSMethodSignature* methodSignature =
[self instanceMethodSignatureForSelector:selector];
NSInvocation* invocation =
[NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = selector;
[flakyTestNames addObject:invocation];
}
}
free(methods);
return flakyTestNames;
}
+ (NSArray*)multitaskingTestNames {
unsigned int count = 0;
Method* methods = class_copyMethodList(self, &count);
NSMutableArray* multitaskingTestNames = [NSMutableArray array];
for (unsigned int i = 0; i < count; i++) {
SEL selector = method_getName(methods[i]);
if ([whiteListedMultitaskingTests
containsObject:base::SysUTF8ToNSString(sel_getName(selector))]) {
NSMethodSignature* methodSignature =
[self instanceMethodSignatureForSelector:selector];
NSInvocation* invocation =
[NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = selector;
[multitaskingTestNames addObject:invocation];
}
}
free(methods);
return multitaskingTestNames;
}
@end