github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/btf/format_test.go (about) 1 package btf 2 3 import ( 4 "errors" 5 "fmt" 6 "go/format" 7 "math" 8 "strings" 9 "testing" 10 ) 11 12 func TestGoTypeDeclaration(t *testing.T) { 13 tests := []struct { 14 typ Type 15 output string 16 }{ 17 {&Int{Size: 1}, "type t uint8"}, 18 {&Int{Size: 1, Encoding: Bool}, "type t bool"}, 19 {&Int{Size: 1, Encoding: Char}, "type t uint8"}, 20 {&Int{Size: 2, Encoding: Signed}, "type t int16"}, 21 {&Int{Size: 4, Encoding: Signed}, "type t int32"}, 22 {&Int{Size: 8}, "type t uint64"}, 23 {&Typedef{Name: "frob", Type: &Int{Size: 8}}, "type t uint64"}, 24 {&Int{Size: 16}, "type t [16]byte /* uint128 */"}, 25 {&Enum{Values: []EnumValue{{"FOO", 32}}, Size: 4}, "type t uint32; const ( tFOO t = 32; )"}, 26 { 27 &Enum{ 28 Values: []EnumValue{ 29 {"MINUS_ONE", math.MaxUint64}, 30 {"MINUS_TWO", math.MaxUint64 - 1}, 31 }, 32 Size: 1, 33 Signed: true, 34 }, 35 "type t int8; const ( tMINUS_ONE t = -1; tMINUS_TWO t = -2; )", 36 }, 37 { 38 &Struct{ 39 Name: "enum literals", 40 Size: 2, 41 Members: []Member{ 42 {Name: "enum", Type: &Enum{Values: []EnumValue{{"BAR", 1}}, Size: 2}, Offset: 0}, 43 }, 44 }, 45 "type t struct { enum uint16; }", 46 }, 47 {&Array{Nelems: 2, Type: &Int{Size: 1}}, "type t [2]uint8"}, 48 { 49 &Union{ 50 Size: 8, 51 Members: []Member{ 52 {Name: "a", Type: &Int{Size: 4}}, 53 {Name: "b", Type: &Int{Size: 8}}, 54 }, 55 }, 56 "type t struct { a uint32; _ [4]byte; }", 57 }, 58 { 59 &Struct{ 60 Name: "field padding", 61 Size: 16, 62 Members: []Member{ 63 {Name: "frob", Type: &Int{Size: 4}, Offset: 0}, 64 {Name: "foo", Type: &Int{Size: 8}, Offset: 8 * 8}, 65 }, 66 }, 67 "type t struct { frob uint32; _ [4]byte; foo uint64; }", 68 }, 69 { 70 &Struct{ 71 Name: "end padding", 72 Size: 16, 73 Members: []Member{ 74 {Name: "foo", Type: &Int{Size: 8}, Offset: 0}, 75 {Name: "frob", Type: &Int{Size: 4}, Offset: 8 * 8}, 76 }, 77 }, 78 "type t struct { foo uint64; frob uint32; _ [4]byte; }", 79 }, 80 { 81 &Struct{ 82 Name: "bitfield", 83 Size: 8, 84 Members: []Member{ 85 {Name: "foo", Type: &Int{Size: 4}, Offset: 0, BitfieldSize: 1}, 86 {Name: "frob", Type: &Int{Size: 4}, Offset: 4 * 8}, 87 }, 88 }, 89 "type t struct { _ [4]byte /* unsupported bitfield */; frob uint32; }", 90 }, 91 { 92 &Struct{ 93 Name: "nested", 94 Size: 8, 95 Members: []Member{ 96 { 97 Name: "foo", 98 Type: &Struct{ 99 Size: 4, 100 Members: []Member{ 101 {Name: "bar", Type: &Int{Size: 4}, Offset: 0}, 102 }, 103 }, 104 }, 105 {Name: "frob", Type: &Int{Size: 4}, Offset: 4 * 8}, 106 }, 107 }, 108 "type t struct { foo struct { bar uint32; }; frob uint32; }", 109 }, 110 { 111 &Struct{ 112 Name: "nested anon union", 113 Size: 8, 114 Members: []Member{ 115 { 116 Name: "", 117 Type: &Union{ 118 Size: 4, 119 Members: []Member{ 120 {Name: "foo", Type: &Int{Size: 4}, Offset: 0}, 121 {Name: "bar", Type: &Int{Size: 4}, Offset: 0}, 122 }, 123 }, 124 }, 125 }, 126 }, 127 "type t struct { foo uint32; _ [4]byte; }", 128 }, 129 { 130 &Datasec{ 131 Size: 16, 132 Vars: []VarSecinfo{ 133 {&Var{Name: "s", Type: &Int{Size: 2}, Linkage: StaticVar}, 0, 2}, 134 {&Var{Name: "g", Type: &Int{Size: 4}, Linkage: GlobalVar}, 4, 4}, 135 {&Var{Name: "e", Type: &Int{Size: 8}, Linkage: ExternVar}, 8, 8}, 136 }, 137 }, 138 "type t struct { _ [4]byte; g uint32; _ [8]byte; }", 139 }, 140 } 141 142 for _, test := range tests { 143 t.Run(fmt.Sprint(test.typ), func(t *testing.T) { 144 have := mustGoTypeDeclaration(t, test.typ, nil, nil) 145 if have != test.output { 146 t.Errorf("Unexpected output:\n\t-%s\n\t+%s", test.output, have) 147 } 148 }) 149 } 150 } 151 152 func TestGoTypeDeclarationNamed(t *testing.T) { 153 e1 := &Enum{Name: "e1", Size: 4} 154 s1 := &Struct{ 155 Name: "s1", 156 Size: 4, 157 Members: []Member{ 158 {Name: "frob", Type: e1}, 159 }, 160 } 161 s2 := &Struct{ 162 Name: "s2", 163 Size: 4, 164 Members: []Member{ 165 {Name: "frood", Type: s1}, 166 }, 167 } 168 td := &Typedef{Name: "td", Type: e1} 169 arr := &Array{Nelems: 1, Type: td} 170 171 tests := []struct { 172 typ Type 173 named []Type 174 output string 175 }{ 176 {e1, []Type{e1}, "type t uint32"}, 177 {s1, []Type{e1, s1}, "type t struct { frob E1; }"}, 178 {s2, []Type{e1}, "type t struct { frood struct { frob E1; }; }"}, 179 {s2, []Type{e1, s1}, "type t struct { frood S1; }"}, 180 {td, nil, "type t uint32"}, 181 {td, []Type{td}, "type t uint32"}, 182 {arr, []Type{td}, "type t [1]TD"}, 183 } 184 185 for _, test := range tests { 186 t.Run(fmt.Sprint(test.typ), func(t *testing.T) { 187 names := make(map[Type]string) 188 for _, t := range test.named { 189 names[t] = strings.ToUpper(t.TypeName()) 190 } 191 192 have := mustGoTypeDeclaration(t, test.typ, names, nil) 193 if have != test.output { 194 t.Errorf("Unexpected output:\n\t-%s\n\t+%s", test.output, have) 195 } 196 }) 197 } 198 } 199 200 func TestGoTypeDeclarationQualifiers(t *testing.T) { 201 i := &Int{Size: 4} 202 want := mustGoTypeDeclaration(t, i, nil, nil) 203 204 tests := []struct { 205 typ Type 206 }{ 207 {&Volatile{Type: i}}, 208 {&Const{Type: i}}, 209 {&Restrict{Type: i}}, 210 } 211 212 for _, test := range tests { 213 t.Run(fmt.Sprint(test.typ), func(t *testing.T) { 214 have := mustGoTypeDeclaration(t, test.typ, nil, nil) 215 if have != want { 216 t.Errorf("Unexpected output:\n\t-%s\n\t+%s", want, have) 217 } 218 }) 219 } 220 } 221 222 func TestGoTypeDeclarationCycle(t *testing.T) { 223 s := &Struct{Name: "cycle"} 224 s.Members = []Member{{Name: "f", Type: s}} 225 226 var gf GoFormatter 227 _, err := gf.TypeDeclaration("t", s) 228 if !errors.Is(err, errNestedTooDeep) { 229 t.Fatal("Expected errNestedTooDeep, got", err) 230 } 231 } 232 233 func TestRejectBogusTypes(t *testing.T) { 234 tests := []struct { 235 typ Type 236 }{ 237 {&Struct{ 238 Size: 1, 239 Members: []Member{ 240 {Name: "foo", Type: &Int{Size: 2}, Offset: 0}, 241 }, 242 }}, 243 {&Int{Size: 2, Encoding: Bool}}, 244 {&Int{Size: 1, Encoding: Char | Signed}}, 245 {&Int{Size: 2, Encoding: Char}}, 246 } 247 for _, test := range tests { 248 t.Run(fmt.Sprint(test.typ), func(t *testing.T) { 249 var gf GoFormatter 250 251 _, err := gf.TypeDeclaration("t", test.typ) 252 if err == nil { 253 t.Fatal("TypeDeclaration does not reject bogus type") 254 } 255 }) 256 } 257 } 258 259 func mustGoTypeDeclaration(tb testing.TB, typ Type, names map[Type]string, id func(string) string) string { 260 tb.Helper() 261 262 gf := GoFormatter{ 263 Names: names, 264 Identifier: id, 265 } 266 267 have, err := gf.TypeDeclaration("t", typ) 268 if err != nil { 269 tb.Fatal(err) 270 } 271 272 _, err = format.Source([]byte(have)) 273 if err != nil { 274 tb.Fatalf("Output can't be formatted: %s\n%s", err, have) 275 } 276 277 return have 278 }