1
2
3
4
5
6
7 package registry_test
8
9 import (
10 "bytes"
11 "crypto/rand"
12 "os"
13 "slices"
14 "syscall"
15 "testing"
16 "unsafe"
17
18 "internal/syscall/windows/registry"
19 )
20
21 func randKeyName(prefix string) string {
22 const numbers = "0123456789"
23 buf := make([]byte, 10)
24 rand.Read(buf)
25 for i, b := range buf {
26 buf[i] = numbers[b%byte(len(numbers))]
27 }
28 return prefix + string(buf)
29 }
30
31 func TestReadSubKeyNames(t *testing.T) {
32 k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS)
33 if err != nil {
34 t.Fatal(err)
35 }
36 defer k.Close()
37
38 names, err := k.ReadSubKeyNames()
39 if err != nil {
40 t.Fatal(err)
41 }
42 var foundStdOle bool
43 for _, name := range names {
44
45 if name == "{00020430-0000-0000-C000-000000000046}" {
46 foundStdOle = true
47 }
48 }
49 if !foundStdOle {
50 t.Fatal("could not find stdole 2.0 OLE Automation")
51 }
52 }
53
54 func TestCreateOpenDeleteKey(t *testing.T) {
55 k, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
56 if err != nil {
57 t.Fatal(err)
58 }
59 defer k.Close()
60
61 testKName := randKeyName("TestCreateOpenDeleteKey_")
62
63 testK, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
64 if err != nil {
65 t.Fatal(err)
66 }
67 defer testK.Close()
68
69 if exist {
70 t.Fatalf("key %q already exists", testKName)
71 }
72
73 testKAgain, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
74 if err != nil {
75 t.Fatal(err)
76 }
77 defer testKAgain.Close()
78
79 if !exist {
80 t.Fatalf("key %q should already exist", testKName)
81 }
82
83 testKOpened, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
84 if err != nil {
85 t.Fatal(err)
86 }
87 defer testKOpened.Close()
88
89 err = registry.DeleteKey(k, testKName)
90 if err != nil {
91 t.Fatal(err)
92 }
93
94 testKOpenedAgain, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
95 if err == nil {
96 defer testKOpenedAgain.Close()
97 t.Fatalf("key %q should already been deleted", testKName)
98 }
99 if err != registry.ErrNotExist {
100 t.Fatalf(`unexpected error ("not exist" expected): %v`, err)
101 }
102 }
103
104 type ValueTest struct {
105 Type uint32
106 Name string
107 Value any
108 WillFail bool
109 }
110
111 var ValueTests = []ValueTest{
112 {Type: registry.SZ, Name: "String1", Value: ""},
113 {Type: registry.SZ, Name: "String2", Value: "\000", WillFail: true},
114 {Type: registry.SZ, Name: "String3", Value: "Hello World"},
115 {Type: registry.SZ, Name: "String4", Value: "Hello World\000", WillFail: true},
116 {Type: registry.EXPAND_SZ, Name: "ExpString1", Value: ""},
117 {Type: registry.EXPAND_SZ, Name: "ExpString2", Value: "\000", WillFail: true},
118 {Type: registry.EXPAND_SZ, Name: "ExpString3", Value: "Hello World"},
119 {Type: registry.EXPAND_SZ, Name: "ExpString4", Value: "Hello\000World", WillFail: true},
120 {Type: registry.EXPAND_SZ, Name: "ExpString5", Value: "%PATH%"},
121 {Type: registry.EXPAND_SZ, Name: "ExpString6", Value: "%NO_SUCH_VARIABLE%"},
122 {Type: registry.EXPAND_SZ, Name: "ExpString7", Value: "%PATH%;."},
123 {Type: registry.BINARY, Name: "Binary1", Value: []byte{}},
124 {Type: registry.BINARY, Name: "Binary2", Value: []byte{1, 2, 3}},
125 {Type: registry.BINARY, Name: "Binary3", Value: []byte{3, 2, 1, 0, 1, 2, 3}},
126 {Type: registry.DWORD, Name: "Dword1", Value: uint64(0)},
127 {Type: registry.DWORD, Name: "Dword2", Value: uint64(1)},
128 {Type: registry.DWORD, Name: "Dword3", Value: uint64(0xff)},
129 {Type: registry.DWORD, Name: "Dword4", Value: uint64(0xffff)},
130 {Type: registry.QWORD, Name: "Qword1", Value: uint64(0)},
131 {Type: registry.QWORD, Name: "Qword2", Value: uint64(1)},
132 {Type: registry.QWORD, Name: "Qword3", Value: uint64(0xff)},
133 {Type: registry.QWORD, Name: "Qword4", Value: uint64(0xffff)},
134 {Type: registry.QWORD, Name: "Qword5", Value: uint64(0xffffff)},
135 {Type: registry.QWORD, Name: "Qword6", Value: uint64(0xffffffff)},
136 {Type: registry.MULTI_SZ, Name: "MultiString1", Value: []string{"a", "b", "c"}},
137 {Type: registry.MULTI_SZ, Name: "MultiString2", Value: []string{"abc", "", "cba"}},
138 {Type: registry.MULTI_SZ, Name: "MultiString3", Value: []string{""}},
139 {Type: registry.MULTI_SZ, Name: "MultiString4", Value: []string{"abcdef"}},
140 {Type: registry.MULTI_SZ, Name: "MultiString5", Value: []string{"\000"}, WillFail: true},
141 {Type: registry.MULTI_SZ, Name: "MultiString6", Value: []string{"a\000b"}, WillFail: true},
142 {Type: registry.MULTI_SZ, Name: "MultiString7", Value: []string{"ab", "\000", "cd"}, WillFail: true},
143 {Type: registry.MULTI_SZ, Name: "MultiString8", Value: []string{"\000", "cd"}, WillFail: true},
144 {Type: registry.MULTI_SZ, Name: "MultiString9", Value: []string{"ab", "\000"}, WillFail: true},
145 }
146
147 func setValues(t *testing.T, k registry.Key) {
148 for _, test := range ValueTests {
149 var err error
150 switch test.Type {
151 case registry.SZ:
152 err = k.SetStringValue(test.Name, test.Value.(string))
153 case registry.EXPAND_SZ:
154 err = k.SetExpandStringValue(test.Name, test.Value.(string))
155 case registry.MULTI_SZ:
156 err = k.SetStringsValue(test.Name, test.Value.([]string))
157 case registry.BINARY:
158 err = k.SetBinaryValue(test.Name, test.Value.([]byte))
159 case registry.DWORD:
160 err = k.SetDWordValue(test.Name, uint32(test.Value.(uint64)))
161 case registry.QWORD:
162 err = k.SetQWordValue(test.Name, test.Value.(uint64))
163 default:
164 t.Fatalf("unsupported type %d for %s value", test.Type, test.Name)
165 }
166 if test.WillFail {
167 if err == nil {
168 t.Fatalf("setting %s value %q should fail, but succeeded", test.Name, test.Value)
169 }
170 } else {
171 if err != nil {
172 t.Fatal(err)
173 }
174 }
175 }
176 }
177
178 func enumerateValues(t *testing.T, k registry.Key) {
179 names, err := k.ReadValueNames()
180 if err != nil {
181 t.Error(err)
182 return
183 }
184 haveNames := make(map[string]bool)
185 for _, n := range names {
186 haveNames[n] = false
187 }
188 for _, test := range ValueTests {
189 wantFound := !test.WillFail
190 _, haveFound := haveNames[test.Name]
191 if wantFound && !haveFound {
192 t.Errorf("value %s is not found while enumerating", test.Name)
193 }
194 if haveFound && !wantFound {
195 t.Errorf("value %s is found while enumerating, but expected to fail", test.Name)
196 }
197 if haveFound {
198 delete(haveNames, test.Name)
199 }
200 }
201 for n, v := range haveNames {
202 t.Errorf("value %s (%v) is found while enumerating, but has not been created", n, v)
203 }
204 }
205
206 func testErrNotExist(t *testing.T, name string, err error) {
207 if err == nil {
208 t.Errorf("%s value should not exist", name)
209 return
210 }
211 if err != registry.ErrNotExist {
212 t.Errorf("reading %s value should return 'not exist' error, but got: %s", name, err)
213 return
214 }
215 }
216
217 func testErrUnexpectedType(t *testing.T, test ValueTest, gottype uint32, err error) {
218 if err == nil {
219 t.Errorf("GetXValue(%q) should not succeed", test.Name)
220 return
221 }
222 if err != registry.ErrUnexpectedType {
223 t.Errorf("reading %s value should return 'unexpected key value type' error, but got: %s", test.Name, err)
224 return
225 }
226 if gottype != test.Type {
227 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
228 return
229 }
230 }
231
232 func testGetStringValue(t *testing.T, k registry.Key, test ValueTest) {
233 got, gottype, err := k.GetStringValue(test.Name)
234 if err != nil {
235 t.Errorf("GetStringValue(%s) failed: %v", test.Name, err)
236 return
237 }
238 if got != test.Value {
239 t.Errorf("want %s value %q, got %q", test.Name, test.Value, got)
240 return
241 }
242 if gottype != test.Type {
243 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
244 return
245 }
246 if gottype == registry.EXPAND_SZ {
247 _, err = registry.ExpandString(got)
248 if err != nil {
249 t.Errorf("ExpandString(%s) failed: %v", got, err)
250 return
251 }
252 }
253 }
254
255 func testGetIntegerValue(t *testing.T, k registry.Key, test ValueTest) {
256 got, gottype, err := k.GetIntegerValue(test.Name)
257 if err != nil {
258 t.Errorf("GetIntegerValue(%s) failed: %v", test.Name, err)
259 return
260 }
261 if got != test.Value.(uint64) {
262 t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
263 return
264 }
265 if gottype != test.Type {
266 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
267 return
268 }
269 }
270
271 func testGetBinaryValue(t *testing.T, k registry.Key, test ValueTest) {
272 got, gottype, err := k.GetBinaryValue(test.Name)
273 if err != nil {
274 t.Errorf("GetBinaryValue(%s) failed: %v", test.Name, err)
275 return
276 }
277 if !bytes.Equal(got, test.Value.([]byte)) {
278 t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
279 return
280 }
281 if gottype != test.Type {
282 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
283 return
284 }
285 }
286
287 func testGetStringsValue(t *testing.T, k registry.Key, test ValueTest) {
288 got, gottype, err := k.GetStringsValue(test.Name)
289 if err != nil {
290 t.Errorf("GetStringsValue(%s) failed: %v", test.Name, err)
291 return
292 }
293 if !slices.Equal(got, test.Value.([]string)) {
294 t.Errorf("want %s value %#v, got %#v", test.Name, test.Value, got)
295 return
296 }
297 if gottype != test.Type {
298 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
299 return
300 }
301 }
302
303 func testGetValue(t *testing.T, k registry.Key, test ValueTest, size int) {
304 if size <= 0 {
305 return
306 }
307
308 gotsize, gottype, err := k.GetValue(test.Name, nil)
309 if err != nil {
310 t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
311 return
312 }
313 if gotsize != size {
314 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
315 return
316 }
317 if gottype != test.Type {
318 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
319 return
320 }
321
322 gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size-1))
323 if err == nil {
324 t.Errorf("GetValue(%s, [%d]byte) should fail, but succeeded", test.Name, size-1)
325 return
326 }
327 if err != registry.ErrShortBuffer {
328 t.Errorf("reading %s value should return 'short buffer' error, but got: %s", test.Name, err)
329 return
330 }
331 if gotsize != size {
332 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
333 return
334 }
335 if gottype != test.Type {
336 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
337 return
338 }
339
340 gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size))
341 if err != nil {
342 t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
343 return
344 }
345 if gotsize != size {
346 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
347 return
348 }
349 if gottype != test.Type {
350 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
351 return
352 }
353
354 _, _, err = k.GetValue(test.Name+"_not_there", make([]byte, size))
355 if err == nil {
356 t.Errorf("GetValue(%q) should not succeed", test.Name)
357 return
358 }
359 if err != registry.ErrNotExist {
360 t.Errorf("GetValue(%q) should return 'not exist' error, but got: %s", test.Name, err)
361 return
362 }
363 }
364
365 func testValues(t *testing.T, k registry.Key) {
366 for _, test := range ValueTests {
367 switch test.Type {
368 case registry.SZ, registry.EXPAND_SZ:
369 if test.WillFail {
370 _, _, err := k.GetStringValue(test.Name)
371 testErrNotExist(t, test.Name, err)
372 } else {
373 testGetStringValue(t, k, test)
374 _, gottype, err := k.GetIntegerValue(test.Name)
375 testErrUnexpectedType(t, test, gottype, err)
376
377
378
379 testGetValue(t, k, test, (len(test.Value.(string))+1)*2)
380 }
381 _, _, err := k.GetStringValue(test.Name + "_string_not_created")
382 testErrNotExist(t, test.Name+"_string_not_created", err)
383 case registry.DWORD, registry.QWORD:
384 testGetIntegerValue(t, k, test)
385 _, gottype, err := k.GetBinaryValue(test.Name)
386 testErrUnexpectedType(t, test, gottype, err)
387 _, _, err = k.GetIntegerValue(test.Name + "_int_not_created")
388 testErrNotExist(t, test.Name+"_int_not_created", err)
389 size := 8
390 if test.Type == registry.DWORD {
391 size = 4
392 }
393 testGetValue(t, k, test, size)
394 case registry.BINARY:
395 testGetBinaryValue(t, k, test)
396 _, gottype, err := k.GetStringsValue(test.Name)
397 testErrUnexpectedType(t, test, gottype, err)
398 _, _, err = k.GetBinaryValue(test.Name + "_byte_not_created")
399 testErrNotExist(t, test.Name+"_byte_not_created", err)
400 testGetValue(t, k, test, len(test.Value.([]byte)))
401 case registry.MULTI_SZ:
402 if test.WillFail {
403 _, _, err := k.GetStringsValue(test.Name)
404 testErrNotExist(t, test.Name, err)
405 } else {
406 testGetStringsValue(t, k, test)
407 _, gottype, err := k.GetStringValue(test.Name)
408 testErrUnexpectedType(t, test, gottype, err)
409 size := 0
410 for _, s := range test.Value.([]string) {
411 size += len(s) + 1
412 }
413 size += 1
414 size *= 2
415 testGetValue(t, k, test, size)
416 }
417 _, _, err := k.GetStringsValue(test.Name + "_strings_not_created")
418 testErrNotExist(t, test.Name+"_strings_not_created", err)
419 default:
420 t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
421 continue
422 }
423 }
424 }
425
426 func testStat(t *testing.T, k registry.Key) {
427 subk, _, err := registry.CreateKey(k, "subkey", registry.CREATE_SUB_KEY)
428 if err != nil {
429 t.Error(err)
430 return
431 }
432 defer subk.Close()
433
434 defer registry.DeleteKey(k, "subkey")
435
436 ki, err := k.Stat()
437 if err != nil {
438 t.Error(err)
439 return
440 }
441 if ki.SubKeyCount != 1 {
442 t.Error("key must have 1 subkey")
443 }
444 if ki.MaxSubKeyLen != 6 {
445 t.Error("key max subkey name length must be 6")
446 }
447 if ki.ValueCount != 24 {
448 t.Errorf("key must have 24 values, but is %d", ki.ValueCount)
449 }
450 if ki.MaxValueNameLen != 12 {
451 t.Errorf("key max value name length must be 10, but is %d", ki.MaxValueNameLen)
452 }
453 if ki.MaxValueLen != 38 {
454 t.Errorf("key max value length must be 38, but is %d", ki.MaxValueLen)
455 }
456 }
457
458 func deleteValues(t *testing.T, k registry.Key) {
459 for _, test := range ValueTests {
460 if test.WillFail {
461 continue
462 }
463 err := k.DeleteValue(test.Name)
464 if err != nil {
465 t.Error(err)
466 continue
467 }
468 }
469 names, err := k.ReadValueNames()
470 if err != nil {
471 t.Error(err)
472 return
473 }
474 if len(names) != 0 {
475 t.Errorf("some values remain after deletion: %v", names)
476 }
477 }
478
479 func TestValues(t *testing.T) {
480 softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
481 if err != nil {
482 t.Fatal(err)
483 }
484 defer softwareK.Close()
485
486 testKName := randKeyName("TestValues_")
487
488 k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
489 if err != nil {
490 t.Fatal(err)
491 }
492 defer k.Close()
493
494 if exist {
495 t.Fatalf("key %q already exists", testKName)
496 }
497
498 defer registry.DeleteKey(softwareK, testKName)
499
500 setValues(t, k)
501
502 enumerateValues(t, k)
503
504 testValues(t, k)
505
506 testStat(t, k)
507
508 deleteValues(t, k)
509 }
510
511 func TestExpandString(t *testing.T) {
512 got, err := registry.ExpandString("%PATH%")
513 if err != nil {
514 t.Fatal(err)
515 }
516 want := os.Getenv("PATH")
517 if got != want {
518 t.Errorf("want %q string expanded, got %q", want, got)
519 }
520 }
521
522 func TestInvalidValues(t *testing.T) {
523 softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
524 if err != nil {
525 t.Fatal(err)
526 }
527 defer softwareK.Close()
528
529 testKName := randKeyName("TestInvalidValues_")
530
531 k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
532 if err != nil {
533 t.Fatal(err)
534 }
535 defer k.Close()
536
537 if exist {
538 t.Fatalf("key %q already exists", testKName)
539 }
540
541 defer registry.DeleteKey(softwareK, testKName)
542
543 var tests = []struct {
544 Type uint32
545 Name string
546 Data []byte
547 }{
548 {registry.DWORD, "Dword1", nil},
549 {registry.DWORD, "Dword2", []byte{1, 2, 3}},
550 {registry.QWORD, "Qword1", nil},
551 {registry.QWORD, "Qword2", []byte{1, 2, 3}},
552 {registry.QWORD, "Qword3", []byte{1, 2, 3, 4, 5, 6, 7}},
553 {registry.MULTI_SZ, "MultiString1", nil},
554 {registry.MULTI_SZ, "MultiString2", []byte{0}},
555 {registry.MULTI_SZ, "MultiString3", []byte{'a', 'b', 0}},
556 {registry.MULTI_SZ, "MultiString4", []byte{'a', 0, 0, 'b', 0}},
557 {registry.MULTI_SZ, "MultiString5", []byte{'a', 0, 0}},
558 }
559
560 for _, test := range tests {
561 err := k.SetValue(test.Name, test.Type, test.Data)
562 if err != nil {
563 t.Fatalf("SetValue for %q failed: %v", test.Name, err)
564 }
565 }
566
567 for _, test := range tests {
568 switch test.Type {
569 case registry.DWORD, registry.QWORD:
570 value, valType, err := k.GetIntegerValue(test.Name)
571 if err == nil {
572 t.Errorf("GetIntegerValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
573 }
574 case registry.MULTI_SZ:
575 value, valType, err := k.GetStringsValue(test.Name)
576 if err == nil {
577 if len(value) != 0 {
578 t.Errorf("GetStringsValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
579 }
580 }
581 default:
582 t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
583 }
584 }
585 }
586
587 func TestGetMUIStringValue(t *testing.T) {
588 var dtzi DynamicTimezoneinformation
589 if _, err := GetDynamicTimeZoneInformation(&dtzi); err != nil {
590 t.Fatal(err)
591 }
592 tzKeyName := syscall.UTF16ToString(dtzi.TimeZoneKeyName[:])
593 timezoneK, err := registry.OpenKey(registry.LOCAL_MACHINE,
594 `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\`+tzKeyName, registry.READ)
595 if err != nil {
596 t.Fatal(err)
597 }
598 defer timezoneK.Close()
599
600 type testType struct {
601 name string
602 want string
603 }
604 var tests = []testType{
605 {"MUI_Std", syscall.UTF16ToString(dtzi.StandardName[:])},
606 }
607 if dtzi.DynamicDaylightTimeDisabled == 0 {
608 tests = append(tests, testType{"MUI_Dlt", syscall.UTF16ToString(dtzi.DaylightName[:])})
609 }
610
611 for _, test := range tests {
612 got, err := timezoneK.GetMUIStringValue(test.name)
613 if err != nil {
614 t.Error("GetMUIStringValue:", err)
615 }
616
617 if got != test.want {
618 t.Errorf("GetMUIStringValue: %s: Got %q, want %q", test.name, got, test.want)
619 }
620 }
621 }
622
623 type DynamicTimezoneinformation struct {
624 Bias int32
625 StandardName [32]uint16
626 StandardDate syscall.Systemtime
627 StandardBias int32
628 DaylightName [32]uint16
629 DaylightDate syscall.Systemtime
630 DaylightBias int32
631 TimeZoneKeyName [128]uint16
632 DynamicDaylightTimeDisabled uint8
633 }
634
635 var (
636 modkernel32 = syscall.NewLazyDLL("kernel32.dll")
637
638 procGetDynamicTimeZoneInformation = modkernel32.NewProc("GetDynamicTimeZoneInformation")
639 )
640
641 func GetDynamicTimeZoneInformation(dtzi *DynamicTimezoneinformation) (rc uint32, err error) {
642 r0, _, e1 := syscall.Syscall(procGetDynamicTimeZoneInformation.Addr(), 1, uintptr(unsafe.Pointer(dtzi)), 0, 0)
643 rc = uint32(r0)
644 if rc == 0xffffffff {
645 if e1 != 0 {
646 err = error(e1)
647 } else {
648 err = syscall.EINVAL
649 }
650 }
651 return
652 }
653
View as plain text