aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/clangtool
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2025-11-17 07:50:28 +0100
committerDmitry Vyukov <dvyukov@google.com>2025-11-17 08:54:02 +0000
commiteefcfd016a5cb195a9a9c86722a2b15aade8b59a (patch)
tree3c2643e02d7924ddd58a22e33bf1836b8211e0df /pkg/clangtool
parenta41f43a1ab652ec0629a63d0812a30bfec0e0faf (diff)
pkg/clangtool/tooltest: add package
Factor out common clang tool testing helpers from the declextract tool test.
Diffstat (limited to 'pkg/clangtool')
-rw-r--r--pkg/clangtool/tooltest/tooltest.go103
1 files changed, 103 insertions, 0 deletions
diff --git a/pkg/clangtool/tooltest/tooltest.go b/pkg/clangtool/tooltest/tooltest.go
new file mode 100644
index 000000000..b9ad18a83
--- /dev/null
+++ b/pkg/clangtool/tooltest/tooltest.go
@@ -0,0 +1,103 @@
+// Copyright 2025 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package tooltest
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/syzkaller/pkg/clangtool"
+ "github.com/google/syzkaller/pkg/osutil"
+ "github.com/google/syzkaller/pkg/testutil"
+)
+
+var (
+ FlagBin = flag.String("bin", "", "path to the clang tool binary to use")
+ FlagUpdate = flag.Bool("update", false, "update golden files")
+)
+
+func TestClangTool[Output any, OutputPtr clangtool.OutputDataPtr[Output]](t *testing.T) {
+ if *FlagBin == "" {
+ t.Skipf("clang tool path is not specified, run with -bin=clangtool flag")
+ }
+ ForEachTestFile(t, func(t *testing.T, cfg *clangtool.Config, file string) {
+ out, err := clangtool.Run[Output, OutputPtr](cfg)
+ if err != nil {
+ t.Fatal(err)
+ }
+ got, err := json.MarshalIndent(out, "", "\t")
+ if err != nil {
+ t.Fatal(err)
+ }
+ CompareGoldenData(t, file+".json", got)
+ })
+}
+
+func ForEachTestFile(t *testing.T, fn func(t *testing.T, cfg *clangtool.Config, file string)) {
+ forEachTestFile(t, func(t *testing.T, file string) {
+ t.Run(filepath.Base(file), func(t *testing.T) {
+ t.Parallel()
+ buildDir := t.TempDir()
+ commands := fmt.Sprintf(`[{
+ "file": "%s",
+ "directory": "%s",
+ "command": "clang -c %s -DKBUILD_BASENAME=foo"
+ }]`,
+ file, buildDir, file)
+ dbFile := filepath.Join(buildDir, "compile_commands.json")
+ if err := os.WriteFile(dbFile, []byte(commands), 0600); err != nil {
+ t.Fatal(err)
+ }
+ cfg := &clangtool.Config{
+ ToolBin: *FlagBin,
+ KernelSrc: osutil.Abs("testdata"),
+ KernelObj: buildDir,
+ CacheFile: filepath.Join(buildDir, filepath.Base(file)+".json"),
+ DebugTrace: &testutil.Writer{TB: t},
+ }
+ fn(t, cfg, file)
+ })
+ })
+}
+
+func forEachTestFile(t *testing.T, fn func(t *testing.T, file string)) {
+ files, err := filepath.Glob(filepath.Join(osutil.Abs("testdata"), "*.c"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(files) == 0 {
+ t.Fatal("found no source files")
+ }
+ for _, file := range files {
+ fn(t, file)
+ }
+}
+
+func CompareGoldenFile(t *testing.T, goldenFile, gotFile string) {
+ got, err := os.ReadFile(gotFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ CompareGoldenData(t, goldenFile, got)
+}
+
+func CompareGoldenData(t *testing.T, goldenFile string, got []byte) {
+ if *FlagUpdate {
+ if err := os.WriteFile(goldenFile, got, 0644); err != nil {
+ t.Fatal(err)
+ }
+ }
+ want, err := os.ReadFile(goldenFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if diff := cmp.Diff(want, got); diff != "" {
+ t.Fatal(diff)
+ }
+}