239 lines
6.5 KiB
Go
239 lines
6.5 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 TestParseParamsExported(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
names []string
|
|
types []string
|
|
isPtr []bool
|
|
}{
|
|
{
|
|
input: "dst []byte, src []byte",
|
|
names: []string{"dst", "src"},
|
|
types: []string{"[]byte", "[]byte"},
|
|
isPtr: []bool{true, true},
|
|
},
|
|
{
|
|
input: "dst, src []byte",
|
|
names: []string{"dst", "src"},
|
|
types: []string{"[]byte", "[]byte"},
|
|
isPtr: []bool{true, true},
|
|
},
|
|
{
|
|
input: "a, b int",
|
|
names: []string{"a", "b"},
|
|
types: []string{"int", "int"},
|
|
isPtr: []bool{false, false},
|
|
},
|
|
{
|
|
input: "src []byte, dst []byte",
|
|
names: []string{"src", "dst"},
|
|
types: []string{"[]byte", "[]byte"},
|
|
isPtr: []bool{true, true},
|
|
},
|
|
{
|
|
input: "swin []int32, dstP []uint32, hist *[32]uint16",
|
|
names: []string{"swin", "dstP", "hist"},
|
|
types: []string{"[]int32", "[]uint32", "*[32]uint16"},
|
|
isPtr: []bool{true, true, true},
|
|
},
|
|
{
|
|
input: "n int, code int",
|
|
names: []string{"n", "code"},
|
|
types: []string{"int", "int"},
|
|
isPtr: []bool{false, false},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
params := parseParamsExported(tt.input)
|
|
if len(params) != len(tt.names) {
|
|
t.Errorf("parseParamsExported(%q): got %d params, want %d", tt.input, len(params), len(tt.names))
|
|
continue
|
|
}
|
|
for i, p := range params {
|
|
if p.Name != tt.names[i] {
|
|
t.Errorf("parseParamsExported(%q)[%d].Name = %q, want %q", tt.input, i, p.Name, tt.names[i])
|
|
}
|
|
if p.Typ != tt.types[i] {
|
|
t.Errorf("parseParamsExported(%q)[%d].Typ = %q, want %q", tt.input, i, p.Typ, tt.types[i])
|
|
}
|
|
if p.IsPointer() != tt.isPtr[i] {
|
|
t.Errorf("parseParamsExported(%q)[%d].IsPointer() = %v, want %v", tt.input, i, p.IsPointer(), tt.isPtr[i])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseBufSpec(t *testing.T) {
|
|
t.Run("empty", func(t *testing.T) {
|
|
specs, err := ParseBufSpec("")
|
|
if err != nil || len(specs) != 0 {
|
|
t.Errorf("ParseBufSpec(\"\") = %v, %v; want nil, nil", specs, err)
|
|
}
|
|
})
|
|
|
|
t.Run("single", func(t *testing.T) {
|
|
specs, err := ParseBufSpec("dst:64:zero")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(specs) != 1 || specs[0].Name != "dst" || specs[0].Size != 64 || specs[0].Pattern != "zero" {
|
|
t.Errorf("ParseBufSpec(\"dst:64:zero\") = %+v; want [{dst 64 zero}]", specs)
|
|
}
|
|
})
|
|
|
|
t.Run("multiple", func(t *testing.T) {
|
|
specs, err := ParseBufSpec("dst:64:zero,src:128:seq")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(specs) != 2 {
|
|
t.Fatalf("got %d specs, want 2", len(specs))
|
|
}
|
|
if specs[0].Name != "dst" || specs[1].Name != "src" {
|
|
t.Errorf("names = %s, %s; want dst, src", specs[0].Name, specs[1].Name)
|
|
}
|
|
})
|
|
|
|
t.Run("invalid", func(t *testing.T) {
|
|
_, err := ParseBufSpec("bad")
|
|
if err == nil {
|
|
t.Error("ParseBufSpec(\"bad\") should error")
|
|
}
|
|
})
|
|
|
|
t.Run("zero-size", func(t *testing.T) {
|
|
_, err := ParseBufSpec("dst:0:zero")
|
|
if err == nil {
|
|
t.Error("ParseBufSpec(\"dst:0:zero\") should error on zero size")
|
|
}
|
|
})
|
|
|
|
t.Run("trailing-garbage-size", func(t *testing.T) {
|
|
// fmt.Sscanf("%d") accepted "1024abc" as 1024; the strict parse
|
|
// must reject the whole field.
|
|
if _, err := ParseBufSpec("dst:1024abc:zero"); err == nil {
|
|
t.Error("ParseBufSpec(\"dst:1024abc:zero\") should reject a size with trailing garbage")
|
|
}
|
|
})
|
|
|
|
t.Run("hex-size", func(t *testing.T) {
|
|
if _, err := ParseBufSpec("dst:0x40:zero"); err == nil {
|
|
t.Error("ParseBufSpec(\"dst:0x40:zero\") should reject a non-decimal size")
|
|
}
|
|
})
|
|
|
|
t.Run("unknown-pattern", func(t *testing.T) {
|
|
if _, err := ParseBufSpec("dst:64:wibble"); err == nil {
|
|
t.Error("ParseBufSpec(\"dst:64:wibble\") should reject an unknown pattern")
|
|
}
|
|
})
|
|
|
|
t.Run("bad-hex-pattern", func(t *testing.T) {
|
|
if _, err := ParseBufSpec("dst:64:zz"); err == nil {
|
|
t.Error("ParseBufSpec(\"dst:64:zz\") should reject undecodable hex")
|
|
}
|
|
})
|
|
|
|
t.Run("empty-pattern", func(t *testing.T) {
|
|
if _, err := ParseBufSpec("dst:64:"); err == nil {
|
|
t.Error("ParseBufSpec(\"dst:64:\") should reject an empty pattern")
|
|
}
|
|
})
|
|
|
|
t.Run("hex-pattern", func(t *testing.T) {
|
|
specs, err := ParseBufSpec("dst:6:deadbeef")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(specs) != 1 || specs[0].Pattern != "deadbeef" {
|
|
t.Errorf("ParseBufSpec(\"dst:6:deadbeef\") = %+v", specs)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBufPoolBuildArgs(t *testing.T) {
|
|
specs, err := ParseBufSpec("dst:64:seq,src:128:zero")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var pool BufPool
|
|
if err := pool.Alloc(specs); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer pool.Close()
|
|
|
|
// Layout for wideCopyAVX2(dst, src []byte): dst at 0, src at 24.
|
|
layout := []ArgOffset{
|
|
{Name: "dst", Typ: "[]byte", Offset: 0, Size: 24, IsPtr: true},
|
|
{Name: "src", Typ: "[]byte", Offset: 24, Size: 24, IsPtr: true},
|
|
}
|
|
args := pool.BuildArgs(layout, 48)
|
|
|
|
// dst.ptr should be non-zero.
|
|
if args[0] == 0 && args[1] == 0 && args[2] == 0 && args[3] == 0 {
|
|
t.Error("dst.ptr is zero; expected a buffer address")
|
|
}
|
|
// dst.len should be 64 (0x40).
|
|
if args[8] != 0x40 {
|
|
t.Errorf("dst.len = %d, want 64", args[8])
|
|
}
|
|
// dst.cap should be 64.
|
|
if args[16] != 0x40 {
|
|
t.Errorf("dst.cap = %d, want 64", args[16])
|
|
}
|
|
// src.ptr should be non-zero.
|
|
if args[24] == 0 && args[25] == 0 && args[26] == 0 && args[27] == 0 {
|
|
t.Error("src.ptr is zero; expected a buffer address")
|
|
}
|
|
// src.len should be 128 (0x80).
|
|
if args[32] != 0x80 {
|
|
t.Errorf("src.len = %d, want 128", args[32])
|
|
}
|
|
}
|
|
|
|
func TestParseScalarArgs(t *testing.T) {
|
|
m, err := ParseScalarArgs("n=57,base=0x1f,x= 12 ")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if m["n"] != 57 || m["base"] != 0x1f || m["x"] != 12 {
|
|
t.Fatalf("parsed %+v", m)
|
|
}
|
|
if _, err := ParseScalarArgs("n"); err == nil {
|
|
t.Error("name-only spec must fail")
|
|
}
|
|
if _, err := ParseScalarArgs("n=zz"); err == nil {
|
|
t.Error("bad value must fail")
|
|
}
|
|
}
|
|
|
|
func TestApplyScalarArgs(t *testing.T) {
|
|
layout := []ArgOffset{
|
|
{Name: "text", Typ: "*byte", Offset: 0, Size: 8, IsPtr: true},
|
|
{Name: "n", Typ: "int", Offset: 8, Size: 8, IsPtr: false},
|
|
}
|
|
args := make([]byte, 16)
|
|
if err := ApplyScalarArgs(args, layout, map[string]uint64{"n": 0x1f}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := args[8]; got != 0x1f {
|
|
t.Fatalf("args[8] = %d, want 31", got)
|
|
}
|
|
// Pointer parameters reject scalar writes.
|
|
if err := ApplyScalarArgs(args, layout, map[string]uint64{"text": 5}); err == nil {
|
|
t.Fatal("pointer param must reject a scalar write")
|
|
}
|
|
// Unknown names reject.
|
|
if err := ApplyScalarArgs(args, layout, map[string]uint64{"nope": 1}); err == nil {
|
|
t.Fatal("unknown param must reject")
|
|
}
|
|
}
|