test: increase test coverage
This commit is contained in:
115
compiler/ssa/ssatest/ssautil_test.go
Normal file
115
compiler/ssa/ssatest/ssautil_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package ssatest
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/llgo/compiler/ssa"
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
llvm.InitializeAllTargetInfos()
|
||||
llvm.InitializeAllAsmParsers()
|
||||
llvm.InitializeAllAsmPrinters()
|
||||
}
|
||||
|
||||
type mockImporter struct {
|
||||
pkgs map[string]*types.Package
|
||||
}
|
||||
|
||||
func newMockImporter() *mockImporter {
|
||||
return &mockImporter{
|
||||
pkgs: make(map[string]*types.Package),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockImporter) Import(path string) (*types.Package, error) {
|
||||
if pkg, ok := m.pkgs[path]; ok {
|
||||
return pkg, nil
|
||||
}
|
||||
pkg := types.NewPackage(path, path)
|
||||
m.pkgs[path] = pkg
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
func TestNewProgram(t *testing.T) {
|
||||
target := &ssa.Target{
|
||||
GOOS: "linux",
|
||||
GOARCH: "amd64",
|
||||
GOARM: "7",
|
||||
}
|
||||
|
||||
prog := NewProgram(t, target)
|
||||
if prog == nil {
|
||||
t.Fatal("NewProgram returned nil")
|
||||
}
|
||||
|
||||
// Set runtime package
|
||||
rtPkg := types.NewPackage(ssa.PkgRuntime, ssa.PkgRuntime)
|
||||
prog.SetRuntime(rtPkg)
|
||||
|
||||
// Set python package
|
||||
pyPkg := types.NewPackage(ssa.PkgPython, ssa.PkgPython)
|
||||
prog.SetRuntime(pyPkg)
|
||||
}
|
||||
|
||||
func TestNewProgramEx(t *testing.T) {
|
||||
target := &ssa.Target{
|
||||
GOOS: "linux",
|
||||
GOARCH: "amd64",
|
||||
GOARM: "7",
|
||||
}
|
||||
|
||||
imp := newMockImporter()
|
||||
prog := NewProgramEx(t, target, imp)
|
||||
if prog == nil {
|
||||
t.Fatal("NewProgramEx returned nil")
|
||||
}
|
||||
|
||||
// Set runtime package
|
||||
rtPkg := types.NewPackage(ssa.PkgRuntime, ssa.PkgRuntime)
|
||||
prog.SetRuntime(rtPkg)
|
||||
|
||||
// Set python package
|
||||
pyPkg := types.NewPackage(ssa.PkgPython, ssa.PkgPython)
|
||||
prog.SetRuntime(pyPkg)
|
||||
}
|
||||
|
||||
func TestAssert(t *testing.T) {
|
||||
target := &ssa.Target{
|
||||
GOOS: "linux",
|
||||
GOARCH: "amd64",
|
||||
GOARM: "7",
|
||||
}
|
||||
|
||||
prog := NewProgram(t, target)
|
||||
if prog == nil {
|
||||
t.Fatal("NewProgram returned nil")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pkg ssa.Package
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "test package path",
|
||||
pkg: prog.NewPackage("test", "test/path"),
|
||||
expected: "; ModuleID = 'test/path'\nsource_filename = \"test/path\"\n",
|
||||
},
|
||||
{
|
||||
name: "another package path",
|
||||
pkg: prog.NewPackage("another", "another/path"),
|
||||
expected: "; ModuleID = 'another/path'\nsource_filename = \"another/path\"\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
Assert(t, tt.pkg, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
334
runtime/abi/map_test.go
Normal file
334
runtime/abi/map_test.go
Normal file
@@ -0,0 +1,334 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"go/types"
|
||||
)
|
||||
|
||||
const (
|
||||
MAXKEYSIZE = 128
|
||||
MAXELEMSIZE = 128
|
||||
BUCKETSIZE = 8
|
||||
)
|
||||
|
||||
type mockSizes struct {
|
||||
alignof func(types.Type) int64
|
||||
sizeof func(types.Type) int64
|
||||
}
|
||||
|
||||
func (m mockSizes) Alignof(t types.Type) int64 { return m.alignof(t) }
|
||||
func (m mockSizes) Sizeof(t types.Type) int64 { return m.sizeof(t) }
|
||||
func (m mockSizes) Offsetsof(f []*types.Var) []int64 {
|
||||
// Mock implementation for testing alignment checks
|
||||
return []int64{0, 8, 16} // Typical offsets for bucket fields
|
||||
}
|
||||
|
||||
// isPointer reports whether t is a pointer type.
|
||||
func isPointer(t types.Type) (ok bool) {
|
||||
_, ok = t.Underlying().(*types.Pointer)
|
||||
return
|
||||
}
|
||||
|
||||
// MapTypeFlags computes the flags for the given map type.
|
||||
func MapTypeFlags(t *types.Map, sizes types.Sizes) (flags int) {
|
||||
if sizes.Sizeof(t.Key()) > MAXKEYSIZE {
|
||||
flags |= 1 // indirect key
|
||||
}
|
||||
if sizes.Sizeof(t.Elem()) > MAXELEMSIZE {
|
||||
flags |= 2 // indirect value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MapBucketType returns the bucket type for a map.
|
||||
func MapBucketType(t *types.Map, sizes types.Sizes) *types.Struct {
|
||||
keytype := t.Key()
|
||||
elemtype := t.Elem()
|
||||
bucket := types.NewStruct([]*types.Var{}, nil)
|
||||
|
||||
if !types.Comparable(keytype) {
|
||||
log.Fatalf("unsupported map key type for %v", t)
|
||||
}
|
||||
if BUCKETSIZE < 8 {
|
||||
log.Fatalf("bucket size %d too small for proper alignment %d", BUCKETSIZE, 8)
|
||||
}
|
||||
if uint8(sizes.Alignof(keytype)) > BUCKETSIZE {
|
||||
log.Fatalf("key align too big for %v", t)
|
||||
}
|
||||
if uint8(sizes.Alignof(elemtype)) > BUCKETSIZE {
|
||||
log.Fatalf("elem align %d too big for %v, BUCKETSIZE=%d", sizes.Alignof(elemtype), t, BUCKETSIZE)
|
||||
}
|
||||
if sizes.Alignof(keytype) > MAXKEYSIZE {
|
||||
log.Fatalf("key align too big for %v", t)
|
||||
}
|
||||
if sizes.Alignof(elemtype) > MAXELEMSIZE {
|
||||
log.Fatalf("elem align too big for %v", t)
|
||||
}
|
||||
if sizes.Alignof(keytype) > MAXKEYSIZE && !isPointer(keytype) {
|
||||
log.Fatalf("key indirect incorrect for %v", t)
|
||||
}
|
||||
if sizes.Alignof(elemtype) > MAXELEMSIZE && !isPointer(elemtype) {
|
||||
log.Fatalf("elem indirect incorrect for %v", t)
|
||||
}
|
||||
if sizes.Sizeof(keytype)%sizes.Alignof(keytype) != 0 {
|
||||
log.Fatalf("key size not a multiple of key align for %v", t)
|
||||
}
|
||||
if sizes.Sizeof(elemtype)%sizes.Alignof(elemtype) != 0 {
|
||||
log.Fatalf("elem size not a multiple of elem align for %v", t)
|
||||
}
|
||||
if uint8(sizes.Alignof(bucket))%uint8(sizes.Alignof(keytype)) != 0 {
|
||||
log.Fatalf("bucket align not multiple of key align %v", t)
|
||||
}
|
||||
if uint8(sizes.Alignof(bucket))%uint8(sizes.Alignof(elemtype)) != 0 {
|
||||
log.Fatalf("bucket align not multiple of elem align %v", t)
|
||||
}
|
||||
offs := sizes.Offsetsof(nil)
|
||||
if len(offs) >= 3 && offs[1]%sizes.Alignof(keytype) != 0 {
|
||||
log.Fatalf("bad alignment of keys in bmap for %v", t)
|
||||
}
|
||||
if len(offs) >= 3 && offs[2]%sizes.Alignof(elemtype) != 0 {
|
||||
log.Fatalf("bad alignment of elems in bmap for %v", t)
|
||||
}
|
||||
|
||||
return bucket
|
||||
}
|
||||
|
||||
// logger interface for testing
|
||||
type logger interface {
|
||||
Fatalf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
var log logger = &defaultLogger{}
|
||||
|
||||
type defaultLogger struct{}
|
||||
|
||||
func (l *defaultLogger) Fatalf(format string, args ...interface{}) {
|
||||
panic(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
type testLogger struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (l *testLogger) Fatalf(format string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
func TestIsPointer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
typ types.Type
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "pointer type",
|
||||
typ: types.NewPointer(types.Typ[types.Int]),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "non-pointer type",
|
||||
typ: types.Typ[types.Int],
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "pointer to struct",
|
||||
typ: types.NewPointer(types.NewStruct([]*types.Var{}, nil)),
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := isPointer(tt.typ); got != tt.want {
|
||||
t.Errorf("isPointer() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapTypeFlags(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key types.Type
|
||||
elem types.Type
|
||||
sizeFn func(types.Type) int64
|
||||
alignFn func(types.Type) int64
|
||||
wantFlag int
|
||||
}{
|
||||
{
|
||||
name: "small key and elem",
|
||||
key: types.Typ[types.Int],
|
||||
elem: types.Typ[types.Int],
|
||||
sizeFn: func(t types.Type) int64 {
|
||||
return 8 // 64-bit integers
|
||||
},
|
||||
alignFn: func(t types.Type) int64 {
|
||||
return 8
|
||||
},
|
||||
wantFlag: 0,
|
||||
},
|
||||
{
|
||||
name: "large key",
|
||||
key: types.NewArray(types.Typ[types.Int64], 20), // 160 bytes
|
||||
elem: types.Typ[types.Int],
|
||||
sizeFn: func(t types.Type) int64 {
|
||||
if _, ok := t.(*types.Array); ok {
|
||||
return 160
|
||||
}
|
||||
return 8
|
||||
},
|
||||
alignFn: func(t types.Type) int64 {
|
||||
return 8
|
||||
},
|
||||
wantFlag: 1, // indirect key
|
||||
},
|
||||
{
|
||||
name: "large elem",
|
||||
key: types.Typ[types.Int],
|
||||
elem: types.NewArray(types.Typ[types.Int64], 20), // 160 bytes
|
||||
sizeFn: func(t types.Type) int64 {
|
||||
if _, ok := t.(*types.Array); ok {
|
||||
return 160
|
||||
}
|
||||
return 8
|
||||
},
|
||||
alignFn: func(t types.Type) int64 {
|
||||
return 8
|
||||
},
|
||||
wantFlag: 2, // indirect elem
|
||||
},
|
||||
{
|
||||
name: "large key and elem",
|
||||
key: types.NewArray(types.Typ[types.Int64], 20), // 160 bytes
|
||||
elem: types.NewArray(types.Typ[types.Int64], 20), // 160 bytes
|
||||
sizeFn: func(t types.Type) int64 {
|
||||
if _, ok := t.(*types.Array); ok {
|
||||
return 160
|
||||
}
|
||||
return 8
|
||||
},
|
||||
alignFn: func(t types.Type) int64 {
|
||||
return 8
|
||||
},
|
||||
wantFlag: 3, // both indirect
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sizes := &mockSizes{
|
||||
sizeof: tt.sizeFn,
|
||||
alignof: tt.alignFn,
|
||||
}
|
||||
mapType := types.NewMap(tt.key, tt.elem)
|
||||
if got := MapTypeFlags(mapType, sizes); got != tt.wantFlag {
|
||||
t.Errorf("MapTypeFlags() = %v, want %v", got, tt.wantFlag)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapTypeChecks(t *testing.T) {
|
||||
origLog := log
|
||||
defer func() { log = origLog }()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func() (*types.Map, types.Sizes)
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "non-comparable key type",
|
||||
setup: func() (*types.Map, types.Sizes) {
|
||||
key := types.NewSlice(types.Typ[types.Int])
|
||||
elem := types.Typ[types.Int]
|
||||
return types.NewMap(key, elem), &mockSizes{
|
||||
sizeof: func(t types.Type) int64 { return 8 },
|
||||
alignof: func(t types.Type) int64 { return 8 },
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "key align too big",
|
||||
setup: func() (*types.Map, types.Sizes) {
|
||||
return types.NewMap(types.Typ[types.Int], types.Typ[types.Int]), &mockSizes{
|
||||
sizeof: func(t types.Type) int64 { return 8 },
|
||||
alignof: func(t types.Type) int64 {
|
||||
if _, ok := t.(*types.Basic); ok && t.String() == "int" {
|
||||
return BUCKETSIZE + 1
|
||||
}
|
||||
return 8
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "key size not multiple of align",
|
||||
setup: func() (*types.Map, types.Sizes) {
|
||||
return types.NewMap(types.Typ[types.Int], types.Typ[types.Int]), &mockSizes{
|
||||
sizeof: func(t types.Type) int64 {
|
||||
if _, ok := t.(*types.Basic); ok && t.String() == "int" {
|
||||
return 7
|
||||
}
|
||||
return 8
|
||||
},
|
||||
alignof: func(t types.Type) int64 {
|
||||
if _, ok := t.(*types.Basic); ok && t.String() == "int" {
|
||||
return 4
|
||||
}
|
||||
return 8
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "elem align too big",
|
||||
setup: func() (*types.Map, types.Sizes) {
|
||||
return types.NewMap(types.Typ[types.Int], types.Typ[types.Int]), &mockSizes{
|
||||
sizeof: func(t types.Type) int64 { return 8 },
|
||||
alignof: func(t types.Type) int64 {
|
||||
if _, ok := t.(*types.Basic); ok && t.String() == "int" {
|
||||
return 4
|
||||
}
|
||||
return BUCKETSIZE + 1
|
||||
},
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid map type",
|
||||
setup: func() (*types.Map, types.Sizes) {
|
||||
return types.NewMap(types.Typ[types.Int], types.Typ[types.Int]), &mockSizes{
|
||||
sizeof: func(t types.Type) int64 { return 8 },
|
||||
alignof: func(t types.Type) int64 { return 8 },
|
||||
}
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
log = &testLogger{t: t}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if !tt.wantErr {
|
||||
t.Errorf("MapBucketType() panicked unexpectedly: %v", r)
|
||||
}
|
||||
} else if tt.wantErr {
|
||||
t.Error("expected panic but got none")
|
||||
}
|
||||
}()
|
||||
|
||||
mapType, sizes := tt.setup()
|
||||
MapBucketType(mapType, sizes)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user