chromium/components/history/core/browser/history_backend_unittest.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "components/history/core/browser/history_backend.h"

#include <stddef.h>

#include <iterator>
#include <limits>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/gtest_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/favicon/core/favicon_backend.h"
#include "components/favicon_base/favicon_usage_data.h"
#include "components/history/core/browser/features.h"
#include "components/history/core/browser/history_backend_client.h"
#include "components/history/core/browser/history_constants.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/in_memory_database.h"
#include "components/history/core/browser/in_memory_history_backend.h"
#include "components/history/core/browser/keyword_search_term.h"
#include "components/history/core/browser/keyword_search_term_util.h"
#include "components/history/core/browser/page_usage_data.h"
#include "components/history/core/test/database_test_utils.h"
#include "components/history/core/test/history_client_fake_bookmarks.h"
#include "components/history/core/test/test_history_database.h"
#include "components/history/core/test/visit_annotations_test_utils.h"
#include "components/sync/base/features.h"
#include "sql/sqlite_result_code_values.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "url/gurl.h"

// This file only tests functionality where it is most convenient to call the
// backend directly. Most of the history backend functions are tested by the
// history unit test. Because of the elaborate callbacks involved, this is no
// harder than calling it directly for many things.

namespace history {

namespace {

FaviconBitmap;
FaviconBitmapType;
IconMapping;
IconType;
IconTypeSet;
ElementsAre;
UnorderedElementsAre;
UnorderedElementsAreArray;

const int kSmallEdgeSize =;
const int kLargeEdgeSize =;

const gfx::Size kSmallSize =;
const gfx::Size kLargeSize =;

MATCHER_P(HasVisitID, visit_id, "") {}

// Minimal representation of a `Cluster` for verifying 2 clusters are equal.
struct ClusterExpectation {};

SimulateNotificationCallback;

void SimulateNotificationURLVisited(HistoryServiceObserver* observer,
                                    const URLRow* row1,
                                    const URLRow* row2,
                                    const URLRow* row3) {}

void SimulateNotificationURLsModified(HistoryServiceObserver* observer,
                                      const URLRow* row1,
                                      const URLRow* row2,
                                      const URLRow* row3) {}

VisitContextAnnotations MakeContextAnnotations(bool omnibox_url_copied) {}

#if BUILDFLAG(IS_IOS)
// Helper to create a SyncDeviceInfoMap where
// `android_phone_originator_cache_guids` and `ios_phone_originator_cache_guids`
// represent Originator Cache GUIDs that map to Android/Phone and iOS/Phone
// device type/OS, respectively.
SyncDeviceInfoMap MakeSyncDeviceInfo(
    const std::vector<std::string>& android_phone_originator_cache_guids,
    const std::vector<std::string>& ios_phone_originator_cache_guids,
    const std::string& local_ios_phone_originator_cache_guid = "") {
  SyncDeviceInfoMap sync_device_info;

  for (const auto& android_phone_originator_cache_guid :
       android_phone_originator_cache_guids) {
    sync_device_info[android_phone_originator_cache_guid] =
        std::make_pair(syncer::DeviceInfo::OsType::kAndroid,
                       syncer::DeviceInfo::FormFactor::kPhone);
  }

  for (const auto& ios_phone_originator_cache_guid :
       ios_phone_originator_cache_guids) {
    sync_device_info[ios_phone_originator_cache_guid] =
        std::make_pair(syncer::DeviceInfo::OsType::kIOS,
                       syncer::DeviceInfo::FormFactor::kPhone);
  }

  if (!local_ios_phone_originator_cache_guid.empty()) {
    sync_device_info[local_ios_phone_originator_cache_guid] =
        std::make_pair(syncer::DeviceInfo::OsType::kIOS,
                       syncer::DeviceInfo::FormFactor::kPhone);
  }

  return sync_device_info;
}
#endif

}  // namespace

class HistoryBackendTestBase;

// This must be a separate object since HistoryBackend manages its lifetime.
// This just forwards the messages we're interested in to the test object.
class HistoryBackendTestDelegate : public HistoryBackend::Delegate {};

// Exposes some of `HistoryBackend`'s private methods.
class TestHistoryBackend : public HistoryBackend {};

class HistoryBackendTestBase : public testing::Test {};

bool HistoryBackendTestDelegate::CanAddURL(const GURL& url) const {}

void HistoryBackendTestDelegate::SetInMemoryBackend(
    std::unique_ptr<InMemoryHistoryBackend> backend) {}

void HistoryBackendTestDelegate::NotifyFaviconsChanged(
    const std::set<GURL>& page_urls,
    const GURL& icon_url) {}

void HistoryBackendTestDelegate::NotifyURLVisited(
    const URLRow& url_row,
    const VisitRow& new_visit,
    std::optional<int64_t> local_navigation_id) {}

void HistoryBackendTestDelegate::NotifyURLsModified(
    const URLRows& changed_urls) {}

void HistoryBackendTestDelegate::NotifyDeletions(DeletionInfo deletion_info) {}

void HistoryBackendTestDelegate::NotifyVisitedLinksAdded(
    const HistoryAddPageArgs& args) {}

void HistoryBackendTestDelegate::NotifyVisitedLinksDeleted(
    const std::vector<DeletedVisitedLink>& links) {}

void HistoryBackendTestDelegate::NotifyKeywordSearchTermUpdated(
    const URLRow& row,
    KeywordID keyword_id,
    const std::u16string& term) {}

void HistoryBackendTestDelegate::NotifyKeywordSearchTermDeleted(URLID url_id) {}

void HistoryBackendTestDelegate::DBLoaded() {}

class HistoryBackendTest : public HistoryBackendTestBase {};

class InMemoryHistoryBackendTest : public HistoryBackendTestBase {};

const KeywordID InMemoryHistoryBackendTest::kTestKeywordId =;
const char16_t InMemoryHistoryBackendTest::kTestSearchTerm1[] =;
const char16_t InMemoryHistoryBackendTest::kTestSearchTerm2[] =;

// http://crbug.com/114287
#if BUILDFLAG(IS_WIN)
#define MAYBE_Loaded
#else
#define MAYBE_Loaded
#endif  // BUILDFLAG(IS_WIN)
TEST_F(HistoryBackendTest, MAYBE_Loaded) {}

TEST_F(HistoryBackendTest, DeleteAll) {}

// Test that clearing all history does not delete bookmark favicons in the
// special case that the bookmark page URL is no longer present in the History
// database's urls table.
TEST_F(HistoryBackendTest, DeleteAllURLPreviouslyDeleted) {}

// Checks that adding a visit, then calling DeleteAll, and then trying to add
// data for the visited page works.  This can happen when clearing the history
// immediately after visiting a page.
TEST_F(HistoryBackendTest, DeleteAllThenAddData) {}

TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {}

// Tests a handful of assertions for a navigation with a type of
// KEYWORD_GENERATED.
TEST_F(HistoryBackendTest, KeywordGenerated) {}

TEST_F(HistoryBackendTest, OpenerWithRedirect) {}

TEST_F(HistoryBackendTest, ClientRedirect) {}

// Do not update original URL on form submission redirect
TEST_F(HistoryBackendTest, FormSubmitRedirect) {}

TEST_F(HistoryBackendTest, AddPagesWithDetails) {}

// This verifies that a notification is fired. In-depth testing of logic should
// be done in HistoryTest.SetTitle.
TEST_F(HistoryBackendTest, SetPageTitleFiresNotificationWithCorrectDetails) {}

// There's no importer on Android.
#if !BUILDFLAG(IS_ANDROID)
TEST_F(HistoryBackendTest, ImportedFaviconsTest) {}
#endif  // !BUILDFLAG(IS_ANDROID)

TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {}

TEST_F(HistoryBackendTest, AddPageVisitBackForward) {}

TEST_F(HistoryBackendTest, AddPageVisitRedirectBackForward) {}

TEST_F(HistoryBackendTest, AddPageVisitSource) {}

TEST_F(HistoryBackendTest, AddPageVisitNotLastVisit) {}

TEST_F(HistoryBackendTest, AddPageVisitFiresNotificationWithCorrectDetails) {}

TEST_F(HistoryBackendTest, AddPageArgsSource) {}

TEST_F(HistoryBackendTest, AddPageArgsConsiderForNewTabPageMostVisited) {}

TEST_F(HistoryBackendTest, AddContentModelAnnotationsWithNoEntryInVisitTable) {}

TEST_F(HistoryBackendTest, AddRelatedSearchesWithNoEntryInVisitTable) {}

TEST_F(HistoryBackendTest, AddSearchMetadataWithNoEntryInVisitTable) {}

TEST_F(HistoryBackendTest, SetBrowsingTopicsAllowed) {}

TEST_F(HistoryBackendTest, AddContentModelAnnotations) {}

TEST_F(HistoryBackendTest, AddRelatedSearches) {}

TEST_F(HistoryBackendTest, AddSearchMetadata) {}

TEST_F(HistoryBackendTest, AddPageMetadata) {}

TEST_F(HistoryBackendTest, SetHasUrlKeyedImage) {}

TEST_F(HistoryBackendTest, MixedContentAnnotationsRequestTypes) {}

TEST_F(HistoryBackendTest, GetMostRecentVisits) {}

TEST_F(HistoryBackendTest, RemoveVisitsTransitions) {}

TEST_F(HistoryBackendTest, RemoveVisitsSource) {}

// Test for migration of adding visit_source table.
TEST_F(HistoryBackendTest, MigrationVisitSource) {}

// Test that `recent_redirects_` stores the full redirect chain in case of
// client redirects. In this case, a server-side redirect is followed by a
// client-side one.
TEST_F(HistoryBackendTest, RecentRedirectsForClientRedirects) {}

// Test that adding a favicon for a new icon URL:
// - Sends a notification that the favicon for the page URL has changed.
// - Does not send a notification that the favicon for the icon URL has changed
//   as there are no other page URLs which use the icon URL.
TEST_F(HistoryBackendTest, FaviconChangedNotificationNewFavicon) {}

// Test that changing the favicon bitmap data for an icon URL:
// - Does not send a notification that the favicon for the page URL has changed.
// - Sends a notification that the favicon for the icon URL has changed (Several
//   page URLs may be mapped to the icon URL).
TEST_F(HistoryBackendTest, FaviconChangedNotificationBitmapDataChanged) {}

// Test that changing the page URL -> icon URL mapping:
// - Sends a notification that the favicon for the page URL has changed.
// - Does not send a notification that the favicon for the icon URL has changed.
TEST_F(HistoryBackendTest, FaviconChangedNotificationIconMappingChanged) {}

// Test that changing the page URL -> icon URL mapping for multiple page URLs
// sends notifications that the favicon for each page URL has changed.
TEST_F(HistoryBackendTest,
       FaviconChangedNotificationIconMappingChangedForMultiplePages) {}

// Test that changing both:
// - The page URL -> icon URL mapping
// - The favicon's bitmap data
// sends notifications that the favicon data for both the page URL and the icon
// URL have changed.
TEST_F(HistoryBackendTest,
       FaviconChangedNotificationIconMappingAndBitmapDataChanged) {}

// Test that if MergeFavicon() copies favicon bitmaps from one favicon to
// another that a notification is sent that the favicon at the destination
// icon URL has changed.
TEST_F(HistoryBackendTest, FaviconChangedNotificationsMergeCopy) {}

// Test that no notifications are broadcast if calling SetFavicons() /
// MergeFavicon() / UpdateFaviconMappingsAndFetch() did not alter the Favicon
// database data (with the exception of the "last updated time").
TEST_F(HistoryBackendTest, NoFaviconChangedNotifications) {}


// Test that CloneFaviconMappingsForPages() propagates favicon mappings to the
// provided pages and their redirects.
TEST_F(HistoryBackendTest, CloneFaviconMappingsForPages) {}

// Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is
// no valid favicon database.
TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) {}

TEST_F(HistoryBackendTest, GetCountsAndLastVisitForOrigins) {}

TEST_F(HistoryBackendTest, UpdateVisitDuration) {}

TEST_F(HistoryBackendTest, MarkVisitAsKnownToSync) {}

// Test for migration of adding visit_duration column.
TEST_F(HistoryBackendTest, MigrationVisitDuration) {}

TEST_F(HistoryBackendTest, AddPageNoVisitForBookmark) {}

TEST_F(HistoryBackendTest, ExpireHistoryForTimes) {}

TEST_F(HistoryBackendTest, ExpireHistory) {}

TEST_F(HistoryBackendTest, DeleteMatchingUrlsForKeyword) {}

// Test DeleteFTSIndexDatabases deletes expected files.
TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) {}

// Tests that calling DatabaseErrorCallback doesn't cause crash. (Regression
// test for https://crbug.com/796138)
TEST_F(HistoryBackendTest, DatabaseError) {}

// Tests that calling DatabaseErrorCallback results in killing the database and
// notifying the TypedURLSyncBridge at the same time so that no further
// notification from the backend can lead to the bridge. (Regression test for
// https://crbug.com/853395)
TEST_F(HistoryBackendTest, DatabaseErrorSynchronouslyKillAndNotifyBridge) {}

// Tests that a typed navigation which results in a redirect from HTTP to HTTPS
// will cause the HTTPS URL to accrue the typed count, and the HTTP URL to not.
TEST_F(HistoryBackendTest, RedirectScoring) {}

TEST_F(HistoryBackendTest, RedirectWithQualifiers) {}

// Tests that a typed navigation will accrue the typed count even when a client
// redirect from HTTP to HTTPS occurs.
TEST_F(HistoryBackendTest, ClientRedirectScoring) {}

// Common implementation for the two tests below, given that the only difference
// between them is the type of the notification sent out.
void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows(
    const SimulateNotificationCallback& callback) {}

TEST_F(InMemoryHistoryBackendTest, OnURLsModified) {}

TEST_F(InMemoryHistoryBackendTest, OnURLsVisisted) {}

TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedPiecewise) {}

TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedEnMasse) {}

void InMemoryHistoryBackendTest::PopulateTestURLsAndSearchTerms(
    URLRow* row1,
    URLRow* row2,
    const std::u16string& term1,
    const std::u16string& term2) {}

TEST_F(InMemoryHistoryBackendTest, SetKeywordSearchTerms) {}

TEST_F(InMemoryHistoryBackendTest, DeleteKeywordSearchTerms) {}

TEST_F(InMemoryHistoryBackendTest, DeleteAllSearchTermsForKeyword) {}

TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedWithSearchTerms) {}

TEST_F(HistoryBackendTest, QueryMostVisitedURLs) {}

TEST_F(HistoryBackendTest, ExpireSegmentData) {}

TEST_F(HistoryBackendTest, QueryMostRepeatedQueriesForKeyword) {}

TEST(FormatUrlForRedirectComparisonTest, TestUrlFormatting) {}

TEST_F(HistoryBackendTest, ExpireVisitDeletes) {}

TEST_F(HistoryBackendTest, AddPageWithContextAnnotations) {}

TEST_F(HistoryBackendTest, GetAnnotatedVisits) {}

TEST_F(HistoryBackendTest, GetAnnotatedVisits_Unclustered) {}

TEST_F(HistoryBackendTest, PreservesAllContextAnnotationsFields) {}

TEST_F(HistoryBackendTest, FindMostRecentClusteredTime) {}

TEST_F(HistoryBackendTest, ReplaceClusters) {}

TEST_F(HistoryBackendTest, GetMostRecentClusters) {}

TEST_F(HistoryBackendTest, AddClusters_GetCluster) {}

TEST_F(HistoryBackendTest, AddClusters_UpdateVisitsInteractionState) {}

TEST_F(HistoryBackendTest, ReserveNextClusterIdWithVisit_GetCluster) {}

TEST_F(
    HistoryBackendTest,
    ReserveNextClusterId_AddVisitsToCluster_GetCluster_GetClusterIdContainingVisit) {}

TEST_F(
    HistoryBackendTest,
    ReserveNextClusterId_AddVisitsToCluster_UpdateClusterTriggerability_GetCluster) {}

TEST_F(HistoryBackendTest,
       AddVisitToSyncedCluster_GetCluster_UpdateClusterVisit) {}

TEST_F(HistoryBackendTest, UpdateClusterVisit_NoClusterAssigned) {}

TEST_F(HistoryBackendTest, GetRedirectChainStart) {}

TEST_F(HistoryBackendTest, GetRedirectChain) {}

TEST_F(HistoryBackendTest, AddSyncedVisitAddsOnlyValidURLs) {}

TEST_F(HistoryBackendTest, AddSyncedVisitWritesIsKnownToSync) {}

#if BUILDFLAG(IS_IOS)
TEST_F(HistoryBackendTest,
       UpdateVisitReferrerOpenerIDs_DoesNotDoubleCountVisitInSegments) {
  backend_->SetCanAddForeignVisitsToSegments(true);

  SyncDeviceInfoMap sync_device_info =
      MakeSyncDeviceInfo({"foreign"}, {}, "local");

  backend_->SetSyncDeviceInfo(std::move(sync_device_info));
  backend_->SetLocalDeviceOriginatorCacheGuid("local");

  VisitRow foreign_visit_1;
  foreign_visit_1.visit_time = base::Time::Now();
  foreign_visit_1.transition = ui::PageTransitionFromInt(
      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_CHAIN_START |
      ui::PAGE_TRANSITION_CHAIN_END);
  foreign_visit_1.originator_cache_guid = "foreign";
  foreign_visit_1.is_known_to_sync = true;
  foreign_visit_1.consider_for_ntp_most_visited = true;

  VisitID foreign_visit_1_id = backend_->AddSyncedVisit(
      GURL("https://some.url"), u"Title", /*hidden=*/false, foreign_visit_1,
      std::nullopt, std::nullopt);

  const ui::PageTransition kLink = ui::PageTransitionFromInt(
      ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CHAIN_START |
      ui::PAGE_TRANSITION_CHAIN_END);

  VisitRow foreign_visit_2;
  foreign_visit_2.visit_time = base::Time::Now();
  foreign_visit_2.referring_visit = foreign_visit_1_id;
  foreign_visit_2.transition = kLink;
  foreign_visit_2.originator_cache_guid = "foreign";
  foreign_visit_2.is_known_to_sync = true;
  foreign_visit_2.consider_for_ntp_most_visited = true;

  backend_->AddSyncedVisit(GURL("https://foobar.url"), u"Foobar",
                           /*hidden=*/false, foreign_visit_2, std::nullopt,
                           std::nullopt);

  // Check that the visits were added.
  VisitVector all_visits;
  backend_->db_->GetAllVisitsInRange(base::Time(), base::Time(), kNoAppIdFilter,
                                     0, &all_visits);
  ASSERT_EQ(2U, all_visits.size());

  // Segments exist for both visits.
  EXPECT_TRUE(HasSegmentWithID(all_visits[0].segment_id));
  EXPECT_TRUE(HasSegmentWithID(all_visits[1].segment_id));

  // The visits belong to the same segment.
  EXPECT_EQ(all_visits[0].segment_id, all_visits[1].segment_id);
  EXPECT_EQ(TotalNumVisitsForSegment(all_visits[0].segment_id), 2);

  // Re-assign the second visit's referrer, which updates segments.
  backend_->UpdateVisitReferrerOpenerIDs(all_visits[1].visit_id, 0, 0);

  VisitVector updated_visits;
  backend_->db_->GetAllVisitsInRange(base::Time(), base::Time(), kNoAppIdFilter,
                                     0, &updated_visits);

  // The second visit no longer belongs to a segment, so the number of visits
  // is decremented.
  EXPECT_NE(updated_visits[0].segment_id, updated_visits[1].segment_id);
  EXPECT_EQ(updated_visits[1].segment_id, 0);
  EXPECT_EQ(TotalNumVisitsForSegment(updated_visits[0].segment_id), 1);
  EXPECT_EQ(TotalNumVisitsForSegment(updated_visits[1].segment_id), 0);
}

TEST_F(HistoryBackendTest,
       UpdateSyncedVisit_DoesNotDoubleCountVisitInSegments) {
  backend_->SetCanAddForeignVisitsToSegments(true);

  SyncDeviceInfoMap sync_device_info =
      MakeSyncDeviceInfo({"foreign"}, {}, "local");

  backend_->SetSyncDeviceInfo(std::move(sync_device_info));
  backend_->SetLocalDeviceOriginatorCacheGuid("local");

  VisitRow foreign_visit_1;
  foreign_visit_1.visit_time = base::Time::Now();
  foreign_visit_1.transition = ui::PageTransitionFromInt(
      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_CHAIN_START |
      ui::PAGE_TRANSITION_CHAIN_END);
  foreign_visit_1.originator_cache_guid = "foreign";
  foreign_visit_1.is_known_to_sync = true;
  foreign_visit_1.consider_for_ntp_most_visited = true;

  VisitID foreign_visit_1_id = backend_->AddSyncedVisit(
      GURL("https://some.url"), u"Title", /*hidden=*/false, foreign_visit_1,
      std::nullopt, std::nullopt);

  const ui::PageTransition kLink = ui::PageTransitionFromInt(
      ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CHAIN_START |
      ui::PAGE_TRANSITION_CHAIN_END);

  VisitRow foreign_visit_2;
  foreign_visit_2.visit_time = base::Time::Now();
  foreign_visit_2.referring_visit = foreign_visit_1_id;
  foreign_visit_2.transition = kLink;
  foreign_visit_2.originator_cache_guid = "foreign";
  foreign_visit_2.is_known_to_sync = true;
  foreign_visit_2.consider_for_ntp_most_visited = true;

  backend_->AddSyncedVisit(GURL("https://foobar.url"), u"Foobar",
                           /*hidden=*/false, foreign_visit_2, std::nullopt,
                           std::nullopt);

  // Check that the visits were added.
  VisitVector all_visits;
  backend_->db_->GetAllVisitsInRange(base::Time(), base::Time(), kNoAppIdFilter,
                                     0, &all_visits);
  ASSERT_EQ(2U, all_visits.size());

  // Segments exist for both visits.
  EXPECT_TRUE(HasSegmentWithID(all_visits[0].segment_id));
  EXPECT_TRUE(HasSegmentWithID(all_visits[1].segment_id));

  // The visits belong to the same segment.
  EXPECT_EQ(all_visits[0].segment_id, all_visits[1].segment_id);
  EXPECT_EQ(TotalNumVisitsForSegment(all_visits[0].segment_id), 2);

  foreign_visit_2.transition = ui::PAGE_TRANSITION_TYPED;
  backend_->UpdateSyncedVisit(GURL("https://foobar.url"), u"Foobar",
                              /*hidden=*/false, foreign_visit_2, std::nullopt,
                              std::nullopt);

  VisitVector updated_visits;
  backend_->db_->GetAllVisitsInRange(base::Time(), base::Time(), kNoAppIdFilter,
                                     0, &updated_visits);

  // The second visit no longer belongs to the segment, so the number of visits
  // is decremented.
  EXPECT_NE(updated_visits[0].segment_id, updated_visits[1].segment_id);
  EXPECT_EQ(TotalNumVisitsForSegment(updated_visits[0].segment_id), 1);
  EXPECT_EQ(TotalNumVisitsForSegment(updated_visits[1].segment_id), 1);
}

TEST_F(HistoryBackendTest,
       AddSyncedVisit_AddsVisitWithValidOriginatorCacheGuidToSegments) {
  backend_->SetCanAddForeignVisitsToSegments(true);

  SyncDeviceInfoMap sync_device_info =
      MakeSyncDeviceInfo({"foreign"}, {}, "local");

  backend_->SetSyncDeviceInfo(std::move(sync_device_info));
  backend_->SetLocalDeviceOriginatorCacheGuid("local");

  VisitRow foreign_visit;
  foreign_visit.visit_time = base::Time::Now();
  foreign_visit.transition = ui::PageTransitionFromInt(
      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_CHAIN_START |
      ui::PAGE_TRANSITION_CHAIN_END);
  foreign_visit.originator_cache_guid = "foreign";
  foreign_visit.is_known_to_sync = true;
  foreign_visit.consider_for_ntp_most_visited = true;

  VisitID added_id = backend_->AddSyncedVisit(
      GURL("https://some.url"), u"Title", /*hidden=*/false, foreign_visit,
      std::nullopt, std::nullopt);

  ASSERT_NE(added_id, kInvalidVisitID);

  VisitRow added_visit;

  ASSERT_TRUE(backend_->GetVisitByID(added_id, &added_visit));
  EXPECT_TRUE(added_visit.consider_for_ntp_most_visited);

  // The visit belongs to a segment.
  EXPECT_NE(added_visit.segment_id, 0);
}

TEST_F(
    HistoryBackendTest,
    AddSyncedVisit_DoesNotAddVisitToSegmentsWithMissingForeignDeviceInformation) {
  backend_->SetCanAddForeignVisitsToSegments(true);

  SyncDeviceInfoMap sync_device_info = MakeSyncDeviceInfo({}, {}, "local");
  sync_device_info["foreign-invalid"] =
      std::make_pair(syncer::DeviceInfo::OsType::kAndroid,
                     syncer::DeviceInfo::FormFactor::kTablet);

  backend_->SetSyncDeviceInfo(std::move(sync_device_info));
  backend_->SetLocalDeviceOriginatorCacheGuid("local");

  VisitRow foreign_visit;
  foreign_visit.visit_time = base::Time::Now();
  foreign_visit.transition = ui::PageTransitionFromInt(
      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_CHAIN_START |
      ui::PAGE_TRANSITION_CHAIN_END);
  foreign_visit.originator_cache_guid = "foreign-invalid";
  foreign_visit.is_known_to_sync = true;
  foreign_visit.consider_for_ntp_most_visited = true;

  VisitID added_id = backend_->AddSyncedVisit(
      GURL("https://some.url"), u"Title", /*hidden=*/false, foreign_visit,
      std::nullopt, std::nullopt);

  ASSERT_NE(added_id, kInvalidVisitID);

  VisitRow added_visit;

  ASSERT_TRUE(backend_->GetVisitByID(added_id, &added_visit));
  EXPECT_TRUE(added_visit.consider_for_ntp_most_visited);

  // The visit does not belong to a segment because its originator_cache_guid
  // isn't known.
  EXPECT_EQ(added_visit.segment_id, 0);
}

TEST_F(
    HistoryBackendTest,
    AddSyncedVisit_DoesNotAddVisitToSegmentsWithInvalidLocalDeviceInformation) {
  backend_->SetCanAddForeignVisitsToSegments(true);

  SyncDeviceInfoMap sync_device_info = MakeSyncDeviceInfo({"foreign"}, {});
  sync_device_info["local-invalid"] =
      std::make_pair(syncer::DeviceInfo::OsType::kIOS,
                     syncer::DeviceInfo::FormFactor::kTablet);

  backend_->SetSyncDeviceInfo(std::move(sync_device_info));
  backend_->SetLocalDeviceOriginatorCacheGuid("local-invalid");

  VisitRow foreign_visit;
  foreign_visit.visit_time = base::Time::Now();
  foreign_visit.transition = ui::PageTransitionFromInt(
      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_CHAIN_START |
      ui::PAGE_TRANSITION_CHAIN_END);
  foreign_visit.originator_cache_guid = "foreign";
  foreign_visit.is_known_to_sync = true;
  foreign_visit.consider_for_ntp_most_visited = true;

  VisitID added_id = backend_->AddSyncedVisit(
      GURL("https://some.url"), u"Title", /*hidden=*/false, foreign_visit,
      std::nullopt, std::nullopt);

  ASSERT_NE(added_id, kInvalidVisitID);

  VisitRow added_visit;

  ASSERT_TRUE(backend_->GetVisitByID(added_id, &added_visit));
  EXPECT_TRUE(added_visit.consider_for_ntp_most_visited);

  // The foreign visit does not belong to a segment because the local device
  // information is invalid.
  EXPECT_EQ(added_visit.segment_id, 0);
}

TEST_F(HistoryBackendTest,
       AddSyncedVisit_DoesNotAddVisitToSegmentsWithInvalidDeviceInformation) {
  backend_->SetCanAddForeignVisitsToSegments(true);

  SyncDeviceInfoMap sync_device_info = MakeSyncDeviceInfo({}, {});
  sync_device_info["foreign-invalid"] =
      std::make_pair(syncer::DeviceInfo::OsType::kAndroid,
                     syncer::DeviceInfo::FormFactor::kTablet);
  sync_device_info["local-invalid"] =
      std::make_pair(syncer::DeviceInfo::OsType::kIOS,
                     syncer::DeviceInfo::FormFactor::kTablet);

  backend_->SetSyncDeviceInfo(std::move(sync_device_info));
  backend_->SetLocalDeviceOriginatorCacheGuid("local-invalid");

  VisitRow foreign_visit;
  foreign_visit.visit_time = base::Time::Now();
  foreign_visit.transition = ui::PageTransitionFromInt(
      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_CHAIN_START |
      ui::PAGE_TRANSITION_CHAIN_END);
  foreign_visit.originator_cache_guid = "foreign-invalid";
  foreign_visit.is_known_to_sync = true;
  foreign_visit.consider_for_ntp_most_visited = true;

  VisitID added_id = backend_->AddSyncedVisit(
      GURL("https://some.url"), u"Title", /*hidden=*/false, foreign_visit,
      std::nullopt, std::nullopt);

  ASSERT_NE(added_id, kInvalidVisitID);

  VisitRow added_visit;

  ASSERT_TRUE(backend_->GetVisitByID(added_id, &added_visit));
  EXPECT_TRUE(added_visit.consider_for_ntp_most_visited);

  // The visit does not belong to a segment.
  EXPECT_EQ(added_visit.segment_id, 0);
}
#endif

TEST_F(HistoryBackendTest, DeleteAllForeignVisitsDoesNotDeleteLocalVisits) {}

TEST_F(HistoryBackendTest, DeleteAllForeignVisitsWorksInBatches) {}

// Regression test for crbug.com/354474887.
TEST_F(HistoryBackendTest, DeleteAllForeignVisitsPendingAtShutdown) {}

TEST_F(HistoryBackendTest, DeleteAllForeignVisitsDoesNotDeleteFutureVisits) {}

TEST_F(HistoryBackendTest, DeleteAllForeignVisitsAlsoDeletesChains) {}

TEST_F(HistoryBackendTest, DeleteAllForeignVisitsResetsIsKnownToSyncFlag) {}

TEST_F(HistoryBackendTest, InternalAndExternalReferrer) {}

TEST_F(HistoryBackendTest, QueryURLs) {}

TEST_F(HistoryBackendTest, GetMostRecentVisitForEachURL) {}

// We want to test with the VisitedLinkDatabase enabled and disabled.
enum TestMode {};

class HistoryBackendTestForVisitedLinks
    : public HistoryBackendTest,
      public ::testing::WithParamInterface<TestMode> {};

INSTANTIATE_TEST_SUITE_P();

TEST_P(HistoryBackendTestForVisitedLinks, AddPageAndSyncedVisit) {}

TEST_P(HistoryBackendTestForVisitedLinks, IncreaseVisitCount) {}

TEST_P(HistoryBackendTestForVisitedLinks, OnlyAddValidVisitedLinks) {}

TEST_P(HistoryBackendTestForVisitedLinks, AddWholeRedirectChain) {}

TEST_P(HistoryBackendTestForVisitedLinks, DecreaseVisitCount) {}

TEST_P(HistoryBackendTestForVisitedLinks, DeleteAllVisitedLinksHistory) {}

TEST_P(HistoryBackendTestForVisitedLinks, NotifyVisitedLinksAdded) {}

// A HistoryDBTask that runs for a specified number of iterations (returning
// false from Run() until the target number of iterations has been reached).
class RepeatingDBTask : public HistoryDBTask {};

// Regression test for crbug.com/352515665.
TEST_F(HistoryBackendTest, ProcessDBTaskWithMultipleIterations) {}

}  // namespace history