Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / base / win / registry_unittest.cc
blob22576634f9a4aaa6e3906bea3130f98dd89924e8
1 // Copyright (c) 2011 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.
5 #include "base/win/registry.h"
7 #include <cstring>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "base/stl_util.h"
15 #include "base/win/windows_version.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 namespace base {
19 namespace win {
21 namespace {
23 class RegistryTest : public testing::Test {
24 protected:
25 #if defined(_WIN64)
26 static const REGSAM kNativeViewMask = KEY_WOW64_64KEY;
27 static const REGSAM kRedirectedViewMask = KEY_WOW64_32KEY;
28 #else
29 static const REGSAM kNativeViewMask = KEY_WOW64_32KEY;
30 static const REGSAM kRedirectedViewMask = KEY_WOW64_64KEY;
31 #endif // _WIN64
33 RegistryTest() {}
34 void SetUp() override {
35 // Create a temporary key.
36 RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
37 key.DeleteKey(kRootKey);
38 ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
39 ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ));
40 foo_software_key_ = L"Software\\";
41 foo_software_key_ += kRootKey;
42 foo_software_key_ += L"\\Foo";
45 void TearDown() override {
46 // Clean up the temporary key.
47 RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE);
48 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
49 ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
52 static bool IsRedirectorPresent() {
53 #if defined(_WIN64)
54 return true;
55 #else
56 return OSInfo::GetInstance()->wow64_status() == OSInfo::WOW64_ENABLED;
57 #endif
60 const wchar_t* const kRootKey = L"Base_Registry_Unittest";
61 std::wstring foo_software_key_;
63 private:
64 DISALLOW_COPY_AND_ASSIGN(RegistryTest);
67 // static
68 const REGSAM RegistryTest::kNativeViewMask;
69 const REGSAM RegistryTest::kRedirectedViewMask;
71 TEST_F(RegistryTest, ValueTest) {
72 RegKey key;
74 std::wstring foo_key(kRootKey);
75 foo_key += L"\\Foo";
76 ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
77 KEY_READ));
80 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(),
81 KEY_READ | KEY_SET_VALUE));
82 ASSERT_TRUE(key.Valid());
84 const wchar_t kStringValueName[] = L"StringValue";
85 const wchar_t kDWORDValueName[] = L"DWORDValue";
86 const wchar_t kInt64ValueName[] = L"Int64Value";
87 const wchar_t kStringData[] = L"string data";
88 const DWORD kDWORDData = 0xdeadbabe;
89 const int64 kInt64Data = 0xdeadbabedeadbabeLL;
91 // Test value creation
92 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kStringValueName, kStringData));
93 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kDWORDValueName, kDWORDData));
94 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kInt64ValueName, &kInt64Data,
95 sizeof(kInt64Data), REG_QWORD));
96 EXPECT_EQ(3U, key.GetValueCount());
97 EXPECT_TRUE(key.HasValue(kStringValueName));
98 EXPECT_TRUE(key.HasValue(kDWORDValueName));
99 EXPECT_TRUE(key.HasValue(kInt64ValueName));
101 // Test Read
102 std::wstring string_value;
103 DWORD dword_value = 0;
104 int64 int64_value = 0;
105 ASSERT_EQ(ERROR_SUCCESS, key.ReadValue(kStringValueName, &string_value));
106 ASSERT_EQ(ERROR_SUCCESS, key.ReadValueDW(kDWORDValueName, &dword_value));
107 ASSERT_EQ(ERROR_SUCCESS, key.ReadInt64(kInt64ValueName, &int64_value));
108 EXPECT_STREQ(kStringData, string_value.c_str());
109 EXPECT_EQ(kDWORDData, dword_value);
110 EXPECT_EQ(kInt64Data, int64_value);
112 // Make sure out args are not touched if ReadValue fails
113 const wchar_t* kNonExistent = L"NonExistent";
114 ASSERT_NE(ERROR_SUCCESS, key.ReadValue(kNonExistent, &string_value));
115 ASSERT_NE(ERROR_SUCCESS, key.ReadValueDW(kNonExistent, &dword_value));
116 ASSERT_NE(ERROR_SUCCESS, key.ReadInt64(kNonExistent, &int64_value));
117 EXPECT_STREQ(kStringData, string_value.c_str());
118 EXPECT_EQ(kDWORDData, dword_value);
119 EXPECT_EQ(kInt64Data, int64_value);
121 // Test delete
122 ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kStringValueName));
123 ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kDWORDValueName));
124 ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kInt64ValueName));
125 EXPECT_EQ(0U, key.GetValueCount());
126 EXPECT_FALSE(key.HasValue(kStringValueName));
127 EXPECT_FALSE(key.HasValue(kDWORDValueName));
128 EXPECT_FALSE(key.HasValue(kInt64ValueName));
132 TEST_F(RegistryTest, BigValueIteratorTest) {
133 RegKey key;
134 std::wstring foo_key(kRootKey);
135 foo_key += L"\\Foo";
136 ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
137 KEY_READ));
138 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(),
139 KEY_READ | KEY_SET_VALUE));
140 ASSERT_TRUE(key.Valid());
142 // Create a test value that is larger than MAX_PATH.
143 std::wstring data(MAX_PATH * 2, L'a');
145 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(data.c_str(), data.c_str()));
147 RegistryValueIterator iterator(HKEY_CURRENT_USER, foo_key.c_str());
148 ASSERT_TRUE(iterator.Valid());
149 EXPECT_STREQ(data.c_str(), iterator.Name());
150 EXPECT_STREQ(data.c_str(), iterator.Value());
151 // ValueSize() is in bytes, including NUL.
152 EXPECT_EQ((MAX_PATH * 2 + 1) * sizeof(wchar_t), iterator.ValueSize());
153 ++iterator;
154 EXPECT_FALSE(iterator.Valid());
157 TEST_F(RegistryTest, TruncatedCharTest) {
158 RegKey key;
159 std::wstring foo_key(kRootKey);
160 foo_key += L"\\Foo";
161 ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
162 KEY_READ));
163 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(),
164 KEY_READ | KEY_SET_VALUE));
165 ASSERT_TRUE(key.Valid());
167 const wchar_t kName[] = L"name";
168 // kData size is not a multiple of sizeof(wchar_t).
169 const uint8 kData[] = { 1, 2, 3, 4, 5 };
170 EXPECT_EQ(5, arraysize(kData));
171 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kName, kData,
172 arraysize(kData), REG_BINARY));
174 RegistryValueIterator iterator(HKEY_CURRENT_USER, foo_key.c_str());
175 ASSERT_TRUE(iterator.Valid());
176 EXPECT_STREQ(kName, iterator.Name());
177 // ValueSize() is in bytes.
178 ASSERT_EQ(arraysize(kData), iterator.ValueSize());
179 // Value() is NUL terminated.
180 int end = (iterator.ValueSize() + sizeof(wchar_t) - 1) / sizeof(wchar_t);
181 EXPECT_NE(L'\0', iterator.Value()[end-1]);
182 EXPECT_EQ(L'\0', iterator.Value()[end]);
183 EXPECT_EQ(0, std::memcmp(kData, iterator.Value(), arraysize(kData)));
184 ++iterator;
185 EXPECT_FALSE(iterator.Valid());
188 TEST_F(RegistryTest, RecursiveDelete) {
189 RegKey key;
190 // Create kRootKey->Foo
191 // \->Bar (TestValue)
192 // \->Foo (TestValue)
193 // \->Bar
194 // \->Foo
195 // \->Moo
196 // \->Foo
197 // and delete kRootKey->Foo
198 std::wstring foo_key(kRootKey);
199 foo_key += L"\\Foo";
200 ASSERT_EQ(ERROR_SUCCESS,
201 key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
202 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
203 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
204 ASSERT_EQ(ERROR_SUCCESS,
205 key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
206 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Moo", KEY_WRITE));
207 ASSERT_EQ(ERROR_SUCCESS,
208 key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
209 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
210 foo_key += L"\\Bar";
211 ASSERT_EQ(ERROR_SUCCESS,
212 key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
213 foo_key += L"\\Foo";
214 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
215 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
216 ASSERT_EQ(ERROR_SUCCESS,
217 key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
219 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
220 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Bar"));
221 ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo"));
222 ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar\\Foo"));
223 ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar"));
224 ASSERT_EQ(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Foo"));
226 ASSERT_EQ(ERROR_SUCCESS,
227 key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
228 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
229 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
230 ASSERT_EQ(ERROR_SUCCESS,
231 key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
232 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L""));
233 ASSERT_NE(ERROR_SUCCESS,
234 key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
236 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
237 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"Foo"));
238 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Foo"));
239 ASSERT_NE(ERROR_SUCCESS,
240 key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
243 // This test requires running as an Administrator as it tests redirected
244 // registry writes to HKLM\Software
245 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384253.aspx
246 // TODO(wfh): flaky test on Vista. See http://crbug.com/377917
247 TEST_F(RegistryTest, DISABLED_Wow64RedirectedFromNative) {
248 if (!IsRedirectorPresent())
249 return;
251 RegKey key;
253 // Test redirected key access from non-redirected.
254 ASSERT_EQ(ERROR_SUCCESS,
255 key.Create(HKEY_LOCAL_MACHINE,
256 foo_software_key_.c_str(),
257 KEY_WRITE | kRedirectedViewMask));
258 ASSERT_NE(ERROR_SUCCESS,
259 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
260 ASSERT_NE(ERROR_SUCCESS,
261 key.Open(HKEY_LOCAL_MACHINE,
262 foo_software_key_.c_str(),
263 KEY_READ | kNativeViewMask));
265 // Open the non-redirected view of the parent and try to delete the test key.
266 ASSERT_EQ(ERROR_SUCCESS,
267 key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE));
268 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
269 ASSERT_EQ(ERROR_SUCCESS,
270 key.Open(HKEY_LOCAL_MACHINE,
271 L"Software",
272 KEY_SET_VALUE | kNativeViewMask));
273 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
275 // Open the redirected view and delete the key created above.
276 ASSERT_EQ(ERROR_SUCCESS,
277 key.Open(HKEY_LOCAL_MACHINE,
278 L"Software",
279 KEY_SET_VALUE | kRedirectedViewMask));
280 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
283 // Test for the issue found in http://crbug.com/384587 where OpenKey would call
284 // Close() and reset wow64_access_ flag to 0 and cause a NOTREACHED to hit on a
285 // subsequent OpenKey call.
286 TEST_F(RegistryTest, SameWowFlags) {
287 RegKey key;
289 ASSERT_EQ(ERROR_SUCCESS,
290 key.Open(HKEY_LOCAL_MACHINE,
291 L"Software",
292 KEY_READ | KEY_WOW64_64KEY));
293 ASSERT_EQ(ERROR_SUCCESS,
294 key.OpenKey(L"Microsoft",
295 KEY_READ | KEY_WOW64_64KEY));
296 ASSERT_EQ(ERROR_SUCCESS,
297 key.OpenKey(L"Windows",
298 KEY_READ | KEY_WOW64_64KEY));
301 // TODO(wfh): flaky test on Vista. See http://crbug.com/377917
302 TEST_F(RegistryTest, DISABLED_Wow64NativeFromRedirected) {
303 if (!IsRedirectorPresent())
304 return;
305 RegKey key;
307 // Test non-redirected key access from redirected.
308 ASSERT_EQ(ERROR_SUCCESS,
309 key.Create(HKEY_LOCAL_MACHINE,
310 foo_software_key_.c_str(),
311 KEY_WRITE | kNativeViewMask));
312 ASSERT_EQ(ERROR_SUCCESS,
313 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
314 ASSERT_NE(ERROR_SUCCESS,
315 key.Open(HKEY_LOCAL_MACHINE,
316 foo_software_key_.c_str(),
317 KEY_READ | kRedirectedViewMask));
319 // Open the redirected view of the parent and try to delete the test key
320 // from the non-redirected view.
321 ASSERT_EQ(ERROR_SUCCESS,
322 key.Open(HKEY_LOCAL_MACHINE,
323 L"Software",
324 KEY_SET_VALUE | kRedirectedViewMask));
325 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
327 ASSERT_EQ(ERROR_SUCCESS,
328 key.Open(HKEY_LOCAL_MACHINE,
329 L"Software",
330 KEY_SET_VALUE | kNativeViewMask));
331 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
334 TEST_F(RegistryTest, OpenSubKey) {
335 RegKey key;
336 ASSERT_EQ(ERROR_SUCCESS,
337 key.Open(HKEY_CURRENT_USER,
338 kRootKey,
339 KEY_READ | KEY_CREATE_SUB_KEY));
341 ASSERT_NE(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
342 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"foo", KEY_READ));
343 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
344 ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
346 std::wstring foo_key(kRootKey);
347 foo_key += L"\\Foo";
348 ASSERT_EQ(ERROR_SUCCESS,
349 key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
351 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
352 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo"));
355 class TestChangeDelegate {
356 public:
357 TestChangeDelegate() : called_(false) {}
358 ~TestChangeDelegate() {}
360 void OnKeyChanged() {
361 MessageLoop::current()->QuitWhenIdle();
362 called_ = true;
365 bool WasCalled() {
366 bool was_called = called_;
367 called_ = false;
368 return was_called;
371 private:
372 bool called_;
375 TEST_F(RegistryTest, ChangeCallback) {
376 RegKey key;
377 TestChangeDelegate delegate;
378 MessageLoop message_loop;
380 std::wstring foo_key(kRootKey);
381 foo_key += L"\\Foo";
382 ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
383 KEY_READ));
385 ASSERT_TRUE(key.StartWatching(Bind(&TestChangeDelegate::OnKeyChanged,
386 Unretained(&delegate))));
387 EXPECT_FALSE(delegate.WasCalled());
389 // Make some change.
390 RegKey key2;
391 ASSERT_EQ(ERROR_SUCCESS, key2.Open(HKEY_CURRENT_USER, foo_key.c_str(),
392 KEY_READ | KEY_SET_VALUE));
393 ASSERT_TRUE(key2.Valid());
394 EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name", L"data"));
396 // Allow delivery of the notification.
397 EXPECT_FALSE(delegate.WasCalled());
398 base::RunLoop().Run();
400 ASSERT_TRUE(delegate.WasCalled());
401 EXPECT_FALSE(delegate.WasCalled());
403 ASSERT_TRUE(key.StartWatching(Bind(&TestChangeDelegate::OnKeyChanged,
404 Unretained(&delegate))));
406 // Change something else.
407 EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name2", L"data2"));
408 base::RunLoop().Run();
409 ASSERT_TRUE(delegate.WasCalled());
411 ASSERT_TRUE(key.StartWatching(Bind(&TestChangeDelegate::OnKeyChanged,
412 Unretained(&delegate))));
413 base::RunLoop().RunUntilIdle();
414 EXPECT_FALSE(delegate.WasCalled());
417 } // namespace
419 } // namespace win
420 } // namespace base