...

Source file src/github.com/cilium/ebpf/btf/format_test.go

Documentation: github.com/cilium/ebpf/btf

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

View as plain text