[go: nahoru, domu]

blob: ae86b979159fea6983c3033e2b0fd95e93f1fe9a [file] [log] [blame]
// Copyright 2021 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 "ash/login/ui/smart_lock_auth_factor_model.h"
#include "ash/login/ui/auth_factor_model.h"
#include "ash/login/ui/auth_icon_view.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/test/ash_test_base.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
namespace {
using AuthFactorState = AuthFactorModel::AuthFactorState;
struct LabelTestcase {
SmartLockState state;
absl::optional<bool> can_use_pin;
int label_id;
int accessible_name_id;
};
constexpr LabelTestcase kLabelTestcases[] = {
{SmartLockState::kDisabled, /*can_use_pin=*/true,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED},
{SmartLockState::kDisabled, /*can_use_pin=*/false,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED},
{SmartLockState::kInactive, /*can_use_pin=*/true,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED},
{SmartLockState::kInactive, /*can_use_pin=*/false,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED},
{SmartLockState::kPasswordReentryRequired, /*can_use_pin=*/true,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED},
{SmartLockState::kPasswordReentryRequired, /*can_use_pin=*/false,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED},
{SmartLockState::kPrimaryUserAbsent, /*can_use_pin=*/true,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED},
{SmartLockState::kPrimaryUserAbsent, /*can_use_pin=*/false,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED},
{SmartLockState::kPhoneNotAuthenticated, /*can_use_pin=*/true,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_OR_PIN_REQUIRED},
{SmartLockState::kPhoneNotAuthenticated, /*can_use_pin=*/false,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED,
IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED},
{SmartLockState::kBluetoothDisabled, /*can_use_pin=*/absl::nullopt,
IDS_SMART_LOCK_LABEL_NO_BLUETOOTH, IDS_SMART_LOCK_LABEL_NO_BLUETOOTH},
{SmartLockState::kPhoneNotLockable, /*can_use_pin=*/absl::nullopt,
IDS_SMART_LOCK_LABEL_NO_PHONE_LOCK_SCREEN,
IDS_SMART_LOCK_LABEL_NO_PHONE_LOCK_SCREEN},
{SmartLockState::kConnectingToPhone, /*can_use_pin=*/absl::nullopt,
IDS_SMART_LOCK_LABEL_LOOKING_FOR_PHONE,
IDS_SMART_LOCK_LABEL_LOOKING_FOR_PHONE},
{SmartLockState::kPhoneFoundLockedAndDistant, /*can_use_pin=*/absl::nullopt,
IDS_SMART_LOCK_LABEL_PHONE_TOO_FAR, IDS_SMART_LOCK_LABEL_PHONE_TOO_FAR},
{SmartLockState::kPhoneFoundUnlockedAndDistant,
/*can_use_pin=*/absl::nullopt, IDS_SMART_LOCK_LABEL_PHONE_TOO_FAR,
IDS_SMART_LOCK_LABEL_PHONE_TOO_FAR},
{SmartLockState::kPhoneNotFound, /*can_use_pin=*/absl::nullopt,
IDS_SMART_LOCK_LABEL_NO_PHONE, IDS_SMART_LOCK_LABEL_NO_PHONE},
{SmartLockState::kPhoneFoundLockedAndProximate,
/*can_use_pin=*/absl::nullopt, IDS_SMART_LOCK_LABEL_PHONE_LOCKED,
IDS_SMART_LOCK_LABEL_PHONE_LOCKED},
{SmartLockState::kPhoneAuthenticated, /*can_use_pin=*/absl::nullopt,
IDS_AUTH_FACTOR_LABEL_CLICK_TO_ENTER,
IDS_AUTH_FACTOR_LABEL_CLICK_TO_ENTER},
};
} // namespace
class SmartLockAuthFactorModelUnittest : public AshTestBase {
public:
SmartLockAuthFactorModelUnittest() = default;
SmartLockAuthFactorModelUnittest(const SmartLockAuthFactorModelUnittest&) =
delete;
SmartLockAuthFactorModelUnittest& operator=(
const SmartLockAuthFactorModelUnittest&) = delete;
~SmartLockAuthFactorModelUnittest() override = default;
protected:
void InitializeSmartLockAuthFactorModel(
SmartLockState initial_state = SmartLockState::kConnectingToPhone) {
smart_lock_model_ = std::make_unique<SmartLockAuthFactorModel>(
initial_state,
base::BindRepeating(
&SmartLockAuthFactorModelUnittest::ArrowButtonTapCallback,
base::Unretained(this)));
model_ = smart_lock_model_.get();
model_->Init(&icon_, base::BindRepeating(
&SmartLockAuthFactorModelUnittest::OnStateChanged,
base::Unretained(this)));
}
void OnStateChanged() { on_state_changed_called_ = true; }
void ArrowButtonTapCallback() { arrow_button_tap_callback_called_ = true; }
void TestArrowButtonAndCheckCallbackCalled(SmartLockState state,
bool should_callback_be_called) {
arrow_button_tap_callback_called_ = false;
smart_lock_model_->SetSmartLockState(state);
smart_lock_model_->OnArrowButtonTapOrClickEvent();
EXPECT_EQ(arrow_button_tap_callback_called_, should_callback_be_called);
}
std::unique_ptr<SmartLockAuthFactorModel> smart_lock_model_;
AuthFactorModel* model_ = nullptr;
AuthIconView icon_;
bool on_state_changed_called_ = false;
bool arrow_button_tap_callback_called_ = false;
};
TEST_F(SmartLockAuthFactorModelUnittest, InitialState_ConnectingToPhone) {
auto initial_state = SmartLockState::kConnectingToPhone;
InitializeSmartLockAuthFactorModel(initial_state);
// Confirm that the desired initial state was set by verifying that no
// change occurs on a subsequent call to `SetSmartLockState()`.
smart_lock_model_->SetSmartLockState(initial_state);
EXPECT_FALSE(on_state_changed_called_);
}
TEST_F(SmartLockAuthFactorModelUnittest, InitialState_Inactive) {
auto initial_state = SmartLockState::kInactive;
InitializeSmartLockAuthFactorModel(initial_state);
// Confirm that the desired initial state was set by verifying that no
// change occurs on a subsequent call to `SetSmartLockState()`.
smart_lock_model_->SetSmartLockState(initial_state);
EXPECT_FALSE(on_state_changed_called_);
}
TEST_F(SmartLockAuthFactorModelUnittest, GetType) {
InitializeSmartLockAuthFactorModel();
EXPECT_EQ(AuthFactorType::kSmartLock, model_->GetType());
}
TEST_F(SmartLockAuthFactorModelUnittest, Disabled) {
InitializeSmartLockAuthFactorModel();
smart_lock_model_->SetSmartLockState(SmartLockState::kDisabled);
EXPECT_TRUE(on_state_changed_called_);
EXPECT_EQ(AuthFactorState::kUnavailable, model_->GetAuthFactorState());
}
TEST_F(SmartLockAuthFactorModelUnittest, ClickRequired) {
InitializeSmartLockAuthFactorModel();
smart_lock_model_->SetSmartLockState(SmartLockState::kPhoneAuthenticated);
EXPECT_TRUE(on_state_changed_called_);
EXPECT_EQ(AuthFactorState::kClickRequired, model_->GetAuthFactorState());
}
TEST_F(SmartLockAuthFactorModelUnittest, AvailableStates) {
InitializeSmartLockAuthFactorModel();
for (SmartLockState state :
{SmartLockState::kPhoneNotFound, SmartLockState::kConnectingToPhone,
SmartLockState::kPhoneFoundLockedAndDistant,
SmartLockState::kPhoneFoundUnlockedAndDistant}) {
smart_lock_model_->SetSmartLockState(state);
EXPECT_TRUE(on_state_changed_called_);
on_state_changed_called_ = false;
EXPECT_EQ(AuthFactorState::kAvailable, model_->GetAuthFactorState());
}
}
TEST_F(SmartLockAuthFactorModelUnittest, ErrorStates) {
InitializeSmartLockAuthFactorModel();
for (SmartLockState state : {SmartLockState::kPasswordReentryRequired,
SmartLockState::kPrimaryUserAbsent,
SmartLockState::kPhoneNotAuthenticated,
SmartLockState::kBluetoothDisabled,
SmartLockState::kPhoneNotLockable}) {
smart_lock_model_->SetSmartLockState(state);
EXPECT_TRUE(on_state_changed_called_);
EXPECT_EQ(AuthFactorState::kErrorPermanent, model_->GetAuthFactorState());
EXPECT_FALSE(model_->has_permanent_error_display_timed_out());
model_->HandleErrorTimeout();
EXPECT_TRUE(model_->has_permanent_error_display_timed_out());
on_state_changed_called_ = false;
}
}
TEST_F(SmartLockAuthFactorModelUnittest, ReadyStates) {
InitializeSmartLockAuthFactorModel();
smart_lock_model_->SetSmartLockState(
SmartLockState::kPhoneFoundLockedAndProximate);
EXPECT_TRUE(on_state_changed_called_);
on_state_changed_called_ = false;
EXPECT_EQ(AuthFactorState::kReady, model_->GetAuthFactorState());
}
TEST_F(SmartLockAuthFactorModelUnittest, OnStateChangedDebounced) {
InitializeSmartLockAuthFactorModel(SmartLockState::kConnectingToPhone);
EXPECT_FALSE(on_state_changed_called_);
smart_lock_model_->SetSmartLockState(
SmartLockState::kPhoneFoundLockedAndProximate);
EXPECT_TRUE(on_state_changed_called_);
on_state_changed_called_ = false;
smart_lock_model_->SetSmartLockState(
SmartLockState::kPhoneFoundLockedAndProximate);
EXPECT_FALSE(on_state_changed_called_);
}
TEST_F(SmartLockAuthFactorModelUnittest, ArrowButtonTapCallback) {
InitializeSmartLockAuthFactorModel();
// Callback should only be called when state is
// SmartLockState::kPhoneAuthenticated
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kDisabled, false);
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kInactive, false);
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kBluetoothDisabled,
false);
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kPhoneNotLockable,
false);
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kPhoneNotFound, false);
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kConnectingToPhone,
false);
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kPhoneNotAuthenticated,
false);
TestArrowButtonAndCheckCallbackCalled(
SmartLockState::kPhoneFoundLockedAndDistant, false);
TestArrowButtonAndCheckCallbackCalled(
SmartLockState::kPhoneFoundLockedAndProximate, false);
TestArrowButtonAndCheckCallbackCalled(
SmartLockState::kPhoneFoundUnlockedAndDistant, false);
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kPhoneAuthenticated,
true);
TestArrowButtonAndCheckCallbackCalled(
SmartLockState::kPasswordReentryRequired, false);
TestArrowButtonAndCheckCallbackCalled(SmartLockState::kPrimaryUserAbsent,
false);
}
TEST_F(SmartLockAuthFactorModelUnittest, NotifySmartLockAuthResult) {
InitializeSmartLockAuthFactorModel();
smart_lock_model_->NotifySmartLockAuthResult(/*result=*/true);
EXPECT_TRUE(on_state_changed_called_);
EXPECT_EQ(AuthFactorState::kAuthenticated, model_->GetAuthFactorState());
on_state_changed_called_ = false;
smart_lock_model_->NotifySmartLockAuthResult(/*result=*/false);
EXPECT_TRUE(on_state_changed_called_);
EXPECT_EQ(AuthFactorState::kErrorPermanent, model_->GetAuthFactorState());
}
TEST_F(SmartLockAuthFactorModelUnittest, GetLabelAndAccessibleName) {
InitializeSmartLockAuthFactorModel();
for (const LabelTestcase& testcase : kLabelTestcases) {
smart_lock_model_->SetSmartLockState(testcase.state);
if (testcase.can_use_pin.has_value()) {
model_->set_can_use_pin(testcase.can_use_pin.value());
}
EXPECT_EQ(testcase.label_id, model_->GetLabelId());
EXPECT_EQ(testcase.accessible_name_id, model_->GetAccessibleNameId());
EXPECT_TRUE(model_->ShouldAnnounceLabel());
}
}
TEST_F(SmartLockAuthFactorModelUnittest, GetLabelAfterPermanentErrorTimeout) {
InitializeSmartLockAuthFactorModel();
smart_lock_model_->NotifySmartLockAuthResult(/*result=*/false);
EXPECT_TRUE(on_state_changed_called_);
EXPECT_EQ(AuthFactorState::kErrorPermanent, model_->GetAuthFactorState());
EXPECT_EQ(IDS_AUTH_FACTOR_LABEL_CANNOT_UNLOCK, model_->GetLabelId());
model_->HandleErrorTimeout();
EXPECT_EQ(IDS_AUTH_FACTOR_LABEL_PASSWORD_REQUIRED, model_->GetLabelId());
}
} // namespace ash