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
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 }
171
View as plain text