Commit 07168912 authored by Hesen Zhang's avatar Hesen Zhang Committed by Commit Bot

[Upboarding]: QueryTile tile group manager implementation.

- Setup the tile group manager component.
- Implemented the load, save and get APIs.
- Added unittests.

- [UPDATED] Have a big update since initial reviews.

Bug: 1068368
Change-Id: I03ebf98f9c67af403d355418cb1304f161bb28e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2138161
Auto-Submit: Hesen Zhang <hesen@chromium.org>
Commit-Queue: Hesen Zhang <hesen@chromium.org>
Reviewed-by: default avatarShakti Sahu <shaktisahu@chromium.org>
Reviewed-by: default avatarXing Liu <xingliu@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Cr-Commit-Position: refs/heads/master@{#761153}
parent de53ef75
...@@ -30,6 +30,8 @@ source_set("internal") { ...@@ -30,6 +30,8 @@ source_set("internal") {
"tile_group.h", "tile_group.h",
"tile_info_fetcher.cc", "tile_info_fetcher.cc",
"tile_info_fetcher.h", "tile_info_fetcher.h",
"tile_manager.cc",
"tile_manager.h",
"tile_service_impl.cc", "tile_service_impl.cc",
"tile_service_impl.h", "tile_service_impl.h",
] ]
...@@ -59,6 +61,7 @@ source_set("unit_tests") { ...@@ -59,6 +61,7 @@ source_set("unit_tests") {
"query_tile_store_unittest.cc", "query_tile_store_unittest.cc",
"tile_group_unittest.cc", "tile_group_unittest.cc",
"tile_info_fetcher_unittest.cc", "tile_info_fetcher_unittest.cc",
"tile_manager_unittest.cc",
] ]
deps = [ deps = [
......
...@@ -17,6 +17,13 @@ constexpr char kDefaultGetQueryTilePath[] = "/v1/querytiles"; ...@@ -17,6 +17,13 @@ constexpr char kDefaultGetQueryTilePath[] = "/v1/querytiles";
// Default state of QueryTile feature. // Default state of QueryTile feature.
constexpr bool kDefaultQueryTileState = false; constexpr bool kDefaultQueryTileState = false;
// Default expire duration.
constexpr base::TimeDelta kDefaultExpireDuration =
base::TimeDelta::FromHours(48);
// Default locale string.
constexpr char kDefaultLocale[] = "en-US";
const GURL BuildGetQueryTileURL(const GURL& base_url, const char* path) { const GURL BuildGetQueryTileURL(const GURL& base_url, const char* path) {
GURL::Replacements replacements; GURL::Replacements replacements;
replacements.SetPathStr(path); replacements.SetPathStr(path);
...@@ -38,7 +45,9 @@ QueryTilesConfig::QueryTilesConfig() ...@@ -38,7 +45,9 @@ QueryTilesConfig::QueryTilesConfig()
: is_enabled(kDefaultQueryTileState), : is_enabled(kDefaultQueryTileState),
base_url(GURL(kDefaultBaseURL)), base_url(GURL(kDefaultBaseURL)),
get_query_tile_url( get_query_tile_url(
BuildGetQueryTileURL(base_url, kDefaultGetQueryTilePath)) {} BuildGetQueryTileURL(base_url, kDefaultGetQueryTilePath)),
expire_duration(kDefaultExpireDuration),
locale(kDefaultLocale) {}
QueryTilesConfig::~QueryTilesConfig() = default; QueryTilesConfig::~QueryTilesConfig() = default;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include "base/time/time.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace upboarding { namespace upboarding {
...@@ -33,6 +34,12 @@ struct QueryTilesConfig { ...@@ -33,6 +34,12 @@ struct QueryTilesConfig {
// The URL for GetQueryTiles RPC. // The URL for GetQueryTiles RPC.
GURL get_query_tile_url; GURL get_query_tile_url;
// The maximum duration for holding current group's info and images.
base::TimeDelta expire_duration;
// Locale setting from operating system.
std::string locale;
}; };
} // namespace upboarding } // namespace upboarding
......
...@@ -16,4 +16,17 @@ enum class TileInfoRequestStatus { ...@@ -16,4 +16,17 @@ enum class TileInfoRequestStatus {
kMaxValue = kFailure, kMaxValue = kFailure,
}; };
enum class TileGroupStatus {
// No errors happen in tile group manager.
kSuccess = 0,
// Database and manager component is not fully initialized.
kUninitialized = 1,
// Db operations failed.
kFailureDbOperation = 2,
// The group status is invalid, reason could be expired or locale not match.
kInvalidGroup = 3,
// Max value.
kMaxValue = kInvalidGroup,
};
#endif // CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_QUERY_TILE_TYPES_H_ #endif // CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_QUERY_TILE_TYPES_H_
// Copyright 2020 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 <map>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/guid.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/upboarding/query_tiles/internal/tile_manager.h"
namespace upboarding {
namespace {
class TileManagerImpl : public TileManager {
public:
TileManagerImpl(std::unique_ptr<TileStore> store,
base::Clock* clock,
QueryTilesConfig* config)
: initialized_(false),
store_(std::move(store)),
clock_(clock),
config_(config) {}
TileManagerImpl(const TileManagerImpl& other) = delete;
TileManagerImpl& operator=(const TileManagerImpl& other) = delete;
private:
// TileManager implementation.
void Init(TileGroupStatusCallback callback) override {
store_->InitAndLoad(base::BindOnce(&TileManagerImpl::OnTileStoreInitialized,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback)));
}
void SaveTiles(std::vector<std::unique_ptr<QueryTileEntry>> top_level_tiles,
TileGroupStatusCallback callback) override {
if (!initialized_) {
std::move(callback).Run(TileGroupStatus::kUninitialized);
return;
}
auto group = std::make_unique<TileGroup>();
group->id = base::GenerateGUID();
group->last_updated_ts = clock_->Now();
group->locale = config_->locale;
group->tiles = std::move(top_level_tiles);
store_->Update(group->id, *group.get(),
base::BindOnce(&TileManagerImpl::OnGroupSaved,
weak_ptr_factory_.GetWeakPtr(),
std::move(group), std::move(callback)));
}
void GetTiles(std::vector<QueryTileEntry*>* tiles) override {
DCHECK(tiles);
if (!initialized_)
return;
tiles->clear();
if (tile_group_ && ValidateGroup(tile_group_.get())) {
for (const auto& tile : tile_group_->tiles)
tiles->emplace_back(tile.get());
}
}
void OnTileStoreInitialized(
TileGroupStatusCallback callback,
bool success,
std::map<std::string, std::unique_ptr<TileGroup>> loaded_group) {
if (!success) {
std::move(callback).Run(TileGroupStatus::kFailureDbOperation);
return;
}
initialized_ = true;
PruneInvalidGroup(std::move(callback), std::move(loaded_group));
}
// Filters out and deletes invalid groups from db and memory.
void PruneInvalidGroup(
TileGroupStatusCallback callback,
std::map<std::string, std::unique_ptr<TileGroup>> loaded_group) {
DCHECK_LE(loaded_group.size(), 1u);
TileGroupStatus status = TileGroupStatus::kSuccess;
std::vector<std::string> to_deprecated_in_db;
for (const auto& pair : loaded_group) {
auto group_id = pair.first;
auto* group = pair.second.get();
if (!ValidateGroup(group)) {
status = TileGroupStatus::kInvalidGroup;
to_deprecated_in_db.emplace_back(group_id);
}
}
for (const auto& key : to_deprecated_in_db) {
DeleteGroup(key);
loaded_group.erase(key);
}
// Moves the valid group into in memory holder.
if (!loaded_group.empty())
std::swap(tile_group_, loaded_group.begin()->second);
std::move(callback).Run(status);
}
// Returns true if the group is not expired and the locale matches OS setting.
bool ValidateGroup(const TileGroup* group) const {
return clock_->Now() - group->last_updated_ts < config_->expire_duration &&
group->locale == config_->locale;
}
void OnGroupSaved(std::unique_ptr<TileGroup> group,
TileGroupStatusCallback callback,
bool success) {
if (!success) {
std::move(callback).Run(TileGroupStatus::kFailureDbOperation);
return;
}
if (tile_group_)
DeleteGroup(tile_group_->id);
std::swap(tile_group_, group);
std::move(callback).Run(TileGroupStatus::kSuccess);
}
void DeleteGroup(const std::string& key) {
store_->Delete(key, base::BindOnce(&TileManagerImpl::OnGroupDeleted,
weak_ptr_factory_.GetWeakPtr()));
}
void OnGroupDeleted(bool success) {
// TODO(hesen): Record db operation metrics.
NOTIMPLEMENTED();
}
// Indicates if the db is fully initialized, rejects calls if not.
bool initialized_;
// Storage layer of query tiles.
std::unique_ptr<TileStore> store_;
// The tile group in-memory holder.
std::unique_ptr<TileGroup> tile_group_;
// Clock object.
base::Clock* clock_;
// QueryTileConfig object.
QueryTilesConfig* config_;
base::WeakPtrFactory<TileManagerImpl> weak_ptr_factory_{this};
};
} // namespace
TileManager::TileManager() = default;
std::unique_ptr<TileManager> TileManager::Create(
std::unique_ptr<TileStore> tile_store,
base::Clock* clock,
QueryTilesConfig* config) {
return std::make_unique<TileManagerImpl>(std::move(tile_store), clock,
config);
}
} // namespace upboarding
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_TILE_MANAGER_H_
#define CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_TILE_MANAGER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/time/clock.h"
#include "chrome/browser/upboarding/query_tiles/internal/config.h"
#include "chrome/browser/upboarding/query_tiles/internal/query_tile_types.h"
#include "chrome/browser/upboarding/query_tiles/internal/store.h"
#include "chrome/browser/upboarding/query_tiles/internal/tile_group.h"
#include "chrome/browser/upboarding/query_tiles/query_tile_entry.h"
namespace upboarding {
// Manages the in-memory tile group and coordinates with storage layer.
class TileManager {
public:
using TileStore = Store<TileGroup>;
using TileGroupStatusCallback = base::OnceCallback<void(TileGroupStatus)>;
// Creates the instance.
static std::unique_ptr<TileManager> Create(
std::unique_ptr<TileStore> tile_store,
base::Clock* clock,
QueryTilesConfig* config);
// Initializes the query tile store, loading them into memory after
// validating.
virtual void Init(TileGroupStatusCallback callback) = 0;
// Returns tiles to the caller.
virtual void GetTiles(std::vector<QueryTileEntry*>* tiles) = 0;
// Save the query tiles into database.
virtual void SaveTiles(
std::vector<std::unique_ptr<QueryTileEntry>> top_level_tiles,
TileGroupStatusCallback callback) = 0;
TileManager();
virtual ~TileManager() = default;
TileManager(const TileManager& other) = delete;
TileManager& operator=(const TileManager& other) = delete;
};
} // namespace upboarding
#endif // CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_TILE_MANAGER_H_
// Copyright 2020 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 "chrome/browser/upboarding/query_tiles/internal/tile_manager.h"
#include <utility>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "chrome/browser/upboarding/query_tiles/internal/config.h"
#include "chrome/browser/upboarding/query_tiles/internal/query_tile_store.h"
#include "chrome/browser/upboarding/query_tiles/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using ::testing::Invoke;
namespace upboarding {
namespace {
class MockQueryTileStore : public Store<TileGroup> {
public:
MockQueryTileStore() = default;
MOCK_METHOD1(InitAndLoad, void(QueryTileStore::LoadCallback));
MOCK_METHOD3(Update,
void(const std::string&,
const TileGroup&,
QueryTileStore::UpdateCallback));
MOCK_METHOD2(Delete,
void(const std::string&, QueryTileStore::DeleteCallback));
};
class TileManagerTest : public testing::Test {
public:
TileManagerTest() = default;
~TileManagerTest() override = default;
TileManagerTest(const TileManagerTest& other) = delete;
TileManagerTest& operator=(const TileManagerTest& other) = delete;
void SetUp() override {
auto tile_store = std::make_unique<MockQueryTileStore>();
tile_store_ = tile_store.get();
config_.locale = "en-US";
config_.is_enabled = true;
base::Time fake_now;
EXPECT_TRUE(base::Time::FromString("03/18/20 01:00:00 AM", &fake_now));
clock_.SetNow(fake_now);
manager_ = TileManager::Create(std::move(tile_store), &clock_, &config_);
}
// Initial and load entries from store_, compare the |expected_status| to the
// actual returned status.
void Init(base::RepeatingClosure closure, TileGroupStatus expected_status) {
manager()->Init(base::BindOnce(&TileManagerTest::OnInitCompleted,
base::Unretained(this), std::move(closure),
expected_status));
}
void OnInitCompleted(base::RepeatingClosure closure,
TileGroupStatus expected_status,
TileGroupStatus status) {
EXPECT_EQ(status, expected_status);
std::move(closure).Run();
}
// Run SaveTiles call from manager_, compare the |expected_status| to the
// actual returned status.
void SaveTiles(std::vector<std::unique_ptr<QueryTileEntry>> tiles,
base::RepeatingClosure closure,
TileGroupStatus expected_status) {
manager()->SaveTiles(
std::move(tiles),
base::BindOnce(&TileManagerTest::OnTilesSaved, base::Unretained(this),
std::move(closure), expected_status));
}
void OnTilesSaved(base::RepeatingClosure closure,
TileGroupStatus expected_status,
TileGroupStatus status) {
EXPECT_EQ(status, expected_status);
std::move(closure).Run();
}
// Run GetTiles call from manager_, compare the |expected| to the actual
// returned tiles.
void GetTiles(std::vector<QueryTileEntry*> expected) {
std::vector<QueryTileEntry*> actual;
manager()->GetTiles(&actual);
EXPECT_TRUE(test::AreTilesIdentical(expected, actual));
}
protected:
TileManager* manager() { return manager_.get(); }
MockQueryTileStore* tile_store() { return tile_store_; }
const QueryTilesConfig& config() const { return config_; }
const base::SimpleTestClock* clock() const { return &clock_; }
private:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<TileManager> manager_;
MockQueryTileStore* tile_store_;
QueryTilesConfig config_;
base::SimpleTestClock clock_;
};
TEST_F(TileManagerTest, InitAndLoadWithDbOperationFailed) {
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[](base::OnceCallback<void(bool, MockQueryTileStore::KeysAndEntries)>
callback) {
std::move(callback).Run(false,
MockQueryTileStore::KeysAndEntries());
}));
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kFailureDbOperation);
GetTiles(std::vector<QueryTileEntry*>() /*expect an empty result*/);
loop.Run();
}
TEST_F(TileManagerTest, InitWithEmptyDb) {
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[](base::OnceCallback<void(bool, MockQueryTileStore::KeysAndEntries)>
callback) {
std::move(callback).Run(true, MockQueryTileStore::KeysAndEntries());
}));
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kSuccess);
GetTiles(std::vector<QueryTileEntry*>() /*expect an empty result*/);
loop.Run();
}
TEST_F(TileManagerTest, InitAndLoadWithLocaleNotMatch) {
auto invalid_group = std::make_unique<TileGroup>();
test::ResetTestGroup(invalid_group.get());
invalid_group->locale = "jp";
MockQueryTileStore::KeysAndEntries input;
input[invalid_group->id] = std::move(invalid_group);
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[&input](base::OnceCallback<void(
bool, MockQueryTileStore::KeysAndEntries)> callback) {
std::move(callback).Run(true, std::move(input));
}));
EXPECT_CALL(*tile_store(), Delete(_, _));
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kInvalidGroup);
GetTiles(std::vector<QueryTileEntry*>() /*expect an empty result*/);
loop.Run();
}
TEST_F(TileManagerTest, InitAndLoadWithExpiredGroup) {
auto invalid_group = std::make_unique<TileGroup>();
test::ResetTestGroup(invalid_group.get());
invalid_group->last_updated_ts =
clock()->Now() - base::TimeDelta::FromDays(3);
MockQueryTileStore::KeysAndEntries input;
input[invalid_group->id] = std::move(invalid_group);
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[&input](base::OnceCallback<void(
bool, MockQueryTileStore::KeysAndEntries)> callback) {
std::move(callback).Run(true, std::move(input));
}));
EXPECT_CALL(*tile_store(), Delete(_, _));
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kInvalidGroup);
GetTiles(std::vector<QueryTileEntry*>() /*expect an empty result*/);
loop.Run();
}
TEST_F(TileManagerTest, InitAndLoadSuccess) {
auto input_group = std::make_unique<TileGroup>();
test::ResetTestGroup(input_group.get());
std::vector<QueryTileEntry*> expected;
input_group->last_updated_ts =
clock()->Now() - base::TimeDelta::FromMinutes(5);
for (const auto& tile : input_group->tiles)
expected.emplace_back(tile.get());
MockQueryTileStore::KeysAndEntries input;
input[input_group->id] = std::move(input_group);
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[&input](base::OnceCallback<void(
bool, MockQueryTileStore::KeysAndEntries)> callback) {
std::move(callback).Run(true, std::move(input));
}));
EXPECT_CALL(*tile_store(), Delete(_, _)).Times(0);
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kSuccess);
GetTiles(expected);
loop.Run();
}
// Failed to init an empty db, and save tiles call failed because of db is
// uninitialized. GetTiles should return empty result.
TEST_F(TileManagerTest, SaveTilesWhenUnintialized) {
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[](base::OnceCallback<void(bool, MockQueryTileStore::KeysAndEntries)>
callback) {
std::move(callback).Run(false,
MockQueryTileStore::KeysAndEntries());
}));
EXPECT_CALL(*tile_store(), Update(_, _, _)).Times(0);
EXPECT_CALL(*tile_store(), Delete(_, _)).Times(0);
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kFailureDbOperation);
auto tile_to_save = std::make_unique<QueryTileEntry>();
test::ResetTestEntry(tile_to_save.get());
std::vector<std::unique_ptr<QueryTileEntry>> tiles_to_save;
tiles_to_save.emplace_back(std::move(tile_to_save));
SaveTiles(std::move(tiles_to_save), loop.QuitClosure(),
TileGroupStatus::kUninitialized);
GetTiles(std::vector<QueryTileEntry*>() /*expect an empty result*/);
loop.Run();
}
// Init with empty db successfully, and save tiles failed because of db
// operation failed. GetTiles should return empty result.
TEST_F(TileManagerTest, SaveTilesFailed) {
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[](base::OnceCallback<void(bool, MockQueryTileStore::KeysAndEntries)>
callback) {
std::move(callback).Run(true, MockQueryTileStore::KeysAndEntries());
}));
EXPECT_CALL(*tile_store(), Update(_, _, _))
.WillOnce(Invoke([](const std::string& id, const TileGroup& group,
MockQueryTileStore::UpdateCallback callback) {
std::move(callback).Run(false);
}));
EXPECT_CALL(*tile_store(), Delete(_, _)).Times(0);
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kSuccess);
auto tile_to_save = std::make_unique<QueryTileEntry>();
test::ResetTestEntry(tile_to_save.get());
std::vector<std::unique_ptr<QueryTileEntry>> tiles_to_save;
tiles_to_save.emplace_back(std::move(tile_to_save));
SaveTiles(std::move(tiles_to_save), loop.QuitClosure(),
TileGroupStatus::kFailureDbOperation);
GetTiles(std::vector<QueryTileEntry*>() /*expect an empty result*/);
loop.Run();
}
// Init with empty db successfully, and save tiles successfully. GetTiles should
// return the recent saved tiles, and no Delete call is executed.
TEST_F(TileManagerTest, SaveTilesSuccess) {
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[](base::OnceCallback<void(bool, MockQueryTileStore::KeysAndEntries)>
callback) {
std::move(callback).Run(true, MockQueryTileStore::KeysAndEntries());
}));
EXPECT_CALL(*tile_store(), Update(_, _, _))
.WillOnce(Invoke([](const std::string& id, const TileGroup& group,
MockQueryTileStore::UpdateCallback callback) {
std::move(callback).Run(true);
}));
EXPECT_CALL(*tile_store(), Delete(_, _)).Times(0);
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kSuccess);
auto tile_to_save = std::make_unique<QueryTileEntry>();
auto expected_tile = std::make_unique<QueryTileEntry>();
test::ResetTestEntry(tile_to_save.get());
test::ResetTestEntry(expected_tile.get());
std::vector<std::unique_ptr<QueryTileEntry>> tiles_to_save;
tiles_to_save.emplace_back(std::move(tile_to_save));
std::vector<QueryTileEntry*> expected;
expected.emplace_back(expected_tile.get());
SaveTiles(std::move(tiles_to_save), loop.QuitClosure(),
TileGroupStatus::kSuccess);
GetTiles(std::move(expected));
loop.Run();
}
// Init with store successfully. The store originally has entries loaded into
// memory. Save new tiles successfully. GetTiles should return the recent saved
// tiles, and Delete() call is executed in store, also replace the old group in
// memory.
TEST_F(TileManagerTest, SaveTilesAndReplaceOldGroupSuccess) {
auto input_group = std::make_unique<TileGroup>();
test::ResetTestGroup(input_group.get());
input_group->last_updated_ts =
clock()->Now() - base::TimeDelta::FromMinutes(5);
MockQueryTileStore::KeysAndEntries input;
input[input_group->id] = std::move(input_group);
EXPECT_CALL(*tile_store(), InitAndLoad(_))
.WillOnce(Invoke(
[&input](base::OnceCallback<void(
bool, MockQueryTileStore::KeysAndEntries)> callback) {
std::move(callback).Run(true, std::move(input));
}));
EXPECT_CALL(*tile_store(), Update(_, _, _))
.WillOnce(Invoke([](const std::string& id, const TileGroup& group,
MockQueryTileStore::UpdateCallback callback) {
std::move(callback).Run(true);
}));
EXPECT_CALL(*tile_store(), Delete("group_guid", _));
base::RunLoop loop;
Init(loop.QuitClosure(), TileGroupStatus::kSuccess);
auto tile_to_save = std::make_unique<QueryTileEntry>();
test::ResetTestEntry(tile_to_save.get());
std::vector<std::unique_ptr<QueryTileEntry>> tiles_to_save;
tiles_to_save.emplace_back(std::move(tile_to_save));
auto expected_tile = std::make_unique<QueryTileEntry>();
test::ResetTestEntry(expected_tile.get());
std::vector<QueryTileEntry*> expected;
expected.emplace_back(std::move(expected_tile.get()));
SaveTiles(std::move(tiles_to_save), loop.QuitClosure(),
TileGroupStatus::kSuccess);
GetTiles(std::move(expected));
loop.Run();
}
} // namespace
} // namespace upboarding
...@@ -165,6 +165,26 @@ bool AreTilesIdentical(const QueryTileEntry& lhs, const QueryTileEntry& rhs) { ...@@ -165,6 +165,26 @@ bool AreTilesIdentical(const QueryTileEntry& lhs, const QueryTileEntry& rhs) {
return true; return true;
} }
bool AreTilesIdentical(std::vector<QueryTileEntry*> lhs,
std::vector<QueryTileEntry*> rhs) {
if (lhs.size() != rhs.size())
return false;
auto entry_comparator = [](QueryTileEntry* a, QueryTileEntry* b) {
return a->id < b->id;
};
std::sort(lhs.begin(), lhs.end(), entry_comparator);
std::sort(rhs.begin(), rhs.end(), entry_comparator);
for (size_t i = 0; i < lhs.size(); i++) {
if (*lhs[i] != *rhs[i])
return false;
}
return true;
}
} // namespace test } // namespace test
} // namespace upboarding } // namespace upboarding
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UPBOARDING_QUERY_TILES_TEST_TEST_UTILS_H_ #define CHROME_BROWSER_UPBOARDING_QUERY_TILES_TEST_TEST_UTILS_H_
#include <string> #include <string>
#include <vector>
#include "chrome/browser/upboarding/query_tiles/internal/tile_group.h" #include "chrome/browser/upboarding/query_tiles/internal/tile_group.h"
#include "chrome/browser/upboarding/query_tiles/query_tile_entry.h" #include "chrome/browser/upboarding/query_tiles/query_tile_entry.h"
...@@ -23,6 +24,8 @@ std::string DebugString(const TileGroup* group); ...@@ -23,6 +24,8 @@ std::string DebugString(const TileGroup* group);
// Build and reset the TileGroup for test usage. // Build and reset the TileGroup for test usage.
void ResetTestGroup(TileGroup* group); void ResetTestGroup(TileGroup* group);
// TODO(hesen): Have a better builder with parameters to specify the structure
// of tree.
// Build and reset the TileEntry for test usage. // Build and reset the TileEntry for test usage.
void ResetTestEntry(QueryTileEntry* entry); void ResetTestEntry(QueryTileEntry* entry);
...@@ -32,6 +35,10 @@ bool AreTileGroupsIdentical(const TileGroup& lhs, const TileGroup& rhs); ...@@ -32,6 +35,10 @@ bool AreTileGroupsIdentical(const TileGroup& lhs, const TileGroup& rhs);
// Returns true if all data in two QueryTileEntries are identical. // Returns true if all data in two QueryTileEntries are identical.
bool AreTilesIdentical(const QueryTileEntry& lhs, const QueryTileEntry& rhs); bool AreTilesIdentical(const QueryTileEntry& lhs, const QueryTileEntry& rhs);
// Returns true if all data in two lists of QueryTileEntry are identical.
bool AreTilesIdentical(std::vector<QueryTileEntry*> lhs,
std::vector<QueryTileEntry*> rhs);
} // namespace test } // namespace test
} // namespace upboarding } // namespace upboarding
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment