154 lines
3.8 KiB
Go
154 lines
3.8 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|||
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
|
||
|
|
package verify
|
||
|
|
|
||
|
|
import (
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestParseFuncSigExported(t *testing.T) {
|
||
|
|
tests := []struct {
|
||
|
|
comment string
|
||
|
|
wantName string
|
||
|
|
nParams int
|
||
|
|
nResults int
|
||
|
|
}{
|
||
|
|
{"// func add(a int64, b int64) int64", "add", 2, 1},
|
||
|
|
{"// func wideCopy(dst []byte, src []byte)", "wideCopy", 2, 0},
|
||
|
|
{"// func decodeBlockAVX2(src, dst []byte) (n int, code int)", "decodeBlockAVX2", 2, 2},
|
||
|
|
{"// func analyzeO1RangeAVX2(swin []int32, dstP []uint32, hist *[32]uint16) (partSum uint64, overflow bool)", "analyzeO1RangeAVX2", 3, 2},
|
||
|
|
{"// not a func", "", 0, 0},
|
||
|
|
{"// func", "", 0, 0},
|
||
|
|
}
|
||
|
|
for _, tt := range tests {
|
||
|
|
sig, ok := ParseFuncSig(tt.comment)
|
||
|
|
if tt.wantName == "" {
|
||
|
|
if ok {
|
||
|
|
t.Errorf("ParseFuncSig(%q): expected not ok", tt.comment)
|
||
|
|
}
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if !ok {
|
||
|
|
t.Errorf("ParseFuncSig(%q): expected ok", tt.comment)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if sig.Name != tt.wantName {
|
||
|
|
t.Errorf("ParseFuncSig(%q).Name = %q, want %q", tt.comment, sig.Name, tt.wantName)
|
||
|
|
}
|
||
|
|
if len(sig.Params) != tt.nParams {
|
||
|
|
t.Errorf("ParseFuncSig(%q): %d params, want %d", tt.comment, len(sig.Params), tt.nParams)
|
||
|
|
}
|
||
|
|
if len(sig.Results) != tt.nResults {
|
||
|
|
t.Errorf("ParseFuncSig(%q): %d results, want %d", tt.comment, len(sig.Results), tt.nResults)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestExtractFuncSig(t *testing.T) {
|
||
|
|
src := `// func add(a int64, b int64) int64
|
||
|
|
TEXT ·add(SB), NOSPLIT, $0-24
|
||
|
|
|
||
|
|
// func wideCopyAVX2(dst, src []byte)
|
||
|
|
TEXT ·wideCopyAVX2(SB), NOSPLIT, $0-48
|
||
|
|
`
|
||
|
|
sig, ok := ExtractFuncSig(src, "add")
|
||
|
|
if !ok {
|
||
|
|
t.Fatal("ExtractFuncSig(add): not found")
|
||
|
|
}
|
||
|
|
if sig.Name != "add" {
|
||
|
|
t.Errorf("Name = %q, want %q", sig.Name, "add")
|
||
|
|
}
|
||
|
|
if len(sig.Params) != 2 {
|
||
|
|
t.Errorf("params = %d, want 2", len(sig.Params))
|
||
|
|
}
|
||
|
|
|
||
|
|
sig, ok = ExtractFuncSig(src, "wideCopyAVX2")
|
||
|
|
if !ok {
|
||
|
|
t.Fatal("ExtractFuncSig(wideCopyAVX2): not found")
|
||
|
|
}
|
||
|
|
if len(sig.Params) != 2 {
|
||
|
|
t.Errorf("params = %d, want 2", len(sig.Params))
|
||
|
|
}
|
||
|
|
|
||
|
|
_, ok = ExtractFuncSig(src, "nonexistent")
|
||
|
|
if ok {
|
||
|
|
t.Error("ExtractFuncSig(nonexistent): expected not found")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestArgLayout(t *testing.T) {
|
||
|
|
sig := FuncSig{
|
||
|
|
Name: "wideCopyAVX2",
|
||
|
|
Params: []Param{
|
||
|
|
{Name: "dst", Typ: "[]byte"},
|
||
|
|
{Name: "src", Typ: "[]byte"},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
layout := ArgLayout(sig)
|
||
|
|
if len(layout) != 2 {
|
||
|
|
t.Fatalf("ArgLayout: %d entries, want 2", len(layout))
|
||
|
|
}
|
||
|
|
// dst: offset 0, size 24 (slice)
|
||
|
|
if layout[0].Name != "dst" || layout[0].Offset != 0 || layout[0].Size != 24 || !layout[0].IsPtr {
|
||
|
|
t.Errorf("layout[0] = %+v", layout[0])
|
||
|
|
}
|
||
|
|
// src: offset 24, size 24 (slice)
|
||
|
|
if layout[1].Name != "src" || layout[1].Offset != 24 || layout[1].Size != 24 || !layout[1].IsPtr {
|
||
|
|
t.Errorf("layout[1] = %+v", layout[1])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestArgLayoutMixed(t *testing.T) {
|
||
|
|
sig := FuncSig{
|
||
|
|
Name: "decodeBlockAVX2",
|
||
|
|
Params: []Param{
|
||
|
|
{Name: "src", Typ: "[]byte"},
|
||
|
|
{Name: "dst", Typ: "[]byte"},
|
||
|
|
},
|
||
|
|
Results: []Param{
|
||
|
|
{Name: "n", Typ: "int"},
|
||
|
|
{Name: "code", Typ: "int"},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
layout := ArgLayout(sig)
|
||
|
|
if len(layout) != 2 {
|
||
|
|
t.Fatalf("ArgLayout: %d entries, want 2", len(layout))
|
||
|
|
}
|
||
|
|
// Two slices: 24 + 24 = 48 bytes of params.
|
||
|
|
if layout[1].Offset != 24 {
|
||
|
|
t.Errorf("src offset = %d, want 24", layout[1].Offset)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestParamSize(t *testing.T) {
|
||
|
|
tests := []struct {
|
||
|
|
typ string
|
||
|
|
want int
|
||
|
|
}{
|
||
|
|
{"[]byte", 24},
|
||
|
|
{"[]int32", 24},
|
||
|
|
{"*uint16", 8},
|
||
|
|
{"*[32]uint16", 8},
|
||
|
|
{"int", 8},
|
||
|
|
{"int64", 8},
|
||
|
|
{"uint64", 8},
|
||
|
|
{"uintptr", 8},
|
||
|
|
{"int32", 4},
|
||
|
|
{"uint32", 4},
|
||
|
|
{"float32", 4},
|
||
|
|
{"int16", 2},
|
||
|
|
{"uint16", 2},
|
||
|
|
{"int8", 1},
|
||
|
|
{"uint8", 1},
|
||
|
|
{"byte", 1},
|
||
|
|
{"bool", 1},
|
||
|
|
{"string", 8}, // unknown type defaults to 8
|
||
|
|
}
|
||
|
|
for _, tt := range tests {
|
||
|
|
if got := paramSize(tt.typ); got != tt.want {
|
||
|
|
t.Errorf("paramSize(%q) = %d, want %d", tt.typ, got, tt.want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|