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  }