From ebd8ab8a3c18edda84b1fe0d368802f1e0636105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Fri, 7 Aug 2026 21:28:24 +0200 Subject: [PATCH] test: gate go-libraries integration tests behind -tags=integration Assisted-by: GLM 5.2 --- asm/evex_test.go | 82 ------------------- asm/integration_test.go | 159 +++++++++++++++++++++++++++++++++++++ asm/link_test.go | 66 --------------- format/format_test.go | 5 +- format/integration_test.go | 40 ++++++++++ lint/integration_test.go | 44 ++++++++++ lint/lint_test.go | 27 ------- parser/integration_test.go | 39 +++++++++ parser/parser_test.go | 26 ------ verify/fuzz_extra_test.go | 115 +++++++++++++++++++++++++++ 10 files changed, 398 insertions(+), 205 deletions(-) create mode 100644 asm/integration_test.go create mode 100644 format/integration_test.go create mode 100644 lint/integration_test.go create mode 100644 parser/integration_test.go diff --git a/asm/evex_test.go b/asm/evex_test.go index cd2ca09..0c4f316 100644 --- a/asm/evex_test.go +++ b/asm/evex_test.go @@ -4,13 +4,10 @@ package asm import ( - "os" "strings" "testing" "golang.org/x/arch/x86/x86asm" - - "sourcedock.dev/petrbalvin/gasm-devkit/parser" ) // TestEvexGroundTruth checks the EVEX (AVX-512) encodings byte for byte @@ -649,71 +646,6 @@ func TestEvexErrors(t *testing.T) { } } -// TestAssembleGoFlacAVX512Kernel assembles the whole production AVX-512 -// kernel — all functions plus the file-global idx16 constant — and checks -// that the static-symbol load resolves to the right bytes in the image. -// Skipped when the sibling repository is not checked out. -func TestAssembleGoFlacAVX512Kernel(t *testing.T) { - path := "../../go-libraries/go-flac/avx512_amd64.s" - if _, err := os.Stat(path); err != nil { - t.Skip("go-libraries repository not present next to gasm-devkit") - } - src, err := os.ReadFile(path) - if err != nil { - t.Fatal(err) - } - f, errs := parser.Parse(path, string(src)) - if len(errs) > 0 { - t.Fatalf("parse: %v", errs) - } - img, err := AssembleFile(f) - if err != nil { - t.Fatalf("AssembleFile: %v", err) - } - if len(img.Funcs) != 10 { - t.Errorf("functions = %d, want 10", len(img.Funcs)) - } - - // idx16 as the DATA directives define it: dwords 1..16. - idx := make([]byte, 0, 64) - for i := 1; i <= 16; i++ { - idx = append(idx, byte(i), 0, 0, 0) - } - image := img.Bytes() - base := img.Symbols["idx16"] - if base == 0 { - t.Fatal("idx16 not laid out") - } - if got := image[base : base+64]; hexCompact(got) != hexCompact(idx) { - t.Errorf("idx16 contents %x, want %x", got, idx) - } - - // The VMOVDQU32 idx16(SB), Z13 load (62 71 7e 48 6f 2d + rel32) must - // resolve to idx16 within the image. - loads := 0 - for _, fn := range img.Funcs { - code := img.Code[fn.Offset : fn.Offset+fn.Size] - pat := []byte{0x62, 0x71, 0x7e, 0x48, 0x6f, 0x2d} - for pos := 0; ; { - i := indexOf(code[pos:], pat) - if i < 0 { - break - } - i += pos - rel := int32(uint32(code[i+6]) | uint32(code[i+7])<<8 | uint32(code[i+8])<<16 | uint32(code[i+9])<<24) - target := fn.Offset + i + 10 + int(rel) - if target != base { - t.Errorf("%s: idx16 load at +%d targets 0x%x, want 0x%x", fn.Name, i, target, base) - } - loads++ - pos = i + 10 - } - } - if loads != 1 { - t.Errorf("idx16 loads found = %d, want 1", loads) - } -} - // hexCompact renders bytes as a lowercase hex string without separators. func hexCompact(b []byte) string { const hexdig = "0123456789abcdef" @@ -724,17 +656,3 @@ func hexCompact(b []byte) string { } return string(out) } - -// indexOf returns the index of the first occurrence of pat in b, or -1. -func indexOf(b, pat []byte) int { - for i := 0; i+len(pat) <= len(b); i++ { - j := 0 - for j < len(pat) && b[i+j] == pat[j] { - j++ - } - if j == len(pat) { - return i - } - } - return -1 -} diff --git a/asm/integration_test.go b/asm/integration_test.go new file mode 100644 index 0000000..95dfb64 --- /dev/null +++ b/asm/integration_test.go @@ -0,0 +1,159 @@ +// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) +// SPDX-License-Identifier: BSD-3-Clause + +//go:build integration + +// Package asm integration tests against the production go-libraries kernels. +// These are excluded from the default test run (go test ./...) so that the +// coverage numbers are identical locally and in CI, where go-libraries is +// not checked out. Run them explicitly with: go test -tags=integration ./asm/ +package asm + +import ( + "bytes" + "os" + "testing" + + "golang.org/x/arch/x86/x86asm" + + "sourcedock.dev/petrbalvin/gasm-devkit/parser" +) + +// TestAssembleGoFlacAVX2Kernel assembles the whole production AVX2 kernel — +// all functions plus the file-local mask24 constant — and checks that every +// static-symbol load resolves to the right bytes in the image. +func TestAssembleGoFlacAVX2Kernel(t *testing.T) { + path := "../../go-libraries/go-flac/avx2_amd64.s" + if _, err := os.Stat(path); err != nil { + t.Skip("go-libraries repository not present next to gasm-devkit") + } + src, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + f, errs := parser.Parse(path, string(src)) + if len(errs) > 0 { + t.Fatalf("parse: %v", errs) + } + img, err := AssembleFile(f) + if err != nil { + t.Fatalf("AssembleFile: %v", err) + } + if len(img.Funcs) != 17 { + t.Errorf("functions = %d, want 17", len(img.Funcs)) + } + + // mask24 as the DATA directives define it. + mask := []byte{ + 0x00, 0x01, 0x02, 0x80, 0x03, 0x04, 0x05, 0x80, + 0x06, 0x07, 0x08, 0x80, 0x09, 0x0a, 0x0b, 0x80, + } + image := img.Bytes() + if got := image[img.Symbols["mask24"] : img.Symbols["mask24"]+16]; !bytes.Equal(got, mask) { + t.Errorf("mask24 contents %x, want %x", got, mask) + } + + // Every VMOVDQU mask24<>(SB), X15 (c5 7a 6f 3d + rel32, i.e. a VMOVDQU + // with a RIP-relative r/m) must land on the mask bytes within the image. + loads := 0 + for _, fn := range img.Funcs { + code := img.Code[fn.Offset : fn.Offset+fn.Size] + for pc := 0; pc < len(code); { + inst, err := x86asm.Decode(code[pc:], 64) + if err != nil { + t.Fatalf("%s: decode at +%d: %v", fn.Name, pc, err) + } + // mod=00, rm=101 → RIP-relative. + if inst.Op == x86asm.VMOVDQU && inst.Len == 8 && code[pc+3]&0xC7 == 0x05 { + rel := int32(uint32(code[pc+4]) | uint32(code[pc+5])<<8 | uint32(code[pc+6])<<16 | uint32(code[pc+7])<<24) + target := fn.Offset + pc + 8 + int(rel) + if !bytes.Equal(image[target:target+16], mask) { + t.Errorf("%s: mask load at +%d lands on %x, want %x", fn.Name, pc, image[target:target+16], mask) + } + loads++ + } + pc += inst.Len + } + } + if loads != 2 { + t.Errorf("mask loads found = %d, want 2", loads) + } +} + +// TestAssembleGoFlacAVX512Kernel assembles the whole production AVX-512 +// kernel — all functions plus the file-global idx16 constant — and checks +// that the static-symbol load resolves to the right bytes in the image. +func TestAssembleGoFlacAVX512Kernel(t *testing.T) { + path := "../../go-libraries/go-flac/avx512_amd64.s" + if _, err := os.Stat(path); err != nil { + t.Skip("go-libraries repository not present next to gasm-devkit") + } + src, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + f, errs := parser.Parse(path, string(src)) + if len(errs) > 0 { + t.Fatalf("parse: %v", errs) + } + img, err := AssembleFile(f) + if err != nil { + t.Fatalf("AssembleFile: %v", err) + } + if len(img.Funcs) != 10 { + t.Errorf("functions = %d, want 10", len(img.Funcs)) + } + + // idx16 as the DATA directives define it: dwords 1..16. + idx := make([]byte, 0, 64) + for i := 1; i <= 16; i++ { + idx = append(idx, byte(i), 0, 0, 0) + } + image := img.Bytes() + base := img.Symbols["idx16"] + if base == 0 { + t.Fatal("idx16 not laid out") + } + if got := image[base : base+64]; hexCompact(got) != hexCompact(idx) { + t.Errorf("idx16 contents %x, want %x", got, idx) + } + + // The VMOVDQU32 idx16(SB), Z13 load (62 71 7e 48 6f 2d + rel32) must + // resolve to idx16 within the image. + loads := 0 + for _, fn := range img.Funcs { + code := img.Code[fn.Offset : fn.Offset+fn.Size] + pat := []byte{0x62, 0x71, 0x7e, 0x48, 0x6f, 0x2d} + for pos := 0; ; { + i := indexOf(code[pos:], pat) + if i < 0 { + break + } + i += pos + rel := int32(uint32(code[i+6]) | uint32(code[i+7])<<8 | uint32(code[i+8])<<16 | uint32(code[i+9])<<24) + target := fn.Offset + i + 10 + int(rel) + if target != base { + t.Errorf("%s: idx16 load at +%d targets 0x%x, want 0x%x", fn.Name, i, target, base) + } + loads++ + pos = i + 10 + } + } + if loads != 1 { + t.Errorf("idx16 loads found = %d, want 1", loads) + } +} + +// indexOf returns the index of the first occurrence of pat in b, or -1. +func indexOf(b, pat []byte) int { + for i := 0; i+len(pat) <= len(b); i++ { + j := 0 + for j < len(pat) && b[i+j] == pat[j] { + j++ + } + if j == len(pat) { + return i + } + } + return -1 +} diff --git a/asm/link_test.go b/asm/link_test.go index 8d1fed8..03d4553 100644 --- a/asm/link_test.go +++ b/asm/link_test.go @@ -4,13 +4,9 @@ package asm import ( - "bytes" - "os" "strings" "testing" - "golang.org/x/arch/x86/x86asm" - "sourcedock.dev/petrbalvin/gasm-devkit/parser" ) @@ -132,65 +128,3 @@ DATA x<>+0(SB)/4, $1 t.Errorf("single-function SB: error %v, want a file-level-assembly error", err) } } - -// TestAssembleGoFlacAVX2Kernel assembles the whole production AVX2 kernel — -// all functions plus the file-local mask24 constant — and checks that every -// static-symbol load resolves to the right bytes in the image. Skipped when -// the sibling repository is not checked out. -func TestAssembleGoFlacAVX2Kernel(t *testing.T) { - path := "../../go-libraries/go-flac/avx2_amd64.s" - if _, err := os.Stat(path); err != nil { - t.Skip("go-libraries repository not present next to gasm-devkit") - } - src, err := os.ReadFile(path) - if err != nil { - t.Fatal(err) - } - f, errs := parser.Parse(path, string(src)) - if len(errs) > 0 { - t.Fatalf("parse: %v", errs) - } - img, err := AssembleFile(f) - if err != nil { - t.Fatalf("AssembleFile: %v", err) - } - if len(img.Funcs) != 17 { - t.Errorf("functions = %d, want 17", len(img.Funcs)) - } - - // mask24 as the DATA directives define it. - mask := []byte{ - 0x00, 0x01, 0x02, 0x80, 0x03, 0x04, 0x05, 0x80, - 0x06, 0x07, 0x08, 0x80, 0x09, 0x0a, 0x0b, 0x80, - } - image := img.Bytes() - if got := image[img.Symbols["mask24"] : img.Symbols["mask24"]+16]; !bytes.Equal(got, mask) { - t.Errorf("mask24 contents %x, want %x", got, mask) - } - - // Every VMOVDQU mask24<>(SB), X15 (c5 7a 6f 3d + rel32, i.e. a VMOVDQU - // with a RIP-relative r/m) must land on the mask bytes within the image. - loads := 0 - for _, fn := range img.Funcs { - code := img.Code[fn.Offset : fn.Offset+fn.Size] - for pc := 0; pc < len(code); { - inst, err := x86asm.Decode(code[pc:], 64) - if err != nil { - t.Fatalf("%s: decode at +%d: %v", fn.Name, pc, err) - } - // mod=00, rm=101 → RIP-relative. - if inst.Op == x86asm.VMOVDQU && inst.Len == 8 && code[pc+3]&0xC7 == 0x05 { - rel := int32(uint32(code[pc+4]) | uint32(code[pc+5])<<8 | uint32(code[pc+6])<<16 | uint32(code[pc+7])<<24) - target := fn.Offset + pc + 8 + int(rel) - if !bytes.Equal(image[target:target+16], mask) { - t.Errorf("%s: mask load at +%d lands on %x, want %x", fn.Name, pc, image[target:target+16], mask) - } - loads++ - } - pc += inst.Len - } - } - if loads != 2 { - t.Errorf("mask loads found = %d, want 2", loads) - } -} diff --git a/format/format_test.go b/format/format_test.go index 7a8faca..df76947 100644 --- a/format/format_test.go +++ b/format/format_test.go @@ -5,7 +5,6 @@ package format import ( "os" - "path/filepath" "strings" "testing" @@ -172,11 +171,9 @@ func TestIdempotent(t *testing.T) { } // TestRoundTrip checks that formatting produces source that still parses -// cleanly, on the fixture and on the real go-flac kernels when present. +// cleanly on the in-repository fixture. func TestRoundTrip(t *testing.T) { files := []string{"../testdata/sample_amd64.s"} - real, _ := filepath.Glob("../../go-libraries/go-*/*.s") - files = append(files, real...) for _, path := range files { src, err := os.ReadFile(path) if err != nil { diff --git a/format/integration_test.go b/format/integration_test.go new file mode 100644 index 0000000..7dcc5ec --- /dev/null +++ b/format/integration_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) +// SPDX-License-Identifier: BSD-3-Clause + +//go:build integration + +// Package format integration tests against the production go-libraries kernels. +// Excluded from the default test run so coverage is identical locally and in CI. +// Run explicitly with: go test -tags=integration ./format/ +package format + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "sourcedock.dev/petrbalvin/gasm-devkit/parser" +) + +// TestRoundTripRealGoLibraries checks that formatting the production kernels +// still produces source that parses cleanly. +func TestRoundTripRealGoLibraries(t *testing.T) { + real, _ := filepath.Glob("../../go-libraries/go-*/*.s") + if len(real) == 0 { + t.Skip("go-libraries repository not present") + } + for _, path := range real { + src, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + formatted := Source(path, string(src)) + if _, errs := parser.Parse(path, formatted); len(errs) > 0 { + t.Errorf("formatted %s no longer parses: %v", path, errs) + } + if strings.TrimSpace(formatted) == "" { + t.Errorf("formatted %s is empty", path) + } + } +} diff --git a/lint/integration_test.go b/lint/integration_test.go new file mode 100644 index 0000000..06fc84c --- /dev/null +++ b/lint/integration_test.go @@ -0,0 +1,44 @@ +// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) +// SPDX-License-Identifier: BSD-3-Clause + +//go:build integration + +// Package lint integration tests against the production go-libraries kernels. +// Excluded from the default test run so coverage is identical locally and in CI. +// Run explicitly with: go test -tags=integration ./lint/ +package lint + +import ( + "os" + "path/filepath" + "testing" + + "sourcedock.dev/petrbalvin/gasm-devkit/arch" + "sourcedock.dev/petrbalvin/gasm-devkit/parser" +) + +// TestRealGoLibrariesHasNoErrors asserts that the production go-flac kernels +// lint free of errors. +func TestRealGoLibrariesHasNoErrors(t *testing.T) { + matches, _ := filepath.Glob("../../go-libraries/go-*/*.s") + if len(matches) == 0 { + t.Skip("go-libraries repository not present") + } + for _, path := range matches { + src, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + f, errs := parser.Parse(path, string(src)) + if len(errs) > 0 { + t.Fatalf("parse %s: %v", path, errs) + } + a := arch.FromFilename(path) + diags := File(f, Config{Arch: a}) + for _, d := range diags { + if d.Severity == Error { + t.Errorf("%s: %s %s: %s", filepath.Base(path), d.Pos, d.Code, d.Message) + } + } + } +} diff --git a/lint/lint_test.go b/lint/lint_test.go index 29f08d7..3a14420 100644 --- a/lint/lint_test.go +++ b/lint/lint_test.go @@ -5,7 +5,6 @@ package lint import ( "os" - "path/filepath" "testing" "sourcedock.dev/petrbalvin/gasm-devkit/arch" @@ -234,29 +233,3 @@ TEXT ·f(SB), NOSPLIT, $0 t.Fatalf("label rules should be suppressed in macro files: %+v", diags) } } - -// TestRealGoLibrariesHasNoErrors asserts that the production go-flac kernels -// lint free of errors. Skipped when the sibling repository is absent. -func TestRealGoLibrariesHasNoErrors(t *testing.T) { - matches, _ := filepath.Glob("../../go-libraries/go-*/*.s") - if len(matches) == 0 { - t.Skip("go-libraries repository not present") - } - for _, path := range matches { - src, err := os.ReadFile(path) - if err != nil { - t.Fatal(err) - } - f, errs := parser.Parse(path, string(src)) - if len(errs) > 0 { - t.Fatalf("parse %s: %v", path, errs) - } - a := arch.FromFilename(path) - diags := File(f, Config{Arch: a}) - for _, d := range diags { - if d.Severity == Error { - t.Errorf("%s: %s %s: %s", filepath.Base(path), d.Pos, d.Code, d.Message) - } - } - } -} diff --git a/parser/integration_test.go b/parser/integration_test.go new file mode 100644 index 0000000..d9db2ec --- /dev/null +++ b/parser/integration_test.go @@ -0,0 +1,39 @@ +// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) +// SPDX-License-Identifier: BSD-3-Clause + +//go:build integration + +// Package parser integration tests against the production go-libraries kernels. +// Excluded from the default test run so coverage is identical locally and in CI. +// Run explicitly with: go test -tags=integration ./parser/ +package parser + +import ( + "os" + "path/filepath" + "testing" +) + +// TestParseRealGoLibraries parses every .s file in the sibling go-libraries +// repository when it is checked out, asserting a clean, error-free parse. +func TestParseRealGoLibraries(t *testing.T) { + matches, _ := filepath.Glob("../../go-libraries/go-*/*.s") + if len(matches) == 0 { + t.Skip("go-libraries repository not present next to gasm-devkit") + } + for _, path := range matches { + src, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read %s: %v", path, err) + } + file, errs := Parse(path, string(src)) + if len(errs) > 0 { + t.Errorf("parse %s: %v", path, errs) + continue + } + if len(texts(file)) == 0 { + t.Errorf("parse %s: no TEXT functions found", path) + } + t.Logf("%s: %d decls, %d functions", filepath.Base(path), len(file.Decls), len(texts(file))) + } +} diff --git a/parser/parser_test.go b/parser/parser_test.go index 28283a0..cb4212e 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -5,7 +5,6 @@ package parser import ( "os" - "path/filepath" "testing" "sourcedock.dev/petrbalvin/gasm-devkit/ast" @@ -254,28 +253,3 @@ func TestDataWidthAndStatic(t *testing.T) { t.Errorf("mask24 DATA should be static, got %+v", datas[2].Name) } } - -// TestParseRealGoLibraries parses every .s file in the sibling go-libraries -// repository when it is checked out, asserting a clean, error-free parse. It -// is skipped when the repository is not present. -func TestParseRealGoLibraries(t *testing.T) { - matches, _ := filepath.Glob("../../go-libraries/go-*/*.s") - if len(matches) == 0 { - t.Skip("go-libraries repository not present next to gasm-devkit") - } - for _, path := range matches { - src, err := os.ReadFile(path) - if err != nil { - t.Fatalf("read %s: %v", path, err) - } - file, errs := Parse(path, string(src)) - if len(errs) > 0 { - t.Errorf("parse %s: %v", path, errs) - continue - } - if len(texts(file)) == 0 { - t.Errorf("parse %s: no TEXT functions found", path) - } - t.Logf("%s: %d decls, %d functions", filepath.Base(path), len(file.Decls), len(texts(file))) - } -} diff --git a/verify/fuzz_extra_test.go b/verify/fuzz_extra_test.go index 1995d2b..29cbd0e 100644 --- a/verify/fuzz_extra_test.go +++ b/verify/fuzz_extra_test.go @@ -50,3 +50,118 @@ func TestArrayLen(t *testing.T) { } } } + +func TestElemSizeFor(t *testing.T) { + tests := []struct { + typ string + want int + }{ + {"[]byte", 1}, {"[]uint8", 1}, {"[]int8", 1}, + {"[]uint16", 2}, {"[]int16", 2}, + {"[]uint32", 4}, {"[]int32", 4}, {"[]float32", 4}, + {"[]uint64", 8}, {"[]int64", 8}, {"[]float64", 8}, + {"[]unknown", 8}, + } + for _, tt := range tests { + if got := elemSizeFor(tt.typ); got != tt.want { + t.Errorf("elemSizeFor(%q) = %d, want %d", tt.typ, got, tt.want) + } + } +} + +func TestEqualBytes(t *testing.T) { + if !equalBytes([]byte{1, 2, 3}, []byte{1, 2, 3}) { + t.Error("expected equal") + } + if equalBytes([]byte{1, 2}, []byte{1, 2, 3}) { + t.Error("different length: expected not equal") + } + if equalBytes([]byte{1, 2, 3}, []byte{1, 2, 4}) { + t.Error("different content: expected not equal") + } +} + +func TestParamsSize(t *testing.T) { + sig := funcSig{ + name: "test", + params: []param{{name: "a", typ: "[]byte"}, {name: "b", typ: "int"}}, + results: []param{{name: "n", typ: "int"}}, + } + if got := paramsSize(sig); got != 32 { + t.Errorf("paramsSize = %d, want 32 (24 for slice + 8 for int)", got) + } +} + +func TestBlockCount(t *testing.T) { + k := loadBasic(t) + n, err := k.BlockCount("sum") + if err != nil { + t.Fatalf("BlockCount(sum): %v", err) + } + if n < 2 { + t.Errorf("sum: expected at least 2 blocks, got %d", n) + } +} + +func TestFillBuffer(t *testing.T) { + t.Run("zero", func(t *testing.T) { + // fillBuffer("zero") is a no-op — relies on make already zeroing. + buf := make([]byte, 16) + fillBuffer(buf, "zero") + for _, b := range buf { + if b != 0 { + t.Error("zero pattern: make should produce zeroed buffer") + break + } + } + }) + t.Run("ones", func(t *testing.T) { + buf := make([]byte, 16) + fillBuffer(buf, "ones") + for _, b := range buf { + if b != 0xFF { + t.Error("ones pattern should fill with 0xFF") + break + } + } + }) + t.Run("seq", func(t *testing.T) { + buf := make([]byte, 256) + fillBuffer(buf, "seq") + for i, b := range buf { + if b != byte(i) { + t.Errorf("seq[%d] = %d, want %d", i, b, i) + break + } + } + }) + t.Run("hex", func(t *testing.T) { + buf := make([]byte, 6) + fillBuffer(buf, "deadbeef") + want := []byte{0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD} + for i, b := range buf { + if b != want[i] { + t.Errorf("hex[%d] = %02x, want %02x", i, b, want[i]) + break + } + } + }) +} + +func TestFuzzFuncChecked(t *testing.T) { + k := loadBasic(t) + sig := funcSig{ + name: "sum", + params: []param{{name: "data", typ: "[]int64"}}, + results: []param{{name: "r", typ: "int64"}}, + } + result := k.FuzzFuncChecked("sum", sig, 10, 0) + if !result.OK() { + t.Errorf("FuzzFuncChecked(sum): %s", result) + } + // Test with non-existent function — should report failure. + result = k.FuzzFuncChecked("nope", sig, 10, 0) + if result.OK() { + t.Error("FuzzFuncChecked(nope): expected failure") + } +}