blob: 13d0181564567ed315b3590bacea6fe0b7b12b9e [file] [log] [blame]
// Copyright 2021 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/chrome/app/safe_mode_app_state_agent.h"
#import "base/ios/block_types.h"
#import "base/test/ios/wait_util.h"
#import "ios/chrome/app/application_delegate/app_init_stage_test_utils.h"
#import "ios/chrome/app/application_delegate/app_state+Testing.h"
#import "ios/chrome/app/safe_mode_app_state_agent+private.h"
#import "ios/chrome/browser/safe_mode/ui_bundled/safe_mode_coordinator.h"
#import "ios/chrome/browser/shared/coordinator/scene/test/fake_scene_state.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/test/block_cleanup_test.h"
#import "ios/chrome/test/scoped_key_window.h"
#import "ios/testing/scoped_block_swizzler.h"
#import "ios/web/public/test/web_task_environment.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
namespace {
// A block that takes self as argument and return a BOOL.
typedef BOOL (^DecisionBlock)(id self);
} // namespace
class SafeModeAppStateAgentTest : public BlockCleanupTest {
protected:
SafeModeAppStateAgentTest() {
profile_ = TestProfileIOS::Builder().Build();
window_ = [OCMockObject mockForClass:[UIWindow class]];
AppState* app_state = [[AppState alloc] initWithStartupInformation:nil];
app_state_mock_ = OCMPartialMock(app_state);
main_scene_state_ =
[[FakeSceneState alloc] initWithAppState:app_state
profile:profile_.get()];
main_scene_state_.window = window_;
agent_ = [[SafeModeAppAgent alloc] init];
[agent_ setAppState:app_state_mock_];
}
~SafeModeAppStateAgentTest() override {
[main_scene_state_ shutdown];
main_scene_state_ = nil;
}
void SwizzleSafeModeShouldStart(BOOL shouldStart) {
safe_mode_swizzle_block_ = ^BOOL(id self) {
return shouldStart;
};
safe_mode_swizzler_.reset(new ScopedBlockSwizzler(
[SafeModeCoordinator class], @selector(shouldStart),
safe_mode_swizzle_block_));
}
// Iterate through the init stages from `curr_stage` up to
// `dest_stage`.
void IterateToStage(AppInitStage curr_stage, AppInitStage dest_stage) {
DCHECK_GE(dest_stage, curr_stage);
while (dest_stage != curr_stage) {
const AppInitStage from_stage = curr_stage;
curr_stage = NextAppInitStage(from_stage);
[app_state_mock_ setInitStage:curr_stage];
[agent_ appState:app_state_mock_ didTransitionFromInitStage:from_stage];
}
}
web::WebTaskEnvironment task_environment_;
std::unique_ptr<TestProfileIOS> profile_;
id app_state_mock_;
FakeSceneState* main_scene_state_;
SafeModeAppAgent* agent_;
std::unique_ptr<ScopedBlockSwizzler> safe_mode_swizzler_;
DecisionBlock safe_mode_swizzle_block_;
id window_;
};
TEST_F(SafeModeAppStateAgentTest, startSafeMode) {
[[window_ expect] makeKeyAndVisible];
[[[window_ stub] andReturn:nil] rootViewController];
[[window_ stub] setRootViewController:[OCMArg any]];
SwizzleSafeModeShouldStart(YES);
IterateToStage(AppInitStage::kStart, AppInitStage::kSafeMode);
[agent_ sceneState:main_scene_state_
transitionedToActivationLevel:SceneActivationLevelForegroundActive];
// Second call that should be deduped.
[agent_ sceneState:main_scene_state_
transitionedToActivationLevel:SceneActivationLevelForegroundActive];
EXPECT_OCMOCK_VERIFY(window_);
// Exit safe mode.
[agent_ coordinatorDidExitSafeMode:agent_.safeModeCoordinator];
EXPECT_EQ(nil, agent_.safeModeCoordinator);
}
TEST_F(SafeModeAppStateAgentTest, dontStartSafeModeBecauseNotNeeded) {
[[app_state_mock_ expect] queueTransitionToNextInitStage];
SwizzleSafeModeShouldStart(NO);
IterateToStage(AppInitStage::kStart, AppInitStage::kSafeMode);
main_scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_OCMOCK_VERIFY(app_state_mock_);
}
TEST_F(SafeModeAppStateAgentTest, dontStartSafeModeBecauseNotActiveLevel) {
[[app_state_mock_ reject] queueTransitionToNextInitStage];
SwizzleSafeModeShouldStart(YES);
IterateToStage(AppInitStage::kStart, AppInitStage::kSafeMode);
main_scene_state_.activationLevel = SceneActivationLevelForegroundInactive;
EXPECT_OCMOCK_VERIFY(app_state_mock_);
}
TEST_F(SafeModeAppStateAgentTest,
dontStartSafeModeBecauseFirstSceneHasAlreadyActivated) {
[[app_state_mock_ reject] queueTransitionToNextInitStage];
SwizzleSafeModeShouldStart(YES);
IterateToStage(AppInitStage::kStart, AppInitStage::kSafeMode);
agent_.firstSceneHasActivated = YES;
main_scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_OCMOCK_VERIFY(app_state_mock_);
}