blob: 5e5e4e72a5a46cae156c1d113179e7826aad02f8 [file]
// Copyright 2020 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 "chromeos/components/phonehub/notification_access_manager_impl.h"
#include <memory>
#include "chromeos/components/phonehub/fake_connection_scheduler.h"
#include "chromeos/components/phonehub/fake_feature_status_provider.h"
#include "chromeos/components/phonehub/fake_message_sender.h"
#include "chromeos/components/phonehub/notification_access_setup_operation.h"
#include "chromeos/components/phonehub/pref_names.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace phonehub {
namespace {
class FakeObserver : public NotificationAccessManager::Observer {
public:
FakeObserver() = default;
~FakeObserver() override = default;
size_t num_calls() const { return num_calls_; }
// NotificationAccessManager::Observer:
void OnNotificationAccessChanged() override { ++num_calls_; }
private:
size_t num_calls_ = 0;
};
class FakeOperationDelegate
: public NotificationAccessSetupOperation::Delegate {
public:
FakeOperationDelegate() = default;
~FakeOperationDelegate() override = default;
NotificationAccessSetupOperation::Status status() const { return status_; }
// NotificationAccessSetupOperation::Delegate:
void OnStatusChange(
NotificationAccessSetupOperation::Status new_status) override {
status_ = new_status;
}
private:
NotificationAccessSetupOperation::Status status_ =
NotificationAccessSetupOperation::Status::kConnecting;
};
} // namespace
class NotificationAccessManagerImplTest : public testing::Test {
protected:
NotificationAccessManagerImplTest() = default;
NotificationAccessManagerImplTest(const NotificationAccessManagerImplTest&) =
delete;
NotificationAccessManagerImplTest& operator=(
const NotificationAccessManagerImplTest&) = delete;
~NotificationAccessManagerImplTest() override = default;
// testing::Test:
void SetUp() override {
NotificationAccessManagerImpl::RegisterPrefs(pref_service_.registry());
fake_feature_status_provider_ =
std::make_unique<FakeFeatureStatusProvider>();
fake_message_sender_ = std::make_unique<FakeMessageSender>();
fake_connection_scheduler_ = std::make_unique<FakeConnectionScheduler>();
}
void TearDown() override { manager_->RemoveObserver(&fake_observer_); }
void Initialize(NotificationAccessManager::AccessStatus expected_status) {
pref_service_.SetInteger(prefs::kNotificationAccessStatus,
static_cast<int>(expected_status));
manager_ = std::make_unique<NotificationAccessManagerImpl>(
&pref_service_, fake_feature_status_provider_.get(),
fake_message_sender_.get(), fake_connection_scheduler_.get());
manager_->AddObserver(&fake_observer_);
}
NotificationAccessSetupOperation::Status
GetNotifcationAccessSetupOperationStatus() {
return fake_delegate_.status();
}
void VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus expected_status) {
EXPECT_EQ(static_cast<int>(expected_status),
pref_service_.GetInteger(prefs::kNotificationAccessStatus));
EXPECT_EQ(expected_status, manager_->GetAccessStatus());
}
bool HasNotificationSetupUiBeenDismissed() {
return manager_->HasNotificationSetupUiBeenDismissed();
}
void DismissSetupRequiredUi() { manager_->DismissSetupRequiredUi(); }
std::unique_ptr<NotificationAccessSetupOperation> StartSetupOperation() {
return manager_->AttemptNotificationSetup(&fake_delegate_);
}
bool IsSetupOperationInProgress() {
return manager_->IsSetupOperationInProgress();
}
void SetAccessStatusInternal(NotificationAccessManager::AccessStatus status) {
manager_->SetAccessStatusInternal(status);
}
void SetFeatureStatus(FeatureStatus status) {
fake_feature_status_provider_->SetStatus(status);
}
FeatureStatus GetFeatureStatus() {
return fake_feature_status_provider_->GetStatus();
}
size_t GetNumScheduleConnectionNowCalls() const {
return fake_connection_scheduler_->num_schedule_connection_now_calls();
}
size_t GetNumShowNotificationAccessSetupRequestCount() const {
return fake_message_sender_->show_notification_access_setup_request_count();
}
size_t GetNumObserverCalls() const { return fake_observer_.num_calls(); }
private:
TestingPrefServiceSimple pref_service_;
FakeObserver fake_observer_;
FakeOperationDelegate fake_delegate_;
std::unique_ptr<FakeFeatureStatusProvider> fake_feature_status_provider_;
std::unique_ptr<FakeMessageSender> fake_message_sender_;
std::unique_ptr<FakeConnectionScheduler> fake_connection_scheduler_;
std::unique_ptr<NotificationAccessManager> manager_;
};
TEST_F(NotificationAccessManagerImplTest, ShouldShowSetupRequiredUi) {
// Notification setup is not dismissed initially even when access has been
// granted.
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_FALSE(HasNotificationSetupUiBeenDismissed());
// Notification setup is not dismissed initially when access has not been
// granted.
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
EXPECT_FALSE(HasNotificationSetupUiBeenDismissed());
// Simlulate dismissal of UI.
DismissSetupRequiredUi();
EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
// Dismissal value is persisted on initialization with access not granted.
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
// Dismissal value is persisted on initialization with access granted.
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_TRUE(HasNotificationSetupUiBeenDismissed());
}
TEST_F(NotificationAccessManagerImplTest, InitiallyGranted) {
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
// Cannot start the notification access setup flow if access has already been
// granted.
auto operation = StartSetupOperation();
EXPECT_FALSE(operation);
}
TEST_F(NotificationAccessManagerImplTest, OnFeatureStatusChanged) {
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Set initial state to disconnected.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
EXPECT_EQ(0u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnecting,
GetNotifcationAccessSetupOperationStatus());
// Simulate feature status to be enabled and connected. SetupOperation is also
// not in progress, so expect no new requests to be sent.
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
EXPECT_EQ(0u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnecting,
GetNotifcationAccessSetupOperationStatus());
// Simulate setup operation is in progress. This will trigger a sent request.
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::
kSentMessageToPhoneAndWaitingForResponse,
GetNotifcationAccessSetupOperationStatus());
// Set another feature status, expect status to be updated.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest, StartDisconnectedAndNoAccess) {
// Set initial state to disconnected.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Start a setup operation with enabled but disconnected status and access
// not granted.
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
EXPECT_EQ(1u, GetNumScheduleConnectionNowCalls());
// Simulate changing states from connecting to connected.
SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
// Verify that the request message has been sent and our operation status
// is updated.
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::
kSentMessageToPhoneAndWaitingForResponse,
GetNotifcationAccessSetupOperationStatus());
// Simulate getting a response back from the phone.
SetAccessStatusInternal(
NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest,
StartDisconnectedAndNoAccess_Prohibited) {
// Set initial state to disconnected.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Start a setup operation with enabled but disconnected status and access
// not granted.
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
EXPECT_EQ(1u, GetNumScheduleConnectionNowCalls());
// Simulate changing states from connecting to connected.
SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
// Verify that the request message has been sent and our operation status
// is updated.
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::
kSentMessageToPhoneAndWaitingForResponse,
GetNotifcationAccessSetupOperationStatus());
// Simulate getting a response back from the phone.
SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kProhibited);
EXPECT_EQ(
NotificationAccessSetupOperation::Status::kProhibitedFromProvidingAccess,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest, StartConnectingAndNoAccess) {
// Set initial state to connecting.
SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Start a setup operation with enabled and connecting status and access
// not granted.
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
// Simulate changing states from connecting to connected.
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
// Verify that the request message has been sent and our operation status
// is updated.
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::
kSentMessageToPhoneAndWaitingForResponse,
GetNotifcationAccessSetupOperationStatus());
// Simulate getting a response back from the phone.
SetAccessStatusInternal(
NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest, StartConnectedAndNoAccess) {
// Set initial state to connected.
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
// Start a setup operation with enabled and connected status and access
// not granted.
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
// Verify that the request message has been sent and our operation status
// is updated.
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
EXPECT_EQ(NotificationAccessSetupOperation::Status::
kSentMessageToPhoneAndWaitingForResponse,
GetNotifcationAccessSetupOperationStatus());
// Simulate getting a response back from the phone.
SetAccessStatusInternal(
NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kCompletedSuccessfully,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest, SimulateConnectingToDisconnected) {
// Set initial state to connecting.
SetFeatureStatus(FeatureStatus::kEnabledAndConnecting);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
// Simulate a disconnection and expect that status has been updated.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kTimedOutConnecting,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest, SimulateConnectedToDisconnected) {
// Simulate connected state.
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
// Simulate a disconnection, expect status update.
SetFeatureStatus(FeatureStatus::kEnabledButDisconnected);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest, SimulateConnectedToDisabled) {
// Simulate connected state.
SetFeatureStatus(FeatureStatus::kEnabledAndConnected);
Initialize(NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
auto operation = StartSetupOperation();
EXPECT_TRUE(operation);
EXPECT_EQ(1u, GetNumShowNotificationAccessSetupRequestCount());
// Simulate disabling the feature, expect status update.
SetFeatureStatus(FeatureStatus::kDisabled);
EXPECT_EQ(NotificationAccessSetupOperation::Status::kConnectionDisconnected,
GetNotifcationAccessSetupOperationStatus());
}
TEST_F(NotificationAccessManagerImplTest, FlipAccessGrantedToNotGranted) {
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
// Simulate flipping the access state to no granted.
SetAccessStatusInternal(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAvailableButNotGranted);
}
TEST_F(NotificationAccessManagerImplTest, FlipAccessGrantedToProhibited) {
Initialize(NotificationAccessManager::AccessStatus::kAccessGranted);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kAccessGranted);
// Simulate flipping the access state to prohibited.
SetAccessStatusInternal(NotificationAccessManager::AccessStatus::kProhibited);
VerifyNotificationAccessGrantedState(
NotificationAccessManager::AccessStatus::kProhibited);
}
} // namespace phonehub
} // namespace chromeos