1package btf
2
3import (
4	"fmt"
5	"testing"
6
7	"github.com/google/go-cmp/cmp"
8)
9
10func TestSizeof(t *testing.T) {
11	testcases := []struct {
12		size int
13		typ  Type
14	}{
15		{0, (*Void)(nil)},
16		{1, &Int{Size: 1}},
17		{4, &Enum{}},
18		{0, &Array{Type: &Pointer{Target: (*Void)(nil)}, Nelems: 0}},
19		{12, &Array{Type: &Enum{}, Nelems: 3}},
20	}
21
22	for _, tc := range testcases {
23		name := fmt.Sprint(tc.typ)
24		t.Run(name, func(t *testing.T) {
25			have, err := Sizeof(tc.typ)
26			if err != nil {
27				t.Fatal("Can't calculate size:", err)
28			}
29			if have != tc.size {
30				t.Errorf("Expected size %d, got %d", tc.size, have)
31			}
32		})
33	}
34}
35
36func TestCopyType(t *testing.T) {
37	_ = copyType((*Void)(nil))
38
39	in := &Int{Size: 4}
40	out := copyType(in)
41
42	in.Size = 8
43	if size := out.(*Int).Size; size != 4 {
44		t.Error("Copy doesn't make a copy, expected size 4, got", size)
45	}
46
47	t.Run("cyclical", func(t *testing.T) {
48		_ = copyType(newCyclicalType(2))
49	})
50}
51
52// The following are valid Types.
53//
54// There currently is no better way to document which
55// types implement an interface.
56func ExampleType_validTypes() {
57	var t Type
58	t = &Void{}
59	t = &Int{}
60	t = &Pointer{}
61	t = &Array{}
62	t = &Struct{}
63	t = &Union{}
64	t = &Enum{}
65	t = &Fwd{}
66	t = &Typedef{}
67	t = &Volatile{}
68	t = &Const{}
69	t = &Restrict{}
70	t = &Func{}
71	t = &FuncProto{}
72	t = &Var{}
73	t = &Datasec{}
74	_ = t
75}
76
77func TestType(t *testing.T) {
78	types := []func() Type{
79		func() Type { return &Void{} },
80		func() Type { return &Int{} },
81		func() Type { return &Pointer{Target: &Void{}} },
82		func() Type { return &Array{Type: &Int{}} },
83		func() Type {
84			return &Struct{
85				Members: []Member{{Type: &Void{}}},
86			}
87		},
88		func() Type {
89			return &Union{
90				Members: []Member{{Type: &Void{}}},
91			}
92		},
93		func() Type { return &Enum{} },
94		func() Type { return &Fwd{Name: "thunk"} },
95		func() Type { return &Typedef{Type: &Void{}} },
96		func() Type { return &Volatile{Type: &Void{}} },
97		func() Type { return &Const{Type: &Void{}} },
98		func() Type { return &Restrict{Type: &Void{}} },
99		func() Type { return &Func{Name: "foo", Type: &Void{}} },
100		func() Type {
101			return &FuncProto{
102				Params: []FuncParam{{Name: "bar", Type: &Void{}}},
103				Return: &Void{},
104			}
105		},
106		func() Type { return &Var{Type: &Void{}} },
107		func() Type {
108			return &Datasec{
109				Vars: []VarSecinfo{{Type: &Void{}}},
110			}
111		},
112	}
113
114	compareTypes := cmp.Comparer(func(a, b *Type) bool {
115		return a == b
116	})
117
118	for _, fn := range types {
119		typ := fn()
120		t.Run(fmt.Sprintf("%T", typ), func(t *testing.T) {
121			if typ == typ.copy() {
122				t.Error("Copy doesn't copy")
123			}
124
125			var first, second copyStack
126			typ.walk(&first)
127			typ.walk(&second)
128
129			if diff := cmp.Diff(first, second, compareTypes); diff != "" {
130				t.Errorf("Walk mismatch (-want +got):\n%s", diff)
131			}
132		})
133	}
134}
135
136func newCyclicalType(n int) Type {
137	ptr := &Pointer{}
138	prev := Type(ptr)
139	for i := 0; i < n; i++ {
140		switch i % 5 {
141		case 0:
142			prev = &Struct{
143				Members: []Member{
144					{Type: prev},
145				},
146			}
147
148		case 1:
149			prev = &Const{Type: prev}
150		case 2:
151			prev = &Volatile{Type: prev}
152		case 3:
153			prev = &Typedef{Type: prev}
154		case 4:
155			prev = &Array{Type: prev}
156		}
157	}
158	ptr.Target = prev
159	return ptr
160}
161