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  }