blob: 495e6a0d6380cba36210b2c5ce111491f52ec5df [file] [log] [blame]
// 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