chromium/chrome/browser/ash/camera_mic/vm_camera_mic_manager_unittest.cc

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

#include "chrome/browser/ash/camera_mic/vm_camera_mic_manager.h"

#include <memory>
#include <utility>

#include "ash/constants/ash_constants.h"
#include "ash/public/cpp/ash_prefs.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/system/notification_center/notification_center_tray.h"
#include "ash/system/privacy/privacy_indicators_tray_item_view.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/test/ash_test_helper.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/notifications/notification_display_service.h"
#include "chrome/browser/notifications/notification_display_service_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "components/account_id/account_id.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/message_center/message_center.h"

namespace ash {

namespace {

using VmType = VmCameraMicManager::VmType;
using DeviceType = VmCameraMicManager::DeviceType;
using NotificationType = VmCameraMicManager::NotificationType;

constexpr VmType kCrostiniVm = VmType::kCrostiniVm;
constexpr VmType kPluginVm = VmType::kPluginVm;
constexpr VmType kBorealis = VmType::kBorealis;

constexpr DeviceType kCamera = DeviceType::kCamera;
constexpr DeviceType kMic = DeviceType::kMic;

constexpr NotificationType kMicNotification =
    VmCameraMicManager::kMicNotification;
constexpr NotificationType kCameraNotification =
    VmCameraMicManager::kCameraNotification;
constexpr NotificationType kCameraAndMicNotification =
    VmCameraMicManager::kCameraAndMicNotification;

constexpr auto kDebounceTime = VmCameraMicManager::kDebounceTime;

class FakeNotificationDisplayService : public NotificationDisplayService {
 public:
  FakeNotificationDisplayService() = default;
  ~FakeNotificationDisplayService() override = default;

  void Display(
      NotificationHandler::Type notification_type,
      const message_center::Notification& notification,
      std::unique_ptr<NotificationCommon::Metadata> metadata) override {
    notification_ids_.insert(notification.id());
  }

  void Close(NotificationHandler::Type notification_type,
             const std::string& notification_id) override {
    size_t count = notification_ids_.erase(notification_id);
    CHECK(count == 1);
  }

  void GetDisplayed(DisplayedNotificationsCallback callback) override {}
  void GetDisplayedForOrigin(const GURL& origin,
                             DisplayedNotificationsCallback callback) override {
  }
  void AddObserver(Observer* observer) override {}
  void RemoveObserver(Observer* observer) override {}

  const std::set<std::string>& notification_ids() { return notification_ids_; }

 private:
  std::set<std::string> notification_ids_;
};

using DeviceActiveMap = base::flat_map<DeviceType, bool>;
using ActiveMap = base::flat_map<VmType, DeviceActiveMap>;

struct IsActiveTestParam {
  ActiveMap active_map;
  // Device types that are expected to be active.
  std::vector<DeviceType> device_expectations;
  // Notification types that are expected to be active.
  std::vector<NotificationType> notification_expectations;
};

// Check the visibility of privacy indicators in all displays.
void ExpectPrivacyIndicatorsVisible(bool visible) {
  for (RootWindowController* root_window_controller :
       Shell::Get()->GetAllRootWindowControllers()) {
    auto* view = root_window_controller->GetStatusAreaWidget()
                     ->notification_center_tray()
                     ->privacy_indicators_view();

    EXPECT_EQ(view->GetVisible(), visible);
  }
}

}  // namespace

class VmCameraMicManagerTest : public testing::Test {
 public:
  // Define here to access `VmCameraMicManager` private members.
  struct NotificationTestParam {
    ActiveMap active_map;
    std::vector<std::string> expected_notifications;

    NotificationTestParam(
        const ActiveMap& active_map,
        const std::vector<std::pair<VmType, NotificationType>>&
            expected_notifications) {
      this->active_map = active_map;
      for (const auto& vm_notification : expected_notifications) {
        this->expected_notifications.push_back(
            VmCameraMicManager::GetNotificationId(vm_notification.first,
                                                  vm_notification.second));
      }
    }
  };

  std::string GetNotificationId(VmType vm, NotificationType type) {
    return ash::kPrivacyIndicatorsNotificationIdPrefix +
           VmCameraMicManager::GetNotificationId(vm, type);
  }

  VmCameraMicManagerTest() {
    fake_user_manager_.Reset(std::make_unique<ash::FakeChromeUserManager>());

    // Make the profile the primary one.
    fake_user_manager_->AddUser(AccountId::FromUserEmailGaiaId(
        testing_profile_.GetProfileUserName(), "id"));

    // Inject a fake notification display service.
    fake_display_service_ = static_cast<FakeNotificationDisplayService*>(
        NotificationDisplayServiceFactory::GetInstance()
            ->SetTestingFactoryAndUse(
                &testing_profile_,
                base::BindRepeating([](content::BrowserContext* context)
                                        -> std::unique_ptr<KeyedService> {
                  return std::make_unique<FakeNotificationDisplayService>();
                })));

    vm_camera_mic_manager_ = std::make_unique<VmCameraMicManager>();
    vm_camera_mic_manager_->OnPrimaryUserSessionStarted(&testing_profile_);
  }

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

  ~VmCameraMicManagerTest() override { fake_user_manager_.Reset(); }

  // testing::Test:
  void SetUp() override {
    // Setting ash prefs for testing multi-display.
    ash::RegisterLocalStatePrefs(local_state_.registry(), /*for_test=*/true);

    ash::AshTestHelper::InitParams params;
    params.local_state = &local_state_;
    ash_test_helper_.SetUp(std::move(params));
  }

  void TearDown() override { ash_test_helper_.TearDown(); }

  void SetCameraAccessing(VmType vm, bool value) {
    vm_camera_mic_manager_->SetCameraAccessing(vm, value);
  }

  void SetCameraPrivacyIsOn(bool value) {
    vm_camera_mic_manager_->SetCameraPrivacyIsOn(value);
  }

  void SetMicActive(VmType vm, bool value) {
    vm_camera_mic_manager_->SetMicActive(vm, value);
  }

  // Note that camera privacy is always set to off by this function.
  void SetActiveAndForwardToStable(const ActiveMap& active_map) {
    SetCameraPrivacyIsOn(false);
    for (const auto& vm_and_device_active_map : active_map) {
      VmType vm = vm_and_device_active_map.first;
      for (const auto& device_active : vm_and_device_active_map.second) {
        switch (device_active.first) {
          case kCamera:
            SetCameraAccessing(vm, device_active.second);
            break;
          case kMic:
            SetMicActive(vm, device_active.second);
            break;
        }
      }
    }

    ForwardToStable();
  }

  void ForwardToStable() {
    // The manager at most does the debounce twice (once for "stage" and once
    // for "target"), so waiting 2 times here is enough.
    for (size_t i = 0; i < 2; ++i) {
      task_environment_.FastForwardBy(kDebounceTime);
    }
  }

  // Checks that all the notifications from `notification_ids` exist as privacy
  // indicators notifications.
  void ExpectNotificationsExist(std::set<std::string> notification_ids) {
    std::set<std::string> all_privacy_indicators_notifications;
    for (message_center::Notification* notification :
         message_center::MessageCenter::Get()->GetVisibleNotifications()) {
      // Leaves out any parent container.
      if (!notification || notification->group_parent()) {
        continue;
      }

      auto& notification_id = notification->id();
      if (base::StartsWith(notification_id,
                           ash::kPrivacyIndicatorsNotificationIdPrefix)) {
        all_privacy_indicators_notifications.insert(notification_id);
      }
    }

    EXPECT_EQ(all_privacy_indicators_notifications, notification_ids);
  }

 protected:
  content::BrowserTaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
      fake_user_manager_;
  TestingProfile testing_profile_;

  raw_ptr<FakeNotificationDisplayService> fake_display_service_;
  std::unique_ptr<VmCameraMicManager> vm_camera_mic_manager_;

  base::test::ScopedFeatureList scoped_feature_list_;

  // Use this for testing multi-display.
  TestingPrefServiceSimple local_state_;

  ash::AshTestHelper ash_test_helper_;
};

TEST_F(VmCameraMicManagerTest, CameraPrivacy) {
  SetCameraAccessing(kPluginVm, false);
  SetCameraPrivacyIsOn(false);
  ForwardToStable();
  EXPECT_FALSE(vm_camera_mic_manager_->IsDeviceActive(kCamera));
  EXPECT_FALSE(
      vm_camera_mic_manager_->IsNotificationActive(kCameraNotification));

  SetCameraAccessing(kPluginVm, true);
  SetCameraPrivacyIsOn(false);
  ForwardToStable();
  EXPECT_TRUE(vm_camera_mic_manager_->IsDeviceActive(kCamera));
  EXPECT_TRUE(
      vm_camera_mic_manager_->IsNotificationActive(kCameraNotification));

  SetCameraAccessing(kPluginVm, false);
  SetCameraPrivacyIsOn(true);
  ForwardToStable();
  EXPECT_FALSE(vm_camera_mic_manager_->IsDeviceActive(kCamera));
  EXPECT_FALSE(
      vm_camera_mic_manager_->IsNotificationActive(kCameraNotification));

  SetCameraAccessing(kPluginVm, true);
  SetCameraPrivacyIsOn(true);
  ForwardToStable();
  EXPECT_FALSE(vm_camera_mic_manager_->IsDeviceActive(kCamera));
  EXPECT_FALSE(
      vm_camera_mic_manager_->IsNotificationActive(kCameraNotification));
}

TEST_F(VmCameraMicManagerTest, PrivacyIndicatorsNotification) {
  SetCameraAccessing(kPluginVm, false);
  SetCameraPrivacyIsOn(false);
  ForwardToStable();
  EXPECT_FALSE(vm_camera_mic_manager_->IsDeviceActive(kCamera));
  EXPECT_FALSE(
      vm_camera_mic_manager_->IsNotificationActive(kCameraNotification));
  ExpectNotificationsExist(std::set<std::string>{});

  SetCameraAccessing(kPluginVm, true);
  SetCameraPrivacyIsOn(false);
  ForwardToStable();
  EXPECT_TRUE(vm_camera_mic_manager_->IsDeviceActive(kCamera));
  EXPECT_TRUE(
      vm_camera_mic_manager_->IsNotificationActive(kCameraNotification));
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(VmType::kPluginVm, kCameraNotification)});
}

TEST_F(VmCameraMicManagerTest, PrivacyIndicatorsView) {
  // Make sure privacy indicators work on multiple displays.
  display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
      .UpdateDisplay("800x700,801+0-800x700");

  SetCameraAccessing(kPluginVm, false);
  SetCameraPrivacyIsOn(false);
  ForwardToStable();
  ExpectPrivacyIndicatorsVisible(/*visible=*/false);

  SetCameraAccessing(kPluginVm, true);
  SetCameraPrivacyIsOn(false);
  ForwardToStable();
  ExpectPrivacyIndicatorsVisible(/*visible=*/true);

  // Switch back to not accessing, the indicator should not be visible.
  SetCameraAccessing(kPluginVm, false);
  SetCameraPrivacyIsOn(false);
  ForwardToStable();
  ExpectPrivacyIndicatorsVisible(/*visible=*/false);
}

// Test `IsDeviceActive()` and `IsNotificationActive()`.
class VmCameraMicManagerIsActiveTest
    : public VmCameraMicManagerTest,
      public testing::WithParamInterface<IsActiveTestParam> {};

TEST_P(VmCameraMicManagerIsActiveTest, IsNotificationActive) {
  SetActiveAndForwardToStable(GetParam().active_map);

  for (auto device : {kCamera, kMic}) {
    EXPECT_EQ(vm_camera_mic_manager_->IsDeviceActive(device),
              base::Contains(GetParam().device_expectations, device));
  }

  for (auto notification :
       {kCameraNotification, kMicNotification, kCameraAndMicNotification}) {
    EXPECT_EQ(
        vm_camera_mic_manager_->IsNotificationActive(notification),
        base::Contains(GetParam().notification_expectations, notification));
  }
}

INSTANTIATE_TEST_SUITE_P(
    ,
    VmCameraMicManagerIsActiveTest,
    testing::Values(
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 0}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*device_expectations=*/{},
            /*notification_expectations=*/{},
        },
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 0}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 1}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*device_expectations=*/{kCamera},
            /*notification_expectations=*/{kCameraNotification},
        },
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*device_expectations=*/{kCamera},
            /*notification_expectations=*/{kCameraNotification},
        },
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 0}, {kMic, 1}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*device_expectations=*/{kMic},
            /*notification_expectations=*/{kMicNotification},
        },
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 0}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 1}}},
            },
            /*device_expectations=*/{kMic},
            /*notification_expectations=*/{kMicNotification},
        },
        // Only a crostini "camera icon" notification is displayed.
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 1}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*device_expectations=*/{kCamera, kMic},
            /*notification_expectations=*/{kCameraAndMicNotification},
        },
        // Crostini "camera icon" notification and pluginvm mic notification are
        // displayed.
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 1}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 1}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*device_expectations=*/{kCamera, kMic},
            /*notification_expectations=*/
            {kCameraAndMicNotification, kMicNotification},
        },
        // Crostini "camera icon" notification and pluginvm camera notification
        // are displayed.
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 1}}},
                {kPluginVm, {{kCamera, 1}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*device_expectations=*/{kCamera, kMic},
            /*notification_expectations=*/
            {kCameraAndMicNotification, kCameraNotification},
        },
        // Crostini camera notification and pluginvm mic notification are
        // displayed.
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 1}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*device_expectations=*/{kCamera, kMic},
            /*notification_expectations=*/
            {kCameraNotification, kMicNotification},
        },
        // Crostini camera notification and borealis mic notification are
        // displayed.
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 1}}},
            },
            /*device_expectations=*/{kCamera, kMic},
            /*notification_expectations=*/
            {kCameraNotification, kMicNotification},
        },
        // Crostini and pluginvm "camera icon" notifications are displayed.
        IsActiveTestParam{
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 1}}},
                {kPluginVm, {{kCamera, 1}, {kMic, 1}}},
                {kBorealis, {{kCamera, 0}, {kMic, 1}}},
            },
            /*device_expectations=*/{kCamera, kMic},
            /*notification_expectations=*/
            {kCameraAndMicNotification, kMicNotification},
        }));

class VmCameraMicManagerNotificationTest
    : public VmCameraMicManagerTest,
      public testing::WithParamInterface<
          VmCameraMicManagerTest::NotificationTestParam> {
 public:
  static std::vector<NotificationTestParam> GetTestValues() {
    return {
        {
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 0}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*expected_notifications=*/{},
        },
        {
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*expected_notifications=*/{{kCrostiniVm, kCameraNotification}},
        },
        {
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 0}, {kMic, 1}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*expected_notifications=*/
            {
                {kCrostiniVm, kCameraNotification},
                {kPluginVm, kMicNotification},
            },
        },
        {
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 0}}},
                {kPluginVm, {{kCamera, 1}, {kMic, 1}}},
                {kBorealis, {{kCamera, 0}, {kMic, 0}}},
            },
            /*expected_notifications=*/
            {
                {kCrostiniVm, kCameraNotification},
                {kPluginVm, kCameraAndMicNotification},
            },
        },
        {
            /*active_map=*/{
                {kCrostiniVm, {{kCamera, 1}, {kMic, 1}}},
                {kPluginVm, {{kCamera, 1}, {kMic, 0}}},
                {kBorealis, {{kCamera, 0}, {kMic, 1}}},
            },
            /*expected_notifications=*/
            {
                {kCrostiniVm, kCameraAndMicNotification},
                {kPluginVm, kCameraNotification},
                {kBorealis, kMicNotification},
            },
        },
    };
  }
};

TEST_P(VmCameraMicManagerNotificationTest, SetActiveAndForwardToStable) {
  const NotificationTestParam& param = GetParam();
  SetActiveAndForwardToStable(param.active_map);

  auto expected_notifications = param.expected_notifications;

  for (auto& id : expected_notifications) {
    id = ash::kPrivacyIndicatorsNotificationIdPrefix + id;
  }

  ExpectNotificationsExist(std::set<std::string>(expected_notifications.begin(),
                                                 expected_notifications.end()));
}

INSTANTIATE_TEST_SUITE_P(
    ,
    VmCameraMicManagerNotificationTest,
    testing::ValuesIn(VmCameraMicManagerNotificationTest::GetTestValues()));

// For testing the debounce behavior.
class VmCameraMicManagerDebounceTest : public VmCameraMicManagerTest {
 public:
  void ForwardDebounceTime(double factor = 1) {
    task_environment_.FastForwardBy(factor * kDebounceTime);
  }
};

TEST_F(VmCameraMicManagerDebounceTest, Simple) {
  SetMicActive(kPluginVm, true);
  ForwardDebounceTime();
  ExpectNotificationsExist(
      std::set<std::string>{GetNotificationId(kPluginVm, kMicNotification)});

  ForwardToStable();
  ExpectNotificationsExist(
      std::set<std::string>{GetNotificationId(kPluginVm, kMicNotification)});
}

TEST_F(VmCameraMicManagerDebounceTest, CombineCameraAndMic) {
  SetMicActive(kPluginVm, true);
  ForwardDebounceTime(/*factor=*/0.51);
  // Within debounce time, so no notification.
  ExpectNotificationsExist(std::set<std::string>{});

  SetCameraAccessing(kPluginVm, true);
  ForwardDebounceTime(/*factor=*/0.51);
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});

  ForwardToStable();
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});
}

// This test that if the devices are turned on and then immediately turned off,
// we will still show notifications for at least kDebounceTime.
TEST_F(VmCameraMicManagerDebounceTest, DisplayStageBeforeTarget) {
  SetMicActive(kPluginVm, true);
  SetMicActive(kPluginVm, false);
  SetCameraAccessing(kPluginVm, true);
  SetCameraAccessing(kPluginVm, false);

  ForwardDebounceTime();
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});

  // Will continue displaying for roughly kDebounceTime.
  ForwardDebounceTime(/*factor=*/0.9);
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});

  // Eventually display the "target" notification, which is no notification at
  // all.
  ForwardDebounceTime(/*factor=*/0.11);
  ExpectNotificationsExist(std::set<std::string>{});

  ForwardToStable();
  ExpectNotificationsExist(std::set<std::string>{});
}

// This test that within debounce time, if the state is reverted back to the
// stable status, then nothing will change.
TEST_F(VmCameraMicManagerDebounceTest, RevertBackToStable) {
  SetMicActive(kPluginVm, true);
  SetCameraAccessing(kPluginVm, true);
  ForwardToStable();
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});

  // First turn both devices off, and then turn them back up. The state should
  // become stable again so nothing should changed.
  SetMicActive(kPluginVm, false);
  SetCameraAccessing(kPluginVm, false);
  ForwardDebounceTime(/*factor=*/0.5);
  SetMicActive(kPluginVm, true);
  SetCameraAccessing(kPluginVm, true);

  // Nothing changed.
  ForwardDebounceTime();
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});

  ForwardToStable();
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});
}

// Simulate one of the more complicated case.
TEST_F(VmCameraMicManagerDebounceTest, SimulateSkypeStartingMeeting) {
  // Simulate the waiting to start screen, in which only the camera is active.
  SetCameraAccessing(kPluginVm, true);
  ForwardToStable();
  ExpectNotificationsExist(
      std::set<std::string>{GetNotificationId(kPluginVm, kCameraNotification)});

  // Simulate what happens after clicking the starting button. Skype will turn
  // on and off the devices multiple time. We should expect the notification
  // only changes to "camera and mic" once.
  SetCameraAccessing(kPluginVm, false);
  ForwardDebounceTime(0.2);
  SetMicActive(kPluginVm, true);
  ForwardDebounceTime(0.2);
  SetCameraAccessing(kPluginVm, true);
  ForwardDebounceTime(0.7);
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});
  ForwardToStable();
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});

  SetCameraAccessing(kPluginVm, false);
  ForwardDebounceTime(0.2);
  SetCameraAccessing(kPluginVm, true);
  ForwardDebounceTime(0.9);
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});
  ForwardToStable();
  ExpectNotificationsExist(std::set<std::string>{
      GetNotificationId(kPluginVm, kCameraAndMicNotification)});
}

}  // namespace ash