github.com/go-darwin/sys@v0.0.0-20220510002607-68fd01f054ca/unsafeheader/unsafeheader_test.go (about) 1 // Copyright 2020 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. 4 5 package unsafeheader_test 6 7 import ( 8 "bytes" 9 "reflect" 10 "testing" 11 "unsafe" 12 13 "github.com/go-darwin/sys/unsafeheader" 14 ) 15 16 // TestTypeMatchesReflectType ensures that the name and layout of the 17 // unsafeheader types matches the corresponding Header types in the reflect 18 // package. 19 func TestTypeMatchesReflectType(t *testing.T) { 20 t.Run("Slice", func(t *testing.T) { 21 testHeaderMatchesReflect(t, unsafeheader.Slice{}, reflect.SliceHeader{}) 22 }) 23 24 t.Run("String", func(t *testing.T) { 25 testHeaderMatchesReflect(t, unsafeheader.String{}, reflect.StringHeader{}) 26 }) 27 } 28 29 func testHeaderMatchesReflect(t *testing.T, header, reflectHeader interface{}) { 30 h := reflect.TypeOf(header) 31 rh := reflect.TypeOf(reflectHeader) 32 33 for i := 0; i < h.NumField(); i++ { 34 f := h.Field(i) 35 rf, ok := rh.FieldByName(f.Name) 36 if !ok { 37 t.Errorf("Field %d of %v is named %s, but no such field exists in %v", i, h, f.Name, rh) 38 continue 39 } 40 if !typeCompatible(f.Type, rf.Type) { 41 t.Errorf("%v.%s has type %v, but %v.%s has type %v", h, f.Name, f.Type, rh, rf.Name, rf.Type) 42 } 43 if f.Offset != rf.Offset { 44 t.Errorf("%v.%s has offset %d, but %v.%s has offset %d", h, f.Name, f.Offset, rh, rf.Name, rf.Offset) 45 } 46 } 47 48 if h.NumField() != rh.NumField() { 49 t.Errorf("%v has %d fields, but %v has %d", h, h.NumField(), rh, rh.NumField()) 50 } 51 if h.Align() != rh.Align() { 52 t.Errorf("%v has alignment %d, but %v has alignment %d", h, h.Align(), rh, rh.Align()) 53 } 54 } 55 56 var ( 57 unsafePointerType = reflect.TypeOf(unsafe.Pointer(nil)) 58 uintptrType = reflect.TypeOf(uintptr(0)) 59 ) 60 61 func typeCompatible(t, rt reflect.Type) bool { 62 return t == rt || (t == unsafePointerType && rt == uintptrType) 63 } 64 65 // TestWriteThroughHeader ensures that the headers in the unsafeheader package 66 // can successfully mutate variables of the corresponding built-in types. 67 // 68 // This test is expected to fail under -race (which implicitly enables 69 // -d=checkptr) if the runtime views the header types as incompatible with the 70 // underlying built-in types. 71 func TestWriteThroughHeader(t *testing.T) { 72 t.Run("Slice", func(t *testing.T) { 73 s := []byte("Hello, checkptr!")[:5] 74 75 var alias []byte 76 hdr := (*unsafeheader.Slice)(unsafe.Pointer(&alias)) 77 hdr.Data = unsafe.Pointer(&s[0]) 78 hdr.Cap = cap(s) 79 hdr.Len = len(s) 80 81 if !bytes.Equal(alias, s) { 82 t.Errorf("alias of %T(%q) constructed via Slice = %T(%q)", s, s, alias, alias) 83 } 84 if cap(alias) != cap(s) { 85 t.Errorf("alias of %T with cap %d has cap %d", s, cap(s), cap(alias)) 86 } 87 }) 88 89 t.Run("String", func(t *testing.T) { 90 s := "Hello, checkptr!" 91 92 var alias string 93 hdr := (*unsafeheader.String)(unsafe.Pointer(&alias)) 94 hdr.Data = (*unsafeheader.String)(unsafe.Pointer(&s)).Data 95 hdr.Len = len(s) 96 97 if alias != s { 98 t.Errorf("alias of %q constructed via String = %q", s, alias) 99 } 100 }) 101 }