| // 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) |
| } |
| }) |
| } |