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