Files
sys/internal/unsafeheader/unsafeheader_test.go
Bryan C. Mills d923437fa5 unix: use a type equivalent to reflect.SliceHeader to manipulate slices
The regression test included in this change verifies that the type is,
in fact, equivalent, while allowing the actual header definitions to
avoid importing the (relatively heavy) "reflect" package itself.

This change is loosely based on Keyan Pishdadian's draft in CL 230557.

For golang/go#37805

Change-Id: I998c69cdeb852154cd66ab5fdaa542a6f19666a2
Reviewed-on: https://go-review.googlesource.com/c/sys/+/231177
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
2020-04-30 20:27:03 +00:00

102 lines
2.9 KiB
Go

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