chromium/components/sync/engine/data_type_worker_unittest.cc

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

#include "components/sync/engine/data_type_worker.h"

#include <stdint.h>

#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "base/uuid.h"
#include "components/sync/base/client_tag_hash.h"
#include "components/sync/base/features.h"
#include "components/sync/base/unique_position.h"
#include "components/sync/engine/cancelation_signal.h"
#include "components/sync/engine/commit_contribution.h"
#include "components/sync/engine/cycle/entity_change_metric_recording.h"
#include "components/sync/engine/cycle/status_controller.h"
#include "components/sync/protocol/autofill_specifics.pb.h"
#include "components/sync/protocol/data_type_state.pb.h"
#include "components/sync/protocol/entity_specifics.pb.h"
#include "components/sync/protocol/password_sharing_invitation_specifics.pb.h"
#include "components/sync/protocol/password_specifics.pb.h"
#include "components/sync/protocol/sync.pb.h"
#include "components/sync/protocol/sync_entity.pb.h"
#include "components/sync/protocol/webauthn_credential_specifics.pb.h"
#include "components/sync/test/fake_cryptographer.h"
#include "components/sync/test/mock_data_type_processor.h"
#include "components/sync/test/mock_invalidation.h"
#include "components/sync/test/mock_invalidation_tracker.h"
#include "components/sync/test/mock_nudge_handler.h"
#include "components/sync/test/single_type_mock_server.h"
#include "components/sync/test/trackable_mock_invalidation.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

Time;
DataTypeState;
EntitySpecifics;
SyncEntity;
ElementsAre;
IsNull;
NotNull;
SizeIs;
UnorderedElementsAre;

namespace syncer {

namespace {

const char kEncryptionKeyNamePrefix[] =;
const char kTag1[] =;
const char kTag2[] =;
const char kTag3[] =;
const char kValue1[] =;
const char kValue2[] =;
const char kValue3[] =;

EntitySpecifics GenerateSpecifics(const std::string& tag,
                                  const std::string& value) {}

std::string GetNthKeyName(int n) {}

sync_pb::EntitySpecifics EncryptPasswordSpecificsWithNthKey(
    int n,
    const sync_pb::PasswordSpecificsData& unencrypted_password) {}

sync_pb::CrossUserSharingPublicKey PublicKeyToProto(
    const CrossUserSharingPublicPrivateKeyPair& key_pair,
    uint32_t version) {}

sync_pb::IncomingPasswordSharingInvitationSpecifics
CreateIncomingPasswordSharingInvitation(const std::string& invitation_guid,
                                        const std::string& signon_realm,
                                        const std::string& username_value,
                                        const std::string& password_value,
                                        const std::string& sender_name,
                                        uint32_t recipient_key_version,
                                        FakeCryptographer* cryptographer) {}

ClientTagHash GeneratePreferenceTagHash(const std::string& tag) {}

MATCHER_P(HasPreferenceClientTag,
          expected_tag,
          base::StringPrintf(
              "expected_tag: %s, hash: %s",
              expected_tag,
              GeneratePreferenceTagHash(expected_tag).value().c_str())) {}

}  // namespace

// Tests the DataTypeWorker.
//
// This class passes messages between the model thread and sync server.
// As such, its code is subject to lots of different race conditions. This
// test harness lets us exhaustively test all possible races. We try to
// focus on just a few interesting cases.
//
// Inputs:
// - Initial data type state from the model thread.
// - Commit requests from the model thread.
// - Update responses from the server.
// - Commit responses from the server.
// - The cryptographer, if encryption is enabled.
//
// Outputs:
// - Commit requests to the server.
// - Commit responses to the model thread.
// - Update responses to the model thread.
// - Nudges to the sync scheduler.
//
// We use the MockDataTypeProcessor to stub out all communication
// with the model thread. That interface is synchronous, which makes it
// much easier to test races.
//
// The interface with the server is built around "pulling" data from this
// class, so we don't have to mock out any of it. We wrap it with some
// convenience functions so we can emulate server behavior.
class DataTypeWorkerTest : public ::testing::Test {};

// Requests a commit and verifies the messages sent to the client and server as
// a result.
//
// This test performs sanity checks on most of the fields in these messages.
// For the most part this is checking that the test code behaves as expected
// and the |worker_| doesn't mess up its simple task of moving around these
// values. It makes sense to have one or two tests that are this thorough, but
// we shouldn't be this verbose in all tests.
TEST_F(DataTypeWorkerTest, SimpleCommit) {}

TEST_F(DataTypeWorkerTest, SimpleDelete) {}

// Verifies the sending of an "initial sync done" signal.
TEST_F(DataTypeWorkerTest, SendInitialSyncDone) {}

// Commit two new entities in two separate commit messages.
TEST_F(DataTypeWorkerTest, TwoNewItemsCommittedSeparately) {}

// Test normal update receipt code path.
TEST_F(DataTypeWorkerTest, ReceiveUpdates) {}

TEST_F(DataTypeWorkerTest,
       ReceiveUpdates_ShouldNotPopulateUpdatedTypesOnTombstone) {}

TEST_F(DataTypeWorkerTest, ReceiveUpdates_NoDuplicateHash) {}

TEST_F(DataTypeWorkerTest, ReceiveUpdates_DuplicateHashWithinPartialUpdate) {}

TEST_F(DataTypeWorkerTest, ReceiveUpdates_DuplicateHashAcrossPartialUpdates) {}

TEST_F(DataTypeWorkerTest,
       ReceiveUpdates_EmptyHashNotConsideredDuplicateIfForDistinctServerIds) {}

TEST_F(DataTypeWorkerTest, ReceiveUpdates_MultipleDuplicateHashes) {}

// Covers the scenario where updates have the same client tag hash but
// different server IDs. This scenario is considered a bug on the server.
TEST_F(DataTypeWorkerTest,
       ReceiveUpdates_DuplicateClientTagHashesForDistinctServerIds) {}

// Covers the scenario where updates have the same GUID as originator client
// item ID but different server IDs. This scenario is considered a bug on the
// server.
TEST_F(DataTypeWorkerTest,
       ReceiveUpdates_DuplicateOriginatorClientIdForDistinctServerIds) {}

// Covers the scenario where two updates have the same originator client item ID
// but different originator cache GUIDs. This is only possible for legacy
// bookmarks created before 2015.
TEST_F(
    DataTypeWorkerTest,
    ReceiveUpdates_DuplicateOriginatorClientIdForDistinctOriginatorCacheGuids) {}

// Test that an update download coming in multiple parts gets accumulated into
// one call to the processor.
TEST_F(DataTypeWorkerTest, ReceiveMultiPartUpdates) {}

// Test that updates with no entities behave correctly.
TEST_F(DataTypeWorkerTest, EmptyUpdates) {}

// Test commit of encrypted updates.
TEST_F(DataTypeWorkerTest, EncryptedCommit) {}

// Test commit of encrypted tombstone.
TEST_F(DataTypeWorkerTest, EncryptedDelete) {}

// Test that updates are not delivered to the processor when encryption is
// required but unavailable.
TEST_F(DataTypeWorkerTest, EncryptionBlocksUpdates) {}

// Test that local changes are not committed when encryption is required but
// unavailable.
TEST_F(DataTypeWorkerTest, EncryptionBlocksCommits) {}

// Test the receipt of decryptable entities.
TEST_F(DataTypeWorkerTest, ReceiveDecryptableEntities) {}

// Test the receipt of decryptable entities, and that the worker will keep the
// entities until the decryption key arrives.
TEST_F(DataTypeWorkerTest,
       ReceiveDecryptableEntitiesShouldWaitTillKeyArrives) {}

// Test initializing a CommitQueue with a cryptographer at startup.
TEST_F(DataTypeWorkerTest, InitializeWithCryptographer) {}

// Test initialzing with a cryptographer that is not ready.
TEST_F(DataTypeWorkerTest, InitializeWithPendingCryptographer) {}

// Test initializing with a cryptographer on first startup.
TEST_F(DataTypeWorkerTest, FirstInitializeWithCryptographer) {}

TEST_F(DataTypeWorkerTest, CryptographerDuringInitialization) {}

// Receive updates that are initially undecryptable, then ensure they get
// delivered to the model thread upon ApplyUpdates() after decryption becomes
// possible.
TEST_F(DataTypeWorkerTest, ReceiveUndecryptableEntries) {}

TEST_F(DataTypeWorkerTest, OverwriteUndecryptableUpdateWithDecryptableOne) {}

// Verify that corrupted encrypted updates don't cause crashes.
TEST_F(DataTypeWorkerTest, ReceiveCorruptEncryption) {}

// See crbug.com/1178418 for more context.
TEST_F(DataTypeWorkerTest, DecryptUpdateIfPossibleDespiteEncryptionDisabled) {}

TEST_F(DataTypeWorkerTest, IgnoreUpdatesEncryptedWithKeysMissingForTooLong) {}

// Test that processor has been disconnected from Sync when worker got
// disconnected.
TEST_F(DataTypeWorkerTest, DisconnectProcessorFromSyncTest) {}

// Test that deleted entity can be recreated again.
TEST_F(DataTypeWorkerTest, RecreateDeletedEntity) {}

TEST_F(DataTypeWorkerTest, CommitOnly) {}

TEST_F(DataTypeWorkerTest, ShouldPropagateCommitFailure) {}

TEST_F(DataTypeWorkerTest, ShouldKeepGcDirectiveDuringSyncCycle) {}

TEST_F(DataTypeWorkerTest, ShouldCleanUpPendingUpdatesOnGcDirective) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     NonBookmarkNorWalletSucceeds) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest, BookmarkTombstone) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     BookmarkWithUniquePositionInSyncEntity) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     BookmarkWithPositionInParent) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     BookmarkWithInsertAfterItemId) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     BookmarkWithMissingPositionFallsBackToRandom) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest, BookmarkWithGUID) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest, BookmarkWithMissingGUID) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     BookmarkWithMissingGUIDAndInvalidOCII) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     WalletDataWithMissingClientTagHash) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     OfferDataWithMissingClientTagHash) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     WebAuthnCredentialWithLegacyClientTagHash) {}

TEST(DataTypeWorkerPopulateUpdateResponseDataTest,
     WebAuthnCredentialWithLegacyClientTagHashForDeletion) {}

class GetLocalChangesRequestTest : public testing::Test {};

GetLocalChangesRequestTest::GetLocalChangesRequestTest()
    :{}

GetLocalChangesRequestTest::~GetLocalChangesRequestTest() = default;

void GetLocalChangesRequestTest::SetUp() {}

void GetLocalChangesRequestTest::TearDown() {}

scoped_refptr<GetLocalChangesRequest>
GetLocalChangesRequestTest::MakeRequest() {}

void GetLocalChangesRequestTest::BlockingWaitForResponseOrCancelation(
    scoped_refptr<GetLocalChangesRequest> request,
    CancelationSignal* cancelation_signal) {}

void GetLocalChangesRequestTest::ScheduleBlockingWait(
    scoped_refptr<GetLocalChangesRequest> request,
    CancelationSignal* cancelation_signal) {}

// Tests that request doesn't block when cancelation signal is already signaled.
TEST_F(GetLocalChangesRequestTest, CancelationSignaledBeforeRequest) {}

// Tests that signaling cancelation signal while request is blocked unblocks it.
TEST_F(GetLocalChangesRequestTest, CancelationSignaledAfterRequest) {}

// Tests that setting response unblocks request.
TEST_F(GetLocalChangesRequestTest, SuccessfulRequest) {}

// Analogous test fixture but uses PASSWORDS instead of PREFERENCES, in order
// to test some special encryption requirements for PASSWORDS.
class DataTypeWorkerPasswordsTest : public DataTypeWorkerTest {};

// Similar to EncryptedCommit but tests PASSWORDS specifically, which use a
// different encryption mechanism.
TEST_F(DataTypeWorkerPasswordsTest, PasswordCommit) {}

// Same as above but uses custom passphrase. In this case, field
// |unencrypted_metadata| should be cleared.
TEST_F(DataTypeWorkerPasswordsTest, PasswordCommitWithCustomPassphrase) {}

// Similar to ReceiveDecryptableEntities but for PASSWORDS, which have a custom
// encryption mechanism.
TEST_F(DataTypeWorkerPasswordsTest, ReceiveDecryptablePasswordEntities) {}

// Similar to ReceiveDecryptableEntities but for PASSWORDS, which have a custom
// encryption mechanism.
TEST_F(DataTypeWorkerPasswordsTest,
       ReceiveDecryptablePasswordShouldWaitTillKeyArrives) {}

// Analogous to ReceiveUndecryptableEntries but for PASSWORDS, which have a
// custom encryption mechanism.
TEST_F(DataTypeWorkerPasswordsTest, ReceiveUndecryptablePasswordEntries) {}

// Similar to ReceiveDecryptableEntities but for PASSWORDS, which have a custom
// encryption mechanism.
TEST_F(DataTypeWorkerPasswordsTest, ReceiveCorruptedPasswordEntities) {}

// Analogous test fixture but uses BOOKMARKS instead of PREFERENCES, in order
// to test some special encryption requirements for BOOKMARKS.
class DataTypeWorkerBookmarksTest : public DataTypeWorkerTest {};

TEST_F(DataTypeWorkerBookmarksTest, CanDecryptUpdateWithMissingBookmarkGUID) {}

TEST_F(DataTypeWorkerBookmarksTest,
       CanDecryptUpdateWithMissingBookmarkGUIDAndInvalidOCII) {}

TEST_F(DataTypeWorkerBookmarksTest,
       CannotDecryptUpdateWithMissingBookmarkGUID) {}

TEST_F(DataTypeWorkerBookmarksTest,
       CannotDecryptUpdateWithMissingBookmarkGUIDAndInvalidOCII) {}

TEST_F(DataTypeWorkerTest, ShouldNotHaveLocalChangesOnSuccessfulLastCommit) {}

TEST_F(DataTypeWorkerTest, ShouldHaveLocalChangesOnCommitFailure) {}

TEST_F(DataTypeWorkerTest, ShouldHaveLocalChangesOnSuccessfulNotLastCommit) {}

TEST_F(DataTypeWorkerTest, ShouldHaveLocalChangesWhenNudgedWhileInFlight) {}

TEST_F(DataTypeWorkerTest, ShouldHaveLocalChangesWhenContributedMaxEntities) {}

TEST_F(DataTypeWorkerPasswordsTest,
       ShouldIgnoreTheEncryptedNotesBackupWhenNotesInPasswordSpecificsData) {}

TEST_F(DataTypeWorkerPasswordsTest,
       ShouldUseTheEncryptedNotesBackupWhenMissingInPasswordSpecificsData) {}

TEST_F(DataTypeWorkerPasswordsTest, ShouldEmitUnsetWhenNoNotesInUpdate) {}

TEST_F(DataTypeWorkerPasswordsTest, ShouldEmitNotesBackupCorrupted) {}

TEST_F(DataTypeWorkerPasswordsTest, ShouldPopulatePasswordNotesBackup) {}

TEST_F(DataTypeWorkerPasswordsTest,
       ShouldPopulatePasswordNotesBackupWhenNoLocalNotes) {}

// Verifies persisting invalidations load from the DataTypeProcessor.
TEST_F(DataTypeWorkerTest, LoadInvalidations) {}

// Verifies StorePendingInvalidations() calls for every incoming invalidation.
TEST_F(DataTypeWorkerTest, StoreInvalidationsCallCount) {}

// Verifies the management of invalidation hints and GU trigger fields.
TEST_F(DataTypeWorkerTest, HintCoalescing) {}

// Verifies the management of pending invalidations and DataTypeState.
TEST_F(DataTypeWorkerTest, DataTypeStateAfterApplyUpdates) {}

// Test the dropping of invalidation hints.  Receives invalidations one by one.
// Pending invalidation vector buffer size is 10.
TEST_F(DataTypeWorkerTest, DropHintsLocally_OneAtATime) {}

// Tests the receipt of 'unknown version' invalidations.
TEST_F(DataTypeWorkerTest, DropHintsAtServer_Alone) {}

// Tests the receipt of 'unknown version' invalidations.  This test also
// includes a known version invalidation to mix things up a bit.
TEST_F(DataTypeWorkerTest, DropHintsAtServer_WithOtherInvalidations) {}

TEST_F(DataTypeWorkerTest, ShouldEncryptOutgoingPasswordSharingInvitation) {}

class DataTypeWorkerIncomingPasswordSharingInvitationTest
    : public DataTypeWorkerTest {};

TEST_F(DataTypeWorkerIncomingPasswordSharingInvitationTest,
       ShouldDecryptIncomingPasswordSharingInvitation) {}

TEST_F(DataTypeWorkerIncomingPasswordSharingInvitationTest,
       ShouldIgnoreCorruptedInvitation) {}

class DataTypeWorkerAckTrackingTest : public DataTypeWorkerTest {};

// Test the acknowledgement of a single invalidation.
TEST_F(DataTypeWorkerAckTrackingTest, SimpleAcknowledgement) {}

// Test the acknowledgement of many invalidations.
TEST_F(DataTypeWorkerAckTrackingTest, ManyAcknowledgements) {}

// Test dropping when the buffer overflows and subsequent drop recovery.
TEST_F(DataTypeWorkerAckTrackingTest, OverflowAndRecover) {}

// Test receipt of an unknown version invalidation from the server.
TEST_F(DataTypeWorkerAckTrackingTest, UnknownVersionFromServer_Simple) {}

// Test receipt of multiple unknown version invalidations from the server.
TEST_F(DataTypeWorkerAckTrackingTest, UnknownVersionFromServer_Complex) {}

TEST_F(DataTypeWorkerAckTrackingTest, AckInvalidationsAddedDuringSyncCycle) {}

// Test invalidations that are used in several proto messages.
TEST_F(DataTypeWorkerAckTrackingTest, MultipleGetUpdates) {}

// Analogous test fixture to DataTypeWorkerTest but uses HISTORY instead of
// PREFERENCES, in order to test special ApplyUpdatesImmediatelyTypes()
// behavior.
class DataTypeWorkerHistoryTest : public DataTypeWorkerTest {};

TEST_F(DataTypeWorkerHistoryTest, AppliesPartialUpdateImmediately) {}

TEST_F(DataTypeWorkerHistoryTest, KeepsInitialSyncMarkedAsDone) {}

// Analogous test fixture to DataTypeWorkerTest but uses SHARED_TAB_GROUP_DATA
// instead of PREFERENCES, in order to test special shared types behavior.
class DataTypeWorkerSharedTabGroupDataTest : public DataTypeWorkerTest {};

TEST_F(DataTypeWorkerSharedTabGroupDataTest,
       ShouldClearUpdatesForInactiveCollaborationsDuringSyncCycle) {}

}  // namespace syncer