chromium/ash/detachable_base/detachable_base_notification_controller_unittest.cc

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

#include "ash/detachable_base/detachable_base_notification_controller.h"

#include <string>

#include "ash/detachable_base/detachable_base_handler.h"
#include "ash/public/cpp/session/user_info.h"
#include "ash/session/session_controller_impl.h"
#include "ash/session/test_session_controller_client.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/time/time.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/user_type.h"
#include "ui/message_center/message_center.h"

namespace ash {

UserInfo CreateTestUserInfo(const std::string& user_email) {
  UserInfo user_info;
  user_info.type = user_manager::UserType::kRegular;
  user_info.account_id = AccountId::FromUserEmail(user_email);
  user_info.display_name = "Test user";
  user_info.display_email = user_email;
  user_info.is_ephemeral = false;
  user_info.is_new_profile = false;
  return user_info;
}

class DetachableBaseNotificationControllerTest : public NoSessionAshTestBase {
 public:
  DetachableBaseNotificationControllerTest() = default;

  DetachableBaseNotificationControllerTest(
      const DetachableBaseNotificationControllerTest&) = delete;
  DetachableBaseNotificationControllerTest& operator=(
      const DetachableBaseNotificationControllerTest&) = delete;

  ~DetachableBaseNotificationControllerTest() override = default;

  void SetUp() override {
    AshTestBase::SetUp();

    chromeos::FakePowerManagerClient::Get()->SetTabletMode(
        chromeos::PowerManagerClient::TabletMode::OFF, base::TimeTicks());
  }

  bool IsBaseChangedNotificationVisible() {
    return message_center::MessageCenter::Get()->FindVisibleNotificationById(
        DetachableBaseNotificationController::kBaseChangedNotificationId);
  }

  bool IsBaseRequiresUpdateNotificationVisible() {
    return message_center::MessageCenter::Get()->FindVisibleNotificationById(
        DetachableBaseNotificationController::
            kBaseRequiresUpdateNotificationId);
  }

  void CloseBaseChangedNotification() {
    message_center::MessageCenter::Get()->RemoveNotification(
        DetachableBaseNotificationController::kBaseChangedNotificationId,
        true /*by_user*/);
  }

  DetachableBaseHandler* detachable_base_handler() {
    return Shell::Get()->detachable_base_handler();
  }

  SessionControllerImpl* session_controller() {
    return Shell::Get()->session_controller();
  }
};

TEST_F(DetachableBaseNotificationControllerTest,
       ShowPairingNotificationIfSessionNotBlocked) {
  CreateUserSessions(1);

  // The first detachable base used by the user - no notification expected.
  detachable_base_handler()->PairChallengeSucceeded({0x01, 0x01});
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  // If the user changes the paired base in session, the detachable base change
  // notification should be shown.
  detachable_base_handler()->PairChallengeSucceeded({0x02, 0x02});
  EXPECT_TRUE(IsBaseChangedNotificationVisible());

  CloseBaseChangedNotification();
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  // Verify that the notification is reshown if the base changes again.
  detachable_base_handler()->PairChallengeSucceeded({0x03, 0x03});
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest,
       ShowNotificationOnNonAuthenticatedBases) {
  CreateUserSessions(1);

  detachable_base_handler()->PairChallengeFailed();
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest,
       UpdateNotificationOnUserSwitch) {
  CreateUserSessions(1);

  // The first detachable base used by the user - no notification expected.
  detachable_base_handler()->PairChallengeSucceeded({0x01, 0x01});
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  SimulateUserLogin("[email protected]");
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  detachable_base_handler()->PairChallengeSucceeded({0x02, 0x02});
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
  CloseBaseChangedNotification();

  GetSessionControllerClient()->SwitchActiveUser(
      session_controller()->GetUserSession(1)->user_info.account_id);

  EXPECT_TRUE(IsBaseChangedNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest,
       NonAuthenticatedBaseNotificationOnUserSwitch) {
  CreateUserSessions(1);

  detachable_base_handler()->PairChallengeFailed();
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
  CloseBaseChangedNotification();

  SimulateUserLogin("[email protected]");
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest,
       NoNotificationIfSessionNotStarted) {
  const char kTestUser[] = "[email protected]";
  UserInfo test_user_info = CreateTestUserInfo(kTestUser);
  // Set a detachable base as previously used by the user before log in.
  detachable_base_handler()->PairChallengeSucceeded({0x01, 0x01});
  EXPECT_TRUE(
      detachable_base_handler()->SetPairedBaseAsLastUsedByUser(test_user_info));

  // Set up another detachable base as attached when the user logs in.
  detachable_base_handler()->PairChallengeSucceeded({0x02, 0x02});
  // No active user, so the notification should not be shown, yet.
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  SimulateUserLogin(kTestUser);
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest,
       NoNotificationOnSessionStartIfBaseMarkedAsLastUsed) {
  const char kTestUser[] = "[email protected]";
  UserInfo test_user_info = CreateTestUserInfo(kTestUser);
  // Set a detachable base as previously used by the user before log in.
  detachable_base_handler()->PairChallengeSucceeded({0x01, 0x01});
  EXPECT_TRUE(
      detachable_base_handler()->SetPairedBaseAsLastUsedByUser(test_user_info));

  // Set up another detachable base as attached when the user logs in.
  detachable_base_handler()->PairChallengeSucceeded({0x02, 0x02});
  // No active user, so the notification should not be shown, yet.
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  // Mark the current device as last used by the user, and verify there is no
  // notification when the user logs in.
  EXPECT_TRUE(
      detachable_base_handler()->SetPairedBaseAsLastUsedByUser(test_user_info));
  SimulateUserLogin(kTestUser);
  EXPECT_FALSE(IsBaseChangedNotificationVisible());
}

// Tests that a notification for non authenticated base is not shown before the
// session is started - the login UI will show a custom UI to inform the user
// about the base.
TEST_F(DetachableBaseNotificationControllerTest,
       NonAuthenticatedBaseNotificationNotShownBeforeLogin) {
  detachable_base_handler()->PairChallengeFailed();
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  CreateUserSessions(1);
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest, NoNotificationOnLockScreen) {
  CreateUserSessions(1);
  // The first detachable base used by the user - no notification expected.
  detachable_base_handler()->PairChallengeSucceeded({0x01, 0x01});

  BlockUserSession(UserSessionBlockReason::BLOCKED_BY_LOCK_SCREEN);

  detachable_base_handler()->PairChallengeSucceeded({0x02, 0x02});
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  UnblockUserSession();
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest,
       NoNotificationAfterLockScreenIfSetAsUsed) {
  CreateUserSessions(1);
  // The first detachable base used by the user - no notification expected.
  detachable_base_handler()->PairChallengeSucceeded({0x01, 0x01});
  BlockUserSession(UserSessionBlockReason::BLOCKED_BY_LOCK_SCREEN);

  detachable_base_handler()->PairChallengeSucceeded({0x02, 0x02});
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  EXPECT_TRUE(detachable_base_handler()->SetPairedBaseAsLastUsedByUser(
      session_controller()->GetUserSession(0)->user_info));

  UnblockUserSession();
  EXPECT_FALSE(IsBaseChangedNotificationVisible());
}

// Tests that a notification for non authenticated base is not shown before the
// session is started - the lock UI will show a custom UI to inform the user
// about the base.
TEST_F(DetachableBaseNotificationControllerTest,
       NonAuthenticatedBaseNotificationNotShownOnLock) {
  BlockUserSession(UserSessionBlockReason::BLOCKED_BY_LOCK_SCREEN);
  detachable_base_handler()->PairChallengeFailed();
  EXPECT_FALSE(IsBaseChangedNotificationVisible());

  UnblockUserSession();
  EXPECT_TRUE(IsBaseChangedNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest, NotificationOnUpdateRequired) {
  CreateUserSessions(1);

  detachable_base_handler()->BaseFirmwareUpdateNeeded();
  EXPECT_TRUE(IsBaseRequiresUpdateNotificationVisible());

  // The notification should be removed when the base gets detached.
  chromeos::FakePowerManagerClient::Get()->SetTabletMode(
      chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks());
  EXPECT_FALSE(IsBaseRequiresUpdateNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest,
       NotificationOnUpdateRequiredBeforeLogin) {
  // Update requirement detected before login - expect the update required
  // notification to be hidden.
  detachable_base_handler()->BaseFirmwareUpdateNeeded();
  EXPECT_FALSE(IsBaseRequiresUpdateNotificationVisible());

  // Login, expect the notification to be shown.
  CreateUserSessions(1);
  EXPECT_TRUE(IsBaseRequiresUpdateNotificationVisible());

  // The notification should be removed when the base gets detached.
  chromeos::FakePowerManagerClient::Get()->SetTabletMode(
      chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks());
  EXPECT_FALSE(IsBaseRequiresUpdateNotificationVisible());
}

TEST_F(DetachableBaseNotificationControllerTest,
       NotificationOnUpdateRequiredOnLockScreen) {
  // Update requirement detected while the session is blocked by the lock
  // screen - expect the update required notification to be shown.
  BlockUserSession(UserSessionBlockReason::BLOCKED_BY_LOCK_SCREEN);

  detachable_base_handler()->BaseFirmwareUpdateNeeded();
  EXPECT_TRUE(IsBaseRequiresUpdateNotificationVisible());

  // The notification should be removed when the base gets detached.
  chromeos::FakePowerManagerClient::Get()->SetTabletMode(
      chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks());
  EXPECT_FALSE(IsBaseRequiresUpdateNotificationVisible());
}

}  // namespace ash