github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/internal/kconfig/kconfig_test.go (about) 1 package kconfig 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "os" 7 "testing" 8 9 "github.com/cilium/ebpf/btf" 10 "github.com/cilium/ebpf/internal" 11 12 "github.com/go-quicktest/qt" 13 ) 14 15 func BenchmarkParse(b *testing.B) { 16 f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") 17 if err != nil { 18 b.Fatal(err) 19 } 20 defer f.Close() 21 22 b.ReportAllocs() 23 b.ResetTimer() 24 25 for n := 0; n < b.N; n++ { 26 _, err := Parse(f, nil) 27 if err != nil { 28 b.Fatal(err) 29 } 30 } 31 } 32 33 func BenchmarkParseFiltered(b *testing.B) { 34 f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") 35 if err != nil { 36 b.Fatal(err) 37 } 38 defer f.Close() 39 40 b.ReportAllocs() 41 b.ResetTimer() 42 43 // CONFIG_ARCH_USE_MEMTEST is the last CONFIG_ in the file. 44 // So, we will easily be able to see how many allocated bytes the filtering 45 // permits reducing compared to unfiltered benchmark. 46 filter := map[string]struct{}{"CONFIG_ARCH_USE_MEMTEST": {}} 47 48 for n := 0; n < b.N; n++ { 49 _, err := Parse(f, filter) 50 if err != nil { 51 b.Fatal(err) 52 } 53 } 54 } 55 56 func TestParse(t *testing.T) { 57 t.Parallel() 58 59 f, err := os.Open("testdata/test.kconfig") 60 if err != nil { 61 t.Fatal("Error reading /testdata/test.kconfig: ", err) 62 } 63 defer f.Close() 64 65 config, err := Parse(f, nil) 66 if err != nil { 67 t.Fatal("Error parsing kconfig: ", err) 68 } 69 70 expected := map[string]string{ 71 "CONFIG_TRISTATE": "m", 72 "CONFIG_BOOL": "y", 73 "CONFIG_CHAR": "100", 74 "CONFIG_USHORT": "30000", 75 "CONFIG_INT": "123456", 76 "CONFIG_ULONG": "0xDEADBEEFC0DE", 77 "CONFIG_STR": `"abracad"`, 78 "CONFIG_FOO": `"foo"`, 79 } 80 qt.Assert(t, qt.DeepEquals(config, expected)) 81 } 82 83 func TestParseFiltered(t *testing.T) { 84 t.Parallel() 85 86 f, err := os.Open("testdata/test.kconfig") 87 if err != nil { 88 t.Fatal("Error reading /testdata/test.kconfig: ", err) 89 } 90 defer f.Close() 91 92 filter := map[string]struct{}{"CONFIG_FOO": {}} 93 94 config, err := Parse(f, filter) 95 if err != nil { 96 t.Fatal("Error parsing gzipped kconfig: ", err) 97 } 98 99 expected := map[string]string{"CONFIG_FOO": `"foo"`} 100 qt.Assert(t, qt.DeepEquals(config, expected)) 101 } 102 103 func TestParseGzipped(t *testing.T) { 104 t.Parallel() 105 106 f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") 107 if err != nil { 108 t.Fatal("Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: ", err) 109 } 110 defer f.Close() 111 112 _, err = Parse(f, nil) 113 if err != nil { 114 t.Fatal("Error parsing gzipped kconfig: ", err) 115 } 116 } 117 118 func TestParseGzippedFiltered(t *testing.T) { 119 t.Parallel() 120 121 f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") 122 if err != nil { 123 t.Fatal("Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: ", err) 124 } 125 defer f.Close() 126 127 filter := map[string]struct{}{"CONFIG_HZ": {}} 128 129 config, err := Parse(f, filter) 130 if err != nil { 131 t.Fatal("Error parsing gzipped kconfig: ", err) 132 } 133 134 expected := map[string]string{"CONFIG_HZ": "1000"} 135 qt.Assert(t, qt.DeepEquals(config, expected)) 136 } 137 138 func TestProcessKconfigBadLine(t *testing.T) { 139 t.Parallel() 140 141 m := make(map[string]string) 142 143 err := processKconfigLine([]byte("CONFIG_FOO"), m, nil) 144 qt.Assert(t, qt.IsNotNil(err), qt.Commentf("line has no '='")) 145 146 err = processKconfigLine([]byte("CONFIG_FOO="), m, nil) 147 qt.Assert(t, qt.IsNotNil(err), qt.Commentf("line has no value")) 148 } 149 150 func TestPutValue(t *testing.T) { 151 t.Parallel() 152 153 type testCase struct { 154 typ btf.Type 155 value string 156 expected any 157 comment string 158 } 159 160 cases := []testCase{ 161 { 162 typ: &btf.Int{ 163 Size: 1, 164 Encoding: btf.Bool, 165 }, 166 value: "n", 167 expected: int8(0), 168 }, 169 { 170 typ: &btf.Int{ 171 Size: 1, 172 Encoding: btf.Bool, 173 }, 174 value: "y", 175 expected: int8(1), 176 }, 177 { 178 typ: &btf.Int{ 179 Size: 1, 180 Encoding: btf.Bool, 181 }, 182 value: "foo", 183 comment: "Bad value", 184 }, 185 { 186 typ: &btf.Int{}, 187 comment: "Encoding is not Bool", 188 }, 189 { 190 typ: &btf.Int{ 191 Encoding: btf.Bool, 192 }, 193 comment: "Size is not 1", 194 }, 195 { 196 typ: &btf.Enum{ 197 Name: "libbpf_tristate", 198 }, 199 value: "y", 200 expected: int64(TriYes), 201 }, 202 { 203 typ: &btf.Enum{ 204 Name: "libbpf_tristate", 205 }, 206 value: "n", 207 expected: int64(TriNo), 208 }, 209 { 210 typ: &btf.Enum{ 211 Name: "libbpf_tristate", 212 }, 213 value: "m", 214 expected: int64(TriModule), 215 }, 216 { 217 typ: &btf.Enum{ 218 Name: "libbpf_tristate", 219 }, 220 value: "foo", 221 comment: "Bad value", 222 }, 223 { 224 typ: &btf.Enum{ 225 Name: "error", 226 }, 227 comment: "Enum name is wrong", 228 }, 229 { 230 typ: &btf.Array{}, 231 value: "y", 232 comment: "Type is not btf.Int", 233 }, 234 { 235 typ: &btf.Int{ 236 Size: 1, 237 }, 238 value: "255", 239 expected: uint8(255), 240 }, 241 { 242 typ: &btf.Int{ 243 Size: 2, 244 }, 245 value: "0xcafe", 246 expected: uint16(0xcafe), 247 }, 248 { 249 typ: &btf.Int{ 250 Size: 2, 251 }, 252 value: "0755", 253 expected: uint16(0755), 254 }, 255 { 256 typ: &btf.Int{ 257 Size: 4, 258 Encoding: btf.Signed, 259 }, 260 value: "-2147483648", 261 expected: int32(-2147483648), 262 }, 263 { 264 typ: &btf.Int{ 265 Size: 4, 266 Encoding: btf.Signed, 267 }, 268 value: "+2147483647", 269 expected: int32(+2147483647), 270 }, 271 { 272 typ: &btf.Int{ 273 Size: 4, 274 }, 275 value: "0xcafec0de", 276 expected: uint32(0xcafec0de), 277 }, 278 { 279 typ: &btf.Int{ 280 Size: 8, 281 Encoding: btf.Signed, 282 }, 283 value: "+1000000000000", 284 expected: int64(1000000000000), 285 }, 286 { 287 typ: &btf.Int{ 288 Size: 8, 289 }, 290 value: "1000000000000", 291 expected: uint64(1000000000000), 292 }, 293 { 294 typ: &btf.Int{ 295 Size: 1, 296 }, 297 value: "foo", 298 comment: "Value is not an int", 299 }, 300 { 301 typ: &btf.Array{}, 302 value: "1", 303 comment: "Type is not btf.Int", 304 }, 305 { 306 typ: &btf.Int{ 307 Size: 16, 308 }, 309 value: "1", 310 comment: "Size is wrong", 311 }, 312 { 313 typ: &btf.Typedef{ 314 Type: &btf.Int{ 315 Size: 1, 316 }, 317 }, 318 value: "1", 319 expected: uint8(1), 320 }, 321 { 322 typ: &btf.Array{ 323 Type: &btf.Int{ 324 Size: 1, 325 Encoding: btf.Char, 326 }, 327 Nelems: 6, 328 }, 329 value: `"foobar"`, 330 expected: []byte("foobar"), 331 }, 332 { 333 typ: &btf.Array{ 334 Type: &btf.Int{ 335 Size: 1, 336 Encoding: btf.Unsigned, 337 }, 338 Nelems: 3, 339 }, 340 value: `"foobar"`, 341 expected: []byte("foo"), 342 }, 343 { 344 typ: &btf.Array{ 345 Type: &btf.Int{ 346 Size: 1, 347 Encoding: btf.Signed, 348 }, 349 Nelems: 2, 350 }, 351 value: `"42"`, 352 expected: []byte("42"), 353 }, 354 { 355 typ: &btf.Int{}, 356 value: `"foo"`, 357 comment: "Type is not btf.Array", 358 }, 359 { 360 typ: &btf.Array{}, 361 value: `"foo"`, 362 comment: "Type is not btf.Array of btf.Int", 363 }, 364 { 365 typ: &btf.Array{ 366 Type: &btf.Int{ 367 Size: 1, 368 Encoding: btf.Bool, 369 }, 370 }, 371 comment: "Type is not btf.Array of btf.Int of size 1 which is not btf.Bool", 372 }, 373 { 374 typ: &btf.Array{ 375 Type: &btf.Int{ 376 Size: 4, 377 Encoding: btf.Char, 378 }, 379 }, 380 value: `"foo"`, 381 comment: "Type is not btf.Array of btf.Char of size 1", 382 }, 383 { 384 typ: &btf.Array{ 385 Type: &btf.Int{ 386 Size: 1, 387 Encoding: btf.Char, 388 }, 389 }, 390 value: `"foo`, 391 comment: `Value does not start and end with '"'`, 392 }, 393 } 394 395 for _, c := range cases { 396 if len(c.comment) > 0 { 397 err := PutValue(make([]byte, 0), c.typ, c.value) 398 399 qt.Assert(t, qt.IsNotNil(err), qt.Commentf(c.comment)) 400 401 continue 402 } 403 404 var buf bytes.Buffer 405 err := binary.Write(&buf, internal.NativeEndian, c.expected) 406 if err != nil { 407 t.Fatal(err) 408 } 409 410 expected := buf.Bytes() 411 data := make([]byte, len(expected)) 412 err = PutValue(data, c.typ, c.value) 413 414 qt.Assert(t, qt.IsNil(err)) 415 416 qt.Assert(t, qt.DeepEquals(data, expected)) 417 } 418 } 419 420 func TestPutInteger(t *testing.T) { 421 t.Parallel() 422 423 type testCase struct { 424 expected []byte 425 integer *btf.Int 426 n uint64 427 err bool 428 comment string 429 } 430 431 cases := []testCase{ 432 { 433 integer: &btf.Int{Size: 1, Encoding: btf.Unsigned}, 434 n: 0x01, 435 expected: []byte{0x01}, 436 }, 437 { 438 integer: &btf.Int{Size: 2, Encoding: btf.Unsigned}, 439 n: 0x902a, 440 expected: []byte{0x2a, 0x90}, 441 }, 442 { 443 integer: &btf.Int{Size: 4, Encoding: btf.Unsigned}, 444 n: 0x01234567, 445 expected: []byte{0x67, 0x45, 0x23, 0x01}, 446 }, 447 { 448 integer: &btf.Int{Size: 1, Encoding: btf.Signed}, 449 n: 0x80, 450 err: true, 451 comment: "outside of range int8 -128 ~ 127", 452 }, 453 { 454 integer: &btf.Int{Size: 2, Encoding: btf.Signed}, 455 n: 0xabcdabcd, 456 err: true, 457 comment: "outside of range int16 -32768 ~ 32767", 458 }, 459 { 460 integer: &btf.Int{Size: 4, Encoding: btf.Signed}, 461 n: 0x1234567890, 462 err: true, 463 comment: "outside of range int32 -2147483648 ~ 2147483647", 464 }, 465 { 466 integer: &btf.Int{Size: 2, Encoding: btf.Signed}, 467 n: 0xffffffffffffffff, 468 expected: []byte{0xff, 0xff, 0x00, 0x00}, 469 comment: "n means -1", 470 }, 471 { 472 integer: &btf.Int{Size: 2, Encoding: btf.Signed}, 473 n: 0xffffffffffffffff - 0x8000, 474 err: true, 475 comment: "n means -32768(-MinInt16) - 1 in signed value", 476 }, 477 { 478 integer: &btf.Int{Size: 2, Encoding: btf.Signed}, 479 n: 0x7fff, 480 expected: []byte{0xff, 0x7f}, 481 comment: "maximum value of int16", 482 }, 483 { 484 integer: &btf.Int{Size: 2, Encoding: btf.Unsigned}, 485 n: 0xffff, 486 expected: []byte{0xff, 0xff}, 487 }, 488 { 489 integer: &btf.Int{Size: 4, Encoding: btf.Unsigned}, 490 n: 0xffffffff, 491 expected: []byte{0xff, 0xff, 0xff, 0xff}, 492 }, 493 { 494 integer: &btf.Int{Size: 4, Encoding: btf.Signed}, 495 n: 0x80000000, 496 err: true, 497 comment: "outside of range int32 ~2147483648 ~ 2147483647", 498 }, 499 { 500 integer: &btf.Int{Size: 4, Encoding: btf.Signed}, 501 n: 0xffffffffffffffff - 0x80000000, 502 err: true, 503 comment: "outside of range int32 ~2147483648 ~ 2147483647", 504 }, 505 { 506 integer: &btf.Int{Size: 8, Encoding: btf.Unsigned}, 507 n: 0xffffffffffffffff, 508 expected: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 509 }, 510 } 511 512 for _, c := range cases { 513 data := make([]byte, len(c.expected)) 514 err := PutInteger(data, c.integer, c.n) 515 516 if c.err { 517 qt.Assert(t, qt.IsNotNil(err)) 518 continue 519 } 520 521 qt.Assert(t, qt.IsNil(err)) 522 qt.Assert(t, qt.DeepEquals(data, c.expected), qt.Commentf(c.comment)) 523 } 524 } 525 526 func TestPutIntegerError(t *testing.T) { 527 qt.Assert(t, qt.IsNotNil(PutInteger(nil, &btf.Int{Size: 2}, 0)), qt.Commentf("slice too small for int")) 528 qt.Assert(t, qt.IsNotNil(PutInteger(nil, &btf.Int{Encoding: btf.Bool}, 2)), qt.Commentf("n too big for bool")) 529 }