1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 "golang.org/x/sys/windows/registry"
21 func randKeyName(prefix string) string {
22 const numbers = "0123456789"
23 buf := make([]byte, 10)
25 for i, b := range buf {
26 buf[i] = numbers[b%byte(len(numbers))]
28 return prefix + string(buf)
31 func TestReadSubKeyNames(t *testing.T) {
32 k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)
38 names, err := k.ReadSubKeyNames(-1)
43 for _, name := range names {
44 // Every PC has "stdole 2.0 OLE Automation" library installed.
45 if name == "{00020430-0000-0000-C000-000000000046}" {
50 t.Fatal("could not find stdole 2.0 OLE Automation")
54 func TestCreateOpenDeleteKey(t *testing.T) {
55 k, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
61 testKName := randKeyName("TestCreateOpenDeleteKey_")
63 testK, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
70 t.Fatalf("key %q already exists", testKName)
73 testKAgain, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
77 defer testKAgain.Close()
80 t.Fatalf("key %q should already exist", testKName)
83 testKOpened, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
87 defer testKOpened.Close()
89 err = registry.DeleteKey(k, testKName)
94 testKOpenedAgain, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
96 defer testKOpenedAgain.Close()
97 t.Fatalf("key %q should already been deleted", testKName)
99 if err != registry.ErrNotExist {
100 t.Fatalf(`unexpected error ("not exist" expected): %v`, err)
104 func equalStringSlice(a, b []string) bool {
105 if len(a) != len(b) {
119 type ValueTest struct {
126 var ValueTests = []ValueTest{
127 {Type: registry.SZ, Name: "String1", Value: ""},
128 {Type: registry.SZ, Name: "String2", Value: "\000", WillFail: true},
129 {Type: registry.SZ, Name: "String3", Value: "Hello World"},
130 {Type: registry.SZ, Name: "String4", Value: "Hello World\000", WillFail: true},
131 {Type: registry.EXPAND_SZ, Name: "ExpString1", Value: ""},
132 {Type: registry.EXPAND_SZ, Name: "ExpString2", Value: "\000", WillFail: true},
133 {Type: registry.EXPAND_SZ, Name: "ExpString3", Value: "Hello World"},
134 {Type: registry.EXPAND_SZ, Name: "ExpString4", Value: "Hello\000World", WillFail: true},
135 {Type: registry.EXPAND_SZ, Name: "ExpString5", Value: "%PATH%"},
136 {Type: registry.EXPAND_SZ, Name: "ExpString6", Value: "%NO_SUCH_VARIABLE%"},
137 {Type: registry.EXPAND_SZ, Name: "ExpString7", Value: "%PATH%;."},
138 {Type: registry.BINARY, Name: "Binary1", Value: []byte{}},
139 {Type: registry.BINARY, Name: "Binary2", Value: []byte{1, 2, 3}},
140 {Type: registry.BINARY, Name: "Binary3", Value: []byte{3, 2, 1, 0, 1, 2, 3}},
141 {Type: registry.DWORD, Name: "Dword1", Value: uint64(0)},
142 {Type: registry.DWORD, Name: "Dword2", Value: uint64(1)},
143 {Type: registry.DWORD, Name: "Dword3", Value: uint64(0xff)},
144 {Type: registry.DWORD, Name: "Dword4", Value: uint64(0xffff)},
145 {Type: registry.QWORD, Name: "Qword1", Value: uint64(0)},
146 {Type: registry.QWORD, Name: "Qword2", Value: uint64(1)},
147 {Type: registry.QWORD, Name: "Qword3", Value: uint64(0xff)},
148 {Type: registry.QWORD, Name: "Qword4", Value: uint64(0xffff)},
149 {Type: registry.QWORD, Name: "Qword5", Value: uint64(0xffffff)},
150 {Type: registry.QWORD, Name: "Qword6", Value: uint64(0xffffffff)},
151 {Type: registry.MULTI_SZ, Name: "MultiString1", Value: []string{"a", "b", "c"}},
152 {Type: registry.MULTI_SZ, Name: "MultiString2", Value: []string{"abc", "", "cba"}},
153 {Type: registry.MULTI_SZ, Name: "MultiString3", Value: []string{""}},
154 {Type: registry.MULTI_SZ, Name: "MultiString4", Value: []string{"abcdef"}},
155 {Type: registry.MULTI_SZ, Name: "MultiString5", Value: []string{"\000"}, WillFail: true},
156 {Type: registry.MULTI_SZ, Name: "MultiString6", Value: []string{"a\000b"}, WillFail: true},
157 {Type: registry.MULTI_SZ, Name: "MultiString7", Value: []string{"ab", "\000", "cd"}, WillFail: true},
158 {Type: registry.MULTI_SZ, Name: "MultiString8", Value: []string{"\000", "cd"}, WillFail: true},
159 {Type: registry.MULTI_SZ, Name: "MultiString9", Value: []string{"ab", "\000"}, WillFail: true},
162 func setValues(t *testing.T, k registry.Key) {
163 for _, test := range ValueTests {
167 err = k.SetStringValue(test.Name, test.Value.(string))
168 case registry.EXPAND_SZ:
169 err = k.SetExpandStringValue(test.Name, test.Value.(string))
170 case registry.MULTI_SZ:
171 err = k.SetStringsValue(test.Name, test.Value.([]string))
172 case registry.BINARY:
173 err = k.SetBinaryValue(test.Name, test.Value.([]byte))
175 err = k.SetDWordValue(test.Name, uint32(test.Value.(uint64)))
177 err = k.SetQWordValue(test.Name, test.Value.(uint64))
179 t.Fatalf("unsupported type %d for %s value", test.Type, test.Name)
183 t.Fatalf("setting %s value %q should fail, but succeeded", test.Name, test.Value)
193 func enumerateValues(t *testing.T, k registry.Key) {
194 names, err := k.ReadValueNames(-1)
199 haveNames := make(map[string]bool)
200 for _, n := range names {
203 for _, test := range ValueTests {
204 wantFound := !test.WillFail
205 _, haveFound := haveNames[test.Name]
206 if wantFound && !haveFound {
207 t.Errorf("value %s is not found while enumerating", test.Name)
209 if haveFound && !wantFound {
210 t.Errorf("value %s is found while enumerating, but expected to fail", test.Name)
213 delete(haveNames, test.Name)
216 for n, v := range haveNames {
217 t.Errorf("value %s (%v) is found while enumerating, but has not been cretaed", n, v)
221 func testErrNotExist(t *testing.T, name string, err error) {
223 t.Errorf("%s value should not exist", name)
226 if err != registry.ErrNotExist {
227 t.Errorf("reading %s value should return 'not exist' error, but got: %s", name, err)
232 func testErrUnexpectedType(t *testing.T, test ValueTest, gottype uint32, err error) {
234 t.Errorf("GetXValue(%q) should not succeed", test.Name)
237 if err != registry.ErrUnexpectedType {
238 t.Errorf("reading %s value should return 'unexpected key value type' error, but got: %s", test.Name, err)
241 if gottype != test.Type {
242 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
247 func testGetStringValue(t *testing.T, k registry.Key, test ValueTest) {
248 got, gottype, err := k.GetStringValue(test.Name)
250 t.Errorf("GetStringValue(%s) failed: %v", test.Name, err)
253 if got != test.Value {
254 t.Errorf("want %s value %q, got %q", test.Name, test.Value, got)
257 if gottype != test.Type {
258 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
261 if gottype == registry.EXPAND_SZ {
262 _, err = registry.ExpandString(got)
264 t.Errorf("ExpandString(%s) failed: %v", got, err)
270 func testGetIntegerValue(t *testing.T, k registry.Key, test ValueTest) {
271 got, gottype, err := k.GetIntegerValue(test.Name)
273 t.Errorf("GetIntegerValue(%s) failed: %v", test.Name, err)
276 if got != test.Value.(uint64) {
277 t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
280 if gottype != test.Type {
281 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
286 func testGetBinaryValue(t *testing.T, k registry.Key, test ValueTest) {
287 got, gottype, err := k.GetBinaryValue(test.Name)
289 t.Errorf("GetBinaryValue(%s) failed: %v", test.Name, err)
292 if !bytes.Equal(got, test.Value.([]byte)) {
293 t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
296 if gottype != test.Type {
297 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
302 func testGetStringsValue(t *testing.T, k registry.Key, test ValueTest) {
303 got, gottype, err := k.GetStringsValue(test.Name)
305 t.Errorf("GetStringsValue(%s) failed: %v", test.Name, err)
308 if !equalStringSlice(got, test.Value.([]string)) {
309 t.Errorf("want %s value %#v, got %#v", test.Name, test.Value, got)
312 if gottype != test.Type {
313 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
318 func testGetValue(t *testing.T, k registry.Key, test ValueTest, size int) {
322 // read data with no buffer
323 gotsize, gottype, err := k.GetValue(test.Name, nil)
325 t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
329 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
332 if gottype != test.Type {
333 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
336 // read data with short buffer
337 gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size-1))
339 t.Errorf("GetValue(%s, [%d]byte) should fail, but succeeded", test.Name, size-1)
342 if err != registry.ErrShortBuffer {
343 t.Errorf("reading %s value should return 'short buffer' error, but got: %s", test.Name, err)
347 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
350 if gottype != test.Type {
351 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
355 gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size))
357 t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
361 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
364 if gottype != test.Type {
365 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
368 // check GetValue returns ErrNotExist as required
369 _, _, err = k.GetValue(test.Name+"_not_there", make([]byte, size))
371 t.Errorf("GetValue(%q) should not succeed", test.Name)
374 if err != registry.ErrNotExist {
375 t.Errorf("GetValue(%q) should return 'not exist' error, but got: %s", test.Name, err)
380 func testValues(t *testing.T, k registry.Key) {
381 for _, test := range ValueTests {
383 case registry.SZ, registry.EXPAND_SZ:
385 _, _, err := k.GetStringValue(test.Name)
386 testErrNotExist(t, test.Name, err)
388 testGetStringValue(t, k, test)
389 _, gottype, err := k.GetIntegerValue(test.Name)
390 testErrUnexpectedType(t, test, gottype, err)
391 // Size of utf16 string in bytes is not perfect,
392 // but correct for current test values.
393 // Size also includes terminating 0.
394 testGetValue(t, k, test, (len(test.Value.(string))+1)*2)
396 _, _, err := k.GetStringValue(test.Name + "_string_not_created")
397 testErrNotExist(t, test.Name+"_string_not_created", err)
398 case registry.DWORD, registry.QWORD:
399 testGetIntegerValue(t, k, test)
400 _, gottype, err := k.GetBinaryValue(test.Name)
401 testErrUnexpectedType(t, test, gottype, err)
402 _, _, err = k.GetIntegerValue(test.Name + "_int_not_created")
403 testErrNotExist(t, test.Name+"_int_not_created", err)
405 if test.Type == registry.DWORD {
408 testGetValue(t, k, test, size)
409 case registry.BINARY:
410 testGetBinaryValue(t, k, test)
411 _, gottype, err := k.GetStringsValue(test.Name)
412 testErrUnexpectedType(t, test, gottype, err)
413 _, _, err = k.GetBinaryValue(test.Name + "_byte_not_created")
414 testErrNotExist(t, test.Name+"_byte_not_created", err)
415 testGetValue(t, k, test, len(test.Value.([]byte)))
416 case registry.MULTI_SZ:
418 _, _, err := k.GetStringsValue(test.Name)
419 testErrNotExist(t, test.Name, err)
421 testGetStringsValue(t, k, test)
422 _, gottype, err := k.GetStringValue(test.Name)
423 testErrUnexpectedType(t, test, gottype, err)
425 for _, s := range test.Value.([]string) {
426 size += len(s) + 1 // nil terminated
428 size += 1 // extra nil at the end
429 size *= 2 // count bytes, not uint16
430 testGetValue(t, k, test, size)
432 _, _, err := k.GetStringsValue(test.Name + "_strings_not_created")
433 testErrNotExist(t, test.Name+"_strings_not_created", err)
435 t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
441 func testStat(t *testing.T, k registry.Key) {
442 subk, _, err := registry.CreateKey(k, "subkey", registry.CREATE_SUB_KEY)
449 defer registry.DeleteKey(k, "subkey")
456 if ki.SubKeyCount != 1 {
457 t.Error("key must have 1 subkey")
459 if ki.MaxSubKeyLen != 6 {
460 t.Error("key max subkey name length must be 6")
462 if ki.ValueCount != 24 {
463 t.Errorf("key must have 24 values, but is %d", ki.ValueCount)
465 if ki.MaxValueNameLen != 12 {
466 t.Errorf("key max value name length must be 10, but is %d", ki.MaxValueNameLen)
468 if ki.MaxValueLen != 38 {
469 t.Errorf("key max value length must be 38, but is %d", ki.MaxValueLen)
471 if mt, ct := ki.ModTime(), time.Now(); ct.Sub(mt) > 100*time.Millisecond {
472 t.Errorf("key mod time is not close to current time: mtime=%v current=%v delta=%v", mt, ct, ct.Sub(mt))
476 func deleteValues(t *testing.T, k registry.Key) {
477 for _, test := range ValueTests {
481 err := k.DeleteValue(test.Name)
487 names, err := k.ReadValueNames(-1)
493 t.Errorf("some values remain after deletion: %v", names)
497 func TestValues(t *testing.T) {
498 softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
502 defer softwareK.Close()
504 testKName := randKeyName("TestValues_")
506 k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
513 t.Fatalf("key %q already exists", testKName)
516 defer registry.DeleteKey(softwareK, testKName)
520 enumerateValues(t, k)
529 func walkKey(t *testing.T, k registry.Key, kname string) {
530 names, err := k.ReadValueNames(-1)
532 t.Fatalf("reading value names of %s failed: %v", kname, err)
534 for _, name := range names {
535 _, valtype, err := k.GetValue(name, nil)
537 t.Fatalf("reading value type of %s of %s failed: %v", name, kname, err)
542 _, _, err := k.GetStringValue(name)
546 case registry.EXPAND_SZ:
547 s, _, err := k.GetStringValue(name)
551 _, err = registry.ExpandString(s)
555 case registry.DWORD, registry.QWORD:
556 _, _, err := k.GetIntegerValue(name)
560 case registry.BINARY:
561 _, _, err := k.GetBinaryValue(name)
565 case registry.MULTI_SZ:
566 _, _, err := k.GetStringsValue(name)
570 case registry.FULL_RESOURCE_DESCRIPTOR, registry.RESOURCE_LIST, registry.RESOURCE_REQUIREMENTS_LIST:
571 // TODO: not implemented
573 t.Fatalf("value type %d of %s of %s failed: %v", valtype, name, kname, err)
577 names, err = k.ReadSubKeyNames(-1)
579 t.Fatalf("reading sub-keys of %s failed: %v", kname, err)
581 for _, name := range names {
583 subk, err := registry.OpenKey(k, name, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)
585 if err == syscall.ERROR_ACCESS_DENIED {
586 // ignore error, if we are not allowed to access this key
589 t.Fatalf("opening sub-keys %s of %s failed: %v", name, kname, err)
593 walkKey(t, subk, kname+`\`+name)
598 func TestWalkFullRegistry(t *testing.T) {
600 t.Skip("skipping long running test in short mode")
602 walkKey(t, registry.CLASSES_ROOT, "CLASSES_ROOT")
603 walkKey(t, registry.CURRENT_USER, "CURRENT_USER")
604 walkKey(t, registry.LOCAL_MACHINE, "LOCAL_MACHINE")
605 walkKey(t, registry.USERS, "USERS")
606 walkKey(t, registry.CURRENT_CONFIG, "CURRENT_CONFIG")
609 func TestExpandString(t *testing.T) {
610 got, err := registry.ExpandString("%PATH%")
614 want := os.Getenv("PATH")
616 t.Errorf("want %q string expanded, got %q", want, got)
620 func TestInvalidValues(t *testing.T) {
621 softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
625 defer softwareK.Close()
627 testKName := randKeyName("TestInvalidValues_")
629 k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
636 t.Fatalf("key %q already exists", testKName)
639 defer registry.DeleteKey(softwareK, testKName)
641 var tests = []struct {
646 {registry.DWORD, "Dword1", nil},
647 {registry.DWORD, "Dword2", []byte{1, 2, 3}},
648 {registry.QWORD, "Qword1", nil},
649 {registry.QWORD, "Qword2", []byte{1, 2, 3}},
650 {registry.QWORD, "Qword3", []byte{1, 2, 3, 4, 5, 6, 7}},
651 {registry.MULTI_SZ, "MultiString1", nil},
652 {registry.MULTI_SZ, "MultiString2", []byte{0}},
653 {registry.MULTI_SZ, "MultiString3", []byte{'a', 'b', 0}},
654 {registry.MULTI_SZ, "MultiString4", []byte{'a', 0, 0, 'b', 0}},
655 {registry.MULTI_SZ, "MultiString5", []byte{'a', 0, 0}},
658 for _, test := range tests {
659 err := k.SetValue(test.Name, test.Type, test.Data)
661 t.Fatalf("SetValue for %q failed: %v", test.Name, err)
665 for _, test := range tests {
667 case registry.DWORD, registry.QWORD:
668 value, valType, err := k.GetIntegerValue(test.Name)
670 t.Errorf("GetIntegerValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
672 case registry.MULTI_SZ:
673 value, valType, err := k.GetStringsValue(test.Name)
676 t.Errorf("GetStringsValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
680 t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
685 func TestGetMUIStringValue(t *testing.T) {
686 if err := registry.LoadRegLoadMUIString(); err != nil {
687 t.Skip("regLoadMUIString not supported; skipping")
689 if err := procGetDynamicTimeZoneInformation.Find(); err != nil {
690 t.Skipf("%s not supported; skipping", procGetDynamicTimeZoneInformation.Name)
692 var dtzi DynamicTimezoneinformation
693 if _, err := GetDynamicTimeZoneInformation(&dtzi); err != nil {
696 tzKeyName := syscall.UTF16ToString(dtzi.TimeZoneKeyName[:])
697 timezoneK, err := registry.OpenKey(registry.LOCAL_MACHINE,
698 `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\`+tzKeyName, registry.READ)
702 defer timezoneK.Close()
704 type testType struct {
708 var tests = []testType{
709 {"MUI_Std", syscall.UTF16ToString(dtzi.StandardName[:])},
711 if dtzi.DynamicDaylightTimeDisabled == 0 {
712 tests = append(tests, testType{"MUI_Dlt", syscall.UTF16ToString(dtzi.DaylightName[:])})
715 for _, test := range tests {
716 got, err := timezoneK.GetMUIStringValue(test.name)
718 t.Error("GetMUIStringValue:", err)
721 if got != test.want {
722 t.Errorf("GetMUIStringValue: %s: Got %q, want %q", test.name, got, test.want)
727 type DynamicTimezoneinformation struct {
729 StandardName [32]uint16
730 StandardDate syscall.Systemtime
732 DaylightName [32]uint16
733 DaylightDate syscall.Systemtime
735 TimeZoneKeyName [128]uint16
736 DynamicDaylightTimeDisabled uint8
740 kernel32DLL = syscall.NewLazyDLL("kernel32")
742 procGetDynamicTimeZoneInformation = kernel32DLL.NewProc("GetDynamicTimeZoneInformation")
745 func GetDynamicTimeZoneInformation(dtzi *DynamicTimezoneinformation) (rc uint32, err error) {
746 r0, _, e1 := syscall.Syscall(procGetDynamicTimeZoneInformation.Addr(), 1, uintptr(unsafe.Pointer(dtzi)), 0, 0)
748 if rc == 0xffffffff {