...

Source file src/github.com/Microsoft/hcsshim/internal/memory/pool_test.go

Documentation: github.com/Microsoft/hcsshim/internal/memory

     1  package memory
     2  
     3  import (
     4  	"testing"
     5  )
     6  
     7  // helper function to test and validate minimal allocation scenario
     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] = &region{
    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] = &region{
    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] = &region{
    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 := &region{
   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 := &region{
   146  		class:  0,
   147  		offset: 0,
   148  	}
   149  	pa.pools[0].free[0] = r
   150  
   151  	testAllocate(t, pa, MiB)
   152  
   153  	// change the actual offset
   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