// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/testing/earl_grey/base_earl_grey_test_case.h"

#import <UIKit/UIKit.h>
#import <objc/runtime.h>

#import "base/feature_list.h"
#import "base/logging.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/testing/earl_grey/app_launch_configuration.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/base_earl_grey_test_case_app_interface.h"
#import "ios/testing/earl_grey/coverage_utils.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ios/testing/earl_grey/system_alert_handler.h"

namespace {

// If true, +setUpForTestCase will be called from -setUp.  This flag is used to
// ensure that +setUpForTestCase is called exactly once per unique XCTestCase
// and is reset in +tearDown.
bool g_needs_set_up_for_test_case = true;
}  // namespace

@implementation BaseEarlGreyTestCase

+ (BOOL)forceRestartAndWipe {
  return YES;
}

+ (void)setUpForTestCase {
}

+ (void)setUp {
  NSArray<NSString*>* blockedURLs = @[
    @".*app-measurement\\.com.*",
    @".*google\\.com.*",
    @".*googleapis\\.com.*",
    @".*app-analytics-services\\.com.*",
  ];
  [[GREYConfiguration sharedConfiguration]
          setValue:blockedURLs
      forConfigKey:kGREYConfigKeyBlockedURLRegex];

  // Configuration for not tracking hidden animations. By default, all hidden
  // animations are tracked, and these sometimes cause flake. Set to YES so
  // tracking *should not* happen for hidden animations.
  [[GREYConfiguration sharedConfiguration]
          setValue:@YES
      forConfigKey:kGREYConfigKeyIgnoreHiddenAnimations];
  [[GREYConfiguration sharedConfiguration]
          setValue:@YES
      forConfigKey:kGREYConfigKeyAutoUntrackMDCActivityIndicators];
}

- (BOOL)loadMinimalAppUI {
  return NO;
}

// Invoked upon starting each test method in a test case.
// Launches the app under test if necessary.
- (void)setUp {
  [super setUp];

  // No need to continue after failure. The app is re-launched between tests.
  self.continueAfterFailure = NO;

  // Before starting a new test, relaunch the app and wipe the profile.
  AppLaunchConfiguration config = [self appConfigurationForTestCase];
  if ([BaseEarlGreyTestCase forceRestartAndWipe]) {
    config.relaunch_policy = RelaunchPolicy::ForceRelaunchByKilling;
    config.additional_args.push_back(std::string("-EGTestWipeProfile"));
  }

  if ([self loadMinimalAppUI]) {
    config.additional_args.push_back(std::string("-load-minimal-app-ui"));
  }

  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
  [SystemAlertHandler handleSystemAlertIfVisible];

  NSString* logFormat = @"*********************************\nStarting test: %@";
  [BaseEarlGreyTestCaseAppInterface
      logMessage:[NSString stringWithFormat:logFormat, self.name]];

  // Calling XCTFail before the application is launched does not assert
  // properly, so failing upon detection of overriding +setUp is delayed until
  // here. See +setUp below for details on why overriding +setUp causes a
  // failure.
  [self failIfSetUpIsOverridden];

  if (g_needs_set_up_for_test_case) {
    g_needs_set_up_for_test_case = false;
    [[self class] setUpForTestCase];
  }
}

+ (void)tearDown {
  g_needs_set_up_for_test_case = true;
  [super tearDown];
}

- (AppLaunchConfiguration)appConfigurationForTestCase {
  return AppLaunchConfiguration();
}

#pragma mark - Private

// Prevents tests inheriting from this class from putting logic in +setUp.
// +setUp will be called before the application is launched,
// and thus is not suitable for most test case setup. Inheriting tests should
// migrate their +setUp logic to use the equivalent -setUpForTestCase.
- (void)failIfSetUpIsOverridden {
  if ([[BaseEarlGreyTestCase class] methodForSelector:@selector(setUp)] !=
      [[self class] methodForSelector:@selector(setUp)]) {
    XCTFail(@"EG2 test class %@ inheriting from BaseEarlGreyTestCase "
            @"should not override +setUp, as it is called before the "
            @"test application is launched. Please convert your "
            @"+setUp method to +setUpForTestCase.",
            NSStringFromClass([self class]));
  }
}

@end
