| Index: chrome/browser/profiles/profile_statistics_browsertest.cc
|
| diff --git a/chrome/browser/profiles/profile_statistics_browsertest.cc b/chrome/browser/profiles/profile_statistics_browsertest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0189643c6b681977798f84d94bb26d433ea82f0b
|
| --- /dev/null
|
| +++ b/chrome/browser/profiles/profile_statistics_browsertest.cc
|
| @@ -0,0 +1,288 @@
|
| +// Copyright (c) 2012 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 <stddef.h>
|
| +#include <stdint.h>
|
| +
|
| +#include <algorithm>
|
| +#include <set>
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/macros.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "build/build_config.h"
|
| +#include "chrome/browser/password_manager/password_store_factory.h"
|
| +#include "chrome/browser/profiles/profile_manager.h"
|
| +#include "chrome/browser/profiles/profile_statistics.h"
|
| +#include "chrome/browser/profiles/profile_statistics_aggregator.h"
|
| +#include "chrome/browser/profiles/profile_statistics_constants.h"
|
| +#include "chrome/browser/profiles/profile_statistics_types.h"
|
| +#include "chrome/browser/ui/browser.h"
|
| +#include "chrome/test/base/in_process_browser_test.h"
|
| +#include "components/password_manager/core/browser/password_manager_test_utils.h"
|
| +#include "components/password_manager/core/browser/test_password_store.h"
|
| +#include "content/public/test/test_utils.h"
|
| +
|
| +namespace {
|
| +
|
| +const std::set<std::string> stats_categories {
|
| + profiles::kProfileStatisticsBrowsingHistory,
|
| + profiles::kProfileStatisticsPasswords,
|
| + profiles::kProfileStatisticsBookmarks,
|
| + profiles::kProfileStatisticsSettings};
|
| +
|
| +const size_t num_of_stats_categories = stats_categories.size();
|
| +
|
| +bool IsProfileCategoryStatEqual(const profiles::ProfileCategoryStat& a,
|
| + const profiles::ProfileCategoryStat& b) {
|
| + return a.category == b.category && a.count == b.count &&
|
| + a.success == b.success;
|
| +}
|
| +
|
| +std::string ProfileCategoryStatToString(
|
| + const profiles::ProfileCategoryStat& a) {
|
| + return base::StringPrintf("category = %s, count = %d, success = %s",
|
| + a.category.c_str(), a.count, a.success ? "true" : "false");
|
| +}
|
| +
|
| +::testing::AssertionResult AssertionProfileCategoryStatEqual(
|
| + const char* actual_expression,
|
| + const char* expected_expression,
|
| + const profiles::ProfileCategoryStat& actual_value,
|
| + const profiles::ProfileCategoryStat& expected_value) {
|
| + if (IsProfileCategoryStatEqual(actual_value, expected_value)) {
|
| + return ::testing::AssertionSuccess();
|
| + } else {
|
| + return ::testing::AssertionFailure()
|
| + << "Value of: " << actual_expression
|
| + << "\n Actual: " << ProfileCategoryStatToString(actual_value)
|
| + << "\nExpected: " << expected_expression
|
| + << "\nWhich is: " << ProfileCategoryStatToString(expected_value);
|
| + }
|
| +}
|
| +
|
| +::testing::AssertionResult AssertionProfileCategoryStatsEqual(
|
| + const char* actual_expression,
|
| + const char* expected_expression,
|
| + const profiles::ProfileCategoryStats& actual_value,
|
| + const profiles::ProfileCategoryStats& expected_value) {
|
| + size_t actual_count = actual_value.size();
|
| + size_t expected_count = expected_value.size();
|
| + std::vector<bool> actual_unused(actual_count, true);
|
| + std::vector<bool> expected_unused(expected_count, true);
|
| +
|
| + bool match_failed = actual_count != expected_count;
|
| + for (size_t actual_pos = 0; actual_pos < actual_count; actual_pos++) {
|
| + for (size_t expected_pos = 0; expected_pos < expected_count;
|
| + expected_pos++) {
|
| + if (expected_unused[expected_pos] &&
|
| + IsProfileCategoryStatEqual(actual_value[actual_pos],
|
| + expected_value[expected_pos])) {
|
| + actual_unused[actual_pos] = false;
|
| + expected_unused[expected_pos] = false;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (actual_unused[actual_pos])
|
| + match_failed = true;
|
| + }
|
| +
|
| + if (!match_failed) {
|
| + return ::testing::AssertionSuccess();
|
| + } else {
|
| + ::testing::AssertionResult result = testing::AssertionFailure();
|
| + result << "ProfileCategoryStats are not equal.";
|
| +
|
| + result << "\n Actual: " << actual_expression;
|
| + bool actual_printed = false;
|
| + for (size_t i = 0; i < actual_count; i++) {
|
| + if (actual_unused[i]) {
|
| + result << "\n"
|
| + << (actual_printed ? " " : "Unmatched rows: ")
|
| + << ProfileCategoryStatToString(actual_value[i]);
|
| + actual_printed = true;
|
| + }
|
| + }
|
| + if (!actual_printed)
|
| + result << "(no unmatched rows found in actual value)";
|
| +
|
| +
|
| + result << "\nExpected: " << expected_expression;
|
| + bool expected_printed = false;
|
| + for (size_t i = 0; i < expected_count; i++) {
|
| + if (expected_unused[i]) {
|
| + result << "\n"
|
| + << (expected_printed ? " " : "Unmatched rows: ")
|
| + << ProfileCategoryStatToString(expected_value[i]);
|
| + expected_printed = true;
|
| + }
|
| + }
|
| + if (!expected_printed)
|
| + result << "(no unmatched rows found in expected value)";
|
| +
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +class ProfileStatisticsAggregatorState {
|
| + public:
|
| + explicit ProfileStatisticsAggregatorState(base::RunLoop* run_loop)
|
| + : run_loop_(run_loop) {}
|
| +
|
| + ProfileStatisticsAggregatorState(base::RunLoop* run_loop,
|
| + size_t required_stat_count)
|
| + : run_loop_(run_loop),
|
| + required_stat_count_(
|
| + std::min(num_of_stats_categories, required_stat_count)) {}
|
| +
|
| + void callback(profiles::ProfileCategoryStats stats_return) {
|
| + size_t newCount = stats_return.size();
|
| + // If newCount is 1, then a new GatherStatistics task has started. Discard
|
| + // the old statistics by setting oldCount to 0 in this case.
|
| + size_t oldCount = newCount == 1 ? 0 : stats_.size();
|
| +
|
| + EXPECT_LT(oldCount, newCount);
|
| + if (oldCount < newCount) {
|
| + for (size_t i = 0; i < oldCount; i++) {
|
| + // Exisiting statistics must be the same.
|
| + EXPECT_PRED_FORMAT2(AssertionProfileCategoryStatEqual,
|
| + stats_[i], stats_return[i]);
|
| + // The new statistic categories must be different.
|
| + for (size_t j = oldCount; j < newCount; j++)
|
| + EXPECT_NE(stats_[i].category, stats_return[j].category);
|
| + }
|
| +
|
| + for (size_t j = oldCount; j < newCount; j++) {
|
| + EXPECT_EQ(1u, stats_categories.count(stats_return[j].category));
|
| + // Count the number of statistics failures (incrementally).
|
| + if (!stats_return[j].success)
|
| + num_of_fails_++;
|
| + }
|
| + }
|
| + stats_ = stats_return;
|
| +
|
| + EXPECT_GE(num_of_stats_categories, newCount);
|
| + if (required_stat_count_ <= newCount)
|
| + run_loop_->Quit();
|
| + }
|
| +
|
| + profiles::ProfileCategoryStats GetStats() const { return stats_; }
|
| + int GetNumOfFails() const { return num_of_fails_; }
|
| + void SetRunLoopAndRequiredStatCount(base::RunLoop* run_loop,
|
| + size_t required_stat_count) {
|
| + run_loop_ = run_loop;
|
| + required_stat_count_ = std::min(num_of_stats_categories,
|
| + required_stat_count);
|
| + }
|
| +
|
| + private:
|
| + profiles::ProfileCategoryStats stats_;
|
| + int num_of_fails_ = 0;
|
| + base::RunLoop* run_loop_;
|
| + size_t required_stat_count_ = num_of_stats_categories;
|
| +};
|
| +
|
| +void WaitMilliseconds(int time_in_milliseconds) {
|
| + base::RunLoop run_loop;
|
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| + FROM_HERE, run_loop.QuitClosure(),
|
| + base::TimeDelta::FromMilliseconds(time_in_milliseconds));
|
| + run_loop.Run();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +class ProfileStatisticsBrowserTest : public InProcessBrowserTest {
|
| + public:
|
| + void SetUpOnMainThread() override {
|
| + // Use TestPasswordStore to remove a possible race. Normally the
|
| + // PasswordStore does its database manipulation on the DB thread, which
|
| + // creates a possible race during navigation. Specifically the
|
| + // PasswordManager will ignore any forms in a page if the load from the
|
| + // PasswordStore has not completed.
|
| + PasswordStoreFactory::GetInstance()->SetTestingFactory(
|
| + browser()->profile(),
|
| + password_manager::BuildPasswordStore<
|
| + content::BrowserContext, password_manager::TestPasswordStore>);
|
| + }
|
| +};
|
| +
|
| +using ProfileStatisticsBrowserDeathTest = ProfileStatisticsBrowserTest;
|
| +
|
| +IN_PROC_BROWSER_TEST_F(ProfileStatisticsBrowserTest, GatherStatistics) {
|
| + Profile* profile = ProfileManager::GetActiveUserProfile();
|
| + ASSERT_TRUE(profile);
|
| +
|
| + base::RunLoop run_loop;
|
| + ProfileStatisticsAggregatorState state(&run_loop);
|
| + EXPECT_EQ(false, ProfileStatistics::HasAggregator(profile));
|
| + ProfileStatistics::GatherProfileStatistics(
|
| + profile,
|
| + base::Bind(&ProfileStatisticsAggregatorState::callback,
|
| + base::Unretained(&state)));
|
| + EXPECT_EQ(true, ProfileStatistics::HasAggregator(profile));
|
| + EXPECT_EQ(1u, ProfileStatistics::GetAggregator(profile)->GetCallbackCount());
|
| + run_loop.Run();
|
| +
|
| + EXPECT_EQ(0, state.GetNumOfFails());
|
| +
|
| + profiles::ProfileCategoryStats stats = state.GetStats();
|
| + for (const auto& stat : stats) {
|
| + if (stat.category != profiles::kProfileStatisticsSettings)
|
| + EXPECT_EQ(0, stat.count);
|
| + }
|
| +
|
| + EXPECT_PRED_FORMAT2(AssertionProfileCategoryStatsEqual, stats,
|
| + ProfileStatistics::GetProfileStatisticsFromCache(profile->GetPath()));
|
| +}
|
| +
|
| +IN_PROC_BROWSER_TEST_F(ProfileStatisticsBrowserTest,
|
| + GatherStatisticsTwoCallbacks) {
|
| + Profile* profile = ProfileManager::GetActiveUserProfile();
|
| + ASSERT_TRUE(profile);
|
| +
|
| + base::RunLoop run_loop1a;
|
| + base::RunLoop run_loop1b;
|
| + base::RunLoop run_loop2;
|
| + ProfileStatisticsAggregatorState state1(&run_loop1a, 1u);
|
| + ProfileStatisticsAggregatorState state2(&run_loop2);
|
| +
|
| + EXPECT_EQ(false, ProfileStatistics::HasAggregator(profile));
|
| + ProfileStatistics::GatherProfileStatistics(
|
| + profile,
|
| + base::Bind(&ProfileStatisticsAggregatorState::callback,
|
| + base::Unretained(&state1)));
|
| + EXPECT_EQ(true, ProfileStatistics::HasAggregator(profile));
|
| + EXPECT_EQ(1u, ProfileStatistics::GetAggregator(profile)->GetCallbackCount());
|
| + run_loop1a.Run();
|
| +
|
| + state1.SetRunLoopAndRequiredStatCount(&run_loop1b, SIZE_MAX);
|
| +
|
| + ProfileStatistics::GatherProfileStatistics(
|
| + profile,
|
| + base::Bind(&ProfileStatisticsAggregatorState::callback,
|
| + base::Unretained(&state2)));
|
| + EXPECT_EQ(true, ProfileStatistics::HasAggregator(profile));
|
| + EXPECT_EQ(2u, ProfileStatistics::GetAggregator(profile)->GetCallbackCount());
|
| + run_loop1b.Run();
|
| + run_loop2.Run();
|
| +
|
| + EXPECT_EQ(0, state1.GetNumOfFails());
|
| +
|
| + EXPECT_PRED_FORMAT2(AssertionProfileCategoryStatsEqual,
|
| + state1.GetStats(), state2.GetStats());
|
| +}
|
| +
|
| +IN_PROC_BROWSER_TEST_F(ProfileStatisticsBrowserTest, CloseBrowser) {
|
| + Profile* profile = ProfileManager::GetActiveUserProfile();
|
| + ASSERT_TRUE(profile);
|
| +
|
| + EXPECT_EQ(false, ProfileStatistics::HasAggregator(profile));
|
| + CloseBrowserSynchronously(browser());
|
| + EXPECT_EQ(true, ProfileStatistics::HasAggregator(profile));
|
| + EXPECT_EQ(0u, ProfileStatistics::GetAggregator(profile)->GetCallbackCount());
|
| +}
|
|
|