blob: 74f4da1acf368e8d9e2a8d9646e47efc70f6f6bd [file] [log] [blame]
package sftp
import (
"strconv"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAllocator(t *testing.T) {
allocator := newAllocator()
// get a page for request order id 1
page := allocator.GetPage(1)
page[1] = uint8(1)
assert.Equal(t, maxMsgLength, len(page))
assert.Equal(t, 1, allocator.countUsedPages())
// get another page for request order id 1, we now have 2 used pages
page = allocator.GetPage(1)
page[0] = uint8(2)
assert.Equal(t, 2, allocator.countUsedPages())
// get another page for request order id 1, we now have 3 used pages
page = allocator.GetPage(1)
page[2] = uint8(3)
assert.Equal(t, 3, allocator.countUsedPages())
// release the page for request order id 1, we now have 3 available pages
allocator.ReleasePages(1)
assert.NotContains(t, allocator.used, 1)
assert.Equal(t, 3, allocator.countAvailablePages())
// get a page for request order id 2
// we get the latest released page, let's verify that by checking the previously written values
// so we are sure we are reusing a previously allocated page
page = allocator.GetPage(2)
assert.Equal(t, uint8(3), page[2])
assert.Equal(t, 2, allocator.countAvailablePages())
assert.Equal(t, 1, allocator.countUsedPages())
page = allocator.GetPage(2)
assert.Equal(t, uint8(2), page[0])
assert.Equal(t, 1, allocator.countAvailablePages())
assert.Equal(t, 2, allocator.countUsedPages())
page = allocator.GetPage(2)
assert.Equal(t, uint8(1), page[1])
// we now have 3 used pages for request order id 2 and no available pages
assert.Equal(t, 0, allocator.countAvailablePages())
assert.Equal(t, 3, allocator.countUsedPages())
assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
// release some request order id with no allocated pages, should have no effect
allocator.ReleasePages(1)
allocator.ReleasePages(3)
assert.Equal(t, 0, allocator.countAvailablePages())
assert.Equal(t, 3, allocator.countUsedPages())
assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
// now get some pages for another request order id
allocator.GetPage(3)
// we now must have 3 used pages for request order id 2 and 1 used page for request order id 3
assert.Equal(t, 0, allocator.countAvailablePages())
assert.Equal(t, 4, allocator.countUsedPages())
assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
assert.True(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be used")
assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
// get another page for request order id 3
allocator.GetPage(3)
assert.Equal(t, 0, allocator.countAvailablePages())
assert.Equal(t, 5, allocator.countUsedPages())
assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
assert.True(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be used")
assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
// now release the pages for request order id 3
allocator.ReleasePages(3)
assert.Equal(t, 2, allocator.countAvailablePages())
assert.Equal(t, 3, allocator.countUsedPages())
assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
assert.False(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be not used")
// again check we are reusing previously allocated pages.
// We have written nothing to the 2 last requested page so release them and get the third one
allocator.ReleasePages(2)
assert.Equal(t, 5, allocator.countAvailablePages())
assert.Equal(t, 0, allocator.countUsedPages())
assert.False(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be not used")
allocator.GetPage(4)
allocator.GetPage(4)
page = allocator.GetPage(4)
assert.Equal(t, uint8(3), page[2])
assert.Equal(t, 2, allocator.countAvailablePages())
assert.Equal(t, 3, allocator.countUsedPages())
assert.True(t, allocator.isRequestOrderIDUsed(4), "page with request order id 4 must be used")
// free the allocator
allocator.Free()
assert.Equal(t, 0, allocator.countAvailablePages())
assert.Equal(t, 0, allocator.countUsedPages())
}
func BenchmarkAllocatorSerial(b *testing.B) {
allocator := newAllocator()
for i := 0; i < b.N; i++ {
benchAllocator(allocator, uint32(i))
}
}
func BenchmarkAllocatorParallel(b *testing.B) {
var counter uint32
allocator := newAllocator()
for i := 1; i <= 8; i *= 2 {
b.Run(strconv.Itoa(i), func(b *testing.B) {
b.SetParallelism(i)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
benchAllocator(allocator, atomic.AddUint32(&counter, 1))
}
})
})
}
}
func benchAllocator(allocator *allocator, requestOrderID uint32) {
// simulates the page requested in recvPacket
allocator.GetPage(requestOrderID)
// simulates the page requested in fileget for downloads
allocator.GetPage(requestOrderID)
// release the allocated pages
allocator.ReleasePages(requestOrderID)
}
// useful for debug
func printAllocatorContents(allocator *allocator) {
for o, u := range allocator.used {
debug("used order id: %v, values: %+v", o, u)
}
for _, v := range allocator.available {
debug("available, values: %+v", v)
}
}