chromium/chrome/browser/nearby_sharing/nearby_share_settings_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/nearby_sharing/nearby_share_settings.h"

#include <memory>

#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
#include "chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-test-utils.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

const char kDefaultDeviceName[] = "Josh's Chromebook";

}  // namespace

using NearbyShareSettingsAsyncWaiter =
    nearby_share::mojom::NearbyShareSettingsAsyncWaiter;

class FakeNearbyShareSettingsObserver
    : public nearby_share::mojom::NearbyShareSettingsObserver {
 public:
  void OnEnabledChanged(bool enabled) override { enabled_ = enabled; }
  void OnFastInitiationNotificationStateChanged(
      nearby_share::mojom::FastInitiationNotificationState state) override {
    fast_initiation_notification_state_ = state;
  }
  void OnIsFastInitiationHardwareSupportedChanged(bool is_supported) override {
    is_fast_initiation_notification_hardware_supported_ = is_supported;
  }
  void OnDeviceNameChanged(const std::string& device_name) override {
    device_name_ = device_name;
  }
  void OnDataUsageChanged(nearby_share::mojom::DataUsage data_usage) override {
    data_usage_ = data_usage;
  }
  void OnVisibilityChanged(
      nearby_share::mojom::Visibility visibility) override {
    visibility_ = visibility;
  }
  void OnAllowedContactsChanged(
      const std::vector<std::string>& allowed_contacts) override {
    allowed_contacts_ = allowed_contacts;
  }
  void OnIsOnboardingCompleteChanged(bool is_complete) override {
    is_onboarding_complete_ = is_complete;
  }

  bool enabled_ = false;
  nearby_share::mojom::FastInitiationNotificationState
      fast_initiation_notification_state_ =
          nearby_share::mojom::FastInitiationNotificationState::kEnabled;
  bool is_fast_initiation_notification_hardware_supported_ = false;
  bool is_onboarding_complete_ = false;
  std::string device_name_ = "uncalled";
  nearby_share::mojom::DataUsage data_usage_ =
      nearby_share::mojom::DataUsage::kUnknown;
  nearby_share::mojom::Visibility visibility_ =
      nearby_share::mojom::Visibility::kUnknown;
  std::vector<std::string> allowed_contacts_;
  mojo::Receiver<nearby_share::mojom::NearbyShareSettingsObserver> receiver_{
      this};
};

class NearbyShareSettingsTest : public ::testing::Test {
 public:
  NearbyShareSettingsTest() : local_device_data_manager_(kDefaultDeviceName) {
    RegisterNearbySharingPrefs(pref_service_.registry());
    nearby_share_settings_ = std::make_unique<NearbyShareSettings>(
        &pref_service_, &local_device_data_manager_);
    nearby_share_settings_waiter_ =
        std::make_unique<NearbyShareSettingsAsyncWaiter>(
            nearby_share_settings_.get());

    nearby_share_settings_->AddSettingsObserver(
        observer_.receiver_.BindNewPipeAndPassRemote());
  }
  ~NearbyShareSettingsTest() override = default;

  void FlushMojoMessages() { observer_.receiver_.FlushForTesting(); }

  NearbyShareSettings* settings() { return nearby_share_settings_.get(); }

  NearbyShareSettingsAsyncWaiter* settings_waiter() {
    return nearby_share_settings_waiter_.get();
  }

  void SetIsOnboardingComplete(bool is_complete) {
    pref_service_.SetBoolean(prefs::kNearbySharingOnboardingCompletePrefName,
                             is_complete);
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  TestingPrefServiceSimple pref_service_;
  FakeNearbyShareLocalDeviceDataManager local_device_data_manager_;
  FakeNearbyShareSettingsObserver observer_;
  std::unique_ptr<NearbyShareSettings> nearby_share_settings_;
  std::unique_ptr<NearbyShareSettingsAsyncWaiter> nearby_share_settings_waiter_;
};

TEST_F(NearbyShareSettingsTest, GetAndSetEnabled) {
  EXPECT_FALSE(observer_.enabled_);
  settings()->SetIsOnboardingComplete(true);
  settings()->SetEnabled(true);
  EXPECT_TRUE(settings()->GetEnabled());
  FlushMojoMessages();
  EXPECT_TRUE(observer_.enabled_);

  bool enabled = false;
  settings_waiter()->GetEnabled(&enabled);
  EXPECT_TRUE(enabled);

  settings()->SetEnabled(false);
  EXPECT_FALSE(settings()->GetEnabled());
  FlushMojoMessages();
  EXPECT_FALSE(observer_.enabled_);

  settings_waiter()->GetEnabled(&enabled);
  EXPECT_FALSE(enabled);

  // Verify that setting the value to false again value doesn't trigger an
  // observer event.
  observer_.enabled_ = true;
  settings()->SetEnabled(false);
  EXPECT_FALSE(settings()->GetEnabled());
  FlushMojoMessages();
  // the observers's value should not have been updated.
  EXPECT_TRUE(observer_.enabled_);
}

TEST_F(NearbyShareSettingsTest, GetAndSetFastInitiationNotificationState) {
  // Fast init notifications are enabled by default.
  EXPECT_EQ(nearby_share::mojom::FastInitiationNotificationState::kEnabled,
            observer_.fast_initiation_notification_state_);
  settings()->SetFastInitiationNotificationState(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByUser);
  EXPECT_EQ(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByUser,
      settings()->GetFastInitiationNotificationState());
  FlushMojoMessages();
  EXPECT_EQ(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByUser,
      observer_.fast_initiation_notification_state_);

  nearby_share::mojom::FastInitiationNotificationState state =
      nearby_share::mojom::FastInitiationNotificationState::kEnabled;
  settings_waiter()->GetFastInitiationNotificationState(&state);
  EXPECT_EQ(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByUser,
      state);
}

TEST_F(NearbyShareSettingsTest,
       ParentFeatureChangesFastInitiationNotificationState) {
  // Fast init notifications are enabled by default.
  EXPECT_EQ(nearby_share::mojom::FastInitiationNotificationState::kEnabled,
            observer_.fast_initiation_notification_state_);
  settings()->SetIsOnboardingComplete(true);
  settings()->SetEnabled(true);
  FlushMojoMessages();

  // Simulate toggling the parent feature off.
  settings()->SetEnabled(false);
  FlushMojoMessages();
  EXPECT_FALSE(settings()->GetEnabled());
  EXPECT_EQ(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByFeature,
      observer_.fast_initiation_notification_state_);

  // Simulate toggling the parent feature on.
  settings()->SetEnabled(true);
  FlushMojoMessages();
  EXPECT_TRUE(settings()->GetEnabled());
  EXPECT_EQ(nearby_share::mojom::FastInitiationNotificationState::kEnabled,
            observer_.fast_initiation_notification_state_);
}

TEST_F(NearbyShareSettingsTest,
       ParentFeatureChangesFastInitiationNotificationDisabedByUser) {
  // Fast init notifications are enabled by default.
  EXPECT_EQ(nearby_share::mojom::FastInitiationNotificationState::kEnabled,
            observer_.fast_initiation_notification_state_);

  // Set explicitly disabled by user.
  settings()->SetFastInitiationNotificationState(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByUser);
  FlushMojoMessages();
  EXPECT_EQ(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByUser,
      observer_.fast_initiation_notification_state_);

  // Simulate toggling parent feature on.
  settings()->SetIsOnboardingComplete(true);
  settings()->SetEnabled(true);
  FlushMojoMessages();

  // Disabled by user should persist if parent feature was turned on.
  EXPECT_EQ(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByUser,
      observer_.fast_initiation_notification_state_);
}

TEST_F(NearbyShareSettingsTest, GetAndSetIsOnboardingComplete) {
  EXPECT_FALSE(observer_.is_onboarding_complete_);
  SetIsOnboardingComplete(true);
  EXPECT_TRUE(settings()->IsOnboardingComplete());
  FlushMojoMessages();
  EXPECT_TRUE(observer_.is_onboarding_complete_);

  bool is_complete = false;
  settings_waiter()->IsOnboardingComplete(&is_complete);
  EXPECT_TRUE(is_complete);
}

TEST_F(NearbyShareSettingsTest, GetAndSetIsFastInitiationHardwareSupported) {
  EXPECT_FALSE(observer_.is_fast_initiation_notification_hardware_supported_);
  settings()->SetIsFastInitiationHardwareSupported(true);

  FlushMojoMessages();
  EXPECT_TRUE(observer_.is_fast_initiation_notification_hardware_supported_);

  bool is_supported = false;
  settings_waiter()->GetIsFastInitiationHardwareSupported(&is_supported);
  EXPECT_TRUE(is_supported);
}

TEST_F(NearbyShareSettingsTest, ValidateDeviceName) {
  auto result = nearby_share::mojom::DeviceNameValidationResult::kValid;
  local_device_data_manager_.set_next_validation_result(
      nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
  settings_waiter()->ValidateDeviceName("", &result);
  EXPECT_EQ(result,
            nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);

  local_device_data_manager_.set_next_validation_result(
      nearby_share::mojom::DeviceNameValidationResult::kValid);
  settings_waiter()->ValidateDeviceName("this string is 32 bytes in UTF-8",
                                        &result);
  EXPECT_EQ(result, nearby_share::mojom::DeviceNameValidationResult::kValid);
}

TEST_F(NearbyShareSettingsTest, GetAndSetDeviceName) {
  std::string name = "not_the_default";
  settings_waiter()->GetDeviceName(&name);
  EXPECT_EQ(kDefaultDeviceName, name);

  // When we get a validation error, setting the name should not succeed.
  EXPECT_EQ("uncalled", observer_.device_name_);
  auto result = nearby_share::mojom::DeviceNameValidationResult::kValid;
  local_device_data_manager_.set_next_validation_result(
      nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
  settings_waiter()->SetDeviceName("", &result);
  EXPECT_EQ(result,
            nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty);
  EXPECT_EQ(kDefaultDeviceName, settings()->GetDeviceName());

  // When the name is valid, setting should succeed.
  EXPECT_EQ("uncalled", observer_.device_name_);
  result = nearby_share::mojom::DeviceNameValidationResult::kValid;
  local_device_data_manager_.set_next_validation_result(
      nearby_share::mojom::DeviceNameValidationResult::kValid);
  settings_waiter()->SetDeviceName("d", &result);
  EXPECT_EQ(result, nearby_share::mojom::DeviceNameValidationResult::kValid);
  EXPECT_EQ("d", settings()->GetDeviceName());

  EXPECT_EQ("uncalled", observer_.device_name_);
  FlushMojoMessages();
  EXPECT_EQ("d", observer_.device_name_);

  settings_waiter()->GetDeviceName(&name);
  EXPECT_EQ("d", name);
}

TEST_F(NearbyShareSettingsTest, GetAndSetDataUsage) {
  EXPECT_EQ(nearby_share::mojom::DataUsage::kUnknown, observer_.data_usage_);
  settings()->SetDataUsage(nearby_share::mojom::DataUsage::kOffline);
  EXPECT_EQ(nearby_share::mojom::DataUsage::kOffline,
            settings()->GetDataUsage());
  FlushMojoMessages();
  EXPECT_EQ(nearby_share::mojom::DataUsage::kOffline, observer_.data_usage_);

  nearby_share::mojom::DataUsage data_usage =
      nearby_share::mojom::DataUsage::kUnknown;
  settings_waiter()->GetDataUsage(&data_usage);
  EXPECT_EQ(nearby_share::mojom::DataUsage::kOffline, data_usage);
}

TEST_F(NearbyShareSettingsTest, GetAndSetVisibility) {
  EXPECT_EQ(nearby_share::mojom::Visibility::kUnknown, observer_.visibility_);
  settings()->SetVisibility(nearby_share::mojom::Visibility::kNoOne);
  EXPECT_EQ(nearby_share::mojom::Visibility::kNoOne,
            settings()->GetVisibility());
  FlushMojoMessages();
  EXPECT_EQ(nearby_share::mojom::Visibility::kNoOne, observer_.visibility_);

  nearby_share::mojom::Visibility visibility =
      nearby_share::mojom::Visibility::kUnknown;
  settings_waiter()->GetVisibility(&visibility);
  EXPECT_EQ(nearby_share::mojom::Visibility::kNoOne, visibility);
}

TEST_F(NearbyShareSettingsTest, GetAndSetAllowedContacts) {
  const std::string id1("1");

  std::vector<std::string> allowed_contacts;

  settings_waiter()->GetAllowedContacts(&allowed_contacts);
  EXPECT_EQ(0u, allowed_contacts.size());

  settings()->SetAllowedContacts({id1});
  FlushMojoMessages();
  EXPECT_EQ(1u, observer_.allowed_contacts_.size());
  EXPECT_TRUE(base::Contains(observer_.allowed_contacts_, id1));

  settings_waiter()->GetAllowedContacts(&allowed_contacts);
  EXPECT_EQ(1u, allowed_contacts.size());
  EXPECT_TRUE(base::Contains(allowed_contacts, id1));

  settings()->SetAllowedContacts({});
  FlushMojoMessages();
  EXPECT_EQ(0u, observer_.allowed_contacts_.size());

  settings_waiter()->GetAllowedContacts(&allowed_contacts);
  EXPECT_EQ(0u, allowed_contacts.size());
}