github.com/cilium/ebpf@v0.16.0/cmd/bpf2go/main_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/cilium/ebpf/cmd/bpf2go/gen"
    14  	"github.com/cilium/ebpf/cmd/bpf2go/internal"
    15  	"github.com/cilium/ebpf/internal/testutils"
    16  	"github.com/go-quicktest/qt"
    17  )
    18  
    19  const minimalSocketFilter = `__attribute__((section("socket"), used)) int main() { return 0; }`
    20  
    21  func TestRun(t *testing.T) {
    22  	clangBin := testutils.ClangBin(t)
    23  	dir := t.TempDir()
    24  	mustWriteFile(t, dir, "test.c", minimalSocketFilter)
    25  
    26  	modRoot, err := filepath.Abs("../..")
    27  	qt.Assert(t, qt.IsNil(err))
    28  
    29  	if _, err := os.Stat(filepath.Join(modRoot, "go.mod")); os.IsNotExist(err) {
    30  		t.Fatal("No go.mod file in", modRoot)
    31  	}
    32  
    33  	modDir := t.TempDir()
    34  	execInModule := func(name string, args ...string) {
    35  		t.Helper()
    36  
    37  		cmd := exec.Command(name, args...)
    38  		cmd.Dir = modDir
    39  		if out, err := cmd.CombinedOutput(); err != nil {
    40  			if out := string(out); out != "" {
    41  				t.Log(out)
    42  			}
    43  			t.Fatalf("Can't execute %s: %v", name, args)
    44  		}
    45  	}
    46  
    47  	module := internal.CurrentModule
    48  
    49  	execInModule("go", "mod", "init", "bpf2go-test")
    50  
    51  	execInModule("go", "mod", "edit",
    52  		// Require the module. The version doesn't matter due to the replace
    53  		// below.
    54  		fmt.Sprintf("-require=%s@v0.0.0", module),
    55  		// Replace the module with the current version.
    56  		fmt.Sprintf("-replace=%s=%s", module, modRoot),
    57  	)
    58  
    59  	goarches := []string{
    60  		"amd64", // little-endian
    61  		"arm64",
    62  		"s390x", // big-endian
    63  	}
    64  
    65  	err = run(io.Discard, []string{
    66  		"-go-package", "main",
    67  		"-output-dir", modDir,
    68  		"-cc", clangBin,
    69  		"-target", strings.Join(goarches, ","),
    70  		"bar",
    71  		filepath.Join(dir, "test.c"),
    72  	})
    73  
    74  	if err != nil {
    75  		t.Fatal("Can't run:", err)
    76  	}
    77  
    78  	mustWriteFile(t, modDir, "main.go",
    79  		`
    80  package main
    81  
    82  func main() {
    83  	var obj barObjects
    84  	println(obj.Main)
    85  }`)
    86  
    87  	for _, arch := range goarches {
    88  		t.Run(arch, func(t *testing.T) {
    89  			goBuild := exec.Command("go", "build", "-mod=mod", "-o", "/dev/null")
    90  			goBuild.Dir = modDir
    91  			goBuild.Env = append(os.Environ(),
    92  				"GOOS=linux",
    93  				"GOARCH="+arch,
    94  				"GOPROXY=off",
    95  				"GOSUMDB=off",
    96  			)
    97  			out, err := goBuild.CombinedOutput()
    98  			if err != nil {
    99  				if out := string(out); out != "" {
   100  					t.Log(out)
   101  				}
   102  				t.Error("Can't compile package:", err)
   103  			}
   104  		})
   105  	}
   106  }
   107  
   108  func TestHelp(t *testing.T) {
   109  	var stdout bytes.Buffer
   110  	err := run(&stdout, []string{"-help"})
   111  	if err != nil {
   112  		t.Fatal("Can't execute -help")
   113  	}
   114  
   115  	if stdout.Len() == 0 {
   116  		t.Error("-help doesn't write to stdout")
   117  	}
   118  }
   119  
   120  func TestErrorMentionsEnvVar(t *testing.T) {
   121  	err := run(io.Discard, nil)
   122  	qt.Assert(t, qt.StringContains(err.Error(), gopackageEnv), qt.Commentf("Error should include name of environment variable"))
   123  }
   124  
   125  func TestDisableStripping(t *testing.T) {
   126  	dir := t.TempDir()
   127  	mustWriteFile(t, dir, "test.c", minimalSocketFilter)
   128  
   129  	err := run(io.Discard, []string{
   130  		"-go-package", "foo",
   131  		"-output-dir", dir,
   132  		"-cc", testutils.ClangBin(t),
   133  		"-strip", "binary-that-certainly-doesnt-exist",
   134  		"-no-strip",
   135  		"bar",
   136  		filepath.Join(dir, "test.c"),
   137  	})
   138  
   139  	if err != nil {
   140  		t.Fatal("Can't run with stripping disabled:", err)
   141  	}
   142  }
   143  
   144  func TestConvertGOARCH(t *testing.T) {
   145  	tmp := t.TempDir()
   146  	mustWriteFile(t, tmp, "test.c",
   147  		`
   148  #ifndef __TARGET_ARCH_x86
   149  #error __TARGET_ARCH_x86 is not defined
   150  #endif`,
   151  	)
   152  
   153  	b2g := bpf2go{
   154  		pkg:              "test",
   155  		stdout:           io.Discard,
   156  		identStem:        "test",
   157  		cc:               testutils.ClangBin(t),
   158  		disableStripping: true,
   159  		sourceFile:       tmp + "/test.c",
   160  		outputDir:        tmp,
   161  	}
   162  
   163  	if err := b2g.convert(gen.TargetsByGoArch()["amd64"], nil); err != nil {
   164  		t.Fatal("Can't target GOARCH:", err)
   165  	}
   166  }
   167  
   168  func TestCTypes(t *testing.T) {
   169  	var ct cTypes
   170  	valid := []string{
   171  		"abcdefghijklmnopqrstuvqxyABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_",
   172  		"y",
   173  	}
   174  	for _, value := range valid {
   175  		if err := ct.Set(value); err != nil {
   176  			t.Fatalf("Set returned an error for %q: %s", value, err)
   177  		}
   178  	}
   179  	qt.Assert(t, qt.ContentEquals(ct, valid))
   180  
   181  	for _, value := range []string{
   182  		"",
   183  		" ",
   184  		" frood",
   185  		"foo\nbar",
   186  		".",
   187  		",",
   188  		"+",
   189  		"-",
   190  	} {
   191  		ct = nil
   192  		if err := ct.Set(value); err == nil {
   193  			t.Fatalf("Set did not return an error for %q", value)
   194  		}
   195  	}
   196  
   197  	ct = nil
   198  	qt.Assert(t, qt.IsNil(ct.Set("foo")))
   199  	qt.Assert(t, qt.IsNotNil(ct.Set("foo")))
   200  }
   201  
   202  func TestParseArgs(t *testing.T) {
   203  	const (
   204  		pkg       = "eee"
   205  		outputDir = "."
   206  		csource   = "testdata/minimal.c"
   207  		stem      = "a"
   208  	)
   209  	t.Run("makebase", func(t *testing.T) {
   210  		t.Setenv(gopackageEnv, pkg)
   211  		basePath, _ := filepath.Abs("barfoo")
   212  		args := []string{"-makebase", basePath, stem, csource}
   213  		b2g, err := newB2G(&bytes.Buffer{}, args)
   214  		qt.Assert(t, qt.IsNil(err))
   215  		qt.Assert(t, qt.Equals(b2g.makeBase, basePath))
   216  	})
   217  
   218  	t.Run("makebase from env", func(t *testing.T) {
   219  		t.Setenv(gopackageEnv, pkg)
   220  		basePath, _ := filepath.Abs("barfoo")
   221  		args := []string{stem, csource}
   222  		t.Setenv("BPF2GO_MAKEBASE", basePath)
   223  		b2g, err := newB2G(&bytes.Buffer{}, args)
   224  		qt.Assert(t, qt.IsNil(err))
   225  		qt.Assert(t, qt.Equals(b2g.makeBase, basePath))
   226  	})
   227  
   228  	t.Run("makebase flag overrides env", func(t *testing.T) {
   229  		t.Setenv(gopackageEnv, pkg)
   230  		basePathFlag, _ := filepath.Abs("barfoo")
   231  		basePathEnv, _ := filepath.Abs("foobar")
   232  		args := []string{"-makebase", basePathFlag, stem, csource}
   233  		t.Setenv("BPF2GO_MAKEBASE", basePathEnv)
   234  		b2g, err := newB2G(&bytes.Buffer{}, args)
   235  		qt.Assert(t, qt.IsNil(err))
   236  		qt.Assert(t, qt.Equals(b2g.makeBase, basePathFlag))
   237  	})
   238  
   239  	t.Run("cc defaults to clang", func(t *testing.T) {
   240  		t.Setenv(gopackageEnv, pkg)
   241  		args := []string{stem, csource}
   242  		b2g, err := newB2G(&bytes.Buffer{}, args)
   243  		qt.Assert(t, qt.IsNil(err))
   244  		qt.Assert(t, qt.Equals(b2g.cc, "clang"))
   245  	})
   246  
   247  	t.Run("cc", func(t *testing.T) {
   248  		t.Setenv(gopackageEnv, pkg)
   249  		args := []string{"-cc", "barfoo", stem, csource}
   250  		b2g, err := newB2G(&bytes.Buffer{}, args)
   251  		qt.Assert(t, qt.IsNil(err))
   252  		qt.Assert(t, qt.Equals(b2g.cc, "barfoo"))
   253  	})
   254  
   255  	t.Run("cc from env", func(t *testing.T) {
   256  		t.Setenv(gopackageEnv, pkg)
   257  		args := []string{stem, csource}
   258  		t.Setenv("BPF2GO_CC", "barfoo")
   259  		b2g, err := newB2G(&bytes.Buffer{}, args)
   260  		qt.Assert(t, qt.IsNil(err))
   261  		qt.Assert(t, qt.Equals(b2g.cc, "barfoo"))
   262  	})
   263  
   264  	t.Run("cc flag overrides env", func(t *testing.T) {
   265  		t.Setenv(gopackageEnv, pkg)
   266  		args := []string{"-cc", "barfoo", stem, csource}
   267  		t.Setenv("BPF2GO_CC", "foobar")
   268  		b2g, err := newB2G(&bytes.Buffer{}, args)
   269  		qt.Assert(t, qt.IsNil(err))
   270  		qt.Assert(t, qt.Equals(b2g.cc, "barfoo"))
   271  	})
   272  
   273  	t.Run("strip defaults to llvm-strip", func(t *testing.T) {
   274  		t.Setenv(gopackageEnv, pkg)
   275  		args := []string{stem, csource}
   276  		b2g, err := newB2G(&bytes.Buffer{}, args)
   277  		qt.Assert(t, qt.IsNil(err))
   278  		qt.Assert(t, qt.Equals(b2g.strip, "llvm-strip"))
   279  	})
   280  
   281  	t.Run("strip", func(t *testing.T) {
   282  		t.Setenv(gopackageEnv, pkg)
   283  		args := []string{"-strip", "barfoo", stem, csource}
   284  		b2g, err := newB2G(&bytes.Buffer{}, args)
   285  		qt.Assert(t, qt.IsNil(err))
   286  		qt.Assert(t, qt.Equals(b2g.strip, "barfoo"))
   287  	})
   288  
   289  	t.Run("strip from env", func(t *testing.T) {
   290  		t.Setenv(gopackageEnv, pkg)
   291  		args := []string{stem, csource}
   292  		t.Setenv("BPF2GO_STRIP", "barfoo")
   293  		b2g, err := newB2G(&bytes.Buffer{}, args)
   294  		qt.Assert(t, qt.IsNil(err))
   295  		qt.Assert(t, qt.Equals(b2g.strip, "barfoo"))
   296  	})
   297  
   298  	t.Run("strip flag overrides env", func(t *testing.T) {
   299  		t.Setenv(gopackageEnv, pkg)
   300  		args := []string{"-strip", "barfoo", stem, csource}
   301  		t.Setenv("BPF2GO_STRIP", "foobar")
   302  		b2g, err := newB2G(&bytes.Buffer{}, args)
   303  		qt.Assert(t, qt.IsNil(err))
   304  		qt.Assert(t, qt.Equals(b2g.strip, "barfoo"))
   305  	})
   306  
   307  	t.Run("no strip defaults to false", func(t *testing.T) {
   308  		t.Setenv(gopackageEnv, pkg)
   309  		args := []string{stem, csource}
   310  		b2g, err := newB2G(&bytes.Buffer{}, args)
   311  		qt.Assert(t, qt.IsNil(err))
   312  		qt.Assert(t, qt.IsFalse(b2g.disableStripping))
   313  	})
   314  
   315  	t.Run("no strip", func(t *testing.T) {
   316  		t.Setenv(gopackageEnv, pkg)
   317  		args := []string{"-no-strip", stem, csource}
   318  		b2g, err := newB2G(&bytes.Buffer{}, args)
   319  		qt.Assert(t, qt.IsNil(err))
   320  		qt.Assert(t, qt.IsTrue(b2g.disableStripping))
   321  	})
   322  
   323  	t.Run("cflags flag", func(t *testing.T) {
   324  		t.Setenv(gopackageEnv, pkg)
   325  		args := []string{"-cflags", "x y z", stem, csource}
   326  		b2g, err := newB2G(&bytes.Buffer{}, args)
   327  		qt.Assert(t, qt.IsNil(err))
   328  		qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z"}))
   329  	})
   330  
   331  	t.Run("cflags multi flag", func(t *testing.T) {
   332  		t.Setenv(gopackageEnv, pkg)
   333  		args := []string{"-cflags", "x y z", "-cflags", "u v", stem, csource}
   334  		b2g, err := newB2G(&bytes.Buffer{}, args)
   335  		qt.Assert(t, qt.IsNil(err))
   336  		qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"u", "v"}))
   337  	})
   338  
   339  	t.Run("cflags flag and args", func(t *testing.T) {
   340  		t.Setenv(gopackageEnv, pkg)
   341  		args := []string{"-cflags", "x y z", "stem", csource, "--", "u", "v"}
   342  		b2g, err := newB2G(&bytes.Buffer{}, args)
   343  		qt.Assert(t, qt.IsNil(err))
   344  		qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z", "u", "v"}))
   345  	})
   346  
   347  	t.Run("cflags from env", func(t *testing.T) {
   348  		t.Setenv(gopackageEnv, pkg)
   349  		args := []string{stem, csource}
   350  		t.Setenv("BPF2GO_CFLAGS", "x y z")
   351  		b2g, err := newB2G(&bytes.Buffer{}, args)
   352  		qt.Assert(t, qt.IsNil(err))
   353  		qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z"}))
   354  	})
   355  
   356  	t.Run("cflags flag overrides env", func(t *testing.T) {
   357  		t.Setenv(gopackageEnv, pkg)
   358  		args := []string{"-cflags", "u v", stem, csource}
   359  		t.Setenv("BPF2GO_CFLAGS", "x y z")
   360  		b2g, err := newB2G(&bytes.Buffer{}, args)
   361  		qt.Assert(t, qt.IsNil(err))
   362  		qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"u", "v"}))
   363  	})
   364  
   365  	t.Run("go package overrides env", func(t *testing.T) {
   366  		t.Setenv(gopackageEnv, pkg)
   367  		args := []string{"-go-package", "aaa", stem, csource}
   368  		b2g, err := newB2G(&bytes.Buffer{}, args)
   369  		qt.Assert(t, qt.IsNil(err))
   370  		qt.Assert(t, qt.Equals(b2g.pkg, "aaa"))
   371  	})
   372  
   373  	t.Run("output dir", func(t *testing.T) {
   374  		t.Setenv(gopackageEnv, pkg)
   375  		args := []string{"-output-dir", outputDir, stem, csource}
   376  		b2g, err := newB2G(&bytes.Buffer{}, args)
   377  		qt.Assert(t, qt.IsNil(err))
   378  		qt.Assert(t, qt.Equals(b2g.outputDir, outputDir))
   379  	})
   380  }
   381  
   382  func mustWriteFile(tb testing.TB, dir, name, contents string) {
   383  	tb.Helper()
   384  	tmpFile := filepath.Join(dir, name)
   385  	if err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil {
   386  		tb.Fatal(err)
   387  	}
   388  }