1 package memory
2
3 import (
4 "testing"
5 )
6
7
8 func testAllocate(t *testing.T, ma *PoolAllocator, sz uint64) {
9 t.Helper()
10 _, err := ma.Allocate(sz)
11 if err != nil {
12 t.Fatalf("unexpected error: %s", err)
13 }
14 if len(ma.pools[0].busy) != 1 {
15 t.Fatal("memory slot wasn't marked as busy")
16 }
17 }
18
19 func Test_MemAlloc_findNextOffset(t *testing.T) {
20 ma := NewPoolMemoryAllocator()
21 cls, offset, err := ma.findNextOffset(0)
22 if err != nil {
23 t.Fatalf("unexpected error: %s", err)
24 }
25
26 if cls != memoryClassNumber-1 {
27 t.Fatalf("expected class=%d, got %d", memoryClassNumber-1, cls)
28 }
29 if offset != 0 {
30 t.Fatalf("expected offset=%d, got %d", 0, offset)
31 }
32 }
33
34 func Test_MemAlloc_allocate_without_expand(t *testing.T) {
35 ma := &PoolAllocator{}
36 ma.pools[0] = newEmptyMemoryPool()
37 ma.pools[0].free[0] = ®ion{
38 class: 0,
39 offset: 0,
40 }
41
42 testAllocate(t, ma, MiB)
43 }
44
45 func Test_MemAlloc_allocate_not_enough_space(t *testing.T) {
46 ma := &PoolAllocator{}
47
48 _, err := ma.Allocate(MiB)
49 if err == nil {
50 t.Fatal("expected error, got nil")
51 }
52 if err != ErrNotEnoughSpace {
53 t.Fatalf("expected error=%s, got error=%s", ErrNotEnoughSpace, err)
54 }
55 }
56
57 func Test_MemAlloc_expand(t *testing.T) {
58 pa := &PoolAllocator{}
59 pa.pools[1] = newEmptyMemoryPool()
60 pa.pools[1].free[0] = ®ion{
61 class: 1,
62 offset: 0,
63 }
64
65 err := pa.split(0)
66 if err != nil {
67 t.Fatalf("unexpected error: %s", err)
68 }
69
70 if _, o, err := pa.findNextOffset(1); err == nil {
71 t.Fatalf("no free offset should be found for class 1, got offset=%d", o)
72 }
73
74 poolCls0 := pa.pools[0]
75 for i := 0; i < 4; i++ {
76 offset := uint64(i) * MiB
77 _, ok := poolCls0.free[offset]
78 if !ok {
79 t.Fatalf("did not find region with offset=%d", offset)
80 }
81 delete(poolCls0.free, offset)
82 }
83
84 if len(poolCls0.free) > 0 {
85 t.Fatalf("extra memory regions: %v", poolCls0.free)
86 }
87 }
88
89 func Test_MemAlloc_allocate_automatically_expands(t *testing.T) {
90 pa := &PoolAllocator{}
91 pa.pools[2] = newEmptyMemoryPool()
92 pa.pools[2].free[MiB] = ®ion{
93 class: 2,
94 offset: MiB,
95 }
96
97 testAllocate(t, pa, MiB)
98
99 if pa.pools[1] == nil {
100 t.Fatalf("memory not extended for class type 1")
101 }
102 if len(pa.pools[2].free) > 0 {
103 t.Fatalf("expected no free regions for class type 2, got: %v", pa.pools[2].free)
104 }
105 }
106
107 func Test_MemAlloc_alloc_and_release(t *testing.T) {
108 pa := &PoolAllocator{}
109 pa.pools[0] = newEmptyMemoryPool()
110 r := ®ion{
111 class: 0,
112 offset: 0,
113 }
114 pa.pools[0].free[0] = r
115
116 testAllocate(t, pa, MiB)
117
118 err := pa.Release(r)
119 if err != nil {
120 t.Fatalf("error releasing resources: %s", err)
121 }
122 if len(pa.pools[0].busy) != 0 {
123 t.Fatalf("resources not marked as free: %v", pa.pools[0].busy)
124 }
125 if len(pa.pools[0].free) == 0 {
126 t.Fatal("resource not assigned back to the free pool")
127 }
128 }
129
130 func Test_MemAlloc_alloc_invalid_larger_than_max(t *testing.T) {
131 pa := &PoolAllocator{}
132
133 _, err := pa.Allocate(maximumClassSize + 1)
134 if err == nil {
135 t.Fatal("no error returned")
136 }
137 if err != ErrInvalidMemoryClass {
138 t.Fatalf("expected error=%s, got error=%s", ErrInvalidMemoryClass, err)
139 }
140 }
141
142 func Test_MemAlloc_release_invalid_offset(t *testing.T) {
143 pa := &PoolAllocator{}
144 pa.pools[0] = newEmptyMemoryPool()
145 r := ®ion{
146 class: 0,
147 offset: 0,
148 }
149 pa.pools[0].free[0] = r
150
151 testAllocate(t, pa, MiB)
152
153
154 r.offset = MiB
155 err := pa.Release(r)
156 if err == nil {
157 t.Fatal("no error returned")
158 }
159 if err != ErrNotAllocated {
160 t.Fatalf("wrong error returned: %s", err)
161 }
162 }
163
164 func Test_MemAlloc_Max_Out(t *testing.T) {
165 ma := NewPoolMemoryAllocator()
166 for i := 0; i < 4096; i++ {
167 _, err := ma.Allocate(MiB)
168 if err != nil {
169 t.Fatalf("unexpected error during memory allocation: %s", err)
170 }
171 }
172 if len(ma.pools[0].busy) != 4096 {
173 t.Fatalf("expected 4096 busy blocks of class 0, got %d instead", len(ma.pools[0].busy))
174 }
175 for i := 0; i < 4096; i++ {
176 offset := uint64(i) * MiB
177 if _, ok := ma.pools[0].busy[offset]; !ok {
178 t.Fatalf("expected to find offset %d", offset)
179 }
180 }
181 }
182
183 func Test_GetMemoryClass(t *testing.T) {
184 type config struct {
185 name string
186 size uint64
187 expected classType
188 }
189
190 testCases := []config{
191 {
192 name: "Size_1MB_Class_0",
193 size: MiB,
194 expected: 0,
195 },
196 {
197 name: "Size_6MB_Class_2",
198 size: 6 * MiB,
199 expected: 2,
200 },
201 {
202 name: "Size_2GB_Class_6",
203 size: 2 * GiB,
204 expected: 6,
205 },
206 }
207
208 for _, tc := range testCases {
209 t.Run(tc.name, func(t *testing.T) {
210 c := GetMemoryClassType(tc.size)
211 if c != tc.expected {
212 t.Fatalf("expected classType for size: %d is %d, got %d instead", tc.size, tc.expected, c)
213 }
214 })
215 }
216 }
217
218 func Test_GetMemoryClassSize(t *testing.T) {
219 type config struct {
220 name string
221 clsType classType
222 expected uint64
223 err error
224 }
225
226 testCases := []config{
227 {
228 name: "Class_0_Size_1MB",
229 clsType: 0,
230 expected: minimumClassSize,
231 },
232 {
233 name: "Class_8_Size_4GB",
234 clsType: 6,
235 expected: maximumClassSize,
236 },
237 {
238 name: "Class_7_ErrorInvalidMemoryClass",
239 clsType: 7,
240 err: ErrInvalidMemoryClass,
241 },
242 }
243 for _, tc := range testCases {
244 t.Run(tc.name, func(t *testing.T) {
245 s, err := GetMemoryClassSize(tc.clsType)
246 if err != tc.err {
247 t.Fatalf("expected error to be %s, got %s instead", tc.err, err)
248 }
249 if s != tc.expected {
250 t.Fatalf("expected size to be %d, got %d instead", tc.expected, s)
251 }
252 })
253 }
254 }
255
View as plain text