github.com/pkg/sftp@v1.13.6/allocator_test.go (about)

     1  package sftp
     2  
     3  import (
     4  	"strconv"
     5  	"sync/atomic"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  )
    10  
    11  func TestAllocator(t *testing.T) {
    12  	allocator := newAllocator()
    13  	// get a page for request order id 1
    14  	page := allocator.GetPage(1)
    15  	page[1] = uint8(1)
    16  	assert.Equal(t, maxMsgLength, len(page))
    17  	assert.Equal(t, 1, allocator.countUsedPages())
    18  	// get another page for request order id 1, we now have 2 used pages
    19  	page = allocator.GetPage(1)
    20  	page[0] = uint8(2)
    21  	assert.Equal(t, 2, allocator.countUsedPages())
    22  	// get another page for request order id 1, we now have 3 used pages
    23  	page = allocator.GetPage(1)
    24  	page[2] = uint8(3)
    25  	assert.Equal(t, 3, allocator.countUsedPages())
    26  	// release the page for request order id 1, we now have 3 available pages
    27  	allocator.ReleasePages(1)
    28  	assert.NotContains(t, allocator.used, 1)
    29  	assert.Equal(t, 3, allocator.countAvailablePages())
    30  	// get a page for request order id 2
    31  	// we get the latest released page, let's verify that by checking the previously written values
    32  	// so we are sure we are reusing a previously allocated page
    33  	page = allocator.GetPage(2)
    34  	assert.Equal(t, uint8(3), page[2])
    35  	assert.Equal(t, 2, allocator.countAvailablePages())
    36  	assert.Equal(t, 1, allocator.countUsedPages())
    37  	page = allocator.GetPage(2)
    38  	assert.Equal(t, uint8(2), page[0])
    39  	assert.Equal(t, 1, allocator.countAvailablePages())
    40  	assert.Equal(t, 2, allocator.countUsedPages())
    41  	page = allocator.GetPage(2)
    42  	assert.Equal(t, uint8(1), page[1])
    43  	// we now have 3 used pages for request order id 2 and no available pages
    44  	assert.Equal(t, 0, allocator.countAvailablePages())
    45  	assert.Equal(t, 3, allocator.countUsedPages())
    46  	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
    47  	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
    48  	// release some request order id with no allocated pages, should have no effect
    49  	allocator.ReleasePages(1)
    50  	allocator.ReleasePages(3)
    51  	assert.Equal(t, 0, allocator.countAvailablePages())
    52  	assert.Equal(t, 3, allocator.countUsedPages())
    53  	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
    54  	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
    55  	// now get some pages for another request order id
    56  	allocator.GetPage(3)
    57  	// we now must have 3 used pages for request order id 2 and 1 used page for request order id 3
    58  	assert.Equal(t, 0, allocator.countAvailablePages())
    59  	assert.Equal(t, 4, allocator.countUsedPages())
    60  	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
    61  	assert.True(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be used")
    62  	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
    63  	// get another page for request order id 3
    64  	allocator.GetPage(3)
    65  	assert.Equal(t, 0, allocator.countAvailablePages())
    66  	assert.Equal(t, 5, allocator.countUsedPages())
    67  	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
    68  	assert.True(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be used")
    69  	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
    70  	// now release the pages for request order id 3
    71  	allocator.ReleasePages(3)
    72  	assert.Equal(t, 2, allocator.countAvailablePages())
    73  	assert.Equal(t, 3, allocator.countUsedPages())
    74  	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
    75  	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
    76  	assert.False(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be not used")
    77  	// again check we are reusing previously allocated pages.
    78  	// We have written nothing to the 2 last requested page so release them and get the third one
    79  	allocator.ReleasePages(2)
    80  	assert.Equal(t, 5, allocator.countAvailablePages())
    81  	assert.Equal(t, 0, allocator.countUsedPages())
    82  	assert.False(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be not used")
    83  	allocator.GetPage(4)
    84  	allocator.GetPage(4)
    85  	page = allocator.GetPage(4)
    86  	assert.Equal(t, uint8(3), page[2])
    87  	assert.Equal(t, 2, allocator.countAvailablePages())
    88  	assert.Equal(t, 3, allocator.countUsedPages())
    89  	assert.True(t, allocator.isRequestOrderIDUsed(4), "page with request order id 4 must be used")
    90  	// free the allocator
    91  	allocator.Free()
    92  	assert.Equal(t, 0, allocator.countAvailablePages())
    93  	assert.Equal(t, 0, allocator.countUsedPages())
    94  }
    95  
    96  func BenchmarkAllocatorSerial(b *testing.B) {
    97  	allocator := newAllocator()
    98  	for i := 0; i < b.N; i++ {
    99  		benchAllocator(allocator, uint32(i))
   100  	}
   101  }
   102  
   103  func BenchmarkAllocatorParallel(b *testing.B) {
   104  	var counter uint32
   105  	allocator := newAllocator()
   106  	for i := 1; i <= 8; i *= 2 {
   107  		b.Run(strconv.Itoa(i), func(b *testing.B) {
   108  			b.SetParallelism(i)
   109  			b.RunParallel(func(pb *testing.PB) {
   110  				for pb.Next() {
   111  					benchAllocator(allocator, atomic.AddUint32(&counter, 1))
   112  				}
   113  			})
   114  		})
   115  	}
   116  }
   117  
   118  func benchAllocator(allocator *allocator, requestOrderID uint32) {
   119  	// simulates the page requested in recvPacket
   120  	allocator.GetPage(requestOrderID)
   121  	// simulates the page requested in fileget for downloads
   122  	allocator.GetPage(requestOrderID)
   123  	// release the allocated pages
   124  	allocator.ReleasePages(requestOrderID)
   125  }
   126  
   127  // useful for debug
   128  func printAllocatorContents(allocator *allocator) {
   129  	for o, u := range allocator.used {
   130  		debug("used order id: %v, values: %+v", o, u)
   131  	}
   132  	for _, v := range allocator.available {
   133  		debug("available, values: %+v", v)
   134  	}
   135  }