| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/sync_sessions/lost_navigations_recorder.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "components/sync/syncable/entry.h" |
| #include "components/sync/syncable/mutable_entry.h" |
| #include "components/sync/syncable/syncable_base_transaction.h" |
| #include "components/sync/syncable/syncable_read_transaction.h" |
| #include "components/sync/syncable/syncable_write_transaction.h" |
| #include "components/sync/test/engine/test_directory_setter_upper.h" |
| #include "components/sync/test/engine/test_id_factory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using syncer::syncable::Entry; |
| using syncer::syncable::Id; |
| using syncer::syncable::MutableEntry; |
| using syncer::syncable::WriteTransaction; |
| |
| namespace sync_sessions { |
| namespace { |
| |
| const char kTab1SyncTag[] = "tab-YWRkcjHvv74="; |
| const char kTab2SyncTag[] = "tab-2FyZDHvv74="; |
| |
| class LostNavigationsRecorderTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| dir_maker_.SetUp(); |
| _id = 1; |
| } |
| |
| void TearDown() override { dir_maker_.TearDown(); } |
| |
| syncer::syncable::Directory* directory() { return dir_maker_.directory(); } |
| |
| LostNavigationsRecorder* recorder() { return &recorder_; } |
| |
| void AddNavigation(sync_pb::SessionSpecifics* tab_base, |
| int id_override = -1) { |
| sync_pb::SessionTab* tab = tab_base->mutable_tab(); |
| sync_pb::TabNavigation* navigation = tab->add_navigation(); |
| navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED); |
| navigation->set_unique_id(id_override > 0 ? id_override : _id++); |
| } |
| |
| void BuildTabSpecifics(const std::string& tag, |
| int tab_id, |
| sync_pb::SessionSpecifics* tab_base, |
| int num_unique_navs = 1) { |
| tab_base->set_session_tag(tag); |
| tab_base->set_tab_node_id(0); |
| sync_pb::SessionTab* tab = tab_base->mutable_tab(); |
| tab->set_tab_id(tab_id); |
| tab->set_current_navigation_index(0); |
| for (int i = 0; i < num_unique_navs; ++i) { |
| AddNavigation(tab_base); |
| } |
| } |
| |
| void BuildWindowSpecifics(int window_id, |
| sync_pb::SessionSpecifics* window_base) { |
| sync_pb::SessionHeader* header = window_base->mutable_header(); |
| sync_pb::SessionWindow* window = header->add_window(); |
| window->set_window_id(window_id); |
| } |
| |
| const Id& CreateEntry() { |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::CREATE, |
| syncer::SESSIONS, wtrans.root_id(), |
| "entrynamutable_entry"); |
| EXPECT_TRUE(mutable_entry.good()); |
| return mutable_entry.GetId(); |
| } |
| |
| const syncer::SyncData CreateLocalData( |
| const sync_pb::SessionSpecifics& specifics, |
| const std::string& sync_tag) const { |
| sync_pb::EntitySpecifics entity; |
| entity.mutable_session()->CopyFrom(specifics); |
| return syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity); |
| } |
| |
| syncer::SyncChange MakeChange(const std::string& sync_tag, |
| const sync_pb::SessionSpecifics& specifics, |
| syncer::SyncChange::SyncChangeType type) const { |
| return syncer::SyncChange(FROM_HERE, type, |
| CreateLocalData(specifics, sync_tag)); |
| } |
| |
| void RecordChange(const Entry* entry, int num_unique_navs) { |
| sync_pb::EntitySpecifics specifics; |
| BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), |
| num_unique_navs); |
| RecordChange(entry, specifics); |
| } |
| |
| void RecordChange(const Entry* entry, sync_pb::EntitySpecifics specifics) { |
| syncer::SyncChange change = MakeChange(kTab1SyncTag, specifics.session(), |
| syncer::SyncChange::ACTION_UPDATE); |
| recorder()->OnLocalChange(entry, change); |
| } |
| |
| void TriggerReconcile(MutableEntry* mutable_entry, |
| bool trigger_by_syncing = true, |
| sync_pb::EntitySpecifics* specifics = nullptr) { |
| mutable_entry->PutSyncing(trigger_by_syncing); |
| mutable_entry->PutIsUnsynced(trigger_by_syncing); |
| if (specifics == nullptr) { |
| RecordChange(mutable_entry, 0); |
| } else { |
| RecordChange(mutable_entry, *specifics); |
| } |
| } |
| |
| private: |
| base::test::ScopedTaskEnvironment task_environment_; |
| int _id; |
| LostNavigationsRecorder recorder_; |
| syncer::TestDirectorySetterUpper dir_maker_; |
| }; |
| |
| TEST_F(LostNavigationsRecorderTest, MultipleNavsNoneLost) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| mutable_entry.PutIsUnsynced(true); |
| |
| RecordChange(&mutable_entry, 6); |
| |
| TriggerReconcile(&mutable_entry); |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 0, 1); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, MultipleNavsOneLost) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| mutable_entry.PutIsUnsynced(true); |
| |
| RecordChange(&mutable_entry, 1); |
| RecordChange(&mutable_entry, 1); |
| |
| TriggerReconcile(&mutable_entry); |
| |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 1, 1); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, MultipleNavsMultipleLost) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| mutable_entry.PutIsUnsynced(true); |
| |
| RecordChange(&mutable_entry, 5); |
| RecordChange(&mutable_entry, 1); |
| |
| TriggerReconcile(&mutable_entry); |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 5, 1); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, MultipleWritesWhileSyncing) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| |
| sync_pb::EntitySpecifics specifics; |
| BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); |
| mutable_entry.PutIsUnsynced(true); |
| RecordChange(&mutable_entry, specifics); |
| |
| mutable_entry.PutSyncing(true); |
| RecordChange(&mutable_entry, specifics); |
| |
| specifics.mutable_session()->mutable_tab()->clear_navigation(); |
| for (int i = 0; i < 5; i++) { |
| AddNavigation(specifics.mutable_session()); |
| RecordChange(&mutable_entry, specifics); |
| } |
| |
| TriggerReconcile(&mutable_entry, false); |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 0, 1); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, MultipleWritesMultipleEntriesNoneLost) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| const Id& id2 = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| MutableEntry mutable_entry2(&wtrans, syncer::syncable::GET_BY_ID, id2); |
| mutable_entry.PutIsUnsynced(true); |
| mutable_entry2.PutIsUnsynced(true); |
| |
| sync_pb::EntitySpecifics specifics; |
| sync_pb::EntitySpecifics specifics2; |
| BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); |
| BuildTabSpecifics(kTab2SyncTag, 2, specifics2.mutable_session(), 5); |
| RecordChange(&mutable_entry, specifics); |
| RecordChange(&mutable_entry2, specifics2); |
| |
| mutable_entry.PutSyncing(true); |
| mutable_entry2.PutSyncing(true); |
| RecordChange(&mutable_entry, specifics); |
| RecordChange(&mutable_entry2, specifics2); |
| |
| specifics.mutable_session()->mutable_tab()->clear_navigation(); |
| specifics2.mutable_session()->mutable_tab()->clear_navigation(); |
| for (int i = 0; i < 5; i++) { |
| AddNavigation(specifics.mutable_session()); |
| AddNavigation(specifics2.mutable_session()); |
| |
| RecordChange(&mutable_entry, specifics); |
| RecordChange(&mutable_entry2, specifics2); |
| } |
| |
| TriggerReconcile(&mutable_entry, false, &specifics); |
| TriggerReconcile(&mutable_entry2, false, &specifics2); |
| |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 0, 4); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, MultipleWritesMultipleEntriesMultipleLost) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| const Id& id2 = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| MutableEntry mutable_entry2(&wtrans, syncer::syncable::GET_BY_ID, id2); |
| mutable_entry.PutIsUnsynced(true); |
| mutable_entry2.PutIsUnsynced(true); |
| |
| sync_pb::EntitySpecifics specifics; |
| sync_pb::EntitySpecifics specifics2; |
| BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); |
| BuildTabSpecifics(kTab2SyncTag, 2, specifics2.mutable_session(), 5); |
| RecordChange(&mutable_entry, specifics); |
| RecordChange(&mutable_entry2, specifics2); |
| |
| mutable_entry.PutSyncing(true); |
| mutable_entry2.PutSyncing(true); |
| RecordChange(&mutable_entry, specifics); |
| RecordChange(&mutable_entry2, specifics2); |
| |
| specifics.mutable_session()->mutable_tab()->clear_navigation(); |
| specifics2.mutable_session()->mutable_tab()->clear_navigation(); |
| for (int i = 0; i < 5; i++) { |
| AddNavigation(specifics.mutable_session()); |
| AddNavigation(specifics2.mutable_session()); |
| |
| RecordChange(&mutable_entry, specifics); |
| RecordChange(&mutable_entry2, specifics2); |
| } |
| |
| specifics.mutable_session() |
| ->mutable_tab() |
| ->mutable_navigation() |
| ->DeleteSubrange(0, 2); |
| specifics2.mutable_session() |
| ->mutable_tab() |
| ->mutable_navigation() |
| ->DeleteSubrange(0, 2); |
| RecordChange(&mutable_entry, specifics); |
| RecordChange(&mutable_entry, specifics2); |
| |
| TriggerReconcile(&mutable_entry, false, &specifics); |
| TriggerReconcile(&mutable_entry2, false, &specifics2); |
| |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 2, 2); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, NoWritesWhileSyncingMultipleLost) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| |
| sync_pb::EntitySpecifics specifics; |
| BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); |
| syncer::SyncChange change = MakeChange(kTab1SyncTag, specifics.session(), |
| syncer::SyncChange::ACTION_UPDATE); |
| mutable_entry.PutIsUnsynced(true); |
| recorder()->OnLocalChange(&mutable_entry, change); |
| |
| specifics.mutable_session()->mutable_tab()->clear_navigation(); |
| AddNavigation(specifics.mutable_session()); |
| change = MakeChange(kTab1SyncTag, specifics.session(), |
| syncer::SyncChange::ACTION_UPDATE); |
| recorder()->OnLocalChange(&mutable_entry, change); |
| |
| mutable_entry.PutIsUnsynced(false); |
| recorder()->OnLocalChange(&mutable_entry, change); |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 5, 1); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, WindowChangeDoesNotTriggerReconcile) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| const Id& id2 = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| MutableEntry mutable_entry2(&wtrans, syncer::syncable::GET_BY_ID, id2); |
| mutable_entry.PutIsUnsynced(true); |
| |
| RecordChange(&mutable_entry, 1); |
| |
| sync_pb::EntitySpecifics specifics; |
| BuildWindowSpecifics(1, specifics.mutable_session()); |
| RecordChange(&mutable_entry2, specifics); |
| |
| EXPECT_EQ(0ul, |
| histogram_tester.GetAllSamples("Sync.LostNavigationCount").size()); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, Samutable_entryNavigationSetAcrossStates) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| |
| sync_pb::EntitySpecifics specifics; |
| BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); |
| syncer::SyncChange change = MakeChange(kTab1SyncTag, specifics.session(), |
| syncer::SyncChange::ACTION_UPDATE); |
| mutable_entry.PutIsUnsynced(true); |
| recorder()->OnLocalChange(&mutable_entry, change); |
| recorder()->OnLocalChange(&mutable_entry, change); |
| |
| mutable_entry.PutIsUnsynced(false); |
| recorder()->OnLocalChange(&mutable_entry, change); |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 0, 1); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, RevisitPreviousNavs) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| mutable_entry.PutIsUnsynced(true); |
| |
| sync_pb::EntitySpecifics specifics; |
| BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 3); |
| RecordChange(&mutable_entry, specifics); |
| |
| AddNavigation(specifics.mutable_session()); |
| RecordChange(&mutable_entry, specifics); |
| |
| specifics.mutable_session() |
| ->mutable_tab() |
| ->mutable_navigation() |
| ->RemoveLast(); |
| RecordChange(&mutable_entry, specifics); |
| |
| TriggerReconcile(&mutable_entry, true, &specifics); |
| |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 1, 1); |
| } |
| |
| TEST_F(LostNavigationsRecorderTest, MultipleNavsMultipleLostWithOverlap) { |
| base::HistogramTester histogram_tester; |
| const Id& id = CreateEntry(); |
| WriteTransaction wtrans(FROM_HERE, syncer::syncable::UNITTEST, directory()); |
| MutableEntry mutable_entry(&wtrans, syncer::syncable::GET_BY_ID, id); |
| mutable_entry.PutIsUnsynced(true); |
| |
| sync_pb::EntitySpecifics specifics; |
| BuildTabSpecifics(kTab1SyncTag, 1, specifics.mutable_session(), 5); |
| RecordChange(&mutable_entry, specifics); |
| |
| AddNavigation(specifics.mutable_session()); |
| AddNavigation(specifics.mutable_session()); |
| |
| specifics.mutable_session() |
| ->mutable_tab() |
| ->mutable_navigation() |
| ->DeleteSubrange(0, 2); |
| RecordChange(&mutable_entry, specifics); |
| |
| TriggerReconcile(&mutable_entry, true, &specifics); |
| |
| histogram_tester.ExpectBucketCount("Sync.LostNavigationCount", 2, 1); |
| } |
| |
| }; // namespace |
| }; // namespace sync_sessions |