github.com/kubeshark/ebpf@v0.9.2/cmd/bpf2go/compile_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "os" 6 "path/filepath" 7 "reflect" 8 "strings" 9 "testing" 10 ) 11 12 const minimalSocketFilter = `__attribute__((section("socket"), used)) int main() { return 0; }` 13 14 // Test against the minimum supported version of clang to avoid regressions. 15 const ( 16 clangBin = "clang-9" 17 ) 18 19 func TestCompile(t *testing.T) { 20 dir := mustWriteTempFile(t, "test.c", minimalSocketFilter) 21 22 var dep bytes.Buffer 23 err := compile(compileArgs{ 24 cc: clangBin, 25 dir: dir, 26 source: filepath.Join(dir, "test.c"), 27 dest: filepath.Join(dir, "test.o"), 28 dep: &dep, 29 }) 30 if err != nil { 31 t.Fatal("Can't compile:", err) 32 } 33 34 stat, err := os.Stat(filepath.Join(dir, "test.o")) 35 if err != nil { 36 t.Fatal("Can't stat output:", err) 37 } 38 39 if stat.Size() == 0 { 40 t.Error("Compilation creates an empty file") 41 } 42 43 if dep.Len() == 0 { 44 t.Error("Compilation doesn't generate depinfo") 45 } 46 47 if _, err := parseDependencies(dir, &dep); err != nil { 48 t.Error("Can't parse dependencies:", err) 49 } 50 } 51 52 func TestReproducibleCompile(t *testing.T) { 53 dir := mustWriteTempFile(t, "test.c", minimalSocketFilter) 54 55 err := compile(compileArgs{ 56 cc: clangBin, 57 dir: dir, 58 source: filepath.Join(dir, "test.c"), 59 dest: filepath.Join(dir, "a.o"), 60 }) 61 if err != nil { 62 t.Fatal("Can't compile:", err) 63 } 64 65 err = compile(compileArgs{ 66 cc: clangBin, 67 dir: dir, 68 source: filepath.Join(dir, "test.c"), 69 dest: filepath.Join(dir, "b.o"), 70 }) 71 if err != nil { 72 t.Fatal("Can't compile:", err) 73 } 74 75 aBytes, err := os.ReadFile(filepath.Join(dir, "a.o")) 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 bBytes, err := os.ReadFile(filepath.Join(dir, "b.o")) 81 if err != nil { 82 t.Fatal(err) 83 } 84 85 if !bytes.Equal(aBytes, bBytes) { 86 t.Error("Compiling the same file twice doesn't give the same result") 87 } 88 } 89 90 func TestTriggerMissingTarget(t *testing.T) { 91 dir := mustWriteTempFile(t, "test.c", `_Pragma(__BPF_TARGET_MISSING);`) 92 93 err := compile(compileArgs{ 94 cc: clangBin, 95 dir: dir, 96 source: filepath.Join(dir, "test.c"), 97 dest: filepath.Join(dir, "a.o"), 98 }) 99 100 if err == nil { 101 t.Fatal("No error when compiling __BPF_TARGET_MISSING") 102 } 103 } 104 105 func TestParseDependencies(t *testing.T) { 106 const input = `main.go: /foo/bar baz 107 108 frob: /gobble \ 109 gubble 110 111 nothing: 112 ` 113 114 have, err := parseDependencies("/foo", strings.NewReader(input)) 115 if err != nil { 116 t.Fatal("Can't parse dependencies:", err) 117 } 118 119 want := []dependency{ 120 {"/foo/main.go", []string{"/foo/bar", "/foo/baz"}}, 121 {"/foo/frob", []string{"/gobble", "/foo/gubble"}}, 122 {"/foo/nothing", nil}, 123 } 124 125 if !reflect.DeepEqual(have, want) { 126 t.Logf("Have: %#v", have) 127 t.Logf("Want: %#v", want) 128 t.Error("Result doesn't match") 129 } 130 131 output, err := adjustDependencies("/foo", want) 132 if err != nil { 133 t.Error("Can't adjust dependencies") 134 } 135 136 const wantOutput = `main.go: \ 137 bar \ 138 baz 139 140 frob: \ 141 ../gobble \ 142 gubble 143 144 nothing: 145 146 ` 147 148 if have := string(output); have != wantOutput { 149 t.Logf("Have:\n%s", have) 150 t.Logf("Want:\n%s", wantOutput) 151 t.Error("Output doesn't match") 152 } 153 } 154 155 func mustWriteTempFile(t *testing.T, name, contents string) string { 156 t.Helper() 157 158 tmp, err := os.MkdirTemp("", "bpf2go") 159 if err != nil { 160 t.Fatal(err) 161 } 162 t.Cleanup(func() { os.RemoveAll(tmp) }) 163 164 tmpFile := filepath.Join(tmp, name) 165 if err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil { 166 t.Fatal(err) 167 } 168 169 return tmp 170 }