OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <set> | |
6 #include <vector> | |
7 | |
8 #include "base/bind.h" | |
9 #include "base/command_line.h" | |
10 #include "base/files/file_path.h" | |
11 #include "base/macros.h" | |
12 #include "base/message_loop/message_loop.h" | |
13 #include "base/metrics/histogram_base.h" | |
14 #include "base/metrics/histogram_samples.h" | |
15 #include "base/metrics/statistics_recorder.h" | |
16 #include "base/prefs/pref_service.h" | |
17 #include "base/strings/string16.h" | |
18 #include "base/threading/sequenced_worker_pool.h" | |
19 #include "base/values.h" | |
20 #include "build/build_config.h" | |
21 #include "chrome/browser/browser_process.h" | |
22 #include "chrome/browser/prefs/chrome_pref_service_factory.h" | |
23 #include "chrome/browser/prefs/pref_hash_store.h" | |
24 #include "chrome/browser/profiles/profile.h" | |
25 #include "chrome/browser/profiles/profile_info_cache.h" | |
26 #include "chrome/browser/profiles/profile_manager.h" | |
27 #include "chrome/browser/profiles/profiles_state.h" | |
28 #include "chrome/common/pref_names.h" | |
29 #include "chrome/test/base/in_process_browser_test.h" | |
30 #include "content/public/browser/browser_thread.h" | |
31 #include "content/public/common/content_switches.h" | |
32 #include "content/public/test/test_utils.h" | |
33 #include "testing/gtest/include/gtest/gtest.h" | |
34 | |
35 #if defined(OS_CHROMEOS) | |
36 #include "chromeos/chromeos_switches.h" | |
37 #endif | |
38 | |
39 namespace { | |
40 | |
41 // An observer that returns back to test code after a new profile is | |
42 // initialized. | |
43 void OnUnblockOnProfileCreation(const base::Closure& callback, | |
44 Profile* profile, | |
45 Profile::CreateStatus status) { | |
46 switch (status) { | |
47 case Profile::CREATE_STATUS_CREATED: | |
48 // Wait for CREATE_STATUS_INITIALIZED. | |
49 break; | |
50 case Profile::CREATE_STATUS_INITIALIZED: | |
51 callback.Run(); | |
52 break; | |
53 default: | |
54 ADD_FAILURE() << "Unexpected Profile::CreateStatus: " << status; | |
55 callback.Run(); | |
56 break; | |
57 } | |
58 } | |
59 | |
60 // Finds a profile path corresponding to a profile that has not been loaded yet. | |
61 base::FilePath GetUnloadedProfilePath() { | |
62 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
63 const ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); | |
64 const std::vector<Profile*> loaded_profiles = | |
65 profile_manager->GetLoadedProfiles(); | |
66 std::set<base::FilePath> profile_paths; | |
67 for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) | |
68 profile_paths.insert(cache.GetPathOfProfileAtIndex(i)); | |
69 for (size_t i = 0; i < loaded_profiles.size(); ++i) | |
70 EXPECT_EQ(1U, profile_paths.erase(loaded_profiles[i]->GetPath())); | |
71 if (profile_paths.size()) | |
72 return *profile_paths.begin(); | |
73 return base::FilePath(); | |
74 } | |
75 | |
76 // Returns the number of times |histogram_name| was reported so far; adding the | |
77 // results of the first 100 buckets (there are only ~14 reporting IDs as of this | |
78 // writting; varies depending on the platform). If |expect_zero| is true, this | |
79 // method will explicitly report IDs that are non-zero for ease of diagnosis. | |
80 int GetTrackedPrefHistogramCount(const char* histogram_name, bool expect_zero) { | |
81 const base::HistogramBase* histogram = | |
82 base::StatisticsRecorder::FindHistogram(histogram_name); | |
83 if (!histogram) | |
84 return 0; | |
85 | |
86 scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples()); | |
87 int sum = 0; | |
88 for (int i = 0; i < 100; ++i) { | |
89 int count_for_id = samples->GetCount(i); | |
90 sum += count_for_id; | |
91 | |
92 if (expect_zero) | |
93 EXPECT_EQ(0, count_for_id) << "Faulty reporting_id: " << i; | |
94 } | |
95 return sum; | |
96 } | |
97 | |
98 } // namespace | |
99 | |
100 class PrefHashBrowserTest : public InProcessBrowserTest, | |
101 public testing::WithParamInterface<std::string> { | |
102 public: | |
103 PrefHashBrowserTest() | |
104 : is_unloaded_profile_seeding_allowed_( | |
105 IsUnloadedProfileSeedingAllowed()) {} | |
106 | |
107 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { | |
108 InProcessBrowserTest::SetUpCommandLine(command_line); | |
109 command_line->AppendSwitchASCII( | |
110 switches::kForceFieldTrials, | |
111 std::string(chrome_prefs::internals::kSettingsEnforcementTrialName) + | |
112 "/" + GetParam() + "/"); | |
113 #if defined(OS_CHROMEOS) | |
114 command_line->AppendSwitch( | |
115 chromeos::switches::kIgnoreUserProfileMappingForTests); | |
116 #endif | |
117 } | |
118 | |
119 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { | |
120 // Force the delayed PrefHashStore update task to happen immediately with | |
121 // no domain check (bots are on a domain). | |
122 chrome_prefs::DisableDelaysAndDomainCheckForTesting(); | |
123 } | |
124 | |
125 virtual void SetUpOnMainThread() OVERRIDE { | |
126 // content::RunAllPendingInMessageLoop() is already called before | |
127 // SetUpOnMainThread() in in_process_browser_test.cc which guarantees that | |
128 // UpdateAllPrefHashStoresIfRequired() has already been called. | |
129 | |
130 // Now flush the blocking pool to force any pending JsonPrefStore async read | |
131 // requests. | |
132 content::BrowserThread::GetBlockingPool()->FlushForTesting(); | |
133 | |
134 // And finally run tasks on this message loop again to process the OnRead() | |
135 // callbacks resulting from the file reads above. | |
136 content::RunAllPendingInMessageLoop(); | |
137 } | |
138 | |
139 protected: | |
140 const bool is_unloaded_profile_seeding_allowed_; | |
141 | |
142 private: | |
143 bool IsUnloadedProfileSeedingAllowed() const { | |
144 #if defined(OFFICIAL_BUILD) | |
145 // SettingsEnforcement can't be forced via --force-fieldtrials in official | |
146 // builds. Explicitly return whether the default in | |
147 // chrome_pref_service_factory.cc allows unloaded profile seeding on this | |
148 // platform. | |
149 return true; | |
150 #endif // defined(OFFICIAL_BUILD) | |
151 return GetParam() == chrome_prefs::internals:: | |
152 kSettingsEnforcementGroupNoEnforcement || | |
153 GetParam() == chrome_prefs::internals:: | |
154 kSettingsEnforcementGroupEnforceOnload; | |
155 } | |
156 }; | |
157 | |
158 #if defined(OS_CHROMEOS) | |
159 // PrefHash service has been disabled on ChromeOS: crbug.com/343261 | |
160 #define MAYBE_PRE_PRE_InitializeUnloadedProfiles DISABLED_PRE_PRE_InitializeUnlo
adedProfiles | |
161 #define MAYBE_PRE_InitializeUnloadedProfiles DISABLED_PRE_InitializeUnloadedProf
iles | |
162 #define MAYBE_InitializeUnloadedProfiles DISABLED_InitializeUnloadedProfiles | |
163 #else | |
164 #define MAYBE_PRE_PRE_InitializeUnloadedProfiles PRE_PRE_InitializeUnloadedProfi
les | |
165 #define MAYBE_PRE_InitializeUnloadedProfiles PRE_InitializeUnloadedProfiles | |
166 #define MAYBE_InitializeUnloadedProfiles InitializeUnloadedProfiles | |
167 #endif | |
168 | |
169 IN_PROC_BROWSER_TEST_P(PrefHashBrowserTest, | |
170 MAYBE_PRE_PRE_InitializeUnloadedProfiles) { | |
171 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
172 | |
173 // Create an additional profile. | |
174 const base::FilePath new_path = | |
175 profile_manager->GenerateNextProfileDirectoryPath(); | |
176 const scoped_refptr<content::MessageLoopRunner> runner( | |
177 new content::MessageLoopRunner); | |
178 profile_manager->CreateProfileAsync( | |
179 new_path, | |
180 base::Bind(&OnUnblockOnProfileCreation, runner->QuitClosure()), | |
181 base::string16(), | |
182 base::string16(), | |
183 std::string()); | |
184 | |
185 // Spin to allow profile creation to take place, loop is terminated | |
186 // by OnUnblockOnProfileCreation when the profile is created. | |
187 runner->Run(); | |
188 | |
189 // No profile should have gone through the unloaded profile initialization in | |
190 // this phase as both profiles should have been loaded normally. | |
191 EXPECT_EQ( | |
192 0, GetTrackedPrefHistogramCount( | |
193 "Settings.TrackedPreferencesAlternateStoreVersionUpdatedFrom", | |
194 true)); | |
195 } | |
196 | |
197 IN_PROC_BROWSER_TEST_P(PrefHashBrowserTest, | |
198 MAYBE_PRE_InitializeUnloadedProfiles) { | |
199 // Creating the profile would have initialized its hash store. Also, we don't | |
200 // know whether the newly created or original profile will be launched (does | |
201 // creating a profile cause it to be the most recently used?). | |
202 | |
203 // So we will find the profile that isn't loaded, reset its hash store, and | |
204 // then verify in the _next_ launch that it is, indeed, restored despite not | |
205 // having been loaded. | |
206 | |
207 const base::DictionaryValue* hashes = | |
208 g_browser_process->local_state()->GetDictionary( | |
209 prefs::kProfilePreferenceHashes); | |
210 | |
211 // 4 is for hash_of_hashes, versions_dict, default profile, and new profile. | |
212 EXPECT_EQ(4U, hashes->size()); | |
213 | |
214 // One of the two profiles should not have been loaded. Reset its hash store. | |
215 const base::FilePath unloaded_profile_path = GetUnloadedProfilePath(); | |
216 chrome_prefs::ResetPrefHashStore(unloaded_profile_path); | |
217 | |
218 // One of the profile hash collections should be gone. | |
219 EXPECT_EQ(3U, hashes->size()); | |
220 | |
221 // No profile should have gone through the unloaded profile initialization in | |
222 // this phase as both profiles were already initialized at the beginning of | |
223 // this phase (resetting the unloaded profile's PrefHashStore should only | |
224 // force initialization in the next phase's startup). | |
225 EXPECT_EQ( | |
226 0, GetTrackedPrefHistogramCount( | |
227 "Settings.TrackedPreferencesAlternateStoreVersionUpdatedFrom", | |
228 true)); | |
229 } | |
230 | |
231 IN_PROC_BROWSER_TEST_P(PrefHashBrowserTest, | |
232 MAYBE_InitializeUnloadedProfiles) { | |
233 const base::DictionaryValue* hashes = | |
234 g_browser_process->local_state()->GetDictionary( | |
235 prefs::kProfilePreferenceHashes); | |
236 | |
237 // The deleted hash collection should be restored only if the current | |
238 // SettingsEnforcement group allows it. | |
239 if (is_unloaded_profile_seeding_allowed_) { | |
240 EXPECT_EQ(4U, hashes->size()); | |
241 | |
242 // Verify that the initialization truly did occur in this phase's startup; | |
243 // rather than in the previous phase's shutdown. | |
244 EXPECT_EQ( | |
245 1, GetTrackedPrefHistogramCount( | |
246 "Settings.TrackedPreferencesAlternateStoreVersionUpdatedFrom", | |
247 false)); | |
248 } else { | |
249 EXPECT_EQ(3U, hashes->size()); | |
250 | |
251 EXPECT_EQ( | |
252 0, GetTrackedPrefHistogramCount( | |
253 "Settings.TrackedPreferencesAlternateStoreVersionUpdatedFrom", | |
254 true)); | |
255 } | |
256 | |
257 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
258 | |
259 // Verify that only one profile was loaded. We assume that the unloaded | |
260 // profile is the same one that wasn't loaded in the last launch (i.e., it's | |
261 // the one whose hash store we reset, and the fact that it is now restored is | |
262 // evidence that we restored the hashes of an unloaded profile.). | |
263 ASSERT_EQ(1U, profile_manager->GetLoadedProfiles().size()); | |
264 | |
265 // Loading the first profile should only have produced unchanged reports. | |
266 EXPECT_EQ( | |
267 0, GetTrackedPrefHistogramCount( | |
268 "Settings.TrackedPreferenceChanged", true)); | |
269 EXPECT_EQ( | |
270 0, GetTrackedPrefHistogramCount( | |
271 "Settings.TrackedPreferenceCleared", true)); | |
272 EXPECT_EQ( | |
273 0, GetTrackedPrefHistogramCount( | |
274 "Settings.TrackedPreferenceInitialized", true)); | |
275 EXPECT_EQ( | |
276 0, GetTrackedPrefHistogramCount( | |
277 "Settings.TrackedPreferenceTrustedInitialized", true)); | |
278 EXPECT_EQ( | |
279 0, GetTrackedPrefHistogramCount( | |
280 "Settings.TrackedPreferenceMigrated", true)); | |
281 int initial_unchanged_count = | |
282 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceUnchanged", | |
283 false); | |
284 EXPECT_GT(initial_unchanged_count, 0); | |
285 | |
286 if (is_unloaded_profile_seeding_allowed_) { | |
287 // Explicitly load the unloaded profile. | |
288 profile_manager->GetProfile(GetUnloadedProfilePath()); | |
289 ASSERT_EQ(2U, profile_manager->GetLoadedProfiles().size()); | |
290 | |
291 // Loading the unloaded profile should only generate unchanged pings; and | |
292 // should have produced as many of them as loading the first profile. | |
293 EXPECT_EQ( | |
294 0, GetTrackedPrefHistogramCount( | |
295 "Settings.TrackedPreferenceChanged", true)); | |
296 EXPECT_EQ( | |
297 0, GetTrackedPrefHistogramCount( | |
298 "Settings.TrackedPreferenceCleared", true)); | |
299 EXPECT_EQ( | |
300 0, GetTrackedPrefHistogramCount( | |
301 "Settings.TrackedPreferenceInitialized", true)); | |
302 EXPECT_EQ( | |
303 0, GetTrackedPrefHistogramCount( | |
304 "Settings.TrackedPreferenceTrustedInitialized", true)); | |
305 EXPECT_EQ( | |
306 0, GetTrackedPrefHistogramCount( | |
307 "Settings.TrackedPreferenceMigrated", true)); | |
308 EXPECT_EQ( | |
309 initial_unchanged_count * 2, | |
310 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceUnchanged", | |
311 false)); | |
312 } | |
313 } | |
314 | |
315 INSTANTIATE_TEST_CASE_P( | |
316 PrefHashBrowserTestInstance, | |
317 PrefHashBrowserTest, | |
318 testing::Values( | |
319 chrome_prefs::internals::kSettingsEnforcementGroupNoEnforcement, | |
320 chrome_prefs::internals::kSettingsEnforcementGroupEnforceOnload, | |
321 chrome_prefs::internals::kSettingsEnforcementGroupEnforceAlways, | |
322 chrome_prefs::internals:: | |
323 kSettingsEnforcementGroupEnforceAlwaysWithExtensions, | |
324 chrome_prefs::internals:: | |
325 kSettingsEnforcementGroupEnforceAlwaysWithExtensionsAndDSE)); | |
OLD | NEW |