| // Copyright 2020 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package unsafeheader_test | |
| import ( | |
| "bytes" | |
| "reflect" | |
| "testing" | |
| "unsafe" | |
| "golang.org/x/sys/internal/unsafeheader" | |
| ) | |
| // TestTypeMatchesReflectType ensures that the name and layout of the | |
| // unsafeheader types matches the corresponding Header types in the reflect | |
| // package. | |
| func TestTypeMatchesReflectType(t *testing.T) { | |
| t.Run("Slice", func(t *testing.T) { | |
| testHeaderMatchesReflect(t, unsafeheader.Slice{}, reflect.SliceHeader{}) | |
| }) | |
| t.Run("String", func(t *testing.T) { | |
| testHeaderMatchesReflect(t, unsafeheader.String{}, reflect.StringHeader{}) | |
| }) | |
| } | |
| func testHeaderMatchesReflect(t *testing.T, header, reflectHeader interface{}) { | |
| h := reflect.TypeOf(header) | |
| rh := reflect.TypeOf(reflectHeader) | |
| for i := 0; i < h.NumField(); i++ { | |
| f := h.Field(i) | |
| rf, ok := rh.FieldByName(f.Name) | |
| if !ok { | |
| t.Errorf("Field %d of %v is named %s, but no such field exists in %v", i, h, f.Name, rh) | |
| continue | |
| } | |
| if !typeCompatible(f.Type, rf.Type) { | |
| t.Errorf("%v.%s has type %v, but %v.%s has type %v", h, f.Name, f.Type, rh, rf.Name, rf.Type) | |
| } | |
| if f.Offset != rf.Offset { | |
| t.Errorf("%v.%s has offset %d, but %v.%s has offset %d", h, f.Name, f.Offset, rh, rf.Name, rf.Offset) | |
| } | |
| } | |
| if h.NumField() != rh.NumField() { | |
| t.Errorf("%v has %d fields, but %v has %d", h, h.NumField(), rh, rh.NumField()) | |
| } | |
| if h.Align() != rh.Align() { | |
| t.Errorf("%v has alignment %d, but %v has alignment %d", h, h.Align(), rh, rh.Align()) | |
| } | |
| } | |
| var ( | |
| unsafePointerType = reflect.TypeOf(unsafe.Pointer(nil)) | |
| uintptrType = reflect.TypeOf(uintptr(0)) | |
| ) | |
| func typeCompatible(t, rt reflect.Type) bool { | |
| return t == rt || (t == unsafePointerType && rt == uintptrType) | |
| } | |
| // TestWriteThroughHeader ensures that the headers in the unsafeheader package | |
| // can successfully mutate variables of the corresponding built-in types. | |
| // | |
| // This test is expected to fail under -race (which implicitly enables | |
| // -d=checkptr) if the runtime views the header types as incompatible with the | |
| // underlying built-in types. | |
| func TestWriteThroughHeader(t *testing.T) { | |
| t.Run("Slice", func(t *testing.T) { | |
| s := []byte("Hello, checkptr!")[:5] | |
| var alias []byte | |
| hdr := (*unsafeheader.Slice)(unsafe.Pointer(&alias)) | |
| hdr.Data = unsafe.Pointer(&s[0]) | |
| hdr.Cap = cap(s) | |
| hdr.Len = len(s) | |
| if !bytes.Equal(alias, s) { | |
| t.Errorf("alias of %T(%q) constructed via Slice = %T(%q)", s, s, alias, alias) | |
| } | |
| if cap(alias) != cap(s) { | |
| t.Errorf("alias of %T with cap %d has cap %d", s, cap(s), cap(alias)) | |
| } | |
| }) | |
| t.Run("String", func(t *testing.T) { | |
| s := "Hello, checkptr!" | |
| var alias string | |
| hdr := (*unsafeheader.String)(unsafe.Pointer(&alias)) | |
| hdr.Data = (*unsafeheader.String)(unsafe.Pointer(&s)).Data | |
| hdr.Len = len(s) | |
| if alias != s { | |
| t.Errorf("alias of %q constructed via String = %q", s, alias) | |
| } | |
| }) | |
| } |