chromium/chrome/browser/chromeos/reporting/websites/website_usage_observer_unittest.cc

// Copyright 2023 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/chromeos/reporting/websites/website_usage_observer.h"

#include <memory>
#include <vector>

#include "base/json/values_util.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/apps/app_service/metrics/website_metrics.h"
#include "chrome/browser/chromeos/reporting/metric_reporting_prefs.h"
#include "chrome/browser/chromeos/reporting/websites/website_metrics_retriever_interface.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/reporting/metrics/fakes/fake_reporting_settings.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::_;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::SizeIs;

namespace reporting {
namespace {

constexpr char kTestUserId[] = "TestUser";
constexpr char kTestUrl[] = "https://a.example.org/";

// Mock retriever for the `WebsiteMetrics` component.
class MockWebsiteMetricsRetriever : public WebsiteMetricsRetrieverInterface {
 public:
  MockWebsiteMetricsRetriever() = default;
  MockWebsiteMetricsRetriever(const MockWebsiteMetricsRetriever&) = delete;
  MockWebsiteMetricsRetriever& operator=(const MockWebsiteMetricsRetriever&) =
      delete;
  ~MockWebsiteMetricsRetriever() override = default;

  MOCK_METHOD(void,
              GetWebsiteMetrics,
              (WebsiteMetricsCallback callback),
              (override));
};

class WebsiteUsageObserverTest : public ::testing::Test {
 protected:
  void SetUp() override {
    ASSERT_TRUE(profile_manager_.SetUp());
    profile_ = profile_manager_.CreateTestingProfile(kTestUserId);
    website_metrics_ = std::make_unique<::apps::WebsiteMetrics>(
        profile_, /*user_type_by_device_type=*/0);
    auto mock_website_metrics_retriever =
        std::make_unique<MockWebsiteMetricsRetriever>();
    EXPECT_CALL(*mock_website_metrics_retriever, GetWebsiteMetrics(_))
        .WillOnce(
            [this](WebsiteMetricsRetrieverInterface::WebsiteMetricsCallback
                       callback) {
              std::move(callback).Run(website_metrics_.get());
            });

    website_usage_observer_ = std::make_unique<WebsiteUsageObserver>(
        profile_->GetWeakPtr(), &reporting_settings_,
        std::move(mock_website_metrics_retriever));
  }

  void SetAllowlistedUrls(const std::vector<std::string>& allowlisted_urls) {
    base::Value::List allowed_urls;
    for (const auto& url : allowlisted_urls) {
      allowed_urls.Append(url);
    }
    reporting_settings_.SetList(kReportWebsiteTelemetryAllowlist,
                                std::move(allowed_urls));
  }

  void SetAllowlistedTelemetryTypes(
      const std::vector<std::string>& allowlisted_telemetry_types) {
    base::Value::List allowed_telemetry_types;
    for (const auto& telemetry_type : allowlisted_telemetry_types) {
      allowed_telemetry_types.Append(telemetry_type);
    }
    reporting_settings_.SetList(kReportWebsiteTelemetry,
                                std::move(allowed_telemetry_types));
  }

  void AssertWebsiteUsageDataInPrefStore(const std::string& url,
                                         const base::TimeDelta& running_time) {
    const auto& usage_dict_pref = profile_->GetPrefs()->GetDict(kWebsiteUsage);
    ASSERT_THAT(usage_dict_pref.Find(url), NotNull());
    EXPECT_THAT(base::ValueToTimeDelta(usage_dict_pref.Find(url)),
                Eq(running_time));
  }

  ::content::BrowserTaskEnvironment task_environment_;
  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
  raw_ptr<::content::WebContents> web_contents_;

  test::FakeReportingSettings reporting_settings_;
  raw_ptr<Profile> profile_;
  std::unique_ptr<::apps::WebsiteMetrics> website_metrics_;
  std::unique_ptr<WebsiteUsageObserver> website_usage_observer_;
};

TEST_F(WebsiteUsageObserverTest, AllowAllUrlsAndTelemetryTypes) {
  SetAllowlistedUrls({ContentSettingsPattern::Wildcard().ToString()});
  SetAllowlistedTelemetryTypes({kWebsiteTelemetryUsageType});

  // Simulate website usage by calling observer callback directly. We will
  // generate real events in browser tests.
  static constexpr base::TimeDelta kRunningTime = base::Minutes(2);
  ASSERT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);

  ASSERT_THAT(profile_->GetPrefs()->GetDict(kWebsiteUsage), SizeIs(1UL));
  AssertWebsiteUsageDataInPrefStore(kTestUrl, kRunningTime);
}

TEST_F(WebsiteUsageObserverTest, AllowlistedUrlsUnset) {
  SetAllowlistedTelemetryTypes({kWebsiteTelemetryUsageType});

  // Simulate website usage by calling observer callback directly. We will
  // generate real events in browser tests.
  static constexpr base::TimeDelta kRunningTime = base::Minutes(2);
  ASSERT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);

  EXPECT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
}

TEST_F(WebsiteUsageObserverTest, DisallowedUrl) {
  SetAllowlistedUrls({});
  SetAllowlistedTelemetryTypes({kWebsiteTelemetryUsageType});

  // Simulate website usage by calling observer callback directly. We will
  // generate real events in browser tests.
  static constexpr base::TimeDelta kRunningTime = base::Minutes(2);
  ASSERT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);

  EXPECT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
}

TEST_F(WebsiteUsageObserverTest, AllowedTelemetryTypesUnset) {
  SetAllowlistedUrls({ContentSettingsPattern::Wildcard().ToString()});

  // Simulate website usage by calling observer callback directly. We will
  // generate real events in browser tests.
  static constexpr base::TimeDelta kRunningTime = base::Minutes(2);
  ASSERT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);

  EXPECT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
}

TEST_F(WebsiteUsageObserverTest, DisallowedUsageTelemetryType) {
  SetAllowlistedUrls({ContentSettingsPattern::Wildcard().ToString()});
  SetAllowlistedTelemetryTypes({});

  // Simulate website usage by calling observer callback directly. We will
  // generate real events in browser tests.
  static constexpr base::TimeDelta kRunningTime = base::Minutes(2);
  ASSERT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);

  EXPECT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
}

TEST_F(WebsiteUsageObserverTest, MicrosecondUsageData) {
  SetAllowlistedUrls({ContentSettingsPattern::Wildcard().ToString()});
  SetAllowlistedTelemetryTypes({kWebsiteTelemetryUsageType});

  // Simulate website usage by calling observer callback directly. We will
  // generate real events in browser tests.
  static constexpr base::TimeDelta kRunningTime = base::Microseconds(200);
  ASSERT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);

  EXPECT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
}

TEST_F(WebsiteUsageObserverTest, SubsequentUsageData) {
  SetAllowlistedUrls({ContentSettingsPattern::Wildcard().ToString()});
  SetAllowlistedTelemetryTypes({kWebsiteTelemetryUsageType});

  // Simulate website usage by calling observer callback directly. We will
  // generate real events in browser tests.
  static constexpr base::TimeDelta kRunningTime = base::Minutes(2);
  ASSERT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);
  ASSERT_THAT(profile_->GetPrefs()->GetDict(kWebsiteUsage), SizeIs(1UL));
  AssertWebsiteUsageDataInPrefStore(kTestUrl, kRunningTime);

  // Simulate further usage and verify this usage data is aggregated with the
  // one in the pref store.
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);
  ASSERT_THAT(profile_->GetPrefs()->GetDict(kWebsiteUsage), SizeIs(1UL));
  AssertWebsiteUsageDataInPrefStore(kTestUrl, kRunningTime + kRunningTime);
}

TEST_F(WebsiteUsageObserverTest, SubsequentUsageDataWithDifferentUrl) {
  SetAllowlistedUrls({ContentSettingsPattern::Wildcard().ToString()});
  SetAllowlistedTelemetryTypes({kWebsiteTelemetryUsageType});

  // Simulate website usage by calling observer callback directly. We will
  // generate real events in browser tests.
  static constexpr base::TimeDelta kRunningTime = base::Minutes(2);
  ASSERT_TRUE(profile_->GetPrefs()->GetDict(kWebsiteUsage).empty());
  website_usage_observer_->OnUrlUsage(GURL(kTestUrl), kRunningTime);
  ASSERT_THAT(profile_->GetPrefs()->GetDict(kWebsiteUsage), SizeIs(1UL));
  AssertWebsiteUsageDataInPrefStore(kTestUrl, kRunningTime);

  // Simulate usage for different URL and verify there is a new entry in the
  // pref store.
  static constexpr char kOtherUrl[] = "https://b.example.org/";
  website_usage_observer_->OnUrlUsage(GURL(kOtherUrl), kRunningTime);
  ASSERT_THAT(profile_->GetPrefs()->GetDict(kWebsiteUsage), SizeIs(2UL));
  AssertWebsiteUsageDataInPrefStore(kTestUrl, kRunningTime);
  AssertWebsiteUsageDataInPrefStore(kOtherUrl, kRunningTime);
}

}  // namespace
}  // namespace reporting