xref: /aosp_15_r20/external/cronet/base/win/registry_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2011 The Chromium Authors
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 "base/win/registry.h"
6 
7 #include <windows.h>
8 
9 #include <shlobj.h>
10 #include <stdint.h>
11 
12 #include <cstring>
13 #include <iterator>
14 #include <string_view>
15 #include <utility>
16 
17 #include "base/compiler_specific.h"
18 #include "base/functional/bind.h"
19 #include "base/functional/callback.h"
20 #include "base/location.h"
21 #include "base/memory/scoped_refptr.h"
22 #include "base/run_loop.h"
23 #include "base/strings/strcat.h"
24 #include "base/test/bind.h"
25 #include "base/test/gmock_expected_support.h"
26 #include "base/test/mock_callback.h"
27 #include "base/test/task_environment.h"
28 #include "base/test/test_mock_time_task_runner.h"
29 #include "base/test/test_reg_util_win.h"
30 #include "base/threading/simple_thread.h"
31 #include "base/win/win_util.h"
32 #include "base/win/windows_version.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 
36 namespace base::win {
37 
38 namespace {
39 
40 constexpr wchar_t kRootKey[] = L"Base_Registry_Unittest";
41 
42 // A test harness for registry tests that operate in HKCU. Each test is given
43 // a valid key distinct from that used by other tests.
44 class RegistryTest : public testing::Test {
45  protected:
RegistryTest()46   RegistryTest() : root_key_(std::wstring(L"Software\\") + kRootKey) {}
47 
SetUp()48   void SetUp() override {
49     ASSERT_NO_FATAL_FAILURE(registry_override_.OverrideRegistry(
50         HKEY_CURRENT_USER, &override_path_));
51 
52     // Create the test's root key.
53     RegKey key(HKEY_CURRENT_USER, L"", KEY_CREATE_SUB_KEY);
54     ASSERT_NE(ERROR_SUCCESS,
55               key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
56     ASSERT_EQ(ERROR_SUCCESS,
57               key.Create(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
58   }
59 
60   // Returns the path to a key under HKCU that is made available for exclusive
61   // use by a test.
root_key() const62   const std::wstring& root_key() const { return root_key_; }
63 
override_path() const64   const std::wstring& override_path() const { return override_path_; }
65 
66  private:
67   registry_util::RegistryOverrideManager registry_override_;
68   const std::wstring root_key_;
69   std::wstring override_path_;
70 };
71 
72 }  // namespace
73 
TEST_F(RegistryTest,ValueTest)74 TEST_F(RegistryTest, ValueTest) {
75   RegKey key;
76 
77   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
78                                     KEY_READ | KEY_SET_VALUE));
79   ASSERT_TRUE(key.Valid());
80 
81   const wchar_t kStringValueName[] = L"StringValue";
82   const wchar_t kDWORDValueName[] = L"DWORDValue";
83   const wchar_t kInt64ValueName[] = L"Int64Value";
84   const wchar_t kStringData[] = L"string data";
85   const DWORD kDWORDData = 0xdeadbabe;
86   const int64_t kInt64Data = 0xdeadbabedeadbabeLL;
87 
88   // Test value creation
89   ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kStringValueName, kStringData));
90   ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kDWORDValueName, kDWORDData));
91   ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kInt64ValueName, &kInt64Data,
92                                           sizeof(kInt64Data), REG_QWORD));
93   EXPECT_THAT(key.GetValueCount(), base::test::ValueIs(3U));
94   EXPECT_TRUE(key.HasValue(kStringValueName));
95   EXPECT_TRUE(key.HasValue(kDWORDValueName));
96   EXPECT_TRUE(key.HasValue(kInt64ValueName));
97 
98   // Test Read
99   std::wstring string_value;
100   DWORD dword_value = 0;
101   int64_t int64_value = 0;
102   ASSERT_EQ(ERROR_SUCCESS, key.ReadValue(kStringValueName, &string_value));
103   ASSERT_EQ(ERROR_SUCCESS, key.ReadValueDW(kDWORDValueName, &dword_value));
104   ASSERT_EQ(ERROR_SUCCESS, key.ReadInt64(kInt64ValueName, &int64_value));
105   EXPECT_EQ(kStringData, string_value);
106   EXPECT_EQ(kDWORDData, dword_value);
107   EXPECT_EQ(kInt64Data, int64_value);
108 
109   // Make sure out args are not touched if ReadValue fails
110   const wchar_t* kNonExistent = L"NonExistent";
111   ASSERT_NE(ERROR_SUCCESS, key.ReadValue(kNonExistent, &string_value));
112   ASSERT_NE(ERROR_SUCCESS, key.ReadValueDW(kNonExistent, &dword_value));
113   ASSERT_NE(ERROR_SUCCESS, key.ReadInt64(kNonExistent, &int64_value));
114   EXPECT_EQ(kStringData, string_value);
115   EXPECT_EQ(kDWORDData, dword_value);
116   EXPECT_EQ(kInt64Data, int64_value);
117 
118   // Test delete
119   ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kStringValueName));
120   ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kDWORDValueName));
121   ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kInt64ValueName));
122   EXPECT_THAT(key.GetValueCount(), base::test::ValueIs(0U));
123   EXPECT_FALSE(key.HasValue(kStringValueName));
124   EXPECT_FALSE(key.HasValue(kDWORDValueName));
125   EXPECT_FALSE(key.HasValue(kInt64ValueName));
126 }
127 
TEST_F(RegistryTest,BigValueIteratorTest)128 TEST_F(RegistryTest, BigValueIteratorTest) {
129   RegKey key;
130   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
131                                     KEY_READ | KEY_SET_VALUE));
132   ASSERT_TRUE(key.Valid());
133 
134   // Create a test value that is larger than MAX_PATH.
135   std::wstring data(MAX_PATH * 2, 'a');
136 
137   ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(data.c_str(), data.c_str()));
138 
139   RegistryValueIterator iterator(HKEY_CURRENT_USER, root_key().c_str());
140   ASSERT_TRUE(iterator.Valid());
141   EXPECT_EQ(data, iterator.Name());
142   EXPECT_EQ(data, iterator.Value());
143   // ValueSize() is in bytes, including NUL.
144   EXPECT_EQ((MAX_PATH * 2 + 1) * sizeof(wchar_t), iterator.ValueSize());
145   ++iterator;
146   EXPECT_FALSE(iterator.Valid());
147 }
148 
TEST_F(RegistryTest,TruncatedCharTest)149 TEST_F(RegistryTest, TruncatedCharTest) {
150   RegKey key;
151   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
152                                     KEY_READ | KEY_SET_VALUE));
153   ASSERT_TRUE(key.Valid());
154 
155   const wchar_t kName[] = L"name";
156   // kData size is not a multiple of sizeof(wchar_t).
157   const uint8_t kData[] = {1, 2, 3, 4, 5};
158   EXPECT_EQ(5u, std::size(kData));
159   ASSERT_EQ(ERROR_SUCCESS,
160             key.WriteValue(kName, kData, std::size(kData), REG_BINARY));
161 
162   RegistryValueIterator iterator(HKEY_CURRENT_USER, root_key().c_str());
163   ASSERT_TRUE(iterator.Valid());
164   // Avoid having to use EXPECT_STREQ here by leveraging std::string_view's
165   // operator== to perform a deep comparison.
166   EXPECT_EQ(std::wstring_view(kName), std::wstring_view(iterator.Name()));
167   // ValueSize() is in bytes.
168   ASSERT_EQ(std::size(kData), iterator.ValueSize());
169   // Value() is NUL terminated.
170   int end = (iterator.ValueSize() + sizeof(wchar_t) - 1) / sizeof(wchar_t);
171   EXPECT_NE('\0', iterator.Value()[end - 1]);
172   EXPECT_EQ('\0', iterator.Value()[end]);
173   EXPECT_EQ(0, std::memcmp(kData, iterator.Value(), std::size(kData)));
174   ++iterator;
175   EXPECT_FALSE(iterator.Valid());
176 }
177 
178 // Tests that the value iterator is okay with an empty key.
TEST_F(RegistryTest,ValueIteratorEmptyKey)179 TEST_F(RegistryTest, ValueIteratorEmptyKey) {
180   RegistryValueIterator iterator(HKEY_CURRENT_USER, root_key().c_str());
181   EXPECT_EQ(iterator.ValueCount(), 0U);
182   EXPECT_FALSE(iterator.Valid());
183 }
184 
185 // Tests that the default value is seen by a value iterator.
TEST_F(RegistryTest,ValueIteratorDefaultValue)186 TEST_F(RegistryTest, ValueIteratorDefaultValue) {
187   const std::wstring_view kTestString(L"i miss you");
188   ASSERT_EQ(RegKey(HKEY_CURRENT_USER, root_key().c_str(), KEY_SET_VALUE)
189                 .WriteValue(nullptr, kTestString.data()),
190             ERROR_SUCCESS);
191   RegistryValueIterator iterator(HKEY_CURRENT_USER, root_key().c_str());
192   EXPECT_EQ(iterator.ValueCount(), 1U);
193   ASSERT_TRUE(iterator.Valid());
194   EXPECT_EQ(std::wstring_view(iterator.Name()), std::wstring_view());
195   EXPECT_EQ(iterator.ValueSize(), (kTestString.size() + 1) * sizeof(wchar_t));
196   EXPECT_EQ(iterator.Type(), REG_SZ);
197   EXPECT_EQ(std::wstring_view(iterator.Value()), kTestString);
198   ++iterator;
199   EXPECT_FALSE(iterator.Valid());
200 }
201 
TEST_F(RegistryTest,NonRecursiveDelete)202 TEST_F(RegistryTest, NonRecursiveDelete) {
203   RegKey key;
204   // Create root_key()
205   //                  \->Bar (TestValue)
206   //                     \->Foo (TestValue)
207   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
208                                     KEY_CREATE_SUB_KEY));
209   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
210   ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
211   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
212   ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
213   key.Close();
214 
215   const std::wstring bar_path = root_key() + L"\\Bar";
216   // Non-recursive delete of Bar from root_key() should fail.
217   ASSERT_EQ(ERROR_SUCCESS,
218             key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_QUERY_VALUE));
219   ASSERT_NE(ERROR_SUCCESS,
220             key.DeleteKey(L"Bar", RegKey::RecursiveDelete(false)));
221   key.Close();
222   ASSERT_TRUE(
223       RegKey(HKEY_CURRENT_USER, bar_path.c_str(), KEY_QUERY_VALUE).Valid());
224 
225   // Non-recursive delete of Bar from itself should fail.
226   ASSERT_EQ(ERROR_SUCCESS,
227             key.Open(HKEY_CURRENT_USER, bar_path.c_str(), KEY_QUERY_VALUE));
228   ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"", RegKey::RecursiveDelete(false)));
229   key.Close();
230   ASSERT_TRUE(
231       RegKey(HKEY_CURRENT_USER, root_key().c_str(), KEY_QUERY_VALUE).Valid());
232 
233   // Non-recursive delete of the subkey and then root_key() should succeed.
234   ASSERT_EQ(ERROR_SUCCESS,
235             key.Open(HKEY_CURRENT_USER, bar_path.c_str(), KEY_QUERY_VALUE));
236   ASSERT_EQ(ERROR_SUCCESS,
237             key.DeleteKey(L"Foo", RegKey::RecursiveDelete(false)));
238   ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"", RegKey::RecursiveDelete(false)));
239   key.Close();
240   ASSERT_FALSE(
241       RegKey(HKEY_CURRENT_USER, bar_path.c_str(), KEY_QUERY_VALUE).Valid());
242 }
243 
TEST_F(RegistryTest,RecursiveDelete)244 TEST_F(RegistryTest, RecursiveDelete) {
245   RegKey key;
246   // Create root_key()
247   //                  \->Bar (TestValue)
248   //                     \->Foo (TestValue)
249   //                        \->Bar
250   //                           \->Foo
251   //                  \->Moo
252   //                  \->Foo
253   // and delete root_key()
254   std::wstring key_path = root_key();
255   ASSERT_EQ(ERROR_SUCCESS,
256             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
257   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
258   ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
259   ASSERT_EQ(ERROR_SUCCESS,
260             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
261   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Moo", KEY_WRITE));
262   ASSERT_EQ(ERROR_SUCCESS,
263             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
264   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
265 
266   key_path += L"\\Bar";
267   ASSERT_EQ(ERROR_SUCCESS,
268             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
269   key_path += L"\\Foo";
270   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
271   ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
272   ASSERT_EQ(ERROR_SUCCESS,
273             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ));
274 
275   ASSERT_EQ(ERROR_SUCCESS,
276             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
277   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
278   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
279   ASSERT_EQ(ERROR_SUCCESS,
280             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_WRITE));
281   ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L""));
282   ASSERT_NE(ERROR_SUCCESS,
283             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ));
284 
285   ASSERT_EQ(ERROR_SUCCESS,
286             key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_WRITE));
287   ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"Bar"));
288   ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Bar"));
289   ASSERT_NE(ERROR_SUCCESS,
290             key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ));
291 }
292 
TEST_F(RegistryTest,OpenSubKey)293 TEST_F(RegistryTest, OpenSubKey) {
294   RegKey key;
295   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
296                                     KEY_READ | KEY_CREATE_SUB_KEY));
297 
298   ASSERT_NE(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
299   ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"foo", KEY_READ));
300   ASSERT_EQ(ERROR_SUCCESS,
301             key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
302   ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
303 
304   std::wstring foo_key = root_key() + L"\\Foo";
305   ASSERT_EQ(ERROR_SUCCESS,
306             key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
307 
308   ASSERT_EQ(ERROR_SUCCESS,
309             key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_WRITE));
310   ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo"));
311 }
312 
TEST_F(RegistryTest,InvalidRelativeKeyCreate)313 TEST_F(RegistryTest, InvalidRelativeKeyCreate) {
314   RegKey key(HKEY_CURRENT_USER,
315              base::StrCat({this->root_key(), L"_DoesNotExist"}).c_str(),
316              KEY_WOW64_32KEY | KEY_READ);
317   ASSERT_EQ(key.CreateKey(L"SomeSubKey", KEY_WOW64_32KEY | KEY_WRITE),
318             ERROR_INVALID_HANDLE);
319 }
320 
TEST_F(RegistryTest,InvalidRelativeKeyOpen)321 TEST_F(RegistryTest, InvalidRelativeKeyOpen) {
322   RegKey key(HKEY_CURRENT_USER,
323              base::StrCat({this->root_key(), L"_DoesNotExist"}).c_str(),
324              KEY_WOW64_32KEY | KEY_READ);
325   ASSERT_EQ(key.OpenKey(L"SomeSubKey", KEY_WOW64_32KEY | KEY_READ),
326             ERROR_INVALID_HANDLE);
327 }
328 
329 namespace {
330 
331 class TestChangeDelegate {
332  public:
333   TestChangeDelegate() = default;
334   ~TestChangeDelegate() = default;
335 
OnKeyChanged(base::OnceClosure quit_closure)336   void OnKeyChanged(base::OnceClosure quit_closure) {
337     std::move(quit_closure).Run();
338     called_ = true;
339   }
340 
WasCalled()341   bool WasCalled() {
342     bool was_called = called_;
343     called_ = false;
344     return was_called;
345   }
346 
347  private:
348   bool called_ = false;
349 };
350 
351 }  // namespace
352 
TEST_F(RegistryTest,ChangeCallback)353 TEST_F(RegistryTest, ChangeCallback) {
354   RegKey key;
355   TestChangeDelegate delegate;
356   test::TaskEnvironment task_environment;
357   base::RunLoop loop1;
358   base::RunLoop loop2;
359   base::RunLoop loop3;
360 
361   ASSERT_EQ(ERROR_SUCCESS,
362             key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
363 
364   ASSERT_TRUE(key.StartWatching(BindOnce(&TestChangeDelegate::OnKeyChanged,
365                                          Unretained(&delegate),
366                                          loop1.QuitWhenIdleClosure())));
367   EXPECT_FALSE(delegate.WasCalled());
368 
369   // Make some change.
370   RegKey key2;
371   ASSERT_EQ(ERROR_SUCCESS, key2.Open(HKEY_CURRENT_USER, root_key().c_str(),
372                                      KEY_READ | KEY_SET_VALUE));
373   ASSERT_TRUE(key2.Valid());
374   EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name", L"data"));
375 
376   // Allow delivery of the notification.
377   EXPECT_FALSE(delegate.WasCalled());
378   loop1.Run();
379 
380   ASSERT_TRUE(delegate.WasCalled());
381   EXPECT_FALSE(delegate.WasCalled());
382 
383   ASSERT_TRUE(key.StartWatching(BindOnce(&TestChangeDelegate::OnKeyChanged,
384                                          Unretained(&delegate),
385                                          loop2.QuitWhenIdleClosure())));
386 
387   // Change something else.
388   EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name2", L"data2"));
389   loop2.Run();
390   ASSERT_TRUE(delegate.WasCalled());
391 
392   ASSERT_TRUE(key.StartWatching(BindOnce(&TestChangeDelegate::OnKeyChanged,
393                                          Unretained(&delegate),
394                                          loop3.QuitWhenIdleClosure())));
395   loop3.RunUntilIdle();
396   EXPECT_FALSE(delegate.WasCalled());
397 }
398 
399 namespace {
400 
401 // A thread that runs tasks from a TestMockTimeTaskRunner.
402 class RegistryWatcherThread : public SimpleThread {
403  public:
RegistryWatcherThread(scoped_refptr<base::TestMockTimeTaskRunner> task_runner)404   explicit RegistryWatcherThread(
405       scoped_refptr<base::TestMockTimeTaskRunner> task_runner)
406       : SimpleThread("RegistryWatcherThread"),
407         task_runner_(std::move(task_runner)) {}
408 
409  private:
Run()410   void Run() override {
411     task_runner_->DetachFromThread();
412     task_runner_->RunUntilIdle();
413   }
414   const scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
415 };
416 
417 }  // namespace
418 
TEST_F(RegistryTest,WatcherNotSignaledOnInitiatingThreadExit)419 TEST_F(RegistryTest, WatcherNotSignaledOnInitiatingThreadExit) {
420   RegKey key;
421 
422   ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ),
423             ERROR_SUCCESS);
424 
425   auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
426       base::TestMockTimeTaskRunner::Type::kBoundToThread);
427   ::testing::StrictMock<base::MockCallback<base::win::RegKey::ChangeCallback>>
428       change_cb;
429 
430   test_task_runner->PostTask(FROM_HERE,
431                              BindOnce(IgnoreResult(&RegKey::StartWatching),
432                                       Unretained(&key), change_cb.Get()));
433 
434   {
435     // Start the watch on a thread that then goes away.
436     RegistryWatcherThread watcher_thread(test_task_runner);
437     watcher_thread.Start();
438     watcher_thread.Join();
439   }
440 
441   // Termination of the thread should not cause a notification to get sent.
442   ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(&change_cb));
443   test_task_runner->DetachFromThread();
444   ASSERT_FALSE(test_task_runner->HasPendingTask());
445 
446   // Expect that a notification is sent when a change is made. Exit the run loop
447   // when this happens.
448   base::RunLoop run_loop;
449   EXPECT_CALL(change_cb, Run).WillOnce([&run_loop]() { run_loop.Quit(); });
450 
451   // Make some change.
452   RegKey key2;
453   ASSERT_EQ(key2.Open(HKEY_CURRENT_USER, root_key().c_str(),
454                       KEY_READ | KEY_SET_VALUE),
455             ERROR_SUCCESS);
456   ASSERT_TRUE(key2.Valid());
457   ASSERT_EQ(key2.WriteValue(L"name", L"data"), ERROR_SUCCESS);
458 
459   // Wait for the watcher to be signaled.
460   run_loop.Run();
461 }
462 
TEST_F(RegistryTest,TestMoveConstruct)463 TEST_F(RegistryTest, TestMoveConstruct) {
464   RegKey key;
465 
466   ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_SET_VALUE),
467             ERROR_SUCCESS);
468   RegKey key2(std::move(key));
469 
470   // The old key should be meaningless now.
471   EXPECT_EQ(key.Handle(), nullptr);
472 
473   // And the new one should work just fine.
474   EXPECT_NE(key2.Handle(), nullptr);
475   EXPECT_EQ(key2.WriteValue(L"foo", 1U), ERROR_SUCCESS);
476 }
477 
TEST_F(RegistryTest,TestMoveAssign)478 TEST_F(RegistryTest, TestMoveAssign) {
479   RegKey key;
480   RegKey key2;
481   const wchar_t kFooValueName[] = L"foo";
482 
483   ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(),
484                      KEY_SET_VALUE | KEY_QUERY_VALUE),
485             ERROR_SUCCESS);
486   ASSERT_EQ(key.WriteValue(kFooValueName, 1U), ERROR_SUCCESS);
487   ASSERT_EQ(key2.Create(HKEY_CURRENT_USER, (root_key() + L"\\child").c_str(),
488                         KEY_SET_VALUE),
489             ERROR_SUCCESS);
490   key2 = std::move(key);
491 
492   // The old key should be meaningless now.
493   EXPECT_EQ(key.Handle(), nullptr);
494 
495   // And the new one should hold what was the old one.
496   EXPECT_NE(key2.Handle(), nullptr);
497   DWORD foo = 0;
498   ASSERT_EQ(key2.ReadValueDW(kFooValueName, &foo), ERROR_SUCCESS);
499   EXPECT_EQ(foo, 1U);
500 }
501 
502 // Verify that either the platform, or the API-integration, causes deletion
503 // attempts via an invalid handle to fail with the expected error code.
TEST_F(RegistryTest,DeleteWithInvalidRegKey)504 TEST_F(RegistryTest, DeleteWithInvalidRegKey) {
505   RegKey key;
506 
507   static const wchar_t kFooName[] = L"foo";
508 
509   EXPECT_EQ(key.DeleteKey(kFooName), ERROR_INVALID_HANDLE);
510   EXPECT_EQ(key.DeleteValue(kFooName), ERROR_INVALID_HANDLE);
511 }
512 
513 // A test harness for tests of RegKey::DeleteKey; parameterized on whether to
514 // perform non-recursive or recursive deletes.
515 class DeleteKeyRegistryTest
516     : public RegistryTest,
517       public ::testing::WithParamInterface<RegKey::RecursiveDelete> {
518  protected:
519   DeleteKeyRegistryTest() = default;
520 
521  private:
522 };
523 
524 // Test that DeleteKey does not follow symbolic links.
TEST_P(DeleteKeyRegistryTest,DoesNotFollowLinks)525 TEST_P(DeleteKeyRegistryTest, DoesNotFollowLinks) {
526   // Create a subkey that should not be deleted.
527   std::wstring target_path = root_key() + L"\\LinkTarget";
528   {
529     RegKey target;
530     ASSERT_EQ(target.Create(HKEY_CURRENT_USER, target_path.c_str(), KEY_WRITE),
531               ERROR_SUCCESS);
532     ASSERT_EQ(target.WriteValue(L"IsTarget", 1U), ERROR_SUCCESS);
533   }
534 
535   // Create a link to the above key.
536   std::wstring source_path = root_key() + L"\\LinkSource";
537   {
538     HKEY link_handle = {};
539     ASSERT_EQ(RegCreateKeyEx(HKEY_CURRENT_USER, source_path.c_str(), 0, nullptr,
540                              REG_OPTION_CREATE_LINK | REG_OPTION_NON_VOLATILE,
541                              KEY_WRITE, nullptr, &link_handle, nullptr),
542               ERROR_SUCCESS);
543     RegKey link(std::exchange(link_handle, HKEY{}));
544     ASSERT_TRUE(link.Valid());
545 
546     std::wstring user_sid;
547     ASSERT_TRUE(GetUserSidString(&user_sid));
548 
549     std::wstring value =
550         base::StrCat({L"\\Registry\\User\\", user_sid, L"\\", override_path(),
551                       L"\\", root_key(), L"\\LinkTarget"});
552     ASSERT_EQ(link.WriteValue(L"SymbolicLinkValue", value.data(),
553                               value.size() * sizeof(wchar_t), REG_LINK),
554               ERROR_SUCCESS);
555   }
556 
557   // Verify that the link works.
558   {
559     RegKey link;
560     ASSERT_EQ(link.Open(HKEY_CURRENT_USER, source_path.c_str(), KEY_READ),
561               ERROR_SUCCESS);
562     DWORD value = 0;
563     ASSERT_EQ(link.ReadValueDW(L"IsTarget", &value), ERROR_SUCCESS);
564     ASSERT_EQ(value, 1U);
565   }
566 
567   // Now delete the link and ensure that it was deleted, but not the target.
568   ASSERT_EQ(RegKey(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ)
569                 .DeleteKey(L"LinkSource", GetParam()),
570             ERROR_SUCCESS);
571   {
572     RegKey source;
573     ASSERT_NE(source.Open(HKEY_CURRENT_USER, source_path.c_str(), KEY_READ),
574               ERROR_SUCCESS);
575   }
576   {
577     RegKey target;
578     ASSERT_EQ(target.Open(HKEY_CURRENT_USER, target_path.c_str(), KEY_READ),
579               ERROR_SUCCESS);
580   }
581 }
582 
583 INSTANTIATE_TEST_SUITE_P(NonRecursive,
584                          DeleteKeyRegistryTest,
585                          ::testing::Values(RegKey::RecursiveDelete(false)));
586 INSTANTIATE_TEST_SUITE_P(Recursive,
587                          DeleteKeyRegistryTest,
588                          ::testing::Values(RegKey::RecursiveDelete(true)));
589 
590 // A test harness for tests that use HKLM to test WoW redirection and such.
591 // TODO(https://crbug.com/377917): The tests here that write to the registry are
592 // disabled because they need work to handle parallel runs of different tests.
593 class RegistryTestHKLM : public ::testing::Test {
594  protected:
595   enum : REGSAM {
596 #if defined(_WIN64)
597     kNativeViewMask = KEY_WOW64_64KEY,
598     kRedirectedViewMask = KEY_WOW64_32KEY,
599 #else
600     kNativeViewMask = KEY_WOW64_32KEY,
601     kRedirectedViewMask = KEY_WOW64_64KEY,
602 #endif  //  _WIN64
603   };
604 
RegistryTestHKLM()605   RegistryTestHKLM()
606       : foo_software_key_(std::wstring(L"Software\\") + kRootKey + L"\\Foo") {}
607 
IsRedirectorPresent()608   static bool IsRedirectorPresent() {
609 #if defined(_WIN64)
610     return true;
611 #else
612     return OSInfo::GetInstance()->IsWowX86OnAMD64();
613 #endif
614   }
615 
616   const std::wstring foo_software_key_;
617 };
618 
619 class RegistryTestHKLMAdmin : public RegistryTestHKLM {
620  protected:
SetUp()621   void SetUp() override {
622     if (!IsRedirectorPresent()) {
623       GTEST_SKIP();
624     }
625     if (!::IsUserAnAdmin()) {
626       GTEST_SKIP();
627     }
628     // Clean up any stale registry keys.
629     for (const REGSAM mask :
630          {this->kNativeViewMask, this->kRedirectedViewMask}) {
631       RegKey key;
632       if (key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE | mask) ==
633           ERROR_SUCCESS) {
634         key.DeleteKey(kRootKey);
635       }
636     }
637   }
638 };
639 
640 // This test requires running as an Administrator as it tests redirected
641 // registry writes to HKLM\Software
642 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384253.aspx
TEST_F(RegistryTestHKLMAdmin,Wow64RedirectedFromNative)643 TEST_F(RegistryTestHKLMAdmin, Wow64RedirectedFromNative) {
644   RegKey key;
645 
646   // Test redirected key access from non-redirected.
647   ASSERT_EQ(ERROR_SUCCESS,
648             key.Create(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
649                        KEY_WRITE | kRedirectedViewMask));
650   ASSERT_NE(ERROR_SUCCESS,
651             key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
652   ASSERT_NE(ERROR_SUCCESS,
653             key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
654                      KEY_READ | kNativeViewMask));
655 
656   // Open the non-redirected view of the parent and try to delete the test key.
657   ASSERT_EQ(ERROR_SUCCESS,
658             key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE));
659   ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
660   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
661                                     KEY_SET_VALUE | kNativeViewMask));
662   ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
663 
664   // Open the redirected view and delete the key created above.
665   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
666                                     KEY_SET_VALUE | kRedirectedViewMask));
667   ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
668 }
669 
670 // Test for the issue found in http://crbug.com/384587 where OpenKey would call
671 // Close() and reset wow64_access_ flag to 0 and cause a NOTREACHED to hit on a
672 // subsequent OpenKey call.
TEST_F(RegistryTestHKLM,SameWowFlags)673 TEST_F(RegistryTestHKLM, SameWowFlags) {
674   RegKey key;
675 
676   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
677                                     KEY_READ | KEY_WOW64_64KEY));
678   ASSERT_EQ(ERROR_SUCCESS,
679             key.OpenKey(L"Microsoft", KEY_READ | KEY_WOW64_64KEY));
680   ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"Windows", KEY_READ | KEY_WOW64_64KEY));
681 }
682 
TEST_F(RegistryTestHKLMAdmin,Wow64NativeFromRedirected)683 TEST_F(RegistryTestHKLMAdmin, Wow64NativeFromRedirected) {
684   RegKey key;
685 
686   // Test non-redirected key access from redirected.
687   ASSERT_EQ(ERROR_SUCCESS,
688             key.Create(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
689                        KEY_WRITE | kNativeViewMask));
690   ASSERT_EQ(ERROR_SUCCESS,
691             key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
692   ASSERT_NE(ERROR_SUCCESS,
693             key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
694                      KEY_READ | kRedirectedViewMask));
695 
696   // Open the redirected view of the parent and try to delete the test key
697   // from the non-redirected view.
698   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
699                                     KEY_SET_VALUE | kRedirectedViewMask));
700   ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
701 
702   ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
703                                     KEY_SET_VALUE | kNativeViewMask));
704   ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
705 }
706 
707 }  // namespace base::win
708