Source file src/internal/unsafeheader/unsafeheader_test.go

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

View as plain text